Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of github.com:midgetspy/Sick-Beard into windows…

…_binaries

Conflicts:
	SickBeard.py
  • Loading branch information...
commit 8f45c4bf3970e5d348fae3eb93a6dfe74ea6bbd2 2 parents 631f576 + 7c7e22d
@midgetspy midgetspy authored
Showing with 2,499 additions and 1,250 deletions.
  1. +1 −1  .gitignore
  2. +269 −238 SickBeard.py
  3. +9 −1 data/css/config.css
  4. +14 −2 data/css/default.css
  5. BIN  data/images/loading16_dddddd.gif
  6. BIN  data/images/loading_posters.gif
  7. BIN  data/images/providers/sick_beard_index.gif
  8. +2 −0  data/interfaces/default/config.tmpl
  9. +7 −5 data/interfaces/default/config_general.tmpl
  10. +12 −12 data/interfaces/default/config_notifications.tmpl
  11. +7 −6 data/interfaces/default/config_postProcessing.tmpl
  12. +19 −29 data/interfaces/default/config_providers.tmpl
  13. +11 −6 data/interfaces/default/config_search.tmpl
  14. +8 −3 data/interfaces/default/displayShow.tmpl
  15. +2 −16 data/interfaces/default/inc_top.tmpl
  16. +22 −0 data/js/ajaxNotifications.js
  17. +24 −1 data/js/config.js
  18. +18 −3 data/js/configProviders.js
  19. +13 −0 data/js/configSearch.js
  20. +39 −11 data/js/displayShow.js
  21. +815 −0 data/js/jquery.form-2.69.js
  22. +95 −0 init.solaris11
  23. +3 −3 lib/httplib2/__init__.py
  24. +2 −1  lib/tvdb_api/tvdb_api.py
  25. +54 −54 sickbeard/__init__.py
  26. +3 −11 sickbeard/autoPostProcesser.py
  27. +1 −2  sickbeard/browser.py
  28. +1 −3 sickbeard/classes.py
  29. +0 −1  sickbeard/common.py
  30. +2 −2 sickbeard/config.py
  31. +7 −8 sickbeard/databases/mainDB.py
  32. +134 −121 sickbeard/db.py
  33. +0 −2  sickbeard/encodingKludge.py
  34. +22 −1 sickbeard/exceptions.py
  35. +21 −3 sickbeard/generic_queue.py
  36. +26 −16 sickbeard/helpers.py
  37. +1 −4 sickbeard/history.py
  38. +1 −3 sickbeard/image_cache.py
  39. +132 −55 sickbeard/logger.py
  40. +13 −15 sickbeard/metadata/generic.py
  41. +4 −8 sickbeard/metadata/helpers.py
  42. +7 −6 sickbeard/metadata/mediabrowser.py
  43. +1 −7 sickbeard/metadata/ps3.py
  44. +3 −6 sickbeard/metadata/wdtv.py
  45. +5 −4 sickbeard/metadata/xbmc.py
  46. +0 −2  sickbeard/name_cache.py
  47. +4 −2 sickbeard/name_parser/parser.py
  48. +52 −17 sickbeard/notifiers/growl.py
  49. +12 −3 sickbeard/notifiers/libnotify.py
  50. +19 −16 sickbeard/notifiers/notifo.py
  51. +11 −4 sickbeard/notifiers/plex.py
  52. +0 −3  sickbeard/notifiers/prowl.py
  53. +7 −9 sickbeard/notifiers/tweet.py
  54. +4 −7 sickbeard/notifiers/xbmc.py
  55. +3 −3 sickbeard/nzbSplitter.py
  56. +1 −2  sickbeard/nzbget.py
  57. +30 −25 sickbeard/postProcessor.py
  58. +8 −5 sickbeard/processTV.py
  59. +8 −6 sickbeard/properFinder.py
  60. +6 −2 sickbeard/providers/__init__.py
  61. +3 −2 sickbeard/providers/ezrss.py
  62. +18 −13 sickbeard/providers/generic.py
  63. +20 −6 sickbeard/providers/newzbin.py
  64. +10 −4 sickbeard/providers/newznab.py
  65. +3 −3 sickbeard/providers/nzbmatrix.py
  66. +5 −4 sickbeard/providers/nzbs_org.py
  67. +0 −1  sickbeard/providers/nzbsrus.py
  68. +0 −120 sickbeard/providers/tvbinz.py
  69. +0 −3  sickbeard/providers/tvtorrents.py
  70. +1 −4 sickbeard/providers/womble.py
  71. +6 −5 sickbeard/sab.py
  72. +13 −4 sickbeard/scene_exceptions.py
  73. +2 −3 sickbeard/scheduler.py
  74. +14 −11 sickbeard/search.py
  75. +10 −9 sickbeard/searchBacklog.py
  76. +3 −7 sickbeard/searchCurrent.py
  77. +54 −3 sickbeard/search_queue.py
  78. +5 −4 sickbeard/showUpdater.py
  79. +17 −7 sickbeard/show_name_helpers.py
  80. +30 −24 sickbeard/show_queue.py
  81. +20 −15 sickbeard/tv.py
  82. +10 −5 sickbeard/tvcache.py
  83. +10 −9 sickbeard/tvrage.py
  84. +90 −62 sickbeard/ui.py
  85. +13 −12 sickbeard/versionChecker.py
  86. +126 −132 sickbeard/webserve.py
  87. +1 −1  sickbeard/webserveInit.py
  88. +20 −1 tests/scene_helpers_tests.py
View
2  .gitignore
@@ -1,6 +1,6 @@
*.pyc
cache/*
-cache.db
+cache.db*
config.ini
Logs/*
sickbeard.db*
View
507 SickBeard.py
@@ -1,165 +1,172 @@
-#!/usr/bin/env python
-# 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 sys
-
-# we only need this for compiling an EXE and I will just always do that on 2.6+
-if sys.hexversion >= 0x020600F0:
- from multiprocessing import Process, freeze_support
-
-import locale
-import os
-import os.path
-import threading
-import time
-import signal
-import sqlite3
-import traceback
-import getopt
-
-import sickbeard
-
-from sickbeard import db
-from sickbeard.tv import TVShow
-from sickbeard import logger
-from sickbeard.common import *
-from sickbeard.version import SICKBEARD_VERSION
-
-from sickbeard.webserveInit import initWebServer
-
-from lib.configobj import ConfigObj
-
-signal.signal(signal.SIGINT, sickbeard.sig_handler)
-signal.signal(signal.SIGTERM, sickbeard.sig_handler)
-
-def loadShowsFromDB():
-
- myDB = db.DBConnection()
- sqlResults = myDB.select("SELECT * FROM tv_shows")
-
- for sqlShow in sqlResults:
- try:
- curShow = TVShow(int(sqlShow["tvdb_id"]))
- sickbeard.showList.append(curShow)
- except Exception, e:
- logger.log(u"There was an error creating the show in "+sqlShow["location"]+": "+str(e).decode('utf-8'), logger.ERROR)
- logger.log(traceback.format_exc(), logger.DEBUG)
-
- #TODO: make it update the existing shows if the showlist has something in it
-
-def daemonize():
- # Make a non-session-leader child process
- try:
- pid = os.fork()
- if pid != 0:
- sys.exit(0)
- except OSError, e:
- raise RuntimeError("1st fork failed: %s [%d]" %
- (e.strerror, e.errno))
-
- os.chdir(sickbeard.PROG_DIR)
- os.setsid()
-
- # Make sure I can read my own files and shut out others
- prev = os.umask(0)
- os.umask(prev and int('077',8))
-
- # Make the child a session-leader by detaching from the terminal
- try:
- pid = os.fork()
- if pid != 0:
- sys.exit(0)
- except OSError, e:
- raise RuntimeError("2st fork failed: %s [%d]" %
- (e.strerror, e.errno))
- raise Exception, "%s [%d]" % (e.strerror, e.errno)
-
- dev_null = file('/dev/null', 'r')
- os.dup2(dev_null.fileno(), sys.stdin.fileno())
-
+#!/usr/bin/env python
+# 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 sys
+
+# we only need this for compiling an EXE and I will just always do that on 2.6+
+if sys.hexversion >= 0x020600F0:
+ from multiprocessing import freeze_support
+
+import locale
+import os
+import threading
+import time
+import signal
+import traceback
+import getopt
+
+import sickbeard
+
+from sickbeard import db
+from sickbeard.tv import TVShow
+from sickbeard import logger
+from sickbeard.version import SICKBEARD_VERSION
+
+from sickbeard.webserveInit import initWebServer
+
+from lib.configobj import ConfigObj
+
+signal.signal(signal.SIGINT, sickbeard.sig_handler)
+signal.signal(signal.SIGTERM, sickbeard.sig_handler)
+
+def loadShowsFromDB():
+
+ myDB = db.DBConnection()
+ sqlResults = myDB.select("SELECT * FROM tv_shows")
+
+ for sqlShow in sqlResults:
+ try:
+ curShow = TVShow(int(sqlShow["tvdb_id"]))
+ sickbeard.showList.append(curShow)
+ except Exception, e:
+ logger.log(u"There was an error creating the show in "+sqlShow["location"]+": "+str(e).decode('utf-8'), logger.ERROR)
+ logger.log(traceback.format_exc(), logger.DEBUG)
+
+ #TODO: make it update the existing shows if the showlist has something in it
+
+def daemonize():
+ # Make a non-session-leader child process
+ try:
+ pid = os.fork() #@UndefinedVariable - only available in UNIX
+ if pid != 0:
+ sys.exit(0)
+ except OSError, e:
+ raise RuntimeError("1st fork failed: %s [%d]" %
+ (e.strerror, e.errno))
+
+ os.setsid() #@UndefinedVariable - only available in UNIX
+
+ # Make sure I can read my own files and shut out others
+ prev = os.umask(0)
+ os.umask(prev and int('077',8))
+
+ # Make the child a session-leader by detaching from the terminal
+ try:
+ pid = os.fork() #@UndefinedVariable - only available in UNIX
+ if pid != 0:
+ sys.exit(0)
+ except OSError, e:
+ raise RuntimeError("2st fork failed: %s [%d]" %
+ (e.strerror, e.errno))
+ raise Exception, "%s [%d]" % (e.strerror, e.errno)
+
+ 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
- sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(sys.argv[0]))
- sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
- sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
- sickbeard.MY_ARGS = sys.argv[1:]
+def main():
+
+ # do some preliminary stuff
+ sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(sys.argv[0]))
+ sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
+ sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
+ sickbeard.DATA_DIR = sickbeard.PROG_DIR
+ sickbeard.MY_ARGS = sys.argv[1:]
sickbeard.CREATEPID = False
- try:
- locale.setlocale(locale.LC_ALL, "")
- except (locale.Error, IOError):
- pass
- sickbeard.SYS_ENCODING = locale.getpreferredencoding()
-
- # for OSes that are poorly configured I'll just force UTF-8
- if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII'):
- sickbeard.SYS_ENCODING = 'UTF-8'
-
- sickbeard.CONFIG_FILE = os.path.join(sickbeard.PROG_DIR, "config.ini")
-
- # need console logging for SickBeard.py and SickBeard-console.exe
- consoleLogging = (not hasattr(sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)
-
- # rename the main thread
- threading.currentThread().name = "MAIN"
-
- try:
- opts, args = getopt.getopt(sys.argv[1:], "qfdp::", ['quiet', 'forceupdate', 'daemon', 'port=', 'tvbinz', 'pidfile='])
- except getopt.GetoptError:
- print "Available options: --quiet, --forceupdate, --port, --daemon --pidfile"
- sys.exit()
-
- forceUpdate = False
+ sickbeard.SYS_ENCODING = None
+
+ try:
+ locale.setlocale(locale.LC_ALL, "")
+ sickbeard.SYS_ENCODING = locale.getpreferredencoding()
+ except (locale.Error, IOError):
+ pass
+
+ # for OSes that are poorly configured I'll just force UTF-8
+ if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
+ sickbeard.SYS_ENCODING = 'UTF-8'
+
+ # need console logging for SickBeard.py and SickBeard-console.exe
+ consoleLogging = (not hasattr(sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)
+
+ # rename the main thread
+ threading.currentThread().name = "MAIN"
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "qfdp::", ['quiet', 'forceupdate', 'daemon', 'port=', 'pidfile=', 'nolaunch', 'config=', 'datadir=']) #@UnusedVariable
+ except getopt.GetoptError:
+ print "Available options: --quiet, --forceupdate, --port, --daemon, --pidfile, --config, --datadir"
+ sys.exit()
+
+ forceUpdate = False
forcedPort = None
-
+ noLaunch = False
+
for o, a in opts:
- # for now we'll just silence the logging
+ # for now we'll just silence the logging
if o in ('-q', '--quiet'):
- consoleLogging = False
- # for now we'll just silence the logging
- if o in ('--tvbinz'):
- sickbeard.SHOW_TVBINZ = True
-
+ consoleLogging = False
+
# 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'):
- forcedPort = int(a)
+ # should we update right away?
+ if o in ('--nolaunch',):
+ noLaunch = True
- # Run as a daemon
+ # use a different port
+ if o in ('-p', '--port'):
+ forcedPort = int(a)
+
+ # Run as a daemon
if o in ('-d', '--daemon'):
- if sys.platform == 'win32':
- print "Daemonize not supported under Windows, starting normally"
- else:
- consoleLogging = False
- sickbeard.DAEMON = True
-
+ if sys.platform == 'win32':
+ print "Daemonize not supported under Windows, starting normally"
+ else:
+ consoleLogging = False
+ sickbeard.DAEMON = True
+
+ # config file
+ if o in ('--config',):
+ sickbeard.CONFIG_FILE = os.path.abspath(a)
+
+ # datadir
+ if o in ('--datadir',):
+ sickbeard.DATA_DIR = os.path.abspath(a)
+
# write a pidfile if requested
- if o in ('--pidfile'):
+ if o in ('--pidfile',):
sickbeard.PIDFILE = str(a)
# if the pidfile already exists, sickbeard may still be running, so exit
@@ -176,96 +183,120 @@ def main():
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
-
- # load the config and publish it to the sickbeard package
- if not os.path.isfile(sickbeard.CONFIG_FILE):
- logger.log(u"Unable to find config.ini, all settings will be default", logger.ERROR)
-
- sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
-
- # initialize the config and our threads
- sickbeard.initialize(consoleLogging=consoleLogging)
-
- sickbeard.showList = []
-
- if sickbeard.DAEMON:
- daemonize()
- # use this pid for everything
- sickbeard.PID = os.getpid()
-
- if forcedPort:
- logger.log(u"Forcing web server to port "+str(forcedPort))
- startPort = forcedPort
- else:
- startPort = sickbeard.WEB_PORT
-
- logger.log(u"Starting Sick Beard on http://localhost:"+str(startPort))
-
- if sickbeard.WEB_LOG:
- log_dir = sickbeard.LOG_DIR
- else:
- log_dir = None
-
- # sickbeard.WEB_HOST is available as a configuration value in various
- # places but is not configurable. It is supported here for historic
- # reasons.
- if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
- webhost = sickbeard.WEB_HOST
- else:
- if sickbeard.WEB_IPV6:
- webhost = '::'
- else:
- webhost = '0.0.0.0'
-
- try:
- initWebServer({
- 'port': startPort,
- 'host': webhost,
- 'data_root': os.path.join(sickbeard.PROG_DIR, 'data'),
- 'web_root': sickbeard.WEB_ROOT,
- 'log_dir': log_dir,
- 'username': sickbeard.WEB_USERNAME,
- 'password': sickbeard.WEB_PASSWORD,
- })
- except IOError:
- logger.log(u"Unable to start web server, is something else running on port %d?" % startPort, logger.ERROR)
- if sickbeard.LAUNCH_BROWSER:
- logger.log(u"Launching browser and exiting", logger.ERROR)
- sickbeard.launchBrowser(startPort)
- sys.exit()
-
- # build from the DB to start with
- logger.log(u"Loading initial show list")
- loadShowsFromDB()
-
- # fire up all our threads
- sickbeard.start()
-
- # launch browser if we're supposed to
- if sickbeard.LAUNCH_BROWSER:
- sickbeard.launchBrowser(startPort)
-
- # start an update if we're supposed to
- if forceUpdate:
- sickbeard.showUpdateScheduler.action.run(force=True)
-
- # stay alive while my threads do the work
- while (True):
-
- if sickbeard.invoked_command:
- logger.log(u"Executing invoked command: "+repr(sickbeard.invoked_command))
- sickbeard.invoked_command()
- sickbeard.invoked_command = None
-
- time.sleep(1)
-
- return
-
-if __name__ == "__main__":
- if sys.hexversion >= 0x020600F0:
- freeze_support()
- main()
+ # if they don't specify a config file then put it in the data dir
+ if not sickbeard.CONFIG_FILE:
+ sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, "config.ini")
+
+ # make sure that we can create the data dir
+ if not os.access(sickbeard.DATA_DIR, os.F_OK):
+ try:
+ os.makedirs(sickbeard.DATA_DIR, 0744)
+ except os.error, e:
+ raise SystemExit("Unable to create datadir '" + sickbeard.DATA_DIR + "'")
+
+ # make sure we can write to the data dir
+ if not os.access(sickbeard.DATA_DIR, os.W_OK):
+ raise SystemExit("Data dir must be writeable '" + sickbeard.DATA_DIR + "'")
+
+ # make sure we can write to the config file
+ if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
+ if os.path.isfile(sickbeard.CONFIG_FILE):
+ raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable")
+ elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
+ raise SystemExit("Config file root dir '" + os.path.dirname(sickbeard.CONFIG_FILE) + "' must be writeable")
+
+ os.chdir(sickbeard.DATA_DIR)
+
+ if consoleLogging:
+ print "Starting up Sick Beard "+SICKBEARD_VERSION+" from " + sickbeard.CONFIG_FILE
+
+ # load the config and publish it to the sickbeard package
+ if not os.path.isfile(sickbeard.CONFIG_FILE):
+ logger.log(u"Unable to find " + sickbeard.CONFIG_FILE + " , all settings will be default", logger.WARNING)
+
+ sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
+
+ # initialize the config and our threads
+ sickbeard.initialize(consoleLogging=consoleLogging)
+
+ sickbeard.showList = []
+
+ if sickbeard.DAEMON:
+ daemonize()
+
+ # use this pid for everything
+ sickbeard.PID = os.getpid()
+
+ if forcedPort:
+ logger.log(u"Forcing web server to port "+str(forcedPort))
+ startPort = forcedPort
+ else:
+ startPort = sickbeard.WEB_PORT
+
+ logger.log(u"Starting Sick Beard on http://localhost:"+str(startPort))
+
+ if sickbeard.WEB_LOG:
+ log_dir = sickbeard.LOG_DIR
+ else:
+ log_dir = None
+
+ # sickbeard.WEB_HOST is available as a configuration value in various
+ # places but is not configurable. It is supported here for historic
+ # reasons.
+ if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
+ webhost = sickbeard.WEB_HOST
+ else:
+ if sickbeard.WEB_IPV6:
+ webhost = '::'
+ else:
+ webhost = '0.0.0.0'
+
+ try:
+ initWebServer({
+ 'port': startPort,
+ 'host': webhost,
+ 'data_root': os.path.join(sickbeard.PROG_DIR, 'data'),
+ 'web_root': sickbeard.WEB_ROOT,
+ 'log_dir': log_dir,
+ 'username': sickbeard.WEB_USERNAME,
+ 'password': sickbeard.WEB_PASSWORD,
+ })
+ except IOError:
+ logger.log(u"Unable to start web server, is something else running on port %d?" % startPort, logger.ERROR)
+ if sickbeard.LAUNCH_BROWSER:
+ logger.log(u"Launching browser and exiting", logger.ERROR)
+ sickbeard.launchBrowser(startPort)
+ sys.exit()
+
+ # build from the DB to start with
+ logger.log(u"Loading initial show list")
+ loadShowsFromDB()
+
+ # fire up all our threads
+ sickbeard.start()
+
+ # launch browser if we're supposed to
+ if sickbeard.LAUNCH_BROWSER and not noLaunch:
+ sickbeard.launchBrowser(startPort)
+
+ # start an update if we're supposed to
+ if forceUpdate:
+ sickbeard.showUpdateScheduler.action.run(force=True) #@UndefinedVariable
+
+ # stay alive while my threads do the work
+ while (True):
+
+ if sickbeard.invoked_command:
+ logger.log(u"Executing invoked command: "+repr(sickbeard.invoked_command))
+ sickbeard.invoked_command()
+ sickbeard.invoked_command = None
+
+ time.sleep(1)
+
+ return
+
+if __name__ == "__main__":
+ if sys.hexversion >= 0x020600F0:
+ freeze_support()
+ main()
View
10 data/css/config.css
@@ -12,6 +12,7 @@
#config-content{display:block;width:835px;text-align:left;clear:both;background:#fff;margin:0 auto;padding:0 0 40px;}
#config-components{float:left;width:auto;}
#config-components-border{float:left;width:auto;border-top:1px solid #999;padding:5px 0;}
+#config .title-group{border-bottom:1px dotted #666;position:relative;padding:25px 15px 25px;}
#config .component-group{border-bottom:1px dotted #666;position:relative;padding:15px 15px 25px;}
#config .component-group-desc{float:left;width:235px;}
#config .component-group-desc h3{font-size:1.5em;}
@@ -57,4 +58,11 @@
.infoTableHeader, .infoTableCell {padding: 5px;}
.infoTableHeader{font-weight:700;}
-#config div.testNotification {border: 1px dotted #CCCCCC; padding: 5px; margin-bottom: 10px; line-height:20px;}
+#config div.testNotification {border: 1px dotted #CCCCCC; padding: 5px; margin-bottom: 10px; line-height:20px;}
+
+.config_message {
+ width: 100%;
+ text-align: center;
+ font-size: 1.3em;
+ background: #ffecac;
+}
View
16 data/css/default.css
@@ -2,7 +2,7 @@
img { border: 0; vertical-align: middle;}
body {
-background-color:#fff;
+background-color:#F5F1E4;
color:#000;
font-family:'Verdana', 'Helvetica', 'Sans-serif', 'sans';
font-size:12px;
@@ -36,6 +36,7 @@ display:inline;
/* these are for incl_top.tmpl */
#header {
+background-color:#fff;
padding: 5px 0;
z-index:2;
}
@@ -130,6 +131,7 @@ background-color:#F5F1E4;
border-top:1px solid #b3b3b3;
color:#4e4e4e;
line-height: 1.4em;
+font-size: 1em;
}
.sickbeardTable {
@@ -349,4 +351,14 @@ div#addShowPortal button .buttontext { position: relative; display: block; padd
.optionWrapper div.selectChoices { float: left; width: 175px; margin-left: 25px; }
.optionWrapper br { clear: both; }
-a.whitelink { color: white; }
+a.whitelink { color: white; }
+
+/* for displayShow notice */
+#show_message {
+ padding: 5px 8px;
+ background: #ffd575;
+ color: #333333;
+ text-shadow: 1px 1px 0 rgba(255,255,255,0.3);
+ font-size: 1em;
+}
+div.ui-pnotify { min-width: 340px; max-width: 550px; width: auto !important;}
View
BIN  data/images/loading16_dddddd.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  data/images/loading_posters.gif
Deleted file not rendered
View
BIN  data/images/providers/sick_beard_index.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
2  data/interfaces/default/config.tmpl
@@ -1,4 +1,5 @@
#import sickbeard
+#from sickbeard import db
#import os.path
#set global $title="Configuration"
@@ -11,6 +12,7 @@
<table class="infoTable" cellspacing="1" border="0" cellpadding="0">
<tr><td class="infoTableHeader">SB Version: </td><td class="infoTableCell">alpha ($sickbeard.version.SICKBEARD_VERSION) <!-- &ndash; build.date //--></td></tr>
<tr><td class="infoTableHeader">SB Config file: </td><td class="infoTableCell">$sickbeard.CONFIG_FILE</td></tr>
+ <tr><td class="infoTableHeader">SB Database file: </td><td class="infoTableCell">$db.dbFilename()</td></tr>
<tr><td class="infoTableHeader">SB Cache Dir: </td><td class="infoTableCell">$sickbeard.CACHE_DIR</td></tr>
<tr><td class="infoTableHeader">SB Arguments: </td><td class="infoTableCell">$sickbeard.MY_ARGS</td></tr>
<tr><td class="infoTableHeader">SB Web Root: </td><td class="infoTableCell">$sickbeard.WEB_ROOT</td></tr>
View
12 data/interfaces/default/config_general.tmpl
@@ -12,11 +12,13 @@
#set global $topmenu="config"#
#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_top.tmpl")
+<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>
+<h5>All non-absolute folder locations are relative to " <span class="path">$sickbeard.DATA_DIR</span> "</h5>
-<form action="saveGeneral" method="post">
+<form id="configForm" action="saveGeneral" method="post">
<div id="config-components">
@@ -56,7 +58,7 @@
</label>
</div>
- <input type="submit" value="Save Changes" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</fieldset>
</div><!-- /component-group1 //-->
@@ -119,11 +121,11 @@
</label>
</div>
- <input type="submit" value="Save Changes" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</fieldset>
</div><!-- /component-group3 //-->
- <br/><input type="submit" value="Save Changes" /><br/>
+ <br/><input type="submit" class="config_submitter" value="Save Changes" /><br/>
</div><!-- /config-components -->
</form>
View
24 data/interfaces/default/config_notifications.tmpl
@@ -13,7 +13,7 @@
<div id="config">
<div id="config-content">
-<form id="form" action="saveNotifications" method="post">
+<form id="configForm" action="saveNotifications" method="post">
<div id="config-components">
@@ -105,7 +105,7 @@
<div class="testNotification" id="testXBMC-result">Click below to test.</div>
<input type="button" value="Test XBMC" id="testXBMC" />
- <input type="submit" value="Save Changes" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</div><!-- /enabler_xbmc //-->
@@ -199,7 +199,7 @@
<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" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</div><!-- /enabler_plex -->
@@ -265,9 +265,9 @@
</label>
</div>
- <div class="testNotification" id="testGrowl-result">Click below to test.</div>
- <input type="button" value="Test Growl" id="testGrowl" />
- <input type="submit" value="Save Changes" />
+ <div class="testNotification" id="testGrowl-result">Click below to register and test Growl, this is required for Growl notifications to work.</div>
+ <input type="button" value="Register Growl" id="testGrowl" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</div><!-- /content_use_growl //-->
@@ -342,7 +342,7 @@
<div class="testNotification" id="testTwitter-result">Click below to test.</div>
<input type="button" value="Test Twitter" id="testTwitter" />
- <input type="submit" value="Save Changes" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</div><!-- /content_use_twitter //-->
@@ -412,7 +412,7 @@
<div class="testNotification" id="testProwl-result">Click below to test.</div>
<input type="button" value="Test Prowl" id="testProwl" />
- <input type="submit" value="Save Changes" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</div><!-- /content_use_prowl //-->
@@ -476,7 +476,7 @@
<div class="testNotification" id="testNotifo-result">Click below to test.</div>
<input type="button" value="Test Notifo" id="testNotifo" />
- <input type="submit" value="Save Changes" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</div><!-- /content_use_notifo //-->
@@ -519,7 +519,7 @@
<div class="testNotification" id="testLibnotify-result">Click below to test.</div>
<input type="button" value="Test Libnotify" id="testLibnotify" />
- <input type="submit" value="Save Changes" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</div><!-- /content_use_libnotify //-->
@@ -589,12 +589,12 @@
<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" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</div><!-- /content_use_nmj //-->
</fieldset>
</div><!-- /component-group //-->
- <br/><input type="submit" value="Save Changes" /><br/>
+ <br/><input type="submit" class="config_submitter" value="Save Changes" /><br/>
</div><!-- /config-components //-->
View
13 data/interfaces/default/config_postProcessing.tmpl
@@ -13,12 +13,13 @@
#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_top.tmpl")
<script type="text/javascript" src="$sbRoot/js/configPostProcessing.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>
+<h5>All non-absolute folder locations are relative to " <span class="path">$sickbeard.DATA_DIR</span> "</h5>
-<form action="savePostProcessing" method="post">
+<form id="configForm" action="savePostProcessing" method="post">
<div id="config-components">
@@ -91,7 +92,7 @@
</div>
<div class="clearfix"></div>
- <input type="submit" value="Save Changes" /><br/>
+ <input type="submit" class="config_submitter" value="Save Changes" /><br/>
</fieldset>
</div><!-- /component-group3 //-->
@@ -159,7 +160,7 @@
</label>
</div>
- <input type="submit" value="Save Changes" /><br/>
+ <input type="submit" class="config_submitter" value="Save Changes" /><br/>
</fieldset>
</div><!-- /component-group2 //-->
@@ -287,11 +288,11 @@
</label>
</div>
- <input type="submit" value="Save Changes" />
+ <input type="submit" class="config_submitter" value="Save Changes" />
</fieldset>
</div><!-- /component-group4 //-->
- <br/><input type="submit" value="Save Changes" /><br/>
+ <br/><input type="submit" class="config_submitter" value="Save Changes" /><br/>
</div><!-- /config-components -->
</form>
View
48 data/interfaces/default/config_providers.tmpl
@@ -10,6 +10,7 @@
#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_top.tmpl")
<script type="text/javascript" src="$sbRoot/js/configProviders.js"></script>
+<script type="text/javascript" src="$sbRoot/js/config.js"></script>
<script type="text/javascript" charset="utf-8">
<!--
\$(document).ready(function(){
@@ -25,7 +26,7 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
<div id="config">
<div id="config-content">
-<form action="saveProviders" method="post">
+<form id="configForm" action="saveProviders" method="post">
<div id="config-components">
@@ -43,9 +44,6 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
<fieldset class="component-group-list">
<ul id="provider_order_list">
#for $curProvider in $sickbeard.providers.sortedProviderList():
- #if $curProvider.getID() == 'tvbinz' and not $sickbeard.SHOW_TVBINZ
- #continue
- #end if
#if $curProvider.providerType == $GenericProvider.NZB and not $sickbeard.USE_NZBS:
#continue
#elif $curProvider.providerType == $GenericProvider.TORRENT and not $sickbeard.USE_TORRENTS:
@@ -62,14 +60,14 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
#end for
</ul>
<input type="hidden" name="provider_order" id="provider_order" value="<%=" ".join([x.getID()+':'+str(int(x.isEnabled())) for x in sickbeard.providers.sortedProviderList()])%>"/>
- <br/><input type="submit" value="Save Changes" /><br/>
+ <br/><input type="submit" class="config_submitter" value="Save Changes" /><br/>
</fieldset>
</div><!-- /component-group1 //-->
<div id="core-component-group2" class="component-group clearfix">
<div class="component-group-desc">
- <h3>Configure Providers</h3>
+ <h3>Configure Built-In<br />Providers</h3>
<p>Check with provider's website on how to obtain an API key if needed.</p>
</div>
@@ -79,11 +77,8 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
<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"):
+ #for $cur_provider in ("nzbs_org", "nzbs_r_us", "newzbin", "nzbmatrix", "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:
@@ -94,7 +89,7 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
#if $provider_config_list:
<select id="editAProvider">
- #for $cur_provider in $provider_config_list:
+ #for $cur_provider in $provider_config_list + [$curProvider for $curProvider in $sickbeard.newznabProviderList if $curProvider.default and $curProvider.needs_auth]:
<option value="$cur_provider.getID()">$cur_provider.name</option>
#end for
</select>
@@ -107,28 +102,23 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
<!-- start div for editing providers //-->
-#if $sickbeard.SHOW_TVBINZ:
-<div class="providerDiv" id="tvbinzDiv">
- <div class="field-pair">
- <label class="clearfix">
- <span class="component-title">TVBinz Cookie UID</span>
- <input class="component-desc" type="text" name="tvbinz_uid" value="$sickbeard.TVBINZ_UID" size="10" />
- </label>
- </div>
+#for $curNewznabProvider in [$curProvider for $curProvider in $sickbeard.newznabProviderList if $curProvider.default and $curProvider.needs_auth]:
+<div class="providerDiv" id="${curNewznabProvider.getID()}Div">
<div class="field-pair">
<label class="clearfix">
- <span class="component-title">TVBinz Hash</span>
- <input class="component-desc" type="text" name="tvbinz_hash" value="$sickbeard.TVBINZ_HASH" size="40" />
+ <span class="component-title">$curNewznabProvider.name URL</span>
+ <input class="component-desc" type="text" id="${curNewznabProvider.getID()}_url" value="$curNewznabProvider.url" size="40" disabled/>
</label>
</div>
<div class="field-pair">
<label class="clearfix">
- <span class="component-title">TVBinz Auth String</span>
- <input class="component-desc" type="text" name="tvbinz_auth" value="$sickbeard.TVBINZ_AUTH" size="40" />
+ <span class="component-title">$curNewznabProvider.name API Key</span>
+ <input class="component-desc newznab_key" type="text" id="${curNewznabProvider.getID()}_hash" value="$curNewznabProvider.key" size="40" />
</label>
</div>
</div>
-#end if
+#end for
+
<div class="providerDiv" id="nzbs_orgDiv">
<div class="field-pair">
@@ -207,7 +197,7 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
<!-- end div for editing providers -->
- <input type="submit" value="Save Changes" /><br/>
+ <input type="submit" class="config_submitter" value="Save Changes" /><br/>
</fieldset>
</div><!-- /component-group2 //-->
@@ -215,9 +205,9 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
<div id="core-component-group3" class="component-group clearfix">
<div class="component-group-desc">
- <h3>Newznab Providers</h3>
- <p>Add and setup newznab providers.</p>
- <p>NZB.su has already been added as an example and can not be deleted.</p>
+ <h3>Configure Custom<br />Newznab Providers</h3>
+ <p>Add and setup custom Newznab providers.</p>
+ <p>Some built-in Newznab providers are already available above.</p>
</div>
<fieldset class="component-group-list">
@@ -268,7 +258,7 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
</div><!-- /component-group3 //-->
- <br/><input type="submit" value="Save Changes" /><br/>
+ <br/><input type="submit" class="config_submitter" value="Save Changes" /><br/>
</div><!-- /config-components //-->
View
17 data/interfaces/default/config_search.tmpl
@@ -13,8 +13,9 @@
<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="saveSearch" method="post">
+<h5>All non-absolute folder locations are relative to " <span class="path">$sickbeard.DATA_DIR</span> "</h5>
+
+<form id="configForm" action="saveSearch" method="post">
<div id="config-components">
@@ -57,7 +58,7 @@
</div>
<div class="clearfix"></div>
- <input type="submit" value="Save Changes" /><br/>
+ <input type="submit" class="config_submitter" value="Save Changes" /><br/>
</fieldset>
</div><!-- /component-group1 //-->
@@ -213,13 +214,17 @@
</div>
<div class="clearfix"></div>
- <input type="submit" value="Save Changes" /><br/>
+ <input type="submit" class="config_submitter" value="Save Changes" /><br/>
</div><!-- /content_use_nzbs //-->
</fieldset>
</div><!-- /component-group2 //-->
+ <div class="title-group clearfix" id="no-torrents">
+ <div class="ui-corner-all config_message">Note: Sick Beard works better with Usenet than with Torrents, <a href="http://www.sickbeard.com/usenet.html" target="_blank">here's why</a>.</div>
+ </div>
+
<div id="core-component-group3" class="component-group clearfix">
<div class="component-group-desc">
@@ -250,13 +255,13 @@
</div>
<div class="clearfix"></div>
- <input type="submit" value="Save Changes" /><br/>
+ <input type="submit" class="config_submitter" value="Save Changes" /><br/>
</div><!-- /content_use_torrents //-->
</fieldset>
</div><!-- /component-group3 //-->
- <br/><input type="submit" value="Save Changes" /><br/>
+ <br/><input type="submit" class="config_submitter" value="Save Changes" /><br/>
</div><!-- /config-components //-->
View
11 data/interfaces/default/displayShow.tmpl
@@ -11,6 +11,7 @@
<script type="text/javascript" src="$sbRoot/js/jquery.bookmarkscroll.js"></script>
+
<div class="h2footer align-right">
#if (len($seasonResults) > 14):
<select id="seasonJump">
@@ -34,6 +35,10 @@
#end if
</div><br/>
+#if $show_message:
+ <div id="show_message" class="ui-corner-all">$show_message</div><br />
+#end if
+
<input type="hidden" id="sbRoot" value="$sbRoot" />
<script type="text/javascript" src="$sbRoot/js/displayShow.js"></script>
@@ -150,10 +155,10 @@ $epLoc
#end if
</td>
- <td>$statusStrings[int($epResult["status"])]</td>
+ <td class="status_column">$statusStrings[int($epResult["status"])]</td>
<td align="center">
- #if int($epResult["season"]) != 0:
- <a href="searchEpisode?show=$show.tvdbid&amp;season=$epResult["season"]&amp;episode=$epResult["episode"]"><img src="$sbRoot/images/search32.png" height="16" alt="search" title="Manual Search" /></a>
+ #if int($epResult["season"]) != 0:
+ <a class="epSearch" href="searchEpisode?show=$show.tvdbid&amp;season=$epResult["season"]&amp;episode=$epResult["episode"]"><img src="$sbRoot/images/search32.png" height="16" alt="search" title="Manual Search" /></a>
#end if
</td>
</tr>
View
18 data/interfaces/default/inc_top.tmpl
@@ -73,6 +73,7 @@ table.tablesorter thead tr .headerSortDown { background-image: url("$sbRoot/imag
<script type="text/javascript" src="$sbRoot/js/tools.tooltip-1.2.5.min.js"></script>
<script type="text/javascript" src="$sbRoot/js/jquery.pnotify-1.0.1.min.js"></script>
<script type="text/javascript" src="$sbRoot/js/jquery.expand-1.3.8.js"></script>
+ <script type="text/javascript" src="$sbRoot/js/jquery.form-2.69.js"></script>
<script type="text/javascript" charset="utf-8">
<!--
@@ -91,6 +92,7 @@ table.tablesorter thead tr .headerSortDown { background-image: url("$sbRoot/imag
</script>
<script type="text/javascript" src="$sbRoot/js/jquery.scrolltopcontrol-1.1.js"></script>
<script type="text/javascript" src="$sbRoot/js/browser.js"></script>
+ <script type="text/javascript" src="$sbRoot/js/ajaxNotifications.js"></script>
<script type="text/javascript">
<!--
@@ -139,22 +141,6 @@ table.tablesorter thead tr .headerSortDown { background-image: url("$sbRoot/imag
\$.pnotify.defaults.pnotify_history = false;
\$.pnotify.defaults.pnotify_delay = 4000;
- #for $curMessage in $flash.messages():
- \$.pnotify({
- pnotify_title: decodeURIComponent('$urllib.quote($curMessage[0].encode('UTF-8'))'),
- pnotify_text: decodeURIComponent('$urllib.quote($curMessage[1].encode('UTF-8'))')
- });
- #end for
-
- #for $curError in $flash.errors():
- \$.pnotify({
- pnotify_type: 'error',
- pnotify_hide: false,
- pnotify_title: decodeURIComponent('$urllib.quote($curError[0].encode('UTF-8'))'),
- pnotify_text: decodeURIComponent('$urllib.quote($curError[1].encode('UTF-8'))')
- });
- #end for
-
});
//-->
</script>
View
22 data/js/ajaxNotifications.js
@@ -0,0 +1,22 @@
+var message_url = sbRoot + '/ui/get_messages';
+
+function check_notifications() {
+ $.getJSON(message_url, function(data){
+ $.each(data, function(name,data){
+ $.pnotify({
+ pnotify_type: data.type,
+ pnotify_hide: data.type == 'notice',
+ pnotify_title: data.title,
+ pnotify_text: data.message
+ });
+ });
+ });
+
+ setTimeout(check_notifications, 3000)
+}
+
+$(document).ready(function(){
+
+ check_notifications();
+
+});
View
25 data/js/config.js
@@ -10,4 +10,27 @@ $(document).ready(function(){
else
$('#content_'+$(this).attr('id')).hide();
});
-});
+
+ // bind 'myForm' and provide a simple callback function
+ $('#configForm').ajaxForm({
+ beforeSubmit: function(){
+ $('.config_submitter').each(function(){
+ $(this).attr("disabled", "disabled");
+ $(this).after('<span><img src="'+sbRoot+'/images/loading16.gif"> Saving...</span>');
+ $(this).hide();
+ });
+ },
+ success: function(){
+ setTimeout('config_success()', 2000)
+ }
+ });
+
+});
+
+function config_success(){
+ $('.config_submitter').each(function(){
+ $(this).removeAttr("disabled");
+ $(this).next().remove();
+ $(this).show();
+ });
+}
View
21 data/js/configProviders.js
@@ -21,8 +21,11 @@ $(document).ready(function(){
var newData = [isDefault, [name, url, key]];
newznabProviders[id] = newData;
- $('#editANewznabProvider').addOption(id, name);
- $(this).populateNewznabSection();
+ if (!isDefault)
+ {
+ $('#editANewznabProvider').addOption(id, name);
+ $(this).populateNewznabSection();
+ }
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>'
@@ -120,10 +123,22 @@ $(document).ready(function(){
}
var newznabProviders = new Array();
+
+ $('.newznab_key').change(function(){
+
+ var provider_id = $(this).attr('id');
+ provider_id = provider_id.substring(0, provider_id.length-'_hash'.length);
+
+ var url = $('#'+provider_id+'_url').val();
+ var key = $(this).val();
+
+ $(this).updateProvider(provider_id, url, key);
+
+ });
$('#newznab_key').change(function(){
- var selectedProvider = $('#editANewznabProvider :selected').val();
+ var selectedProvider = $('#editANewznabProvider :selected').val();
var url = $('#newznab_url').val();
var key = $('#newznab_key').val();
View
13 data/js/configSearch.js
@@ -1,5 +1,12 @@
$(document).ready(function(){
+ function toggle_torrent_title(){
+ if ($('#use_torrents').attr('checked'))
+ $('#no-torrents').show();
+ else
+ $('#no-torrents').hide();
+ }
+
$.fn.nzb_method_handler = function() {
var selectedProvider = $('#nzb_method :selected').val();
@@ -24,4 +31,10 @@ $(document).ready(function(){
$(this).nzb_method_handler();
+ $('#use_torrents').click(function(){
+ toggle_torrent_title();
+ });
+
+ toggle_torrent_title();
+
});
View
50 data/js/displayShow.js
@@ -1,12 +1,43 @@
$(document).ready(function(){
+ $('.epSearch').click(function(){
+ var parent = $(this).parent();
+
+ // put the ajax spinner (for non white bg) placeholder while we wait
+ parent.empty();
+ parent.append($("<img/>").attr({"src": sbRoot+"/images/loading16_dddddd.gif", "height": "16", "alt": "", "title": "loading"}));
+
+ $.getJSON($(this).attr('href'), function(data){
+ // if they failed then just put the red X
+ if (data.result == 'failure') {
+ img_name = 'no16.png';
+ img_result = 'failed';
+
+ // if the snatch was successful then apply the corresponding class and fill in the row appropriately
+ } else {
+ img_name = 'yes16.png';
+ img_result = 'success';
+ // color the row
+ parent.parent().removeClass('skipped wanted qual good unaired').addClass('good');
+ parent.siblings('.status_column').html(data.result);
+ }
+
+ // put the corresponding image as the result for the the row
+ parent.empty();
+ parent.append($("<img/>").attr({"src": sbRoot+"/images/"+img_name, "height": "16", "alt": img_result, "title": img_result}));
+ });
+
+ // fon't follow the link
+ return false;
+ });
+
$('#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');
+ 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(){
@@ -20,7 +51,6 @@ $(document).ready(function(){
});
$('#changeStatus').click(function(){
-
var sbRoot = $('#sbRoot').val()
var epArr = new Array()
@@ -36,13 +66,11 @@ $(document).ready(function(){
return false
url = sbRoot+'/home/setStatus?show='+$('#showID').attr('value')+'&eps='+epArr.join('|')+'&status='+$('#statusSelect').attr('value')
-
window.location.href = url
});
$('.seasonCheck').click(function(){
-
var seasCheck = this;
var seasNo = $(seasCheck).attr('id');
@@ -50,8 +78,8 @@ $(document).ready(function(){
var epParts = $(this).attr('id').split('x')
if (epParts[0] == seasNo) {
- this.checked = seasCheck.checked
- }
+ this.checked = seasCheck.checked
+ }
});
});
View
815 data/js/jquery.form-2.69.js
@@ -0,0 +1,815 @@
+/*!
+ * jQuery Form Plugin
+ * version: 2.69 (06-APR-2011)
+ * @requires jQuery v1.3.2 or later
+ *
+ * Examples and documentation at: http://malsup.com/jquery/form/
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+;(function($) {
+
+/*
+ Usage Note:
+ -----------
+ Do not use both ajaxSubmit and ajaxForm on the same form. These
+ functions are intended to be exclusive. Use ajaxSubmit if you want
+ to bind your own submit handler to the form. For example,
+
+ $(document).ready(function() {
+ $('#myForm').bind('submit', function(e) {
+ e.preventDefault(); // <-- important
+ $(this).ajaxSubmit({
+ target: '#output'
+ });
+ });
+ });
+
+ Use ajaxForm when you want the plugin to manage all the event binding
+ for you. For example,
+
+ $(document).ready(function() {
+ $('#myForm').ajaxForm({
+ target: '#output'
+ });
+ });
+
+ When using ajaxForm, the ajaxSubmit function will be invoked for you
+ at the appropriate time.
+*/
+
+/**
+ * ajaxSubmit() provides a mechanism for immediately submitting
+ * an HTML form using AJAX.
+ */
+$.fn.ajaxSubmit = function(options) {
+ // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
+ if (!this.length) {
+ log('ajaxSubmit: skipping submit process - no element selected');
+ return this;
+ }
+
+ if (typeof options == 'function') {
+ options = { success: options };
+ }
+
+ var action = this.attr('action');
+ var url = (typeof action === 'string') ? $.trim(action) : '';
+ if (url) {
+ // clean url (don't include hash vaue)
+ url = (url.match(/^([^#]+)/)||[])[1];
+ }
+ url = url || window.location.href || '';
+
+ options = $.extend(true, {
+ url: url,
+ success: $.ajaxSettings.success,
+ type: this[0].getAttribute('method') || 'GET', // IE7 massage (see issue 57)
+ iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
+ }, options);
+
+ // hook for manipulating the form data before it is extracted;
+ // convenient for use with rich editors like tinyMCE or FCKEditor
+ var veto = {};
+ this.trigger('form-pre-serialize', [this, options, veto]);
+ if (veto.veto) {
+ log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
+ return this;
+ }
+
+ // provide opportunity to alter form data before it is serialized
+ if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
+ log('ajaxSubmit: submit aborted via beforeSerialize callback');
+ return this;
+ }
+
+ var n,v,a = this.formToArray(options.semantic);
+ if (options.data) {
+ options.extraData = options.data;
+ for (n in options.data) {
+ if(options.data[n] instanceof Array) {
+ for (var k in options.data[n]) {
+ a.push( { name: n, value: options.data[n][k] } );
+ }
+ }
+ else {
+ v = options.data[n];
+ v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
+ a.push( { name: n, value: v } );
+ }
+ }
+ }
+
+ // give pre-submit callback an opportunity to abort the submit
+ if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
+ log('ajaxSubmit: submit aborted via beforeSubmit callback');
+ return this;
+ }
+
+ // fire vetoable 'validate' event
+ this.trigger('form-submit-validate', [a, this, options, veto]);
+ if (veto.veto) {
+ log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
+ return this;
+ }
+
+ var q = $.param(a);
+
+ if (options.type.toUpperCase() == 'GET') {
+ options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
+ options.data = null; // data is null for 'get'
+ }
+ else {
+ options.data = q; // data is the query string for 'post'
+ }
+
+ var $form = this, callbacks = [];
+ if (options.resetForm) {
+ callbacks.push(function() { $form.resetForm(); });
+ }
+ if (options.clearForm) {
+ callbacks.push(function() { $form.clearForm(); });
+ }
+
+ // perform a load on the target only if dataType is not provided
+ if (!options.dataType && options.target) {
+ var oldSuccess = options.success || function(){};
+ callbacks.push(function(data) {
+ var fn = options.replaceTarget ? 'replaceWith' : 'html';
+ $(options.target)[fn](data).each(oldSuccess, arguments);
+ });
+ }
+ else if (options.success) {
+ callbacks.push(options.success);
+ }
+
+ options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
+ var context = options.context || options; // jQuery 1.4+ supports scope context
+ for (var i=0, max=callbacks.length; i < max; i++) {
+ callbacks[i].apply(context, [data, status, xhr || $form, $form]);
+ }
+ };
+
+ // are there files to upload?
+ var fileInputs = $('input:file', this).length > 0;
+ var mp = 'multipart/form-data';
+ var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
+
+ // options.iframe allows user to force iframe mode
+ // 06-NOV-09: now defaulting to iframe mode if file input is detected
+ if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
+ // hack to fix Safari hang (thanks to Tim Molendijk for this)
+ // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
+ if (options.closeKeepAlive) {
+ $.get(options.closeKeepAlive, fileUpload);
+ }
+ else {
+ fileUpload();
+ }
+ }
+ else {
+ $.ajax(options);
+ }
+
+ // fire 'notify' event
+ this.trigger('form-submit-notify', [this, options]);
+ return this;
+
+
+ // private function for handling file uploads (hat tip to YAHOO!)
+ function fileUpload() {
+ var form = $form[0];
+
+ if ($(':input[name=submit],:input[id=submit]', form).length) {
+ // if there is an input with a name or id of 'submit' then we won't be
+ // able to invoke the submit fn on the form (at least not x-browser)
+ alert('Error: Form elements must not have name or id of "submit".');
+ return;
+ }
+
+ var s = $.extend(true, {}, $.ajaxSettings, options);
+ s.context = s.context || s;
+ var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
+ var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" />');
+ var io = $io[0];
+
+ $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
+
+ var xhr = { // mock object
+ aborted: 0,
+ responseText: null,
+ responseXML: null,
+ status: 0,
+ statusText: 'n/a',
+ getAllResponseHeaders: function() {},
+ getResponseHeader: function() {},
+ setRequestHeader: function() {},
+ abort: function() {
+ log('aborting upload...');
+ var e = 'aborted';
+ this.aborted = 1;
+ $io.attr('src', s.iframeSrc); // abort op in progress
+ xhr.error = e;
+ s.error && s.error.call(s.context, xhr, 'error', e);
+ g && $.event.trigger("ajaxError", [xhr, s, e]);
+ s.complete && s.complete.call(s.context, xhr, 'error');
+ }
+ };
+
+ var g = s.global;
+ // trigger ajax global events so that activity/block indicators work like normal
+ if (g && ! $.active++) {
+ $.event.trigger("ajaxStart");
+ }
+ if (g) {
+ $.event.trigger("ajaxSend", [xhr, s]);
+ }
+
+ if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
+ if (s.global) {
+ $.active--;
+ }
+ return;
+ }
+ if (xhr.aborted) {
+ return;
+ }
+
+ var timedOut = 0;
+
+ // add submitting element to data if we know it
+ var sub = form.clk;
+ if (sub) {
+ var n = sub.name;
+ if (n && !sub.disabled) {
+ s.extraData = s.extraData || {};
+ s.extraData[n] = sub.value;
+ if (sub.type == "image") {
+ s.extraData[n+'.x'] = form.clk_x;
+ s.extraData[n+'.y'] = form.clk_y;
+ }
+ }
+ }
+
+ // take a breath so that pending repaints get some cpu time before the upload starts
+ function doSubmit() {
+ // make sure form attrs are set
+ var t = $form.attr('target'), a = $form.attr('action');
+
+ // update form attrs in IE friendly way
+ form.setAttribute('target',id);
+ if (form.getAttribute('method') != 'POST') {
+ form.setAttribute('method', 'POST');
+ }
+ if (form.getAttribute('action') != s.url) {
+ form.setAttribute('action', s.url);
+ }
+
+ // ie borks in some cases when setting encoding
+ if (! s.skipEncodingOverride) {
+ $form.attr({
+ encoding: 'multipart/form-data',
+ enctype: 'multipart/form-data'
+ });
+ }
+
+ // support timout
+ if (s.timeout) {
+ setTimeout(function() { timedOut = true; cb(); }, s.timeout);
+ }
+
+ // add "extra" data to form if provided in options
+ var extraInputs = [];
+ try {
+ if (s.extraData) {
+ for (var n in s.extraData) {
+ extraInputs.push(
+ $('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />')
+ .appendTo(form)[0]);
+ }
+ }
+
+ // add iframe to doc and submit the form
+ $io.appendTo('body');
+ io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
+ form.submit();
+ }
+ finally {
+ // reset attrs and remove "extra" input elements
+ form.setAttribute('action',a);
+ if(t) {
+ form.setAttribute('target', t);
+ } else {
+ $form.removeAttr('target');
+ }
+ $(extraInputs).remove();
+ }
+ }
+
+ if (s.forceSync) {
+ doSubmit();
+ }
+ else {
+ setTimeout(doSubmit, 10); // this lets dom updates render
+ }
+
+ var data, doc, domCheckCount = 50;
+
+ function cb() {
+ if (xhr.aborted) {
+ return;
+ }
+
+ var doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
+ if (!doc || doc.location.href == s.iframeSrc) {
+ // response not received yet
+ if (!timedOut)
+ return;
+ }
+ io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
+
+ var ok = true;
+ try {
+ if (timedOut) {
+ throw 'timeout';
+ }
+
+ var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
+ log('isXml='+isXml);
+ if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
+ if (--domCheckCount) {
+ // in some browsers (Opera) the iframe DOM is not always traversable when
+ // the onload callback fires, so we loop a bit to accommodate
+ log('requeing onLoad callback, DOM not available');
+ setTimeout(cb, 250);
+ return;
+ }
+ // let this fall through because server response could be an empty document
+ //log('Could not access iframe DOM after mutiple tries.');
+ //throw 'DOMException: not available';
+ }
+
+ //log('response detected');
+ xhr.responseText = doc.body ? doc.body.innerHTML : doc.documentElement ? doc.documentElement.innerHTML : null;
+ xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
+ xhr.getResponseHeader = function(header){
+ var headers = {'content-type': s.dataType};
+ return headers[header];
+ };
+
+ var scr = /(json|script)/.test(s.dataType);
+ if (scr || s.textarea) {
+ // see if user embedded response in textarea
+ var ta = doc.getElementsByTagName('textarea')[0];
+ if (ta) {
+ xhr.responseText = ta.value;
+ }
+ else if (scr) {
+ // account for browsers injecting pre around json response
+ var pre = doc.getElementsByTagName('pre')[0];
+ var b = doc.getElementsByTagName('body')[0];
+ if (pre) {
+ xhr.responseText = pre.textContent;
+ }
+ else if (b) {
+ xhr.responseText = b.innerHTML;
+ }
+ }
+ }
+ else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
+ xhr.responseXML = toXml(xhr.responseText);
+ }
+
+ data = httpData(xhr, s.dataType, s);
+ }
+ catch(e){
+ log('error caught:',e);
+ ok = false;
+ xhr.error = e;
+ s.error && s.error.call(s.context, xhr, 'error', e);
+ g && $.event.trigger("ajaxError", [xhr, s, e]);
+ }
+
+ if (xhr.aborted) {
+ log('upload aborted');
+ ok = false;
+ }
+
+ // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
+ if (ok) {
+ s.success && s.success.call(s.context, data, 'success', xhr);
+ g && $.event.trigger("ajaxSuccess", [xhr, s]);
+ }
+
+ g && $.event.trigger("ajaxComplete", [xhr, s]);
+
+ if (g && ! --$.active) {
+ $.event.trigger("ajaxStop");
+ }
+
+ s.complete && s.complete.call(s.context, xhr, ok ? 'success' : 'error');
+
+ // clean up
+ setTimeout(function() {
+ $io.removeData('form-plugin-onload');
+ $io.remove();
+ xhr.responseXML = null;
+ }, 100);
+ }
+
+ var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
+ if (window.ActiveXObject) {
+ doc = new ActiveXObject('Microsoft.XMLDOM');
+ doc.async = 'false';
+ doc.loadXML(s);
+ }
+ else {
+ doc = (new DOMParser()).parseFromString(s, 'text/xml');
+ }
+ return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
+ };
+ var parseJSON = $.parseJSON || function(s) {
+ return window['eval']('(' + s + ')');
+ };
+
+ var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
+ var ct = xhr.getResponseHeader('content-type') || '',
+ xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if (xml && data.documentElement.nodeName === 'parsererror') {
+ $.error && $.error('parsererror');
+ }
+ if (s && s.dataFilter) {
+ data = s.dataFilter(data, type);
+ }
+ if (typeof data === 'string') {
+ if (type === 'json' || !type && ct.indexOf('json') >= 0) {
+ data = parseJSON(data);
+ } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
+ $.globalEval(data);
+ }
+ }
+ return data;
+ };
+ }
+};
+
+/**
+ * ajaxForm() provides a mechanism for fully automating form submission.
+ *
+ * The advantages of using this method instead of ajaxSubmit() are:
+ *
+ * 1: This method will include coordinates for <input type="image" /> elements (if the element
+ * is used to submit the form).
+ * 2. This method will include the submit element's name/value data (for the element that was
+ * used to submit the form).
+ * 3. This method binds the submit() method to the form for you.
+ *
+ * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
+ * passes the options argument along after properly binding events for submit elements and
+ * the form itself.
+ */
+$.fn.ajaxForm = function(options) {
+ // in jQuery 1.3+ we can fix mistakes with the ready state
+ if (this.length === 0) {
+ var o = { s: this.selector, c: this.context };
+ if (!$.isReady && o.s) {
+ log('DOM not ready, queuing ajaxForm');
+ $(function() {
+ $(o.s,o.c).ajaxForm(options);
+ });
+ return this;
+ }
+ // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
+ log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
+ return this;
+ }
+
+ return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
+ if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
+ e.preventDefault();
+ $(this).ajaxSubmit(options);
+ }
+ }).bind('click.form-plugin', function(e) {
+ var target = e.target;
+ var $el = $(target);
+ if (!($el.is(":submit,input:image"))) {
+ // is this a child element of the submit el? (ex: a span within a button)
+ var t = $el.closest(':submit');
+ if (t.length == 0) {
+ return;
+ }
+ target = t[0];
+ }
+ var form = this;
+ form.clk = target;
+ if (target.type == 'image') {
+ if (e.offsetX != undefined) {
+ form.clk_x = e.offsetX;
+ form.clk_y = e.offsetY;
+ } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
+ var offset = $el.offset();
+ form.clk_x = e.pageX - offset.left;
+ form.clk_y = e.pageY - offset.top;
+ } else {
+ form.clk_x = e.pageX - target.offsetLeft;
+ form.clk_y = e.pageY - target.offsetTop;
+ }
+ }
+ // clear form vars
+ setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
+ });
+};
+
+// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
+$.fn.ajaxFormUnbind = function() {
+ return this.unbind('submit.form-plugin click.form-plugin');
+};
+
+/**
+ * formToArray() gathers form element data into an array of objects that can
+ * be passed to any of the following ajax functions: $.get, $.post, or load.
+ * Each object in the array has both a 'name' and 'value' property. An example of
+ * an array for a simple login form might be:
+ *
+ * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
+ *
+ * It is this array that is passed to pre-submit callback functions provided to the
+ * ajaxSubmit() and ajaxForm() methods.
+ */
+$.fn.formToArray = function(semantic) {
+ var a = [];
+ if (this.length === 0) {
+ return a;
+ }
+
+ var form = this[0];
+ var els = semantic ? form.getElementsByTagName('*') : form.elements;
+ if (!els) {
+ return a;
+ }
+
+ var i,j,n,v,el,max,jmax;
+ for(i=0, max=els.length; i < max; i++) {
+ el = els[i];
+ n = el.name;
+ if (!n) {
+ continue;
+ }
+
+ if (semantic && form.clk && el.type == "image") {
+ // handle image inputs on the fly when semantic == true
+ if(!el.disabled && form.clk == el) {
+ a.push({name: n, value: $(el).val()});
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
+ }
+ continue;
+ }
+
+ v = $.fieldValue(el, true);
+ if (v && v.constructor == Array) {
+ for(j=0, jmax=v.length; j < jmax; j++) {
+ a.push({name: n, value: v[j]});
+ }
+ }
+ else if (v !== null && typeof v != 'undefined') {
+ a.push({name: n, value: v});
+ }
+ }
+
+ if (!semantic && form.clk) {
+ // input type=='image' are not found in elements array! handle it here
+ var $input = $(form.clk), input = $input[0];
+ n = input.name;
+ if (n && !input.disabled && input.type == 'image') {
+ a.push({name: n, value: $input.val()});
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
+ }
+ }
+ return a;
+};
+
+/**
+ * Serializes form data into a 'submittable' string. This method will return a string
+ * in the format: name1=value1&amp;name2=value2
+ */
+$.fn.formSerialize = function(semantic) {
+ //hand off to jQuery.param for proper encoding
+ return $.param(this.formToArray(semantic));
+};
+
+/**
+ * Serializes all field elements in the jQuery object into a query string.
+ * This method will return a string in the format: name1=value1&amp;name2=value2
+ */
+$.fn.fieldSerialize = function(successful) {
+ var a = [];
+ this.each(function() {
+ var n = this.name;
+ if (!n) {
+ return;
+ }
+ var v = $.fieldValue(this, successful);
+ if (v && v.constructor == Array) {
+ for (var i=0,max=v.length; i < max; i++) {
+ a.push({name: n, value: v[i]});
+ }
+ }
+ else if (v !== null && typeof v != 'undefined') {
+ a.push({name: this.name, value: v});
+ }
+ });
+ //hand off to jQuery.param for proper encoding
+ return $.param(a);
+};
+
+/**
+ * Returns the value(s) of the element in the matched set. For example, consider the following form:
+ *
+ * <form><fieldset>
+ * <input name="A" type="text" />
+ * <input name="A" type="text" />
+ * <input name="B" type="checkbox" value="B1" />
+ * <input name="B" type="checkbox" value="B2"/>
+ * <input name="C" type="radio" value="C1" />
+ * <input name="C" type="radio" value="C2" />
+ * </fieldset></form>
+ *
+ * var v = $(':text').fieldValue();
+ * // if no values are entered into the text inputs
+ * v == ['','']
+ * // if values entered into the text inputs are 'foo' and 'bar'
+ * v == ['foo','bar']
+ *
+ * var v = $(':checkbox').fieldValue();
+ * // if neither checkbox is checked
+ * v === undefined
+ * // if both checkboxes are checked
+ * v == ['B1', 'B2']
+ *
+ * var v = $(':radio').fieldValue();
+ * // if neither radio is checked
+ * v === undefined
+ * // if first radio is checked
+ * v == ['C1']
+ *
+ * The successful argument controls whether or not the field element must be 'successful'
+ * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
+ * The default value of the successful argument is true. If this value is false the value(s)
+ * for each element is returned.
+ *
+ * Note: This method *always* returns an array. If no valid value can be determined the
+ * array will be empty, otherwise it will contain one or more values.
+ */
+$.fn.fieldValue = function(successful) {
+ for (var val=[], i=0, max=this.length; i < max; i++) {
+ var el = this[i];
+ var v = $.fieldValue(el, successful);
+ if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
+ continue;
+ }
+ v.constructor == Array ? $.merge(val, v) : val.push(v);
+ }
+ return val;
+};
+
+/**
+ * Returns the value of the field element.
+ */
+$.fieldValue = function(el, successful) {
+ var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
+ if (successful === undefined) {
+ successful = true;
+ }
+
+ if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
+ (t == 'checkbox' || t == 'radio') && !el.checked ||
+ (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
+ tag == 'select' && el.selectedIndex == -1)) {
+ return null;
+ }
+
+ if (tag == 'select') {
+ var index = el.selectedIndex;
+ if (index < 0) {
+ return null;
+ }
+ var a = [], ops = el.options;
+ var one = (t == 'select-one');
+ var max = (one ? index+1 : ops.length);
+ for(var i=(one ? index : 0); i < max; i++) {
+ var op = ops[i];
+ if (op.selected) {
+ var v = op.value;
+ if (!v) { // extra pain for IE...
+ v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
+ }
+ if (one) {
+ return v;
+ }
+ a.push(v);
+ }
+ }
+ return a;
+ }
+ return $(el).val();
+};
+
+/**
+ * Clears the form data. Takes the following actions on the form's input fields:
+ * - input text fields will have their 'value' property set to the empty string
+ * - select elements will have their 'selectedIndex' property set to -1
+ * - checkbox and radio inputs will have their 'checked' property set to false
+ * - inputs of type submit, button, reset, and hidden will *not* be effected
+ * - button elements will *not* be effected
+ */
+$.fn.clearForm = function() {
+ return this.each(function() {
+ $('input,select,textarea', this).clearFields();
+ });
+};
+
+/**
+ * Clears the selected form elements.
+ */
+$.fn.clearFields = $.fn.clearInputs = function() {
+ return this.each(function() {
+ var t = this.type, tag = this.tagName.toLowerCase();
+ if (t == 'text' || t == 'password' || tag == 'textarea') {
+ this.value = '';
+ }
+ else if (t == 'checkbox' || t == 'radio') {
+ this.checked = false;
+ }
+ else if (tag == 'select') {
+ this.selectedIndex = -1;
+ }
+ });
+};
+
+/**
+ * Resets the form data. Causes all form elements to be reset to their original value.
+ */
+$.fn.resetForm = function() {
+ return this.each(function() {
+ // guard against an input with the name of 'reset'
+ // note that IE reports the reset function as an 'object'
+ if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
+ this.reset();
+ }
+ });
+};
+
+/**
+ * Enables or disables any matching elements.
+ */
+$.fn.enable = function(b) {
+ if (b === undefined) {
+ b = true;
+ }
+ return this.each(function() {
+ this.disabled = !b;
+ });
+};
+
+/**
+ * Checks/unchecks any matching checkboxes or radio buttons and
+ * selects/deselects and matching option elements.
+ */
+$.fn.selected = function(select) {
+ if (select === undefined) {
+ select = true;
+ }
+ return this.each(function() {
+ var t = this.type;
+ if (t == 'checkbox' || t == 'radio') {
+ this.checked = select;
+ }
+ else if (this.tagName.toLowerCase() == 'option') {
+ var $sel = $(this).parent('select');
+ if (select && $sel[0] && $sel[0].type == 'select-one') {
+ // deselect all other options
+ $sel.find('option').selected(false);
+ }
+ this.selected = select;
+ }
+ });
+};
+
+// helper fn for console logging
+// set $.fn.ajaxSubmit.debug to true to enable debug logging
+function log() {
+ if ($.fn.ajaxSubmit.debug) {
+ var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
+ if (window.console && window.console.log) {
+ window.console.log(msg);
+ }
+ else if (window.opera && window.opera.postError) {
+ window.opera.postError(msg);
+ }