diff --git a/SickBeard.py b/SickBeard.py index 6e06ba6514a5859c46b3ca97b83a3b95ea52b611..ba46efc0a3ab8e055b797fc723d64c3aa261e280 100755 --- a/SickBeard.py +++ b/SickBeard.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Author: Nic Wolfe <nic@wolfeden.ca> # URL: http://code.google.com/p/sickbeard/ # @@ -93,6 +93,11 @@ def daemonize(): dev_null = file('/dev/null', 'r') os.dup2(dev_null.fileno(), sys.stdin.fileno()) + if sickbeard.CREATEPID: + pid = str(os.getpid()) + logger.log(u"Writing PID " + pid + " to " + str(sickbeard.PIDFILE)) + file(sickbeard.PIDFILE, 'w').write("%s\n" % pid) + def main(): # do some preliminary stuff @@ -100,6 +105,7 @@ def main(): sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME) sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME) sickbeard.MY_ARGS = sys.argv[1:] + sickbeard.CREATEPID = False try: locale.setlocale(locale.LC_ALL, "") @@ -120,9 +126,9 @@ def main(): threading.currentThread().name = "MAIN" try: - opts, args = getopt.getopt(sys.argv[1:], "qfdp:", ['quiet', 'forceupdate', 'daemon', 'port=', 'tvbinz']) + opts, args = getopt.getopt(sys.argv[1:], "qfdp::", ['quiet', 'forceupdate', 'daemon', 'port=', 'tvbinz', 'pidfile=']) except getopt.GetoptError: - print "Available options: --quiet, --forceupdate, --port, --daemon" + print "Available options: --quiet, --forceupdate, --port, --daemon --pidfile" sys.exit() forceUpdate = False @@ -130,28 +136,47 @@ def main(): for o, a in opts: # for now we'll just silence the logging - if (o in ('-q', '--quiet')): + if o in ('-q', '--quiet'): consoleLogging = False # for now we'll just silence the logging - if (o in ('--tvbinz')): + if o in ('--tvbinz'): sickbeard.SHOW_TVBINZ = True # should we update right away? - if (o in ('-f', '--forceupdate')): + if o in ('-f', '--forceupdate'): forceUpdate = True # use a different port - if (o in ('-p', '--port')): + if o in ('-p', '--port'): forcedPort = int(a) # Run as a daemon - if (o in ('-d', '--daemon')): + if o in ('-d', '--daemon'): if sys.platform == 'win32': print "Daemonize not supported under Windows, starting normally" else: consoleLogging = False sickbeard.DAEMON = True + # write a pidfile if requested + if o in ('--pidfile'): + sickbeard.PIDFILE = str(a) + + # if the pidfile already exists, sickbeard may still be running, so exit + if os.path.exists(sickbeard.PIDFILE): + sys.exit("PID file " + sickbeard.PIDFILE + " already exists. Exiting.") + + # a pidfile is only useful in daemon mode + # also, test to make sure we can write the file properly + if sickbeard.DAEMON: + sickbeard.CREATEPID = True + try: + file(sickbeard.PIDFILE, 'w').write("pid\n") + except IOError, e: + raise SystemExit("Unable to write PID file: %s [%d]" % (e.strerror, e.errno)) + else: + logger.log(u"Not running in daemon mode. PID file creation disabled.") + if consoleLogging: print "Starting up Sick Beard "+SICKBEARD_VERSION+" from " + sickbeard.CONFIG_FILE diff --git a/autoProcessTV/hellaToSickBeard.py b/autoProcessTV/hellaToSickBeard.py index 65aadeae7df2d81716d05f0092c109f442be937c..5b73da7eb3a286c3f536177bf6e35f0e8bd65d00 100755 --- a/autoProcessTV/hellaToSickBeard.py +++ b/autoProcessTV/hellaToSickBeard.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Author: Nic Wolfe <nic@wolfeden.ca> # URL: http://code.google.com/p/sickbeard/ diff --git a/autoProcessTV/sabToSickBeard.py b/autoProcessTV/sabToSickBeard.py index c2d5e546b6b8a08f20be48f64796cc07b24b1fc8..8df7b1cc2e5dca4f05f8cdf957fab7be4938fc64 100755 --- a/autoProcessTV/sabToSickBeard.py +++ b/autoProcessTV/sabToSickBeard.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Author: Nic Wolfe <nic@wolfeden.ca> # URL: http://code.google.com/p/sickbeard/ @@ -28,4 +28,4 @@ if len(sys.argv) < 2: elif len(sys.argv) >= 3: autoProcessTV.processEpisode(sys.argv[1], sys.argv[2]) else: - autoProcessTV.processEpisode(sys.argv[1]) \ No newline at end of file + autoProcessTV.processEpisode(sys.argv[1]) diff --git a/data/css/config.css b/data/css/config.css index 20c12187cee024236e90068f2c1fdb22677d6ca3..f52f8a54c70c4ef51e32d64bf9110be7ad261276 100644 --- a/data/css/config.css +++ b/data/css/config.css @@ -1,7 +1,8 @@ #config{text-align:center;padding:0 30px 20px;} #config *{font-family:"Trebuchet MS", Verdana, sans-serif;margin:0;padding:0;} -#config .imgLink img {padding-bottom:1px;} -#config ul{list-style-type:none;} +#config h3 a {text-decoration: none;} +#config h3 img {vertical-align: baseline; padding-right: 5px;} +#config ul{list-style-type:none;padding-left: 20px;} #config h3{font-size:1.5em;color:#000;} #config h4{font-size:1em;color:#333;text-transform:uppercase;margin:0 0 .2em;} #config h5{font-size:1em;color:#000;margin:0 0 .2em;} diff --git a/data/css/default.css b/data/css/default.css index 22034baff171604b6d6448a1cc7418ae88ebdd38..3435ae355a20a9211f623f4876ac3bf507b38c8c 100644 --- a/data/css/default.css +++ b/data/css/default.css @@ -85,9 +85,13 @@ font-weight:700; } .h2footer { -margin-top:-33px; -margin-right:5px; +margin: -33px 5px 6px 0px; } +.h2footer select { +margin-top: -6px; +margin-bottom: -6px; +} + .h2footer span { padding:2px; } diff --git a/data/css/smooth-grinder/images/ui-bg_glass_55_fbf9ee_1x400.png b/data/css/smooth-grinder/images/ui-bg_glass_55_fbf9ee_1x400.png index ad3d6346e00f246102f72f2e026ed0491988b394..b39a6fb27ffbb1f3712e6cfa09e32d8ac084469b 100644 Binary files a/data/css/smooth-grinder/images/ui-bg_glass_55_fbf9ee_1x400.png and b/data/css/smooth-grinder/images/ui-bg_glass_55_fbf9ee_1x400.png differ diff --git a/data/css/smooth-grinder/images/ui-bg_highlight-soft_75_dcdcdc_1x100.png b/data/css/smooth-grinder/images/ui-bg_highlight-soft_75_dcdcdc_1x100.png index 66acd27c22878fbd9c68d3e8b88ba292d595c118..cb6f952f2dc0fff714668e9c79358437078e6fd3 100644 Binary files a/data/css/smooth-grinder/images/ui-bg_highlight-soft_75_dcdcdc_1x100.png and b/data/css/smooth-grinder/images/ui-bg_highlight-soft_75_dcdcdc_1x100.png differ diff --git a/data/css/smooth-grinder/images/ui-bg_highlight-soft_75_efefef_1x100.png b/data/css/smooth-grinder/images/ui-bg_highlight-soft_75_efefef_1x100.png index 27fa2fa9fb2c8bb3002e94a81cb04a7a039ec69d..077a5024dbd1dd8e8724f41a10bd8314eb57c9b9 100644 Binary files a/data/css/smooth-grinder/images/ui-bg_highlight-soft_75_efefef_1x100.png and b/data/css/smooth-grinder/images/ui-bg_highlight-soft_75_efefef_1x100.png differ diff --git a/data/css/smooth-grinder/images/ui-bg_inset-soft_75_dfdfdf_1x100.png b/data/css/smooth-grinder/images/ui-bg_inset-soft_75_dfdfdf_1x100.png index e6aece058547c64052a118372fded69fa25b1400..c33667aee3d67c6620bdcf7b8a6a7b1c40d4a9ea 100644 Binary files a/data/css/smooth-grinder/images/ui-bg_inset-soft_75_dfdfdf_1x100.png and b/data/css/smooth-grinder/images/ui-bg_inset-soft_75_dfdfdf_1x100.png differ diff --git a/data/css/smooth-grinder/images/ui-icons_8c291d_256x240.png b/data/css/smooth-grinder/images/ui-icons_8c291d_256x240.png index 961c8fea3bd431af0918d53a6ff55f8f8bfd014e..a90f0cec755b4a555e4d505ed45a02893c305dd5 100644 Binary files a/data/css/smooth-grinder/images/ui-icons_8c291d_256x240.png and b/data/css/smooth-grinder/images/ui-icons_8c291d_256x240.png differ diff --git a/data/css/smooth-grinder/jquery-ui-1.8.10.custom.css b/data/css/smooth-grinder/jquery-ui-1.8.11.custom.css similarity index 94% rename from data/css/smooth-grinder/jquery-ui-1.8.10.custom.css rename to data/css/smooth-grinder/jquery-ui-1.8.11.custom.css index b813dbcc0e465c993a8e88ee0f5627b878d1e554..7399261c996ffcfb55b78315f9174ddfa297cc97 100644 --- a/data/css/smooth-grinder/jquery-ui-1.8.10.custom.css +++ b/data/css/smooth-grinder/jquery-ui-1.8.11.custom.css @@ -1,5 +1,5 @@ /* - * jQuery UI CSS Framework 1.8.10 + * jQuery UI CSS Framework 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -42,7 +42,7 @@ /* - * jQuery UI CSS Framework 1.8.10 + * jQuery UI CSS Framework 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -50,7 +50,7 @@ * * http://docs.jquery.com/UI/Theming/API * - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ctl=themeroller&ffDefault=Verdana,Arial,sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=ffffff&bgTextureHeader=01_flat.png&bgImgOpacityHeader=0&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=dcdcdc&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=efefef&bgTextureDefault=03_highlight_soft.png&bgImgOpacityDefault=75&borderColorDefault=aaaaaa&fcDefault=222222&iconColorDefault=8c291d&bgColorHover=dddddd&bgTextureHover=03_highlight_soft.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=222222&iconColorHover=222222&bgColorActive=dfdfdf&bgTextureActive=05_inset_soft.png&bgImgOpacityActive=75&borderColorActive=aaaaaa&fcActive=140f06&iconColorActive=8c291d&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=aaaaaa&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=aaaaaa&fcError=8c291d&iconColorError=cd0a0a&bgColorOverlay=6e4f1c&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=35&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=35&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ctl=themeroller&ctl=themeroller&ffDefault=Verdana,Arial,sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=ffffff&bgTextureHeader=01_flat.png&bgImgOpacityHeader=0&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=dcdcdc&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=efefef&bgTextureDefault=03_highlight_soft.png&bgImgOpacityDefault=75&borderColorDefault=aaaaaa&fcDefault=222222&iconColorDefault=8c291d&bgColorHover=dddddd&bgTextureHover=03_highlight_soft.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=222222&iconColorHover=222222&bgColorActive=dfdfdf&bgTextureActive=05_inset_soft.png&bgImgOpacityActive=75&borderColorActive=aaaaaa&fcActive=140f06&iconColorActive=8c291d&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=aaaaaa&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=aaaaaa&fcError=8c291d&iconColorError=cd0a0a&bgColorOverlay=6e4f1c&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=35&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=35&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px */ @@ -298,7 +298,7 @@ .ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; opacity: .35;filter:Alpha(Opacity=35); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; } */ /* - * jQuery UI Resizable 1.8.10 + * jQuery UI Resizable 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -317,7 +317,7 @@ .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* - * jQuery UI Selectable 1.8.10 + * jQuery UI Selectable 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -327,7 +327,7 @@ */ .ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } /* - * jQuery UI Accordion 1.8.10 + * jQuery UI Accordion 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -346,7 +346,7 @@ .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } .ui-accordion .ui-accordion-content-active { display: block; } /* - * jQuery UI Button 1.8.10 + * jQuery UI Button 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -384,7 +384,7 @@ input.ui-button { padding: .4em 1em; } /* workarounds */ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ /* - * jQuery UI Dialog 1.8.10 + * jQuery UI Dialog 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -405,7 +405,7 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } .ui-draggable .ui-dialog-titlebar { cursor: move; } /* - * jQuery UI Slider 1.8.10 + * jQuery UI Slider 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -429,7 +429,7 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad .ui-slider-vertical .ui-slider-range-min { bottom: 0; } .ui-slider-vertical .ui-slider-range-max { top: 0; } /* - * jQuery UI Tabs 1.8.10 + * jQuery UI Tabs 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -447,7 +447,7 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } .ui-tabs .ui-tabs-hide { display: none !important; } /* - * jQuery UI Datepicker 1.8.10 + * jQuery UI Datepicker 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -514,7 +514,7 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad width: 200px; /*must have*/ height: 200px; /*must have*/ }/* - * jQuery UI Progressbar 1.8.10 + * jQuery UI Progressbar 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/data/images/notifiers/growl.gif b/data/images/notifiers/growl.gif new file mode 100644 index 0000000000000000000000000000000000000000..df0b42bd943d4908e4931cade78369141fb18ccd Binary files /dev/null and b/data/images/notifiers/growl.gif differ diff --git a/data/images/notifiers/libnotify.gif b/data/images/notifiers/libnotify.gif new file mode 100644 index 0000000000000000000000000000000000000000..b6c2e553bde7266530e370b218ced6803d96d7cb Binary files /dev/null and b/data/images/notifiers/libnotify.gif differ diff --git a/data/images/notifiers/nmj.gif b/data/images/notifiers/nmj.gif new file mode 100644 index 0000000000000000000000000000000000000000..1673ab030168b93d0ad6aa57dcf11a0ae12de14c Binary files /dev/null and b/data/images/notifiers/nmj.gif differ diff --git a/data/images/notifiers/notifo.gif b/data/images/notifiers/notifo.gif new file mode 100644 index 0000000000000000000000000000000000000000..ea145d3ccfb1134c69c074d26045178692b7e23b Binary files /dev/null and b/data/images/notifiers/notifo.gif differ diff --git a/data/images/notifiers/plex.gif b/data/images/notifiers/plex.gif new file mode 100644 index 0000000000000000000000000000000000000000..5f3103f07bbad1f16d300a281b1ae48793880950 Binary files /dev/null and b/data/images/notifiers/plex.gif differ diff --git a/data/images/notifiers/prowl.gif b/data/images/notifiers/prowl.gif new file mode 100644 index 0000000000000000000000000000000000000000..082bcf479b2e8f84e7c06a68d3a35c0afc289305 Binary files /dev/null and b/data/images/notifiers/prowl.gif differ diff --git a/data/images/notifiers/twitter.gif b/data/images/notifiers/twitter.gif new file mode 100644 index 0000000000000000000000000000000000000000..973344d0261f98bdadfca13816d1516783642cbb Binary files /dev/null and b/data/images/notifiers/twitter.gif differ diff --git a/data/images/notifiers/xbmc.gif b/data/images/notifiers/xbmc.gif new file mode 100644 index 0000000000000000000000000000000000000000..cd4b597bfd5393283b0d771e179f264f8856b3f4 Binary files /dev/null and b/data/images/notifiers/xbmc.gif differ diff --git a/data/images/providers/tvtorrents.gif b/data/images/providers/tvtorrents.gif new file mode 100644 index 0000000000000000000000000000000000000000..9ce79fe39b05658e570aa485104594d4d895c323 Binary files /dev/null and b/data/images/providers/tvtorrents.gif differ diff --git a/data/interfaces/default/config_general.tmpl b/data/interfaces/default/config_general.tmpl index b29734a4d6318dedd74e8be8af374ae2f05b8a0f..2cbcd3975a4e520779b08ab7cc85ec3e688a1ffe 100644 --- a/data/interfaces/default/config_general.tmpl +++ b/data/interfaces/default/config_general.tmpl @@ -12,51 +12,6 @@ #set global $topmenu="config"# #include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_top.tmpl") -<script type="text/javascript"> -<!-- -nameTestURL = "$sbRoot/config/general/testNaming"; -//--> -</script> -<script type="text/javascript" src="$sbRoot/js/configGeneral.js"></script> -#set $anyQualities, $bestQualities = $Quality.splitQuality($sickbeard.QUALITY_DEFAULT) -<script type="text/javascript" charset="utf-8"> -<!-- -\$(document).ready(function(){ - - if (#if $sickbeard.QUALITY_DEFAULT in $qualityPresets then "1" else "0"#) { - \$('#customQuality').hide(); - } - - \$.fn.setFromPresets = function(preset) { - if (preset == 0) { - \$('#customQuality').show(); - return - } else - \$('#customQuality').hide(); - - \$('#anyQualities option').each(function(i) { - var result = preset & \$(this).val() - if (result > 0) \$(this).attr('selected', 'selected'); - else \$(this).attr('selected', false); - }); - - \$('#bestQualities option').each(function(i) { - var result = preset & (\$(this).val() << 16) - if (result > 0) \$(this).attr('selected', 'selected'); - else \$(this).attr('selected', false); - }); - - return - } - - \$('#qualityPreset').change(function(){ - \$(this).setFromPresets(\$('#qualityPreset :selected').val()); - }); - -}); -//--> -</script> - <div id="config"> <div id="config-content"> <h5>All non-absolute folder locations are relative to " <span class="path">$sickbeard.PROG_DIR</span> "</h5> @@ -105,73 +60,6 @@ nameTestURL = "$sbRoot/config/general/testNaming"; </fieldset> </div><!-- /component-group1 //--> - <div id="core-component-group2" class="component-group clearfix"> - - <div class="component-group-desc"> - <h3>Metadata</h3> - <p>The data associated to the data. These are files associated to a TV show in the form of images and text that, when supported, will enhance the viewing experience.</p> - </div> - - <fieldset class="component-group-list"> - <div class="field-pair"> - <label class="clearfix"> - <span class="component-title jumbo">Metadata Type:</span> - <span class="component-desc"> - #set $m_dict = $metadata.get_metadata_generator_dict() - <select id="metadataType"> - #for ($cur_name, $cur_generator) in $m_dict.items(): - <option value="$GenericMetadata.makeID($cur_name)">$cur_name</option> - #end for - </select> - </span> - </label> - <span>Toggle the metadata options that you wish to be created. <b>Multiple targets may be used.</b></span> - </div> - - <div id="metadataLegend"> - <div style="width: 190px; float: left;">Create:</div> - <div style="width: 260px; float: left;">Results:</div> - </div> - <div class="clearfix"></div> - -#for ($cur_name, $cur_generator) in $m_dict.items(): -#set $cur_metadata_inst = $sickbeard.metadata_provider_dict[$cur_generator.name] -#set $cur_id = $GenericMetadata.makeID($cur_name) -<div class="metadataDiv" id="$cur_id"> - <div class="metadata_options"> - <label for="${cur_id}_show_metadata"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_show_metadata" #if $cur_metadata_inst.show_metadata then "checked=\"checked\"" else ""#/> Show Metadata</label> - <label for="${cur_id}_episode_metadata"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_episode_metadata" #if $cur_metadata_inst.episode_metadata then "checked=\"checked\"" else ""#/> Episode Metadata</label> - <label for="${cur_id}_fanart"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_fanart" #if $cur_metadata_inst.fanart then "checked=\"checked\"" else ""#/> Show Fanart Image</label> - <label for="${cur_id}_poster"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_poster" #if $cur_metadata_inst.poster then "checked=\"checked\"" else ""#/> Show Folder Image</label> - <label for="${cur_id}_episode_thumbnails"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_episode_thumbnails" #if $cur_metadata_inst.episode_thumbnails then "checked=\"checked\"" else ""#/> Episode Thumbnail</label> - <label for="${cur_id}_season_thumbnails"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_season_thumbnails" #if $cur_metadata_inst.season_thumbnails then "checked=\"checked\"" else ""#/> Season Thumbnail</label> - </div> - <div class="metadata_example"> - <label for="${cur_id}_show_metadata"><span id="${cur_id}_eg_show_metadata">$cur_metadata_inst.eg_show_metadata</span></label> - <label for="${cur_id}_episode_metadata"><span id="${cur_id}_eg_episode_metadata">$cur_metadata_inst.eg_episode_metadata</span></label> - <label for="${cur_id}_fanart"><span id="${cur_id}_eg_fanart">$cur_metadata_inst.eg_fanart</span></label> - <label for="${cur_id}_poster"><span id="${cur_id}_eg_poster">$cur_metadata_inst.eg_poster</span></label> - <label for="${cur_id}_episode_thumbnails"><span id="${cur_id}_eg_episode_thumbnails">$cur_metadata_inst.eg_episode_thumbnails</span></label> - <label for="${cur_id}_season_thumbnails"><span id="${cur_id}_eg_season_thumbnails">$cur_metadata_inst.eg_season_thumbnails</span></label> - </div> - <input type="hidden" name="${cur_id}_data" id="${cur_id}_data" value="$cur_metadata_inst.get_config()" /> -</div> -#end for - - <div class="clearfix" style="clear:left;"></div> - - <div class="field-pair clearfix"> - <input type="checkbox" name="use_banner" id="use_banner" #if $sickbeard.USE_BANNER then "checked=checked" else ""#/> - <label class="clearfix" for="use_banner"> - <span class="component-title">Use Banners</span> - <span class="component-desc">Use banners instead of posters for your Show Folder Images</span> - </label> - </div> - - <input type="submit" value="Save Changes" /><br/> - - </fieldset> - </div><!-- /component-group2 //--> <div id="core-component-group3" class="component-group clearfix"> @@ -235,133 +123,6 @@ nameTestURL = "$sbRoot/config/general/testNaming"; </fieldset> </div><!-- /component-group3 //--> - <div id="core-component-group4" class="component-group clearfix"> - - <div class="component-group-desc"> - <h3>Episode Naming</h3> - <p>If post-processing 'Rename episodes' is enabled then use these settings.</p> - </div> - - #set $naming_ep_type_text = ("1x02", "s01e02", "S01E02") - #set $naming_multi_ep_type_text = ("extend", "duplicate", "repeat") - - <fieldset class="component-group-list"> - <div class="field-pair"> - <input type="checkbox" name="naming_show_name" id="naming_show_name" #if $sickbeard.NAMING_SHOW_NAME then "checked=\"checked\"" else ""#/> - <label class="clearfix" for="naming_show_name"> - <span class="component-title">Show Name</span> - <span class="component-desc">Include the TV show's name when renaming the file?</span> - </label> - </div> - - <div class="field-pair"> - <input type="checkbox" name="naming_ep_name" id="naming_ep_name" #if $sickbeard.NAMING_EP_NAME then "checked=\"checked\"" else ""#/> - <label class="clearfix" for="naming_ep_name"> - <span class="component-title">Episode Name</span> - <span class="component-desc">Include the TV show's episode title when renaming the file?</span> - </label> - </div> - - <div class="field-pair"> - <input type="checkbox" name="naming_use_periods" id="naming_use_periods" #if $sickbeard.NAMING_USE_PERIODS then "checked=\"checked\"" else ""#/> - <label class="clearfix" for="naming_use_periods"> - <span class="component-title">Use Periods</span> - <span class="component-desc">Replace the spaces with periods in the filename instead?</span> - </label> - </div> - - <div class="field-pair"> - <input type="checkbox" name="naming_quality" id="naming_quality" #if $sickbeard.NAMING_QUALITY then "checked=\"checked\"" else ""#/> - <label class="clearfix" for="naming_quality"> - <span class="component-title">Quality</span> - <span class="component-desc">Append the show quality to the end of the filename?</span> - </label> - </div> - - <div class="field-pair"> - <input type="checkbox" name="naming_dates" id="naming_dates" #if $sickbeard.NAMING_DATES then "checked=\"checked\"" else ""#/> - <label class="clearfix" for="naming_dates"> - <span class="component-title">Air-By-Date Format</span> - <span class="component-desc">Use the date instead of the season/episode format?</span> - </label> - <label class="nocheck clearfix"> - <span class="component-title"> </span> - <span class="component-desc">Only applies to air-by-date shows. (eg. 2010-02-15 vs S12E23)</span> - </label> - </div> - - <div class="field-pair"> - <label class="nocheck clearfix" for="naming_sep_type"> - <span class="component-title">Separator Style</span> - <span class="component-desc"> - <select name="naming_sep_type" id="naming_sep_type"> - #for ($i, $ex) in enumerate($config.naming_sep_type_text): - <option value="$i" #if $i == int($sickbeard.NAMING_SEP_TYPE) then "selected=\"selected\"" else ""#>$ex</option> - #end for - </select> - </span> - </label> - </div> - - <div class="field-pair"> - <label class="nocheck clearfix" for="naming_ep_type"> - <span class="component-title">Number Style</span> - <span class="component-desc"> - <select name="naming_ep_type" id="naming_ep_type"> - #for ($i, $ex) in enumerate($config.naming_ep_type_text): - <option value="$i" #if $i == int($sickbeard.NAMING_EP_TYPE) then "selected=\"selected\"" else ""#>$ex</option> - #end for - </select> - </span> - </label> - </div> - - <div class="field-pair" style="padding:10px; background: #efefef;"> - <label class="clearfix" for="naming_ep_type"> - <span class="component-title jumbo">Single-Ep Example:</span> - <span class="component-desc jumbo" id="normalExampleText"></span> - </label> - </div> - - <div class="field-pair"> - <label class="nocheck clearfix" for="naming_multi_ep_type"> - <span class="component-title">Multi-episode Style</span> - <span class="component-desc"> - <select name="naming_multi_ep_type" id="naming_multi_ep_type"> - #for ($i, $ex) in enumerate($config.naming_multi_ep_type_text): - <option value="$i" #if $i == int($sickbeard.NAMING_MULTI_EP_TYPE) then "selected=\"selected\"" else ""#>$ex</option> - #end for - </select> - </span> - </label> - </div> - - <div class="field-pair" style="padding:10px; background: #efefef;"> - <label class="clearfix" for="naming_multi_ep_type"> - <span class="component-title jumbo">Multi-Ep Example:</span> - <span class="component-desc jumbo" id="multiExampleText"></span> - </label> - </div> - - <div class="field-pair"> - <label class="nocheck clearfix"> - <span class="component-title">Season Folder Format</span> - <input type="text" id="season_folders_format" name="season_folders_format" value="$sickbeard.SEASON_FOLDERS_FORMAT" size="15" /> - </label> - <label class="nocheck clearfix"> - <span class="component-title"> </span> - <span class="component-desc">Format to use when creating season folders.</span> - </label> - <label class="nocheck clearfix"> - <span class="component-title"> </span> - <span class="component-desc">(eg. 'Season %0d' or 'season%02d')</span> - </label> - </div> - - <input type="submit" value="Save Changes" /> - </fieldset> - </div><!-- /component-group4 //--> - <br/><input type="submit" value="Save Changes" /><br/> </div><!-- /config-components --> diff --git a/data/interfaces/default/config_notifications.tmpl b/data/interfaces/default/config_notifications.tmpl index b2133838293abf7de2e8086f1c516891260d2d8b..d8a8596606b42b4e51140305456ed02f5ab18ce2 100644 --- a/data/interfaces/default/config_notifications.tmpl +++ b/data/interfaces/default/config_notifications.tmpl @@ -8,6 +8,7 @@ #include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_top.tmpl") <script type="text/javascript" src="$sbRoot/js/configNotifications.js"></script> +<script type="text/javascript" src="$sbRoot/js/config.js"></script> <div id="config"> <div id="config-content"> @@ -19,7 +20,7 @@ <div id="core-component-group1" class="component-group clearfix"> <div class="component-group-desc"> - <h3>XBMC</h3> + <h3><a href="http://xbmc.org/" onclick="window.open(this.href, '_blank'); return false;"><img src="$sbRoot/images/notifiers/xbmc.gif" alt="XBMC" title="XBMC" width="16" height="16" /> XBMC</a></h3> <p>A free and open source cross-platform media center and home entertainment system software with a 10-foot user interface designed for the living-room TV.</p> </div> @@ -114,7 +115,101 @@ <div id="core-component-group2" class="component-group clearfix"> <div class="component-group-desc"> - <h3>Growl</h3> + <h3><a href="http://www.plexapp.com/" onclick="window.open(this.href, '_blank'); return false;"><img src="$sbRoot/images/notifiers/plex.gif" alt="Plex Media Server" title="Plex Media Server" width="16" height="16" /> Plex Media Server</a></h3> + <p>Experience your media on a visually stunning, easy to use interface on your Mac connected to your TV. Your media library has never looked this good!</p> + </div> + + <fieldset class="component-group-list"> + <div class="field-pair"> + <input type="checkbox" class="enabler" name="use_plex" id="use_plex" #if $sickbeard.USE_PLEX then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="use_plex"> + <span class="component-title">Enable</span> + <span class="component-desc">Should Sick Beard send Plex commands?</span> + </label> + </div> + + <div id="content_use_plex"> + <div class="field-pair"> + <input type="checkbox" name="plex_notify_onsnatch" id="plex_notify_onsnatch" #if $sickbeard.PLEX_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="plex_notify_onsnatch"> + <span class="component-title">Notify on Snatch</span> + <span class="component-desc">Send notification when we start a download?</span> + </label> + </div> + + <div class="field-pair"> + <input type="checkbox" name="plex_notify_ondownload" id="plex_notify_ondownload" #if $sickbeard.PLEX_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="plex_notify_ondownload"> + <span class="component-title">Notify on Download</span> + <span class="component-desc">Send notification when we finish a download?</span> + </label> + </div> + + <div class="field-pair"> + <input type="checkbox" name="plex_update_library" id="plex_update_library" #if $sickbeard.PLEX_UPDATE_LIBRARY then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="plex_update_library"> + <span class="component-title">Update Library</span> + <span class="component-desc">Update Plex Media Server library when we finish a download?</span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">Plex Media Server IP:Port</span> + <input type="text" name="plex_server_host" id="plex_server_host" value="$sickbeard.PLEX_SERVER_HOST" size="35" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Host running Plex Media Server (eg. 192.168.1.100:32400)</span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">Plex Client IP:Port</span> + <input type="text" name="plex_host" id="plex_host" value="$sickbeard.PLEX_HOST" size="35" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Host running Plex Client (eg. 192.168.1.100:3000)</span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">Plex Client Username</span> + <input type="text" name="plex_username" id="plex_username" value="$sickbeard.PLEX_USERNAME" size="35" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Username of your Plex client API (blank for none)</span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">Plex Client Password</span> + <input type="password" name="plex_password" id="plex_password" value="$sickbeard.PLEX_PASSWORD" size="35" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Password of your Plex client API (blank for none)</span> + </label> + </div> + + <div class="testNotification" id="testPLEX-result">Click below to test.</div> + <input type="button" value="Test Plex Media Server" id="testPLEX" /> + <input type="submit" value="Save Changes" /> + + </div><!-- /enabler_plex --> + + </fieldset> + </div><!-- /component-group --> + + <div id="core-component-group3" class="component-group clearfix"> + + <div class="component-group-desc"> + <h3><a href="http://growl.info/" onclick="window.open(this.href, '_blank'); return false;"><img src="$sbRoot/images/notifiers/growl.gif" alt="Growl" title="Growl" width="16" height="16" /> Growl</a></h3> <p>A cross-platform unobtrusive global notification system.</p> </div> @@ -179,10 +274,10 @@ </fieldset> </div><!-- /component-group //--> - <div id="core-component-group3" class="component-group clearfix"> + <div id="core-component-group4" class="component-group clearfix"> <div class="component-group-desc"> - <h3>Twitter</h3> + <h3><a href="http://www.twitter.com/" onclick="window.open(this.href, '_blank'); return false;"><img src="$sbRoot/images/notifiers/twitter.gif" alt="Twitter" title="Twitter" width="16" height="16" /> Twitter</a></h3> <p>A social networking and microblogging service, enabling its users to send and read other users' messages called tweets.</p> </div> @@ -254,10 +349,10 @@ </fieldset> </div><!-- /component-group //--> - <div id="core-component-group4" class="component-group clearfix"> + <div id="core-component-group5" class="component-group clearfix"> <div class="component-group-desc"> - <h3>Prowl</h3> + <h3><a href="http://www.prowlapp.com/" onclick="window.open(this.href, '_blank'); return false;"><img src="$sbRoot/images/notifiers/prowl.gif" alt="Prowl" title="Prowl" width="16" height="16" /> Prowl</a></h3> <p>A Growl client for iOS.</p> </div> @@ -324,10 +419,10 @@ </fieldset> </div><!-- /component-group //--> - <div id="core-component-group5" class="component-group clearfix"> + <div id="core-component-group6" class="component-group clearfix"> <div class="component-group-desc"> - <h3>Notifo</h3> + <h3><a href="http://notifo.com/" onclick="window.open(this.href, '_blank'); return false;"><img src="$sbRoot/images/notifiers/notifo.gif" alt="Notifo" title="Notifo" width="16" height="16" /> Notifo</a></h3> <p>A platform for push-notifications to either mobile or desktop clients</p> </div> @@ -390,9 +485,9 @@ </div> - <div id="core-component-group6" class="component-group clearfix"> + <div id="core-component-group7" class="component-group clearfix"> <div class="component-group-desc"> - <h3>Libnotify</h3> + <h3><a href="http://library.gnome.org/devel/libnotify/" onclick="window.open(this.href, '_blank'); return false;"><img src="$sbRoot/images/notifiers/libnotify.gif" alt="Libnotify" title="Libnotify" width="16" height="16" /> Libnotify</a></h3> <p>The standard desktop notification API for Linux/*nix systems. This notifier will only function if the pynotify module is installed (Ubuntu/Debian package <a href="apt:python-notify">python-notify</a>).</p> </div> @@ -401,7 +496,7 @@ <input type="checkbox" class="enabler" name="use_libnotify" id="use_libnotify" #if $sickbeard.USE_LIBNOTIFY then "checked=\"checked\"" else ""# /> <label class="clearfix" for="use_libnotify"> <span class="component-title">Enable</span> - <span class="component-desc">Should Sick Beard send libnotify notifications?</span> + <span class="component-desc">Should Sick Beard send Libnotify notifications?</span> </label> </div> @@ -431,6 +526,74 @@ </fieldset> </div><!-- /component-group //--> + <div id="core-component-group8" class="component-group clearfix"> + + <div class="component-group-desc"> + <h3><a href="http://www.popcornhour.com/" onclick="window.open(this.href, '_blank'); return false;"><img src="$sbRoot/images/notifiers/nmj.gif" alt="Networked Media Jukebox" title="Networked Media Jukebox" width="16" height="16" /> NMJ</a></h3> + <p>The Networked Media Jukebox, or NMJ, is the official media jukebox interface made available for the Popcorn Hour 200-series.</p> + </div> + + <fieldset class="component-group-list"> + <div class="field-pair"> + <input type="checkbox" class="enabler" name="use_nmj" id="use_nmj" #if $sickbeard.USE_NMJ then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="use_nmj"> + <span class="component-title">Enable</span> + <span class="component-desc">Should Sick Beard send update commands to NMJ?</span> + </label> + </div> + + <div id="content_use_nmj"> + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">Popcorn IP address</span> + <input type="text" name="nmj_host" id="nmj_host" value="$sickbeard.NMJ_HOST" size="35" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">IP of Popcorn 200-series (eg. 192.168.1.100)</span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">Get Settings</span> + <input type="button" value="Get Settings" id="settingsNMJ" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">The Popcorn Hour device must be powered on and NMJ running.</span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">NMJ Database</span> + <input type="text" name="nmj_database" id="nmj_database" value="$sickbeard.NMJ_DATABASE" size="35" #if $sickbeard.NMJ_DATABASE then "readonly=\"readonly\"" else ""# /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Automatically filled via the 'Get Settings' button.</span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">NMJ Mount URL</span> + <input type="text" name="nmj_mount" id="nmj_mount" value="$sickbeard.NMJ_MOUNT" size="35" #if $sickbeard.NMJ_MOUNT then "readonly=\"readonly\"" else ""# /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Automatically filled via the 'Get Settings' button.</span> + </label> + </div> + + <div class="testNotification" id="testNMJ-result">Click below to test.</div> + <input type="button" value="Test NMJ" id="testNMJ" /> + <input type="submit" value="Save Changes" /> + </div><!-- /content_use_nmj //--> + </fieldset> + </div><!-- /component-group //--> + <br/><input type="submit" value="Save Changes" /><br/> </div><!-- /config-components //--> diff --git a/data/interfaces/default/config_postProcessing.tmpl b/data/interfaces/default/config_postProcessing.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..eeb2434ab3ee76570f9021755d3e4c93275f6b4b --- /dev/null +++ b/data/interfaces/default/config_postProcessing.tmpl @@ -0,0 +1,302 @@ +#import os.path +#import sickbeard +#from sickbeard.common import * +#from sickbeard import config +#from sickbeard import metadata +#from sickbeard.metadata.generic import GenericMetadata +#set global $title = "Config - Post Processing" +#set global $header = "Post Processing Configuration" + +#set global $sbPath="../.." + +#set global $topmenu="config"# +#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_top.tmpl") + +<script type="text/javascript" src="$sbRoot/js/configPostProcessing.js"></script> + +<div id="config"> +<div id="config-content"> +<h5>All non-absolute folder locations are relative to " <span class="path">$sickbeard.PROG_DIR</span> "</h5> + +<form action="savePostProcessing" method="post"> + + <div id="config-components"> + + <div id="core-component-group3" class="component-group clearfix"> + + <div class="component-group-desc"> + <h3>Post-Processing</h3> + <p>Settings that dictate how Sick Beard should process completed downloads.</p> + </div> + + <fieldset class="component-group-list"> + <div class="field-pair"> + <label class="nocheck clearfix" for="tv_download_dir"> + <span class="component-title">TV Download Dir</span> + <input type="text" name="tv_download_dir" id="tv_download_dir" value="$sickbeard.TV_DOWNLOAD_DIR" size="45" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">The folder where your download client puts your TV downloads.</span> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc"><b>NOTE:</b> Use only if not using SABnzbd+ post processing.</span> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Or if SABnzbd+ and Sick Beard are on different PCs.</span> + </label> + </div> + + + <div class="field-pair"> + <input type="checkbox" name="keep_processed_dir" id="keep_processed_dir" #if $sickbeard.KEEP_PROCESSED_DIR == True then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="keep_processed_dir"> + <span class="component-title">Keep Original Files</span> + <span class="component-desc">Keep original files after they've been processed?</span> + </label> + </div> + + <div class="field-pair"> + <input type="checkbox" name="move_associated_files" id="move_associated_files" #if $sickbeard.MOVE_ASSOCIATED_FILES == True then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="move_associated_files"> + <span class="component-title">Move Associated Files</span> + <span class="component-desc">Move srr/srt/sfv/etc files with the episode when processed?</span> + </label> + <label class="nocheck clearfix" for="move_associated_files"> + <span class="component-title"> </span> + <span class="component-desc"><b>NOTE:</b> <i>.nfo</i> will be renamed to <i>.nfo-orig</i> when moved.</span> + </label> + </div> + + <div class="field-pair"> + <input type="checkbox" name="rename_episodes" id="rename_episodes" #if $sickbeard.RENAME_EPISODES == True then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="rename_episodes"> + <span class="component-title">Rename Episodes</span> + <span class="component-desc">Rename episode using the naming settings below?</span> + </label> + </div> + + <div class="field-pair"> + <input type="checkbox" name="process_automatically" id="process_automatically" #if $sickbeard.PROCESS_AUTOMATICALLY == True then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="process_automatically"> + <span class="component-title">Scan and Process</span> + <span class="component-desc">Scan and post-process any files in your <i>TV Download Dir</i>?</span> + </label> + <label class="nocheck clearfix" for="process_automatically"> + <span class="component-title"> </span> + <span class="component-desc"><b>NOTE:</b> Do not use if you use sabToSickbeard w/ SABnzbd+!</span> + </label> + </div> + + <div class="clearfix"></div> + <input type="submit" value="Save Changes" /><br/> + + </fieldset> + </div><!-- /component-group3 //--> + + <div id="core-component-group2" class="component-group clearfix"> + + <div class="component-group-desc"> + <h3>Metadata</h3> + <p>The data associated to the data. These are files associated to a TV show in the form of images and text that, when supported, will enhance the viewing experience.</p> + </div> + + <fieldset class="component-group-list"> + <div class="field-pair"> + <label class="clearfix"> + <span class="component-title jumbo">Metadata Type:</span> + <span class="component-desc"> + #set $m_dict = $metadata.get_metadata_generator_dict() + <select id="metadataType"> + #for ($cur_name, $cur_generator) in $m_dict.items(): + <option value="$GenericMetadata.makeID($cur_name)">$cur_name</option> + #end for + </select> + </span> + </label> + <span>Toggle the metadata options that you wish to be created. <b>Multiple targets may be used.</b></span> + </div> + + <div id="metadataLegend"> + <div style="width: 190px; float: left;">Create:</div> + <div style="width: 260px; float: left;">Results:</div> + </div> + <div class="clearfix"></div> + +#for ($cur_name, $cur_generator) in $m_dict.items(): +#set $cur_metadata_inst = $sickbeard.metadata_provider_dict[$cur_generator.name] +#set $cur_id = $GenericMetadata.makeID($cur_name) +<div class="metadataDiv" id="$cur_id"> + <div class="metadata_options"> + <label for="${cur_id}_show_metadata"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_show_metadata" #if $cur_metadata_inst.show_metadata then "checked=\"checked\"" else ""#/> Show Metadata</label> + <label for="${cur_id}_episode_metadata"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_episode_metadata" #if $cur_metadata_inst.episode_metadata then "checked=\"checked\"" else ""#/> Episode Metadata</label> + <label for="${cur_id}_fanart"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_fanart" #if $cur_metadata_inst.fanart then "checked=\"checked\"" else ""#/> Show Fanart Image</label> + <label for="${cur_id}_poster"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_poster" #if $cur_metadata_inst.poster then "checked=\"checked\"" else ""#/> Show Folder Image</label> + <label for="${cur_id}_episode_thumbnails"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_episode_thumbnails" #if $cur_metadata_inst.episode_thumbnails then "checked=\"checked\"" else ""#/> Episode Thumbnail</label> + <label for="${cur_id}_season_thumbnails"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_season_thumbnails" #if $cur_metadata_inst.season_thumbnails then "checked=\"checked\"" else ""#/> Season Thumbnail</label> + </div> + <div class="metadata_example"> + <label for="${cur_id}_show_metadata"><span id="${cur_id}_eg_show_metadata">$cur_metadata_inst.eg_show_metadata</span></label> + <label for="${cur_id}_episode_metadata"><span id="${cur_id}_eg_episode_metadata">$cur_metadata_inst.eg_episode_metadata</span></label> + <label for="${cur_id}_fanart"><span id="${cur_id}_eg_fanart">$cur_metadata_inst.eg_fanart</span></label> + <label for="${cur_id}_poster"><span id="${cur_id}_eg_poster">$cur_metadata_inst.eg_poster</span></label> + <label for="${cur_id}_episode_thumbnails"><span id="${cur_id}_eg_episode_thumbnails">$cur_metadata_inst.eg_episode_thumbnails</span></label> + <label for="${cur_id}_season_thumbnails"><span id="${cur_id}_eg_season_thumbnails">$cur_metadata_inst.eg_season_thumbnails</span></label> + </div> + <input type="hidden" name="${cur_id}_data" id="${cur_id}_data" value="$cur_metadata_inst.get_config()" /> +</div> +#end for + + <div class="clearfix" style="clear:left;"></div> + + <div class="field-pair clearfix"> + <input type="checkbox" name="use_banner" id="use_banner" #if $sickbeard.USE_BANNER then "checked=checked" else ""#/> + <label class="clearfix" for="use_banner"> + <span class="component-title">Use Banners</span> + <span class="component-desc">Use banners instead of posters for your Show Folder Images</span> + </label> + </div> + + <input type="submit" value="Save Changes" /><br/> + + </fieldset> + </div><!-- /component-group2 //--> + + <div id="core-component-group4" class="component-group clearfix"> + + <div class="component-group-desc"> + <h3>Episode Naming</h3> + <p>If post-processing 'Rename episodes' is enabled then use these settings.</p> + </div> + + #set $naming_ep_type_text = ("1x02", "s01e02", "S01E02") + #set $naming_multi_ep_type_text = ("extend", "duplicate", "repeat") + + <fieldset class="component-group-list"> + <div class="field-pair"> + <input type="checkbox" name="naming_show_name" id="naming_show_name" #if $sickbeard.NAMING_SHOW_NAME then "checked=\"checked\"" else ""#/> + <label class="clearfix" for="naming_show_name"> + <span class="component-title">Show Name</span> + <span class="component-desc">Include the TV show's name when renaming the file?</span> + </label> + </div> + + <div class="field-pair"> + <input type="checkbox" name="naming_ep_name" id="naming_ep_name" #if $sickbeard.NAMING_EP_NAME then "checked=\"checked\"" else ""#/> + <label class="clearfix" for="naming_ep_name"> + <span class="component-title">Episode Name</span> + <span class="component-desc">Include the TV show's episode title when renaming the file?</span> + </label> + </div> + + <div class="field-pair"> + <input type="checkbox" name="naming_use_periods" id="naming_use_periods" #if $sickbeard.NAMING_USE_PERIODS then "checked=\"checked\"" else ""#/> + <label class="clearfix" for="naming_use_periods"> + <span class="component-title">Use Periods</span> + <span class="component-desc">Replace the spaces with periods in the filename instead?</span> + </label> + </div> + + <div class="field-pair"> + <input type="checkbox" name="naming_quality" id="naming_quality" #if $sickbeard.NAMING_QUALITY then "checked=\"checked\"" else ""#/> + <label class="clearfix" for="naming_quality"> + <span class="component-title">Quality</span> + <span class="component-desc">Append the show quality to the end of the filename?</span> + </label> + </div> + + <div class="field-pair"> + <input type="checkbox" name="naming_dates" id="naming_dates" #if $sickbeard.NAMING_DATES then "checked=\"checked\"" else ""#/> + <label class="clearfix" for="naming_dates"> + <span class="component-title">Air-By-Date Format</span> + <span class="component-desc">Use the date instead of the season/episode format?</span> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Only applies to air-by-date shows. (eg. 2010-02-15 vs S12E23)</span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix" for="naming_sep_type"> + <span class="component-title">Separator Style</span> + <span class="component-desc"> + <select name="naming_sep_type" id="naming_sep_type"> + #for ($i, $ex) in enumerate($config.naming_sep_type_text): + <option value="$i" #if $i == int($sickbeard.NAMING_SEP_TYPE) then "selected=\"selected\"" else ""#>$ex</option> + #end for + </select> + </span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix" for="naming_ep_type"> + <span class="component-title">Number Style</span> + <span class="component-desc"> + <select name="naming_ep_type" id="naming_ep_type"> + #for ($i, $ex) in enumerate($config.naming_ep_type_text): + <option value="$i" #if $i == int($sickbeard.NAMING_EP_TYPE) then "selected=\"selected\"" else ""#>$ex</option> + #end for + </select> + </span> + </label> + </div> + + <div class="field-pair" style="padding:10px; background: #efefef;"> + <label class="clearfix" for="naming_ep_type"> + <span class="component-title jumbo">Single-Ep Example:</span> + <span class="component-desc jumbo" id="normalExampleText"></span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix" for="naming_multi_ep_type"> + <span class="component-title">Multi-episode Style</span> + <span class="component-desc"> + <select name="naming_multi_ep_type" id="naming_multi_ep_type"> + #for ($i, $ex) in enumerate($config.naming_multi_ep_type_text): + <option value="$i" #if $i == int($sickbeard.NAMING_MULTI_EP_TYPE) then "selected=\"selected\"" else ""#>$ex</option> + #end for + </select> + </span> + </label> + </div> + + <div class="field-pair" style="padding:10px; background: #efefef;"> + <label class="clearfix" for="naming_multi_ep_type"> + <span class="component-title jumbo">Multi-Ep Example:</span> + <span class="component-desc jumbo" id="multiExampleText"></span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">Season Folder Format</span> + <input type="text" id="season_folders_format" name="season_folders_format" value="$sickbeard.SEASON_FOLDERS_FORMAT" size="15" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Format to use when creating season folders.</span> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">(eg. 'Season %0d' or 'season%02d')</span> + </label> + </div> + + <input type="submit" value="Save Changes" /> + </fieldset> + </div><!-- /component-group4 //--> + + <br/><input type="submit" value="Save Changes" /><br/> + </div><!-- /config-components --> + +</form> +</div></div> +<div class="clearfix"></div> + + +#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_bottom.tmpl") diff --git a/data/interfaces/default/config_providers.tmpl b/data/interfaces/default/config_providers.tmpl index d5acb09404e7f493066c566b968ed6858348b74a..ff951fe05ab3e7091c264b67a973cb73d0b6c1ee 100644 --- a/data/interfaces/default/config_providers.tmpl +++ b/data/interfaces/default/config_providers.tmpl @@ -1,4 +1,5 @@ #import sickbeard +#from sickbeard.providers.generic import GenericProvider #set global $title="Config - Providers" #set global $header="Search Providers" @@ -11,11 +12,11 @@ <script type="text/javascript" src="$sbRoot/js/configProviders.js"></script> <script type="text/javascript" charset="utf-8"> <!-- -sbRoot = '$sbRoot'; \$(document).ready(function(){ +var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#; #for $curNewznabProvider in $sickbeard.newznabProviderList: - \$(this).addProvider('$curNewznabProvider.getID()', '$curNewznabProvider.name', '$curNewznabProvider.url', '$curNewznabProvider.key', $int($curNewznabProvider.default)); +\$(this).addProvider('$curNewznabProvider.getID()', '$curNewznabProvider.name', '$curNewznabProvider.url', '$curNewznabProvider.key', $int($curNewznabProvider.default), show_nzb_providers); #end for }); //--> @@ -34,7 +35,7 @@ sbRoot = '$sbRoot'; <h3>Provider Priorities</h3> <p>Check off and drag the providers into the order you want them to be used.</p> <p class="note">At least one provider is required but two are recommended.</p> - <p>More providers offer redundancy but at the expense of a longer search period.</p> + <p>NZB/Torrent providers can be toggled in <a href="$sbRoot/config/search">Search Settings</a></p> <p class="note"><span style="color: #654B24; font-size: 16px;">*</span> Provider does not support backlog searches at this time.</p> <p class="note"><span style="color: #654B24; font-size: 16px;">**</span> Provider supports <b>limited</b> backlog searches, all episodes/qualities may not be available.</p> </div> @@ -45,15 +46,15 @@ sbRoot = '$sbRoot'; #if $curProvider.getID() == 'tvbinz' and not $sickbeard.SHOW_TVBINZ #continue #end if - #set $curName = $curProvider.getID() - #if $curProvider in $sickbeard.newznabProviderList: - #set $imgName = 'newznab.gif' - #else: - #set $imgName = $curName + '.gif' + #if $curProvider.providerType == $GenericProvider.NZB and not $sickbeard.USE_NZBS: + #continue + #elif $curProvider.providerType == $GenericProvider.TORRENT and not $sickbeard.USE_TORRENTS: + #continue #end if + #set $curName = $curProvider.getID() <li class="ui-state-default" id="$curName"> - <input type="checkbox" id="enable_$curName" class="enabler" #if $curProvider.isEnabled() then "checked=\"checked\"" else ""#/> - <a href="$curProvider.url" class="imgLink" target="_new"><img src="$sbRoot/images/providers/$imgName" alt="$curProvider.name" title="$curProvider.name" width="16" height="16" /></a> + <input type="checkbox" id="enable_$curName" class="provider_enabler" #if $curProvider.isEnabled() then "checked=\"checked\"" else ""#/> + <a href="$curProvider.url" class="imgLink" target="_new"><img src="$sbRoot/images/providers/$curProvider.imageName()" alt="$curProvider.name" title="$curProvider.name" width="16" height="16" /></a> $curProvider.name #if not $curProvider.supportsBacklog then "*" else ""# #if $curProvider.name == "EZRSS" then "**" else ""# @@ -75,15 +76,31 @@ sbRoot = '$sbRoot'; <fieldset class="component-group-list"> <div class="field-pair"> <label class="clearfix" for="editAProvider"> - <span class="component-title jumbo">Select Provider:</span> + <span class="component-title jumbo">Configure Provider:</span> <span class="component-desc"> + #set $provider_config_list = [] + #for $cur_provider in ("nzbs_org", "nzbs_r_us", "newzbin", "nzbmatrix", "tvbinz", "tvtorrents"): + #set $cur_provider_obj = $sickbeard.providers.getProviderClass($cur_provider) + #if $cur_provider_obj.getID() == 'tvbinz' and not $sickbeard.SHOW_TVBINZ + #continue + #end if + #if $cur_provider_obj.providerType == $GenericProvider.NZB and not $sickbeard.USE_NZBS: + #continue + #elif $cur_provider_obj.providerType == $GenericProvider.TORRENT and not $sickbeard.USE_TORRENTS: + #continue + #end if + $provider_config_list.append($cur_provider_obj) + #end for + + #if $provider_config_list: <select id="editAProvider"> - #if $sickbeard.SHOW_TVBINZ then "<option value=\"tvbinz\">TVBinz" else ""# - <option value="nzbs_org">NZBs.org</option> - <option value="nzbs_r_us">NZBs'R'US</option> - <option value="newzbin">Newzbin</option> - <option value="nzbmatrix">NZBMatrix</option> + #for $cur_provider in $provider_config_list: + <option value="$cur_provider.getID()">$cur_provider.name</option> + #end for </select> + #else: + No providers available to configure. + #end if </span> </label> </div> @@ -172,7 +189,23 @@ sbRoot = '$sbRoot'; </label> </div> </div> -<!-- end div for editing providers //--> + +<div class="providerDiv" id="tvtorrentsDiv"> + <div class="field-pair"> + <label class="clearfix"> + <span class="component-title">TvTorrents Digest:</span> + <input class="component-desc" type="text" name="tvtorrents_digest" value="$sickbeard.TVTORRENTS_DIGEST" size="40" /> + </label> + </div> + <div class="field-pair"> + <label class="clearfix"> + <span class="component-title">TvTorrents Hash:</span> + <input class="component-desc" type="text" name="tvtorrents_hash" value="$sickbeard.TVTORRENTS_HASH" size="40" /> + </label> + </div> +</div> + +<!-- end div for editing providers --> <input type="submit" value="Save Changes" /><br/> diff --git a/data/interfaces/default/config_episodedownloads.tmpl b/data/interfaces/default/config_search.tmpl similarity index 68% rename from data/interfaces/default/config_episodedownloads.tmpl rename to data/interfaces/default/config_search.tmpl index eb54bcd2ec6a94867aa8dec70ed0164487482638..1d7b64f42d4df46bc0189d96dbfa039837b7cb8a 100644 --- a/data/interfaces/default/config_episodedownloads.tmpl +++ b/data/interfaces/default/config_search.tmpl @@ -1,6 +1,6 @@ #import sickbeard #set global $title="Config - Episode Search" -#set global $header="Episode Download Options" +#set global $header="Search Options" #set global $sbPath="../.." @@ -8,36 +8,13 @@ #import os.path #include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_top.tmpl") -<script type="text/javascript" charset="utf-8"> -<!-- -\$(document).ready(function(){ - - \$.fn.nzb_method_handler = function() { - - var selectedProvider = \$('#nzb_method :selected').val(); - - if (selectedProvider == "blackhole") { - \$('#blackhole_settings').show(); - \$('#sabnzbd_settings').hide(); - } else { - \$('#blackhole_settings').hide(); - \$('#sabnzbd_settings').show(); - } - - } - - \$('#nzb_method').change(\$(this).nzb_method_handler); - - \$(this).nzb_method_handler(); - -}); -//--> -</script> +<script type="text/javascript" src="$sbRoot/js/configSearch.js"></script> +<script type="text/javascript" src="$sbRoot/js/config.js"></script> <div id="config"> <div id="config-content"> <h5>All non-absolute folder locations are relative to " <span class="path">$sickbeard.PROG_DIR</span> "</h5> -<form action="saveEpisodeDownloads" method="post"> +<form action="saveSearch" method="post"> <div id="config-components"> @@ -88,18 +65,28 @@ <div id="core-component-group2" class="component-group clearfix"> <div class="component-group-desc"> - <h3>Episode Results</h3> - <p>Settings that dictate how Sick Beard handles NZB and Torrents search results.</p> + <h3>NZB Search</h3> + <p>Settings that dictate how Sick Beard handles NZB search results.</p> </div> <fieldset class="component-group-list"> + + <div class="field-pair"> + <input type="checkbox" name="use_nzbs" class="enabler" id="use_nzbs" #if $sickbeard.USE_NZBS then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="use_nzbs"> + <span class="component-title">Search NZBs</span> + <span class="component-desc">Should Sick Beard search for NZB files?</span> + </label> + </div> + + <div id="content_use_nzbs"> <div class="field-pair"> <label class="clearfix" for="nzb_method"> <span class="component-title jumbo">NZB Method:</span> <span class="component-desc"> <select name="nzb_method" id="nzb_method"> - #set $nzb_method_text = {'blackhole': "Black hole", 'sabnzbd': "SABnzbd"} - #for $curAction in ('sabnzbd', 'blackhole'): + #set $nzb_method_text = {'blackhole': "Black hole", 'sabnzbd': "SABnzbd", 'nzbget': "NZBget"} + #for $curAction in ('sabnzbd', 'blackhole', 'nzbget'): #if $sickbeard.NZB_METHOD == $curAction: #set $nzb_method = "selected=\"selected\"" #else @@ -112,17 +99,6 @@ </label> </div> - <div class="field-pair"> - <label class="nocheck clearfix"> - <span class="component-title">Torrent Black Hole</span> - <input type="text" name="torrent_dir" id="torrent_dir" value="$sickbeard.TORRENT_DIR" size="45" /> - </label> - <label class="nocheck clearfix"> - <span class="component-title"> </span> - <span class="component-desc">The directory where Sick Beard should store your <i>Torrent</i> files.</span> - </label> - </div> - <div id="blackhole_settings"> <div class="field-pair"> <label class="nocheck clearfix"> @@ -196,84 +172,87 @@ </label> </div> </div> + + <div id="nzbget_settings"> + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">NZBget HOST:PORT</span> + <input type="text" name="nzbget_host" value="$sickbeard.NZBGET_HOST" size="45" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Hostname and portnumber of the NZBget RPC (not NZBgetweb!)</span> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">(eg. localhost:6789)</span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">NZBget Password</span> + <input type="password" name="nzbget_password" value="$sickbeard.NZBGET_PASSWORD" size="20" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Password found in nzbget.conf (by default tegbzn6789)</span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">NZBget Category</span> + <input type="text" name="nzbget_category" value="$sickbeard.NZBGET_CATEGORY" size="20" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Category for downloads to go into (eg. TV)</span> + </label> + </div> + </div> <div class="clearfix"></div> <input type="submit" value="Save Changes" /><br/> + </div><!-- /content_use_nzbs //--> + </fieldset> </div><!-- /component-group2 //--> <div id="core-component-group3" class="component-group clearfix"> <div class="component-group-desc"> - <h3>Post-Processing</h3> - <p>Settings that dictate how Sick Beard should process completed downloads.</p> + <h3>Torrent Search</h3> + <p>Settings that dictate how Sick Beard handles Torrent search results.</p> </div> <fieldset class="component-group-list"> - <div class="field-pair"> - <label class="nocheck clearfix" for="tv_download_dir"> - <span class="component-title">TV Download Dir</span> - <input type="text" name="tv_download_dir" id="tv_download_dir" value="$sickbeard.TV_DOWNLOAD_DIR" size="45" /> - </label> - <label class="nocheck clearfix"> - <span class="component-title"> </span> - <span class="component-desc">The folder where your download client puts your TV downloads.</span> - </label> - <label class="nocheck clearfix"> - <span class="component-title"> </span> - <span class="component-desc"><b>NOTE:</b> Use only if not using SABnzbd+ post processing.</span> - </label> - <label class="nocheck clearfix"> - <span class="component-title"> </span> - <span class="component-desc">Or if SABnzbd+ and Sick Beard are on different PCs.</span> - </label> - </div> - - - <div class="field-pair"> - <input type="checkbox" name="keep_processed_dir" id="keep_processed_dir" #if $sickbeard.KEEP_PROCESSED_DIR == True then "checked=\"checked\"" else ""# /> - <label class="clearfix" for="keep_processed_dir"> - <span class="component-title">Keep Original Files</span> - <span class="component-desc">Keep original files after they've been processed?</span> - </label> - </div> - - <div class="field-pair"> - <input type="checkbox" name="move_associated_files" id="move_associated_files" #if $sickbeard.MOVE_ASSOCIATED_FILES == True then "checked=\"checked\"" else ""# /> - <label class="clearfix" for="move_associated_files"> - <span class="component-title">Move Associated Files</span> - <span class="component-desc">Move srr/srt/sfv/etc files with the episode when processed?</span> - </label> - <label class="nocheck clearfix" for="move_associated_files"> - <span class="component-title"> </span> - <span class="component-desc"><b>NOTE:</b> <i>.nfo</i> will be renamed to <i>.nfo-orig</i> when moved.</span> - </label> - </div> <div class="field-pair"> - <input type="checkbox" name="rename_episodes" id="rename_episodes" #if $sickbeard.RENAME_EPISODES == True then "checked=\"checked\"" else ""# /> - <label class="clearfix" for="rename_episodes"> - <span class="component-title">Rename Episodes</span> - <span class="component-desc">Rename episode using the naming settings in <a href="$sbRoot/config/general">General</a>?</span> + <input type="checkbox" name="use_torrents" class="enabler" id="use_torrents" #if $sickbeard.USE_TORRENTS == True then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="use_torrents"> + <span class="component-title">Search Torrents</span> + <span class="component-desc">Should Sick Beard search for torrent files?</span> </label> </div> + <div id="content_use_torrents"> <div class="field-pair"> - <input type="checkbox" name="process_automatically" id="process_automatically" #if $sickbeard.PROCESS_AUTOMATICALLY == True then "checked=\"checked\"" else ""# /> - <label class="clearfix" for="process_automatically"> - <span class="component-title">Scan and Process</span> - <span class="component-desc">Scan and post-process any files in your <i>TV Download Dir</i>?</span> + <label class="nocheck clearfix"> + <span class="component-title">Torrent Black Hole</span> + <input type="text" name="torrent_dir" id="torrent_dir" value="$sickbeard.TORRENT_DIR" size="45" /> </label> - <label class="nocheck clearfix" for="process_automatically"> + <label class="nocheck clearfix"> <span class="component-title"> </span> - <span class="component-desc"><b>NOTE:</b> Do not use if you use sabToSickbeard w/ SABnzbd+!</span> + <span class="component-desc">The directory where Sick Beard should store your <i>Torrent</i> files.</span> </label> </div> - + <div class="clearfix"></div> <input type="submit" value="Save Changes" /><br/> - + </div><!-- /content_use_torrents //--> + </fieldset> </div><!-- /component-group3 //--> diff --git a/data/interfaces/default/displayShow.tmpl b/data/interfaces/default/displayShow.tmpl index 845923888e2e0e51831edfe4f0571d57bc0ed945..d1e0bd3900c351543ee245817d2808e96988509a 100644 --- a/data/interfaces/default/displayShow.tmpl +++ b/data/interfaces/default/displayShow.tmpl @@ -11,14 +11,27 @@ <script type="text/javascript" src="$sbRoot/js/jquery.bookmarkscroll.js"></script> -<div class="h2footer align-right" style="margin-left:250px; line-height: 20px;"> +<div class="h2footer align-right"> +#if (len($seasonResults) > 14): + <select id="seasonJump"> + <option value="jump">Jump to Season</option> + #for $seasonNum in $seasonResults: + <option value="#season-$seasonNum["season"]">#if int($seasonNum["season"]) == 0 then "Specials" else "Season " + str($seasonNum["season"])#</option> + #end for + </select> +#else: <b>Season:</b> #for $seasonNum in $seasonResults: - #if int($seasonNum["season"]) == 0 then "<span class=\"separator\">|</span> " else ""# - <a href="#season-$seasonNum["season"]"> - #if int($seasonNum["season"]) == 0 then "Specials</a>" else str($seasonNum["season"])+"</a> "# - #if int($seasonNum["season"]) >1 then "<span class=\"separator\">|</span> " else ""# + #if int($seasonNum["season"]) == 0: + <a href="#season-$seasonNum["season"]">Specials</a> + #else: + <a href="#season-$seasonNum["season"]">${str($seasonNum["season"])}</a> + #end if + #if $seasonNum != $seasonResults[-1]: + <span class="separator">|</span> + #end if #end for +#end if </div><br/> <input type="hidden" id="sbRoot" value="$sbRoot" /> @@ -60,13 +73,8 @@ replace with: <b><%=", ".join([Quality.qualityStrings[x] for x in bestQualities] </td></tr> <tr><td class="showLegend">Language:</td><td>$show.lang</td></tr> <tr><td class="showLegend">Season Folders: </td><td><img src="$sbRoot/images/#if $show.seasonfolders == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr> -#if $show.air_by_date or ($show.genre and "Talk Show" in $show.genre): -#set $air_by_date = True -#else -#set $air_by_date = False -#end if <tr><td class="showLegend">Active: </td><td><img src="$sbRoot/images/#if int($show.paused) == 0 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr> - <tr><td class="showLegend">Air-by-Date: </td><td><img src="$sbRoot/images/#if int($air_by_date) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr> + <tr><td class="showLegend">Air-by-Date: </td><td><img src="$sbRoot/images/#if int($show.air_by_date) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr> </table> </div> @@ -151,6 +159,6 @@ $epLoc </tr> #end for -</table><br /><br /> +</table><br /> #include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_bottom.tmpl") diff --git a/data/interfaces/default/editShow.tmpl b/data/interfaces/default/editShow.tmpl index afde1c78becbde904d60d0f7259f9b57b68fc52f..f9ccdc245937fa4a75dc1c76f710267df92a184d 100644 --- a/data/interfaces/default/editShow.tmpl +++ b/data/interfaces/default/editShow.tmpl @@ -54,19 +54,17 @@ Quality: #include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_qualityChooser.tmpl") <br /> <br /> -Language: <select name="tvdbLang" id="tvdbLangSelect"></select> +Language: <select name="tvdbLang" id="tvdbLangSelect"></select><br /> +Note: This will only affect the language of the retrieved metadata file contents and episode filenames.<br /> +This <b>DOES NOT</b> allow Sick Beard to download non-english TV episodes!<br /> <br /> <br /> Use Season Folders: <input type="checkbox" name="seasonfolders" #if $show.seasonfolders == 1 then "checked=\"checked\"" else ""# /><br /><br /> Paused: <input type="checkbox" name="paused" #if $show.paused == 1 then "checked=\"checked\"" else ""# /><br /><br /> Air by date: -#if ($show.genre and "Talk Show" not in $show.genre) or not $show.genre: <input type="checkbox" name="air_by_date" #if $show.air_by_date == 1 then "checked=\"checked\"" else ""# /><br /> (check this if the show is released as Show.03.02.2010 rather than Show.S02E03) -#else -<input type="checkbox" disabled=\"disabled\" checked=\"checked\" /> -#end if <br /><br /> <input type="submit" value="Submit" /> </form> diff --git a/data/interfaces/default/history.tmpl b/data/interfaces/default/history.tmpl index afaf97bee1ab64e6c357c094429611dfd10f46f8..63b5ef19e96e8ba51e8234a97927950c337c0259 100644 --- a/data/interfaces/default/history.tmpl +++ b/data/interfaces/default/history.tmpl @@ -25,7 +25,6 @@ //--> </script> - <table id="historyTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0"> <thead><tr><th class="nowrap">Time</th><th>Episode</th><th>Action</th><th>Provider</th><th>Quality</th></tr></thead> <tbody> diff --git a/data/interfaces/default/home_addShows.tmpl b/data/interfaces/default/home_addShows.tmpl index 5eb7736ac5060018fa588ce400a7bd7056b0b531..4bf428947d0f6c268f925815d4feb91901b8c8de 100644 --- a/data/interfaces/default/home_addShows.tmpl +++ b/data/interfaces/default/home_addShows.tmpl @@ -32,7 +32,7 @@ <div class="button"><img src="$sbRoot/images/add-new32.png" alt="Add New Show"/></div> <div class="buttontext"> <h2>Add New Show</h2> - <p>For shows that you haven't downloaded yet, this option finds a show on TVDB.com, creates a directory for its episodes, and adds it to Sick Beard.</p> + <p>For shows that you haven't downloaded yet, this option finds a show on theTVDB.com, creates a directory for its episodes, and adds it to Sick Beard.</p> </div> </button> diff --git a/data/interfaces/default/inc_top.tmpl b/data/interfaces/default/inc_top.tmpl index 57933ecd78b1ce07f2a49696650b81c6d30daa70..2ebccfb271aa64f25f8f27b70718b842c137c7dd 100644 --- a/data/interfaces/default/inc_top.tmpl +++ b/data/interfaces/default/inc_top.tmpl @@ -16,7 +16,7 @@ <link rel="stylesheet" type="text/css" href="$sbRoot/css/config.css" /> <link rel="stylesheet" type="text/css" href="$sbRoot/css/jquery.pnotify.default.css" /> <link rel="stylesheet" type="text/css" href="$sbRoot/css/jquery.autocomplete.css" /> - <link rel="stylesheet" type="text/css" href="$sbRoot/css/smooth-grinder/jquery-ui-1.8.10.custom.css" /> + <link rel="stylesheet" type="text/css" href="$sbRoot/css/smooth-grinder/jquery-ui-1.8.11.custom.css" /> <link rel="stylesheet" type="text/css" href="$sbRoot/css/superfish.css" /> <link rel="stylesheet" type="text/css" href="$sbRoot/css/tablesorter.css"/> <link rel="stylesheet" type="text/css" media="only screen and (max-device-width: 480px)" href="$sbRoot/css/iphone.css" /> @@ -61,7 +61,7 @@ table.tablesorter thead tr .headerSortDown { background-image: url("$sbRoot/imag </style> <script type="text/javascript" src="$sbRoot/js/jquery-1.5.1.min.js"></script> - <script type="text/javascript" src="$sbRoot/js/jquery-ui-1.8.10.custom.min.js"></script> + <script type="text/javascript" src="$sbRoot/js/jquery-ui-1.8.11.custom.min.js"></script> <script type="text/javascript" src="$sbRoot/js/superfish-1.4.8.js"></script> <script type="text/javascript" src="$sbRoot/js/supersubs-0.2b.js"></script> <script type="text/javascript" src="$sbRoot/js/jquery.autocomplete.min.js"></script> @@ -200,9 +200,10 @@ table.tablesorter thead tr .headerSortDown { background-image: url("$sbRoot/imag <li id="NAVconfig"><a href="$sbRoot/config">Config</a> <ul> <li><a href="$sbRoot/config/general/"><img src="$sbRoot/images/menu/config16.png" alt="" width="16" height="16" />General</a></li> - <li><a href="$sbRoot/config/episodedownloads/"><img src="$sbRoot/images/menu/config16.png" alt="" width="16" height="16" />Episode Downloads</a></li> - <li><a href="$sbRoot/config/notifications/"><img src="$sbRoot/images/menu/config16.png" alt="" width="16" height="16" />Notifications</a></li> + <li><a href="$sbRoot/config/search/"><img src="$sbRoot/images/menu/config16.png" alt="" width="16" height="16" />Search Settings</a></li> <li><a href="$sbRoot/config/providers/"><img src="$sbRoot/images/menu/config16.png" alt="" width="16" height="16" />Search Providers</a></li> + <li><a href="$sbRoot/config/postProcessing/"><img src="$sbRoot/images/menu/config16.png" alt="" width="16" height="16" />Post Processing</a></li> + <li><a href="$sbRoot/config/notifications/"><img src="$sbRoot/images/menu/config16.png" alt="" width="16" height="16" />Notifications</a></li> </ul> </li> <li id="NAVerrorlogs"><a href="$sbRoot/errorlogs">$logPageTitle</a> diff --git a/data/interfaces/default/manage_manageSearches.tmpl b/data/interfaces/default/manage_manageSearches.tmpl index 9a3c157ebd1d87b31fa325ff99a8f11ddb5b4051..ece479634e566abbcd484cd6ddc2cbac14a9dfdd 100644 --- a/data/interfaces/default/manage_manageSearches.tmpl +++ b/data/interfaces/default/manage_manageSearches.tmpl @@ -11,6 +11,7 @@ <script type="text/javascript" src="$sbRoot/js/plotTooltip.js"></script> + <b>Backlog Search:</b><br /> #if not $backlogRunning: diff --git a/data/interfaces/default/restart.tmpl b/data/interfaces/default/restart.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..2a3114254d515afe38166a3e9a6ebc8c4ea57023 --- /dev/null +++ b/data/interfaces/default/restart.tmpl @@ -0,0 +1,17 @@ +#import sickbeard +#import datetime +#from sickbeard.common import * +#from sickbeard import db + +#set global $title="Home" +#set global $header="Restarting Sick Beard" + +#set global $sbPath = ".." + +#set global $topmenu="home"# +#import os.path +#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_top.tmpl") + +#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/restart_bare.tmpl") + +#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_bottom.tmpl") diff --git a/data/interfaces/default/restart_bare.tmpl b/data/interfaces/default/restart_bare.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..6add0cd72c20f1ae2e73697670a846559f88282a --- /dev/null +++ b/data/interfaces/default/restart_bare.tmpl @@ -0,0 +1,33 @@ +<script type="text/javascript" src="$sbRoot/js/jquery-1.5.1.min.js"></script> +<script type="text/javascript" charset="utf-8"> +<!-- +sbRoot = "$sbRoot"; +//--> +</script> + +<script type="text/javascript" src="$sbRoot/js/restart.js"></script> + +<h2>Performing Restart</h2> +<br /> +<div id="shut_down_message"> +Waiting for Sick Beard to shut down: +<img src="$sbRoot/images/loading16.gif" height="16" width="16" id="shut_down_loading" /> +<img src="$sbRoot/images/yes16.png" height="16" width="16" id="shut_down_success" style="display: none;" /> +</div> + +<div id="restart_message" style="display: none;"> +Waiting for Sick Beard to start again: +<img src="$sbRoot/images/loading16.gif" height="16" width="16" id="restart_loading" /> +<img src="$sbRoot/images/yes16.png" height="16" width="16" id="restart_success" style="display: none;" /> +<img src="$sbRoot/images/no16.png" height="16" width="16" id="restart_failure" style="display: none;" /> +</div> + +<div id="refresh_message" style="display: none;"> +Loading the home page: +<img src="$sbRoot/images/loading16.gif" height="16" width="16" id="refresh_loading" /> +</div> + +<div id="restart_fail_message" style="display: none;"> +Error: The restart has timed out, perhaps something prevented Sick Beard from starting again? +</div> + diff --git a/data/interfaces/default/viewlogs.tmpl b/data/interfaces/default/viewlogs.tmpl index 6f596523a65e0662d4e6050b38e87561ba7798cc..eab5e1d2d5a12e33fcc1b9b53d2951b2f2a6e2a2 100644 --- a/data/interfaces/default/viewlogs.tmpl +++ b/data/interfaces/default/viewlogs.tmpl @@ -21,7 +21,7 @@ //--> </script> -<div class="h2footer align-right"><b>Minimum logging level to display:</b> <select name="minLevel" id="minLevel" style="margin-top: -5px;"> +<div class="h2footer align-right"><b>Minimum logging level to display:</b> <select name="minLevel" id="minLevel"> #set $levels = $reverseNames.keys() $levels.sort(lambda x,y: cmp($reverseNames[$x], $reverseNames[$y])) #for $level in $levels: diff --git a/data/js/config.js b/data/js/config.js new file mode 100644 index 0000000000000000000000000000000000000000..37d3a24c1344320e075ae0ad44238afdaf363d32 --- /dev/null +++ b/data/js/config.js @@ -0,0 +1,13 @@ +$(document).ready(function(){ + $(".enabler").each(function(){ + if (!$(this).attr('checked')) + $('#content_'+$(this).attr('id')).hide(); + }); + + $(".enabler").click(function() { + if ($(this).attr('checked')) + $('#content_'+$(this).attr('id')).show(); + else + $('#content_'+$(this).attr('id')).hide(); + }); +}); \ No newline at end of file diff --git a/data/js/configNotifications.js b/data/js/configNotifications.js index da95ee87a3674a97e391b2a42ac27d48448ca8e5..cacfdd02ee001c7d7c3e46bbe16be09cfa3ae8d0 100644 --- a/data/js/configNotifications.js +++ b/data/js/configNotifications.js @@ -2,72 +2,112 @@ $(document).ready(function(){ var loading = '<img src="'+sbRoot+'/images/loading16.gif" height="16" width="16" />'; $('#testGrowl').click(function(){ - document.getElementById('testGrowl-result').innerHTML = loading; + $('#testGrowl-result').html(loading); var growl_host = $("#growl_host").val(); var growl_password = $("#growl_password").val(); var growl_result = $.get(sbRoot+"/home/testGrowl", {'host': growl_host, 'password': growl_password}, - function (data){ document.getElementById('testGrowl-result').innerHTML = data;}); + function (data){ $('#testGrowl-result').html(data); }); }); $('#testProwl').click(function(){ - document.getElementById('testProwl-result').innerHTML = loading; + $('#testProwl-result').html(loading); var prowl_api = $("#prowl_api").val(); var prowl_priority = $("#prowl_priority").val(); var prowl_result = $.get(sbRoot+"/home/testProwl", {'prowl_api': prowl_api, 'prowl_priority': prowl_priority}, - function (data){ document.getElementById('testProwl-result').innerHTML = data;}); + function (data){ $('#testProwl-result').html(data); }); }); $('#testXBMC').click(function(){ - document.getElementById('testXBMC-result').innerHTML = loading; + $('#testXBMC-result').html(loading); var xbmc_host = $("#xbmc_host").val(); var xbmc_username = $("#xbmc_username").val(); var xbmc_password = $("#xbmc_password").val(); $.get(sbRoot+"/home/testXBMC", {'host': xbmc_host, 'username': xbmc_username, 'password': xbmc_password}, - function (data){ document.getElementById('testXBMC-result').innerHTML = data;}); + function (data){ $('#testXBMC-result').html(data); }); + }); + + $('#testPLEX').click(function(){ + $('#testPLEX-result').html(loading); + var plex_host = $("#plex_host").val(); + var plex_username = $("#plex_username").val(); + var plex_password = $("#plex_password").val(); + + $.get(sbRoot+"/home/testPLEX", {'host': plex_host, 'username': plex_username, 'password': plex_password}, + function (data){ $('#testPLEX-result').html(data);}); }); $('#testNotifo').click(function(){ - document.getElementById('testNotifo-result').innerHTML = loading; + $('#testNotifo-result').html(loading); var notifo_username = $("#notifo_username").val(); var notifo_apisecret = $("#notifo_apisecret").val(); $.get(sbRoot+"/home/testNotifo", {'username': notifo_username, 'apisecret': notifo_apisecret}, - function (data){ document.getElementById('testNotifo-result').innerHTML = data; }); + function (data){ $('#testNotifo-result').html(data); }); }); $('#testLibnotify').click(function(){ $('#testLibnotify-result').html(loading); - $.get("$sbRoot/home/testLibnotify", - function(message){ document.getElementById('testLibnotify-result').innerHTML = message; }); + $.get(sbRoot+"/home/testLibnotify", + function(message){ $('#testLibnotify-result').html(message); }); }); $('#twitterStep1').click(function(){ - document.getElementById('testTwitter-result').innerHTML = loading; + $('#testTwitter-result').html(loading); var twitter1_result = $.get(sbRoot+"/home/twitterStep1", function (data){window.open(data)}) - .complete(function() { document.getElementById('testTwitter-result').innerHTML = '<b>Step1:</b> Confirm Authorization'; }); + .complete(function() { $('#testTwitter-result').html('<b>Step1:</b> Confirm Authorization'); }); }); $('#twitterStep2').click(function(){ - document.getElementById('testTwitter-result').innerHTML = loading; + $('#testTwitter-result').html(loading); var twitter_key = $("#twitter_key").val(); $.get(sbRoot+"/home/twitterStep2", {'key': twitter_key}, - function (data){ document.getElementById('testTwitter-result').innerHTML = data; }); + function (data){ $('#testTwitter-result').html(data); }); }); $('#testTwitter').click(function(){ $.get(sbRoot+"/home/testTwitter", - function (data){ document.getElementById('testTwitter-result').innerHTML = data;}); + function (data){ $('#testTwitter-result').html(data); }); }); - $(".enabler").each(function(){ - if (!$(this).attr('checked')) - $('#content_'+$(this).attr('id')).hide(); + $('#settingsNMJ').click(function(){ + if (!$('#nmj_host').val()) { + alert('Please fill in the Popcorn IP address'); + $('#nmj_host').focus(); + return; + } + $('#testNMJ-result').html(loading); + var nmj_host = $('#nmj_host').val(); + + $.get(sbRoot+"/home/settingsNMJ", {'host': nmj_host}, + function (data){ + if (data == null) { + $('#nmj_database').removeAttr('readonly'); + $('#nmj_mount').removeAttr('readonly'); + } + var JSONData = $.parseJSON(data); + $('#testNMJ-result').html(JSONData.message); + $('#nmj_database').val(JSONData.database); + $('#nmj_mount').val(JSONData.mount); + + if (JSONData.database) + $('#nmj_database').attr('readonly', true); + else + $('#nmj_database').removeAttr('readonly'); + + if (JSONData.mount) + $('#nmj_mount').attr('readonly', true); + else + $('#nmj_mount').removeAttr('readonly'); + }); }); - $(".enabler").click(function() { - if ($(this).attr('checked')) - $('#content_'+$(this).attr('id')).show(); - else - $('#content_'+$(this).attr('id')).hide(); - }); -}); \ No newline at end of file + $('#testNMJ').click(function(){ + $('#testNMJ-result').html(loading); + var nmj_host = $("#nmj_host").val(); + var nmj_database = $("#nmj_database").val(); + var nmj_mount = $("#nmj_mount").val(); + + $.get(sbRoot+"/home/testNMJ", {'host': nmj_host, 'database': nmj_database, 'mount': nmj_mount}, + function (data){ $('#testNMJ-result').html(data); }); + }); +}); diff --git a/data/js/configGeneral.js b/data/js/configPostProcessing.js similarity index 94% rename from data/js/configGeneral.js rename to data/js/configPostProcessing.js index 1787d6dafd44867c98de52c2a0fe243f9f870376..82f3376906358aa0c2d90763f3108ccaca82b824 100644 --- a/data/js/configGeneral.js +++ b/data/js/configPostProcessing.js @@ -12,13 +12,13 @@ $(document).ready(function(){ 'whichTest': 'single' } - $.get(nameTestURL, params, + $.get(sbRoot+"/config/postProcessing/testNaming", params, function(data){ $('#normalExampleText').text(data); }); params['whichTest'] = 'multi' - $.get(nameTestURL, params, + $.get(sbRoot+"/config/postProcessing/testNaming", params, function(data){ $('#multiExampleText').text(data); }); diff --git a/data/js/configProviders.js b/data/js/configProviders.js index d54bac82148eb4d41f148fe49a846418f294cc8e..137b8ce0b402b424856196f2071b349f977a9d8b 100644 --- a/data/js/configProviders.js +++ b/data/js/configProviders.js @@ -13,7 +13,7 @@ $(document).ready(function(){ }); } - $.fn.addProvider = function (id, name, url, key, isDefault) { + $.fn.addProvider = function (id, name, url, key, isDefault, showProvider) { if (url.match('/$') == null) url = url + '/' @@ -24,8 +24,8 @@ $(document).ready(function(){ $('#editANewznabProvider').addOption(id, name); $(this).populateNewznabSection(); - if ($('#provider_order_list > #'+id).length == 0) { - var toAdd = '<li class="ui-state-default" id="'+id+'"> <input type="checkbox" id="enable_'+id+'" class="enabler" CHECKED> <a href="'+url+'" class="imgLink" target="_new"><img src="'+sbRoot+'/images/providers/newznab.gif" alt="'+name+'" width="16" height="16"></a> '+name+'</li>' + if ($('#provider_order_list > #'+id).length == 0 && showProvider != false) { + var toAdd = '<li class="ui-state-default" id="'+id+'"> <input type="checkbox" id="enable_'+id+'" class="provider_enabler" CHECKED> <a href="'+url+'" class="imgLink" target="_new"><img src="'+sbRoot+'/images/providers/newznab.gif" alt="'+name+'" width="16" height="16"></a> '+name+'</li>' $('#provider_order_list').append(toAdd); $('#provider_order_list').sortable("refresh"); @@ -140,7 +140,7 @@ $(document).ready(function(){ $(this).populateNewznabSection(); }); - $('.enabler').live('click', function(){ + $('.provider_enabler').live('click', function(){ $(this).refreshProviderList(); }); diff --git a/data/js/configSearch.js b/data/js/configSearch.js new file mode 100644 index 0000000000000000000000000000000000000000..01d8f89bc8beb40a835b97a29c4f1278998deb26 --- /dev/null +++ b/data/js/configSearch.js @@ -0,0 +1,27 @@ +$(document).ready(function(){ + + $.fn.nzb_method_handler = function() { + + var selectedProvider = $('#nzb_method :selected').val(); + + if (selectedProvider == "blackhole") { + $('#blackhole_settings').show(); + $('#sabnzbd_settings').hide(); + $('#nzbget_settings').hide(); + } else if (selectedProvider == "nzbget") { + $('#blackhole_settings').hide(); + $('#sabnzbd_settings').hide(); + $('#nzbget_settings').show(); + } else { + $('#blackhole_settings').hide(); + $('#sabnzbd_settings').show(); + $('#nzbget_settings').hide(); + } + + } + + $('#nzb_method').change($(this).nzb_method_handler); + + $(this).nzb_method_handler(); + +}); diff --git a/data/js/displayShow.js b/data/js/displayShow.js index bfcecdfadaa19314224dcde5d827b5c6b58dbb55..d5e0ab23670afd7d3b7dce40df9cd52b400dbcbf 100644 --- a/data/js/displayShow.js +++ b/data/js/displayShow.js @@ -1,12 +1,13 @@ $(document).ready(function(){ - $("table.sickbeardTable tr").click( function(event) { - if (event.target.type !== "checkbox") { - $(this).find("input:checkbox.epCheck").each(function(){ - $(this).attr("checked", !$(this).attr("checked")); - }); - } - }); + $('#seasonJump').change(function() { + var id = $(this).val(); + if (id && id != 'jump') { + $('html,body').animate({scrollTop: $(id).offset().top},'slow'); + location.hash = id; + } + $(this).val('jump'); + }); $("#prevShow").click(function(){ $('#pickShow option:selected').prev('option').attr('selected', 'selected'); diff --git a/data/js/jquery-ui-1.8.10.custom.min.js b/data/js/jquery-ui-1.8.11.custom.min.js similarity index 76% rename from data/js/jquery-ui-1.8.10.custom.min.js rename to data/js/jquery-ui-1.8.11.custom.min.js index dec36337246f9bf6f2eb7ea1ccad21c81401a755..e32859f38bd221b0a0e0d161b1f8ea06220ea409 100644 --- a/data/js/jquery-ui-1.8.10.custom.min.js +++ b/data/js/jquery-ui-1.8.11.custom.min.js @@ -1,5 +1,5 @@ /*! - * jQuery UI 1.8.10 + * jQuery UI 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -7,7 +7,7 @@ * * http://docs.jquery.com/UI */ -(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.10",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106, +(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.11",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106, NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this, "position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position"); if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f, @@ -16,7 +16,7 @@ d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&& b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)}})}})(jQuery); ;/*! - * jQuery UI Widget 1.8.10 + * jQuery UI Widget 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -31,7 +31,7 @@ this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetNa widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this}, enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery); ;/*! - * jQuery UI Mouse 1.8.10 + * jQuery UI Mouse 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -42,13 +42,13 @@ enable:function(){return this._setOption("disabled",false)},disable:function(){r * Depends: * jquery.ui.widget.js */ -(function(c){c.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(b){return a._mouseDown(b)}).bind("click."+this.widgetName,function(b){if(true===c.data(b.target,a.widgetName+".preventClickEvent")){c.removeData(b.target,a.widgetName+".preventClickEvent");b.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(a){a.originalEvent= -a.originalEvent||{};if(!a.originalEvent.mouseHandled){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var b=this,e=a.which==1,f=typeof this.options.cancel=="string"?c(a.target).parents().add(a.target).filter(this.options.cancel).length:false;if(!e||f||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){b.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted= -this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();return true}}this._mouseMoveDelegate=function(d){return b._mouseMove(d)};this._mouseUpDelegate=function(d){return b._mouseUp(d)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return a.originalEvent.mouseHandled=true}},_mouseMove:function(a){if(c.browser.msie&&!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a); -return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;a.target==this._mouseDownEvent.target&&c.data(a.target,this.widgetName+".preventClickEvent", -true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); +(function(b){b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(a){a.originalEvent= +a.originalEvent||{};if(!a.originalEvent.mouseHandled){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,e=a.which==1,f=typeof this.options.cancel=="string"?b(a.target).parents().add(a.target).filter(this.options.cancel).length:false;if(!e||f||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted= +this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(d){return c._mouseMove(d)};this._mouseUpDelegate=function(d){return c._mouseUp(d)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return a.originalEvent.mouseHandled= +true}},_mouseMove:function(a){if(b.browser.msie&&!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate); +if(this._mouseStarted){this._mouseStarted=false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); ;/* - * jQuery UI Position 1.8.10 + * jQuery UI Position 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -64,7 +64,7 @@ d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a) a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b), g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery); ;/* - * jQuery UI Draggable 1.8.10 + * jQuery UI Draggable 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -87,34 +87,34 @@ this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone():this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]|| 0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top- -(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment== -"parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[(a.containment=="document"?0:d(window).scrollLeft())-this.offset.relative.left-this.offset.parent.left,(a.containment=="document"?0:d(window).scrollTop())-this.offset.relative.top-this.offset.parent.top,(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"? -0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){var b=d(a.containment)[0];if(b){a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"), -10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}}else if(a.containment.constructor== -Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop(): -f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,g=a.pageY; -if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])e=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])e=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/ -b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;e=this.originalPageX+Math.round((e-this.originalPageX)/b.grid[0])*b.grid[0];e=this.containment?!(e-this.offset.click.left<this.containment[0]||e-this.offset.click.left>this.containment[2])?e:!(e-this.offset.click.left<this.containment[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:g-this.offset.click.top- -this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!= -this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.10"}); -d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var g=d.data(this,"sortable");if(g&&!g.options.disabled){c.sortables.push({instance:g,shouldRevert:g.options.revert});g._refreshItems();g._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver= -0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs= -c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().appendTo(this.instance.element).data("sortable-item",true);this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a, -true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver= -0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor= -a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","iframeFix",{start:function(){var a=d(this).data("draggable").options;d(a.iframeFix===true?"iframe":a.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")})}, -stop:function(){d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!= -document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!="x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop- -c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()- -(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable", -"snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,g=b.offset.left,n=g+c.helperProportions.width,m=b.offset.top,o=m+c.helperProportions.height,h= -c.snapElements.length-1;h>=0;h--){var i=c.snapElements[h].left,k=i+c.snapElements[h].width,j=c.snapElements[h].top,l=j+c.snapElements[h].height;if(i-e<g&&g<k+e&&j-e<m&&m<l+e||i-e<g&&g<k+e&&j-e<o&&o<l+e||i-e<n&&n<k+e&&j-e<m&&m<l+e||i-e<n&&n<k+e&&j-e<o&&o<l+e){if(f.snapMode!="inner"){var p=Math.abs(j-o)<=e,q=Math.abs(l-m)<=e,r=Math.abs(i-n)<=e,s=Math.abs(k-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:j-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative", -{top:l,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:i-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:k}).left-c.margins.left}var t=p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(j-m)<=e;q=Math.abs(l-o)<=e;r=Math.abs(i-g)<=e;s=Math.abs(k-n)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:j,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:l-c.helperProportions.height, -left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:i}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:k-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[h].snapping&&(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[h].item}));c.snapElements[h].snapping=p||q||r||s||t}else{c.snapElements[h].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element, -a,d.extend(c._uiHash(),{snapItem:c.snapElements[h].item}));c.snapElements[h].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a, -b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery); +(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(), +height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[(a.containment=="document"?0:d(window).scrollLeft())-this.offset.relative.left-this.offset.parent.left,(a.containment=="document"?0:d(window).scrollTop())-this.offset.relative.top-this.offset.parent.top,(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"? +document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){var b=d(a.containment)[0];if(b){a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"), +10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),a.top+(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"), +10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom]}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&& +d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], +this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])e=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])e=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g= +this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;e=this.originalPageX+Math.round((e-this.originalPageX)/b.grid[0])*b.grid[0];e=this.containment?!(e-this.offset.click.left<this.containment[0]||e-this.offset.click.left>this.containment[2])? +e:!(e-this.offset.click.left<this.containment[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft(): +f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition, +offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.11"});d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var g=d.data(this,"sortable");if(g&&!g.options.disabled){c.sortables.push({instance:g,shouldRevert:g.options.revert});g.refreshPositions();g._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({}, +b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c= +d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().appendTo(this.instance.element).data("sortable-item",true);this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]}; +a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&& +this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor", +{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","iframeFix",{start:function(){var a=d(this).data("draggable").options;d(a.iframeFix===true?"iframe":a.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+ +"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")})},stop:function(){d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity", +a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!="x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+ +c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()< +c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+ +c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable","snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"), +f=c.options,e=f.snapTolerance,g=b.offset.left,n=g+c.helperProportions.width,m=b.offset.top,o=m+c.helperProportions.height,h=c.snapElements.length-1;h>=0;h--){var i=c.snapElements[h].left,k=i+c.snapElements[h].width,j=c.snapElements[h].top,l=j+c.snapElements[h].height;if(i-e<g&&g<k+e&&j-e<m&&m<l+e||i-e<g&&g<k+e&&j-e<o&&o<l+e||i-e<n&&n<k+e&&j-e<m&&m<l+e||i-e<n&&n<k+e&&j-e<o&&o<l+e){if(f.snapMode!="inner"){var p=Math.abs(j-o)<=e,q=Math.abs(l-m)<=e,r=Math.abs(i-n)<=e,s=Math.abs(k-g)<=e;if(p)b.position.top= +c._convertPositionTo("relative",{top:j-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:l,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:i-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:k}).left-c.margins.left}var t=p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(j-m)<=e;q=Math.abs(l-o)<=e;r=Math.abs(i-g)<=e;s=Math.abs(k-n)<=e;if(p)b.position.top= +c._convertPositionTo("relative",{top:j,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:l-c.helperProportions.height,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:i}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:k-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[h].snapping&&(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(), +{snapItem:c.snapElements[h].item}));c.snapElements[h].snapping=p||q||r||s||t}else{c.snapElements[h].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[h].item}));c.snapElements[h].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b= +parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery); ;/* - * jQuery UI Droppable 1.8.10 + * jQuery UI Droppable 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -133,14 +133,14 @@ a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass); this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g= d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop", -a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.10"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height; +a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.11"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height; switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>= i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!= -"none";if(c[f].visible){c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight};e=="mousedown"&&c[f]._activate.call(c[f],b)}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem|| +"none";if(c[f].visible){e=="mousedown"&&c[f]._activate.call(c[f],b);c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight}}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem|| a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);if(c=!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e= d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})}}})(jQuery); ;/* - * jQuery UI Resizable 1.8.10 + * jQuery UI Resizable 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -173,7 +173,7 @@ this.size.height,k=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&k)b.left=i-a.minW this.options;this.elementOffset=this.element.offset();if(this._helper){this.helper=this.helper||e('<div style="overflow:hidden;"></div>');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b, a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a, c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize, -originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.10"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize= +originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.11"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize= b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var k=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:k.parents(a.originalElement[0]).length?["width","height"]:["width", "height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(k.css("position"))){c._revertToRelativePosition=true;k.css({position:"absolute",top:"auto",left:"auto"})}k.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})}; if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height- @@ -187,7 +187,7 @@ a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.heigh b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/ (a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},l=function(b){return!isNaN(parseInt(b,10))}})(jQuery); ;/* - * jQuery UI Selectable 1.8.10 + * jQuery UI Selectable 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -207,9 +207,9 @@ c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){va this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.right<b||a.top>i||a.bottom<g);else if(d.tolerance=="fit")k=a.left>b&&a.right<h&&a.top>g&&a.bottom<i;if(k){if(a.selected){a.$element.removeClass("ui-selected");a.selected=false}if(a.unselecting){a.$element.removeClass("ui-unselecting"); a.unselecting=false}if(!a.selecting){a.$element.addClass("ui-selecting");a.selecting=true;f._trigger("selecting",c,{selecting:a.element})}}else{if(a.selecting)if(c.metaKey&&a.startselected){a.$element.removeClass("ui-selecting");a.selecting=false;a.$element.addClass("ui-selected");a.selected=true}else{a.$element.removeClass("ui-selecting");a.selecting=false;if(a.startselected){a.$element.addClass("ui-unselecting");a.unselecting=true}f._trigger("unselecting",c,{unselecting:a.element})}if(a.selected)if(!c.metaKey&& !a.startselected){a.$element.removeClass("ui-selected");a.selected=false;a.$element.addClass("ui-unselecting");a.unselecting=true;f._trigger("unselecting",c,{unselecting:a.element})}}}});return false}},_mouseStop:function(c){var f=this;this.dragged=false;e(".ui-unselecting",this.element[0]).each(function(){var d=e.data(this,"selectable-item");d.$element.removeClass("ui-unselecting");d.unselecting=false;d.startselected=false;f._trigger("unselected",c,{unselected:d.element})});e(".ui-selecting",this.element[0]).each(function(){var d= -e.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected");d.selecting=false;d.selected=true;d.startselected=true;f._trigger("selected",c,{selected:d.element})});this._trigger("stop",c);this.helper.remove();return false}});e.extend(e.ui.selectable,{version:"1.8.10"})})(jQuery); +e.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected");d.selecting=false;d.selected=true;d.startselected=true;f._trigger("selected",c,{selected:d.element})});this._trigger("stop",c);this.helper.remove();return false}});e.extend(e.ui.selectable,{version:"1.8.11"})})(jQuery); ;/* - * jQuery UI Sortable 1.8.10 + * jQuery UI Sortable 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -223,29 +223,29 @@ e.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass(" * jquery.ui.widget.js */ (function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){this.containerCache={};this.element.addClass("ui-sortable"); -this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a==="disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this, -arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem= -c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset, -{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment(); -if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start", -a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute"); -if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop+b.scrollSpeed;else if(a.pageY-this.overflowOffset.top<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop-b.scrollSpeed;if(this.overflowOffset.left+ -this.scrollParent[0].offsetWidth-a.pageX<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft+b.scrollSpeed;else if(a.pageX-this.overflowOffset.left<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft-b.scrollSpeed}else{if(a.pageY-d(document).scrollTop()<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()-b.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()+ -b.scrollSpeed);if(a.pageX-d(document).scrollLeft()<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()-b.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()+b.scrollSpeed)}c!==false&&d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+ -"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(b=this.items.length-1;b>=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a, -c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]== -document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var b=this.containers.length- -1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,dragging:false,reverting:false,_noFinalSort:null}); -this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},toArray:function(a){var b=this._getItemsAsjQuery(a&& -a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+j<k&&b+l>g&&b+l<h;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||this.options.tolerance!="pointer"&&this.helperProportions[this.floating? -"width":"height"]>a[this.floating?"width":"height"]?j:g<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<h&&i<e+this.helperProportions.height/2&&f-this.helperProportions.height/2<k},_intersectsWithPointer:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left,a.width);b=b&&a;a=this._getDragVerticalDirection();var c=this._getDragHorizontalDirection();if(!b)return false;return this.floating? -c&&c=="right"||a=="down"?2:1:a&&(a=="down"?2:1)},_intersectsWithSides:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top+a.height/2,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left+a.width/2,a.width);var c=this._getDragVerticalDirection(),e=this._getDragHorizontalDirection();return this.floating&&e?e=="right"&&a||e=="left"&&!a:c&&(c=="down"&&b||c=="up"&&!b)},_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top; -return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h= -d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)}); -return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(a){this.items=[];this.containers=[this];var b=this.items,c=[[d.isFunction(this.options.items)?this.options.items.call(this.element[0],a,{item:this.currentItem}):d(this.options.items,this.element),this]],e=this._connectWith();if(e)for(var f=e.length-1;f>=0;f--)for(var g= -d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h<g;h++){i=d(e[h]);i.data("sortable-item",a);b.push({item:i,instance:a,width:0,height:0,left:0,top:0})}}},refreshPositions:function(a){if(this.offsetParent&&this.helper)this.offset.parent= -this._getParentOffset();for(var b=this.items.length-1;b>=0;b--){var c=this.items[b],e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top= -e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0]; +this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a==="disabled"){this.options[a]= +b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&!b){var f=false; +d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left- +this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]}; +this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!= +document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a); +return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop+b.scrollSpeed;else if(a.pageY-this.overflowOffset.top< +b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop-b.scrollSpeed;if(this.overflowOffset.left+this.scrollParent[0].offsetWidth-a.pageX<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft+b.scrollSpeed;else if(a.pageX-this.overflowOffset.left<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft-b.scrollSpeed}else{if(a.pageY-d(document).scrollTop()<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()- +b.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()+b.scrollSpeed);if(a.pageX-d(document).scrollLeft()<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()-b.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()+b.scrollSpeed)}c!==false&&d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this, +a)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(b=this.items.length-1;b>=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0], +e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset(); +c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"): +this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null, +dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")}, +toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+j<k&&b+l>g&&b+l<h;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers|| +this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?j:g<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<h&&i<e+this.helperProportions.height/2&&f-this.helperProportions.height/2<k},_intersectsWithPointer:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left,a.width);b=b&&a;a=this._getDragVerticalDirection(); +var c=this._getDragHorizontalDirection();if(!b)return false;return this.floating?c&&c=="right"||a=="down"?2:1:a&&(a=="down"?2:1)},_intersectsWithSides:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top+a.height/2,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left+a.width/2,a.width);var c=this._getDragVerticalDirection(),e=this._getDragHorizontalDirection();return this.floating&&e?e=="right"&&a||e=="left"&&!a:c&&(c=="down"&&b||c=="up"&&!b)}, +_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith(); +if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), +this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(a){this.items=[];this.containers=[this];var b=this.items,c=[[d.isFunction(this.options.items)?this.options.items.call(this.element[0],a,{item:this.currentItem}):d(this.options.items,this.element), +this]],e=this._connectWith();if(e)for(var f=e.length-1;f>=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h<g;h++){i=d(e[h]);i.data("sortable-item",a);b.push({item:i,instance:a,width:0,height:0,left:0,top:0})}}},refreshPositions:function(a){if(this.offsetParent&& +this.helper)this.offset.parent=this._getParentOffset();for(var b=this.items.length-1;b>=0;b--){var c=this.items[b],e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left= +e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0]; if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder); c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length=== 1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-f)<b){b=Math.abs(h-f);e=this.items[g]}}if(e||this.options.dropOnEmpty){this.currentContainer= @@ -267,9 +267,9 @@ this.fromOutside&&!b&&c.push(function(f){this._trigger("receive",f,this._uiHash( this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out", g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",a,this._uiHash());for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}return false}b|| this._trigger("beforeStop",a,this._uiHash());this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.helper[0]!=this.currentItem[0]&&this.helper.remove();this.helper=null;if(!b){for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}this.fromOutside=false;return true},_trigger:function(){d.Widget.prototype._trigger.apply(this,arguments)===false&&this.cancel()},_uiHash:function(a){var b=a||this;return{helper:b.helper,placeholder:b.placeholder||d([]),position:b.position, -originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,sender:a?a.element:null}}});d.extend(d.ui.sortable,{version:"1.8.10"})})(jQuery); +originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,sender:a?a.element:null}}});d.extend(d.ui.sortable,{version:"1.8.11"})})(jQuery); ;/* - * jQuery UI Accordion 1.8.10 + * jQuery UI Accordion 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -284,8 +284,8 @@ originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,send (function(c){c.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"); a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom"); if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion", -function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a=this.options;if(a.icons){c("<span></span>").addClass("ui-icon "+ -a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("tabIndex"); +function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a= +this.options;if(a.icons){c("<span></span>").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"); this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons(); b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target); a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+ @@ -294,12 +294,12 @@ if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.ac if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(), e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight|| e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false", -tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.10",animations:{slide:function(a,b){a= -c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/);f[i]={value:j[1], -unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide",paddingTop:"hide", -paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery); +"aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.11", +animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/); +f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide", +paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery); ;/* - * jQuery UI Button 1.8.10 + * jQuery UI Button 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -316,16 +316,15 @@ i);if(typeof this.options.disabled!=="boolean")this.options.disabled=this.elemen function(){if(!c.disabled){a(this).addClass("ui-state-hover");this===g&&a(this).addClass("ui-state-active")}}).bind("mouseleave.button",function(){c.disabled||a(this).removeClass(f)}).bind("focus.button",function(){a(this).addClass("ui-state-focus")}).bind("blur.button",function(){a(this).removeClass("ui-state-focus")});d&&this.element.bind("change.button",function(){b.refresh()});if(this.type==="checkbox")this.buttonElement.bind("click.button",function(){if(c.disabled)return false;a(this).toggleClass("ui-state-active"); b.buttonElement.attr("aria-pressed",b.element[0].checked)});else if(this.type==="radio")this.buttonElement.bind("click.button",function(){if(c.disabled)return false;a(this).addClass("ui-state-active");b.buttonElement.attr("aria-pressed",true);var e=b.element[0];h(e).not(e).map(function(){return a(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed",false)});else{this.buttonElement.bind("mousedown.button",function(){if(c.disabled)return false;a(this).addClass("ui-state-active"); g=this;a(document).one("mouseup",function(){g=null})}).bind("mouseup.button",function(){if(c.disabled)return false;a(this).removeClass("ui-state-active")}).bind("keydown.button",function(e){if(c.disabled)return false;if(e.keyCode==a.ui.keyCode.SPACE||e.keyCode==a.ui.keyCode.ENTER)a(this).addClass("ui-state-active")}).bind("keyup.button",function(){a(this).removeClass("ui-state-active")});this.buttonElement.is("a")&&this.buttonElement.keyup(function(e){e.keyCode===a.ui.keyCode.SPACE&&a(this).click()})}this._setOption("disabled", -c.disabled)},_determineButtonType:function(){this.type=this.element.is(":checkbox")?"checkbox":this.element.is(":radio")?"radio":this.element.is("input")?"input":"button";if(this.type==="checkbox"||this.type==="radio"){this.buttonElement=this.element.parents().last().find("label[for="+this.element.attr("id")+"]");this.element.addClass("ui-helper-hidden-accessible");var b=this.element.is(":checked");b&&this.buttonElement.addClass("ui-state-active");this.buttonElement.attr("aria-pressed",b)}else this.buttonElement= -this.element},widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible");this.buttonElement.removeClass("ui-button ui-widget ui-state-default ui-corner-all ui-state-hover ui-state-active ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only").removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html());this.hasTitle|| -this.buttonElement.removeAttr("title");a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments);if(b==="disabled")c?this.element.attr("disabled",true):this.element.removeAttr("disabled");this._resetButton()},refresh:function(){var b=this.element.is(":disabled");b!==this.options.disabled&&this._setOption("disabled",b);if(this.type==="radio")h(this.element[0]).each(function(){a(this).is(":checked")?a(this).button("widget").addClass("ui-state-active").attr("aria-pressed", +c.disabled)},_determineButtonType:function(){this.type=this.element.is(":checkbox")?"checkbox":this.element.is(":radio")?"radio":this.element.is("input")?"input":"button";if(this.type==="checkbox"||this.type==="radio"){var b=this.element.parents().filter(":last"),c="label[for="+this.element.attr("id")+"]";this.buttonElement=b.find(c);if(!this.buttonElement.length){b=b.length?b.siblings():this.element.siblings();this.buttonElement=b.filter(c);if(!this.buttonElement.length)this.buttonElement=b.find(c)}this.element.addClass("ui-helper-hidden-accessible"); +(b=this.element.is(":checked"))&&this.buttonElement.addClass("ui-state-active");this.buttonElement.attr("aria-pressed",b)}else this.buttonElement=this.element},widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible");this.buttonElement.removeClass("ui-button ui-widget ui-state-default ui-corner-all ui-state-hover ui-state-active ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only").removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()); +this.hasTitle||this.buttonElement.removeAttr("title");a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments);if(b==="disabled")c?this.element.attr("disabled",true):this.element.removeAttr("disabled");this._resetButton()},refresh:function(){var b=this.element.is(":disabled");b!==this.options.disabled&&this._setOption("disabled",b);if(this.type==="radio")h(this.element[0]).each(function(){a(this).is(":checked")?a(this).button("widget").addClass("ui-state-active").attr("aria-pressed", true):a(this).button("widget").removeClass("ui-state-active").attr("aria-pressed",false)});else if(this.type==="checkbox")this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed",true):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed",false)},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var b=this.buttonElement.removeClass("ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only"), -c=a("<span></span>").addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,f=d.primary&&d.secondary,e=[];if(d.primary||d.secondary){e.push("ui-button-text-icon"+(f?"s":d.primary?"-primary":"-secondary"));d.primary&&b.prepend("<span class='ui-button-icon-primary ui-icon "+d.primary+"'></span>");d.secondary&&b.append("<span class='ui-button-icon-secondary ui-icon "+d.secondary+"'></span>");if(!this.options.text){e.push(f?"ui-button-icons-only":"ui-button-icon-only"); -b.removeClass("ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary");this.hasTitle||b.attr("title",c)}}else e.push("ui-button-text-only");b.addClass(e.join(" "))}}});a.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c);a.Widget.prototype._setOption.apply(this, -arguments)},refresh:function(){this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end()},destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy"); -a.Widget.prototype.destroy.call(this)}})})(jQuery); +c=a("<span></span>").addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,f=d.primary&&d.secondary,e=[];if(d.primary||d.secondary){if(this.options.text)e.push("ui-button-text-icon"+(f?"s":d.primary?"-primary":"-secondary"));d.primary&&b.prepend("<span class='ui-button-icon-primary ui-icon "+d.primary+"'></span>");d.secondary&&b.append("<span class='ui-button-icon-secondary ui-icon "+d.secondary+"'></span>");if(!this.options.text){e.push(f?"ui-button-icons-only": +"ui-button-icon-only");this.hasTitle||b.attr("title",c)}}else e.push("ui-button-text-only");b.addClass(e.join(" "))}}});a.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c);a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end()}, +destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");a.Widget.prototype.destroy.call(this)}})})(jQuery); ;/* - * jQuery UI Dialog 1.8.10 + * jQuery UI Dialog 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -359,13 +358,13 @@ h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c l)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"):e.removeClass("ui-dialog-disabled"); break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||" "));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a=this.options,b,d,e= this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height-b,0));this.uiDialog.is(":data(resizable)")&& -this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.10",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(a){if(this.instances.length=== +this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.11",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(a){if(this.instances.length=== 0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()<c.ui.dialog.overlay.maxZ)return false})},1);c(document).bind("keydown.dialog-overlay",function(d){if(a.options.closeOnEscape&&d.keyCode&&d.keyCode===c.ui.keyCode.ESCAPE){a.close(d);d.preventDefault()}});c(window).bind("resize.dialog-overlay",c.ui.dialog.overlay.resize)}var b=(this.oldInstances.pop()||c("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(), height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight); b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a<b?c(window).height()+"px":a+"px"}else return c(document).height()+"px"},width:function(){var a,b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth);b=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return a<b?c(window).width()+"px":a+"px"}else return c(document).width()+"px"},resize:function(){var a=c([]);c.each(c.ui.dialog.overlay.instances, function(){a=a.add(this)});a.css({width:0,height:0}).css({width:c.ui.dialog.overlay.width(),height:c.ui.dialog.overlay.height()})}});c.extend(c.ui.dialog.overlay.prototype,{destroy:function(){c.ui.dialog.overlay.destroy(this.$el)}})})(jQuery); ;/* - * jQuery UI Slider 1.8.10 + * jQuery UI Slider 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -396,9 +395,9 @@ this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui return a=this._trimAlignValue(a)}else{a=this.options.values.slice();for(c=0;c<a.length;c+=1)a[c]=this._trimAlignValue(a[c]);return a}},_trimAlignValue:function(b){if(b<=this._valueMin())return this._valueMin();if(b>=this._valueMax())return this._valueMax();var a=this.options.step>0?this.options.step:1,c=(b-this._valueMin())%a;alignValue=b-c;if(Math.abs(c)*2>=a)alignValue+=c>0?a:-a;return parseFloat(alignValue.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max}, _refreshValue:function(){var b=this.options.range,a=this.options,c=this,e=!this._animateOff?a.animate:false,f,h={},g,i,j,l;if(this.options.values&&this.options.values.length)this.handles.each(function(k){f=(c.values(k)-c._valueMin())/(c._valueMax()-c._valueMin())*100;h[c.orientation==="horizontal"?"left":"bottom"]=f+"%";d(this).stop(1,1)[e?"animate":"css"](h,a.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(k===0)c.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},a.animate); if(k===1)c.range[e?"animate":"css"]({width:f-g+"%"},{queue:false,duration:a.animate})}else{if(k===0)c.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},a.animate);if(k===1)c.range[e?"animate":"css"]({height:f-g+"%"},{queue:false,duration:a.animate})}g=f});else{i=this.value();j=this._valueMin();l=this._valueMax();f=l!==j?(i-j)/(l-j)*100:0;h[c.orientation==="horizontal"?"left":"bottom"]=f+"%";this.handle.stop(1,1)[e?"animate":"css"](h,a.animate);if(b==="min"&&this.orientation==="horizontal")this.range.stop(1, -1)[e?"animate":"css"]({width:f+"%"},a.animate);if(b==="max"&&this.orientation==="horizontal")this.range[e?"animate":"css"]({width:100-f+"%"},{queue:false,duration:a.animate});if(b==="min"&&this.orientation==="vertical")this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},a.animate);if(b==="max"&&this.orientation==="vertical")this.range[e?"animate":"css"]({height:100-f+"%"},{queue:false,duration:a.animate})}}});d.extend(d.ui.slider,{version:"1.8.10"})})(jQuery); +1)[e?"animate":"css"]({width:f+"%"},a.animate);if(b==="max"&&this.orientation==="horizontal")this.range[e?"animate":"css"]({width:100-f+"%"},{queue:false,duration:a.animate});if(b==="min"&&this.orientation==="vertical")this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},a.animate);if(b==="max"&&this.orientation==="vertical")this.range[e?"animate":"css"]({height:100-f+"%"},{queue:false,duration:a.animate})}}});d.extend(d.ui.slider,{version:"1.8.11"})})(jQuery); ;/* - * jQuery UI Tabs 1.8.10 + * jQuery UI Tabs 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -430,10 +429,10 @@ if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1<this this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this}, load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c, "cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this}, -url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.10"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k<a.anchors.length?k:0)},b);j&&j.stopPropagation()});e=a._unrotate||(a._unrotate=!e?function(j){j.clientX&& +url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.11"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k<a.anchors.length?k:0)},b);j&&j.stopPropagation()});e=a._unrotate||(a._unrotate=!e?function(j){j.clientX&& a.rotate(null)}:function(){t=c.selected;h()});if(b){this.element.bind("tabsshow",h);this.anchors.bind(c.event+".tabs",e);h()}else{clearTimeout(a.rotation);this.element.unbind("tabsshow",h);this.anchors.unbind(c.event+".tabs",e);delete this._rotate;delete this._unrotate}return this}})})(jQuery); ;/* - * jQuery UI Datepicker 1.8.10 + * jQuery UI Datepicker 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -444,51 +443,52 @@ a.rotate(null)}:function(){t=c.selected;h()});if(b){this.element.bind("tabsshow" * Depends: * jquery.ui.core.js */ -(function(d,G){function K(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass= +(function(d,A){function K(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass= "ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su", "Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10", -minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false};d.extend(this._defaults,this.regional[""]);this.dpDiv=d('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')}function E(a,b){d.extend(a,b);for(var c in b)if(b[c]== -null||b[c]==G)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.10"}});var y=(new Date).getTime();d.extend(K.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){E(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase(); +minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false};d.extend(this._defaults,this.regional[""]);this.dpDiv=d('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')}function F(a,b){d.extend(a,b);for(var c in b)if(b[c]== +null||b[c]==A)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.11"}});var y=(new Date).getTime();d.extend(K.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){F(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase(); f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:d('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')}}, _connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&& b.append.remove();if(c){b.append=d('<span class="'+this._appendClass+'">'+c+"</span>");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("<img/>").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('<button type="button"></button>').addClass(this._triggerClass).html(f== ""?c:d("<img/>").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;g<f.length;g++)if(f[g].length>h){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a, c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b), -true);this._updateDatepicker(b);this._updateAlternate(b);b.dpDiv.show()}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d('<input type="text" id="'+("dp"+this.uuid)+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}E(a.settings,e||{}); +true);this._updateDatepicker(b);this._updateAlternate(b);b.dpDiv.show()}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d('<input type="text" id="'+("dp"+this.uuid)+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}F(a.settings,e||{}); b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass); this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup", this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().removeClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs, function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().addClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null: f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return true;return false},_getInst:function(a){try{return d.data(a,"datepicker")}catch(b){throw"Missing instance data for this datepicker";}},_optionDatepicker:function(a,b,c){var e=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?d.extend({},d.datepicker._defaults):e?b=="all"?d.extend({}, -e.settings):this._get(e,b):null;var f=b||{};if(typeof b=="string"){f={};f[b]=c}if(e){this._curInst==e&&this._hideDatepicker();var h=this._getDateDatepicker(a,true);E(e.settings,f);this._attachments(d(a),e);this._autoSize(e);this._setDateDatepicker(a,h);this._updateDatepicker(e)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){(a=this._getInst(a))&&this._updateDatepicker(a)},_setDateDatepicker:function(a,b){if(a=this._getInst(a)){this._setDate(a,b); -this._updateDatepicker(a);this._updateAlternate(a)}},_getDateDatepicker:function(a,b){(a=this._getInst(a))&&!a.inline&&this._setDateFromField(a,b);return a?this._getDate(a):null},_doKeyDown:function(a){var b=d.datepicker._getInst(a.target),c=true,e=b.dpDiv.is(".ui-datepicker-rtl");b._keyEvent=true;if(d.datepicker._datepickerShowing)switch(a.keyCode){case 9:d.datepicker._hideDatepicker();c=false;break;case 13:c=d("td."+d.datepicker._dayOverClass+":not(."+d.datepicker._currentClass+")",b.dpDiv);c[0]? -d.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,c[0]):d.datepicker._hideDatepicker();return false;case 27:d.datepicker._hideDatepicker();break;case 33:d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 34:d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 35:if(a.ctrlKey||a.metaKey)d.datepicker._clearDate(a.target);c=a.ctrlKey|| -a.metaKey;break;case 36:if(a.ctrlKey||a.metaKey)d.datepicker._gotoToday(a.target);c=a.ctrlKey||a.metaKey;break;case 37:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?+1:-1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 38:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,-7,"D");c=a.ctrlKey||a.metaKey;break;case 39:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target, -e?-1:+1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 40:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,+7,"D");c=a.ctrlKey||a.metaKey;break;default:c=false}else if(a.keyCode==36&&a.ctrlKey)d.datepicker._showDatepicker(this);else c=false;if(c){a.preventDefault();a.stopPropagation()}},_doKeyPress:function(a){var b=d.datepicker._getInst(a.target);if(d.datepicker._get(b, -"constrainInput")){b=d.datepicker._possibleChars(d.datepicker._get(b,"dateFormat"));var c=String.fromCharCode(a.charCode==G?a.keyCode:a.charCode);return a.ctrlKey||a.metaKey||c<" "||!b||b.indexOf(c)>-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true}, -_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");E(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos= -d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b, -c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){d.datepicker._datepickerShowing=true;var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.effects&& -d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this,c=d.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a));var e=a.dpDiv.find("iframe.ui-datepicker-cover");e.length&&e.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});a.dpDiv.find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout", -function(){d(this).removeClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!b._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!= --1&&d(this).addClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end();c=this._getNumberOfMonths(a);e=c[1];e>1?a.dpDiv.addClass("ui-datepicker-multi-"+e).css("width",17*e+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(c[0]!=1||c[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a, -"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var f=a.yearshtml;setTimeout(function(){f===a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);f=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))), -parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left, -b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b); -this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")}, -_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"): -0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear= -false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){var b=this._getInst(d(a)[0]);b.input&&b._selectingMonthYear&&setTimeout(function(){b.input.focus()},0);b._selectingMonthYear=!b._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay= -d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a); -else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b= -a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort, -g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=z+1<a.length&&a.charAt(z+1)==p)&&z++;return p},m=function(p){var v=o(p);p=new RegExp("^\\d{1,"+(p=="@"?14:p=="!"?20:p=="y"&&v?4:p=="o"?3:2)+"}");p=b.substring(s).match(p);if(!p)throw"Missing number at position "+s;s+=p[0].length;return parseInt(p[0],10)},n=function(p,v,H){p=o(p)?H:v;for(v=0;v<p.length;v++)if(b.substr(s,p[v].length).toLowerCase()==p[v].toLowerCase()){s+=p[v].length;return v+1}throw"Unknown name at position "+ +e.settings):this._get(e,b):null;var f=b||{};if(typeof b=="string"){f={};f[b]=c}if(e){this._curInst==e&&this._hideDatepicker();var h=this._getDateDatepicker(a,true),i=this._getMinMaxDate(e,"min"),g=this._getMinMaxDate(e,"max");F(e.settings,f);if(i!==null&&f.dateFormat!==A&&f.minDate===A)e.settings.minDate=this._formatDate(e,i);if(g!==null&&f.dateFormat!==A&&f.maxDate===A)e.settings.maxDate=this._formatDate(e,g);this._attachments(d(a),e);this._autoSize(e);this._setDateDatepicker(a,h);this._updateDatepicker(e)}}, +_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){(a=this._getInst(a))&&this._updateDatepicker(a)},_setDateDatepicker:function(a,b){if(a=this._getInst(a)){this._setDate(a,b);this._updateDatepicker(a);this._updateAlternate(a)}},_getDateDatepicker:function(a,b){(a=this._getInst(a))&&!a.inline&&this._setDateFromField(a,b);return a?this._getDate(a):null},_doKeyDown:function(a){var b=d.datepicker._getInst(a.target),c=true,e=b.dpDiv.is(".ui-datepicker-rtl"); +b._keyEvent=true;if(d.datepicker._datepickerShowing)switch(a.keyCode){case 9:d.datepicker._hideDatepicker();c=false;break;case 13:c=d("td."+d.datepicker._dayOverClass+":not(."+d.datepicker._currentClass+")",b.dpDiv);c[0]?d.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,c[0]):d.datepicker._hideDatepicker();return false;case 27:d.datepicker._hideDatepicker();break;case 33:d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"), +"M");break;case 34:d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 35:if(a.ctrlKey||a.metaKey)d.datepicker._clearDate(a.target);c=a.ctrlKey||a.metaKey;break;case 36:if(a.ctrlKey||a.metaKey)d.datepicker._gotoToday(a.target);c=a.ctrlKey||a.metaKey;break;case 37:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?+1:-1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey? +-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 38:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,-7,"D");c=a.ctrlKey||a.metaKey;break;case 39:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?-1:+1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 40:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target, ++7,"D");c=a.ctrlKey||a.metaKey;break;default:c=false}else if(a.keyCode==36&&a.ctrlKey)d.datepicker._showDatepicker(this);else c=false;if(c){a.preventDefault();a.stopPropagation()}},_doKeyPress:function(a){var b=d.datepicker._getInst(a.target);if(d.datepicker._get(b,"constrainInput")){b=d.datepicker._possibleChars(d.datepicker._get(b,"dateFormat"));var c=String.fromCharCode(a.charCode==A?a.keyCode:a.charCode);return a.ctrlKey||a.metaKey||c<" "||!b||b.indexOf(c)>-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target); +if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a); +d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");F(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-= +document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim"); +var f=d.datepicker._get(b,"duration"),h=function(){d.datepicker._datepickerShowing=true;var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst= +b}}},_updateDatepicker:function(a){var b=this,c=d.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a));var e=a.dpDiv.find("iframe.ui-datepicker-cover");e.length&&e.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});a.dpDiv.find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",function(){d(this).removeClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover"); +this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!b._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).addClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}}).end().find("."+ +this._dayOverClass+" a").trigger("mouseover").end();c=this._getNumberOfMonths(a);e=c[1];e>1?a.dpDiv.addClass("ui-datepicker-multi-"+e).css("width",17*e+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(c[0]!=1||c[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&& +a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var f=a.yearshtml;setTimeout(function(){f===a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);f=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth(): +0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a), +"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"? +"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(d.datepicker._curInst){a= +d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a= +d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear=false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c== +"M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){var b=this._getInst(d(a)[0]);b.input&&b._selectingMonthYear&&setTimeout(function(){b.input.focus()},0);b._selectingMonthYear=!b._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth= +b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker(); +this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0); +a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c? +c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=z+1<a.length&&a.charAt(z+1)==p)&&z++;return p},m=function(p){var v=o(p);p=new RegExp("^\\d{1,"+(p=="@"?14:p=="!"?20:p=="y"&&v?4:p=="o"?3:2)+"}");p=b.substring(s).match(p);if(!p)throw"Missing number at position "+s;s+=p[0].length;return parseInt(p[0],10)},n=function(p,v,H){p=o(p)?H:v;for(v=0;v<p.length;v++)if(b.substr(s,p[v].length).toLowerCase()==p[v].toLowerCase()){s+=p[v].length;return v+1}throw"Unknown name at position "+ s;},r=function(){if(b.charAt(s)!=a.charAt(z))throw"Unexpected literal at position "+s;s++},s=0,z=0;z<a.length;z++)if(k)if(a.charAt(z)=="'"&&!o("'"))k=false;else r();else switch(a.charAt(z)){case "d":l=m("d");break;case "D":n("D",f,h);break;case "o":u=m("o");break;case "m":j=m("m");break;case "M":j=n("M",i,g);break;case "y":c=m("y");break;case "@":var w=new Date(m("@"));c=w.getFullYear();j=w.getMonth()+1;l=w.getDate();break;case "!":w=new Date((m("!")-this._ticksTo1970)/1E4);c=w.getFullYear();j=w.getMonth()+ 1;l=w.getDate();break;case "'":if(o("'"))r();else k=true;break;default:r()}if(c==-1)c=(new Date).getFullYear();else if(c<100)c+=(new Date).getFullYear()-(new Date).getFullYear()%100+(c<=e?0:-100);if(u>-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}w=this._daylightSavingAdjust(new Date(c,j-1,l));if(w.getFullYear()!=c||w.getMonth()+1!=j||w.getDate()!=l)throw"Invalid date";return w},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y", RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=k+1<a.length&& a.charAt(k+1)==o)&&k++;return o},g=function(o,m,n){m=""+m;if(i(o))for(;m.length<n;)m="0"+m;return m},j=function(o,m,n,r){return i(o)?r[m]:n[m]},l="",u=false;if(b)for(var k=0;k<a.length;k++)if(u)if(a.charAt(k)=="'"&&!i("'"))u=false;else l+=a.charAt(k);else switch(a.charAt(k)){case "d":l+=g("d",b.getDate(),2);break;case "D":l+=j("D",b.getDay(),e,f);break;case "o":l+=g("o",(b.getTime()-(new Date(b.getFullYear(),0,0)).getTime())/864E5,3);break;case "m":l+=g("m",b.getMonth()+1,2);break;case "M":l+=j("M", b.getMonth(),h,c);break;case "y":l+=i("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case "@":l+=b.getTime();break;case "!":l+=b.getTime()*1E4+this._ticksTo1970;break;case "'":if(i("'"))l+="'";else u=true;break;default:l+=a.charAt(k)}return l},_possibleChars:function(a){for(var b="",c=false,e=function(h){(h=f+1<a.length&&a.charAt(f+1)==h)&&f++;return h},f=0;f<a.length;f++)if(c)if(a.charAt(f)=="'"&&!e("'"))c=false;else b+=a.charAt(f);else switch(a.charAt(f)){case "d":case "m":case "y":case "@":b+= -"0123456789";break;case "D":case "M":return null;case "'":if(e("'"))b+="'";else c=true;break;default:b+=a.charAt(f)}return b},_get:function(a,b){return a.settings[b]!==G?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),e=a.lastVal=a.input?a.input.val():null,f,h;f=h=this._getDefaultDate(a);var i=this._getFormatConfig(a);try{f=this.parseDate(c,e,i)||h}catch(g){this.log(g);e=b?"":e}a.selectedDay=f.getDate();a.drawMonth=a.selectedMonth= +"0123456789";break;case "D":case "M":return null;case "'":if(e("'"))b+="'";else c=true;break;default:b+=a.charAt(f)}return b},_get:function(a,b){return a.settings[b]!==A?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),e=a.lastVal=a.input?a.input.val():null,f,h;f=h=this._getDefaultDate(a);var i=this._getFormatConfig(a);try{f=this.parseDate(c,e,i)||h}catch(g){this.log(g);e=b?"":e}a.selectedDay=f.getDate();a.drawMonth=a.selectedMonth= f.getMonth();a.drawYear=a.selectedYear=f.getFullYear();a.currentDay=e?f.getDate():0;a.currentMonth=e?f.getMonth():0;a.currentYear=e?f.getFullYear():0;this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var e=function(h){var i=new Date;i.setDate(i.getDate()+h);return i},f=function(h){try{return d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),h,d.datepicker._getFormatConfig(a))}catch(i){}var g= (h.toLowerCase().match(/^c/)?d.datepicker._getDate(a):null)||new Date,j=g.getFullYear(),l=g.getMonth();g=g.getDate();for(var u=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,k=u.exec(h);k;){switch(k[2]||"d"){case "d":case "D":g+=parseInt(k[1],10);break;case "w":case "W":g+=parseInt(k[1],10)*7;break;case "m":case "M":l+=parseInt(k[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(j,l));break;case "y":case "Y":j+=parseInt(k[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(j,l));break}k=u.exec(h)}return new Date(j, l,g)};if(b=(b=b==null||b===""?c:typeof b=="string"?f(b):typeof b=="number"?isNaN(b)?c:e(b):new Date(b.getTime()))&&b.toString()=="Invalid Date"?c:b){b.setHours(0);b.setMinutes(0);b.setSeconds(0);b.setMilliseconds(0)}return this._daylightSavingAdjust(b)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay= @@ -498,12 +498,12 @@ this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.g (c?"e":"w")+'">'+n+"</span></a>":f?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>";var r=this._get(a,"nextText");r=!h?r:this.formatDate(r,this._daylightSavingAdjust(new Date(m,g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+y+".datepicker._adjustDate('#"+a.id+"', +"+j+", 'M');\" title=\""+r+'"><span class="ui-icon ui-icon-circle-triangle-'+ (c?"w":"e")+'">'+r+"</span></a>":f?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+r+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+r+"</span></a>";j=this._get(a,"currentText");r=this._get(a,"gotoCurrent")&&a.currentDay?u:b;j=!h?j:this.formatDate(j,r,this._getFormatConfig(a));h=!a.inline?'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+y+'.datepicker._hideDatepicker();">'+this._get(a, "closeText")+"</button>":"";e=e?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?h:"")+(this._isInRange(a,r)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+y+".datepicker._gotoToday('#"+a.id+"');\">"+j+"</button>":"")+(c?"":h)+"</div>":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");r=this._get(a,"dayNames");this._get(a,"dayNamesShort");var s=this._get(a,"dayNamesMin"),z= -this._get(a,"monthNames"),w=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),v=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var L=this._getDefaultDate(a),I="",C=0;C<i[0];C++){for(var M="",D=0;D<i[1];D++){var N=this._daylightSavingAdjust(new Date(m,g,a.selectedDay)),t=" ui-corner-all",x="";if(l){x+='<div class="ui-datepicker-group';if(i[1]>1)switch(D){case 0:x+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]- -1:x+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:x+=" ui-datepicker-group-middle";t="";break}x+='">'}x+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+t+'">'+(/all|left/.test(t)&&C==0?c?f:n:"")+(/all|right/.test(t)&&C==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,C>0||D>0,z,w)+'</div><table class="ui-datepicker-calendar"><thead><tr>';var A=j?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(t=0;t<7;t++){var q= -(t+h)%7;A+="<th"+((t+h+6)%7>=5?' class="ui-datepicker-week-end"':"")+'><span title="'+r[q]+'">'+s[q]+"</span></th>"}x+=A+"</tr></thead><tbody>";A=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,A);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;A=l?6:Math.ceil((t+A)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var O=0;O<A;O++){x+="<tr>";var P=!j?"":'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(q)+"</td>";for(t=0;t<7;t++){var F= -p?p.apply(a.input?a.input[0]:null,[q]):[true,""],B=q.getMonth()!=g,J=B&&!H||!F[0]||k&&q<k||o&&q>o;P+='<td class="'+((t+h+6)%7>=5?" ui-datepicker-week-end":"")+(B?" ui-datepicker-other-month":"")+(q.getTime()==N.getTime()&&g==a.selectedMonth&&a._keyEvent||L.getTime()==q.getTime()&&L.getTime()==N.getTime()?" "+this._dayOverClass:"")+(J?" "+this._unselectableClass+" ui-state-disabled":"")+(B&&!v?"":" "+F[1]+(q.getTime()==u.getTime()?" "+this._currentClass:"")+(q.getTime()==b.getTime()?" ui-datepicker-today": -""))+'"'+((!B||v)&&F[2]?' title="'+F[2]+'"':"")+(J?"":' onclick="DP_jQuery_'+y+".datepicker._selectDay('#"+a.id+"',"+q.getMonth()+","+q.getFullYear()+', this);return false;"')+">"+(B&&!v?" ":J?'<span class="ui-state-default">'+q.getDate()+"</span>":'<a class="ui-state-default'+(q.getTime()==b.getTime()?" ui-state-highlight":"")+(q.getTime()==u.getTime()?" ui-state-active":"")+(B?" ui-priority-secondary":"")+'" href="#">'+q.getDate()+"</a>")+"</td>";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+= -P+"</tr>"}g++;if(g>11){g=0;m++}x+="</tbody></table>"+(l?"</div>"+(i[0]>0&&D==i[1]-1?'<div class="ui-datepicker-row-break"></div>':""):"");M+=x}I+=M}I+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':"");a._keyEvent=false;return I},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='<div class="ui-datepicker-title">', +this._get(a,"monthNames"),w=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),v=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var L=this._getDefaultDate(a),I="",D=0;D<i[0];D++){for(var M="",E=0;E<i[1];E++){var N=this._daylightSavingAdjust(new Date(m,g,a.selectedDay)),t=" ui-corner-all",x="";if(l){x+='<div class="ui-datepicker-group';if(i[1]>1)switch(E){case 0:x+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]- +1:x+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:x+=" ui-datepicker-group-middle";t="";break}x+='">'}x+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+t+'">'+(/all|left/.test(t)&&D==0?c?f:n:"")+(/all|right/.test(t)&&D==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,D>0||E>0,z,w)+'</div><table class="ui-datepicker-calendar"><thead><tr>';var B=j?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(t=0;t<7;t++){var q= +(t+h)%7;B+="<th"+((t+h+6)%7>=5?' class="ui-datepicker-week-end"':"")+'><span title="'+r[q]+'">'+s[q]+"</span></th>"}x+=B+"</tr></thead><tbody>";B=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,B);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;B=l?6:Math.ceil((t+B)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var O=0;O<B;O++){x+="<tr>";var P=!j?"":'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(q)+"</td>";for(t=0;t<7;t++){var G= +p?p.apply(a.input?a.input[0]:null,[q]):[true,""],C=q.getMonth()!=g,J=C&&!H||!G[0]||k&&q<k||o&&q>o;P+='<td class="'+((t+h+6)%7>=5?" ui-datepicker-week-end":"")+(C?" ui-datepicker-other-month":"")+(q.getTime()==N.getTime()&&g==a.selectedMonth&&a._keyEvent||L.getTime()==q.getTime()&&L.getTime()==N.getTime()?" "+this._dayOverClass:"")+(J?" "+this._unselectableClass+" ui-state-disabled":"")+(C&&!v?"":" "+G[1]+(q.getTime()==u.getTime()?" "+this._currentClass:"")+(q.getTime()==b.getTime()?" ui-datepicker-today": +""))+'"'+((!C||v)&&G[2]?' title="'+G[2]+'"':"")+(J?"":' onclick="DP_jQuery_'+y+".datepicker._selectDay('#"+a.id+"',"+q.getMonth()+","+q.getFullYear()+', this);return false;"')+">"+(C&&!v?" ":J?'<span class="ui-state-default">'+q.getDate()+"</span>":'<a class="ui-state-default'+(q.getTime()==b.getTime()?" ui-state-highlight":"")+(q.getTime()==u.getTime()?" ui-state-active":"")+(C?" ui-priority-secondary":"")+'" href="#">'+q.getDate()+"</a>")+"</td>";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+= +P+"</tr>"}g++;if(g>11){g=0;m++}x+="</tbody></table>"+(l?"</div>"+(i[0]>0&&E==i[1]-1?'<div class="ui-datepicker-row-break"></div>':""):"");M+=x}I+=M}I+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':"");a._keyEvent=false;return I},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='<div class="ui-datepicker-title">', o="";if(h||!j)o+='<span class="ui-datepicker-month">'+i[b]+"</span>";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+y+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" onclick=\"DP_jQuery_"+y+".datepicker._clickMonthYear('#"+a.id+"');\">";for(var n=0;n<12;n++)if((!i||n>=e.getMonth())&&(!m||n<=f.getMonth()))o+='<option value="'+n+'"'+(n==b?' selected="selected"':"")+">"+g[n]+"</option>";o+="</select>"}u||(k+=o+(h||!(j&& l)?" ":""));a.yearshtml="";if(h||!l)k+='<span class="ui-datepicker-year">'+c+"</span>";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)?r:s};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+y+".datepicker._selectMonthYear('#"+ a.id+"', this, 'Y');\" onclick=\"DP_jQuery_"+y+".datepicker._clickMonthYear('#"+a.id+"');\">";b<=g;b++)a.yearshtml+='<option value="'+b+'"'+(b==c?' selected="selected"':"")+">"+b+"</option>";a.yearshtml+="</select>";if(d.browser.mozilla)k+='<select class="ui-datepicker-year"><option value="'+c+'" selected="selected">'+c+"</option></select>";else{k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?" ":"")+o;k+="</div>";return k},_adjustInstDate:function(a,b,c){var e= @@ -512,9 +512,9 @@ a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a, "dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker= function(a){if(!this.length)return this;if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker, -[this[0]].concat(b));return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new K;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.10";window["DP_jQuery_"+y]=d})(jQuery); +[this[0]].concat(b));return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new K;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.11";window["DP_jQuery_"+y]=d})(jQuery); ;/* - * jQuery UI Progressbar 1.8.10 + * jQuery UI Progressbar 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -528,9 +528,9 @@ function(a){if(!this.length)return this;if(!d.datepicker.initialized){d(document */ (function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100* -this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.10"})})(jQuery); +this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.11"})})(jQuery); ;/* - * jQuery UI Effects 1.8.10 + * jQuery UI Effects 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -547,7 +547,7 @@ a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number 211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},r=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b, d){if(f.isFunction(b)){d=b;b=null}return this.queue("fx",function(){var e=f(this),g=e.attr("style")||" ",h=q(p.call(this)),l,v=e.attr("className");f.each(r,function(w,i){c[i]&&e[i+"Class"](c[i])});l=q(p.call(this));e.attr("className",v);e.animate(u(h,l),a,b,function(){f.each(r,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments)});h=f.queue(this);l=h.splice(h.length-1,1)[0]; h.splice(1,0,l);f.dequeue(this)})};f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c, -a):f.effects.animateClass.apply(this,[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.10",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c, +a):f.effects.animateClass.apply(this,[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.11",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c, a){var b;switch(c[0]){case "top":b=0;break;case "middle":b=0.5;break;case "bottom":b=1;break;default:b=c[0]/a.height}switch(c[1]){case "left":c=0;break;case "center":c=0.5;break;case "right":c=1;break;default:c=c[1]/a.width}return{x:c,y:b}},createWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent();var a={width:c.outerWidth(true),height:c.outerHeight(true),"float":c.css("float")},b=f("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent", border:"none",margin:0,padding:0});c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c); return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});return d.call(this,b)},_show:f.fn.show,show:function(c){if(m(c))return this._show.apply(this,arguments); @@ -560,7 +560,7 @@ g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2* h);if(a<1)return-0.5*h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)+b;return h*Math.pow(2,-10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)*0.5+d+b},easeInBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*(a/=e)*a*((g+1)*a-g)+b},easeOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*((a=a/e-1)*a*((g+1)*a+g)+1)+b},easeInOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;if((a/=e/2)<1)return d/2*a*a*(((g*=1.525)+1)*a-g)+b;return d/2*((a-=2)*a*(((g*=1.525)+1)*a+g)+2)+b},easeInBounce:function(c, a,b,d,e){return d-f.easing.easeOutBounce(c,e-a,0,d,e)+b},easeOutBounce:function(c,a,b,d,e){return(a/=e)<1/2.75?d*7.5625*a*a+b:a<2/2.75?d*(7.5625*(a-=1.5/2.75)*a+0.75)+b:a<2.5/2.75?d*(7.5625*(a-=2.25/2.75)*a+0.9375)+b:d*(7.5625*(a-=2.625/2.75)*a+0.984375)+b},easeInOutBounce:function(c,a,b,d,e){if(a<e/2)return f.easing.easeInBounce(c,a*2,0,d,e)*0.5+b;return f.easing.easeOutBounce(c,a*2-e,0,d,e)*0.5+d*0.5+b}})}(jQuery); ;/* - * jQuery UI Effects Blind 1.8.10 + * jQuery UI Effects Blind 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -574,7 +574,7 @@ a,b,d,e){return d-f.easing.easeOutBounce(c,e-a,0,d,e)+b},easeOutBounce:function( (function(b){b.effects.blind=function(c){return this.queue(function(){var a=b(this),g=["position","top","bottom","left","right"],f=b.effects.setMode(a,c.options.mode||"hide"),d=c.options.direction||"vertical";b.effects.save(a,g);a.show();var e=b.effects.createWrapper(a).css({overflow:"hidden"}),h=d=="vertical"?"height":"width";d=d=="vertical"?e.height():e.width();f=="show"&&e.css(h,0);var i={};i[h]=f=="show"?d:0;e.animate(i,c.duration,c.options.easing,function(){f=="hide"&&a.hide();b.effects.restore(a, g);b.effects.removeWrapper(a);c.callback&&c.callback.apply(a[0],arguments);a.dequeue()})})}})(jQuery); ;/* - * jQuery UI Effects Bounce 1.8.10 + * jQuery UI Effects Bounce 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -589,7 +589,7 @@ g);b.effects.removeWrapper(a);c.callback&&c.callback.apply(a[0],arguments);a.deq 3);if(h=="show")a.css("opacity",0).css(f,d=="pos"?-c:c);if(h=="hide")c/=m*2;h!="hide"&&m--;if(h=="show"){var g={opacity:1};g[f]=(d=="pos"?"+=":"-=")+c;a.animate(g,i/2,b.options.easing);c/=2;m--}for(g=0;g<m;g++){var j={},k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing);c=h=="hide"?c*2:c/2}if(h=="hide"){g={opacity:0};g[f]=(d=="pos"?"-=":"+=")+c;a.animate(g,i/2,b.options.easing,function(){a.hide();e.effects.restore(a,l);e.effects.removeWrapper(a); b.callback&&b.callback.apply(this,arguments)})}else{j={};k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing,function(){e.effects.restore(a,l);e.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments)})}a.queue("fx",function(){a.dequeue()});a.dequeue()})}})(jQuery); ;/* - * jQuery UI Effects Clip 1.8.10 + * jQuery UI Effects Clip 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -603,7 +603,7 @@ b.callback&&b.callback.apply(this,arguments)})}else{j={};k={};j[f]=(d=="pos"?"-= (function(b){b.effects.clip=function(e){return this.queue(function(){var a=b(this),i=["position","top","bottom","left","right","height","width"],f=b.effects.setMode(a,e.options.mode||"hide"),c=e.options.direction||"vertical";b.effects.save(a,i);a.show();var d=b.effects.createWrapper(a).css({overflow:"hidden"});d=a[0].tagName=="IMG"?d:a;var g={size:c=="vertical"?"height":"width",position:c=="vertical"?"top":"left"};c=c=="vertical"?d.height():d.width();if(f=="show"){d.css(g.size,0);d.css(g.position, c/2)}var h={};h[g.size]=f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h,{queue:false,duration:e.duration,easing:e.options.easing,complete:function(){f=="hide"&&a.hide();b.effects.restore(a,i);b.effects.removeWrapper(a);e.callback&&e.callback.apply(a[0],arguments);a.dequeue()}})})}})(jQuery); ;/* - * jQuery UI Effects Drop 1.8.10 + * jQuery UI Effects Drop 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -617,7 +617,7 @@ c/2)}var h={};h[g.size]=f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h, (function(c){c.effects.drop=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right","opacity"],e=c.effects.setMode(a,d.options.mode||"hide"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a);var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true})/2:a.outerWidth({margin:true})/2);if(e=="show")a.css("opacity",0).css(f,b=="pos"?-g:g);var i={opacity:e== "show"?1:0};i[f]=(e=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); ;/* - * jQuery UI Effects Explode 1.8.10 + * jQuery UI Effects Explode 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -632,7 +632,7 @@ c/2)}var h={};h[g.size]=f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h, 0;f<d;f++)b.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); ;/* - * jQuery UI Effects Fade 1.8.10 + * jQuery UI Effects Fade 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -645,7 +645,7 @@ e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.m */ (function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery); ;/* - * jQuery UI Effects Fold 1.8.10 + * jQuery UI Effects Fold 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -659,7 +659,7 @@ e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.m (function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1], 10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); ;/* - * jQuery UI Effects Highlight 1.8.10 + * jQuery UI Effects Highlight 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -673,7 +673,7 @@ e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.m (function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); ;/* - * jQuery UI Effects Pulsate 1.8.10 + * jQuery UI Effects Pulsate 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -687,7 +687,7 @@ this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments (function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c<times;c++){b.animate({opacity:animateTo},duration,a.options.easing);animateTo=(animateTo+1)%2}b.animate({opacity:animateTo},duration, a.options.easing,function(){animateTo==0&&b.hide();a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()}).dequeue()})}})(jQuery); ;/* - * jQuery UI Effects Scale 1.8.10 + * jQuery UI Effects Scale 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -707,7 +707,7 @@ a.css("overflow","hidden").css(a.from);if(m=="content"||m=="both"){f=f.concat([" child.to=c.effects.setTransition(child,f,d.to.y,child.to)}if(d.from.x!=d.to.x){child.from=c.effects.setTransition(child,k,d.from.x,child.from);child.to=c.effects.setTransition(child,k,d.to.x,child.to)}child.css(child.from);child.animate(child.to,b.duration,b.options.easing,function(){n&&c.effects.restore(child,h)})})}a.animate(a.to,{queue:false,duration:b.duration,easing:b.options.easing,complete:function(){a.to.opacity===0&&a.css("opacity",a.from.opacity);p=="hide"&&a.hide();c.effects.restore(a, n?e:g);c.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); ;/* - * jQuery UI Effects Shake 1.8.10 + * jQuery UI Effects Shake 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -721,7 +721,7 @@ n?e:g);c.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments);a (function(d){d.effects.shake=function(a){return this.queue(function(){var b=d(this),j=["position","top","bottom","left","right"];d.effects.setMode(b,a.options.mode||"effect");var c=a.options.direction||"left",e=a.options.distance||20,l=a.options.times||3,f=a.duration||a.options.duration||140;d.effects.save(b,j);b.show();d.effects.createWrapper(b);var g=c=="up"||c=="down"?"top":"left",h=c=="up"||c=="left"?"pos":"neg";c={};var i={},k={};c[g]=(h=="pos"?"-=":"+=")+e;i[g]=(h=="pos"?"+=":"-=")+e*2;k[g]= (h=="pos"?"-=":"+=")+e*2;b.animate(c,f,a.options.easing);for(e=1;e<l;e++)b.animate(i,f,a.options.easing).animate(k,f,a.options.easing);b.animate(i,f,a.options.easing).animate(c,f/2,a.options.easing,function(){d.effects.restore(b,j);d.effects.removeWrapper(b);a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()});b.dequeue()})}})(jQuery); ;/* - * jQuery UI Effects Slide 1.8.10 + * jQuery UI Effects Slide 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -735,7 +735,7 @@ n?e:g);c.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments);a (function(c){c.effects.slide=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right"],f=c.effects.setMode(a,d.options.mode||"show"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a).css({overflow:"hidden"});var g=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var e=d.options.distance||(g=="top"?a.outerHeight({margin:true}):a.outerWidth({margin:true}));if(f=="show")a.css(g,b=="pos"?isNaN(e)?"-"+e:-e:e); var i={};i[g]=(f=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+e;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){f=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); ;/* - * jQuery UI Effects Transfer 1.8.10 + * jQuery UI Effects Transfer 1.8.11 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/data/js/restart.js b/data/js/restart.js new file mode 100644 index 0000000000000000000000000000000000000000..86160c9482b6fdc6bfa61ba2e0bc0895323f0130 --- /dev/null +++ b/data/js/restart.js @@ -0,0 +1,59 @@ +var is_alive_url = sbRoot+'/home/is_alive'; +var timeout_id; +var current_pid = ''; +var num_restart_waits = 0; + +function is_alive() { + timeout_id = 0; + $.get(is_alive_url, function(data) { + + // if it's still initalizing then just wait and try again + if (data == 'nope') { + $('#shut_down_loading').hide(); + $('#shut_down_success').show(); + $('#restart_message').show(); + setTimeout('is_alive()', 1000); + } else { + // if this is before we've even shut down then just try again later + if (current_pid == '' || data == current_pid) { + current_pid = data; + setTimeout(is_alive, 1000); + + // if we're ready to go then refresh the page which'll forward to /home + } else { + $('#restart_loading').hide(); + $('#restart_success').show(); + $('#refresh_message').show(); + location.reload(); + } + } + }); +} + +$(document).ready(function() +{ + + is_alive(); + + $('#shut_down_message').ajaxError(function(e, jqxhr, settings, exception) { + if (settings.url != is_alive_url) + return; + num_restart_waits += 1; + + $('#shut_down_loading').hide(); + $('#shut_down_success').show(); + $('#restart_message').show(); + + // if it is taking forever just give up + if (num_restart_waits > 90) { + $('#restart_loading').hide(); + $('#restart_failure').show(); + $('#restart_fail_message').show(); + return; + } + + if (timeout_id == 0) + timeout_id = setTimeout('is_alive()', 1000); + }); + +}); diff --git a/data/js/rootDirs.js b/data/js/rootDirs.js index 2ab7cd37a8a0c4da447b883ad8bec2971e855c5c..1947646b404d96f947aef43c8048f8531ca3cff6 100644 --- a/data/js/rootDirs.js +++ b/data/js/rootDirs.js @@ -20,7 +20,7 @@ $(document).ready(function(){ refreshRootDirs(); - $.get('/config/general/saveRootDirs', { rootDirString: $('#rootDirText').val() }); + $.get(sbRoot+'/config/general/saveRootDirs', { rootDirString: $('#rootDirText').val() }); } @@ -37,7 +37,7 @@ $(document).ready(function(){ } refreshRootDirs(); - $.get('/config/general/saveRootDirs', {rootDirString: $('#rootDirText').val()}); + $.get(sbRoot+'/config/general/saveRootDirs', {rootDirString: $('#rootDirText').val()}); } $('#addRootDir').click(function(){$(this).nFileBrowser(addRootDir)}); @@ -73,14 +73,14 @@ $(document).ready(function(){ } refreshRootDirs(); - $.get('/config/general/saveRootDirs', {rootDirString: $('#rootDirText').val()}); + $.get(sbRoot+'/config/general/saveRootDirs', {rootDirString: $('#rootDirText').val()}); }); $('#defaultRootDir').click(function(){ if ($("#rootDirs option:selected").length) setDefault($("#rootDirs option:selected").attr('id')); refreshRootDirs(); - $.get('/config/general/saveRootDirs', 'rootDirString='+$('#rootDirText').val()); + $.get(sbRoot+'/config/general/saveRootDirs', 'rootDirString='+$('#rootDirText').val()); }); function setDefault(which, force){ diff --git a/init.fedora b/init.fedora new file mode 100755 index 0000000000000000000000000000000000000000..d763c373f58c58f7e2f37612d49448e60c6f170b --- /dev/null +++ b/init.fedora @@ -0,0 +1,86 @@ +### BEGIN INIT INFO +# Provides: Sick Beard application instance +# Required-Start: $all +# Required-Stop: $all +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: starts Sick Beard +# Description: starts Sick Beard +### END INIT INFO + +# Source function library. +. /etc/init.d/functions + +# Source SickBeard configuration +if [ -f /etc/sysconfig/sickbeard ]; then + . /etc/sysconfig/sickbeard +fi + +prog=sickbeard +lockfile=/var/lock/subsys/$prog + +## Edit user configuation in /etc/sysconfig/sickbeard to change +## the defaults +username=${SB_USER-sickbeard} +homedir=${SB_HOME-/opt/sickbeard} +pidfile=${SB_PIDFILE-/var/run/sickbeard/sickbeard.pid} +nice=${SB_NICE-} +## + +pidpath=`dirname ${pidfile}` +options=" --daemon --pidfile=${pidfile}" + +# create PID directory if not exist and ensure the SickBeard user can write to it +if [ ! -d $pidpath ]; then + mkdir -p $pidpath + chown $username $pidpath +fi + +start() { + # Start daemon. + echo -n $"Starting $prog: " + daemon --user=${username} --pidfile=${pidfile} ${nice} python ${homedir}/SickBeard.py ${options} + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch $lockfile + return $RETVAL +} + +stop() { + echo -n $"Shutting down $prog: " + killproc -p ${pidfile} python + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && rm -f $lockfile + return $RETVAL +} + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status $prog + ;; + restart|force-reload) + stop + start + ;; + try-restart|condrestart) + if status $prog > /dev/null; then + stop + start + fi + ;; + reload) + exit 3 + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|try-restart|force-reload}" + exit 2 +esac + diff --git a/initscript b/init.ubuntu similarity index 70% rename from initscript rename to init.ubuntu index 85619f86133799593c312f14528a6e8c28f9df7e..6509898c04075f82b3f45777978ece232ec26398 100755 --- a/initscript +++ b/init.ubuntu @@ -17,8 +17,12 @@ APP_PATH=PATH_TO_SICKBEARD_DIRECTORY # path to python bin DAEMON=/usr/bin/python +# Path to store PID file +PID_FILE=/var/run/sickbeard/sickbeard.pid +PID_PATH=`dirname $PID_FILE` + # startup args -DAEMON_OPTS=" SickBeard.py -q" +DAEMON_OPTS=" SickBeard.py -q --daemon --pidfile=${PID_FILE}" # script name NAME=sickbeard @@ -29,18 +33,21 @@ DESC=SickBeard # user RUN_AS=SICKBEARD_USER -PID_FILE=/var/run/sickbeard.pid - ############### END EDIT ME ################## test -x $DAEMON || exit 0 set -e +if [ ! -d $PID_PATH ]; then + mkdir -p $PID_PATH + chown $RUN_AS $PID_PATH +fi + case "$1" in start) echo "Starting $DESC" - start-stop-daemon -d $APP_PATH -c $RUN_AS --start --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS + start-stop-daemon -d $APP_PATH -c $RUN_AS --start --pidfile $PID_FILE --exec $DAEMON -- $DAEMON_OPTS ;; stop) echo "Stopping $DESC" @@ -51,7 +58,7 @@ case "$1" in echo "Restarting $DESC" start-stop-daemon --stop --pidfile $PID_FILE sleep 15 - start-stop-daemon -d $APP_PATH -c $RUN_AS --start --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS + start-stop-daemon -d $APP_PATH -c $RUN_AS --start --pidfile $PID_FILE --exec $DAEMON -- $DAEMON_OPTS ;; *) N=/etc/init.d/$NAME diff --git a/lib/httplib2/__init__.py b/lib/httplib2/__init__.py index 4285b1c2ed4a26b187c5b0f46cd9733b8639b1de..39b78f4d1eeae608d99602ac2c33583990eefa21 100644 --- a/lib/httplib2/__init__.py +++ b/lib/httplib2/__init__.py @@ -869,7 +869,9 @@ the same interface as FileCache.""" conn.close() raise ServerNotFoundError("Unable to find the server at %s" % conn.host) except socket.error, e: - if e.errno == errno.ECONNREFUSED: # Connection refused + if not hasattr(e, 'errno'): # I don't know what this is so lets raise it if it happens + raise + elif e.errno == errno.ECONNREFUSED: # Connection refused raise # Just because the server closed the connection doesn't apparently mean # that the server didn't send a response. diff --git a/lib/tvdb_api/tvdb_api.py b/lib/tvdb_api/tvdb_api.py index 8e4e17ba0d6079821f1c7fab0ca51dddcf26aa0c..00902c91dca3a130fab529c467c2856cd204595a 100644 --- a/lib/tvdb_api/tvdb_api.py +++ b/lib/tvdb_api/tvdb_api.py @@ -28,6 +28,7 @@ import warnings import logging import datetime import time +import traceback try: import xml.etree.cElementTree as ElementTree @@ -71,9 +72,9 @@ def clean_cache(cachedir): # Does our cachedir exists if not os.path.isdir(cachedir): - log().debug("Told to clean cache dir %s but it does not exist" % - cachedir) - return + log().debug("Told to clean cache dir %s but it does not exist" % + cachedir) + return now = time.time() day = 86400 @@ -81,14 +82,14 @@ def clean_cache(cachedir): files = os.listdir(cachedir) for file in files: - ffile = os.path.join(cachedir,file) - # If modified time is > 24 hrs ago, die! - # log().debug("Comparing %s mtime" % ffile) - if now - os.stat(ffile).st_mtime > day: - try: - os.remove(ffile) - except: - raise tvdb_error("Couldn't remove %s" % ffile) + ffile = os.path.join(cachedir,file) + # If modified time is > 24 hrs ago, die! + # log().debug("Comparing %s mtime" % ffile) + if now - os.stat(ffile).st_mtime > day: + try: + os.remove(ffile) + except: + raise tvdb_error("Couldn't remove %s" % ffile) class ShowContainer(dict): """Simple dict that holds a series of Show instances @@ -309,7 +310,7 @@ class Tvdb: select_first = False, debug = False, cache = True, - cache_dir = False, + cache_dir = False, banners = False, actors = False, custom_ui = None, @@ -336,11 +337,11 @@ class Tvdb: cache (True/False/Recache): Retrieved XML are persisted to to disc. If true, stores in tvdb_api folder under directory specified by cache_dir. If False, disables - caching entirely. If Refresh, requests a fresh copy and caches it - for further use. + caching entirely. If Refresh, requests a fresh copy and caches it + for further use. - cache_dir (str/unicode): - Location for the cache directory, defaults to systems TEMP_DIR. + cache_dir (str/unicode): + Location for the cache directory, defaults to systems TEMP_DIR. banners (True/False): Retrieves the banners for a show. These are accessed @@ -416,20 +417,20 @@ class Tvdb: self.config['search_all_languages'] = search_all_languages - if cache_dir: - self.config['cache_location'] = cache_dir - else: + if cache_dir: + self.config['cache_location'] = cache_dir + else: self.config['cache_location'] = self._getTempDir() - if cache: - self.config['cache_enabled'] = cache - else: + if cache: + self.config['cache_enabled'] = cache + else: self.config['cache_enabled'] = False - # Clean cache, this might need to be moved elsewhere - if self.config['cache_enabled'] and self.config['cache_location']: - # log().debug("Cleaning cache %s " % self.config['cache_location']) - clean_cache(self.config['cache_location']) + # Clean cache, this might need to be moved elsewhere + if self.config['cache_enabled'] and self.config['cache_location']: + # log().debug("Cleaning cache %s " % self.config['cache_location']) + clean_cache(self.config['cache_location']) self.config['banners_enabled'] = banners self.config['actors_enabled'] = actors @@ -496,11 +497,11 @@ class Tvdb: def _loadUrl(self, url, recache = False): global lastTimeout - # Do we want caching? - if self.config['cache_enabled'] and self.config['cache_location']: - h_cache = self.config['cache_location'] - else: - h_cache = False + # Do we want caching? + if self.config['cache_enabled'] and self.config['cache_location']: + h_cache = self.config['cache_location'] + else: + h_cache = False if self.config['http_proxy'] != '' and self.config['http_proxy'] != None and socks != None: parsedURI = socks.parseproxyuri(self.config['http_proxy']) @@ -508,12 +509,12 @@ class Tvdb: else: h = httplib2.Http(cache=h_cache) - # Handle a recache request, this will get fresh content and cache again - # if enabled - if str(self.config['cache_enabled']).lower() == 'recache' or recache: - h_header = {'cache-control':'no-cache'} - else: - h_header = {} + # Handle a recache request, this will get fresh content and cache again + # if enabled + if str(self.config['cache_enabled']).lower() == 'recache' or recache: + h_header = {'cache-control':'no-cache'} + else: + h_header = {} try: log().debug("Retrieving URL %s" % url) @@ -522,10 +523,10 @@ class Tvdb: if not str(errormsg).startswith('HTTP Error'): lastTimeout = datetime.datetime.now() raise tvdb_error("Could not connect to server %s: %s" % (url, errormsg)) - except (AttributeError), errormsg: - raise tvdb_error("Silly upstream module timed out and didn't give a \ - good error. Failed hitting %s, error message: %s" % (url, - str(errormsg))) + except (AttributeError), errormsg: + raise tvdb_error("Silly upstream module timed out and didn't give a \ + good error. Failed hitting %s, error message: %s" % (url, + str(errormsg))) #end try return str(resp) diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 3266451cb4a5ecc14443f5ff331359c9761215e5..930c36b0f6928a3e06df6ec7867638803b235f3d 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -30,7 +30,7 @@ from threading import Lock # apparently py2exe won't build these unless they're imported somewhere from sickbeard import providers, metadata -from providers import ezrss, nzbs_org, nzbmatrix, tvbinz, nzbsrus, newznab, womble, newzbin +from providers import ezrss, tvtorrents, nzbs_org, nzbmatrix, tvbinz, nzbsrus, newznab, womble, newzbin from sickbeard import searchCurrent, searchBacklog, showUpdater, versionChecker, properFinder, autoPostProcesser from sickbeard import helpers, db, exceptions, show_queue, search_queue, scheduler @@ -38,7 +38,7 @@ from sickbeard import logger from sickbeard.common import * -from sickbeard.databases import mainDB +from sickbeard.databases import mainDB, cache_db from lib.configobj import ConfigObj @@ -51,7 +51,7 @@ PID = None CFG = None CONFIG_FILE = None -PROG_DIR = None +PROG_DIR = '.' MY_FULLNAME = None MY_NAME = None MY_ARGS = [] @@ -81,6 +81,7 @@ VERSION_NOTIFY = None INIT_LOCK = Lock() __INITIALIZED__ = False +started = False LOG_DIR = None @@ -122,6 +123,9 @@ TVDB_API_KEY = '9DAF49C96CBF8DAC' TVDB_BASE_URL = None TVDB_API_PARMS = {} +USE_NZBS = None +USE_TORRENTS = None + NZB_METHOD = None NZB_DIR = None USENET_RETENTION = None @@ -135,6 +139,11 @@ MIN_SEARCH_FREQUENCY = 10 DEFAULT_SEARCH_FREQUENCY = 60 EZRSS = False +TVTORRENTS = False +TVTORRENTS_DIGEST = None +TVTORRENTS_HASH = None + + TORRENT_DIR = None RENAME_EPISODES = False @@ -173,6 +182,10 @@ SAB_APIKEY = None SAB_CATEGORY = None SAB_HOST = None +NZBGET_PASSWORD = None +NZBGET_CATEGORY = None +NZBGET_HOST = None + USE_XBMC = False XBMC_NOTIFY_ONSNATCH = False XBMC_NOTIFY_ONDOWNLOAD = False @@ -182,6 +195,15 @@ XBMC_HOST = None XBMC_USERNAME = None XBMC_PASSWORD = None +USE_PLEX = False +PLEX_NOTIFY_ONSNATCH = False +PLEX_NOTIFY_ONDOWNLOAD = False +PLEX_UPDATE_LIBRARY = False +PLEX_SERVER_HOST = None +PLEX_HOST = None +PLEX_USERNAME = None +PLEX_PASSWORD = None + USE_GROWL = False GROWL_NOTIFY_ONSNATCH = False GROWL_NOTIFY_ONDOWNLOAD = False @@ -212,6 +234,11 @@ USE_LIBNOTIFY = False LIBNOTIFY_NOTIFY_ONSNATCH = False LIBNOTIFY_NOTIFY_ONDOWNLOAD = False +USE_NMJ = False +NMJ_HOST = None +NMJ_DATABASE = None +NMJ_MOUNT = None + COMING_EPS_LAYOUT = None COMING_EPS_DISPLAY_PAUSED = None @@ -311,15 +338,18 @@ def initialize(consoleLogging=True): with INIT_LOCK: global LOG_DIR, WEB_PORT, WEB_LOG, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, \ - NZB_METHOD, NZB_DIR, TVBINZ, TVBINZ_UID, TVBINZ_HASH, DOWNLOAD_PROPERS, \ + USE_NZBS, USE_TORRENTS, NZB_METHOD, NZB_DIR, TVBINZ, TVBINZ_UID, TVBINZ_HASH, DOWNLOAD_PROPERS, \ SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_HOST, \ - XBMC_NOTIFY_ONSNATCH, XBMC_NOTIFY_ONDOWNLOAD, XBMC_UPDATE_FULL, \ - XBMC_UPDATE_LIBRARY, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, currentSearchScheduler, backlogSearchScheduler, \ + NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_HOST, currentSearchScheduler, backlogSearchScheduler, \ + USE_XBMC, XBMC_NOTIFY_ONSNATCH, XBMC_NOTIFY_ONDOWNLOAD, XBMC_UPDATE_FULL, \ + XBMC_UPDATE_LIBRARY, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, \ + USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_UPDATE_LIBRARY, \ + PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, \ showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, showList, loadingShowList, \ - NZBS, NZBS_UID, NZBS_HASH, EZRSS, TORRENT_DIR, USENET_RETENTION, SOCKET_TIMEOUT, \ + NZBS, NZBS_UID, NZBS_HASH, EZRSS, TVTORRENTS, TVTORRENTS_DIGEST, TVTORRENTS_HASH, TORRENT_DIR, USENET_RETENTION, SOCKET_TIMEOUT, \ SEARCH_FREQUENCY, DEFAULT_SEARCH_FREQUENCY, BACKLOG_SEARCH_FREQUENCY, \ QUALITY_DEFAULT, SEASON_FOLDERS_FORMAT, SEASON_FOLDERS_DEFAULT, STATUS_DEFAULT, \ - USE_XBMC, GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, \ + GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, \ USE_GROWL, GROWL_HOST, GROWL_PASSWORD, USE_PROWL, PROWL_NOTIFY_ONSNATCH, PROWL_NOTIFY_ONDOWNLOAD, PROWL_API, PROWL_PRIORITY, PROG_DIR, NZBMATRIX, NZBMATRIX_USERNAME, \ NZBMATRIX_APIKEY, versionCheckScheduler, VERSION_NOTIFY, PROCESS_AUTOMATICALLY, \ KEEP_PROCESSED_DIR, TV_DOWNLOAD_DIR, TVDB_BASE_URL, MIN_SEARCH_FREQUENCY, \ @@ -330,7 +360,7 @@ def initialize(consoleLogging=True): NZBSRUS, NZBSRUS_UID, NZBSRUS_HASH, NAMING_QUALITY, providerList, newznabProviderList, \ NAMING_DATES, EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \ USE_NOTIFO, NOTIFO_USERNAME, NOTIFO_APISECRET, NOTIFO_NOTIFY_ONDOWNLOAD, NOTIFO_NOTIFY_ONSNATCH, \ - USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, \ + USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, \ USE_BANNER, USE_LISTVIEW, METADATA_XBMC, METADATA_MEDIABROWSER, METADATA_PS3, metadata_provider_dict, \ NEWZBIN, NEWZBIN_USERNAME, NEWZBIN_PASSWORD, GIT_PATH, MOVE_ASSOCIATED_FILES, \ COMING_EPS_LAYOUT, COMING_EPS_SORT, COMING_EPS_DISPLAY_PAUSED, METADATA_WDTV @@ -345,10 +375,13 @@ def initialize(consoleLogging=True): CheckSection('Newzbin') CheckSection('TVBinz') CheckSection('SABnzbd') + CheckSection('NZBget') CheckSection('XBMC') + CheckSection('PLEX') CheckSection('Growl') CheckSection('Prowl') CheckSection('Twitter') + CheckSection('NMJ') LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', 'Logs') if not helpers.makeDir(LOG_DIR): @@ -418,8 +451,11 @@ def initialize(consoleLogging=True): TVDB_BASE_URL = 'http://www.thetvdb.com/api/' + TVDB_API_KEY + USE_NZBS = bool(check_setting_int(CFG, 'General', 'use_nzbs', 1)) + USE_TORRENTS = bool(check_setting_int(CFG, 'General', 'use_torrents', 0)) + NZB_METHOD = check_setting_str(CFG, 'General', 'nzb_method', 'blackhole') - if NZB_METHOD not in ('blackhole', 'sabnzbd'): + if NZB_METHOD not in ('blackhole', 'sabnzbd', 'nzbget'): NZB_METHOD = 'blackhole' DOWNLOAD_PROPERS = bool(check_setting_int(CFG, 'General', 'download_propers', 1)) @@ -442,6 +478,10 @@ def initialize(consoleLogging=True): EZRSS = bool(check_setting_int(CFG, 'General', 'use_torrent', 0)) if not EZRSS: EZRSS = bool(check_setting_int(CFG, 'EZRSS', 'ezrss', 0)) + + TVTORRENTS = bool(check_setting_int(CFG, 'TVTORRENTS', 'tvtorrents', 0)) + TVTORRENTS_DIGEST = check_setting_str(CFG, 'TVTORRENTS', 'tvtorrents_digest', '') + TVTORRENTS_HASH = check_setting_str(CFG, 'TVTORRENTS', 'tvtorrents_hash', '') TVBINZ = bool(check_setting_int(CFG, 'TVBinz', 'tvbinz', 0)) TVBINZ_UID = check_setting_str(CFG, 'TVBinz', 'tvbinz_uid', '') @@ -472,6 +512,10 @@ def initialize(consoleLogging=True): SAB_CATEGORY = check_setting_str(CFG, 'SABnzbd', 'sab_category', 'tv') SAB_HOST = check_setting_str(CFG, 'SABnzbd', 'sab_host', '') + NZBGET_PASSWORD = check_setting_str(CFG, 'NZBget', 'nzbget_password', 'tegbzn6789') + NZBGET_CATEGORY = check_setting_str(CFG, 'NZBget', 'nzbget_category', 'tv') + NZBGET_HOST = check_setting_str(CFG, 'NZBget', 'nzbget_host', '') + USE_XBMC = bool(check_setting_int(CFG, 'XBMC', 'use_xbmc', 0)) XBMC_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'XBMC', 'xbmc_notify_onsnatch', 0)) XBMC_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'XBMC', 'xbmc_notify_ondownload', 0)) @@ -481,6 +525,15 @@ def initialize(consoleLogging=True): XBMC_USERNAME = check_setting_str(CFG, 'XBMC', 'xbmc_username', '') XBMC_PASSWORD = check_setting_str(CFG, 'XBMC', 'xbmc_password', '') + USE_PLEX = bool(check_setting_int(CFG, 'Plex', 'use_plex', 0)) + PLEX_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Plex', 'plex_notify_onsnatch', 0)) + PLEX_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Plex', 'plex_notify_ondownload', 0)) + PLEX_UPDATE_LIBRARY = bool(check_setting_int(CFG, 'Plex', 'plex_update_library', 0)) + PLEX_SERVER_HOST = check_setting_str(CFG, 'Plex', 'plex_server_host', '') + PLEX_HOST = check_setting_str(CFG, 'Plex', 'plex_host', '') + PLEX_USERNAME = check_setting_str(CFG, 'Plex', 'plex_username', '') + PLEX_PASSWORD = check_setting_str(CFG, 'Plex', 'plex_password', '') + USE_GROWL = bool(check_setting_int(CFG, 'Growl', 'use_growl', 0)) GROWL_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Growl', 'growl_notify_onsnatch', 0)) GROWL_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Growl', 'growl_notify_ondownload', 0)) @@ -510,6 +563,12 @@ def initialize(consoleLogging=True): LIBNOTIFY_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Libnotify', 'libnotify_notify_onsnatch', 0)) LIBNOTIFY_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Libnotify', 'libnotify_notify_ondownload', 0)) + USE_NMJ = bool(check_setting_int(CFG, 'NMJ', 'use_nmj', 0)) + NMJ_HOST = check_setting_str(CFG, 'NMJ', 'nmj_host', '') + NMJ_DATABASE = check_setting_str(CFG, 'NMJ', 'nmj_database', '') + NMJ_MOUNT = check_setting_str(CFG, 'NMJ', 'nmj_mount', '') + + GIT_PATH = check_setting_str(CFG, 'General', 'git_path', '') EXTRA_SCRIPTS = [x for x in check_setting_str(CFG, 'General', 'extra_scripts', '').split('|') if x] @@ -583,6 +642,9 @@ def initialize(consoleLogging=True): # initialize the main SB database db.upgradeDatabase(db.DBConnection(), mainDB.InitialSchema) + # initialize the cache database + db.upgradeDatabase(db.DBConnection("cache.db"), cache_db.InitialSchema) + # fix up any db problems db.sanityCheckDatabase(db.DBConnection(), mainDB.MainSanityCheck) @@ -641,7 +703,8 @@ def start(): global __INITIALIZED__, currentSearchScheduler, backlogSearchScheduler, \ showUpdateScheduler, versionCheckScheduler, showQueueScheduler, \ - properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler + properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \ + started with INIT_LOCK: @@ -670,11 +733,14 @@ def start(): # start the proper finder autoPostProcesserScheduler.thread.start() + + started = True def halt (): global __INITIALIZED__, currentSearchScheduler, backlogSearchScheduler, showUpdateScheduler, \ - showQueueScheduler, properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler + showQueueScheduler, properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \ + started with INIT_LOCK: @@ -766,13 +832,17 @@ def saveAll(): def saveAndShutdown(restart=False): - logger.log(u"Killing cherrypy") - cherrypy.engine.exit() - halt() saveAll() + logger.log(u"Killing cherrypy") + cherrypy.engine.exit() + + if sickbeard.CREATEPID: + logger.log(u"Removing pidfile " + str(sickbeard.PIDFILE)) + os.remove(sickbeard.PIDFILE) + if restart: install_type = sickbeard.versionCheckScheduler.action.install_type @@ -838,6 +908,8 @@ def save_config(): new_config['General']['web_root'] = WEB_ROOT new_config['General']['web_username'] = WEB_USERNAME new_config['General']['web_password'] = WEB_PASSWORD + new_config['General']['use_nzbs'] = int(USE_NZBS) + new_config['General']['use_torrents'] = int(USE_TORRENTS) new_config['General']['nzb_method'] = NZB_METHOD new_config['General']['usenet_retention'] = int(USENET_RETENTION) new_config['General']['search_frequency'] = int(SEARCH_FREQUENCY) @@ -882,6 +954,11 @@ def save_config(): new_config['EZRSS'] = {} new_config['EZRSS']['ezrss'] = int(EZRSS) + + new_config['TVTORRENTS'] = {} + new_config['TVTORRENTS']['tvtorrents'] = int(TVTORRENTS) + new_config['TVTORRENTS']['tvtorrents_digest'] = TVTORRENTS_DIGEST + new_config['TVTORRENTS']['tvtorrents_hash'] = TVTORRENTS_HASH new_config['TVBinz'] = {} new_config['TVBinz']['tvbinz'] = int(TVBINZ) @@ -919,8 +996,13 @@ def save_config(): new_config['SABnzbd']['sab_category'] = SAB_CATEGORY new_config['SABnzbd']['sab_host'] = SAB_HOST + new_config['NZBget'] = {} + new_config['NZBget']['nzbget_password'] = NZBGET_PASSWORD + new_config['NZBget']['nzbget_category'] = NZBGET_CATEGORY + new_config['NZBget']['nzbget_host'] = NZBGET_HOST + new_config['XBMC'] = {} - new_config['XBMC']['use_xbmc'] = int(USE_XBMC) + new_config['XBMC']['use_xbmc'] = int(USE_XBMC) new_config['XBMC']['xbmc_notify_onsnatch'] = int(XBMC_NOTIFY_ONSNATCH) new_config['XBMC']['xbmc_notify_ondownload'] = int(XBMC_NOTIFY_ONDOWNLOAD) new_config['XBMC']['xbmc_update_library'] = int(XBMC_UPDATE_LIBRARY) @@ -928,6 +1010,16 @@ def save_config(): new_config['XBMC']['xbmc_host'] = XBMC_HOST new_config['XBMC']['xbmc_username'] = XBMC_USERNAME new_config['XBMC']['xbmc_password'] = XBMC_PASSWORD + + new_config['Plex'] = {} + new_config['Plex']['use_plex'] = int(USE_PLEX) + new_config['Plex']['plex_notify_onsnatch'] = int(PLEX_NOTIFY_ONSNATCH) + new_config['Plex']['plex_notify_ondownload'] = int(PLEX_NOTIFY_ONDOWNLOAD) + new_config['Plex']['plex_update_library'] = int(PLEX_UPDATE_LIBRARY) + new_config['Plex']['plex_server_host'] = PLEX_SERVER_HOST + new_config['Plex']['plex_host'] = PLEX_HOST + new_config['Plex']['plex_username'] = PLEX_USERNAME + new_config['Plex']['plex_password'] = PLEX_PASSWORD new_config['Growl'] = {} new_config['Growl']['use_growl'] = int(USE_GROWL) @@ -963,6 +1055,12 @@ def save_config(): new_config['Libnotify']['libnotify_notify_onsnatch'] = int(LIBNOTIFY_NOTIFY_ONSNATCH) new_config['Libnotify']['libnotify_notify_ondownload'] = int(LIBNOTIFY_NOTIFY_ONDOWNLOAD) + new_config['NMJ'] = {} + new_config['NMJ']['use_nmj'] = int(USE_NMJ) + new_config['NMJ']['nmj_host'] = NMJ_HOST + new_config['NMJ']['nmj_database'] = NMJ_DATABASE + new_config['NMJ']['nmj_mount'] = NMJ_MOUNT + new_config['Newznab'] = {} new_config['Newznab']['newznab_data'] = '!!!'.join([x.configStr() for x in newznabProviderList]) diff --git a/sickbeard/browser.py b/sickbeard/browser.py index 783afa13c74fe3da2895357f96516a952efc0a45..69f89e4e13383ea9a27b3eb20b8095ce6955ab8b 100644 --- a/sickbeard/browser.py +++ b/sickbeard/browser.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import os import glob import string diff --git a/sickbeard/classes.py b/sickbeard/classes.py index a97b2dd7ca1b779d2dd9a0fd32b0e84f88844dc6..3c2cf32acf699003d3896463e287c333727ba2ae 100644 --- a/sickbeard/classes.py +++ b/sickbeard/classes.py @@ -31,32 +31,64 @@ class SickBeardURLopener(urllib.FancyURLopener): version = USER_AGENT class AuthURLOpener(SickBeardURLopener): + """ + URLOpener class that supports http auth without needing interactive password entry. + If the provided username/password don't work it simply fails. + + user: username to use for HTTP auth + pw: password to use for HTTP auth + """ def __init__(self, user, pw): self.username = user self.password = pw + + # remember if we've tried the username/password before self.numTries = 0 + + # call the base class urllib.FancyURLopener.__init__(self) def prompt_user_passwd(self, host, realm): + """ + Override this function and instead of prompting just give the + username/password that were provided when the class was instantiated. + """ + + # if this is the first try then provide a username/password if self.numTries == 0: self.numTries = 1 return (self.username, self.password) + + # if we've tried before then return blank which cancels the request else: return ('', '') + # this is pretty much just a hack for convenience def openit(self, url): self.numTries = 0 return SickBeardURLopener.open(self, url) class SearchResult: + """ + Represents a search result from an indexer. + """ def __init__(self, episodes): self.provider = -1 + + # URL to the NZB/torrent file self.url = "" + + # used by some providers to store extra info associated with the result self.extraInfo = [] + + # list of TVEpisode objects that this result is associated with self.episodes = episodes - self.predownloaded = False + + # quality of the release self.quality = -1 + + # release name self.name = "" def __str__(self): @@ -74,16 +106,30 @@ class SearchResult: return self.episodes[0].prettyName(True) + "." + self.resultType class NZBSearchResult(SearchResult): + """ + Regular NZB result with an URL to the NZB + """ resultType = "nzb" class NZBDataSearchResult(SearchResult): + """ + NZB result where the actual NZB XML data is stored in the extraInfo + """ resultType = "nzbdata" class TorrentSearchResult(SearchResult): + """ + Torrent result with an URL to the torrent + """ resultType = "torrent" class ShowListUI: + """ + This class is for tvdb-api. Instead of prompting with a UI to pick the + desired result out of a list of shows it tries to be smart about it + based on what shows are in SB. + """ def __init__(self, config, log=None): self.config = config self.log = log @@ -116,6 +162,10 @@ class Proper: class ErrorViewer(): + """ + Keeps a static list of UIErrors to be displayed on the UI and allows + the list to be cleared. + """ errors = [] @@ -131,6 +181,9 @@ class ErrorViewer(): ErrorViewer.errors = [] class UIError(): + """ + Represents an error to be displayed in the web UI. + """ def __init__(self, message): self.message = message self.time = datetime.datetime.now() diff --git a/sickbeard/common.py b/sickbeard/common.py index 57beeaaedd0492101a697c1ec816e9fd4e9b2a59..9692dadd78cbaeae361505294a77352807d32660 100644 --- a/sickbeard/common.py +++ b/sickbeard/common.py @@ -28,7 +28,8 @@ USER_AGENT = 'Sick Beard/alpha2-'+version.SICKBEARD_VERSION.replace(' ','-')+' ( mediaExtensions = ['avi', 'mkv', 'mpg', 'mpeg', 'wmv', 'ogm', 'mp4', 'iso', 'img', 'divx', 'm2ts', 'm4v', 'ts', 'flv', 'f4v', - 'mov', 'rmvb', 'vob'] + 'mov', 'rmvb', 'vob', 'dvr-ms', 'wtv', + ] ### Other constants MULTI_EP_RESULT = -1 @@ -236,64 +237,8 @@ XML_NSMAP = {'xsi': 'http://www.w3.org/2001/XMLSchema-instance', 'xsd': 'http://www.w3.org/2001/XMLSchema'} - -##################################################################### -### -### DO NOT EDIT THIS MANUALLY! If you find a show that isn't -### being found please submit a ticket on google code so that -### I can fix the problem for everybody: -### http://code.google.com/p/sickbeard/issues/entry -### -##################################################################### - -sceneExceptions = {72546: ['CSI'], - 73696: ['CSI: New York'], - 110381: ['Archer'], - 83897: ['Life After People: The Series'], - 80552: ['Kitchen Nightmares (US)'], - 71256: ['The Daily Show'], - 75692: ['Law & Order: SVU'], - 71489: ['Law & Order: Criminal Intent', 'Law & Order: CI'], - 79590: ['Dancing With The Stars (US)'], - 73387: ['Craig Ferguson'], - 85355: ['Jimmy Fallon'], - 75088: ['David Letterman'], - 76706: ['Big Brother (US)'], - 105521: ['The Colony', 'The Colony (US)'], - 76235: ['America\'s Funniest Home Videos', 'AFHV'], - 139941: ['Childrens Hospital (US)', 'Childrens Hospital'], - 83123: ['Merlin', 'Merlin (2008)'], - 76779: ['WWE Monday Night RAW'], - 164951: ['Shit My Dad Says'], - 83714: ['Genius with Dave Gorman'], - 168161: ['Law & Order: Los Angeles', 'Law & Order: LA'], - 77526: ['Star Trek: TOS'], - 72194: ['The Ellen Degeneres Show', 'Ellen Degeneres'], - 72073: ['Star Trek DS9'], - 195831: ['Zane Lamprey\'s Drinking Made Easy'], - 76133: ['Poirot', 'Agatha Christie\'s Poirot'], - 70870: ['The Real World Road Rules Challenge', 'The Challenge Cutthroat'], - 77444: ['This Old House Program'], - 73290: ['60 Minutes (US)'], - 194751: ['Conan', 'Conan (2010)'], - 164451: ['Carlos (2010)'], - 70726: ['Babylon 5', 'Babylon5'], - 83714: ['Genius', 'Genius With Dave Gormand'], - 212571: ['Come Fly With Me (2010)'], - 81563: ['Border Security', 'Border Security Australia\'s Frontline'], - 172381: ['Silent Library (US)'], - 131791: ['Sci-Fi Science'], - 80646: ['Frontline (US)', 'Frontline'], - 189931: ['RBT (AU)'], - 73255: ['House', 'House M D'], - 73244: ['The Office (US)', 'The Office'], - 81386: ['Being Human', 'Being Human (UK)'], - 89991: ['Out of the Wild: The Alaskan Experiment', 'Out of the Wild: Venezuela'], - 222551: ['Only in America With Larry the Cable Guy', 'Only in America'], - 77733: ['Degrassi: The Next Generation', 'Degrassi TNG'], - } - countryList = {'Australia': 'AU', 'Canada': 'CA', 'USA': 'US' } + diff --git a/sickbeard/databases/__init__.py b/sickbeard/databases/__init__.py index 9666180624c2329afa8d4c80f0be996004f25ccb..3c75828d8f1dcedcef7055bde578f8afb29a5ea2 100644 --- a/sickbeard/databases/__init__.py +++ b/sickbeard/databases/__init__.py @@ -1 +1,19 @@ -__all__ = ["mainDB"] \ No newline at end of file +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + +__all__ = ["mainDB", "cache"] \ No newline at end of file diff --git a/sickbeard/databases/cache_db.py b/sickbeard/databases/cache_db.py new file mode 100644 index 0000000000000000000000000000000000000000..997b09bf2e8996bed71daa3a2e6871c140f56149 --- /dev/null +++ b/sickbeard/databases/cache_db.py @@ -0,0 +1,51 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + +from sickbeard import db + +# Add new migrations at the bottom of the list; subclass the previous migration. +class InitialSchema (db.SchemaUpgrade): + def test(self): + return self.hasTable("lastUpdate") + + def execute(self): + + queries = [ + ("CREATE TABLE lastUpdate (provider TEXT, time NUMERIC);",), + ("CREATE TABLE db_version (db_version INTEGER);",), + ("INSERT INTO db_version (db_version) VALUES (?)", 1), + ] + for query in queries: + if len(query) == 1: + self.connection.action(query[0]) + else: + self.connection.action(query[0], query[1:]) + +class AddSceneExceptions(InitialSchema): + def test(self): + return self.hasTable("scene_exceptions") + + def execute(self): + self.connection.action("CREATE TABLE scene_exceptions (exception_id INTEGER PRIMARY KEY, tvdb_id INTEGER KEY, show_name TEXT)") + +class AddSceneNameCache(AddSceneExceptions): + def test(self): + return self.hasTable("scene_names") + + def execute(self): + self.connection.action("CREATE TABLE scene_names (tvdb_id INTEGER, name TEXT)") \ No newline at end of file diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index ca4d154e01e27f9ac67279cabc290edd631b1fbc..2acb6fe20b36a67115ed9627f56baf8430219166 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -1,10 +1,32 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import sickbeard +import shutil, time, os.path, sys + from sickbeard import db from sickbeard import common from sickbeard import logger +from sickbeard.providers.generic import GenericProvider from sickbeard import encodingKludge as ek -import shutil, time, os.path, sys + + class MainSanityCheck(db.DBSanityCheck): @@ -156,8 +178,12 @@ class NewQualitySettings (NumericProviders): ### Update episode statuses toUpdate = self.connection.select("SELECT episode_id, location, status FROM tv_episodes WHERE status IN (?, ?, ?, ?, ?, ?, ?)", [common.DOWNLOADED, common.SNATCHED, PREDOWNLOADED, MISSED, BACKLOG, DISCBACKLOG, SNATCHED_BACKLOG]) + didUpdate = False for curUpdate in toUpdate: + # remember that we changed something + didUpdate = True + newStatus = None oldStatus = int(curUpdate["status"]) if oldStatus == common.SNATCHED: @@ -184,6 +210,10 @@ class NewQualitySettings (NumericProviders): self.connection.action("UPDATE tv_episodes SET status = ? WHERE episode_id = ?", [common.Quality.compositeStatus(common.DOWNLOADED, newQuality), curUpdate["episode_id"]]) + # if no updates were done then the backup is useless + if didUpdate: + os.remove(ek.ek(os.path.join, sickbeard.PROG_DIR, 'sickbeard.db.v0')) + ### Update show qualities toUpdate = self.connection.select("SELECT * FROM tv_shows") @@ -322,8 +352,50 @@ class PopulateRootDirs (AddLang): sickbeard.ROOT_DIRS = new_root_dirs + sickbeard.save_config() + + self.incDBVersion() + + +class SetNzbTorrentSettings(PopulateRootDirs): + + def test(self): + return self.checkDBVersion() >= 8 + + def execute(self): + + use_torrents = False + use_nzbs = False + + for cur_provider in sickbeard.providers.sortedProviderList(): + if cur_provider.isEnabled(): + if cur_provider.providerType == GenericProvider.NZB: + use_nzbs = True + logger.log(u"Provider "+cur_provider.name+" is enabled, enabling NZBs in the upgrade") + break + elif cur_provider.providerType == GenericProvider.TORRENT: + use_torrents = True + logger.log(u"Provider "+cur_provider.name+" is enabled, enabling Torrents in the upgrade") + break + + sickbeard.USE_TORRENTS = use_torrents + sickbeard.USE_NZBS = use_nzbs + + sickbeard.save_config() + self.incDBVersion() + +class FixAirByDateSetting(SetNzbTorrentSettings): + + def test(self): + return self.checkDBVersion() >= 9 + + def execute(self): + shows = self.connection.select("SELECT * FROM tv_shows") + for cur_show in shows: + if cur_show["genre"] and "talk show" in cur_show["genre"].lower(): + self.connection.action("UPDATE tv_shows SET air_by_date = ? WHERE tvdb_id = ?", [1, cur_show["tvdb_id"]]) - \ No newline at end of file + self.incDBVersion() \ No newline at end of file diff --git a/sickbeard/encodingKludge.py b/sickbeard/encodingKludge.py index 9debc5a09bad281fc3d5281c65277c064fdcc765..6b505808c3d12f2b6d891f44e684c40fd71503b5 100644 --- a/sickbeard/encodingKludge.py +++ b/sickbeard/encodingKludge.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import os import os.path import locale diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index bb4fcaadde6e69ff7d4275ffc31b732c461980ff..47cbc0ab5ad59f68646636bb61722168a9e7850d 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -84,6 +84,10 @@ def isMediaFile (file): if re.search('(^|[\W_])sample\d*[\W_]', file): return False + # ignore MAC OS's retarded "resource fork" files + if file.startswith('._'): + return False + sepFile = file.rpartition(".") if sepFile[2].lower() in mediaExtensions: return True @@ -307,11 +311,11 @@ def buildNFOXML(myShow): def searchDBForShow(regShowName): - showNames = [regShowName.replace(' ','_')] + showNames = [re.sub('[. -]', ' ', regShowName)] myDB = db.DBConnection() - yearRegex = "(.*?)\s*([(]?)(\d{4})(?(2)[)]?).*" + yearRegex = "([^()]+?)\s*(\()?(\d{4})(?(2)\))$" for showName in showNames: @@ -324,7 +328,7 @@ def searchDBForShow(regShowName): # if we didn't get exactly one result then try again with the year stripped off if possible match = re.match(yearRegex, showName) - if match: + if match and match.group(1): logger.log(u"Unable to match original name but trying to manually strip and specify show year", logger.DEBUG) sqlResults = myDB.select("SELECT * FROM tv_shows WHERE (show_name LIKE ? OR tvr_name LIKE ?) AND startyear = ?", [match.group(1)+'%', match.group(1)+'%', match.group(3)]) @@ -386,6 +390,7 @@ def copyFile(srcFile, destFile): def moveFile(srcFile, destFile): try: ek.ek(os.rename, srcFile, destFile) + fixSetGroupID(destFile) except OSError: copyFile(srcFile, destFile) ek.ek(os.unlink, srcFile) @@ -415,7 +420,7 @@ def chmodAsParent(childPath): parentMode = stat.S_IMODE(os.stat(parentPath)[stat.ST_MODE]) if ek.ek(os.path.isfile, childPath): - childMode = readwriteBits(parentMode) + childMode = fileBitFilter(parentMode) else: childMode = parentMode @@ -425,14 +430,61 @@ def chmodAsParent(childPath): except OSError: logger.log(u"Failed to set permission for %s to %o" % (childPath, childMode), logger.ERROR) -def readwriteBits(currentMode): - newMode = 0 +def fileBitFilter(mode): + for bit in [stat.S_IXUSR, stat.S_IXGRP, stat.S_IXOTH, stat.S_ISUID, stat.S_ISGID]: + if mode & bit: + mode -= bit + + return mode + +def fixSetGroupID(childPath): + if os.name == 'nt' or os.name == 'ce': + return + + parentPath = ek.ek(os.path.dirname, childPath) + parentStat = os.stat(parentPath) + parentMode = stat.S_IMODE(parentStat[stat.ST_MODE]) + + if parentMode & stat.S_ISGID: + parentGID = parentStat[stat.ST_GID] + childGID = os.stat(childPath)[stat.ST_GID] + + if childGID == parentGID: + return + + try: + ek.ek(os.chown, childPath, -1, parentGID) + logger.log(u"Respecting the set-group-ID bit on the parent directory for %s" % (childPath), logger.DEBUG) + except OSError: + logger.log(u"Failed to respect the set-group-ID bit on the parent directory for %s (setting group ID %i)" % (childPath, parentGID), logger.ERROR) + +def sanitizeSceneName (name, ezrss=False): + """ + Takes a show name and returns the "scenified" version of it. + + ezrss: If true the scenified version will follow EZRSS's cracksmoker rules as best as possible + + Returns: A string containing the scene version of the show name given. + """ - for bit in [stat.S_IRUSR, stat.S_IWUSR, stat.S_IRGRP, stat.S_IWGRP, stat.S_IROTH, stat.S_IWOTH]: - if currentMode & bit: - newMode += bit + if not ezrss: + bad_chars = ",:()'!?" + # ezrss leaves : and ! in their show names as far as I can tell + else: + bad_chars = ",()'?" - return newMode + # strip out any bad chars + for x in bad_chars: + name = name.replace(x, "") + + # tidy up stuff that doesn't belong in scene names + name = name.replace("- ", ".").replace(" ", ".").replace("&", "and").replace('/','.') + name = re.sub("\.\.*", ".", name) + + if name.endswith('.'): + name = name[:-1] + + return name if __name__ == '__main__': diff --git a/sickbeard/history.py b/sickbeard/history.py index c8e2bcfb44cbd7c317f67491528d88383c132e04..a2eb4ce97e9025421b4d5983d7dfe992cfd9e0f0 100644 --- a/sickbeard/history.py +++ b/sickbeard/history.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import db import sqlite3 import datetime diff --git a/sickbeard/image_cache.py b/sickbeard/image_cache.py index d80021d1a2c914c569a106745d80d593663e9fd2..4f9640235751b67b951edcc853dcd916361ba837 100644 --- a/sickbeard/image_cache.py +++ b/sickbeard/image_cache.py @@ -36,22 +36,45 @@ class ImageCache: pass def _cache_dir(self): + """ + Builds up the full path to the image cache directory + """ return ek.ek(os.path.abspath, ek.ek(os.path.join, sickbeard.CACHE_DIR, 'images')) def poster_path(self, tvdb_id): + """ + Builds up the path to a poster cache for a given tvdb id + + returns: a full path to the cached poster file for the given tvdb id + + tvdb_id: ID of the show to use in the file name + """ poster_file_name = str(tvdb_id) + '.poster.jpg' return ek.ek(os.path.join, self._cache_dir(), poster_file_name) def banner_path(self, tvdb_id): + """ + Builds up the path to a banner cache for a given tvdb id + + returns: a full path to the cached banner file for the given tvdb id + + tvdb_id: ID of the show to use in the file name + """ banner_file_name = str(tvdb_id) + '.banner.jpg' return ek.ek(os.path.join, self._cache_dir(), banner_file_name) def has_poster(self, tvdb_id): + """ + Returns true if a cached poster exists for the given tvdb id + """ poster_path = self.poster_path(tvdb_id) logger.log(u"Checking if file "+str(poster_path)+" exists", logger.DEBUG) return ek.ek(os.path.isfile, poster_path) def has_banner(self, tvdb_id): + """ + Returns true if a cached banner exists for the given tvdb id + """ banner_path = self.banner_path(tvdb_id) logger.log(u"Checking if file "+str(banner_path)+" exists", logger.DEBUG) return ek.ek(os.path.isfile, banner_path) @@ -60,9 +83,19 @@ class ImageCache: POSTER = 2 def which_type(self, path): + """ + Analyzes the image provided and attempts to determine whether it is a poster or banner. + + returns: BANNER, POSTER if it concluded one or the other, or None if the image was neither (or didn't exist) + + path: full path to the image + """ + if not ek.ek(os.path.isfile, path): logger.log(u"Couldn't check the type of "+str(path)+" cause it doesn't exist", logger.WARNING) return None + + # use hachoir to parse the image for us img_parser = createParser(path) img_metadata = extractMetadata(img_parser) @@ -74,8 +107,11 @@ class ImageCache: img_parser.stream._input.close() + # most posters are around 0.68 width/height ratio (eg. 680/1000) if 0.55 < img_ratio < 0.8: return self.POSTER + + # most banners are around 5.4 width/height ratio (eg. 758/140) elif 5 < img_ratio < 6: return self.BANNER else: @@ -83,6 +119,17 @@ class ImageCache: return None def _cache_image_from_file(self, image_path, img_type, tvdb_id): + """ + Takes the image provided and copies it to the cache folder + + returns: bool representing success + + image_path: path to the image we're caching + img_type: BANNER or POSTER + tvdb_id: id of the show this image belongs to + """ + + # generate the path based on the type & tvdb_id if img_type == self.POSTER: dest_path = self.poster_path(tvdb_id) elif img_type == self.BANNER: @@ -91,6 +138,7 @@ class ImageCache: logger.log(u"Invalid cache image type: "+str(img_type), logger.ERROR) return False + # make sure the cache folder exists before we try copying to it if not ek.ek(os.path.isdir, self._cache_dir()): logger.log(u"Image cache dir didn't exist, creating it at "+str(self._cache_dir())) ek.ek(os.makedirs, self._cache_dir()) @@ -101,6 +149,16 @@ class ImageCache: return True def _cache_image_from_tvdb(self, show_obj, img_type): + """ + Retrieves an image of the type specified from TVDB and saves it to the cache folder + + returns: bool representing success + + show_obj: TVShow object that we want to cache an image for + img_type: BANNER or POSTER + """ + + # generate the path based on the type & tvdb_id if img_type == self.POSTER: img_type_name = 'poster' dest_path = self.poster_path(show_obj.tvdbid) @@ -111,6 +169,7 @@ class ImageCache: logger.log(u"Invalid cache image type: "+str(img_type), logger.ERROR) return False + # retrieve the image from TVDB using the generic metadata class #TODO: refactor metadata_generator = GenericMetadata() img_data = metadata_generator._retrieve_show_image(img_type_name, show_obj) @@ -119,6 +178,12 @@ class ImageCache: return result def fill_cache(self, show_obj): + """ + Caches all images for the given show. Copies them from the show dir if possible, or + downloads them from TVDB if they aren't in the show dir. + + show_obj: TVShow object to cache images for + """ logger.log(u"Checking if we need any cache images for show "+str(show_obj.tvdbid), logger.DEBUG) diff --git a/sickbeard/metadata/__init__.py b/sickbeard/metadata/__init__.py index 4bd27b5d8fa1cd77ffd90667c40e1b29361d3adf..e66ead3242f8e881fde72b7979ce4da56cb9ad88 100644 --- a/sickbeard/metadata/__init__.py +++ b/sickbeard/metadata/__init__.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + __all__ = ['generic', 'helpers', 'xbmc', 'mediabrowser', 'ps3', 'wdtv'] import sys diff --git a/sickbeard/name_cache.py b/sickbeard/name_cache.py new file mode 100644 index 0000000000000000000000000000000000000000..a0629f4ce226eeaa28ad992a94b790aa0cc913b0 --- /dev/null +++ b/sickbeard/name_cache.py @@ -0,0 +1,67 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + +from sickbeard import db +from sickbeard.helpers import sanitizeSceneName + +from sickbeard import logger + +def addNameToCache(name, tvdb_id): + """ + Adds the show & tvdb id to the scene_names table in cache.db. + + name: The show name to cache + tvdb_id: The tvdb id that this show should be cached with (can be None/0 for unknown) + """ + + # standardize the name we're using to account for small differences in providers (aka NZBMatrix sucks) + name = sanitizeSceneName(name) + + if not tvdb_id: + tvdb_id = 0 + + cacheDB = db.DBConnection('cache.db') + cacheDB.action("INSERT INTO scene_names (tvdb_id, name) VALUES (?, ?)", [tvdb_id, name]) + +def retrieveNameFromCache(name): + """ + Looks up the given name in the scene_names table in cache.db. + + name: The show name to look up. + + Returns: the tvdb id that resulted from the cache lookup or None if the show wasn't found in the cache + """ + + # standardize the name we're using to account for small differences in providers (aka NZBMatrix sucks) + name = sanitizeSceneName(name) + + cacheDB = db.DBConnection('cache.db') + cache_results = cacheDB.select("SELECT * FROM scene_names WHERE name = ?", [name]) + + if not cache_results: + return None + + return int(cache_results[0]["tvdb_id"]) + +def clearCache(): + """ + Deletes all "unknown" entries from the cache (names with tvdb_id of 0). + """ + cacheDB = db.DBConnection('cache.db') + cacheDB.action("DELETE FROM scene_names WHERE tvdb_id = ?", [0]) + diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index 85122677ea7f8e95efc6762c3139ed16304f9f2b..dd8c31f5f79d73477702fb221adf7e5e4c8d025e 100644 --- a/sickbeard/name_parser/parser.py +++ b/sickbeard/name_parser/parser.py @@ -114,7 +114,12 @@ class NameParser(object): raise InvalidNameException(str(e)) if 'extra_info' in named_groups: - result.extra_info = match.group('extra_info') + tmp_extra_info = match.group('extra_info') + + # Show.S04.Special is almost certainly not every episode in the season + if tmp_extra_info and cur_regex_name == 'season_only' and re.match(r'([. _-]|^)(special|extra)\w*([. _-]|$)', tmp_extra_info, re.I): + continue + result.extra_info = tmp_extra_info if 'release_group' in named_groups: result.release_group = match.group('release_group') diff --git a/sickbeard/notifiers/__init__.py b/sickbeard/notifiers/__init__.py index d5adfb9aaa11933690496cc3534cab04fbb4b6e0..d2e8d4c60eef7bfbd7569848ae5573261cbed0dd 100644 --- a/sickbeard/notifiers/__init__.py +++ b/sickbeard/notifiers/__init__.py @@ -1,29 +1,53 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import sickbeard -import xbmc +import xbmc +import plex import growl import prowl import tweet from . import libnotify import notifo +import nmj from sickbeard.common import * -xbmc_notifier = xbmc.XBMCNotifier() +xbmc_notifier = xbmc.XBMCNotifier() +plex_notifier = plex.PLEXNotifier() growl_notifier = growl.GrowlNotifier() prowl_notifier = prowl.ProwlNotifier() twitter_notifier = tweet.TwitterNotifier() notifo_notifier = notifo.NotifoNotifier() libnotify_notifier = libnotify.LibnotifyNotifier() +nmj_notifier = nmj.NMJNotifier() notifiers = [ # Libnotify notifier goes first because it doesn't involve blocking on # network activity. libnotify_notifier, - xbmc_notifier, + xbmc_notifier, + plex_notifier, growl_notifier, prowl_notifier, twitter_notifier, + nmj_notifier, ] def notify_download(ep_name): @@ -35,20 +59,3 @@ def notify_snatch(ep_name): for n in notifiers: n.notify_snatch(ep_name) notifo_notifier.notify_snatch(ep_name) - -def notify(type, message): - - if type == NOTIFY_DOWNLOAD and sickbeard.XBMC_NOTIFY_ONDOWNLOAD == True: - xbmc.notifyXBMC(message, notifyStrings[type]) - - if type == NOTIFY_SNATCH and sickbeard.XBMC_NOTIFY_ONSNATCH: - xbmc.notifyXBMC(message, notifyStrings[type]) - - growl.sendGrowl(notifyStrings[type], message) - - prowl.sendProwl(message) - - notifo.notifyNotifo(message) - - if type == NOTIFY_DOWNLOAD: - tweet.notifyTwitter(message) diff --git a/sickbeard/notifiers/growl.py b/sickbeard/notifiers/growl.py index 6c0b81fcbb2d25d3edb05395fbeac4bc19b1ef08..68f94edde83d7df98b2442eb235fe6a9d16c3823 100644 --- a/sickbeard/notifiers/growl.py +++ b/sickbeard/notifiers/growl.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import socket import sys diff --git a/sickbeard/notifiers/libnotify.py b/sickbeard/notifiers/libnotify.py index 6f79eef597a6d3d4051c11b014c642ba1c19917e..13cda96abfb0cbd8dce730cac574045a7118a378 100644 --- a/sickbeard/notifiers/libnotify.py +++ b/sickbeard/notifiers/libnotify.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import os import cgi import sickbeard diff --git a/sickbeard/notifiers/nmj.py b/sickbeard/notifiers/nmj.py new file mode 100644 index 0000000000000000000000000000000000000000..de074f048547feb3fe9dda939fe130852abb37ba --- /dev/null +++ b/sickbeard/notifiers/nmj.py @@ -0,0 +1,142 @@ +# Author: Nico Berlee http://nico.berlee.nl/ +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + +import urllib, urllib2 +import sickbeard +import telnetlib +import re + +from sickbeard import logger + +try: + import xml.etree.cElementTree as etree +except ImportError: + import xml.etree.ElementTree as etree + + +class NMJNotifier: + def notify_settings(self, host): + terminal = False + try: + terminal = telnetlib.Telnet(host) + except Exception: + logger.log(u"Warning: unable to get a telnet session to %s" % (host), logger.ERROR) + return False + + logger.log(u"Connected to %s via telnet" % (host), logger.DEBUG) + terminal.read_until("sh-3.00# ") + terminal.write("cat /tmp/source\n") + terminal.write("cat /tmp/netshare\n") + terminal.write("exit\n") + tnoutput = terminal.read_all() + + database = "" + device = "" + match = re.search(r"(.+\.db)\r\n?(.+)(?=sh-3.00# cat /tmp/netshare)", tnoutput) + + if match: + database = match.group(1) + device = match.group(2) + logger.log(u"Found NMJ database %s on device %s" % (database, device), logger.DEBUG) + sickbeard.NMJ_DATABASE = database + else: + logger.log(u"Could not get current NMJ database on %s, NMJ is probably not running!" % (host), logger.ERROR) + return False + + if device.startswith("NETWORK_SHARE/"): + match = re.search(".*(?=\r\n?%s)" % (re.escape(device[14:])), tnoutput) + + if match: + mount = match.group().replace("127.0.0.1", host) + logger.log(u"Found mounting url on the Popcorn Hour in configuration: %s" % (mount), logger.DEBUG) + sickbeard.NMJ_MOUNT = mount + else: + logger.log(u"Detected a network share on the Popcorn Hour, but could not get the mounting url", logger.DEBUG) + return False + + return True + + def notify_snatch(self, ep_name): + return False + #Not implemented: Start the scanner when snatched does not make any sense + + def notify_download(self, ep_name): + self._notifyNMJ() + + def test_notify(self, host, database, mount): + return self._sendNMJ(host, database, mount) + + def _sendNMJ(self, host, database, mount=None): + if mount: + try: + req = urllib2.Request(mount) + logger.log(u"Try to mount network drive via url: %s" % (mount), logger.DEBUG) + handle = urllib2.urlopen(req) + except IOError, e: + logger.log(u"Warning: Couldn't contact popcorn hour on host %s: %s" % (host, e)) + return False + + UPDATE_URL = "http://%(host)s:8008/metadata_database?%(params)s" + params = { + "arg0": "scanner_start", + "arg1": database, + "arg2": "background", + "arg3": ""} + params = urllib.urlencode(params) + updateUrl = UPDATE_URL % {"host": host, "params": params} + + try: + req = urllib2.Request(updateUrl) + logger.log(u"Sending NMJ scan update command via url: %s" % (updateUrl), logger.DEBUG) + handle = urllib2.urlopen(req) + response = handle.read() + except IOError, e: + logger.log(u"Warning: Couldn't contact Popcorn Hour on host %s: %s" % (host, e)) + return False + + try: + et = etree.fromstring(response) + result = et.findtext("returnValue") + except SyntaxError, e: + logger.log(u"Unable to parse XML returned from the Popcorn Hour: %s" % (e), logger.ERROR) + return False + + if int(result) > 0: + logger.log(u"Popcorn Hour returned an errorcode: %s" % (result)) + return False + else: + logger.log(u"NMJ started background scan") + return True + + def _notifyNMJ(self, host=None, database=None, mount=None, force=False): + if not sickbeard.USE_NMJ and not force: + logger.log("Notification for NMJ scan update not enabled, skipping this notification", logger.DEBUG) + return False + + if not host: + host = sickbeard.NMJ_HOST + if not database: + database = sickbeard.NMJ_DATABASE + if not mount: + mount = sickbeard.NMJ_MOUNT + + logger.log(u"Sending scan command for NMJ ", logger.DEBUG) + + return self._sendNMJ(host, database, mount) + +notifier = NMJNotifier diff --git a/sickbeard/notifiers/notifo.py b/sickbeard/notifiers/notifo.py index ca26cfbdb9547520b508e38d912c37a4ad0fd44b..24b904b639355906eae921f9e3f69f7fc181adb1 100644 --- a/sickbeard/notifiers/notifo.py +++ b/sickbeard/notifiers/notifo.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import urllib import sickbeard @@ -23,14 +41,13 @@ class NotifoNotifier: "msg": msg, }) - data = urllib.urlopen(apiurl, data) try: - try: - result = json.load(data) - except IOError: - return False - finally: - data.close() + data = urllib.urlopen(apiurl, data) + result = json.load(data) + except IOError: + return False + + data.close() if result["status"] != "success" or result["response_message"] != "OK": return False diff --git a/sickbeard/notifiers/plex.py b/sickbeard/notifiers/plex.py new file mode 100644 index 0000000000000000000000000000000000000000..1401d45460a464f01a851c2b69d2a57000b552ad --- /dev/null +++ b/sickbeard/notifiers/plex.py @@ -0,0 +1,85 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + +import urllib +import sickbeard + +from xml.dom import minidom +from sickbeard import logger, common +from sickbeard.notifiers.xbmc import XBMCNotifier + +class PLEXNotifier(XBMCNotifier): + + def notify_snatch(self, ep_name): + if sickbeard.PLEX_NOTIFY_ONSNATCH: + self._notifyXBMC(ep_name, common.notifyStrings[common.NOTIFY_SNATCH]) + + def notify_download(self, ep_name): + if sickbeard.PLEX_NOTIFY_ONDOWNLOAD: + self._notifyXBMC(ep_name, common.notifyStrings[common.NOTIFY_DOWNLOAD]) + + def test_notify(self, host, username, password): + return self._notifyXBMC("Testing Plex notifications from Sick Beard", "Test Notification", host, username, password, force=True) + + def update_library(self): + if sickbeard.PLEX_UPDATE_LIBRARY: + self._update_library() + + def _username(self): + return sickbeard.PLEX_USERNAME + + def _password(self): + return sickbeard.PLEX_PASSWORD + + def _use_me(self): + return sickbeard.USE_PLEX + + def _hostname(self): + return sickbeard.PLEX_HOST + + + def _update_library(self): + if not sickbeard.USE_PLEX: + logger.log(u"Notifications for Plex Media Server not enabled, skipping library update", logger.DEBUG) + return False + + logger.log(u"Updating Plex Media Server library", logger.DEBUG) + + if not sickbeard.PLEX_SERVER_HOST: + logger.log(u"No host specified, no updates done", logger.DEBUG) + return False + + logger.log(u"Plex Media Server updating " + sickbeard.PLEX_SERVER_HOST, logger.DEBUG) + + url = "http://%s/library/sections" % sickbeard.PLEX_SERVER_HOST + xml_sections = minidom.parse(urllib.urlopen(url)) + sections = xml_sections.getElementsByTagName('Directory') + + for s in sections: + if s.getAttribute('type') == "show": + url = "http://%s/library/sections/%s/refresh" % (sickbeard.PLEX_SERVER_HOST, s.getAttribute('key')) + + try: + x = urllib.urlopen(url) + except Exception, e: + logger.log(u"Error updating library section: "+str(e).decode('utf-8'), logger.ERROR) + return False + + return True + +notifier = PLEXNotifier diff --git a/sickbeard/notifiers/prowl.py b/sickbeard/notifiers/prowl.py index a9b330ac7e2063ad04f9590120efba20b162195d..6491db817872cf8e9dd30791f9c446a33027ead4 100644 --- a/sickbeard/notifiers/prowl.py +++ b/sickbeard/notifiers/prowl.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import socket import sys from httplib import HTTPSConnection diff --git a/sickbeard/notifiers/tweet.py b/sickbeard/notifiers/tweet.py index 16b3b4d8ee2cebb5c1a3c0102d9c764aa802f50e..35a29027858bf4cd9cd9f0f7bf35fb4008f4f6ae 100644 --- a/sickbeard/notifiers/tweet.py +++ b/sickbeard/notifiers/tweet.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import socket import os import sys diff --git a/sickbeard/notifiers/xbmc.py b/sickbeard/notifiers/xbmc.py index 0299316da053eea8ce99e66e17dd615c262805f4..8144b0fa4ccae32de6f67bfdd1e7dca6155808c0 100644 --- a/sickbeard/notifiers/xbmc.py +++ b/sickbeard/notifiers/xbmc.py @@ -57,6 +57,17 @@ class XBMCNotifier: logger.log(u"Update of show directory failed on " + curHost + ", trying full update as requested", logger.ERROR) self._update_library(curHost) + def _username(self): + return sickbeard.XBMC_USERNAME + + def _password(self): + return sickbeard.XBMC_PASSWORD + + def _use_me(self): + return sickbeard.USE_XBMC + + def _hostname(self): + return sickbeard.XBMC_HOST def _sendToXBMC(self, command, host, username=None, password=None): ''' @@ -69,9 +80,9 @@ class XBMCNotifier: ''' if not username: - username = sickbeard.XBMC_USERNAME + username = self._username() if not password: - password = sickbeard.XBMC_PASSWORD + password = self._password() for key in command: if type(command[key]) == unicode: @@ -104,25 +115,31 @@ class XBMCNotifier: def _notifyXBMC(self, input, title="Sick Beard", host=None, username=None, password=None, force=False): - if not sickbeard.USE_XBMC and not force: + if not self._use_me() and not force: logger.log("Notification for XBMC not enabled, skipping this notification", logger.DEBUG) return False if not host: - host = sickbeard.XBMC_HOST + host = self._hostname() if not username: - username = sickbeard.XBMC_USERNAME + username = self._username() if not password: - password = sickbeard.XBMC_PASSWORD + password = self._password() logger.log(u"Sending notification for " + input, logger.DEBUG) fileString = title + "," + input + result = '' + for curHost in [x.strip() for x in host.split(",")]: command = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' +fileString + ')' } logger.log(u"Sending notification to XBMC via host: "+ curHost +"username: "+ username + " password: " + password, logger.DEBUG) - return self._sendToXBMC(command, curHost, username, password) + if result: + result += ', ' + result += curHost + ':' + self._sendToXBMC(command, curHost, username, password) + + return result def _update_library(self, host, showName=None): diff --git a/sickbeard/nzbSplitter.py b/sickbeard/nzbSplitter.py index 50e3cd3c7c948f3f3a5abee02ed6d2c240ad79d1..9de912fc049d304a238474e1972bb3d8e7cfeeae 100644 --- a/sickbeard/nzbSplitter.py +++ b/sickbeard/nzbSplitter.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import urllib2 import xml.etree.cElementTree as etree diff --git a/sickbeard/nzbget.py b/sickbeard/nzbget.py new file mode 100644 index 0000000000000000000000000000000000000000..3d73d1b4cda2e97beba68010b8b81b991c20785b --- /dev/null +++ b/sickbeard/nzbget.py @@ -0,0 +1,89 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + + + +import httplib +import datetime + +import sickbeard + +from base64 import standard_b64encode +import xmlrpclib + +from sickbeard.providers.generic import GenericProvider + +from sickbeard.common import * +from sickbeard import logger, classes + +def sendNZB(nzb): + + addToTop = False + nzbgetXMLrpc = "http://nzbget:%(password)s@%(host)s/xmlrpc" + + if sickbeard.NZBGET_HOST == None: + logger.log(u"No NZBget host found in configuration. Please configure it.", logger.ERROR) + return False + + url = nzbgetXMLrpc % {"host": sickbeard.NZBGET_HOST, "password": sickbeard.NZBGET_PASSWORD} + + nzbGetRPC = xmlrpclib.ServerProxy(url) + try: + if nzbGetRPC.writelog("INFO", "Sickbeard connected to drop of %s any moment now." % (nzb.name + ".nzb")): + logger.log(u"Successful connected to NZBget", logger.DEBUG) + else: + logger.log(u"Successful connected to NZBget, but unable to send a message" % (nzb.name + ".nzb"), logger.ERROR) + + except httplib.socket.error: + logger.log(u"Please check your NZBget host and port (if it is running). NZBget is not responding to this combination", logger.ERROR) + return False + + except xmlrpclib.ProtocolError, e: + if (e.errmsg == "Unauthorized"): + logger.log(u"NZBget password is incorrect.", logger.ERROR) + else: + logger.log(u"Protocol Error: " + e.errmsg, logger.ERROR) + return False + + # if it aired recently make it high priority + for curEp in nzb.episodes: + if datetime.date.today() - curEp.airdate <= datetime.timedelta(days=7): + addToTop = True + + # if it's a normal result need to download the NZB content + if nzb.resultType == "nzb": + genProvider = GenericProvider("") + data = genProvider.getURL(nzb.url) + if (data == None): + return False + + # if we get a raw data result thats even better + elif nzb.resultType == "nzbdata": + data = nzb.extraInfo[0] + + nzbcontent64 = standard_b64encode(data) + + logger.log(u"Sending NZB to NZBget") + logger.log(u"URL: " + url, logger.DEBUG) + + if nzbGetRPC.append(nzb.name + ".nzb", sickbeard.NZBGET_CATEGORY, addToTop, nzbcontent64): + logger.log(u"NZB sent to NZBget successfully", logger.DEBUG) + return True + else: + logger.log(u"NZBget could not add %s to the queue" % (nzb.name + ".nzb"), logger.ERROR) + return False \ No newline at end of file diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py index 3ff63365af2b0fd3e75411119a4a7778f8619999..3dd8f229e6b443d8d7ff3a54102c5b7b9cd6e303 100755 --- a/sickbeard/postProcessor.py +++ b/sickbeard/postProcessor.py @@ -35,7 +35,8 @@ from sickbeard import helpers from sickbeard import history from sickbeard import logger from sickbeard import notifiers -from sickbeard import sceneHelpers +from sickbeard import show_name_helpers +from sickbeard import scene_exceptions from sickbeard import encodingKludge as ek @@ -104,7 +105,7 @@ class PostProcessor(object): def _list_associated_files(self, file_path): - if not file_path or not ek.ek(os.path.isfile, file_path): + if not file_path: return [] file_path_list = [] @@ -248,7 +249,7 @@ class PostProcessor(object): # if we couldn't find the right one then just use the season folder defaut format if season_folder == '': # for air-by-date shows use the year as the season folder - if ep_obj.show.is_air_by_date: + if ep_obj.show.air_by_date: season_folder = str(ep_obj.airdate.year) else: season_folder = sickbeard.SEASON_FOLDERS_FORMAT % (ep_obj.season) @@ -327,7 +328,7 @@ class PostProcessor(object): to_return = (None, season, episodes) # do a scene reverse-lookup to get a list of all possible names - name_list = sceneHelpers.sceneToNormalShowNames(parse_result.series_name) + name_list = show_name_helpers.sceneToNormalShowNames(parse_result.series_name) if not name_list: return (None, season, episodes) @@ -340,13 +341,11 @@ class PostProcessor(object): # for each possible interpretation of that scene name for cur_name in name_list: self._log(u"Checking scene exceptions for a match on "+cur_name, logger.DEBUG) - for exceptionID in common.sceneExceptions: - # for each exception name - for curException in common.sceneExceptions[exceptionID]: - if cur_name.lower() in (curException.lower(), sceneHelpers.sanitizeSceneName(curException).lower().replace('.',' ')): - self._log(u"Scene exception lookup got tvdb id "+str(exceptionID)+u", using that", logger.DEBUG) - _finalize(parse_result) - return (exceptionID, season, episodes) + scene_id = scene_exceptions.get_scene_exception_by_name(cur_name) + if scene_id: + self._log(u"Scene exception lookup got tvdb id "+str(scene_id)+u", using that", logger.DEBUG) + _finalize(parse_result) + return (scene_id, season, episodes) # see if we can find the name directly in the DB, if so use it for cur_name in name_list: @@ -701,8 +700,11 @@ class PostProcessor(object): ep_obj.createMetaFiles() ep_obj.saveToDB() - # do the library update - notifiers.xbmc_notifier.update_library(ep_obj.show.name) + # do the library update + notifiers.xbmc_notifier.update_library(ep_obj.show.name) + + # do the library update for Plex Media Server + notifiers.plex_notifier.update_library() # run extra_scripts self._run_extra_scripts(ep_obj) diff --git a/sickbeard/properFinder.py b/sickbeard/properFinder.py index 0dba779c15da58412f1798a8f39035881793a82f..e21c70354c0289419ce7f3c8605debe923065a18 100644 --- a/sickbeard/properFinder.py +++ b/sickbeard/properFinder.py @@ -22,7 +22,7 @@ import operator import sickbeard from sickbeard import db -from sickbeard import classes, common, helpers, logger, sceneHelpers +from sickbeard import classes, common, helpers, logger, show_name_helpers from sickbeard import providers from sickbeard import search from sickbeard import history @@ -118,7 +118,7 @@ class ProperFinder(): genericName = self._genericName(parse_result.series_name) # get the scene name masks - sceneNames = set(sceneHelpers.makeSceneShowSearchStrings(curShow)) + sceneNames = set(show_name_helpers.makeSceneShowSearchStrings(curShow)) # for each scene name mask for curSceneName in sceneNames: @@ -140,7 +140,7 @@ class ProperFinder(): if curProper.tvdbid == -1: continue - if not sceneHelpers.filterBadReleases(curProper.name): + if not show_name_helpers.filterBadReleases(curProper.name): logger.log(u"Proper "+curProper.name+" isn't a valid scene release that we want, igoring it", logger.DEBUG) continue diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py index 8242fbe89e61a71e2744bb09ccf57dfae40b6ca7..f642a1200abc97bb8b383f4c21f09b5b25abd6c6 100644 --- a/sickbeard/providers/__init__.py +++ b/sickbeard/providers/__init__.py @@ -1,4 +1,23 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + __all__ = ['ezrss', + 'tvtorrents', 'nzbmatrix', 'nzbs_org', 'tvbinz', diff --git a/sickbeard/providers/ezrss.py b/sickbeard/providers/ezrss.py index 29cc174424ad538ed8b3be0374aa7d30c49916f3..f0d2ba9580ea7b28aca5915d360d09db93ec957d 100644 --- a/sickbeard/providers/ezrss.py +++ b/sickbeard/providers/ezrss.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import urllib import re @@ -8,7 +26,8 @@ import generic from sickbeard.common import * from sickbeard import logger -from sickbeard import tvcache, sceneHelpers +from sickbeard import tvcache +from sickbeard.helpers import sanitizeSceneName class EZRSSProvider(generic.TorrentProvider): @@ -40,7 +59,7 @@ class EZRSSProvider(generic.TorrentProvider): results = {} - if show.is_air_by_date: + if show.air_by_date: logger.log(u"EZRSS doesn't support air-by-date backlog because of limitations on their RSS search.", logger.WARNING) return results @@ -54,7 +73,7 @@ class EZRSSProvider(generic.TorrentProvider): if not show: return params - params['show_name'] = sceneHelpers.sanitizeSceneName(show.name).replace('.',' ').encode('utf-8') + params['show_name'] = sanitizeSceneName(show.name, ezrss=True).replace('.',' ').encode('utf-8') if season != None: params['season'] = season @@ -68,9 +87,9 @@ class EZRSSProvider(generic.TorrentProvider): if not ep_obj: return params - params['show_name'] = sceneHelpers.sanitizeSceneName(ep_obj.show.name).replace('.',' ').encode('utf-8') + params['show_name'] = sanitizeSceneName(ep_obj.show.name, ezrss=True).replace('.',' ').encode('utf-8') - if ep_obj.show.is_air_by_date: + if ep_obj.show.air_by_date: params['date'] = str(ep_obj.airdate) else: params['season'] = ep_obj.season diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index cbce6ba235f9b393a60360ef6c843fc4e54e015c..ced160fb7f8667d48213691209b2fd6fc3f0734a 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -34,6 +34,8 @@ from sickbeard.common import * from sickbeard import tvcache from sickbeard import encodingKludge as ek +from lib.hachoir_parser import createParser + from sickbeard.name_parser.parser import NameParser, InvalidNameException class GenericProvider: @@ -66,9 +68,9 @@ class GenericProvider: return def isActive(self): - if self.providerType == GenericProvider.NZB: + if self.providerType == GenericProvider.NZB and sickbeard.USE_NZBS: return self.isEnabled() - elif self.providerType == GenericProvider.TORRENT: + elif self.providerType == GenericProvider.TORRENT and sickbeard.USE_TORRENTS: return self.isEnabled() else: return False @@ -116,6 +118,9 @@ class GenericProvider: return result def downloadResult(self, result): + """ + Save the result to disk. + """ logger.log(u"Downloading a result from " + self.name+" at " + result.url) @@ -124,6 +129,7 @@ class GenericProvider: if data == None: return False + # use the appropriate watch folder if self.providerType == GenericProvider.NZB: saveDir = sickbeard.NZB_DIR writeMode = 'w' @@ -133,6 +139,7 @@ class GenericProvider: else: return False + # use the result name as the filename fileName = ek.ek(os.path.join, saveDir, helpers.sanitizeFileName(result.name) + '.' + self.providerType) logger.log(u"Saving to " + fileName, logger.DEBUG) @@ -141,10 +148,26 @@ class GenericProvider: fileOut = open(fileName, writeMode) fileOut.write(data) fileOut.close() + helpers.chmodAsParent(fileName) except IOError, e: - logger.log("Unable to save the NZB: "+str(e).decode('utf-8'), logger.ERROR) + logger.log("Unable to save the file: "+str(e).decode('utf-8'), logger.ERROR) return False + # as long as it's a valid download then consider it a successful snatch + return self._verify_download(fileName) + + def _verify_download(self, file_name=None): + """ + Checks the saved file to see if it was actually valid, if not then consider the download a failure. + """ + + # primitive verification of torrents, just make sure we didn't get a text file or something + if self.providerType == GenericProvider.TORRENT: + parser = createParser(file_name) + if not parser or parser._getMimeType() != 'application/x-bittorrent': + logger.log(u"Result is not a valid torrent file", logger.WARNING) + return False + return True def searchRSS(self): @@ -206,7 +229,7 @@ class GenericProvider: logger.log(u"Unable to parse the filename "+title+" into a valid episode", logger.WARNING) continue - if episode.show.is_air_by_date: + if episode.show.air_by_date: if parse_result.air_date != episode.airdate: logger.log("Episode "+title+" didn't air on "+str(episode.airdate)+", skipping it", logger.DEBUG) continue @@ -255,7 +278,7 @@ class GenericProvider: logger.log(u"Unable to parse the filename "+title+" into a valid episode", logger.WARNING) continue - if not show.is_air_by_date: + if not show.air_by_date: # this check is meaningless for non-season searches if (parse_result.season_number != None and parse_result.season_number != season) or (parse_result.season_number == None and season != 1): logger.log(u"The result "+title+" doesn't seem to be a valid episode for season "+str(season)+", ignoring") diff --git a/sickbeard/providers/newzbin.py b/sickbeard/providers/newzbin.py index 5622859abfe9f6a98184b6ee2ca76ad4102fd352..e3d72ef18ea405aa94349a8c04108fa79bb4110b 100644 --- a/sickbeard/providers/newzbin.py +++ b/sickbeard/providers/newzbin.py @@ -27,7 +27,7 @@ import sickbeard import generic import sickbeard.encodingKludge as ek -from sickbeard import classes, logger, helpers, exceptions, sceneHelpers, db +from sickbeard import classes, logger, helpers, exceptions, db, show_name_helpers from sickbeard import tvcache from sickbeard.common import * @@ -238,9 +238,9 @@ class NewzbinProvider(generic.NZBProvider): def _get_season_search_strings(self, show, season): - nameList = set(sceneHelpers.allPossibleShowNames(show)) + nameList = set(show_name_helpers.allPossibleShowNames(show)) - if show.is_air_by_date: + if show.air_by_date: suffix = '' else: suffix = 'x' @@ -256,8 +256,8 @@ class NewzbinProvider(generic.NZBProvider): def _get_episode_search_strings(self, ep_obj): - nameList = set(sceneHelpers.allPossibleShowNames(ep_obj.show)) - if not ep_obj.show.is_air_by_date: + nameList = set(show_name_helpers.allPossibleShowNames(ep_obj.show)) + if not ep_obj.show.air_by_date: searchStr = " OR ".join(['^"'+x+' - %dx%02d"'%(ep_obj.season, ep_obj.episode) for x in nameList]) else: searchStr = " OR ".join(['^"'+x+' - '+str(ep_obj.airdate)+'"' for x in nameList]) diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index b43df70f4bb3967c805fcc83512b15fc7f9b8d4a..a1827531d73c9f074a4ba5fc6fd25b0edbd5ff08 100644 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -27,7 +27,8 @@ import xml.etree.cElementTree as etree import sickbeard import generic -from sickbeard import classes, sceneHelpers +from sickbeard import classes +from sickbeard.helpers import sanitizeSceneName from sickbeard import exceptions from sickbeard.common import * @@ -71,11 +72,11 @@ class NewznabProvider(generic.NZBProvider): params['rid'] = show.tvrid # if we can't then fall back on a very basic name search else: - params['q'] = sceneHelpers.sanitizeSceneName(show.name) + params['q'] = sanitizeSceneName(show.name) if season != None: # air-by-date means &season=2010&q=2010.03, no other way to do it atm - if show.is_air_by_date: + if show.air_by_date: params['season'] = season.split('-')[0] if 'q' in params: params['q'] += '.' + season.replace('-', '.') @@ -98,9 +99,9 @@ class NewznabProvider(generic.NZBProvider): params['rid'] = ep_obj.show.tvrid # if we can't then fall back on a very basic name search else: - params['q'] = sceneHelpers.sanitizeSceneName(ep_obj.show.name) + params['q'] = sanitizeSceneName(ep_obj.show.name) - if ep_obj.show.is_air_by_date: + if ep_obj.show.air_by_date: date_str = str(ep_obj.airdate) params['season'] = date_str.partition('-')[0] diff --git a/sickbeard/providers/nzbmatrix.py b/sickbeard/providers/nzbmatrix.py index d8a5563198e3314bbd4e4fbb3fa72b5908c7bc44..f74de9bfed8fd78649e4eaac62bfbd2577c69b12 100644 --- a/sickbeard/providers/nzbmatrix.py +++ b/sickbeard/providers/nzbmatrix.py @@ -26,7 +26,7 @@ import xml.etree.cElementTree as etree import sickbeard import generic -from sickbeard import classes, logger, sceneHelpers, db +from sickbeard import classes, logger, show_name_helpers, db from sickbeard import tvcache from sickbeard.common import * @@ -46,14 +46,14 @@ class NZBMatrixProvider(generic.NZBProvider): return sickbeard.NZBMATRIX def _get_season_search_strings(self, show, season): - sceneSearchStrings = set(sceneHelpers.makeSceneSeasonSearchString(show, season, "nzbmatrix")) + sceneSearchStrings = set(show_name_helpers.makeSceneSeasonSearchString(show, season, "nzbmatrix")) # search for all show names and episode numbers like ("a","b","c") in a single search return [' '.join(sceneSearchStrings)] def _get_episode_search_strings(self, ep_obj): - sceneSearchStrings = set(sceneHelpers.makeSceneSearchString(ep_obj)) + sceneSearchStrings = set(show_name_helpers.makeSceneSearchString(ep_obj)) # search for all show names and episode numbers like ("a","b","c") in a single search return ['("' + '","'.join(sceneSearchStrings) + '")'] diff --git a/sickbeard/providers/nzbs_org.py b/sickbeard/providers/nzbs_org.py index 1d9914c97039e844f43f83f6deb5c2592a2d3d13..b390c2eed178defb7cf6ebf6360ab6d26757ecfe 100644 --- a/sickbeard/providers/nzbs_org.py +++ b/sickbeard/providers/nzbs_org.py @@ -27,7 +27,7 @@ import xml.etree.cElementTree as etree import sickbeard import generic -from sickbeard import classes, sceneHelpers +from sickbeard import classes, show_name_helpers from sickbeard import exceptions, logger, db from sickbeard.common import * @@ -53,10 +53,10 @@ class NZBsProvider(generic.NZBProvider): raise exceptions.AuthException("NZBs.org authentication details are empty, check your config") def _get_season_search_strings(self, show, season): - return ['^'+x for x in sceneHelpers.makeSceneSeasonSearchString(show, season)] + return ['^'+x for x in show_name_helpers.makeSceneSeasonSearchString(show, season)] def _get_episode_search_strings(self, ep_obj): - return ['^'+x for x in sceneHelpers.makeSceneSearchString(ep_obj)] + return ['^'+x for x in show_name_helpers.makeSceneSearchString(ep_obj)] def _doSearch(self, curString, show=None): diff --git a/sickbeard/providers/tvtorrents.py b/sickbeard/providers/tvtorrents.py new file mode 100644 index 0000000000000000000000000000000000000000..5281a9a37531ff06fc7d599873f3419225095696 --- /dev/null +++ b/sickbeard/providers/tvtorrents.py @@ -0,0 +1,91 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + +import urllib + +import xml.etree.cElementTree as etree + +import sickbeard +import generic + +from sickbeard.common import * +from sickbeard import logger +from sickbeard import tvcache + +class TvTorrentsProvider(generic.TorrentProvider): + + def __init__(self): + + generic.TorrentProvider.__init__(self, "TvTorrents") + + self.supportsBacklog = False + + self.cache = TvTorrentsCache(self) + + self.url = 'http://www.tvtorrents.com/' + + def isEnabled(self): + return sickbeard.TVTORRENTS + + def imageName(self): + return 'tvtorrents.gif' + +class TvTorrentsCache(tvcache.TVCache): + + def __init__(self, provider): + + tvcache.TVCache.__init__(self, provider) + + # only poll TvTorrents every 15 minutes max + self.minTime = 15 + + + def _getRSSData(self): + # These will be ignored on the serverside. + ignore_regex = "all.month|month.of|season[\s\d]*complete" + + url = 'http://www.tvtorrents.com/RssServlet?digest='+ sickbeard.TVTORRENTS_DIGEST +'&hash='+ sickbeard.TVTORRENTS_HASH +'&fname=true&exclude=(' + ignore_regex + ')' + logger.log(u"TvTorrents cache update URL: "+ url, logger.DEBUG) + + data = self.provider.getURL(url) + + xml_content = etree.fromstring(data) + description = xml_content.findtext('channel/description') + + if "User can't be found" in description: + logger.log(u"TvTorrents invalid digest, check your config", logger.ERROR) + + if "Invalid Hash" in description: + logger.log(u"TvTorrents invalid hash, check your config", logger.ERROR) + + return data + + def _parseItem(self, item): + + title = item.findtext('title') + url = item.findtext('link') + + if not title or not url: + logger.log(u"The XML returned from the TvTorrents RSS feed is incomplete, this result is unusable", logger.ERROR) + return + + logger.log(u"Adding item from RSS to cache: "+title, logger.DEBUG) + + self._addCacheEntry(title, url) + +provider = TvTorrentsProvider() \ No newline at end of file diff --git a/sickbeard/scene_exceptions.py b/sickbeard/scene_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..999f869dd176c03528bffe81c7cf2373a7b2cd47 --- /dev/null +++ b/sickbeard/scene_exceptions.py @@ -0,0 +1,96 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + +import re +import urllib + +from sickbeard.helpers import sanitizeSceneName +from sickbeard import name_cache +from sickbeard import logger +from sickbeard import db + +def get_scene_exceptions(tvdb_id): + """ + Given a tvdb_id, return a list of all the scene exceptions. + """ + + myDB = db.DBConnection("cache.db") + exceptions = myDB.select("SELECT show_name FROM scene_exceptions WHERE tvdb_id = ?", [tvdb_id]) + return [cur_exception["show_name"] for cur_exception in exceptions] + +def get_scene_exception_by_name(show_name): + """ + Given a show name, return the tvdbid of the exception, None if no exception + is present. + """ + + myDB = db.DBConnection("cache.db") + + # try the obvious case first + exception_result = myDB.select("SELECT tvdb_id FROM scene_exceptions WHERE LOWER(show_name) = ?", [show_name.lower()]) + if exception_result: + return int(exception_result[0]["tvdb_id"]) + + all_exception_results = myDB.select("SELECT show_name, tvdb_id FROM scene_exceptions") + for cur_exception in all_exception_results: + + cur_exception_name = cur_exception["show_name"] + cur_tvdb_id = int(cur_exception["tvdb_id"]) + + if show_name.lower() in (cur_exception_name.lower(), sanitizeSceneName(cur_exception_name).lower().replace('.',' ')): + logger.log(u"Scene exception lookup got tvdb id "+str(cur_tvdb_id)+u", using that", logger.DEBUG) + return cur_tvdb_id + + return None + +def retrieve_exceptions(): + """ + Looks up the exceptions on github, parses them into a dict, and inserts them into the + scene_exceptions table in cache.db. Also clears the scene name cache. + """ + + exception_dict = {} + + # exceptions are stored on github pages + url = 'http://midgetspy.github.com/sb_tvdb_scene_exceptions/exceptions.txt' + open_url = urllib.urlopen(url) + + # each exception is on one line with the format tvdb_id: 'show name 1', 'show name 2', etc + for cur_line in open_url.readlines(): + tvdb_id, sep, aliases = cur_line.partition(':') + + if not aliases: + continue + + tvdb_id = int(tvdb_id) + + # regex out the list of shows, taking \' into account + alias_list = [re.sub(r'\\(.)', r'\1', x) for x in re.findall(r"'(.*?)(?<!\\)',?", aliases)] + + exception_dict[tvdb_id] = alias_list + + myDB = db.DBConnection("cache.db") + myDB.action("DELETE FROM scene_exceptions WHERE 1=1") + + # write all the exceptions we got off the net into the database + for cur_tvdb_id in exception_dict: + for cur_exception in exception_dict[cur_tvdb_id]: + myDB.action("INSERT INTO scene_exceptions (tvdb_id, show_name) VALUES (?,?)", [cur_tvdb_id, cur_exception]) + + # since this could invalidate the results of the cache we clear it out after updating + name_cache.clearCache() \ No newline at end of file diff --git a/sickbeard/scheduler.py b/sickbeard/scheduler.py index fb7ce97c03b4dc15ad5a468a44ff790a4a390595..55b6deefea3dbee7731ae724e6ef1f56dbea7ab2 100644 --- a/sickbeard/scheduler.py +++ b/sickbeard/scheduler.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import datetime import time import threading diff --git a/sickbeard/search.py b/sickbeard/search.py index aca2b7ce44e9e36962618a0f42f8558c9bd44af5..f4a1726ee13ea3d713b8537a23a0f39de20d0ad2 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -24,16 +24,26 @@ import sickbeard from common import * -from sickbeard import logger, db, sceneHelpers, exceptions, helpers +from sickbeard import logger, db, show_name_helpers, exceptions, helpers from sickbeard import sab +from sickbeard import nzbget from sickbeard import history from sickbeard import notifiers from sickbeard import nzbSplitter +from sickbeard import encodingKludge as ek + from sickbeard.providers import * from sickbeard import providers def _downloadResult(result): + """ + Downloads a result to the appropriate black hole folder. + + Returns a bool representing success. + + result: SearchResult instance to download. + """ resProvider = result.provider @@ -43,25 +53,33 @@ def _downloadResult(result): logger.log(u"Invalid provider name - this is a coding error, report it please", logger.ERROR) return False + # nzbs with an URL can just be downloaded from the provider if result.resultType == "nzb": newResult = resProvider.downloadResult(result) + + # if it's an nzb data result elif result.resultType == "nzbdata": - fileName = os.path.join(sickbeard.NZB_DIR, result.name + ".nzb") + + # get the final file path to the nzb + fileName = ek.ek(os.path.join, sickbeard.NZB_DIR, result.name + ".nzb") logger.log(u"Saving NZB to " + fileName) newResult = True + # save the data to disk try: fileOut = open(fileName, "w") fileOut.write(result.extraInfo[0]) fileOut.close() + helpers.chmodAsParent(fileName) except IOError, e: logger.log(u"Error trying to save NZB to black hole: "+str(e).decode('utf-8'), logger.ERROR) newResult = False elif resProvider.providerType == "torrent": newResult = resProvider.downloadResult(result) + else: logger.log(u"Invalid provider type - this is a coding error, report it please", logger.ERROR) return False @@ -69,15 +87,29 @@ def _downloadResult(result): return newResult def snatchEpisode(result, endStatus=SNATCHED): - + """ + Contains the internal logic necessary to actually "snatch" a result that + has been found. + + Returns a bool representing success. + + result: SearchResult instance to be snatched. + endStatus: the episode status that should be used for the episode object once it's snatched. + """ + + # NZBs can be sent straight to SAB or saved to disk if result.resultType in ("nzb", "nzbdata"): if sickbeard.NZB_METHOD == "blackhole": dlResult = _downloadResult(result) elif sickbeard.NZB_METHOD == "sabnzbd": dlResult = sab.sendNZB(result) + elif sickbeard.NZB_METHOD == "nzbget": + dlResult = nzbget.sendNZB(result) else: logger.log(u"Unknown NZB action specified in config: " + sickbeard.NZB_METHOD, logger.ERROR) dlResult = False + + # torrents are always saved to disk elif result.resultType == "torrent": dlResult = _downloadResult(result) else: @@ -162,7 +194,7 @@ def pickBestResult(results, quality_list=None): # find the best result for the current episode bestResult = None for cur_result in results: - logger.log("Quality of "+cur_result.name+" is "+str(cur_result.quality)) + logger.log("Quality of "+cur_result.name+" is "+Quality.qualityStrings[cur_result.quality]) if quality_list and cur_result.quality not in quality_list: logger.log(cur_result.name+" is a quality we know we don't want, rejecting it", logger.DEBUG) @@ -210,7 +242,7 @@ def findEpisode(episode, manualSearch=False): didSearch = True # skip non-tv crap - curFoundResults = filter(lambda x: sceneHelpers.filterBadReleases(x.name) and sceneHelpers.isGoodResult(x.name, episode.show), curFoundResults) + curFoundResults = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, episode.show), curFoundResults) foundResults += curFoundResults @@ -241,7 +273,7 @@ def findSeason(show, season): for curEp in curResults: # skip non-tv crap - curResults[curEp] = filter(lambda x: sceneHelpers.filterBadReleases(x.name) and sceneHelpers.isGoodResult(x.name, show), curResults[curEp]) + curResults[curEp] = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), curResults[curEp]) if curEp in foundResults: foundResults[curEp] += curResults[curEp] @@ -316,7 +348,7 @@ def findSeason(show, season): # if not, break it apart and add them as the lowest priority results individualResults = nzbSplitter.splitResult(bestSeasonNZB) - individualResults = filter(lambda x: sceneHelpers.filterBadReleases(x.name) and sceneHelpers.isGoodResult(x.name, show), individualResults) + individualResults = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), individualResults) for curResult in individualResults: if len(curResult.episodes) == 1: diff --git a/sickbeard/searchBacklog.py b/sickbeard/searchBacklog.py index e4c86466832408d8cda6f59ae0353140aba40e2f..11e2bbf83a552b49dabdc16738bb49e4318d35aa 100644 --- a/sickbeard/searchBacklog.py +++ b/sickbeard/searchBacklog.py @@ -94,8 +94,8 @@ class BacklogSearcher: numSeasonResults = myDB.select("SELECT DISTINCT(season), showid FROM tv_episodes ep, tv_shows show WHERE season != 0 AND ep.showid = show.tvdb_id AND show.paused = 0 AND ep.airdate > ?", [fromDate.toordinal()]) # get separate lists of the season/date shows - season_shows = [x for x in show_list if not x.is_air_by_date] - air_by_date_shows = [x for x in show_list if x.is_air_by_date] + season_shows = [x for x in show_list if not x.air_by_date] + air_by_date_shows = [x for x in show_list if x.air_by_date] # figure out how many segments of air by date shows we're going to do air_by_date_segments = [] @@ -113,7 +113,7 @@ class BacklogSearcher: if curShow.paused: continue - if curShow.is_air_by_date: + if curShow.air_by_date: segments = [x[1] for x in self._get_air_by_date_segments(curShow.tvdbid, fromDate)] else: segments = self._get_season_segments(curShow.tvdbid, fromDate) diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py index 963c6bc8d0c9da9b01a47be03f88881c876cf4f7..da136ade643be0334740c89a9ed866b8882c8167 100644 --- a/sickbeard/search_queue.py +++ b/sickbeard/search_queue.py @@ -129,7 +129,7 @@ class BacklogQueueItem(generic_queue.QueueItem): myDB = db.DBConnection() # see if there is anything in this season worth searching for - if not self.show.is_air_by_date: + if not self.show.air_by_date: statusResults = myDB.select("SELECT status FROM tv_episodes WHERE showid = ? AND season = ?", [self.show.tvdbid, self.segment]) else: segment_year, segment_month = map(int, self.segment.split('-')) diff --git a/sickbeard/showUpdater.py b/sickbeard/showUpdater.py index d8d7c1626d41a2ae373c71ddcd0e012ff42e42d0..5ffd5008874a32903bd5bf433ac8531366072887 100644 --- a/sickbeard/showUpdater.py +++ b/sickbeard/showUpdater.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import datetime from sickbeard.common import * diff --git a/sickbeard/sceneHelpers.py b/sickbeard/show_name_helpers.py similarity index 79% rename from sickbeard/sceneHelpers.py rename to sickbeard/show_name_helpers.py index 0ef745b3e489101a61125c7875e5a18ea71561ec..7b7944c70210270eef883cb5b5855a44b685ec06 100644 --- a/sickbeard/sceneHelpers.py +++ b/sickbeard/show_name_helpers.py @@ -16,20 +16,32 @@ # You should have received a copy of the GNU General Public License # along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. -from sickbeard import common +from sickbeard.common import countryList +from sickbeard.helpers import sanitizeSceneName +from sickbeard.scene_exceptions import get_scene_exceptions from sickbeard import logger from sickbeard import db import re import datetime +import urllib from name_parser.parser import NameParser, InvalidNameException resultFilters = ("sub(pack|s|bed)", "nlsub(bed|s)?", "swesub(bed)?", "(dir|sample|nfo)fix", "sample", "(dvd)?extras", - "dub(bed)?", "german", "french", "core2hd") + "dub(bed)?", "german", "french", "core2hd", + "dutch", "swedish") def filterBadReleases(name): + """ + Filters out non-english and just all-around stupid releases by comparing them + to the resultFilters contents. + + name: the release name to check + + Returns: True if the release name is OK, False if it's bad. + """ try: fp = NameParser() @@ -50,19 +62,14 @@ def filterBadReleases(name): return True -def sanitizeSceneName (name): - for x in ",:()'!?": - name = name.replace(x, "") - - name = name.replace("- ", ".").replace(" ", ".").replace("&", "and").replace('/','.') - name = re.sub("\.\.*", ".", name) - - if name.endswith('.'): - name = name[:-1] - - return name - def sceneToNormalShowNames(name): + """ + Takes a show name from a scene dirname and converts it to a more "human-readable" format. + + name: The show name to convert + + Returns: a list of all the possible "normal" names + """ if not name: return [] @@ -81,7 +88,7 @@ def sceneToNormalShowNames(name): results.append(re.sub('(\D)(\d{4})$', '\\1(\\2)', cur_name)) # add brackets around the country - country_match_str = '|'.join(common.countryList.values()) + country_match_str = '|'.join(countryList.values()) results.append(re.sub('(?i)([. _-])('+country_match_str+')$', '\\1(\\2)', cur_name)) results += name_list @@ -100,7 +107,7 @@ def makeSceneSeasonSearchString (show, segment, extraSearchType=None): myDB = db.DBConnection() - if show.is_air_by_date: + if show.air_by_date: numseasons = 0 # the search string for air by date shows is just @@ -140,7 +147,7 @@ def makeSceneSeasonSearchString (show, segment, extraSearchType=None): toReturn.append('"'+curShow+' '+str(segment).replace('-',' ')+'"') else: term_list = [x+'*' for x in seasonStrings] - if show.is_air_by_date: + if show.air_by_date: term_list = ['"'+x+'"' for x in term_list] toReturn.append('"'+curShow+'"') @@ -159,7 +166,7 @@ def makeSceneSearchString (episode): numseasons = int(numseasonsSQlResult[0][0]) # see if we should use dates instead of episodes - if episode.show.is_air_by_date and episode.airdate != datetime.date.fromordinal(1): + if episode.show.air_by_date and episode.airdate != datetime.date.fromordinal(1): epStrings = [str(episode.airdate)] else: epStrings = ["S%02iE%02i" % (int(episode.season), int(episode.episode)), @@ -179,12 +186,42 @@ def makeSceneSearchString (episode): return toReturn +def isGoodResult(name, show, log=True): + """ + Use an automatically-created regex to make sure the result actually is the show it claims to be + """ + + all_show_names = allPossibleShowNames(show) + showNames = map(sanitizeSceneName, all_show_names) + all_show_names + + for curName in set(showNames): + escaped_name = re.sub('\\\\[\\s.-]', '\W+', re.escape(curName)) + curRegex = '^' + escaped_name + '\W+(?:(?:S\d[\dE._ -])|(?:\d\d?x)|(?:\d{4}\W\d\d\W\d\d)|(?:(?:part|pt)[\._ -]?(\d|[ivx]))|Season\W+\d+\W+|E\d+\W+)' + if log: + logger.log(u"Checking if show "+name+" matches " + curRegex, logger.DEBUG) + + match = re.search(curRegex, name, re.I) + + if match: + logger.log(u"Matched "+curRegex+" to "+name, logger.DEBUG) + return True + + if log: + logger.log(u"Provider gave result "+name+" but that doesn't seem like a valid result for "+show.name+" so I'm ignoring it") + return False + def allPossibleShowNames(show): + """ + Figures out every possible variation of the name for a particular show. Includes TVDB name, TVRage name, + country codes on the end, eg. "Show Name (AU)", and any scene exception names. + + show: a TVShow object that we should get the names of + + Returns: a list of all the possible show names + """ showNames = [show.name] - - if int(show.tvdbid) in common.sceneExceptions: - showNames += common.sceneExceptions[int(show.tvdbid)] + showNames += [name for name in get_scene_exceptions(show.tvdbid)] # if we have a tvrage name then use it if show.tvrname != "" and show.tvrname != None: @@ -192,8 +229,8 @@ def allPossibleShowNames(show): newShowNames = [] - country_list = common.countryList - country_list.update(dict(zip(common.countryList.values(), common.countryList.keys()))) + country_list = countryList + country_list.update(dict(zip(countryList.values(), countryList.keys()))) # if we have "Show Name Australia" or "Show Name (Australia)" this will add "Show Name (AU)" for # any countries defined in common.countryList @@ -211,26 +248,3 @@ def allPossibleShowNames(show): return showNames -def isGoodResult(name, show, log=True): - """ - Use an automatically-created regex to make sure the result actually is the show it claims to be - """ - - all_show_names = allPossibleShowNames(show) - showNames = map(sanitizeSceneName, all_show_names) + all_show_names - - for curName in set(showNames): - escaped_name = re.sub('\\\\[\\s.-]', '\W+', re.escape(curName)) - curRegex = '^' + escaped_name + '\W+(?:(?:S\d\d)|(?:\d\d?x)|(?:\d{4}\W\d\d\W\d\d)|(?:(?:part|pt)[\._ -]?(\d|[ivx]))|Season\W+\d+\W+|E\d+\W+)' - if log: - logger.log(u"Checking if show "+name+" matches " + curRegex, logger.DEBUG) - - match = re.search(curRegex, name, re.I) - - if match: - logger.log(u"Matched "+curRegex+" to "+name, logger.DEBUG) - return True - - if log: - logger.log(u"Provider gave result "+name+" but that doesn't seem like a valid result for "+show.name+" so I'm ignoring it") - return False diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py index 83785fd636af22f119d9571c76482af3b41852be..1ff65aa50043c459fe631e90037b61ecdfd15001 100644 --- a/sickbeard/show_queue.py +++ b/sickbeard/show_queue.py @@ -29,6 +29,7 @@ from sickbeard.common import * from sickbeard.tv import TVShow from sickbeard import exceptions, helpers, logger, ui, db from sickbeard import generic_queue +from sickbeard import name_cache class ShowQueue(generic_queue.GenericQueue): @@ -177,6 +178,10 @@ class QueueItemAdd(ShowQueueItem): ShowQueueItem.__init__(self, ShowQueueActions.ADD, self.show) def _getName(self): + """ + Returns the show name if there is a show object created, if not returns + the dir that the show is being added to. + """ if self.show == None: return self.showDir return self.show.name @@ -184,6 +189,10 @@ class QueueItemAdd(ShowQueueItem): show_name = property(_getName) def _isLoading(self): + """ + Returns True if we've gotten far enough to have a show object, or False + if we still only know the folder name. + """ if self.show == None: return True return False @@ -203,17 +212,26 @@ class QueueItemAdd(ShowQueueItem): if self.lang: ltvdb_api_parms['language'] = self.lang + logger.log(u"TVDB: "+repr(ltvdb_api_parms)) + t = tvdb_api.Tvdb(**ltvdb_api_parms) s = t[self.tvdb_id] + + # this usually only happens if they have an NFO in their show dir which gave us a TVDB ID that has no + # proper english version of the show if not s or not s['seriesname']: - ui.flash.error("Unable to add show", "Show in "+str(self.showDir)+" has no name on TVDB, probably the wrong language. Delete .nfo and add manually in the correct language.") + ui.flash.error("Unable to add show", "Show in "+self.showDir+" has no name on TVDB, probably the wrong language. Delete .nfo and add manually in the correct language.") self._finishEarly() return except tvdb_exceptions.tvdb_exception, e: - ui.flash.error("Unable to add show", "Unable to look up the show in "+str(self.showDir)+" on TVDB, not using the NFO. Delete .nfo and add manually in the correct language.") + logger.log(u"Error contacting TVDB: "+str(e), logger.ERROR) + ui.flash.error("Unable to add show", "Unable to look up the show in "+self.showDir+" on TVDB, not using the NFO. Delete .nfo and add manually in the correct language.") self._finishEarly() return + # clear the name cache + name_cache.clearCache() + newShow = TVShow(self.tvdb_id, self.lang) newShow.loadFromTVDB() @@ -224,6 +242,10 @@ class QueueItemAdd(ShowQueueItem): self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.seasonfolders = self.season_folders if self.season_folders != None else sickbeard.SEASON_FOLDERS_DEFAULT self.show.paused = False + + # be smartish about this + if self.show.genre and "talk show" in self.show.genre.lower(): + self.show.air_by_date = 1 except tvdb_exceptions.tvdb_exception, e: logger.log(u"Unable to add show due to an error with TVDB: "+str(e).decode('utf-8'), logger.ERROR) diff --git a/sickbeard/tv.py b/sickbeard/tv.py index a2f337ad49ceb4f0b1d0cc1c69c0d7f6d7839bf2..885b7428191b62bc43451fad59428c497c4f3639 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -37,6 +37,7 @@ from sickbeard import helpers, exceptions, logger from sickbeard import tvrage from sickbeard import config from sickbeard import image_cache +from sickbeard import postProcessor from sickbeard import encodingKludge as ek @@ -78,11 +79,6 @@ class TVShow(object): self.saveToDB() - def _is_air_by_date(self): - return self.air_by_date or (self.genre and "Talk Show" in self.genre) - - is_air_by_date = property(_is_air_by_date) - def _getLocation(self): if ek.ek(os.path.isdir, self._location): return self._location @@ -770,7 +766,8 @@ class TVShow(object): for relEp in rootEp.relatedEps: relEp.location = result - fileList = ek.ek(glob.glob, ek.ek(os.path.join, curEpDir, actualName[0] + "*").replace("[","*").replace("]","*")) + fileList = postProcessor.PostProcessor(curLocation)._list_associated_files(curLocation) + logger.log(u"Files associated to "+curLocation+": "+str(fileList), logger.DEBUG) for file in fileList: result = helpers.rename_file(file, rootEp.prettyName()) @@ -1396,7 +1393,7 @@ class TVEpisode(object): if naming_quality == None: naming_quality = sickbeard.NAMING_QUALITY - if ((self.show.genre and "Talk Show" in self.show.genre) or self.show.air_by_date) and sickbeard.NAMING_DATES: + if self.show.air_by_date and sickbeard.NAMING_DATES: try: goodEpString = self.airdate.strftime("%Y.%m.%d") except ValueError: diff --git a/sickbeard/tvcache.py b/sickbeard/tvcache.py index 92740d538c35fbe0335345fa8dfbbde05e463d93..f2b1fae485c8a678fc1caa0dedc05f24d5fbeac7 100644 --- a/sickbeard/tvcache.py +++ b/sickbeard/tvcache.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import time import datetime import sqlite3 @@ -8,8 +26,9 @@ from sickbeard import db from sickbeard import logger from sickbeard.common import * -from sickbeard import helpers, classes, exceptions, sceneHelpers +from sickbeard import helpers, classes, exceptions, show_name_helpers from sickbeard import providers +from sickbeard import name_cache import xml.etree.cElementTree as etree @@ -207,30 +226,52 @@ class TVCache(): # if they're both empty then fill out as much info as possible by searching the show name else: - showResult = helpers.searchDBForShow(parse_result.series_name) - if showResult: - logger.log(parse_result.series_name+" was found to be show "+showResult[1]+" ("+str(showResult[0])+") in our DB.", logger.DEBUG) - tvdb_id = showResult[0] - + # check the name cache and see if we already know what show this is + logger.log(u"Checking the cache to see if we already know the tvdb id of "+parse_result.series_name, logger.DEBUG) + tvdb_id = name_cache.retrieveNameFromCache(parse_result.series_name) + + # remember if the cache lookup worked or not so we know whether we should bother updating it later + if tvdb_id == None: + logger.log(u"No cache results returned, continuing on with the search", logger.DEBUG) + from_cache = False else: + logger.log(u"Cache lookup found "+repr(tvdb_id)+", using that", logger.DEBUG) + from_cache = True + + # if the cache failed, try looking up the show name in the database + if tvdb_id == None: + logger.log(u"Trying to look the show up in the show database", logger.DEBUG) + showResult = helpers.searchDBForShow(parse_result.series_name) + if showResult: + logger.log(parse_result.series_name+" was found to be show "+showResult[1]+" ("+str(showResult[0])+") in our DB.", logger.DEBUG) + tvdb_id = showResult[0] + + # if the DB lookup fails then do a comprehensive regex search + if tvdb_id == None: logger.log(u"Couldn't figure out a show name straight from the DB, trying a regex search instead", logger.DEBUG) for curShow in sickbeard.showList: - if sceneHelpers.isGoodResult(name, curShow, False): + if show_name_helpers.isGoodResult(name, curShow, False): logger.log(u"Successfully matched "+name+" to "+curShow.name+" with regex", logger.DEBUG) tvdb_id = curShow.tvdbid tvdb_lang = curShow.lang break - if tvdb_id: + # if tvdb_id was anything but None (0 or a number) then + if not from_cache: + name_cache.addNameToCache(parse_result.series_name, tvdb_id) - showObj = helpers.findCertainShow(sickbeard.showList, tvdb_id) - if not showObj: - logger.log(u"This should never have happened, post a bug about this!", logger.ERROR) - raise Exception("BAD STUFF HAPPENED") - tvrage_id = showObj.tvrid - tvdb_lang = showObj.lang + # if we came out with tvdb_id = None it means we couldn't figure it out at all, just use 0 for that + if tvdb_id == None: + tvdb_id = 0 + # if we found the show then retrieve the show object + if tvdb_id: + showObj = helpers.findCertainShow(sickbeard.showList, tvdb_id) + if showObj: + tvrage_id = showObj.tvrid + tvdb_lang = showObj.lang + # if we weren't provided with season/episode information then get it from the name that we parsed if not season: season = parse_result.season_number if parse_result.season_number != None else 1 if not episodes: @@ -256,7 +297,6 @@ class TVCache(): episodeText = "|"+"|".join(map(str, episodes))+"|" - # get the current timestamp curTimestamp = int(time.mktime(datetime.datetime.today().timetuple())) @@ -300,7 +340,7 @@ class TVCache(): for curResult in sqlResults: # skip non-tv crap (but allow them for Newzbin cause we assume it's filtered well) - if self.providerID != 'newzbin' and not sceneHelpers.filterBadReleases(curResult["name"]): + if self.providerID != 'newzbin' and not show_name_helpers.filterBadReleases(curResult["name"]): continue # get the show object, or if it's not one of our shows then ignore it diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py index d5b59d940b5165fe304d7dba2acd5464d64b862f..e91c7e0d0b9790764a0a0830e123ff2bb0cae65a 100644 --- a/sickbeard/versionChecker.py +++ b/sickbeard/versionChecker.py @@ -19,6 +19,7 @@ import sickbeard from sickbeard import helpers, version, ui from sickbeard import logger +from sickbeard import scene_exceptions import os, os.path, platform, shutil, time import subprocess, re @@ -29,6 +30,9 @@ from urllib2 import URLError from lib.pygithub import github class CheckVersion(): + """ + Version check class meant to run as a thread object with the SB scheduler. + """ def __init__(self): self.install_type = self.find_install_type() @@ -44,8 +48,19 @@ class CheckVersion(): def run(self): self.check_for_new_version() + + # refresh scene exceptions too + scene_exceptions.retrieve_exceptions() def find_install_type(self): + """ + Determines how this copy of SB was installed. + + returns: type of installation. Possible values are: + 'win': any compiled windows build + 'git': running from source using git + 'source': running from source without git + """ # check if we're a windows build if version.SICKBEARD_VERSION.startswith('build '): @@ -58,6 +73,13 @@ class CheckVersion(): return install_type def check_for_new_version(self, force=False): + """ + Checks the internet for a newer version. + + returns: bool, True for new version or False for no new version. + + force: if true the VERSION_NOTIFY setting will be ignored and a check will be forced + """ if not sickbeard.VERSION_NOTIFY and not force: logger.log(u"Version checking is disabled, not checking for the newest version") @@ -74,7 +96,6 @@ class CheckVersion(): return True def update(self): - if self.updater.need_update(): return self.updater.update() diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 3501c5a997c4ef7f85266378ec61bba8bdbe2dba..9d06b381d1493da5684bc2e391e780015b072d85 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -42,6 +42,7 @@ from sickbeard import search_queue from sickbeard import image_cache from sickbeard.notifiers import xbmc +from sickbeard.notifiers import plex from sickbeard.providers import newznab from sickbeard.common import * @@ -589,9 +590,10 @@ class History: ConfigMenu = [ { 'title': 'General', 'path': 'config/general/' }, - { 'title': 'Episode Downloads', 'path': 'config/episodedownloads/' }, - { 'title': 'Notifications', 'path': 'config/notifications/' }, + { 'title': 'Search Settings', 'path': 'config/search/' }, { 'title': 'Search Providers', 'path': 'config/providers/' }, + { 'title': 'Post Processing', 'path': 'config/postProcessing/' }, + { 'title': 'Notifications', 'path': 'config/notifications/' }, ] class ConfigGeneral: @@ -636,12 +638,7 @@ class ConfigGeneral: @cherrypy.expose def saveGeneral(self, log_dir=None, web_port=None, web_log=None, web_ipv6=None, launch_browser=None, web_username=None, - web_password=None, season_folders_format=None, - version_notify=None, naming_show_name=None, naming_ep_type=None, - naming_multi_ep_type=None, naming_ep_name=None, - naming_use_periods=None, naming_sep_type=None, naming_quality=None, naming_dates=None, - xbmc_data=None, mediabrowser_data=None, sony_ps3_data=None, - wdtv_data=None, use_banner=None): + web_password=None, version_notify=None): results = [] @@ -665,6 +662,134 @@ class ConfigGeneral: else: version_notify = 0 + if not config.change_LOG_DIR(log_dir): + results += ["Unable to create directory " + os.path.normpath(log_dir) + ", log dir not changed."] + + sickbeard.LAUNCH_BROWSER = launch_browser + + sickbeard.WEB_PORT = int(web_port) + sickbeard.WEB_IPV6 = web_ipv6 + sickbeard.WEB_LOG = web_log + sickbeard.WEB_USERNAME = web_username + sickbeard.WEB_PASSWORD = web_password + + config.change_VERSION_NOTIFY(version_notify) + + sickbeard.save_config() + + if len(results) > 0: + for x in results: + logger.log(x, logger.ERROR) + ui.flash.error('Error(s) Saving Configuration', + '<br />\n'.join(results)) + else: + ui.flash.message('Configuration Saved', ek.ek(os.path.join, sickbeard.PROG_DIR, 'config.ini') ) + + redirect("/config/general/") + + +class ConfigSearch: + + @cherrypy.expose + def index(self): + + t = PageTemplate(file="config_search.tmpl") + t.submenu = ConfigMenu + return _munge(t) + + @cherrypy.expose + def saveSearch(self, use_nzbs=None, use_torrents=None, nzb_dir=None, sab_username=None, sab_password=None, + sab_apikey=None, sab_category=None, sab_host=None, nzbget_password=None, nzbget_category=None, nzbget_host=None, + torrent_dir=None, nzb_method=None, usenet_retention=None, search_frequency=None, download_propers=None): + + results = [] + + if not config.change_NZB_DIR(nzb_dir): + results += ["Unable to create directory " + os.path.normpath(nzb_dir) + ", dir not changed."] + + if not config.change_TORRENT_DIR(torrent_dir): + results += ["Unable to create directory " + os.path.normpath(torrent_dir) + ", dir not changed."] + + config.change_SEARCH_FREQUENCY(search_frequency) + + if download_propers == "on": + download_propers = 1 + else: + download_propers = 0 + + if use_nzbs == "on": + use_nzbs = 1 + else: + use_nzbs = 0 + + if use_torrents == "on": + use_torrents = 1 + else: + use_torrents = 0 + + if usenet_retention == None: + usenet_retention = 200 + + sickbeard.USE_NZBS = use_nzbs + sickbeard.USE_TORRENTS = use_torrents + + sickbeard.NZB_METHOD = nzb_method + sickbeard.USENET_RETENTION = int(usenet_retention) + + sickbeard.DOWNLOAD_PROPERS = download_propers + + sickbeard.SAB_USERNAME = sab_username + sickbeard.SAB_PASSWORD = sab_password + sickbeard.SAB_APIKEY = sab_apikey.strip() + sickbeard.SAB_CATEGORY = sab_category + + if sab_host and not re.match('https?://.*', sab_host): + sab_host = 'http://' + sab_host + + if not sab_host.endswith('/'): + sab_host = sab_host + '/' + + sickbeard.SAB_HOST = sab_host + + sickbeard.NZBGET_PASSWORD = nzbget_password + sickbeard.NZBGET_CATEGORY = nzbget_category + sickbeard.NZBGET_HOST = nzbget_host + + + sickbeard.save_config() + + if len(results) > 0: + for x in results: + logger.log(x, logger.ERROR) + ui.flash.error('Error(s) Saving Configuration', + '<br />\n'.join(results)) + else: + ui.flash.message('Configuration Saved', ek.ek(os.path.join, sickbeard.PROG_DIR, 'config.ini') ) + + redirect("/config/search/") + +class ConfigPostProcessing: + + @cherrypy.expose + def index(self): + + t = PageTemplate(file="config_postProcessing.tmpl") + t.submenu = ConfigMenu + return _munge(t) + + @cherrypy.expose + def savePostProcessing(self, season_folders_format=None, naming_show_name=None, naming_ep_type=None, + naming_multi_ep_type=None, naming_ep_name=None, naming_use_periods=None, + naming_sep_type=None, naming_quality=None, naming_dates=None, + xbmc_data=None, mediabrowser_data=None, sony_ps3_data=None, wdtv_data=None, use_banner=None, + keep_processed_dir=None, process_automatically=None, rename_episodes=None, + move_associated_files=None, tv_download_dir=None): + + results = [] + + if not config.change_TV_DOWNLOAD_DIR(tv_download_dir): + results += ["Unable to create directory " + os.path.normpath(tv_download_dir) + ", dir not changed."] + if naming_show_name == "on": naming_show_name = 1 else: @@ -695,10 +820,30 @@ class ConfigGeneral: else: use_banner = 0 - if not config.change_LOG_DIR(log_dir): - results += ["Unable to create directory " + os.path.normpath(log_dir) + ", log dir not changed."] + if process_automatically == "on": + process_automatically = 1 + else: + process_automatically = 0 - sickbeard.LAUNCH_BROWSER = launch_browser + if rename_episodes == "on": + rename_episodes = 1 + else: + rename_episodes = 0 + + if keep_processed_dir == "on": + keep_processed_dir = 1 + else: + keep_processed_dir = 0 + + if move_associated_files == "on": + move_associated_files = 1 + else: + move_associated_files = 0 + + sickbeard.PROCESS_AUTOMATICALLY = process_automatically + sickbeard.KEEP_PROCESSED_DIR = keep_processed_dir + sickbeard.RENAME_EPISODES = rename_episodes + sickbeard.MOVE_ASSOCIATED_FILES = move_associated_files sickbeard.metadata_provider_dict['XBMC'].set_config(xbmc_data) sickbeard.metadata_provider_dict['MediaBrowser'].set_config(mediabrowser_data) @@ -716,16 +861,8 @@ class ConfigGeneral: sickbeard.NAMING_MULTI_EP_TYPE = int(naming_multi_ep_type) sickbeard.NAMING_SEP_TYPE = int(naming_sep_type) - sickbeard.WEB_PORT = int(web_port) - sickbeard.WEB_IPV6 = web_ipv6 - sickbeard.WEB_LOG = web_log - sickbeard.WEB_USERNAME = web_username - sickbeard.WEB_PASSWORD = web_password - sickbeard.USE_BANNER = use_banner - config.change_VERSION_NOTIFY(version_notify) - sickbeard.save_config() if len(results) > 0: @@ -734,10 +871,9 @@ class ConfigGeneral: ui.flash.error('Error(s) Saving Configuration', '<br />\n'.join(results)) else: - ui.flash.message('Configuration Saved', os.path.join(sickbeard.PROG_DIR, 'config.ini') ) - - redirect("/config/general/") + ui.flash.message('Configuration Saved', ek.ek(os.path.join, sickbeard.PROG_DIR, 'config.ini') ) + redirect("/config/postProcessing/") @cherrypy.expose def testNaming(self, show_name=None, ep_type=None, multi_ep_type=None, ep_name=None, @@ -820,99 +956,6 @@ class ConfigGeneral: return name -class ConfigEpisodeDownloads: - - @cherrypy.expose - def index(self): - - t = PageTemplate(file="config_episodedownloads.tmpl") - t.submenu = ConfigMenu - return _munge(t) - - @cherrypy.expose - def saveEpisodeDownloads(self, nzb_dir=None, sab_username=None, sab_password=None, - sab_apikey=None, sab_category=None, sab_host=None, - torrent_dir=None, nzb_method=None, usenet_retention=None, - search_frequency=None, tv_download_dir=None, - keep_processed_dir=None, process_automatically=None, rename_episodes=None, - download_propers=None, move_associated_files=None): - - results = [] - - if not config.change_TV_DOWNLOAD_DIR(tv_download_dir): - results += ["Unable to create directory " + os.path.normpath(tv_download_dir) + ", dir not changed."] - - if not config.change_NZB_DIR(nzb_dir): - results += ["Unable to create directory " + os.path.normpath(nzb_dir) + ", dir not changed."] - - if not config.change_TORRENT_DIR(torrent_dir): - results += ["Unable to create directory " + os.path.normpath(torrent_dir) + ", dir not changed."] - - config.change_SEARCH_FREQUENCY(search_frequency) - - if download_propers == "on": - download_propers = 1 - else: - download_propers = 0 - - if process_automatically == "on": - process_automatically = 1 - else: - process_automatically = 0 - - if rename_episodes == "on": - rename_episodes = 1 - else: - rename_episodes = 0 - - if keep_processed_dir == "on": - keep_processed_dir = 1 - else: - keep_processed_dir = 0 - - if move_associated_files == "on": - move_associated_files = 1 - else: - move_associated_files = 0 - - if usenet_retention == None: - usenet_retention = 200 - - sickbeard.PROCESS_AUTOMATICALLY = process_automatically - sickbeard.KEEP_PROCESSED_DIR = keep_processed_dir - sickbeard.RENAME_EPISODES = rename_episodes - sickbeard.MOVE_ASSOCIATED_FILES = move_associated_files - - sickbeard.NZB_METHOD = nzb_method - sickbeard.USENET_RETENTION = int(usenet_retention) - - sickbeard.DOWNLOAD_PROPERS = download_propers - - sickbeard.SAB_USERNAME = sab_username - sickbeard.SAB_PASSWORD = sab_password - sickbeard.SAB_APIKEY = sab_apikey.strip() - sickbeard.SAB_CATEGORY = sab_category - - if sab_host and not re.match('https?://.*', sab_host): - sab_host = 'http://' + sab_host - - if not sab_host.endswith('/'): - sab_host = sab_host + '/' - - sickbeard.SAB_HOST = sab_host - - sickbeard.save_config() - - if len(results) > 0: - for x in results: - logger.log(x, logger.ERROR) - ui.flash.error('Error(s) Saving Configuration', - '<br />\n'.join(results)) - else: - ui.flash.message('Configuration Saved', os.path.join(sickbeard.PROG_DIR, 'config.ini') ) - - redirect("/config/episodedownloads/") - class ConfigProviders: @cherrypy.expose @@ -985,6 +1028,7 @@ class ConfigProviders: nzbs_org_hash=None, nzbmatrix_username=None, nzbmatrix_apikey=None, tvbinz_auth=None, provider_order=None, nzbs_r_us_uid=None, nzbs_r_us_hash=None, newznab_string=None, + tvtorrents_digest=None, tvtorrents_hash=None, newzbin_username=None, newzbin_password=None): results = [] @@ -1048,6 +1092,8 @@ class ConfigProviders: sickbeard.WOMBLE = curEnabled elif curProvider == 'ezrss': sickbeard.EZRSS = curEnabled + elif curProvider == 'tvtorrents': + sickbeard.TVTORRENTS = curEnabled elif curProvider in newznabProviderDict: newznabProviderDict[curProvider].enabled = bool(curEnabled) else: @@ -1059,6 +1105,9 @@ class ConfigProviders: sickbeard.TVBINZ_HASH = tvbinz_hash.strip() if tvbinz_auth: sickbeard.TVBINZ_AUTH = tvbinz_auth.strip() + + sickbeard.TVTORRENTS_DIGEST = tvtorrents_digest.strip() + sickbeard.TVTORRENTS_HASH = tvtorrents_hash.strip() sickbeard.NZBS_UID = nzbs_org_uid.strip() sickbeard.NZBS_HASH = nzbs_org_hash.strip() @@ -1082,7 +1131,7 @@ class ConfigProviders: ui.flash.error('Error(s) Saving Configuration', '<br />\n'.join(results)) else: - ui.flash.message('Configuration Saved', os.path.join(sickbeard.PROG_DIR, 'config.ini') ) + ui.flash.message('Configuration Saved', ek.ek(os.path.join, sickbeard.PROG_DIR, 'config.ini') ) redirect("/config/providers/") @@ -1097,11 +1146,14 @@ class ConfigNotifications: @cherrypy.expose def saveNotifications(self, use_xbmc=None, xbmc_notify_onsnatch=None, xbmc_notify_ondownload=None, xbmc_update_library=None, xbmc_update_full=None, xbmc_host=None, xbmc_username=None, xbmc_password=None, + use_plex=None, plex_notify_onsnatch=None, plex_notify_ondownload=None, plex_update_library=None, + plex_server_host=None, plex_host=None, plex_username=None, plex_password=None, use_growl=None, growl_notify_onsnatch=None, growl_notify_ondownload=None, growl_host=None, growl_password=None, use_prowl=None, prowl_notify_onsnatch=None, prowl_notify_ondownload=None, prowl_api=None, prowl_priority=0, use_twitter=None, twitter_notify_onsnatch=None, twitter_notify_ondownload=None, use_notifo=None, notifo_notify_onsnatch=None, notifo_notify_ondownload=None, notifo_username=None, notifo_apisecret=None, - use_libnotify=None, libnotify_notify_onsnatch=None, libnotify_notify_ondownload=None): + use_libnotify=None, libnotify_notify_onsnatch=None, libnotify_notify_ondownload=None, + use_nmj=None, nmj_host=None, nmj_database=None, nmj_mount=None): results = [] @@ -1129,7 +1181,27 @@ class ConfigNotifications: use_xbmc = 1 else: use_xbmc = 0 - + + if plex_update_library == "on": + plex_update_library = 1 + else: + plex_update_library = 0 + + if plex_notify_onsnatch == "on": + plex_notify_onsnatch = 1 + else: + plex_notify_onsnatch = 0 + + if plex_notify_ondownload == "on": + plex_notify_ondownload = 1 + else: + plex_notify_ondownload = 0 + + if use_plex == "on": + use_plex = 1 + else: + use_plex = 0 + if growl_notify_onsnatch == "on": growl_notify_onsnatch = 1 else: @@ -1139,6 +1211,7 @@ class ConfigNotifications: growl_notify_ondownload = 1 else: growl_notify_ondownload = 0 + if use_growl == "on": use_growl = 1 else: @@ -1186,6 +1259,11 @@ class ConfigNotifications: else: use_notifo = 0 + if use_nmj == "on": + use_nmj = 1 + else: + use_nmj = 0 + sickbeard.USE_XBMC = use_xbmc sickbeard.XBMC_NOTIFY_ONSNATCH = xbmc_notify_onsnatch sickbeard.XBMC_NOTIFY_ONDOWNLOAD = xbmc_notify_ondownload @@ -1195,6 +1273,15 @@ class ConfigNotifications: sickbeard.XBMC_USERNAME = xbmc_username sickbeard.XBMC_PASSWORD = xbmc_password + sickbeard.USE_PLEX = use_plex + sickbeard.PLEX_NOTIFY_ONSNATCH = plex_notify_onsnatch + sickbeard.PLEX_NOTIFY_ONDOWNLOAD = plex_notify_ondownload + sickbeard.PLEX_UPDATE_LIBRARY = plex_update_library + sickbeard.PLEX_HOST = plex_host + sickbeard.PLEX_SERVER_HOST = plex_server_host + sickbeard.PLEX_USERNAME = plex_username + sickbeard.PLEX_PASSWORD = plex_password + sickbeard.USE_GROWL = use_growl sickbeard.GROWL_NOTIFY_ONSNATCH = growl_notify_onsnatch sickbeard.GROWL_NOTIFY_ONDOWNLOAD = growl_notify_ondownload @@ -1221,6 +1308,11 @@ class ConfigNotifications: sickbeard.LIBNOTIFY_NOTIFY_ONSNATCH = libnotify_notify_onsnatch == "on" sickbeard.LIBNOTIFY_NOTIFY_ONDOWNLOAD = libnotify_notify_ondownload == "on" + sickbeard.USE_NMJ = use_nmj + sickbeard.NMJ_HOST = nmj_host + sickbeard.NMJ_DATABASE = nmj_database + sickbeard.NMJ_MOUNT = nmj_mount + sickbeard.save_config() if len(results) > 0: @@ -1229,7 +1321,7 @@ class ConfigNotifications: ui.flash.error('Error(s) Saving Configuration', '<br />\n'.join(results)) else: - ui.flash.message('Configuration Saved', os.path.join(sickbeard.PROG_DIR, 'config.ini') ) + ui.flash.message('Configuration Saved', ek.ek(os.path.join, sickbeard.PROG_DIR, 'config.ini') ) redirect("/config/notifications/") @@ -1245,20 +1337,26 @@ class Config: general = ConfigGeneral() - episodedownloads = ConfigEpisodeDownloads() + search = ConfigSearch() + + postProcessing = ConfigPostProcessing() providers = ConfigProviders() notifications = ConfigNotifications() -def haveXBMC(): - return sickbeard.XBMC_HOST != None and len(sickbeard.XBMC_HOST) > 0 - +def haveXBMC(): + return sickbeard.XBMC_HOST + +def havePLEX(): + return sickbeard.PLEX_SERVER_HOST + def HomeMenu(): return [ { 'title': 'Add Shows', 'path': 'home/addShows/', }, { 'title': 'Manual Post-Processing', 'path': 'home/postprocess/' }, - { 'title': 'Update XBMC', 'path': 'home/updateXBMC/', 'requires': haveXBMC }, + { 'title': 'Update XBMC', 'path': 'home/updateXBMC/', 'requires': haveXBMC }, + { 'title': 'Update Plex', 'path': 'home/updatePLEX/', 'requires': havePLEX }, { 'title': 'Restart', 'path': 'home/restart/?pid='+str(sickbeard.PID) }, { 'title': 'Shutdown', 'path': 'home/shutdown/' }, ] @@ -1699,6 +1797,15 @@ class ErrorLogs: class Home: + @cherrypy.expose + def is_alive(self): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + + if sickbeard.started: + return str(sickbeard.PID) + else: + return "nope" + @cherrypy.expose def index(self): @@ -1712,6 +1819,8 @@ class Home: @cherrypy.expose def testGrowl(self, host=None, password=None): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + result = notifiers.growl_notifier.test_notify(host, password) if password==None or password=='': pw_append = '' @@ -1725,6 +1834,8 @@ class Home: @cherrypy.expose def testProwl(self, prowl_api=None, prowl_priority=0): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + result = notifiers.prowl_notifier.test_notify(prowl_api, prowl_priority) if result: return "Test prowl notice sent successfully" @@ -1733,6 +1844,8 @@ class Home: @cherrypy.expose def testNotifo(self, username=None, apisecret=None): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + result = notifiers.notifo_notifier.test_notify(username, apisecret) if result: return "Notifo notification succeeded. Check your Notifo clients to make sure it worked" @@ -1741,10 +1854,14 @@ class Home: @cherrypy.expose def twitterStep1(self): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + return notifiers.twitter_notifier._get_authorization() @cherrypy.expose def twitterStep2(self, key): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + result = notifiers.twitter_notifier._get_credentials(key) logger.log(u"result: "+str(result)) if result: @@ -1754,6 +1871,8 @@ class Home: @cherrypy.expose def testTwitter(self): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + result = notifiers.twitter_notifier.test_notify() if result: return "Tweet successful, check your twitter to make sure it worked" @@ -1762,19 +1881,53 @@ class Home: @cherrypy.expose def testXBMC(self, host=None, username=None, password=None): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + result = notifiers.xbmc_notifier.test_notify(urllib.unquote_plus(host), username, password) if result: return "Test notice sent successfully to "+urllib.unquote_plus(host) else: return "Test notice failed to "+urllib.unquote_plus(host) + @cherrypy.expose + def testPLEX(self, host=None, username=None, password=None): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + + result = notifiers.plex_notifier.test_notify(urllib.unquote_plus(host), username, password) + if result: + return "Test notice sent successfully to "+urllib.unquote_plus(host) + else: + return "Test notice failed to "+urllib.unquote_plus(host) + @cherrypy.expose def testLibnotify(self): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + if notifiers.libnotify_notifier.test_notify(): return "Tried sending desktop notification via libnotify" else: return notifiers.libnotify.diagnose() + @cherrypy.expose + def testNMJ(self, host=None, database=None, mount=None): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + + result = notifiers.nmj_notifier.test_notify(urllib.unquote_plus(host), database, mount) + if result: + return "Successfull started the scan update" + else: + return "Test failed to start the scan update" + + @cherrypy.expose + def settingsNMJ(self, host=None): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + + result = notifiers.nmj_notifier.notify_settings(urllib.unquote_plus(host)) + if result: + return '{"message": "Got settings from %(host)s", "database": "%(database)s", "mount": "%(mount)s"}' % {"host": host, "database": sickbeard.NMJ_DATABASE, "mount": sickbeard.NMJ_MOUNT} + else: + return '{"message": "Failed! Make sure your Popcorn is on and NMJ is running. (see Log & Errors -> Debug for detailed info)", "database": "", "mount": ""}' + @cherrypy.expose def shutdown(self): @@ -1792,13 +1945,13 @@ class Home: if str(pid) != str(sickbeard.PID): redirect("/home") + t = PageTemplate(file="restart.tmpl") + t.submenu = HomeMenu() + # do a soft restart threading.Timer(2, sickbeard.invoke_restart, [False]).start() - title = "Restarting" - message = "Sick Beard is restarting, refresh in 30 seconds." - - return _genericMessage(title, message) + return _munge(t) @cherrypy.expose def update(self, pid=None): @@ -1811,7 +1964,8 @@ class Home: if updated: # do a hard restart threading.Timer(2, sickbeard.invoke_restart, [False]).start() - return "Sick Beard is restarting, refresh in 30 seconds." + t = PageTemplate(file="restart_bare.tmpl") + return _munge(t) else: return _genericMessage("Update Failed","Update wasn't successful, not restarting. Check your log for more information.") @@ -2091,15 +2245,27 @@ class Home: redirect("/home/displayShow?show="+str(showObj.tvdbid)) - @cherrypy.expose - def updateXBMC(self, showName=None): - - for curHost in [x.strip() for x in sickbeard.XBMC_HOST.split(",")]: - if notifiers.xbmc_notifier._update_library(curHost, showName=showName): - ui.flash.message("Command sent to XBMC host " + curHost + " to update library") - else: - ui.flash.error("Unable to contact XBMC host " + curHost) - redirect('/home') + @cherrypy.expose + def updateXBMC(self, showName=None): + + for curHost in [x.strip() for x in sickbeard.XBMC_HOST.split(",")]: + if notifiers.xbmc_notifier._update_library(curHost, showName=showName): + ui.flash.message("Command sent to XBMC host " + curHost + " to update library") + else: + ui.flash.error("Unable to contact XBMC host " + curHost) + redirect('/home') + + + @cherrypy.expose + def updatePLEX(self): + + if notifiers.plex_notifier._update_library(): + ui.flash.message("Command sent to Plex Media Server host " + sickbeard.PLEX_HOST + " to update library") + logger.log(u"Plex library update initiated for host " + sickbeard.PLEX_HOST, logger.DEBUG) + else: + ui.flash.error("Unable to contact Plex Media Server host " + sickbeard.PLEX_HOST) + logger.log(u"Plex library update failed for host " + sickbeard.PLEX_HOST, logger.ERROR) + redirect('/home') @cherrypy.expose @@ -2163,7 +2329,7 @@ class Home: if int(status) == WANTED: # figure out what segment the episode is in and remember it so we can backlog it - if epObj.show.is_air_by_date: + if epObj.show.air_by_date: ep_segment = str(epObj.airdate)[:7] else: ep_segment = epObj.season diff --git a/sickbeard/webserveInit.py b/sickbeard/webserveInit.py index 640e37a4efebac50ea796b5d092520dba54eb651..26275c159f733ee52bcafdd98633122454ef3ffc 100644 --- a/sickbeard/webserveInit.py +++ b/sickbeard/webserveInit.py @@ -1,3 +1,21 @@ +# Author: Nic Wolfe <nic@wolfeden.ca> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. + import cherrypy import cherrypy.lib.auth_basic import os.path diff --git a/tests/scene_helpers_tests.py b/tests/scene_helpers_tests.py index b7e518405467247a8d4e24a04f50a50efd577850..6f4fc626b2c5a5277de4738ee735c3e8716b4c80 100644 --- a/tests/scene_helpers_tests.py +++ b/tests/scene_helpers_tests.py @@ -3,7 +3,11 @@ import unittest import sys, os.path sys.path.append(os.path.abspath('..')) -from sickbeard import sceneHelpers, common +from sickbeard import show_name_helpers, scene_exceptions, common + +import sickbeard +from sickbeard import db +from sickbeard.databases import cache_db class Show: def __init__(self, name, tvdbid, tvrname): @@ -14,24 +18,24 @@ class Show: class SceneTests(unittest.TestCase): def _test_sceneToNormalShowNames(self, name, expected): - result = sceneHelpers.sceneToNormalShowNames(name) + result = show_name_helpers.sceneToNormalShowNames(name) self.assertTrue(len(set(expected).intersection(set(result))) == len(expected)) - dot_result = sceneHelpers.sceneToNormalShowNames(name.replace(' ','.')) + dot_result = show_name_helpers.sceneToNormalShowNames(name.replace(' ','.')) dot_expected = [x.replace(' ','.') for x in expected] self.assertTrue(len(set(dot_expected).intersection(set(dot_result))) == len(dot_expected)) def _test_allPossibleShowNames(self, name, tvdbid=0, tvrname=None, expected=[]): - result = sceneHelpers.allPossibleShowNames(Show(name, tvdbid, tvrname)) + result = show_name_helpers.allPossibleShowNames(Show(name, tvdbid, tvrname)) self.assertTrue(len(set(expected).intersection(set(result))) == len(expected)) def _test_filterBadReleases(self, name, expected): - result = sceneHelpers.filterBadReleases(name) + result = show_name_helpers.filterBadReleases(name) self.assertEqual(result, expected) def _test_isGoodName(self, name, show): - self.assertTrue(sceneHelpers.isGoodResult(name, show)) + self.assertTrue(show_name_helpers.isGoodResult(name, show)) def test_isGoodName(self): self._test_isGoodName('Show.Name.S01E02.Test-Test', Show('Show/Name', 0, '')) @@ -58,7 +62,9 @@ class SceneTests(unittest.TestCase): self._test_sceneToNormalShowNames('Show Name YA', ['Show Name YA']) def test_allPossibleShowNames(self): - common.sceneExceptions[-1] = ['Exception Test'] + #common.sceneExceptions[-1] = ['Exception Test'] + myDB = db.DBConnection("cache.db") + myDB.action("INSERT INTO scene_exceptions (tvdb_id, show_name) VALUES (?,?)", [-1, 'Exception Test']) common.countryList['Full Country Name'] = 'FCN' self._test_allPossibleShowNames('Show Name', expected=['Show Name']) @@ -77,9 +83,38 @@ class SceneTests(unittest.TestCase): self._test_filterBadReleases('German.Show.S02.Some.Stuff-Grp', True) self._test_filterBadReleases('Show.S02.This.Is.German', False) + + + +print 'Loading exceptions...', +db.upgradeDatabase(db.DBConnection("cache.db"), cache_db.InitialSchema) +scene_exceptions.retrieve_exceptions() +print 'done.' + +class SceneExceptionTestCase(unittest.TestCase): + + def test_sceneExceptionsEmpty(self): + self.assertEqual(scene_exceptions.get_scene_exceptions(0), []) + + def test_sceneExceptionsBabylon5(self): + self.assertEqual(sorted(scene_exceptions.get_scene_exceptions(70726)), ['Babylon 5', 'Babylon5']) + + def test_sceneExceptionByName(self): + self.assertEqual(scene_exceptions.get_scene_exception_by_name('Babylon5'), 70726) + self.assertEqual(scene_exceptions.get_scene_exception_by_name('babylon 5'), 70726) + self.assertEqual(scene_exceptions.get_scene_exception_by_name('Carlos 2010'), 164451) + + def test_sceneExceptionByNameEmpty(self): + self.assertEqual(scene_exceptions.get_scene_exception_by_name('nothing useful'), None) + + + if __name__ == '__main__': if len(sys.argv) > 1: - suite = unittest.TestLoader().loadTestsFromName('scene_helpers_tests.SceneTests.test_'+sys.argv[1]) + suite = unittest.TestLoader().loadTestsFromName('scene_helpers_tests.SceneExceptionTestCase.test_'+sys.argv[1]) + unittest.TextTestRunner(verbosity=2).run(suite) else: suite = unittest.TestLoader().loadTestsFromTestCase(SceneTests) - unittest.TextTestRunner(verbosity=2).run(suite) + unittest.TextTestRunner(verbosity=2).run(suite) + suite = unittest.TestLoader().loadTestsFromTestCase(SceneExceptionTestCase) + unittest.TextTestRunner(verbosity=2).run(suite)