Compare commits

..

181 Commits

Author SHA1 Message Date
Safihre
4605c3fd30 Set version to 2.1.0 and make identical to develop 2017-06-09 11:35:46 +02:00
Safihre
ed7dc3f827 Merge branch 'develop' 2017-06-09 11:30:43 +02:00
Safihre
e69eeebdd8 Merge branch 'develop' - Update to 2.0.1 2017-05-24 12:14:56 +02:00
Safihre
5da5f1adc1 Set version to 2.0.0 2017-04-09 12:25:05 +02:00
Safihre
f47e92dec0 Merge branch 'develop' 2017-04-09 11:59:13 +02:00
Safihre
a894ca5171 Set version to 1.2.3 2017-03-18 13:05:04 +01:00
Safihre
5abe1140ae Merge branch '1.2.x' 2017-03-18 13:03:36 +01:00
Safihre
d34e14370c Chain certificates not loaded in all situations of extra servers 2017-03-18 11:43:46 +01:00
Safihre
c4f4a3131c Update 1.2.3 text files for extra bugfix 2017-03-18 11:40:57 +01:00
Safihre
dcbd9b57f3 CherryPy would fallback to (non-existing) pyopenssl for extra servers
Causing failed starts
2017-03-18 11:39:40 +01:00
Safihre
aad3b54a17 Update Text Files for 1.2.3
These patches were already tested within 2.0.0Alpha/Beta, so considerd safe.
2017-03-18 11:04:57 +01:00
Safihre
cde142a371 Windows paths can't end in a dot 2017-03-18 11:02:14 +01:00
Safihre
8bfc98ffc6 Glitter server unblock button wasn't working
Closes #864
2017-03-18 11:01:51 +01:00
Jonathon Saine
e46f21d566 Fix -w option as it no longer needs arguments. 2017-03-18 11:00:46 +01:00
Safihre
0e45fdcdfd Fix BPS manager breaking the downloader due to Quota 2017-03-18 10:28:31 +01:00
Safihre
eec7af16d7 Improve log obfuscation
It needs to match any charachters not just alpha-numeric
2017-03-18 10:28:03 +01:00
Safihre
6532425902 Kill the UnRar after a Windows failure
On Windows Server it seems unrar otherwise won't stop: https://forums.sabnzbd.org/viewtopic.php?f=3&t=22492
2017-03-18 10:27:57 +01:00
Safihre
44b896522c Unzip needs clipped paths
(7zip doesn't)
2017-03-18 10:26:32 +01:00
Safihre
1b16ee44cb Set version to 1.2.2 2017-02-28 11:17:24 +01:00
Safihre
d5f608c28c Merged branch 1.2.x into master 2017-02-28 11:16:59 +01:00
Safihre
555d8418e7 Update text files for 1.2.2 2017-02-25 22:12:34 +01:00
Safihre
8c22e35da4 Script paths were not clipped correctly 2017-02-25 22:07:25 +01:00
Safihre
95a7924b31 Set version to 1.2.1 and fix small discrepancies 2017-02-24 11:22:55 +01:00
Safihre
5830bebd95 Merge branch '1.2.x' 2017-02-24 11:16:18 +01:00
shypike
d32cf57c75 Update translations 2017-02-24 10:03:07 +01:00
Safihre
6d9242ebc5 Update text files for 1.2.1 Final 2017-02-23 11:53:56 +01:00
Safihre
cbc4f6a964 When retry of unpack due to Windows bug, send the actual password
We converted the password to "-p<password>" and when we had to retry it due to a Windows long-path fail, it would become "-p-p<password" and would incorrectly report that the password was wrong.
2017-02-21 20:09:07 +01:00
Safihre
2a3b2b9556 Forced diskspace check seperate from cached one
Closes #826. Otherwise the complete/incomplete get out of sync.
2017-02-20 15:43:55 +01:00
Safihre
53a219f12b Tooltip for Download/Incomplete in Status Windows
Closes #827
2017-02-20 09:03:45 +01:00
Safihre
48519dcfa0 Correct mistake in Diskspace calculation for Unix 2017-02-19 16:12:15 +01:00
Safihre
92542c58fe Diskspace for Complete folder was not calculated 2017-02-19 16:12:07 +01:00
Safihre
7eafe730f9 Modify RarFile to properly handle testrar on Windows
Because unrar doesn't support \\?\ notation for the path to the rarfile we used to clip the path. However, the Python functions that RarFile uses then fail on unicode or jobs with '?' in the filename. Now this is handled correctly, at the very last moment before testing the RAR.
2017-02-19 01:47:29 +01:00
Safihre
494e72a996 Update to 1.2.0 and set version 2017-01-13 17:00:51 +01:00
Safihre
84cc86f1d3 Update to 1.1.1 and set version (#731) 2016-11-11 13:58:07 +01:00
shypike
64479e2e5d Correct the merge of release 1.1.0 to the "master" branch. 2016-09-14 08:26:02 +02:00
shypike
13b523d9bd Merge pull request #681 from Safihre/master
Master to 1.1.0
2016-09-13 16:25:05 +02:00
Safihre
181881a21b Merge branch 'develop' 2016-09-11 22:16:42 +02:00
shypike
86d11095ac Merge pull request #592 from sabnzbd/1.0.x
Update master to 1.0.3
2016-06-10 19:01:03 +02:00
shypike
927ba3cd9d Update text files for 1.0.3 2016-06-04 13:34:11 +02:00
Safihre
6296fc1762 #568 Add code 482 to check for too-many-connections 2016-06-03 23:24:07 +02:00
shypike
60fbe44724 Support X-DNZB-PASSWORD header. 2016-06-03 22:18:56 +02:00
shypike
29e45da431 Fix NZB association for Windows.
Make sure that the second SABnzbd instance sends an UTF-8 encoded URL to the first instance.
Otherwise CherryPy will reject the API call.
2016-06-03 22:07:44 +02:00
shypike
d82e69eef4 Handle checksum error reports from unrar. 2016-06-02 23:19:57 +02:00
shypike
8c7d557252 Prevent job from hanging when adding back par2 files.
Sometimes already completed par2 files are being re-added to the queue as extra par2 files.
Because these files are already complete, there will be no attempt to download them
and as a result they will never leave the queue.
2016-06-02 22:11:59 +02:00
shypike
a56d6e5517 Merge pull request #577 from sabnzbd/1.0.x
1.0.x to master
2016-05-23 18:11:23 +02:00
shypike
7548d9e975 Correct base version number for 1.0.x releases. 2016-05-10 14:43:14 +02:00
shypike
b7e2bd9684 Update text files for 1.0.2 2016-05-03 19:39:45 +02:00
shypike
f0a243e3d3 Fix API status issues.
Remove the "ToPP" status, it serves no purpose.
Only truly deleted jobs should get the "Deleted" status,
not jobs moving from Download to PP queue.
2016-05-01 14:42:07 +02:00
shypike
6e108c9ef2 Prevent Completed and Failed jobs from getting status Deleted. 2016-04-30 11:47:03 +02:00
shypike
89edcc1924 Log the preferred character encoding 2016-04-30 11:18:32 +02:00
shypike
8a6aca47a1 Prevent stalling at 100% when QuickCheck is Off and "Download-all-pars" is On.
The repair function sent all extra par2 files back to the queue
even though they were already downloaded.
2016-04-28 22:42:51 +02:00
shypike
d03e5780b8 Fix API compatibility of queue statuses.
The new statuses TO_PP and DELETED should not be returned by the API.
Tools may not be able to handle them and they are only useful for internal purposes.
2016-04-27 12:03:37 +02:00
shypike
209d8f9b40 NNTP error 502 should not aways be interpreted as bad login.
It can also mean "too many connections".
2016-04-27 11:52:56 +02:00
shypike
c257b1be3d Update text files for 1.0.1 2016-04-26 19:57:22 +02:00
Safihre
2c48c8de2e Force MIME types for CSS and JS files
Caused problems on Windows if external programs overwriten it in registery.
See: http://forums.sabnzbd.org/viewtopic.php?f=2&t=20490
And: https://www.reddit.com/r/usenet/comments/4fkmcx/my_sab_interface_is_text_only/
2016-04-26 19:48:47 +02:00
shypike
a767ef6aed Fix API compatibility of queue.
The new statuses TO_PP and DELETED should not be returned by the API.
Tools may not be able to handle them and they are only useful for internal purposes.
2016-04-26 19:47:38 +02:00
shypike
ad61d1dd03 The pre-queue script can now return an accept value of 2, meaning immediate failure.
Supports front-ends which need the signal that an NZB has been
rejected by the pre-queue script.
2016-04-22 21:54:45 +02:00
shypike
33c3d187a0 Add start script for portable Windows installations 2016-04-22 16:44:37 +02:00
shypike
4eb486d4e2 Update text files for 1.0.0RC1 2016-04-16 15:31:48 +02:00
shypike
bfb6c167a4 Another attempt to set the default cache to 450M. 2016-04-16 15:27:35 +02:00
shypike
44abf3bdf6 Update text files for 1.0.1RC1 2016-04-15 23:12:22 +02:00
shypike
c950572592 Set default cache to 450M 2016-04-15 23:11:53 +02:00
shypike
3999cb13fd Update text files for 1.0.1RC1 2016-04-15 21:06:55 +02:00
shypike
af65075f0c Update text files for 1.0.0RC1 2016-04-15 21:05:53 +02:00
shypike
de2a2b465b Update text files for 1.0.1RC1 2016-04-13 22:41:13 +02:00
shypike
cd7a77f02d Revert "Set default cache size to 750MB on Windows and OSX."
This reverts commit 9b420e91c9.
2016-04-13 22:30:05 +02:00
shypike
f4a5394b63 Prevent creating orphan items in "incomplete" when deleting downloading jobs.
Due to previous issues where articles could be lost,
the nzf.deleted and no.deleted flags were not obeyed.
This could lead to creation of orphans when lost articles would be flushed.

Better solution: drop articles only when job is in a final state.
Also prevent NZO files from being saved when job is in "deleted" state.
2016-04-13 18:32:23 +02:00
jdfalk
3fb6a8dedb Update sabnzbd@.service
1. Added requirement for network to be up before sab starts.
2. Explicitly set service type to simple.
3. Enabled sabnzbd restart on service failure via systemd.
2016-04-13 18:22:11 +02:00
Safihre
50c8f84eba #448-#126 Forced item with missing articles caused overflow in paused queue
Closes #448
Closes #126
2016-04-13 18:21:41 +02:00
Safihre
2c7ecdee92 #464 Grabbing items don't always have status=grabbing
But now they do!
2016-04-07 21:45:28 +02:00
Safihre
72390a793a Add Optional label to Retry password 2016-04-07 21:45:17 +02:00
Safihre
04ad4e5d3e Update cache text to more 2016 values 2016-04-07 21:45:05 +02:00
Safihre
5ef9c6a433 #530 do not ignore files in QuickCheck
Par2 wouldn't ignore them either
2016-04-07 21:44:51 +02:00
shypike
e6baffc839 Prevent API crashes when 'mode' or 'name' have double entries. 2016-04-07 21:35:07 +02:00
shypike
e361eb25a5 Fix potential race condition in BPSmeter. 2016-04-01 21:18:19 +02:00
shypike
9b420e91c9 Set default cache size to 750MB on Windows and OSX.
To make handling of posts with large files more efficient.
2016-04-01 21:09:15 +02:00
Safihre
3a4bf971b2 #529 Fix unicode strip in OptionStr 2016-04-01 20:47:11 +02:00
Safihre
1128691c5d #527 Fix "Download all par2 files" behavior 2016-03-29 21:03:48 +02:00
Safihre
15043aef3f DB Strings should be encapsulated in ' not " 2016-03-29 21:03:30 +02:00
savef
2a3b4afa03 Treat ambiguous numeric values as number of minutes for custom pause time.
Currently if you just type "100" into the custom pause field it'll think you want 143015 minutes, that's useless. A lot of people are probably used to the old Plush behaviour of entering the number of minutes you want to pause for, it's also a much saner default. So in the case that the user just enters some numbers and nothing else, this assumes they want to pause for that many minutes.
2016-03-24 21:01:04 +01:00
shypike
00a98efa81 Fix race-condition when deleting an actively downloading job.
Closes #237
2016-03-24 20:57:27 +01:00
shypike
f013dd7f0d Accept MIME records that have only LF line endings.
Some tool developers just ignore the rule requiring CRLF.
2016-03-23 23:29:12 +01:00
shypike
7b91b1c769 Fix race condition when API and postprocessor both want to delete a history item. 2016-03-23 23:03:28 +01:00
shypike
5583cce322 When urlgrabber receives a 404, stop trying. 2016-03-23 23:00:38 +01:00
shypike
b995c5f992 Fix PushOver support.
Adjust priority levels to the current PushOver API.
Re-enable device field now that the PushOver API supports readable device labels.
2016-03-22 21:25:05 +01:00
shypike
214ac4a53d Prevent incompatibility due to missing 'script_log' field.
Fixes commit c0f2f59fc1
2016-03-20 15:24:07 +01:00
shypike
fc7e87f0df Merge branch '1.0.x' 2016-03-19 11:49:11 +01:00
Safihre
c0f2f59fc1 Fix breaking Glitter bug with large script_log 2016-03-18 14:11:54 +01:00
shypike
b90a847a6f Fix omission in README.mkd 2016-03-15 21:42:16 +01:00
shypike
a58bb385f5 Update text files for 1.0.0 2016-03-15 20:12:50 +01:00
shypike
9754baeb1c Fix handling of changed "ignore_samples" option.
Closes #510
2016-03-15 20:08:35 +01:00
shypike
ffcd154966 Update text files for 1.0.0 Final. 2016-03-14 19:12:59 +01:00
shypike
97cfe9488c Update text files for 1.0.0RC5 2016-03-11 19:24:27 +01:00
shypike
374b6f616a Fix crash in CherryPy when it reports problems with some IPv6 addresses.
A bug in Python's traceback logging causes a crash when an IPv6 address with an embedded % is reported.
2016-03-11 19:22:49 +01:00
Sander Jonkers
e2f51595b6 Because of dual IPv4/IPv6 clients, finding the public ipv4 needs special attention 2016-03-10 22:52:22 +01:00
shypike
04091a16aa Fix display of SABnzbd's icon by OSX Notification Center.
El Capitan doesn't accept the -sender parameter, so just omit it.
2016-03-08 16:40:44 +01:00
shypike
9d9d2fd9a2 Revert "Fix Plush dashboard"
This reverts commit 42f1a4926c.
2016-03-08 16:00:24 +01:00
shypike
5746115331 Suppress errors/warnings about bad "Rating" files when the feature is disabled. 2016-03-08 15:53:52 +01:00
Safihre
42f1a4926c Fix Plush dashboard 2016-03-05 11:23:19 +01:00
Safihre
7d87fd461b Fix Glitter display in <1MB/s range 2016-03-05 11:22:57 +01:00
shypike
1ba9976979 Update text files for 1.0.0RC4 2016-03-05 08:52:09 +01:00
shypike
659c199043 Fix --ipv6_hosting option.
Repairs commit 1cbff28
2016-03-05 08:50:13 +01:00
shypike
81a3f53226 Update text files for 1.0.0RC3 2016-03-04 20:39:01 +01:00
shypike
1cbff28f67 Disable listening on IPv6 addresses by the internal web server.
Setting Config->Special->ipv6_hosting to 1 will enable IPv6 listening.
Command line option --ipv6_hosting allows forcing the choice, should SABnzbd not start.
Closes #492
2016-03-04 19:26:50 +01:00
shypike
8e15acbf30 Update translations 2016-03-04 18:55:20 +01:00
Safihre
e07be60db6 #489 wrongly encoded & in the preload 2016-03-01 22:20:51 +01:00
Safihre
539c9662ff 'Default' not translated in Server-category 2016-03-01 22:20:35 +01:00
shypike
b396014f8d Fix IP test at startup.
Correction of problem introduced by commit afff88b "Use self-test.sabnzbd.org for connection tests".
2016-02-24 22:30:02 +01:00
shypike
1db32415b6 Update translations 2016-02-24 21:37:22 +01:00
shypike
b24629db6b Update text files for 1.0.0RC2 2016-02-24 21:11:43 +01:00
Safihre
9b5cdcf8fb Add trailing "/" to href's 2016-02-24 21:05:12 +01:00
shypike
4831415d14 Use self-test.sabnzbd.org for connection tests 2016-02-24 20:34:04 +01:00
Chris Thorn
a4c51f0b20 Parse bandwidth limit as a float instead of an integer so that non-integer values can be used as bandwidth limit 2016-02-24 20:09:02 +01:00
shypike
ec3ba1fb93 Changing server priorities during a download could lead to unexpected results and lockups.
Target priority of articles must be kept at the TryList level so that they
are reset when the try_list is reset.
Closes #378
2016-02-24 20:08:51 +01:00
Safihre
61966f7036 Plush dashboard local IPv4 wouldn't show if external failed
Typo!
For 1.0.0
2016-02-24 20:08:38 +01:00
Safihre
4f69e81841 Add link to information about SSL/yEnc 2016-02-24 20:07:48 +01:00
Safihre
d0d90581df Tweak first Config page 2016-02-24 20:07:35 +01:00
Safihre
8ea5c27633 Improve display of message on blocked server 2016-02-24 20:07:16 +01:00
shypike
517500fdf3 Add link to issue list on support site. 2016-02-19 22:42:30 +01:00
shypike
c4c1c9b6ab Prevent UI errors due to history_db handle not being available. 2016-02-19 22:22:42 +01:00
Safihre
2388889ede Filegrabber did not sanatize filename 2016-02-19 22:21:28 +01:00
Safihre
55cfe878d7 Catch failing Windows Notification
I assumed Windows notifications could not fail, but clearly they can:
http://forums.sabnzbd.org/viewtopic.php?f=11&t=20211&p=104438
When no tray-icon is available, it will fail and in the case of
completed NZB message it will restart the whole of SABnzbd.
2016-02-17 21:25:42 +01:00
Safihre
a2daaee468 Add Pystone score to Glitter status 2016-02-17 21:06:43 +01:00
Safihre
2c360e395e Limit to max 250 items per page (realistic limit) 2016-02-17 21:05:55 +01:00
Jonathon Saine
399cfee594 Add pyOpenSSL info to startup/utility/config base. Add OpenSSL & yEnc to config base as well. 2016-02-17 21:04:13 +01:00
shypike
be646ae6ab Set default for https verification to off. 2016-02-17 20:50:24 +01:00
shypike
b470253d9f Disable https verification when uploading NZB to running instance of SABnzbd. 2016-02-13 16:12:04 +01:00
shypike
b83c493492 Update translations 2016-02-11 01:06:52 +01:00
Safihre
991277bb01 Glitter broke on empty preload strings
See
http://forums.sabnzbd.org/viewtopic.php?f=11&t=20192&p=104368#p104368
In case of UnicodeError
2016-02-10 22:59:11 +01:00
Safihre
5626013b81 Don't allow name change or filesview on grabbing 2016-02-10 22:58:56 +01:00
Safihre
2810d37758 Make disabled servers look more disabled 2016-02-10 22:58:40 +01:00
Safihre
c2f08f01e0 #408 Show red when Add-NZB left empty 2016-02-10 22:57:30 +01:00
Safihre
17ff087e06 Do not confirm clearing warnings 2016-02-05 23:36:47 +01:00
Safihre
77de565b7c Link in message about Orphans was broken 2016-02-05 23:36:36 +01:00
Safihre
54d238aa4d Message labels were not translated
INFO and WARNING
2016-02-05 23:36:23 +01:00
Safihre
379d09f8cc Improve message about no localStorage
It happens more than I expected, so better make a proper message.
2016-02-05 23:35:23 +01:00
shypike
00de72b127 Update text files for 1.0.xRC1 2016-02-03 21:18:42 +01:00
shypike
f9c84fa7dd Fix trouble with disk speed meter (especially on Windows) Part 2.
Improve the benchmark by reducing Python overhead by writing larger blocks.
2016-02-03 21:08:47 +01:00
shypike
c8e46691bb Solve file name encoding issues for OSX.
- Names returned by unrar-commandline need to be Unicoded.
- For yEnc embedded names, only test for UTF-8 and CP1252, but not the local codepage.
- Suppress some bogus warnings
- Log the output of the unrar tool
2016-02-03 20:59:12 +01:00
shypike
df1bb636e5 Fix links in text files to pooit to 1.0.0 entries in the Wiki. 2016-02-03 20:51:19 +01:00
shypike
ff886fad0d Fix all links in the templates to point to 1.0.0 (or 1-0) entries in the Wiki. 2016-02-03 20:50:13 +01:00
shypike
6dbee7a413 Fix trouble with disk speed meter (especially on Windows) Part 2.
Move fix outside of the measurement loop.
Don't remove the test-file within the loop, but only after it's finished,
otherwise we'll still get a "Permission denied".
2016-01-30 10:52:28 +01:00
shypike
3f8fcd7172 Fix trouble with disk speed meter (especially on Windows).
Better handling when folder is not available or writable.
Windows requires some access outside of the Python code to avoid "permission denied".
2016-01-29 23:54:32 +01:00
shypike
d94f7388e6 Add a link on the main Config page, to the "issues" page on the Wiki 2016-01-29 19:55:04 +01:00
Safihre
ad8b49fea8 #408 Firefox-fix: word-wrap instead of word-break 2016-01-29 19:21:59 +01:00
Safihre
ce00270c12 #447 only show arguments-field for speedlimit 2016-01-29 19:21:41 +01:00
Safihre
8c501f8f58 #447 Servers in scheduler more clear 2016-01-29 19:21:30 +01:00
Safihre
ce313ebc65 #445 Reduce statusinfo timeout on startup 2016-01-29 19:21:18 +01:00
Safihre
887ad881a2 #444 HTTPS instead of HTTP for RSS favicon 2016-01-29 19:21:07 +01:00
Safihre
ce40827552 Fix breaking RSS page
Fixes http://forums.sabnzbd.org/viewtopic.php?f=11&t=20114
2016-01-29 19:20:52 +01:00
shypike
2777d89482 Fix typos that prevented notifications about disk full being sent. 2016-01-28 23:19:36 +01:00
shypike
727b300a0e Update translations 2016-01-24 16:34:40 +01:00
shypike
652b021a8e Update text files for 0.8.0Beta6 2016-01-24 16:26:26 +01:00
shypike
fdf33acfbb Allow "None" as selection for secondary skin. 2016-01-24 16:15:02 +01:00
shypike
b001bc9b6f Prevent multiple resume notifications.
Only report "Resume" when coming from Paused mode.
2016-01-24 15:18:24 +01:00
shypike
8802cb1d8c Notifications template contained two instances of "ncenter_enable", leading to problems.
The result was a list instead of a single value.
2016-01-24 15:18:07 +01:00
shypike
e19a2fbae7 Improve handling of an old queue when upgrading to 0.8.0+
Warn only for a non-empty queue.
For Windows, when using a relative "download_dir" based on the user profile,
prepend the path with "Documents".
No action when the -f parameter was used.
This way it will be easier to restore existing jobs.
2016-01-24 15:17:47 +01:00
Safihre
53e38f98f9 Firefox needs more time to process the Server AJAX 2016-01-24 15:16:52 +01:00
Safihre
e783e227f6 #438 Try to stop Firefox from checking checkboxes 2016-01-24 15:16:39 +01:00
shypike
f3dfbe4181 Fix problem where a stray RAR file would cause a failed unpack run.
When a job of which the RAR files are renamed by par2,
needs repair of one more more files, the original damaged files will stay behind.
This will cause SABnzbd to try a doomed attempt at unpacking.
SABnzbd should keep track of such files and delete them after repair.
2016-01-24 15:15:57 +01:00
shypike
bcd8ca8bc4 Fix potential crash when reverting par2 renames.
Can happen when files have been modified outside of SABnzbd's control.
2016-01-24 15:12:06 +01:00
shypike
816d6a63cd For SSL protocol choice, default to auto-negotiation.
The "V23" method is interpreted by OpenSSL as "negotiate the highest available protocol"
and should therefor be a safe choice (best chance of working and best security).
If a user wants to, it is possible to fix the protocol, to prevent interference in the negotiation.
We cannot just assume that we can use our highest fixed protocol, because some Usenet servers
are being slow with implementing TLS1.2
2016-01-24 15:11:30 +01:00
shypike
88d3f25700 Perform IPv6 test on port 443 instead of 80.
Works better with some proxies.
Closes issue #274
2016-01-19 18:13:42 +01:00
shypike
80f69b11db When trying to connect to another SABnzbd instance over HTTPS, don't verify certificates.
Very few SABnzbd installations will have valid certificates.
2016-01-19 18:03:33 +01:00
Safihre
81a11f20c8 Re-order Switches page 2016-01-19 17:30:01 +01:00
Safihre
9e2a839953 Config fixes 2016-01-19 17:30:01 +01:00
Safihre
3cefcde270 #408 Refresh on Config Special save to update the * 2016-01-19 17:30:01 +01:00
Safihre
87a1eacfe7 #408 Also close history-details on history-row click
Before it would only open
2016-01-19 17:30:01 +01:00
Safihre
7cbc1a8419 #408 Browser navbar to black on mobile 2016-01-19 17:30:01 +01:00
Safihre
7b5570eb0b #408 Extra space next to Folder icon 2016-01-19 17:30:00 +01:00
Safihre
1a43a4dcf0 #432 Change filename to name in Add NZB 2016-01-19 17:30:00 +01:00
Safihre
2c2a6592c7 End of queue script was forgotten in Glitter 2016-01-19 17:30:00 +01:00
shypike
f31de6ee4e The compiled OSX build wasn't restarted with original command line arguments.
Rare use case where the App was originally started with parameters.
Essential for correct preservation of the -f parameter.
2016-01-19 17:29:11 +01:00
shypike
8fcd1f6b6c Merge branch 'develop' into R0.8.0 2016-01-16 12:25:38 +01:00
shypike
d7f3a473d7 Merge branch 'develop' into R0.8.0 2016-01-13 21:51:51 +01:00
shypike
ab2eb0c94e Update text files for 0.8.0Beta5 2016-01-13 20:17:32 +01:00
shypike
e51f4fc45a Merge pull request #181 from discordianfish/add-dockerfile
Add Dockerfile
2014-10-08 21:38:15 +02:00
Johannes 'fish' Ziemke
65278120e2 Add Dockerfile
Usage:

        docker build -t sabnzbd .
        docker run -p 127.0.0.1:8080:8080 sabnzbd
2014-09-24 13:46:43 +02:00
shypike
2eed355e9c Revert "newsunpack.py: also handle output of unrar5 with password protected files"
This reverts commit 12fd63c1cf.
2013-11-11 20:24:11 +01:00
shypike
018955f4d5 Merge pull request #122 from sanderjo/patch-4
newsunpack.py: also handle output of unrar5 with password protected file...
2013-11-11 11:19:20 -08:00
sanderjo
12fd63c1cf newsunpack.py: also handle output of unrar5 with password protected files
as discussed http://forums.sabnzbd.org/viewtopic.php?f=3&t=16166&p=89054#p89054
2013-11-07 17:02:24 +01:00
215 changed files with 36972 additions and 44923 deletions

7
.gitignore vendored
View File

@@ -19,10 +19,6 @@ SABnzbd*.dmg
# WingIDE project files
*.wp[ru]
# Testing folders
.cache
.xprocess
# General junk
*.keep
*.bak
@@ -31,6 +27,3 @@ SABnzbd*.dmg
# Some people use Emacs as an editor
\#*
.\#*
.DS_Store
/venv

View File

@@ -1,15 +0,0 @@
language: python
python:
- "2.7"
before_install:
- sudo add-apt-repository ppa:jcfp -y
- sudo apt-get update -q
- sudo apt-get install sabnzbdplus -y
install:
- pip install --upgrade -r tests/requirements.txt
script:
- pytest
notifications:
email:
on_success: never
on_failure: always

View File

@@ -1,5 +1,5 @@
*******************************************
*** This is SABnzbd 2.4.0 ***
*** This is SABnzbd 2.1.0 ***
*******************************************
SABnzbd is an open-source cross-platform binary newsreader.
It simplifies the process of downloading from Usenet dramatically,

View File

@@ -1,4 +1,4 @@
SABnzbd 2.3.1
SABnzbd 2.1.0
-------------------------------------------------------------------------------
0) LICENSE
@@ -27,57 +27,58 @@ Just run the downloaded EXE file and the installer will start.
It's just a simple standard installer.
After installation, find the SABnzbd program in the Start menu and start it.
Within a few seconds your web browser will start and show the user interface.
Within 5-10 seconds your web browser will start and show the user interface.
Use the "Help" button in the web-interface to be directed to the Help Wiki.
-------------------------------------------------------------------------------
2) INSTALL pre-built Windows binaries
-------------------------------------------------------------------------------
Unzip pre-built version to any folder of your liking.
Start the SABnzbd.exe program.
Within a few seconds your web browser will start and show the user interface.
Within 5-10 seconds your web browser will start and show the user interface.
Use the "Help" button in the web-interface to be directed to the Help Wiki.
-------------------------------------------------------------------------------
3) INSTALL pre-built macOS binaries
-------------------------------------------------------------------------------
Download the DMG file, mount and drag the SABnzbd icon to Programs.
Just like you do with so many apps.
Make sure you pick the right folder, depending on your macOS version.
-------------------------------------------------------------------------------
4) INSTALL with only sources
-------------------------------------------------------------------------------
Specific guides to install from source are available for Windows and macOS:
https://sabnzbd.org/wiki/installation/install-macos
https://sabnzbd.org/wiki/installation/install-from-source-windows
You need to have Python installed plus some non-standard Python modules
and a few tools.
All platforms
Unix/Linux/macOS
Python-2.7.latest http://www.python.org (2.7.9+ recommended)
Windows
Python-2.7.latest http://www.python.org (2.7.9+ recommended)
PyWin32 use "pip install pypiwin32"
subprocessww use "pip install subprocessww"
Essential modules
cheetah-2.0.1+ use "pip install cheetah"
par2cmdline >= 0.4 https://github.com/Parchive/par2cmdline/releases
See also: https://sabnzbd.org/wiki/installation/multicore-par2
unrar >= 5.00+ http://www.rarlab.com/rar_add.htm
openssl >= 1.0.0 http://www.openssl.org/
Optional modules
unzip >= 6.00 http://www.info-zip.org/
7zip >= 9.20 http://www.7zip.org/
sabyenc == 3.3.1 use "pip install sabyenc"
sabyenc == 3.0.2 use "pip install sabyenc"
More information: https://sabnzbd.org/sabyenc
openssl >= 1.0.0 http://www.openssl.org/
v0.9.8 will work, but limits certificate validation
cryptography >= 1.0 use "pip install cryptography"
Enables certificate generation and detection of encrypted RAR-files
Optional modules Linux
Optional modules Unix/Linux/macOS
pynotify Should be part of GTK for Python support on Debian/Ubuntu
If not, you cannot use the NotifyOSD feature.
python-dbus Enable option to Shutdown/Restart/Standby PC on queue finish.
@@ -87,17 +88,17 @@ Embedded modules (preferably use the included version)
Unpack the ZIP-file containing the SABnzbd sources to any folder of your liking.
If you want multiple languages, you need to compile the translations.
Start this from a shell terminal (or command prompt):
python tools/make_mo.py
Start this from a shell terminal (or command prompt):
python -OO SABnzbd.py
python SABnzbd.py
Within a few seconds your web browser will start and show the user interface.
Within 5-10 seconds your web browser will start and show the user interface.
Use the "Help" button in the web-interface to be directed to the Help Wiki.
-------------------------------------------------------------------------------
5) TROUBLESHOOTING
-------------------------------------------------------------------------------
@@ -112,7 +113,7 @@ or
You may of course try other port numbers too.
For troubleshooting on Windows you can use the program SABnzbd-console.exe.
For troubleshooting you can use the program SABnzbd-console.exe.
This will show a black window where logging information will be shown. This
may help you solve problems easier.
@@ -120,13 +121,14 @@ may help you solve problems easier.
6) MORE INFORMATION
-------------------------------------------------------------------------------
Visit our wiki:
Visit the WIKI site:
https://sabnzbd.org/wiki/
-------------------------------------------------------------------------------
7) CREDITS
-------------------------------------------------------------------------------
Several parts of SABnzbd were built by other people, illustrating the
wonderful world of Free Open Source Software.
See the licenses folder of the main program and of the skin folders.
See the licenses folder of the main program and of the skin folders.

View File

@@ -9,18 +9,28 @@
- When par2 or unrar hang up, never just stop SABnzbd.
Instead use your operating system's task manager to stop the par2 or unrar program.
Forcing SABnzbd to quit may damage your queues.
Windows-only:
If you keep having trouble with par2 multicore you can disable it
in Config->Switches.
This will force the use of the old and tried, but slower par2cmdline program.
- A bug in Windows 7 may cause severe memory leaks when you use SABnzbd in
combination with some virus scanners and firewalls.
Install this hotfix:
Description: http://support.microsoft.com/kb/979223/en-us
Download location: http://support.microsoft.com/hotfix/KBHotfix.aspx?kbnum=979223&kbln=en-us
- Some Usenet servers have intermittent login (or other) problems.
For these the server blocking method is not very favourable.
There is an INI-only option that will limit blocks to 1 minute.
no_penalties = 1
See: https://sabnzbd.org/wiki/configuration/2.3/special
See: https://sabnzbd.org/wiki/configuration/2.1/special
- Some third-party utilties try to probe SABnzbd API in such a way that you will
often see warnings about unauthenticated access.
If you are sure these probes are harmless, you can suppress the warnings by
setting the option "api_warnings" to 0.
See: https://sabnzbd.org/wiki/configuration/2.3/special
See: https://sabnzbd.org/wiki/configuration/2.1/special
- On OSX you may encounter downloaded files with foreign characters.
The par2 repair may fail when the files were created on a Windows system.
@@ -31,7 +41,7 @@
You will see this only when downloaded files contain accented characters.
You need to fix it yourself by running the convmv utility (available for most Linux platforms).
Possible the file system override setting 'fsys_type' might be solve things:
See: https://sabnzbd.org/wiki/configuration/2.3/special
See: https://sabnzbd.org/wiki/configuration/2.1/special
- The "Watched Folder" sometimes fails to delete the NZB files it has
processed. This happens when other software still accesses these files.
@@ -41,10 +51,6 @@
- Memory usage can sometimes have high peaks. This makes using SABnzbd on very low
memory systems (e.g. a NAS device or a router) a challenge.
In particular on Synology (SynoCommunity) the device may report that SABnzbd is using
a lot of memory even when idle. In this case the memory is usually not actually used by
SABnzbd and will be available if required by other apps or the system. More information
can be found in the discussion here: https://github.com/SynoCommunity/spksrc/issues/2856
- SABnzbd is not compatible with some software firewall versions.
The Microsoft Windows Firewall works fine, but remember to tell this
@@ -66,3 +72,13 @@
Config->Special->wait_for_dfolder to 1.
SABnzbd will appear to hang until the drive is mounted.
- On some operating systems it looks like there is a problem with one of the standard Python libraries.
It is possible that you get errors about saving admin files and even unexplained crashes.
If so, you can enable the option for the alternative library.
It has the same functionality, but is slower.
We've had reports about this issue on non-mainstream Linux platforms.
- OpenElec
- Squeeze Linux
There is a "special" option that will allow you to select an alternative library.
use_pickle = 1
See: https://sabnzbd.org/wiki/configuration/2.1/special

View File

@@ -1,8 +1,8 @@
Metadata-Version: 1.0
Name: SABnzbd
Version: 2.3.2RC2
Summary: SABnzbd-2.3.2RC2
Home-page: https://sabnzbd.org
Version: 2.1.0
Summary: SABnzbd-2.1.0
Home-page: http://sabnzbd.org
Author: The SABnzbd Team
Author-email: team@sabnzbd.org
License: GNU General Public License 2 (GPL2 or later)

View File

@@ -5,7 +5,7 @@ SABnzbd is an Open Source Binary Newsreader written in Python.
It's totally free, incredibly easy to use, and works practically everywhere.
SABnzbd makes Usenet as simple and streamlined as possible by automating everything we can. All you have to do is add an `.nzb`. SABnzbd takes over from there, where it will be automatically downloaded, verified, repaired, extracted and filed away with zero human interaction.
If you want to know more you can head over to our website: https://sabnzbd.org.
If you want to know more you can head over to our website: http://sabnzbd.org.
## Resolving Dependencies
@@ -13,6 +13,7 @@ SABnzbd has a good deal of dependencies you'll need before you can get running.
- `python` (only 2.7.x and higher, but not 3.x.x)
- `python-cheetah`
- `python-support`
- `par2` (Multi-threaded par2 installation guide can be found [here](https://sabnzbd.org/wiki/installation/multicore-par2))
- `unrar` (Make sure you get the "official" non-free version of unrar)
- `sabyenc` (installation guide can be found [here](https://sabnzbd.org/sabyenc))

View File

@@ -1,51 +1,38 @@
Release Notes - SABnzbd 2.3.2 RC 2
Release Notes - SABnzbd 2.1.0
=========================================================
## Changes since 2.3.2 RC 1
- Simplify Config pages by hiding Advanced Settings
- Automatically set Article Cache to 25% of system memory (max. 2GB)
- Added Hebrew date-time texts
- Added option '%dn' to Date Sorting to rename files as job name
- Added 'Job Name as Folder Name' as Sorting Preset for de-obfuscation
- Add per-day download-statistics to 'server_stats' API-call
- Having 'Download all par2' enabled would result in disappearing jobs
- Pre-queue scripts would fail to run
## Changes since 2.0.1
- Windows-only: Will now use MultiPar for verification and repair. MultiPar uses
the latest optimizations, multiple cores and can utilize the GPU, resulting in
repairs often being twice as fast. Created by Yutaka Sawada (Windows only)
"Extra Par2 Parameters" are also passed to MultiPar
For example: Set "/lc32" to enable GPU for repair (can also be slower!)
- Minimal macOS version set to 10.9 (Mavericks)
- Human readable history date/time default setting in Glitter (x hours ago, etc)
- Post-processing script execution can be aborted
- Removed Specials settings: never_repair, nr_decoders, prio_sort_list,
create_group_folder, enable_par_cleanup, enable_quickcheck, allow_64bit_tools
## Changes since 2.3.1
- SABYenc updated to 3.3.2 to fix rare crash during downloading
- Minor updates of SABYenc (such as 3.3.2) are no longer mandatory
- Server usage graphs are now linked to make comparing servers easier
- First article of each file is downloaded first to identify filenames
- Jobs finished by Direct Unpack will be post-processed first
- URLs that fail to fetch due to server errors will only be retried 10x
- Delay between URL retries increases when not specified by server
- Job password entered by user is always shown in History
- If available, 7zip will be used instead of unzip
# Bugfixes
- File join would fail incorrectly
- Catch and show 'Disk full' messages from PAR2/MultiPar
- MemoryError occurred when opening the interface after long period of inactivity
## Bugfixes since 2.3.1
- Dropped connections could result in stalled downloads
- Pre-queue script output was not always parsed correctly
- Notifications were always send for 'Default' category
- 'History Retention' also checked on start of program
- macOS: Restore compatibility with macOS 10.11.6 on some systems
- Windows: Unpacking could fail due to paths not being quoted
- Windows: Complete folder in root of drive could crash post-processing
- Windows: Automatically correct 'Extra Par2 Parameters' for MultiPar
- Windows: Prevent potential pause/unpause loop after tray icon click
## Upgrading from 2.2.x and older
## Upgrading from 0.7.x and older
- Finish queue
- Stop SABnzbd
- Install new version
- Start SABnzbd
## Upgrade notices
- When upgrading from 2.2.0 or older the queue will be converted. Job order,
settings and data will be preserved, but all jobs will be unpaused and
URL's that did not finish fetching before the upgrade will be lost.
## Upgrade notices (from pre-2.x.x)
- Windows: When starting the Post-Processing script, the path to the job folder
is no longer in short-path notation but includes the full path. To support
long paths (>255), you might need to alter them to long-path notation (\\?\).
- Schedule items are converted when upgrading to 2.x.x and will break when
reverted back to pre-2.x.x releases.
- The organization of the download queue is different from 0.7.x releases.
This version will not see the 0.7.x queue, but you can restore the jobs
by going to Status page and using Queue Repair.
So 2.x.x will not see the existing queue, but you can go to Status->Queue Repair
and "Repair" the old queue.
## Known problems and solutions
- Read the file "ISSUES.txt"

View File

@@ -32,13 +32,11 @@ except:
import logging
import logging.handlers
import traceback
import os
import getopt
import signal
import socket
import platform
import ssl
import time
import re
@@ -58,6 +56,8 @@ if [int(n) for n in cherrypy.__version__.split('.')] < [8, 1, 2]:
print 'Sorry, requires Python module Cherrypy 8.1.2+ (use the included version)'
sys.exit(1)
from cherrypy import _cpserver
SQLITE_DLL = True
try:
from sqlite3 import version as sqlite3_version
@@ -87,10 +87,10 @@ import sabnzbd.interface
from sabnzbd.constants import *
import sabnzbd.newsunpack
from sabnzbd.misc import real_path, \
check_latest_version, exit_sab, get_from_url, \
check_latest_version, exit_sab, \
split_host, get_ext, create_https_certificates, \
windows_variant, ip_extract, set_serv_parms, get_serv_parms, globber_full
from sabnzbd.panic import panic_tmpl, panic_port, panic_host, \
from sabnzbd.panic import panic_tmpl, panic_port, panic_host, panic_fwall, \
panic_sqlite, panic, launch_a_browser
import sabnzbd.scheduler as scheduler
import sabnzbd.config as config
@@ -99,6 +99,7 @@ import sabnzbd.downloader
from sabnzbd.encoding import unicoder, deunicode
import sabnzbd.notifier as notifier
import sabnzbd.zconfig
import sabnzbd.utils.sslinfo
from threading import Thread
@@ -151,11 +152,7 @@ class guiHandler(logging.Handler):
# Loose the oldest record
self.store.pop(0)
try:
# Append traceback, if available
warning = {'type': record.levelname, 'text': record.msg % record.args, 'time': int(time.time())}
if record.exc_info:
warning['text'] = '%s\n%s' % (warning['text'], traceback.format_exc())
self.store.append(warning)
self.store.append(self.format(record))
except UnicodeDecodeError:
# Catch elusive Unicode conversion problems
pass
@@ -166,6 +163,12 @@ class guiHandler(logging.Handler):
def count(self):
return len(self.store)
def last(self):
if self.store:
return self.store[len(self.store) - 1]
else:
return ""
def content(self):
""" Return an array with last records """
return self.store
@@ -201,11 +204,11 @@ def print_help():
print " --repair-all Try to reconstruct the queue from the incomplete folder"
print " with full data reconstruction"
print " --https <port> Port to use for HTTPS server"
print " --ipv6_hosting <0|1> Listen on IPv6 address [::1] [*]"
print " --no-login Start with username and password reset"
print " --log-all Log all article handling (for developers)"
print " --console Force console logging for OSX app"
print " --new Run a new instance of SABnzbd"
print " --ipv6_hosting <0|1> Listen on IPv6 address [::1]"
def print_version():
@@ -401,7 +404,7 @@ def print_modules():
""" Log all detected optional or external modules """
if sabnzbd.decoder.SABYENC_ENABLED:
# Yes, we have SABYenc, and it's the correct version, so it's enabled
logging.info("SABYenc module (v%s)... found!", sabnzbd.decoder.SABYENC_VERSION)
logging.info("SABYenc module (v%s)... found!", sabnzbd.constants.SABYENC_VERSION_REQUIRED)
else:
# Something wrong with SABYenc, so let's determine and print what:
if sabnzbd.decoder.SABYENC_VERSION:
@@ -425,12 +428,13 @@ def print_modules():
if sabnzbd.newsunpack.PAR2_COMMAND:
logging.info("par2 binary... found (%s)", sabnzbd.newsunpack.PAR2_COMMAND)
else:
logging.error('%s %s' % (T('par2 binary... NOT found!'), T('Verification and repair will not be possible.')))
logging.error(T('par2 binary... NOT found!'))
if sabnzbd.newsunpack.PAR2C_COMMAND:
logging.info("par2cmdline binary... found (%s)", sabnzbd.newsunpack.PAR2C_COMMAND)
if sabnzbd.newsunpack.MULTIPAR_COMMAND:
logging.info("MultiPar binary... found (%s)", sabnzbd.newsunpack.MULTIPAR_COMMAND)
elif sabnzbd.WIN32:
logging.error('%s %s' % (T('MultiPar binary... NOT found!'), T('Verification and repair will not be possible.')))
if sabnzbd.newsunpack.RAR_COMMAND:
logging.info("UNRAR binary... found (%s)", sabnzbd.newsunpack.RAR_COMMAND)
@@ -441,9 +445,9 @@ def print_modules():
want_str = '%.2f' % (float(sabnzbd.constants.REC_RAR_VERSION) / 100)
logging.warning(T('Your UNRAR version is %s, we recommend version %s or higher.<br />') % (have_str, want_str))
elif not (sabnzbd.WIN32 or sabnzbd.DARWIN):
logging.info('UNRAR binary version %.2f', (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
logging.debug('UNRAR binary version %.2f', (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
else:
logging.error('%s %s' % (T('unrar binary... NOT found'), T('Downloads will not unpacked.')))
logging.error(T('unrar binary... NOT found'))
if sabnzbd.newsunpack.ZIP_COMMAND:
logging.info("unzip binary... found (%s)", sabnzbd.newsunpack.ZIP_COMMAND)
@@ -657,7 +661,7 @@ def is_sabnzbd_running(url):
url = '%s&mode=version' % (url)
# Do this without certificate verification, few installations will have that
prev = sabnzbd.set_https_verification(False)
ver = get_from_url(url)
ver = sabnzbd.newsunpack.get_from_url(url)
sabnzbd.set_https_verification(prev)
return (ver and (re.search(r'\d+\.\d+\.', ver) or ver.strip() == sabnzbd.__version__))
except:
@@ -723,6 +727,24 @@ def evaluate_inipath(path):
return path
def cherrypy_logging(log_path, log_handler):
""" Setup CherryPy logging """
log = cherrypy.log
log.access_file = ''
log.error_file = ''
# Max size of 512KB
maxBytes = getattr(log, "rot_maxBytes", 524288)
# cherrypy.log cherrypy.log.1 cherrypy.log.2
backupCount = getattr(log, "rot_backupCount", 3)
# Make a new RotatingFileHandler for the error log.
fname = getattr(log, "rot_error_file", log_path)
h = log_handler(fname, 'a', maxBytes, backupCount)
h.setLevel(logging.DEBUG)
h.setFormatter(cherrypy._cplogging.logfmt)
log.error_log.addHandler(h)
def commandline_handler(frozen=True):
""" Split win32-service commands are true parameters
Returns:
@@ -821,7 +843,7 @@ def main():
logging_level = None
web_dir = None
vista_plus = False
win64 = False
vista64 = False
repair = 0
api_url = None
no_login = False
@@ -946,8 +968,8 @@ def main():
# Detect Windows variant
if sabnzbd.WIN32:
vista_plus, win64 = windows_variant()
sabnzbd.WIN64 = win64
vista_plus, vista64 = windows_variant()
sabnzbd.WIN64 = vista64
if not SQLITE_DLL:
panic_sqlite(sabnzbd.MY_FULLNAME)
@@ -1026,7 +1048,7 @@ def main():
pass
else:
if not url:
url = 'https://%s:%s%s/api?' % (browserhost, port, sabnzbd.cfg.url_base())
url = 'https://%s:%s/sabnzbd/api?' % (browserhost, port)
if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
# Bail out if we have fixed our ports after first start-up
if sabnzbd.cfg.fixed_ports():
@@ -1055,7 +1077,7 @@ def main():
pass
else:
if not url:
url = 'http://%s:%s%s/api?' % (browserhost, cherryport, sabnzbd.cfg.url_base())
url = 'http://%s:%s/sabnzbd/api?' % (browserhost, cherryport)
if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
# Bail out if we have fixed our ports after first start-up
if sabnzbd.cfg.fixed_ports():
@@ -1150,12 +1172,14 @@ def main():
logging.info('Full executable path = %s', sabnzbd.MY_FULLNAME)
if sabnzbd.WIN32:
suffix = ''
if win64:
suffix = '(win64)'
if vista_plus:
suffix = ' (=Vista+)'
if vista64:
suffix = ' (=Vista+ x64)'
try:
logging.info('Platform = %s %s', platform.platform(), suffix)
logging.info('Platform=%s%s Class=%s', platform.platform(), suffix, os.name)
except:
logging.info('Platform = %s <unknown>', suffix)
logging.info('Platform=%s <unknown> Class=%s', suffix, os.name)
else:
logging.info('Platform = %s', os.name)
logging.info('Python-version = %s', sys.version)
@@ -1173,24 +1197,7 @@ def main():
if not sabnzbd.WIN32 and not sabnzbd.DARWIN and not ('utf' in preferredencoding.lower() and '8' in preferredencoding.lower()):
logging.warning(T("SABnzbd was started with encoding %s, this should be UTF-8. Expect problems with Unicoded file and directory names in downloads.") % preferredencoding)
# SSL Information
logging.info("SSL version = %s", ssl.OPENSSL_VERSION)
# Load (extra) certificates in the binary distributions
if hasattr(sys, "frozen") and (sabnzbd.WIN32 or sabnzbd.DARWIN):
# The certifi package brings the latest certificates on build
# This will cause the create_default_context to load it automatically
os.environ["SSL_CERT_FILE"] = os.path.join(sabnzbd.DIR_PROG, 'cacert.pem')
logging.info('Loaded additional certificates from %s', os.environ["SSL_CERT_FILE"])
# Extra startup info
if sabnzbd.cfg.log_level() > 1:
# List the number of certificates available (can take up to 1.5 seconds)
if sabnzbd.HAVE_SSL_CONTEXT:
ctx = ssl.create_default_context()
logging.debug('Available certificates: %s', repr(ctx.cert_store_stats()))
# Show IPv4/IPv6 address
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6
mylocalipv4 = localipv4()
@@ -1215,12 +1222,12 @@ def main():
from sabnzbd.utils.getperformance import getpystone, getcpu
pystoneperf = getpystone()
if pystoneperf:
logging.debug('CPU Pystone available performance = %s', pystoneperf)
logging.debug('CPU Pystone available performance is %s', pystoneperf)
else:
logging.debug('CPU Pystone available performance could not be calculated')
cpumodel = getcpu() # Linux only
if cpumodel:
logging.debug('CPU model = %s', cpumodel)
logging.debug('CPU model name is %s', cpumodel)
logging.info('Read INI file %s', inifile)
@@ -1264,9 +1271,12 @@ def main():
# Find external programs
sabnzbd.newsunpack.find_programs(sabnzbd.DIR_PROG)
print_modules()
# HTTPS certificate generation
logging.info("SSL version %s", sabnzbd.utils.sslinfo.ssl_version())
logging.info("SSL supported protocols %s", str(sabnzbd.utils.sslinfo.ssl_protocols_labels()))
https_cert = sabnzbd.cfg.https_cert.get_path()
https_key = sabnzbd.cfg.https_key.get_path()
https_chain = sabnzbd.cfg.https_chain.get_path()
@@ -1282,7 +1292,6 @@ def main():
logging.warning(T('Disabled HTTPS because of missing CERT and KEY files'))
enable_https = False
# Starting of the webserver
# Determine if this system has multiple definitions for 'localhost'
hosts = all_localhosts()
multilocal = len(hosts) > 1 and cherryhost in ('localhost', '0.0.0.0')
@@ -1335,7 +1344,6 @@ def main():
'server.socket_port': cherryport,
'server.shutdown_timeout': 0,
'log.screen': False,
'engine.timeout_monitor.on': False,
'engine.autoreload.on': False,
'tools.encode.on': True,
'tools.gzip.on': True,
@@ -1345,6 +1353,7 @@ def main():
'error_page.404': sabnzbd.panic.error_page_404
})
# Do we want CherryPy Logging? Cannot be done via the config
if cherrypylogging:
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
@@ -1372,7 +1381,7 @@ def main():
# Make available from both URLs
main_page = sabnzbd.interface.MainPage()
cherrypy.tree.mount(main_page, '/', config=appconfig)
cherrypy.tree.mount(main_page, sabnzbd.cfg.url_base(), config=appconfig)
cherrypy.tree.mount(main_page, '/sabnzbd/', config=appconfig)
# Set authentication for CherryPy
sabnzbd.interface.set_auth(cherrypy.config)
@@ -1389,15 +1398,15 @@ def main():
# Wait for server to become ready
cherrypy.engine.wait(cherrypy.process.wspbus.states.STARTED)
sabnzbd.zconfig.set_bonjour(cherryhost, cherryport)
# Window Service support
mail = None
if sabnzbd.WIN32:
if enable_https:
mode = 's'
else:
mode = ''
api_url = 'http%s://%s:%s%s/api?apikey=%s' % (mode, browserhost, cherryport, sabnzbd.cfg.url_base(), sabnzbd.cfg.api_key())
api_url = 'http%s://%s:%s/sabnzbd/api?apikey=%s' % (mode, browserhost, cherryport, sabnzbd.cfg.api_key())
if sabnzbd.WIN_SERVICE:
mail = MailSlot()
@@ -1430,9 +1439,9 @@ def main():
# Set URL for browser
if enable_https:
browser_url = "https://%s:%s%s" % (browserhost, cherryport, sabnzbd.cfg.url_base())
browser_url = "https://%s:%s/sabnzbd" % (browserhost, cherryport)
else:
browser_url = "http://%s:%s%s" % (browserhost, cherryport, sabnzbd.cfg.url_base())
browser_url = "http://%s:%s/sabnzbd" % (browserhost, cherryport)
sabnzbd.BROWSER_URL = browser_url
if not autorestarted:
@@ -1446,13 +1455,6 @@ def main():
check_latest_version()
autorestarted = False
# ZeroConfig/Bonjour needs a ip. Lets try to find it.
try:
z_host = socket.gethostbyname(socket.gethostname())
except socket.gaierror:
z_host = cherryhost
sabnzbd.zconfig.set_bonjour(z_host, cherryport)
# Have to keep this running, otherwise logging will terminate
timer = 0
while not sabnzbd.SABSTOP:
@@ -1701,8 +1703,9 @@ if __name__ == '__main__':
main()
elif getattr(sys, 'frozen', None) == 'macosx_app':
# OSX binary
try:
# OSX binary runner
from PyObjCTools import AppHelper
from sabnzbd.osxmenu import SABnzbdDelegate
@@ -1727,7 +1730,9 @@ if __name__ == '__main__':
sabApp = startApp()
sabApp.start()
AppHelper.runEventLoop()
except:
main()
else:
main()

View File

@@ -1,6 +0,0 @@
install:
- pip install --upgrade -r tests/requirements.txt
- pip install pypiwin32 subprocessww
build_script:
- pytest

View File

@@ -596,7 +596,7 @@ class MemcachedSession(Session):
# Wrap all .get and .set operations in a single lock.
mc_lock = threading.RLock()
# This is a separate set of locks per session id.
# This is a seperate set of locks per session id.
locks = {}
servers = ['127.0.0.1:11211']

View File

@@ -196,13 +196,12 @@ socket_errors_to_ignore = plat_specific_errors(
)
socket_errors_to_ignore.append('timed out')
socket_errors_to_ignore.append('The read operation timed out')
if sys.platform == 'darwin':
socket_errors_to_ignore.append(plat_specific_errors('EPROTOTYPE'))
socket_errors_nonblocking = plat_specific_errors(
'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
if sys.platform == 'darwin':
socket_errors_to_ignore.extend(plat_specific_errors('EPROTOTYPE'))
socket_errors_nonblocking.extend(plat_specific_errors('EPROTOTYPE'))
comma_separated_headers = [
ntob(h) for h in
['Accept', 'Accept-Charset', 'Accept-Encoding',

View File

@@ -98,12 +98,6 @@ class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
# The connection can safely be dropped.
return None, {}
raise
except:
# Temporary fix for https://github.com/cherrypy/cherrypy/issues/1618
e = sys.exc_info()[1]
if e.args == (0, 'Error'):
return None, {}
raise
return s, self.get_environ(s)
# TODO: fill this out more with mod ssl env

View File

@@ -0,0 +1,16 @@
import six
import mock
from cherrypy import wsgiserver
class TestWSGIGateway_u0:
@mock.patch('cherrypy.wsgiserver.WSGIGateway_10.get_environ',
lambda self: {'foo': 'bar'})
def test_decodes_items(self):
req = mock.MagicMock(path=b'/', qs=b'')
gw = wsgiserver.WSGIGateway_u0(req=req)
env = gw.get_environ()
assert env['foo'] == 'bar'
assert isinstance(env['foo'], six.text_type)

View File

@@ -0,0 +1,509 @@
import re
import hashlib
import time
import StringIO
__version__ = '0.8'
#GNTP/<version> <messagetype> <encryptionAlgorithmID>[:<ivValue>][ <keyHashAlgorithmID>:<keyHash>.<salt>]
GNTP_INFO_LINE = re.compile(
'GNTP/(?P<version>\d+\.\d+) (?P<messagetype>REGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)' +
' (?P<encryptionAlgorithmID>[A-Z0-9]+(:(?P<ivValue>[A-F0-9]+))?) ?' +
'((?P<keyHashAlgorithmID>[A-Z0-9]+):(?P<keyHash>[A-F0-9]+).(?P<salt>[A-F0-9]+))?\r\n',
re.IGNORECASE
)
GNTP_INFO_LINE_SHORT = re.compile(
'GNTP/(?P<version>\d+\.\d+) (?P<messagetype>REGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)',
re.IGNORECASE
)
GNTP_HEADER = re.compile('([\w-]+):(.+)')
GNTP_EOL = '\r\n'
class BaseError(Exception):
def gntp_error(self):
error = GNTPError(self.errorcode, self.errordesc)
return error.encode()
class ParseError(BaseError):
errorcode = 500
errordesc = 'Error parsing the message'
class AuthError(BaseError):
errorcode = 400
errordesc = 'Error with authorization'
class UnsupportedError(BaseError):
errorcode = 500
errordesc = 'Currently unsupported by gntp.py'
class _GNTPBuffer(StringIO.StringIO):
"""GNTP Buffer class"""
def writefmt(self, message="", *args):
"""Shortcut function for writing GNTP Headers"""
self.write((message % args).encode('utf8', 'replace'))
self.write(GNTP_EOL)
class _GNTPBase(object):
"""Base initilization
:param string messagetype: GNTP Message type
:param string version: GNTP Protocol version
:param string encription: Encryption protocol
"""
def __init__(self, messagetype=None, version='1.0', encryption=None):
self.info = {
'version': version,
'messagetype': messagetype,
'encryptionAlgorithmID': encryption
}
self.headers = {}
self.resources = {}
def __str__(self):
return self.encode()
def _parse_info(self, data):
"""Parse the first line of a GNTP message to get security and other info values
:param string data: GNTP Message
:return dict: Parsed GNTP Info line
"""
match = GNTP_INFO_LINE.match(data)
if not match:
raise ParseError('ERROR_PARSING_INFO_LINE')
info = match.groupdict()
if info['encryptionAlgorithmID'] == 'NONE':
info['encryptionAlgorithmID'] = None
return info
def set_password(self, password, encryptAlgo='MD5'):
"""Set a password for a GNTP Message
:param string password: Null to clear password
:param string encryptAlgo: Supports MD5, SHA1, SHA256, SHA512
"""
hash = {
'MD5': hashlib.md5,
'SHA1': hashlib.sha1,
'SHA256': hashlib.sha256,
'SHA512': hashlib.sha512,
}
self.password = password
self.encryptAlgo = encryptAlgo.upper()
if not password:
self.info['encryptionAlgorithmID'] = None
self.info['keyHashAlgorithm'] = None
return
if not self.encryptAlgo in hash.keys():
raise UnsupportedError('INVALID HASH "%s"' % self.encryptAlgo)
hashfunction = hash.get(self.encryptAlgo)
password = password.encode('utf8')
seed = time.ctime()
salt = hashfunction(seed).hexdigest()
saltHash = hashfunction(seed).digest()
keyBasis = password + saltHash
key = hashfunction(keyBasis).digest()
keyHash = hashfunction(key).hexdigest()
self.info['keyHashAlgorithmID'] = self.encryptAlgo
self.info['keyHash'] = keyHash.upper()
self.info['salt'] = salt.upper()
def _decode_hex(self, value):
"""Helper function to decode hex string to `proper` hex string
:param string value: Human readable hex string
:return string: Hex string
"""
result = ''
for i in range(0, len(value), 2):
tmp = int(value[i:i + 2], 16)
result += chr(tmp)
return result
def _decode_binary(self, rawIdentifier, identifier):
rawIdentifier += '\r\n\r\n'
dataLength = int(identifier['Length'])
pointerStart = self.raw.find(rawIdentifier) + len(rawIdentifier)
pointerEnd = pointerStart + dataLength
data = self.raw[pointerStart:pointerEnd]
if not len(data) == dataLength:
raise ParseError('INVALID_DATA_LENGTH Expected: %s Recieved %s' % (dataLength, len(data)))
return data
def _validate_password(self, password):
"""Validate GNTP Message against stored password"""
self.password = password
if password == None:
raise AuthError('Missing password')
keyHash = self.info.get('keyHash', None)
if keyHash is None and self.password is None:
return True
if keyHash is None:
raise AuthError('Invalid keyHash')
if self.password is None:
raise AuthError('Missing password')
password = self.password.encode('utf8')
saltHash = self._decode_hex(self.info['salt'])
keyBasis = password + saltHash
key = hashlib.md5(keyBasis).digest()
keyHash = hashlib.md5(key).hexdigest()
if not keyHash.upper() == self.info['keyHash'].upper():
raise AuthError('Invalid Hash')
return True
def validate(self):
"""Verify required headers"""
for header in self._requiredHeaders:
if not self.headers.get(header, False):
raise ParseError('Missing Notification Header: ' + header)
def _format_info(self):
"""Generate info line for GNTP Message
:return string:
"""
info = u'GNTP/%s %s' % (
self.info.get('version'),
self.info.get('messagetype'),
)
if self.info.get('encryptionAlgorithmID', None):
info += ' %s:%s' % (
self.info.get('encryptionAlgorithmID'),
self.info.get('ivValue'),
)
else:
info += ' NONE'
if self.info.get('keyHashAlgorithmID', None):
info += ' %s:%s.%s' % (
self.info.get('keyHashAlgorithmID'),
self.info.get('keyHash'),
self.info.get('salt')
)
return info
def _parse_dict(self, data):
"""Helper function to parse blocks of GNTP headers into a dictionary
:param string data:
:return dict:
"""
dict = {}
for line in data.split('\r\n'):
match = GNTP_HEADER.match(line)
if not match:
continue
key = unicode(match.group(1).strip(), 'utf8', 'replace')
val = unicode(match.group(2).strip(), 'utf8', 'replace')
dict[key] = val
return dict
def add_header(self, key, value):
if isinstance(value, unicode):
self.headers[key] = value
else:
self.headers[key] = unicode('%s' % value, 'utf8', 'replace')
def add_resource(self, data):
"""Add binary resource
:param string data: Binary Data
"""
identifier = hashlib.md5(data).hexdigest()
self.resources[identifier] = data
return 'x-growl-resource://%s' % identifier
def decode(self, data, password=None):
"""Decode GNTP Message
:param string data:
"""
self.password = password
self.raw = data
parts = self.raw.split('\r\n\r\n')
self.info = self._parse_info(data)
self.headers = self._parse_dict(parts[0])
def encode(self):
"""Encode a generic GNTP Message
:return string: GNTP Message ready to be sent
"""
buffer = _GNTPBuffer()
buffer.writefmt(self._format_info())
#Headers
for k, v in self.headers.iteritems():
buffer.writefmt('%s: %s', k, v)
buffer.writefmt()
#Resources
for resource, data in self.resources.iteritems():
buffer.writefmt('Identifier: %s', resource)
buffer.writefmt('Length: %d', len(data))
buffer.writefmt()
buffer.write(data)
buffer.writefmt()
buffer.writefmt()
return buffer.getvalue()
class GNTPRegister(_GNTPBase):
"""Represents a GNTP Registration Command
:param string data: (Optional) See decode()
:param string password: (Optional) Password to use while encoding/decoding messages
"""
_requiredHeaders = [
'Application-Name',
'Notifications-Count'
]
_requiredNotificationHeaders = ['Notification-Name']
def __init__(self, data=None, password=None):
_GNTPBase.__init__(self, 'REGISTER')
self.notifications = []
if data:
self.decode(data, password)
else:
self.set_password(password)
self.add_header('Application-Name', 'pygntp')
self.add_header('Notifications-Count', 0)
def validate(self):
'''Validate required headers and validate notification headers'''
for header in self._requiredHeaders:
if not self.headers.get(header, False):
raise ParseError('Missing Registration Header: ' + header)
for notice in self.notifications:
for header in self._requiredNotificationHeaders:
if not notice.get(header, False):
raise ParseError('Missing Notification Header: ' + header)
def decode(self, data, password):
"""Decode existing GNTP Registration message
:param string data: Message to decode
"""
self.raw = data
parts = self.raw.split('\r\n\r\n')
self.info = self._parse_info(data)
self._validate_password(password)
self.headers = self._parse_dict(parts[0])
for i, part in enumerate(parts):
if i == 0:
continue # Skip Header
if part.strip() == '':
continue
notice = self._parse_dict(part)
if notice.get('Notification-Name', False):
self.notifications.append(notice)
elif notice.get('Identifier', False):
notice['Data'] = self._decode_binary(part, notice)
#open('register.png','wblol').write(notice['Data'])
self.resources[notice.get('Identifier')] = notice
def add_notification(self, name, enabled=True):
"""Add new Notification to Registration message
:param string name: Notification Name
:param boolean enabled: Enable this notification by default
"""
notice = {}
notice['Notification-Name'] = u'%s' % name
notice['Notification-Enabled'] = u'%s' % enabled
self.notifications.append(notice)
self.add_header('Notifications-Count', len(self.notifications))
def encode(self):
"""Encode a GNTP Registration Message
:return string: Encoded GNTP Registration message
"""
buffer = _GNTPBuffer()
buffer.writefmt(self._format_info())
#Headers
for k, v in self.headers.iteritems():
buffer.writefmt('%s: %s', k, v)
buffer.writefmt()
#Notifications
if len(self.notifications) > 0:
for notice in self.notifications:
for k, v in notice.iteritems():
buffer.writefmt('%s: %s', k, v)
buffer.writefmt()
#Resources
for resource, data in self.resources.iteritems():
buffer.writefmt('Identifier: %s', resource)
buffer.writefmt('Length: %d', len(data))
buffer.writefmt()
buffer.write(data)
buffer.writefmt()
buffer.writefmt()
return buffer.getvalue()
class GNTPNotice(_GNTPBase):
"""Represents a GNTP Notification Command
:param string data: (Optional) See decode()
:param string app: (Optional) Set Application-Name
:param string name: (Optional) Set Notification-Name
:param string title: (Optional) Set Notification Title
:param string password: (Optional) Password to use while encoding/decoding messages
"""
_requiredHeaders = [
'Application-Name',
'Notification-Name',
'Notification-Title'
]
def __init__(self, data=None, app=None, name=None, title=None, password=None):
_GNTPBase.__init__(self, 'NOTIFY')
if data:
self.decode(data, password)
else:
self.set_password(password)
if app:
self.add_header('Application-Name', app)
if name:
self.add_header('Notification-Name', name)
if title:
self.add_header('Notification-Title', title)
def decode(self, data, password):
"""Decode existing GNTP Notification message
:param string data: Message to decode.
"""
self.raw = data
parts = self.raw.split('\r\n\r\n')
self.info = self._parse_info(data)
self._validate_password(password)
self.headers = self._parse_dict(parts[0])
for i, part in enumerate(parts):
if i == 0:
continue # Skip Header
if part.strip() == '':
continue
notice = self._parse_dict(part)
if notice.get('Identifier', False):
notice['Data'] = self._decode_binary(part, notice)
#open('notice.png','wblol').write(notice['Data'])
self.resources[notice.get('Identifier')] = notice
class GNTPSubscribe(_GNTPBase):
"""Represents a GNTP Subscribe Command
:param string data: (Optional) See decode()
:param string password: (Optional) Password to use while encoding/decoding messages
"""
_requiredHeaders = [
'Subscriber-ID',
'Subscriber-Name',
]
def __init__(self, data=None, password=None):
_GNTPBase.__init__(self, 'SUBSCRIBE')
if data:
self.decode(data, password)
else:
self.set_password(password)
class GNTPOK(_GNTPBase):
"""Represents a GNTP OK Response
:param string data: (Optional) See _GNTPResponse.decode()
:param string action: (Optional) Set type of action the OK Response is for
"""
_requiredHeaders = ['Response-Action']
def __init__(self, data=None, action=None):
_GNTPBase.__init__(self, '-OK')
if data:
self.decode(data)
if action:
self.add_header('Response-Action', action)
class GNTPError(_GNTPBase):
"""Represents a GNTP Error response
:param string data: (Optional) See _GNTPResponse.decode()
:param string errorcode: (Optional) Error code
:param string errordesc: (Optional) Error Description
"""
_requiredHeaders = ['Error-Code', 'Error-Description']
def __init__(self, data=None, errorcode=None, errordesc=None):
_GNTPBase.__init__(self, '-ERROR')
if data:
self.decode(data)
if errorcode:
self.add_header('Error-Code', errorcode)
self.add_header('Error-Description', errordesc)
def error(self):
return (self.headers.get('Error-Code', None),
self.headers.get('Error-Description', None))
def parse_gntp(data, password=None):
"""Attempt to parse a message as a GNTP message
:param string data: Message to be parsed
:param string password: Optional password to be used to verify the message
"""
match = GNTP_INFO_LINE_SHORT.match(data)
if not match:
raise ParseError('INVALID_GNTP_INFO')
info = match.groupdict()
if info['messagetype'] == 'REGISTER':
return GNTPRegister(data, password=password)
elif info['messagetype'] == 'NOTIFY':
return GNTPNotice(data, password=password)
elif info['messagetype'] == 'SUBSCRIBE':
return GNTPSubscribe(data, password=password)
elif info['messagetype'] == '-OK':
return GNTPOK(data)
elif info['messagetype'] == '-ERROR':
return GNTPError(data)
raise ParseError('INVALID_GNTP_MESSAGE')

View File

@@ -1,141 +0,0 @@
# Copyright: 2013 Paul Traylor
# These sources are released under the terms of the MIT license: see LICENSE
import logging
import os
import sys
from optparse import OptionParser, OptionGroup
from gntp.notifier import GrowlNotifier
from gntp.shim import RawConfigParser
from gntp.version import __version__
DEFAULT_CONFIG = os.path.expanduser('~/.gntp')
config = RawConfigParser({
'hostname': 'localhost',
'password': None,
'port': 23053,
})
config.read([DEFAULT_CONFIG])
if not config.has_section('gntp'):
config.add_section('gntp')
class ClientParser(OptionParser):
def __init__(self):
OptionParser.__init__(self, version="%%prog %s" % __version__)
group = OptionGroup(self, "Network Options")
group.add_option("-H", "--host",
dest="host", default=config.get('gntp', 'hostname'),
help="Specify a hostname to which to send a remote notification. [%default]")
group.add_option("--port",
dest="port", default=config.getint('gntp', 'port'), type="int",
help="port to listen on [%default]")
group.add_option("-P", "--password",
dest='password', default=config.get('gntp', 'password'),
help="Network password")
self.add_option_group(group)
group = OptionGroup(self, "Notification Options")
group.add_option("-n", "--name",
dest="app", default='Python GNTP Test Client',
help="Set the name of the application [%default]")
group.add_option("-s", "--sticky",
dest='sticky', default=False, action="store_true",
help="Make the notification sticky [%default]")
group.add_option("--image",
dest="icon", default=None,
help="Icon for notification (URL or /path/to/file)")
group.add_option("-m", "--message",
dest="message", default=None,
help="Sets the message instead of using stdin")
group.add_option("-p", "--priority",
dest="priority", default=0, type="int",
help="-2 to 2 [%default]")
group.add_option("-d", "--identifier",
dest="identifier",
help="Identifier for coalescing")
group.add_option("-t", "--title",
dest="title", default=None,
help="Set the title of the notification [%default]")
group.add_option("-N", "--notification",
dest="name", default='Notification',
help="Set the notification name [%default]")
group.add_option("--callback",
dest="callback",
help="URL callback")
self.add_option_group(group)
# Extra Options
self.add_option('-v', '--verbose',
dest='verbose', default=0, action='count',
help="Verbosity levels")
def parse_args(self, args=None, values=None):
values, args = OptionParser.parse_args(self, args, values)
if values.message is None:
print('Enter a message followed by Ctrl-D')
try:
message = sys.stdin.read()
except KeyboardInterrupt:
exit()
else:
message = values.message
if values.title is None:
values.title = ' '.join(args)
# If we still have an empty title, use the
# first bit of the message as the title
if values.title == '':
values.title = message[:20]
values.verbose = logging.WARNING - values.verbose * 10
return values, message
def main():
(options, message) = ClientParser().parse_args()
logging.basicConfig(level=options.verbose)
if not os.path.exists(DEFAULT_CONFIG):
logging.info('No config read found at %s', DEFAULT_CONFIG)
growl = GrowlNotifier(
applicationName=options.app,
notifications=[options.name],
defaultNotifications=[options.name],
hostname=options.host,
password=options.password,
port=options.port,
)
result = growl.register()
if result is not True:
exit(result)
# This would likely be better placed within the growl notifier
# class but until I make _checkIcon smarter this is "easier"
if options.icon and growl._checkIcon(options.icon) is False:
logging.info('Loading image %s', options.icon)
f = open(options.icon, 'rb')
options.icon = f.read()
f.close()
result = growl.notify(
noteType=options.name,
title=options.title,
description=message,
icon=options.icon,
sticky=options.sticky,
priority=options.priority,
callback=options.callback,
identifier=options.identifier,
)
if result is not True:
exit(result)
if __name__ == "__main__":
main()

View File

@@ -1,77 +0,0 @@
# Copyright: 2013 Paul Traylor
# These sources are released under the terms of the MIT license: see LICENSE
"""
The gntp.config module is provided as an extended GrowlNotifier object that takes
advantage of the ConfigParser module to allow us to setup some default values
(such as hostname, password, and port) in a more global way to be shared among
programs using gntp
"""
import logging
import os
import gntp.notifier
import gntp.shim
__all__ = [
'mini',
'GrowlNotifier'
]
logger = logging.getLogger(__name__)
class GrowlNotifier(gntp.notifier.GrowlNotifier):
"""
ConfigParser enhanced GrowlNotifier object
For right now, we are only interested in letting users overide certain
values from ~/.gntp
::
[gntp]
hostname = ?
password = ?
port = ?
"""
def __init__(self, *args, **kwargs):
config = gntp.shim.RawConfigParser({
'hostname': kwargs.get('hostname', 'localhost'),
'password': kwargs.get('password'),
'port': kwargs.get('port', 23053),
})
config.read([os.path.expanduser('~/.gntp')])
# If the file does not exist, then there will be no gntp section defined
# and the config.get() lines below will get confused. Since we are not
# saving the config, it should be safe to just add it here so the
# code below doesn't complain
if not config.has_section('gntp'):
logger.info('Error reading ~/.gntp config file')
config.add_section('gntp')
kwargs['password'] = config.get('gntp', 'password')
kwargs['hostname'] = config.get('gntp', 'hostname')
kwargs['port'] = config.getint('gntp', 'port')
super(GrowlNotifier, self).__init__(*args, **kwargs)
def mini(description, **kwargs):
"""Single notification function
Simple notification function in one line. Has only one required parameter
and attempts to use reasonable defaults for everything else
:param string description: Notification message
"""
kwargs['notifierFactory'] = GrowlNotifier
gntp.notifier.mini(description, **kwargs)
if __name__ == '__main__':
# If we're running this module directly we're likely running it as a test
# so extra debugging is useful
logging.basicConfig(level=logging.INFO)
mini('Testing mini notification')

View File

@@ -1,518 +0,0 @@
# Copyright: 2013 Paul Traylor
# These sources are released under the terms of the MIT license: see LICENSE
import hashlib
import re
import time
import gntp.shim
import gntp.errors as errors
__all__ = [
'GNTPRegister',
'GNTPNotice',
'GNTPSubscribe',
'GNTPOK',
'GNTPError',
'parse_gntp',
]
#GNTP/<version> <messagetype> <encryptionAlgorithmID>[:<ivValue>][ <keyHashAlgorithmID>:<keyHash>.<salt>]
GNTP_INFO_LINE = re.compile(
'GNTP/(?P<version>\d+\.\d+) (?P<messagetype>REGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)' +
' (?P<encryptionAlgorithmID>[A-Z0-9]+(:(?P<ivValue>[A-F0-9]+))?) ?' +
'((?P<keyHashAlgorithmID>[A-Z0-9]+):(?P<keyHash>[A-F0-9]+).(?P<salt>[A-F0-9]+))?\r\n',
re.IGNORECASE
)
GNTP_INFO_LINE_SHORT = re.compile(
'GNTP/(?P<version>\d+\.\d+) (?P<messagetype>REGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)',
re.IGNORECASE
)
GNTP_HEADER = re.compile('([\w-]+):(.+)')
GNTP_EOL = gntp.shim.b('\r\n')
GNTP_SEP = gntp.shim.b(': ')
class _GNTPBuffer(gntp.shim.StringIO):
"""GNTP Buffer class"""
def writeln(self, value=None):
if value:
self.write(gntp.shim.b(value))
self.write(GNTP_EOL)
def writeheader(self, key, value):
if not isinstance(value, str):
value = str(value)
self.write(gntp.shim.b(key))
self.write(GNTP_SEP)
self.write(gntp.shim.b(value))
self.write(GNTP_EOL)
class _GNTPBase(object):
"""Base initilization
:param string messagetype: GNTP Message type
:param string version: GNTP Protocol version
:param string encription: Encryption protocol
"""
def __init__(self, messagetype=None, version='1.0', encryption=None):
self.info = {
'version': version,
'messagetype': messagetype,
'encryptionAlgorithmID': encryption
}
self.hash_algo = {
'MD5': hashlib.md5,
'SHA1': hashlib.sha1,
'SHA256': hashlib.sha256,
'SHA512': hashlib.sha512,
}
self.headers = {}
self.resources = {}
# For Python2 we can just return the bytes as is without worry
# but on Python3 we want to make sure we return the packet as
# a unicode string so that things like logging won't get confused
if gntp.shim.PY2:
def __str__(self):
return self.encode()
else:
def __str__(self):
return gntp.shim.u(self.encode())
def _parse_info(self, data):
"""Parse the first line of a GNTP message to get security and other info values
:param string data: GNTP Message
:return dict: Parsed GNTP Info line
"""
match = GNTP_INFO_LINE.match(data)
if not match:
raise errors.ParseError('ERROR_PARSING_INFO_LINE')
info = match.groupdict()
if info['encryptionAlgorithmID'] == 'NONE':
info['encryptionAlgorithmID'] = None
return info
def set_password(self, password, encryptAlgo='MD5'):
"""Set a password for a GNTP Message
:param string password: Null to clear password
:param string encryptAlgo: Supports MD5, SHA1, SHA256, SHA512
"""
if not password:
self.info['encryptionAlgorithmID'] = None
self.info['keyHashAlgorithm'] = None
return
self.password = gntp.shim.b(password)
self.encryptAlgo = encryptAlgo.upper()
if not self.encryptAlgo in self.hash_algo:
raise errors.UnsupportedError('INVALID HASH "%s"' % self.encryptAlgo)
hashfunction = self.hash_algo.get(self.encryptAlgo)
password = password.encode('utf8')
seed = time.ctime().encode('utf8')
salt = hashfunction(seed).hexdigest()
saltHash = hashfunction(seed).digest()
keyBasis = password + saltHash
key = hashfunction(keyBasis).digest()
keyHash = hashfunction(key).hexdigest()
self.info['keyHashAlgorithmID'] = self.encryptAlgo
self.info['keyHash'] = keyHash.upper()
self.info['salt'] = salt.upper()
def _decode_hex(self, value):
"""Helper function to decode hex string to `proper` hex string
:param string value: Human readable hex string
:return string: Hex string
"""
result = ''
for i in range(0, len(value), 2):
tmp = int(value[i:i + 2], 16)
result += chr(tmp)
return result
def _decode_binary(self, rawIdentifier, identifier):
rawIdentifier += '\r\n\r\n'
dataLength = int(identifier['Length'])
pointerStart = self.raw.find(rawIdentifier) + len(rawIdentifier)
pointerEnd = pointerStart + dataLength
data = self.raw[pointerStart:pointerEnd]
if not len(data) == dataLength:
raise errors.ParseError('INVALID_DATA_LENGTH Expected: %s Recieved %s' % (dataLength, len(data)))
return data
def _validate_password(self, password):
"""Validate GNTP Message against stored password"""
self.password = password
if password is None:
raise errors.AuthError('Missing password')
keyHash = self.info.get('keyHash', None)
if keyHash is None and self.password is None:
return True
if keyHash is None:
raise errors.AuthError('Invalid keyHash')
if self.password is None:
raise errors.AuthError('Missing password')
keyHashAlgorithmID = self.info.get('keyHashAlgorithmID','MD5')
password = self.password.encode('utf8')
saltHash = self._decode_hex(self.info['salt'])
keyBasis = password + saltHash
self.key = self.hash_algo[keyHashAlgorithmID](keyBasis).digest()
keyHash = self.hash_algo[keyHashAlgorithmID](self.key).hexdigest()
if not keyHash.upper() == self.info['keyHash'].upper():
raise errors.AuthError('Invalid Hash')
return True
def validate(self):
"""Verify required headers"""
for header in self._requiredHeaders:
if not self.headers.get(header, False):
raise errors.ParseError('Missing Notification Header: ' + header)
def _format_info(self):
"""Generate info line for GNTP Message
:return string:
"""
info = 'GNTP/%s %s' % (
self.info.get('version'),
self.info.get('messagetype'),
)
if self.info.get('encryptionAlgorithmID', None):
info += ' %s:%s' % (
self.info.get('encryptionAlgorithmID'),
self.info.get('ivValue'),
)
else:
info += ' NONE'
if self.info.get('keyHashAlgorithmID', None):
info += ' %s:%s.%s' % (
self.info.get('keyHashAlgorithmID'),
self.info.get('keyHash'),
self.info.get('salt')
)
return info
def _parse_dict(self, data):
"""Helper function to parse blocks of GNTP headers into a dictionary
:param string data:
:return dict: Dictionary of parsed GNTP Headers
"""
d = {}
for line in data.split('\r\n'):
match = GNTP_HEADER.match(line)
if not match:
continue
key = match.group(1).strip()
val = match.group(2).strip()
d[key] = val
return d
def add_header(self, key, value):
self.headers[key] = value
def add_resource(self, data):
"""Add binary resource
:param string data: Binary Data
"""
data = gntp.shim.b(data)
identifier = hashlib.md5(data).hexdigest()
self.resources[identifier] = data
return 'x-growl-resource://%s' % identifier
def decode(self, data, password=None):
"""Decode GNTP Message
:param string data:
"""
self.password = password
self.raw = gntp.shim.u(data)
parts = self.raw.split('\r\n\r\n')
self.info = self._parse_info(self.raw)
self.headers = self._parse_dict(parts[0])
def encode(self):
"""Encode a generic GNTP Message
:return string: GNTP Message ready to be sent. Returned as a byte string
"""
buff = _GNTPBuffer()
buff.writeln(self._format_info())
#Headers
for k, v in self.headers.items():
buff.writeheader(k, v)
buff.writeln()
#Resources
for resource, data in self.resources.items():
buff.writeheader('Identifier', resource)
buff.writeheader('Length', len(data))
buff.writeln()
buff.write(data)
buff.writeln()
buff.writeln()
return buff.getvalue()
class GNTPRegister(_GNTPBase):
"""Represents a GNTP Registration Command
:param string data: (Optional) See decode()
:param string password: (Optional) Password to use while encoding/decoding messages
"""
_requiredHeaders = [
'Application-Name',
'Notifications-Count'
]
_requiredNotificationHeaders = ['Notification-Name']
def __init__(self, data=None, password=None):
_GNTPBase.__init__(self, 'REGISTER')
self.notifications = []
if data:
self.decode(data, password)
else:
self.set_password(password)
self.add_header('Application-Name', 'pygntp')
self.add_header('Notifications-Count', 0)
def validate(self):
'''Validate required headers and validate notification headers'''
for header in self._requiredHeaders:
if not self.headers.get(header, False):
raise errors.ParseError('Missing Registration Header: ' + header)
for notice in self.notifications:
for header in self._requiredNotificationHeaders:
if not notice.get(header, False):
raise errors.ParseError('Missing Notification Header: ' + header)
def decode(self, data, password):
"""Decode existing GNTP Registration message
:param string data: Message to decode
"""
self.raw = gntp.shim.u(data)
parts = self.raw.split('\r\n\r\n')
self.info = self._parse_info(self.raw)
self._validate_password(password)
self.headers = self._parse_dict(parts[0])
for i, part in enumerate(parts):
if i == 0:
continue # Skip Header
if part.strip() == '':
continue
notice = self._parse_dict(part)
if notice.get('Notification-Name', False):
self.notifications.append(notice)
elif notice.get('Identifier', False):
notice['Data'] = self._decode_binary(part, notice)
#open('register.png','wblol').write(notice['Data'])
self.resources[notice.get('Identifier')] = notice
def add_notification(self, name, enabled=True):
"""Add new Notification to Registration message
:param string name: Notification Name
:param boolean enabled: Enable this notification by default
"""
notice = {}
notice['Notification-Name'] = name
notice['Notification-Enabled'] = enabled
self.notifications.append(notice)
self.add_header('Notifications-Count', len(self.notifications))
def encode(self):
"""Encode a GNTP Registration Message
:return string: Encoded GNTP Registration message. Returned as a byte string
"""
buff = _GNTPBuffer()
buff.writeln(self._format_info())
#Headers
for k, v in self.headers.items():
buff.writeheader(k, v)
buff.writeln()
#Notifications
if len(self.notifications) > 0:
for notice in self.notifications:
for k, v in notice.items():
buff.writeheader(k, v)
buff.writeln()
#Resources
for resource, data in self.resources.items():
buff.writeheader('Identifier', resource)
buff.writeheader('Length', len(data))
buff.writeln()
buff.write(data)
buff.writeln()
buff.writeln()
return buff.getvalue()
class GNTPNotice(_GNTPBase):
"""Represents a GNTP Notification Command
:param string data: (Optional) See decode()
:param string app: (Optional) Set Application-Name
:param string name: (Optional) Set Notification-Name
:param string title: (Optional) Set Notification Title
:param string password: (Optional) Password to use while encoding/decoding messages
"""
_requiredHeaders = [
'Application-Name',
'Notification-Name',
'Notification-Title'
]
def __init__(self, data=None, app=None, name=None, title=None, password=None):
_GNTPBase.__init__(self, 'NOTIFY')
if data:
self.decode(data, password)
else:
self.set_password(password)
if app:
self.add_header('Application-Name', app)
if name:
self.add_header('Notification-Name', name)
if title:
self.add_header('Notification-Title', title)
def decode(self, data, password):
"""Decode existing GNTP Notification message
:param string data: Message to decode.
"""
self.raw = gntp.shim.u(data)
parts = self.raw.split('\r\n\r\n')
self.info = self._parse_info(self.raw)
self._validate_password(password)
self.headers = self._parse_dict(parts[0])
for i, part in enumerate(parts):
if i == 0:
continue # Skip Header
if part.strip() == '':
continue
notice = self._parse_dict(part)
if notice.get('Identifier', False):
notice['Data'] = self._decode_binary(part, notice)
#open('notice.png','wblol').write(notice['Data'])
self.resources[notice.get('Identifier')] = notice
class GNTPSubscribe(_GNTPBase):
"""Represents a GNTP Subscribe Command
:param string data: (Optional) See decode()
:param string password: (Optional) Password to use while encoding/decoding messages
"""
_requiredHeaders = [
'Subscriber-ID',
'Subscriber-Name',
]
def __init__(self, data=None, password=None):
_GNTPBase.__init__(self, 'SUBSCRIBE')
if data:
self.decode(data, password)
else:
self.set_password(password)
class GNTPOK(_GNTPBase):
"""Represents a GNTP OK Response
:param string data: (Optional) See _GNTPResponse.decode()
:param string action: (Optional) Set type of action the OK Response is for
"""
_requiredHeaders = ['Response-Action']
def __init__(self, data=None, action=None):
_GNTPBase.__init__(self, '-OK')
if data:
self.decode(data)
if action:
self.add_header('Response-Action', action)
class GNTPError(_GNTPBase):
"""Represents a GNTP Error response
:param string data: (Optional) See _GNTPResponse.decode()
:param string errorcode: (Optional) Error code
:param string errordesc: (Optional) Error Description
"""
_requiredHeaders = ['Error-Code', 'Error-Description']
def __init__(self, data=None, errorcode=None, errordesc=None):
_GNTPBase.__init__(self, '-ERROR')
if data:
self.decode(data)
if errorcode:
self.add_header('Error-Code', errorcode)
self.add_header('Error-Description', errordesc)
def error(self):
return (self.headers.get('Error-Code', None),
self.headers.get('Error-Description', None))
def parse_gntp(data, password=None):
"""Attempt to parse a message as a GNTP message
:param string data: Message to be parsed
:param string password: Optional password to be used to verify the message
"""
data = gntp.shim.u(data)
match = GNTP_INFO_LINE_SHORT.match(data)
if not match:
raise errors.ParseError('INVALID_GNTP_INFO')
info = match.groupdict()
if info['messagetype'] == 'REGISTER':
return GNTPRegister(data, password=password)
elif info['messagetype'] == 'NOTIFY':
return GNTPNotice(data, password=password)
elif info['messagetype'] == 'SUBSCRIBE':
return GNTPSubscribe(data, password=password)
elif info['messagetype'] == '-OK':
return GNTPOK(data)
elif info['messagetype'] == '-ERROR':
return GNTPError(data)
raise errors.ParseError('INVALID_GNTP_MESSAGE')

View File

@@ -1,25 +0,0 @@
# Copyright: 2013 Paul Traylor
# These sources are released under the terms of the MIT license: see LICENSE
class BaseError(Exception):
pass
class ParseError(BaseError):
errorcode = 500
errordesc = 'Error parsing the message'
class AuthError(BaseError):
errorcode = 400
errordesc = 'Error with authorization'
class UnsupportedError(BaseError):
errorcode = 500
errordesc = 'Currently unsupported by gntp.py'
class NetworkError(BaseError):
errorcode = 500
errordesc = "Error connecting to growl server"

View File

@@ -1,6 +1,3 @@
# Copyright: 2013 Paul Traylor
# These sources are released under the terms of the MIT license: see LICENSE
"""
The gntp.notifier module is provided as a simple way to send notifications
using GNTP
@@ -12,15 +9,10 @@ using GNTP
`Original Python bindings <http://code.google.com/p/growl/source/browse/Bindings/python/Growl.py>`_
"""
import gntp
import socket
import logging
import platform
import socket
import sys
from gntp.version import __version__
import gntp.core
import gntp.errors as errors
import gntp.shim
__all__ = [
'mini',
@@ -30,6 +22,45 @@ __all__ = [
logger = logging.getLogger(__name__)
def mini(description, applicationName='PythonMini', noteType="Message",
title="Mini Message", applicationIcon=None, hostname='localhost',
password=None, port=23053, sticky=False, priority=None,
callback=None, notificationIcon=None, identifier=None):
"""Single notification function
Simple notification function in one line. Has only one required parameter
and attempts to use reasonable defaults for everything else
:param string description: Notification message
.. warning::
For now, only URL callbacks are supported. In the future, the
callback argument will also support a function
"""
growl = GrowlNotifier(
applicationName=applicationName,
notifications=[noteType],
defaultNotifications=[noteType],
applicationIcon=applicationIcon,
hostname=hostname,
password=password,
port=port,
)
result = growl.register()
if result is not True:
return result
return growl.notify(
noteType=noteType,
title=title,
description=description,
icon=notificationIcon,
sticky=sticky,
priority=priority,
callback=callback,
identifier=identifier,
)
class GrowlNotifier(object):
"""Helper class to simplfy sending Growl messages
@@ -68,9 +99,8 @@ class GrowlNotifier(object):
If it's a simple URL icon, then we return True. If it's a data icon
then we return False
'''
logger.info('Checking icon')
return gntp.shim.u(data)[:4] in ['http', 'file']
logger.debug('Checking icon')
return data.startswith('http')
def register(self):
"""Send GNTP Registration
@@ -79,8 +109,8 @@ class GrowlNotifier(object):
Before sending notifications to Growl, you need to have
sent a registration message at least once
"""
logger.info('Sending registration to %s:%s', self.hostname, self.port)
register = gntp.core.GNTPRegister()
logger.debug('Sending registration to %s:%s', self.hostname, self.port)
register = gntp.GNTPRegister()
register.add_header('Application-Name', self.applicationName)
for notification in self.notifications:
enabled = notification in self.defaultNotifications
@@ -89,8 +119,8 @@ class GrowlNotifier(object):
if self._checkIcon(self.applicationIcon):
register.add_header('Application-Icon', self.applicationIcon)
else:
resource = register.add_resource(self.applicationIcon)
register.add_header('Application-Icon', resource)
id = register.add_resource(self.applicationIcon)
register.add_header('Application-Icon', id)
if self.password:
register.set_password(self.password, self.passwordHash)
self.add_origin_info(register)
@@ -98,7 +128,7 @@ class GrowlNotifier(object):
return self._send('register', register)
def notify(self, noteType, title, description, icon=None, sticky=False,
priority=None, callback=None, identifier=None, custom={}):
priority=None, callback=None, identifier=None):
"""Send a GNTP notifications
.. warning::
@@ -111,16 +141,14 @@ class GrowlNotifier(object):
:param boolean sticky: Sticky notification
:param integer priority: Message priority level from -2 to 2
:param string callback: URL callback
:param dict custom: Custom attributes. Key names should be prefixed with X-
according to the spec but this is not enforced by this class
.. warning::
For now, only URL callbacks are supported. In the future, the
callback argument will also support a function
"""
logger.info('Sending notification [%s] to %s:%s', noteType, self.hostname, self.port)
logger.debug('Sending notification [%s] to %s:%s', noteType, self.hostname, self.port)
assert noteType in self.notifications
notice = gntp.core.GNTPNotice()
notice = gntp.GNTPNotice()
notice.add_header('Application-Name', self.applicationName)
notice.add_header('Notification-Name', noteType)
notice.add_header('Notification-Title', title)
@@ -134,8 +162,8 @@ class GrowlNotifier(object):
if self._checkIcon(icon):
notice.add_header('Notification-Icon', icon)
else:
resource = notice.add_resource(icon)
notice.add_header('Notification-Icon', resource)
id = notice.add_resource(icon)
notice.add_header('Notification-Icon', id)
if description:
notice.add_header('Notification-Text', description)
@@ -144,9 +172,6 @@ class GrowlNotifier(object):
if identifier:
notice.add_header('Notification-Coalescing-ID', identifier)
for key in custom:
notice.add_header(key, custom[key])
self.add_origin_info(notice)
self.notify_hook(notice)
@@ -154,7 +179,7 @@ class GrowlNotifier(object):
def subscribe(self, id, name, port):
"""Send a Subscribe request to a remote machine"""
sub = gntp.core.GNTPSubscribe()
sub = gntp.GNTPSubscribe()
sub.add_header('Subscriber-ID', id)
sub.add_header('Subscriber-Name', name)
sub.add_header('Subscriber-Port', port)
@@ -170,7 +195,7 @@ class GrowlNotifier(object):
"""Add optional Origin headers to message"""
packet.add_header('Origin-Machine-Name', platform.node())
packet.add_header('Origin-Software-Name', 'gntp.py')
packet.add_header('Origin-Software-Version', __version__)
packet.add_header('Origin-Software-Version', gntp.__version__)
packet.add_header('Origin-Platform-Name', platform.system())
packet.add_header('Origin-Platform-Version', platform.platform())
@@ -189,78 +214,34 @@ class GrowlNotifier(object):
packet.validate()
data = packet.encode()
logger.debug('To : %s:%s <%s>\n%s', self.hostname, self.port, packet.__class__, data)
#logger.debug('To : %s:%s <%s>\n%s', self.hostname, self.port, packet.__class__, data)
#Less verbose
logger.debug('To : %s:%s <%s>', self.hostname, self.port, packet.__class__)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(self.socketTimeout)
try:
s.connect((self.hostname, self.port))
s.send(data)
recv_data = s.recv(1024)
while not recv_data.endswith(gntp.shim.b("\r\n\r\n")):
recv_data += s.recv(1024)
except socket.error:
# Python2.5 and Python3 compatibile exception
exc = sys.exc_info()[1]
raise errors.NetworkError(exc)
response = gntp.core.parse_gntp(recv_data)
s.connect((self.hostname, self.port))
s.send(data)
recv_data = s.recv(1024)
while not recv_data.endswith("\r\n\r\n"):
recv_data += s.recv(1024)
response = gntp.parse_gntp(recv_data)
s.close()
logger.debug('From : %s:%s <%s>\n%s', self.hostname, self.port, response.__class__, response)
#logger.debug('From : %s:%s <%s>\n%s', self.hostname, self.port, response.__class__, response)
#Less verbose
logger.debug('From : %s:%s <%s>', self.hostname, self.port, response.__class__)
if type(response) == gntp.core.GNTPOK:
if type(response) == gntp.GNTPOK:
return True
if response.error()[0] == '404' and 'disabled' in response.error()[1]:
# Ignore message saying that user has disabled this class
return True
logger.error('Invalid response: %s', response.error())
return response.error()
def mini(description, applicationName='PythonMini', noteType="Message",
title="Mini Message", applicationIcon=None, hostname='localhost',
password=None, port=23053, sticky=False, priority=None,
callback=None, notificationIcon=None, identifier=None,
notifierFactory=GrowlNotifier):
"""Single notification function
Simple notification function in one line. Has only one required parameter
and attempts to use reasonable defaults for everything else
:param string description: Notification message
.. warning::
For now, only URL callbacks are supported. In the future, the
callback argument will also support a function
"""
try:
growl = notifierFactory(
applicationName=applicationName,
notifications=[noteType],
defaultNotifications=[noteType],
applicationIcon=applicationIcon,
hostname=hostname,
password=password,
port=port,
)
result = growl.register()
if result is not True:
return result
return growl.notify(
noteType=noteType,
title=title,
description=description,
icon=notificationIcon,
sticky=sticky,
priority=priority,
callback=callback,
identifier=identifier,
)
except Exception:
# We want the "mini" function to be simple and swallow Exceptions
# in order to be less invasive
logger.exception("Growl error")
if __name__ == '__main__':
# If we're running this module directly we're likely running it as a test
# so extra debugging is useful
logging.basicConfig(level=logging.INFO)
logging.basicConfig(level=logging.DEBUG)
mini('Testing mini notification')

View File

@@ -1,46 +0,0 @@
# Copyright: 2013 Paul Traylor
# These sources are released under the terms of the MIT license: see LICENSE
"""
Python2.5 and Python3.3 compatibility shim
Heavily inspirted by the "six" library.
https://pypi.python.org/pypi/six
"""
import sys
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
if PY3:
def b(s):
if isinstance(s, bytes):
return s
return s.encode('utf8', 'replace')
def u(s):
if isinstance(s, bytes):
return s.decode('utf8', 'replace')
return s
from io import BytesIO as StringIO
from configparser import RawConfigParser
else:
def b(s):
if isinstance(s, unicode):
return s.encode('utf8', 'replace')
return s
def u(s):
if isinstance(s, unicode):
return s
if isinstance(s, int):
s = str(s)
return unicode(s, "utf8", "replace")
from StringIO import StringIO
from ConfigParser import RawConfigParser
b.__doc__ = "Ensure we have a byte string"
u.__doc__ = "Ensure we have a unicode string"

View File

@@ -1,4 +0,0 @@
# Copyright: 2013 Paul Traylor
# These sources are released under the terms of the MIT license: see LICENSE
__version__ = '1.0.3'

View File

@@ -22,7 +22,7 @@
</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="apple-mobile-web-app-title" content="SABnzbd" />
<link rel="apple-touch-icon" sizes="76x76" href="${root}staticcfg/ico/apple-touch-icon-76x76-precomposed.png" />
@@ -32,9 +32,7 @@
<link rel="apple-touch-icon" sizes="192x192" href="${root}staticcfg/ico/android-192x192.png" />
<link rel="stylesheet" type="text/css" href="${root}staticcfg/bootstrap/css/bootstrap.min.css?v=$version" />
<link rel="stylesheet" type="text/css" href="${root}staticcfg/css/chartist.min.css" />
<link rel="stylesheet" type="text/css" href="${root}staticcfg/css/style.css?v=$version" />
<link rel="stylesheet" type="text/css" href="${root}staticcfg/css/style.css?p=$pid" />
<link rel="shortcut icon" href="${root}staticcfg/ico/favicon.ico?v=$version" />
<script type="text/javascript">
@@ -45,7 +43,6 @@
// Information we need
var sabSession = '$session';
var rootURL = '${root}'
var urlBase = '${url_base}'
var folderBrowseUrl = '${root}tapi?mode=browse&output=json&apikey=$session';
var folderSeperator = '#if $os.sep == '\\' then '\\\\' else '/'#'
@@ -62,7 +59,7 @@
configTranslate.confirmLeave = "$T('confirmWithoutSavingPrompt')";
configTranslate.searchPages = ['$T('cmenu-general')', '$T('cmenu-folders')', '$T('cmenu-switches')', '$T('cmenu-sorting')', '$T('cmenu-notif')', '$T('cmenu-special')']
</script>
<script type="text/javascript" src="${root}staticcfg/js/jquery-3.2.1.min.js?v=$version"></script>
<script type="text/javascript" src="${root}staticcfg/js/jquery-3.1.1.min.js?v=$version"></script>
<script type="text/javascript" src="${root}staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
<script type="text/javascript" src="${root}staticcfg/js/script.js?v=$version"></script>
<script type="text/javascript">

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Config"#-->
<!--#set global $help_uri="configuration/2.3/configure"#-->
<!--#set global $help_uri="configuration/2.1/configure"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<!--#from locale import getpreferredencoding#-->
@@ -30,7 +30,7 @@
<tr>
<th scope="row">OpenSSL:</th>
<td>
$ssl_version
$ssl_version &nbsp; [$ssl_protocols]
</td>
</tr>
<!--#if not $have_ssl_context#-->
@@ -41,16 +41,12 @@
</td>
</tr>
<!--#end if#-->
<!--#if not $nt and not $darwin#-->
<!--#if not $have_mt_par2#-->
<tr>
<th scope="row">$T('opt-multicore-par2')</th>
<th scope="row">Multicore Par2</th>
<td>
<!--#if $have_mt_par2#-->
<span class="glyphicon glyphicon-ok"></span>
<!--#else#-->
<span class="label label-warning">$T('notAvailable')</span> $T('explain-getpar2mt')
<a href="${helpuri}installation/multicore-par2" target="_blank">${helpuri}installation/multicore-par2</a>
<!--#end if#-->
</td>
</tr>
<!--#end if#-->
@@ -81,7 +77,7 @@
</td>
</tr>
<!--#end if#-->
<!--#if not $have_unzip and not $have_7zip #-->
<!--#if not $have_unzip #-->
<tr>
<th scope="row">$T('opt-enable_unzip'):</th>
<td>
@@ -109,7 +105,7 @@
<tbody>
<tr>
<th scope="row">$T('homePage') </th>
<td><a href="https://sabnzbd.org/" target="_blank">https://sabnzbd.org/</a></td>
<td><a href="http://sabnzbd.org/" target="_blank">http://sabnzbd.org/</a></td>
</tr>
<tr>
<th scope="row">$T('menu-wiki') </th>
@@ -117,7 +113,7 @@
</tr>
<tr>
<th scope="row">$T('menu-forums') </th>
<td><a href="https://forums.sabnzbd.org/" target="_blank">https://forums.sabnzbd.org/</a></td>
<td><a href="http://forums.sabnzbd.org/" target="_blank">http://forums.sabnzbd.org/</a></td>
</tr>
<tr>
<th scope="row">$T('source') </th>
@@ -131,10 +127,6 @@
<th scope="row">$T('menu-issues') </th>
<td><a href="https://sabnzbd.org/wiki/introduction/known-issues" target="_blank">https://sabnzbd.org/wiki/introduction/known-issues</a></td>
</tr>
<tr>
<th scope="row">$T('menu-donate') </th>
<td><a href="https://sabnzbd.org/donate" target="_blank">https://sabnzbd.org/donate</a></td>
</tr>
</tbody>
</table>
</div>

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Categories"#-->
<!--#set global $help_uri="configuration/2.3/categories"#-->
<!--#set global $help_uri="configuration/2.1/categories"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
<div class="section">

View File

@@ -1,13 +1,8 @@
<!--#set global $pane="Folders"#-->
<!--#set global $help_uri="configuration/2.3/folders"#-->
<!--#set global $help_uri="configuration/2.1/folders"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
<div class="padding alt section">
<label for="advanced-settings-button" class="form-control advanced-button ">
<input type="checkbox" id="advanced-settings-button" name="advanced-settings-button"> $T('button-advanced')
</label>
</div>
<form action="saveDirectories" method="post" name="fullform" class="fullform" autocomplete="off">
<input type="hidden" id="session" name="session" value="$session" />
<input type="hidden" id="ajax" name="ajax" value="1" />
@@ -26,7 +21,7 @@
<input type="text" name="download_dir" id="download_dir" value="$download_dir" data-initialdir="$my_home" />
<span class="desc">$T('explain-download_dir')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="download_free">$T('opt-download_free')</label>
<input type="text" name="download_free" id="download_free" value="$download_free" class="smaller_input" />
<span class="desc">$T('explain-download_free')</span>
@@ -37,7 +32,7 @@
<span class="desc">$T('explain-complete_dir')</span>
</div>
<!--#if not $nt#-->
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="permissions">$T('opt-permissions')</label>
<input type="text" name="permissions" id="permissions" value="$permissions" class="smaller_input" />
<span class="desc">$T('explain-permissions')</span>
@@ -48,7 +43,7 @@
<input type="text" name="dirscan_dir" id="dirscan_dir" value="$dirscan_dir" data-initialdir="$my_home" />
<span class="desc">$T('explain-dirscan_dir')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="dirscan_speed">$T('opt-dirscan_speed')</label>
<input type="number" name="dirscan_speed" id="dirscan_speed" value="$dirscan_speed" min="0" max="3600" />
<span class="desc">$T('explain-dirscan_speed')</span>
@@ -58,12 +53,12 @@
<input type="text" name="script_dir" id="script_dir" value="$script_dir" data-initialdir="$my_home" />
<span class="desc">$T('explain-script_dir')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="email_dir">$T('opt-email_dir')</label>
<input type="text" name="email_dir" id="email_dir" value="$email_dir" data-initialdir="$my_home" />
<span class="desc">$T('explain-email_dir')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="password_file">$T('opt-password_file')</label>
<input type="text" name="password_file" id="password_file" value="$password_file" />
<span class="desc">$T('explain-password_file')</span>
@@ -77,7 +72,7 @@
</fieldset>
</div><!-- /col1 -->
</div><!-- /section -->
<div class="section advanced-settings">
<div class="section">
<div class="col2">
<h3>$T('systemFolders') <a href="$helpuri$help_uri#toc1" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
<p>$T('explain-folderConfig')</p>

View File

@@ -1,14 +1,9 @@
<!--#set global $pane="General"#-->
<!--#set global $help_uri="configuration/2.3/general"#-->
<!--#set global $help_uri="configuration/2.1/general"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
<div class="padding alt section">
<label for="advanced-settings-button" class="form-control advanced-button ">
<input type="checkbox" id="advanced-settings-button" name="advanced-settings-button"> $T('button-advanced')
</label>
</div>
<form action="saveGeneral" method="post" name="fullform" class="fullform" autocomplete="off">
<form action="saveGeneral" method="post" name="fullform" class="fullform" autocomplete="off" novalidate>
<input type="hidden" id="session" name="session" value="$session" />
<input type="hidden" id="ajax" name="ajax" value=1 />
<div class="section">
@@ -30,10 +25,10 @@
</div>
<div class="field-pair">
<label class="config" for="enable_https">$T('opt-enable_https')</label>
<input type="checkbox" name="enable_https" id="enable_https" value="1" <!--#if int($enable_https) > 0 then 'checked="checked" data-original="1"' else ""#-->/>
<input type="checkbox" name="enable_https" id="enable_https" value="1" <!--#if int($enable_https) > 0 then 'checked="checked"' else ""#-->/>
<span class="desc">$T('explain-enable_https')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="web_dir">$T('opt-web_dir')</label>
<select name="web_dir" id="web_dir">
<!--#for $webline in $web_list#-->
@@ -94,6 +89,7 @@
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default sabnzbd_restart"><span class="glyphicon glyphicon-refresh"></span> $T('button-restart') SABnzbd</button>
<button class="btn btn-default advancedButton enable_https_options"><span class="glyphicon glyphicon-cog"></span> $T('button-advanced')</button>
</div>
</fieldset>
</div>
@@ -177,7 +173,7 @@
</select>
<span class="desc">$T('explain-check_new_rel')</span>
</div>
<div class="field-pair advanced-settings <!--#if int($have_ssl_context) == 0 then "disabled" else ""#-->">
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#-->">
<label class="config" for="enable_https_verification">$T('opt-enable_https_verification')</label>
<input type="checkbox" name="enable_https_verification" id="enable_https_verification" value="1" <!--#if int($enable_https_verification) > 0 then 'checked="checked"' else ""#--> <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#--> />
<span class="desc">$T('explain-enable_https_verification')</span>
@@ -204,12 +200,12 @@
</select>
<input type="hidden" name="bandwidth_max" id="bandwidth_max" value="$bandwidth_max" />
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="bandwidth_perc">$T('opt-bandwidth_perc')</label>
<input type="number" name="bandwidth_perc" id="bandwidth_perc" value="$bandwidth_perc" step="10" min="0" max="100"/>
<span class="desc">$T('explain-bandwidth_perc')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="cache_limit">$T('opt-cache_limitstr')</label>
<input type="text" name="cache_limit" id="cache_limit" value="$cache_limit" class="smaller_input" />
<span class="desc">$T('explain-cache_limitstr').replace("64M", "256M").replace("128M", "512M")</span>
@@ -244,45 +240,9 @@
\$('.alert-translate').show()
}
}
\$('#language').on('change', function() {
// Show message
hideOrShowTranslate()
// Re-load page on submit
\$('.fullform').submit(function() {
// Skip the fancy stuff, just submit
this.submit()
})
// No JSON reponse
\$('#ajax').val('')
})
\$('#language').on('change', hideOrShowTranslate)
hideOrShowTranslate()
// Highlight in case user is not safe
// So when exposed to internet and no password, no external limit or no username/password
var safeCheck = \$('#host, #local_ranges, #inet_exposure, #${pid}_wu, #${pid}_wp')
function checkSafety() {
if(\$('#host').val() != 'localhost' && \$('#host').val() != '127.0.0.1') {
// No limitation on local-network
if(!\$('#local_ranges').val() || \$('#inet_exposure').val() > 3) {
// And no username and password?
if(!\$('#${pid}_wu').val() || !\$('#${pid}_wp').val()) {
// Add warning icon if not there already
if(!\$('.host-warning').length) {
safeCheck.after('<span class="glyphicon glyphicon-alert host-warning"></span>')
\$('.host-warning').tooltip({'title': '$T('checkSafety')'})
}
return
}
}
}
// Remove warnings
\$('.host-warning').remove()
safeCheck.removeClass('host-warning-highlight')
}
checkSafety()
safeCheck.on('change', checkSafety)
// Click functions
\$('#apikey, #nzbkey').click(function () { \$(this).select() });
\$('#generate_new_apikey').click(function () {
@@ -354,19 +314,14 @@
if(bandwidthLimit) {
var bandwithLimitNumber = parseFloat(bandwidthLimit)
var bandwithLimitText = bandwidthLimit.replace(/[^a-zA-Z]+/g, '');
if(bandwithLimitNumber) {
\$('#bandwidth_max_value').val(bandwithLimitNumber)
\$('#bandwidth_max_dropdown').val(bandwithLimitText)
}
\$('#bandwidth_max_value').val(bandwithLimitNumber)
\$('#bandwidth_max_dropdown').val(bandwithLimitText)
}
// Update the value
\$('#bandwidth_max_value, #bandwidth_max_dropdown').on('change', function() {
if(\$('#bandwidth_max_value').val()) {
\$('#bandwidth_max').val(\$('#bandwidth_max_value').val() + \$('#bandwidth_max_dropdown').val())
} else {
\$('#bandwidth_max').val('')
}
\$('#bandwidth_max').val(\$('#bandwidth_max_value').val() + \$('#bandwidth_max_dropdown').val())
})
});

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Email"#-->
<!--#set global $help_uri="configuration/2.3/notifications"#-->
<!--#set global $help_uri="configuration/2.1/notifications"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<!--#def show_notify_checkboxes($section_label)#-->
@@ -13,34 +13,14 @@
<!--#end for#-->
<!--#end def#-->
<!--#def show_cat_box($section_label)#-->
<div class="col2-cats" <!--#if int($getVar($section_label + '_enable')) > 0 then '' else 'style="display:none"'#-->>
<hr>
<b>$T('affectedCat')</b><br/>
<select name="${section_label}_cats" multiple="multiple" class="multiple_cats">
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $ct in $getVar($section_label + '_cats') then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
<!--#end for#-->
</select>
</div>
<!--#end def#-->
<div class="colmask">
<form action="saveEmail" method="post" name="fullform" class="fullform" autocomplete="off">
<form action="saveEmail" method="post" name="fullform" class="fullform" autocomplete="off" novalidate>
<input type="hidden" id="session" name="session" value="$session" />
<input type="hidden" id="ajax" name="ajax" value="1" />
<div class="section" id="email">
<div class="col2">
<h3>$T('cmenu-email') <a href="$helpuri$help_uri#toc0" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
<div class="col2-cats" <!--#if int($email_endjob) > 0 then '' else 'style="display:none"'#-->>
<b>$T('affectedCat')</b><br/>
<select name="email_cats" multiple="multiple" class="multiple_cats">
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $ct in $email_cats then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
<!--#end for#-->
</select>
</div>
</div>
</div><!-- /col2 -->
<div class="col1">
<fieldset>
<div class="field-pair">
@@ -91,7 +71,7 @@
<input type="text" name="email_pwd" id="email_pwd" value="$email_pwd" />
<span class="desc">$T('explain-email_pwd')</span>
</div>
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default" type="button" id="test_email" rel="$T('askTestEmail')"><span class="glyphicon glyphicon-envelope"></span> $T('link-testEmail')</button>
</div>
@@ -99,8 +79,8 @@
<div class="alert"></div>
</div>
</fieldset>
</div>
</div>
</div><!-- /col1 -->
</div><!-- /section -->
<!--#if $have_ncenter#-->
<div class="section">
<div class="col2">
@@ -111,11 +91,11 @@
<td><label for="ncenter_enable"> $T('opt-ncenter_enable')</label></td>
</tr>
</table>
</div>
</div><!-- /col2 -->
<div class="col1" <!--#if int($ncenter_enable) > 0 then '' else 'style="display:none"'#-->>
<fieldset>
$show_notify_checkboxes('ncenter')
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default" type="button" id="test_notif"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
</div>
@@ -123,8 +103,8 @@
<div class="alert"></div>
</div>
</fieldset>
</div>
</div>
</div><!-- /col1 -->
</div><!-- /section -->
<!--#end if#-->
<!--#if $nt#-->
<div class="section">
@@ -136,12 +116,11 @@
<td><label for="acenter_enable"> $T('opt-acenter_enable')</label></td>
</tr>
</table>
$show_cat_box('acenter')
</div>
</div><!-- /col2 -->
<div class="col1" <!--#if int($acenter_enable) > 0 then '' else 'style="display:none"'#-->>
<fieldset>
$show_notify_checkboxes('acenter')
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default" type="button" id="test_windows"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
</div>
@@ -149,8 +128,8 @@
<div class="alert"></div>
</div>
</fieldset>
</div>
</div>
</div><!-- /col1 -->
</div><!-- /section -->
<!--#end if#-->
<!--#if $have_ntfosd#-->
<div class="section">
@@ -162,12 +141,11 @@
<td><label for="ntfosd_enable"> $T('opt-ntfosd_enable')</label></td>
</tr>
</table>
$show_cat_box('ntfosd')
</div>
</div><!-- /col2 -->
<div class="col1" <!--#if int($ntfosd_enable) > 0 then '' else 'style="display:none"'#-->>
<fieldset>
$show_notify_checkboxes('ntfosd')
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default" type="button" id="test_osd"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
</div>
@@ -175,48 +153,9 @@
<div class="alert"></div>
</div>
</fieldset>
</div>
</div>
</div><!-- /col1 -->
</div><!-- /section -->
<!--#end if#-->
<div class="section" id="nscript">
<div class="col2">
<h3>$T('section-NScript')</h3>
<table>
<tr>
<td><input type="checkbox" name="nscript_enable" id="nscript_enable" value="1" <!--#if int($nscript_enable) > 0 then 'checked="checked"' else ""#--> /></td>
<td><label for="nscript_enable"> $T('opt-nscript_enable')</label></td>
</tr>
</table>
<em>$T('explain-nscript_enable')</em><br><a href="$helpuri$help_uri#nscript" target="_blank">$T('readwiki')</a>
$show_cat_box('nscript')
</div>
<div class="col1" <!--#if int($nscript_enable) > 0 then '' else 'style="display:none"'#-->>
<fieldset>
<div class="field-pair">
<label class="config" for="nscript_script">$T('opt-nscript_script')</label>
<select name="nscript_script">
<!--#for $sc in $scripts#-->
<option value="$sc" <!--#if $nscript_script == $sc then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
<!--#end for#-->
</select>
<span class="desc">$T('explain-nscript_script')</span>
</div>
<div class="field-pair">
<label class="config" for="nscript_parameters">$T('opt-nscript_parameters')</label>
<input type="text" name="nscript_parameters" id="nscript_parameters" value="$nscript_parameters" />
<span class="desc">$T('Optional') - $T('explain-nscript_parameters')</span>
</div>
$show_notify_checkboxes('nscript')
<div class="field-pair no-field-pair-bg">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default" type="button" id="test_nscript"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
</div>
<div class="field-pair result-box">
<div class="alert"></div>
</div>
</fieldset>
</div>
</div>
<div class="section" id="growl">
<div class="col2">
<h3>$T('growlSettings') <a href="$helpuri$help_uri#toc3" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
@@ -226,8 +165,7 @@
<td><label for="growl_enable"> $T('opt-growl_enable')</label></td>
</tr>
</table>
$show_cat_box('growl')
</div>
</div><!-- /col2 -->
<div class="col1" <!--#if int($growl_enable) > 0 then '' else 'style="display:none"'#-->>
<fieldset>
<div class="field-pair">
@@ -241,7 +179,7 @@
<span class="desc">$T('explain-growl_password')</span>
</div>
$show_notify_checkboxes('growl')
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default" type="button" id="test_growl"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
</div>
@@ -249,8 +187,8 @@
<div class="alert"></div>
</div>
</fieldset>
</div>
</div>
</div><!-- /col1 -->
</div><!-- /section -->
<div class="section" id="prowl">
<div class="col2">
<h3>$T('section-Prowl')</h3>
@@ -261,8 +199,7 @@
</tr>
</table>
<em>$T('explain-prowl_enable')</em>
$show_cat_box('prowl')
</div>
</div><!-- /col2 -->
<div class="col1" <!--#if int($prowl_enable) > 0 then '' else 'style="display:none"'#-->>
<fieldset>
<div class="field-pair">
@@ -286,7 +223,7 @@
</select>
</div>
<!--#end for#-->
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default" type="button" id="test_prowl"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
</div>
@@ -294,8 +231,8 @@
<div class="alert"></div>
</div>
</fieldset>
</div>
</div>
</div><!-- /col1 -->
</div><!-- /section -->
<div class="section" id="pushover">
<div class="col2">
@@ -307,8 +244,7 @@
</tr>
</table>
<em>$T('explain-pushover_enable')</em>
$show_cat_box('pushover')
</div>
</div><!-- /col2 -->
<div class="col1" <!--#if int($pushover_enable) > 0 then '' else 'style="display:none"'#-->>
<fieldset>
<div class="field-pair">
@@ -326,16 +262,6 @@
<input type="text" name="pushover_device" id="pushover_device" value="$pushover_device" />
<span class="desc">$T('explain-pushover_device')</span>
</div>
<div class="field-pair">
<label class="config" for="pushover_emergency_retry">$T('opt-pushover_emergency_retry')</label>
<input type="text" name="pushover_emergency_retry" id="pushover_emergency_retry" value="$pushover_emergency_retry" />
<span class="desc">$T('explain-pushover_emergency_retry')</span>
</div>
<div class="field-pair">
<label class="config" for="pushover_emergency_expire">$T('opt-pushover_emergency_expire')</label>
<input type="text" name="pushover_emergency_expire" id="pushover_emergency_expire" value="$pushover_emergency_expire" />
<span class="desc">$T('explain-pushover_emergency_expire')</span>
</div>
<!--#set $section_label = 'pushover'#-->
<!--#for $type in $notify_keys#-->
<div class="field-pair">
@@ -352,7 +278,7 @@
</select>
</div>
<!--#end for#-->
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default" type="button" id="test_pushover"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
</div>
@@ -360,8 +286,8 @@
<div class="alert"></div>
</div>
</fieldset>
</div>
</div>
</div><!-- /col1 -->
</div><!-- /section -->
<div class="section" id="pushbullet">
<div class="col2">
<h3>$T('section-Pushbullet')</h3>
@@ -372,8 +298,7 @@
</tr>
</table>
<em>$T('explain-pushbullet_enable')</em>
$show_cat_box('pushbullet')
</div>
</div><!-- /col2 -->
<div class="col1" <!--#if int($pushbullet_enable) > 0 then '' else 'style="display:none"'#-->>
<fieldset>
<div class="field-pair">
@@ -389,7 +314,7 @@
</div>
<!--#end if#-->
$show_notify_checkboxes('pushbullet')
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default" type="button" id="test_pushbullet"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
</div>
@@ -397,8 +322,46 @@
<div class="alert"></div>
</div>
</fieldset>
</div>
</div>
</div><!-- /col1 -->
</div><!-- /section -->
<div class="section" id="nscript">
<div class="col2">
<h3>$T('section-NScript')</h3>
<table>
<tr>
<td><input type="checkbox" name="nscript_enable" id="nscript_enable" value="1" <!--#if int($nscript_enable) > 0 then 'checked="checked"' else ""#--> /></td>
<td><label for="nscript_enable"> $T('opt-nscript_enable')</label></td>
</tr>
</table>
<em>$T('explain-nscript_enable')</em>
</div><!-- /col2 -->
<div class="col1" <!--#if int($nscript_enable) > 0 then '' else 'style="display:none"'#-->>
<fieldset>
<div class="field-pair">
<label class="config" for="nscript_script">$T('opt-nscript_script')</label>
<select name="nscript_script">
<!--#for $sc in $scripts#-->
<option value="$sc" <!--#if $nscript_script == $sc then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
<!--#end for#-->
</select>
<span class="desc">$T('explain-nscript_script')</span>
</div>
<div class="field-pair">
<label class="config" for="nscript_parameters">$T('opt-nscript_parameters')</label>
<input type="text" name="nscript_parameters" id="nscript_parameters" value="$nscript_parameters" />
<span class="desc">$T('Optional') - $T('explain-nscript_parameters')</span>
</div>
$show_notify_checkboxes('nscript')
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default" type="button" id="test_nscript"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
</div>
<div class="field-pair result-box">
<div class="alert"></div>
</div>
</fieldset>
</div><!-- /col1 -->
</div><!-- /section -->
</form>
</div><!-- /colmask -->
@@ -411,20 +374,10 @@
\$('.col2 input[name$="enable"]').change(function() {
if(this.checked) {
\$(this).parents('.section').find('.col1').show()
\$(this).parents('.col2').find('.col2-cats').show()
} else {
\$(this).parents('.section').find('.col1').hide()
\$(this).parents('.col2').find('.col2-cats').hide()
}
\$('form').submit()
addRowColor()
})
\$('#email_endjob').change(function() {
if(\$(this).val() > 0) {
\$(this).parents('.section').find('.col2-cats').show()
} else {
\$(this).parents('.section').find('.col2-cats').hide()
}
})
/**

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="RSS"#-->
<!--#set global $help_uri="configuration/2.3/rss"#-->
<!--#set global $help_uri="configuration/2.1/rss"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
<!--#if not $active_feed#-->
@@ -7,7 +7,7 @@
<div class="padTable">
<a class="main-helplink" href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
<p>$T('explain-RSS')</p>
<form action="add_rss_feed" method="post" autocomplete="off">
<form action="add_rss_feed" method="post" autocomplete="off" novalidate>
<input type="hidden" name="session" value="$session" />
<table class="catTable">
<tr>
@@ -37,7 +37,7 @@
<!--#if $rss#-->
<div class="section">
<div class="padTable">
<form action="save_rss_feed" method="post" autocomplete="off">
<form action="save_rss_feed" method="post" autocomplete="off" novalidate>
<input type="hidden" name="session" value="$session" />
<table id="subscriptions">
<tbody>
@@ -73,7 +73,7 @@
</form>
<!--#if $feeds#-->
<br/>
<form action="rss_now" method="post" autocomplete="off">
<form action="rss_now" method="post" autocomplete="off" novalidate>
<input type="hidden" name="session" value="$session" />
<button type="submit" class="btn btn-default readAll"><span class="glyphicon glyphicon-sort"></span> $T('button-rssNow')</button>
</form>
@@ -154,7 +154,7 @@
<option value=">"> $T('rss-atleast')</option>
<option value="<"> $T('rss-atmost')</option>
<option value="F"> $T('rss-from')</option>
<option value="F"> $T('rss-from-show') ($T('rss-accept'))</option>
<option value="F"> $T('rss-from-show')</option>
</select>
</td>
<td>
@@ -226,8 +226,8 @@
<option value="C"> $T('rss-mustcat')</option>
<option value=">"> $T('rss-atleast')</option>
<option value="<"> $T('rss-atmost')</option>
<option value="S"> $T('rss-from-show')</option>
<option value="F"> $T('rss-from')</option>
<option value="S"> $T('rss-from-show') ($T('rss-accept'))</option>
</select>
</td>
<td>
@@ -287,7 +287,7 @@
<input type="hidden" name="feed" value="$feed" />
<table class="catTable">
<tbody>
<tr class="<!--#if $odd then " alt " else " "#--> <!--#if $filter[3]!="A" and $filter[3]!="S" then 'disabled_options_rule' else ""#-->">
<tr class="<!--#if $odd then " alt " else " "#--> <!--#if $filter[3]!="A" then 'disabled_options_rule' else ""#-->">
<td>
<input type="checkbox" name="enabled" value="1" <!--#if $filter[6]=='1' then 'checked="checked"' else ""#--> />
</td>
@@ -302,8 +302,8 @@
<option value="C" <!--#if $filter[3]=="C" then 'selected="selected"' else ""#-->> $T('rss-mustcat')</option>
<option value=">" <!--#if $filter[3]==">" then 'selected="selected"' else ""#-->> $T('rss-atleast')</option>
<option value="<" <!--#if $filter[3]=="<" then 'selected="selected"' else ""#-->> $T('rss-atmost')</option>
<option value="S" <!--#if $filter[3]=="S" then 'selected="selected"' else ""#-->> $T('rss-from-show')</option>
<option value="F" <!--#if $filter[3]=="F" then 'selected="selected"' else ""#-->> $T('rss-from')</option>
<option value="S" <!--#if $filter[3]=="S" then 'selected="selected"' else ""#-->> $T('rss-from-show') ($T('rss-accept'))</option>
</select>
</td>
<td>
@@ -568,7 +568,7 @@ function urlencode(str) {
// Only the Accept filter needs all the options
\$('form[action="upd_rss_filter"]').find('select[name="filter_type"]').change(function() {
\$(this).parent().parent().find('select:not([name="filter_type"])').attr('disabled', \$(this).val() != "A" && \$(this).val() != "S")
\$(this).parent().parent().find('select:not([name="filter_type"])').attr('disabled', \$(this).val() != "A")
})
// Trigger on-load for all
\$('.disabled_options_rule').find('td select:not([name="filter_type"])').attr('disabled', true)

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Scheduling"#-->
<!--#set global $help_uri="configuration/2.3/scheduling"#-->
<!--#set global $help_uri="configuration/2.1/scheduling"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<%
@@ -59,13 +59,6 @@ else:
<option value="$server" data-action="0" data-noarg="1">$T('sch-disable_server') "$actions_servers[$server]"</option>
<!--#end for#-->
</optgroup>
<optgroup label="$T('cmenu-cat')">
<!--#for $cat in $categories#-->
<!--#set $cat_text = $T('Default') if $cat == '*' else $cat#-->
<option value="pause_cat" data-action="$cat" data-noarg="1">$T('sch-pause_cat') "$cat_text"</option>
<option value="resume_cat" data-action="$cat" data-noarg="1">$T('sch-resume_cat') "$cat_text"</option>
<!--#end for#-->
</optgroup>
</select>
</div>
<div class="field-pair" id="hidden_arguments" style="display: none">

View File

@@ -1,537 +1,396 @@
<!--#set global $pane="Servers"#-->
<!--#set global $help_uri="configuration/2.3/servers"#-->
<!--#set global $help_uri="configuration/2.1/servers"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<!--
We need to find how many months we have recorded so far, so we
loop over all the dates to find the lowest value and then use
this to calculate the date-selector and maximum value per month.
-->
<!--#import json#-->
<!--#import datetime#-->
<!--#import sabnzbd.misc#-->
<!--#set month_names = [$T('January'), $T('February'), $T('March'), $T('April'), $T('May'), $T('June'), $T('July'), $T('August'), $T('September'), $T('October'), $T('November'), $T('December')] #-->
<!--#set min_date = datetime.date.today()#-->
<!--#set max_data_all = {}#-->
<!--#for $server in $servers #-->
<!--#if 'amounts' in $server#-->
<!--#set max_data_server = {}#-->
<!--#for date in $server['amounts'][4]#-->
<!--#set split_date = $date.split('-')#-->
<!--#set min_date = min(min_date, datetime.date(int(split_date[0]), int(split_date[1]), 1))#-->
<!--#set month_date = $date[:7]#-->
<!--#if $month_date not in $max_data_server#-->
<!--#set max_data_server[$month_date] = 0#-->
<!--#end if#-->
<!--#set max_data_server[$month_date] = max(max_data_server[$month_date], $server['amounts'][4][$date])#-->
<!--#end for#-->
<!--#for month_date in max_data_server#-->
<!--#if $month_date not in $max_data_all#-->
<!--#set max_data_all[$month_date] = 0#-->
<!--#end if#-->
<!--#set max_data_all[$month_date] = max(max_data_all[$month_date], max_data_server[$month_date])#-->
<!--#end for#-->
<!--#end if#-->
<!--#end for#-->
<!--#set months_recorded = list(sabnzbd.misc.monthrange(min_date, datetime.date.today()))#-->
<!--#$months_recorded.reverse()#-->
<script type="text/javascript">
// Define variable needed for the server-plots
var serverData = {}
</script>
<div class="colmask">
<div class="padding alt section">
<button type="button" class="btn btn-default" id="addServerButton"><span class="glyphicon glyphicon-plus"></span> $T('button-addServer')</button>
<label for="advanced-settings-button" class="form-control advanced-button ">
<input type="checkbox" id="advanced-settings-button" name="advanced-settings-button"> $T('button-advanced')
</label>
<!--#if $months_recorded#-->
<div class="advanced-buttonSeperator"></div>
<div class="chart-selector-container" title="$T('srv-bandwidth')">
<span class="glyphicon glyphicon-signal"></span>
<select name="chart-selector" id="chart-selector">
<!--#for $cur_date in months_recorded#-->
<!--#set month_date = '%d-%02d' % ($cur_date.year, $cur_date.month)#-->
<!--#if $month_date not in $max_data_all#-->
<!--#set max_data_all[$month_date] = 0#-->
<!--#end if#-->
<option value="$month_date" data-max="$max_data_all[$month_date]">$month_names[$cur_date.month-1] $cur_date.year</option>
<!--#end for#-->
</select>
<form action="addServer" method="post" autocomplete="off" onsubmit="removeObfuscation();" novalidate>
<input type="hidden" name="session" value="$session" />
<div id="addServer">
<div class="padding alt">
<button type="button" class="btn btn-default" id="addServerButton"><span class="glyphicon glyphicon-plus"></span> $T('button-addServer')</button>
</div>
<!--#end if#-->
</div>
<div class="section" id="addServerContent" style="display: none;">
<div class="col2">
<h3>$T('addServer') <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
</div>
</div><!-- /col2 -->
<div class="col1">
<form action="addServer" method="post" autocomplete="off" onsubmit="removeObfuscation();">
<input type="hidden" name="session" value="$session" />
<fieldset>
<div class="field-pair">
<label class="config" for="enable">$T('srv-enable')</label>
<input type="checkbox" name="enable" id="enable" value="1" checked="checked" />
<span class="desc">$T('srv-enable')</span>
<fieldset>
<div class="field-pair">
<label class="config" for="enable">$T('srv-enable')</label>
<input type="checkbox" name="enable" id="enable" value="1" checked="checked" />
<span class="desc">$T('srv-enable')</span>
</div>
<div class="field-pair">
<label class="config" for="host">$T('srv-host')</label>
<input type="text" name="host" id="host" />
</div>
<div class="field-pair">
<label class="config" for="port">$T('srv-port')</label>
<input type="number" name="port" id="port" size="8" value="119" />
</div>
<div class="field-pair">
<label class="config" for="ssl">$T('srv-ssl')</label>
<input type="checkbox" name="ssl" id="ssl" value="1" />
<span class="desc">$T('explain-ssl')</span>
</div>
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
<div class="field-pair">
<label class="config" for="${pid}_00">$T('srv-username')</label>
<input type="text" name="${pid}_00" id="${pid}_00" data-hide="username" />
</div>
<div class="field-pair">
<label class="config" for="${pid}_01">$T('srv-password')</label>
<input type="text" name="${pid}_01" id="${pid}_01" data-hide="password" />
</div>
<div class="field-pair">
<label class="config" for="connections">$T('srv-connections')</label>
<input type="number" name="connections" id="connections" min="0" max="100" value="8" />
</div>
<div class="field-pair">
<label class="config" for="priority">$T('srv-priority')</label>
<input type="number" name="priority" id="priority" min="0" max="100" /> <i>$T('explain-svrprio')</i>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="retention">$T('srv-retention')</label>
<input type="number" name="retention" id="retention" min="0" /> <i>$T('days')</i>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="timeout">$T('srv-timeout')</label>
<input type="number" name="timeout" id="timeout" min="30" /> <i>$T('seconds')</i>
</div>
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#--> advanced-settings">
<label class="config" for="ssl_verify">$T('opt-ssl_verify')</label>
<select name="ssl_verify" id="ssl_verify" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
<option value="2" selected>$T('ssl_verify-strict')</option>
<option value="1">$T('ssl_verify-normal')</option>
<option value="0">$T('ssl_verify-disabled')</option>
</select>
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="send_group">$T('srv-send_group')</label>
<input type="checkbox" name="send_group" id="send_group" value="1" />
<span class="desc">$T('srv-explain-send_group')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="optional">$T('srv-optional')</label>
<input type="checkbox" name="optional" id="optional" value="1" />
<span class="desc">$T('explain-optional')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="categories">$T('srv-categories')</label>
<select name="categories" id="categories" multiple>
<!--#for $cat in $cats#-->
<option value="$cat" <!--#if $cat == "Default"#-->selected<!--#end if#-->>
<!--#if $cat == "Default" then $T('Default') else $cat#-->
</option>
<!--#end for#-->
</select>
<span class="desc">$T('srv-explain-categories')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="displayname">$T('srv-displayname')</label>
<input type="text" name="displayname" id="displayname" />
</div>
<div class="field-pair advanced-settings">
<label class="config" for="notes">$T('srv-notes')</label>
<textarea name="notes" id="notes" rows="3" cols="50"></textarea>
</div>
<div class="field-pair">
<button class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> $T('button-addServer')</button>
<button class="btn btn-default advancedButton"><span class="glyphicon glyphicon-cog"></span> $T('button-advanced')</button>
<button class="btn btn-default testServer" type="button"><span class="glyphicon glyphicon-sort"></span> $T('button-testServer')</button>
</div>
<div class="field-pair result-box">
<div class="alert"></div>
</div>
</fieldset>
</div><!-- /col1 -->
</div><!-- /section -->
</form>
<!--#set $prio_colors = ["#59cc33", "#3366cc","#7f33cc", "#cc33a6", "#cc3333"] #-->
<!--#set $cur_prio_color = -1 #-->
<!--#set $last_prio = -1 #-->
<!--#for $cur, $server in enumerate($servers) #-->
<form action="saveServer" method="post" class="fullform" autocomplete="off" novalidate>
<input type="hidden" name="session" value="$session" />
<input type="hidden" name="server" value="$server['name']" />
<div class="section <!--#if int($server['enable']) == 0 then 'server-disabled' else ""#-->">
<div class="col2 <!--#if int($server['enable']) == 0 then 'server-disabled' else ""#-->">
<h3>$server['displayname'] <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
<!--#if int($server['enable']) != 0 #-->
<!--#if $last_prio != $server['priority'] and $cur_prio_color+1 < len($prio_colors) #-->
<!--#set $cur_prio_color = $cur_prio_color+1 #-->
<!--#set $last_prio = $server['priority'] #-->
<!--#end if#-->
<span class="label label-primary" style="background-color: $prio_colors[$cur_prio_color]">$server['priority']</span>
<span class="label label-primary" style="background-color: $prio_colors[$cur_prio_color]">$T('srv-priority'):</span>
<!--#end if#-->
<table><tr>
<td><input type="checkbox" class="toggleServerCheckbox" id="enable_$cur" name="$server['name']" value="1" <!--#if int($server['enable']) != 0 then 'checked="checked"' else ""#--> /></td>
<td><label for="enable_$cur">$T('enabled')</label></td>
</tr></table>
<button type="button" class="btn btn-default showserver"><span class="glyphicon glyphicon-pencil"></span> $T('showDetails')</button>
<button type="button" class="btn btn-default clrServer"><span class="glyphicon glyphicon-remove"></span> $T('button-clrServer')</button>
</div><!-- /col2 -->
<div class="col1" style="display:none;">
<input type="hidden" name="enable" id="enable$cur" value="$int($server['enable'])" />
<fieldset>
<div class="field-pair">
<label class="config" for="host$cur">$T('srv-host')</label>
<input type="text" name="host" id="host$cur" value="$server['host']" />
</div>
<div class="field-pair">
<label class="config" for="port$cur">$T('srv-port')</label>
<input type="number" name="port" id="port$cur" value="$server['port']" size="8" />
</div>
<div class="field-pair">
<label class="config" for="ssl$cur">$T('srv-ssl')</label>
<input type="checkbox" name="ssl" id="ssl$cur" value="1" <!--#if int($server['ssl']) != 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-ssl')</span>
</div>
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
<div class="field-pair">
<label class="config" for="${pid}_${cur}0">$T('srv-username')</label>
<input type="text" name="${pid}_${cur}0" id="${pid}_${cur}0" value="$server['username']" data-hide="username" />
</div>
<div class="field-pair">
<label class="config" for="${pid}_${cur}1">$T('srv-password')</label>
<input type="text" name="${pid}_${cur}1" id="${pid}_${cur}1" value="$server['password']" data-hide="password" />
</div>
<div class="field-pair">
<label class="config" for="connections$cur">$T('srv-connections')</label>
<input type="number" name="connections" id="connections$cur" value="$server['connections']" min="0" max="100" />
</div>
<div class="field-pair">
<label class="config" for="priority$cur">$T('srv-priority')</label>
<input type="number" name="priority" id="priority$cur" value="$server['priority']" min="0" max="100" /> <i>$T('explain-svrprio')</i>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="retention$cur">$T('srv-retention')</label>
<input type="number" name="retention" id="retention$cur" value="$server['retention']" min="0" /> <i>$T('days')</i>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="timeout$cur">$T('srv-timeout')</label>
<input type="number" name="timeout" id="timeout$cur" value="$server['timeout']" min="30" /> <i>$T('seconds')</i>
</div>
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#--> advanced-settings">
<label class="config" for="ssl_verify$cur">$T('opt-ssl_verify')</label>
<select name="ssl_verify" id="ssl_verify$cur" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
<option value="2" <!--#if $server['ssl_verify'] == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
<option value="1" <!--#if $server['ssl_verify'] == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
<option value="0" <!--#if $server['ssl_verify'] == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
</select>
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="optional$cur">$T('srv-optional')</label>
<input type="checkbox" name="optional" id="optional$cur" value="1" <!--#if int($server['optional']) != 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-optional')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="send_group$cur">$T('srv-send_group')</label>
<input type="checkbox" name="send_group" id="send_group$cur" value="1" <!--#if int($server['send_group']) != 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('srv-explain-send_group')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="categories$cur">$T('srv-categories')</label>
<select name="categories" id="categories$cur" multiple>
<!--#for $cat in $cats#-->
<option value="$cat" <!--#if $cat in $server['categories'] then 'selected' else ""#-->>
<!--#if $cat == "Default" then $T('Default') else $cat#-->
</option>
<!--#end for#-->
</select>
<span class="desc">$T('srv-explain-categories')</span>
<div class="alert alert-info alert-no-category">
<span class="glyphicon glyphicon-info-sign"></span>
$T('srv-explain-no-categories')
</div>
<div class="field-pair">
<label class="config" for="host">$T('srv-host')</label>
<input type="text" name="host" id="host" required />
</div>
<div class="field-pair">
<label class="config" for="port">$T('srv-port')</label>
<input type="number" name="port" id="port" size="8" value="119" min="0" />
</div>
<div class="field-pair">
<label class="config" for="ssl">$T('srv-ssl')</label>
<input type="checkbox" name="ssl" id="ssl" value="1" />
<span class="desc">$T('explain-ssl')</span>
</div>
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
<div class="field-pair">
<label class="config" for="${pid}_00">$T('srv-username')</label>
<input type="text" name="${pid}_00" id="${pid}_00" data-hide="username" />
</div>
<div class="field-pair">
<label class="config" for="${pid}_01">$T('srv-password')</label>
<input type="text" name="${pid}_01" id="${pid}_01" data-hide="password" />
</div>
<div class="field-pair">
<label class="config" for="connections">$T('srv-connections')</label>
<input type="number" name="connections" id="connections" min="1" max="100" value="8" required />
</div>
<div class="field-pair">
<label class="config" for="priority">$T('srv-priority')</label>
<input type="number" name="priority" id="priority" min="0" max="99" /> <i>$T('explain-svrprio')</i>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="retention">$T('srv-retention')</label>
<input type="number" name="retention" id="retention" min="0" /> <i>$T('days')</i>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="timeout">$T('srv-timeout')</label>
<input type="number" name="timeout" id="timeout" min="20" max="240" /> <i>$T('seconds')</i>
</div>
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#--> advanced-settings">
<label class="config" for="ssl_verify">$T('opt-ssl_verify')</label>
<select name="ssl_verify" id="ssl_verify" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
<option value="2" selected>$T('ssl_verify-strict')</option>
<option value="1">$T('ssl_verify-normal')</option>
<option value="0">$T('ssl_verify-disabled')</option>
</select>
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="send_group">$T('srv-send_group')</label>
<input type="checkbox" name="send_group" id="send_group" value="1" />
<span class="desc">$T('srv-explain-send_group')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="optional">$T('srv-optional')</label>
<input type="checkbox" name="optional" id="optional" value="1" />
<span class="desc">$T('explain-optional')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="displayname">$T('srv-displayname')</label>
<input type="text" name="displayname" id="displayname" />
</div>
<div class="field-pair advanced-settings">
<label class="config" for="notes">$T('srv-notes')</label>
<textarea name="notes" id="notes" rows="3" cols="50"></textarea>
</div>
<div class="field-pair no-field-pair-bg">
<button class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> $T('button-addServer')</button>
<button class="btn btn-default testServer" type="button"><span class="glyphicon glyphicon-sort"></span> $T('button-testServer')</button>
</div>
<div class="field-pair result-box">
<div class="alert"></div>
</div>
</fieldset>
</form>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="displayname$cur">$T('srv-displayname')</label>
<input type="text" name="displayname" id="displayname$cur" value="$server['displayname']" />
</div>
<div class="field-pair advanced-settings">
<label class="config" for="notes$cur">$T('srv-notes')</label>
<textarea name="notes" id="notes$cur" rows="3" cols="50">$server['notes']</textarea>
</div>
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default advancedButton"><span class="glyphicon glyphicon-cog"></span> $T('button-advanced')</button>
<button class="btn btn-default testServer" type="button"><span class="glyphicon glyphicon-sort"></span> $T('button-testServer')</button>
<button class="btn btn-default delServer"><span class="glyphicon glyphicon-trash"></span> $T('button-delServer')</button>
</div>
<div class="field-pair result-box">
<div class="alert"></div>
</div>
</fieldset>
</div><!-- /col1 -->
<div class="col2" style="display:block;">
<!--#if 'amounts' in $server#-->
<b>$T('srv-bandwidth'):</b><br/>
$T('total'): $(server['amounts'][0])B<br/>
$T('today'): $(server['amounts'][3])B<br/>
$T('thisWeek'): $(server['amounts'][2])B<br/>
$T('thisMonth'): $(server['amounts'][1])B
<!--#end if#-->
</div>
</div>
</div><!-- /section -->
</form>
<!--#end for#-->
<!--#set $prio_colors = ["#59cc33", "#3366cc","#7f33cc", "#cc33a6", "#cc3333"] #-->
<!--#set $cur_prio_color = -1 #-->
<!--#set $last_prio = -1 #-->
<!--#for $cur, $server in enumerate($servers) #-->
<form action="saveServer" method="post" class="fullform" autocomplete="off">
<input type="hidden" name="session" value="$session" />
<input type="hidden" name="server" value="$server['name']" />
</div><!-- /colmask -->
<div class="section <!--#if int($server['enable']) == 0 then 'server-disabled' else ""#-->">
<div class="col2 <!--#if int($server['enable']) == 0 then 'server-disabled' else ""#-->">
<h3>$server['displayname'] <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
<!--#if int($server['enable']) != 0 #-->
<!--#if $last_prio != $server['priority'] and $cur_prio_color+1 < len($prio_colors) #-->
<!--#set $cur_prio_color = $cur_prio_color+1 #-->
<!--#set $last_prio = $server['priority'] #-->
<!--#end if#-->
<span class="label label-primary" style="background-color: $prio_colors[$cur_prio_color]">$server['priority']</span>
<span class="label label-primary" style="background-color: $prio_colors[$cur_prio_color]">$T('srv-priority'):</span>
<!--#end if#-->
<table>
<tr>
<td><input type="checkbox" class="toggleServerCheckbox" id="enable_$cur" name="$server['name']" value="1" <!--#if int($server['enable']) != 0 then 'checked="checked"' else ""#--> /></td>
<td><label for="enable_$cur">$T('enabled')</label></td>
</tr>
</table>
<button type="button" class="btn btn-default showserver"><span class="glyphicon glyphicon-pencil"></span> $T('showDetails')</button>
<button type="button" class="btn btn-default clrServer"><span class="glyphicon glyphicon-remove"></span> $T('button-clrServer')</button>
</div>
<div class="col1" style="display:none;">
<input type="hidden" name="enable" id="enable$cur" value="$int($server['enable'])" />
<fieldset>
<div class="field-pair">
<label class="config" for="host$cur">$T('srv-host')</label>
<input type="text" name="host" id="host$cur" value="$server['host']" required />
</div>
<div class="field-pair">
<label class="config" for="port$cur">$T('srv-port')</label>
<input type="number" name="port" id="port$cur" value="$server['port']" size="8" min="0" required />
</div>
<div class="field-pair">
<label class="config" for="ssl$cur">$T('srv-ssl')</label>
<input type="checkbox" name="ssl" id="ssl$cur" value="1" <!--#if int($server['ssl']) != 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-ssl')</span>
</div>
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
<div class="field-pair">
<label class="config" for="${pid}_${cur}0">$T('srv-username')</label>
<input type="text" name="${pid}_${cur}0" id="${pid}_${cur}0" value="$server['username']" data-hide="username" />
</div>
<div class="field-pair">
<label class="config" for="${pid}_${cur}1">$T('srv-password')</label>
<input type="text" name="${pid}_${cur}1" id="${pid}_${cur}1" value="$server['password']" data-hide="password" />
</div>
<div class="field-pair">
<label class="config" for="connections$cur">$T('srv-connections')</label>
<input type="number" name="connections" id="connections$cur" value="$server['connections']" min="1" max="100" required />
</div>
<div class="field-pair">
<label class="config" for="priority$cur">$T('srv-priority')</label>
<input type="number" name="priority" id="priority$cur" value="$server['priority']" min="0" max="99" required /> <i>$T('explain-svrprio')</i>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="retention$cur">$T('srv-retention')</label>
<input type="number" name="retention" id="retention$cur" value="$server['retention']" min="0" required /> <i>$T('days')</i>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="timeout$cur">$T('srv-timeout')</label>
<input type="number" name="timeout" id="timeout$cur" value="$server['timeout']" min="20" max="240" required /> <i>$T('seconds')</i>
</div>
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#--> advanced-settings">
<label class="config" for="ssl_verify$cur">$T('opt-ssl_verify')</label>
<select name="ssl_verify" id="ssl_verify$cur" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
<option value="2" <!--#if $server['ssl_verify'] == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
<option value="1" <!--#if $server['ssl_verify'] == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
<option value="0" <!--#if $server['ssl_verify'] == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
</select>
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="optional$cur">$T('srv-optional')</label>
<input type="checkbox" name="optional" id="optional$cur" value="1" <!--#if int($server['optional']) != 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-optional')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="send_group$cur">$T('srv-send_group')</label>
<input type="checkbox" name="send_group" id="send_group$cur" value="1" <!--#if int($server['send_group']) != 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('srv-explain-send_group')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="displayname$cur">$T('srv-displayname')</label>
<input type="text" name="displayname" id="displayname$cur" value="$server['displayname']" />
</div>
<div class="field-pair advanced-settings">
<label class="config" for="notes$cur">$T('srv-notes')</label>
<textarea name="notes" id="notes$cur" rows="3" cols="50">$server['notes']</textarea>
</div>
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default testServer" type="button"><span class="glyphicon glyphicon-sort"></span> $T('button-testServer')</button>
<button class="btn btn-default delServer"><span class="glyphicon glyphicon-trash"></span> $T('button-delServer')</button>
</div>
<div class="field-pair result-box">
<div class="alert"></div>
</div>
</fieldset>
</div>
<div class="col1" style="display:block;">
<!--#if 'amounts' in $server#-->
<div class="server-amounts-text">
<b>$T('srv-bandwidth'):</b><br/>
$T('total'): $(server['amounts'][0])B<br/>
$T('today'): $(server['amounts'][3])B<br/>
$T('thisWeek'): $(server['amounts'][2])B<br/>
$T('thisMonth'): $(server['amounts'][1])B<br/>
<span id="server-data-label-${cur}"></span>: <span id="server-data-value-${cur}"></span>
</div>
<div class="server-chart" data-serverid="${cur}"s>
<div id="server-chart-${cur}" class="ct-chart"></div>
</div>
<script type="text/javascript">
// Server data
serverData[${cur}] = <!--#echo json.dumps($server['amounts'][4])#-->
</script>
<!--#end if#-->
</div>
</div>
</form>
<!--#end for#-->
</div>
<script type="text/javascript" src="${root}staticcfg/js/chartist.min.js"></script>
<script type="text/javascript" src="${root}staticcfg/js/filesize.min.js"></script>
<script type="text/javascript">
// Standardize chart options
var chartOptions = {
fullWidth: true,
showArea: true,
axisX: {
labelOffset: {
x: -5
},
showGrid: false
},
axisY: {
labelOffset: {
y: 7
},
scaleMinSpace: 30
},
chartPadding: {
top: 9,
bottom: 0,
left: 30,
right: 20
}
}
function showCharts() {
// This month
var theMonth = \$('#chart-selector').val()
var thisDay = new Date()
// What month are we doing?
var inputDate = new Date(theMonth+'-01')
var baseDate = new Date(inputDate.getUTCFullYear(), inputDate.getUTCMonth(), 1)
var maxDaysInMonth = new Date(baseDate.getFullYear(), baseDate.getMonth()+1, 0).getDate()
// Set the new maximum
chartOptions.axisY.high = \$('#chart-selector :selected').data('max');
chartOptions.axisY.low = 0
// For each chart
\$('.server-chart').each(function(i, elemn) {
var server_id = \$(elemn).data('serverid')
// Fill the data array
var data = {
labels: [],
series: [[]]
};
var totalThisMonth = 0
for(var i = 1; i < maxDaysInMonth+1; i++) {
// Add X-label
if(i % 3 == 1) {
data['labels'].push(i)
} else {
data['labels'].push(NaN)
}
// Get formatted date
baseDate.setDate(i)
var dateCheck = toFormattedDate(baseDate)
// Add data if we have it
if(dateCheck in serverData[server_id]) {
data['series'][0].push(serverData[server_id][dateCheck])
totalThisMonth += serverData[server_id][dateCheck]
} else if(thisDay.getYear() == baseDate.getYear() && thisDay.getMonth() == baseDate.getMonth() && thisDay.getDate() < i) {
data['series'][0].push(NaN)
} else {
data['series'][0].push(0)
}
}
// Update the text value
\$('#server-data-label-' + server_id).text(\$('#chart-selector :selected').text())
\$('#server-data-value-' + server_id).text(filesize(totalThisMonth, {round: 1}))
// Show the chart
chart = new Chartist.Line('#server-chart-'+server_id, data, chartOptions);
chart.on('created', function(context) {
// Make sure to add this as the first child so it's at the bottom
context.svg.elem('rect', {
x: context.chartRect.x1,
y: context.chartRect.y2-1,
width: context.chartRect.width(),
height: context.chartRect.height()+2,
fill: 'none',
stroke: '#B9B9B9',
'stroke-width': '1px'
}, '', context.svg, true)
\$('#server-chart-'+server_id+' .ct-label.ct-vertical').each(function(index, elmn) {
elmn.innerHTML = filesize(elmn.innerHTML, {round: 1}).replace(' ','')
})
});
\$(document).ready(function(){
// Exception when change of priority, reload
\$('input[name="priority"], input[name="displayname"]').on('change', function() {
\$('.fullform').submit(function() {
// Skip the fancy stuff, just submit
this.submit()
})
}
// Need to mitigate timezone effects!
function toFormattedDate(date) {
var local = new Date(date);
local.setMinutes(date.getMinutes() - date.getTimezoneOffset());
return local.toJSON().slice(0, 10);
}
})
/**
When finished loading
Message on no Default category selected
**/
\$(document).ready(function(){
// Exception when change of priority, reload
\$('input[name="priority"], input[name="displayname"]').on('change', function() {
\$('.fullform').submit(function() {
// Skip the fancy stuff, just submit
this.submit()
})
})
/**
Update charts when changed
**/
\$('#chart-selector').on('change', function(elemn) {
showCharts()
// Lets us leave (needs to be called after the change event)
setTimeout(function() {
formWasSubmitted = true;
formHasChanged = false;
}, 100)
})
// And on page-load
showCharts()
/**
Click events
**/
\$('.showserver').click(function () {
if(\$(this).parent().hasClass('server-disabled')) {
\$(this).parent().parent().toggleClass('server-disabled')
}
\$(this).parent().next().toggle();
\$(this).parent().next().next().toggle();
if (\$(this).text().indexOf("$T('showDetails')") > 0) {
\$(this).html(\$(this).html().replace("$T('showDetails')", "$T('hideDetails')"));
} else {
\$(this).html(\$(this).html().replace("$T('hideDetails')", "$T('showDetails')"));
}
// Add coloring
addRowColor()
});
\$('#addServerButton').click(function(){
\$('#addServerContent').show();
// Add coloring
addRowColor()
});
\$('[name="ssl"]').click(function() {
// Use CSS transitions to do some highlighting
var portBox = \$(this).parent().parent().find('[name="port"]')
if(this.checked) {
// Enabled SSL change port when not already a custom port
if(portBox.val() == '119') {
portBox.val('563')
portBox.addClass('port-highlight')
}
} else {
// Remove SSL port
if(portBox.val() == '563') {
portBox.val('119')
portBox.addClass('port-highlight')
function checkServerCats() {
// Now we check all of them
var hasDefault = false;
// Only check the active servers, not the add-server one
\$('.section:not(#addServerContent) select[name="categories"]').each(function() {
// See if this server is enabled
if(!\$(this).parents('.section').find('.col2').hasClass('server-disabled') ) {
// Is there Default?
if(\$(this).val() && \$(this).val().indexOf('Default') > -1) {
// Hide
\$('.alert-no-category').hide()
hasDefault = true
// All good!
return true
}
}
setTimeout(function() { portBox.removeClass('port-highlight') }, 2000)
})
// We found nothing.. Let's show a warning
if(!hasDefault) \$('.alert-no-category').show()
}
\$('.testServer').click(function(event){
removeObfuscation()
var theButton = \$(this)
var resultBox = theButton.parents('.col1').find('.result-box .alert');
theButton.attr("disabled", "disabled")
\$('select[name="categories"]').on('change', checkServerCats)
checkServerCats()
/**
Click events
**/
\$('.showserver').click(function () {
if(\$(this).parent().hasClass('server-disabled')) {
\$(this).parent().parent().toggleClass('server-disabled')
}
\$(this).parent().next().toggle();
\$(this).parent().next().next().toggle();
if (\$(this).attr("value") == "$T('showDetails')") {
\$(this).attr("value", "$T('hideDetails')");
} else {
\$(this).attr("value", "$T('showDetails')");
}
});
\$('#addServerButton').click(function(){
\$('#addServer').hide();
\$('#addServerContent').show();
});
\$('[name="ssl"]').click(function() {
// Use CSS transitions to do some highlighting
var portBox = \$(this).parent().parent().find('[name="port"]')
if(this.checked) {
// Enabled SSL change port when not already a custom port
if(portBox.val() == '119') {
portBox.val('563')
portBox.addClass('port-highlight')
}
} else {
// Remove SSL port
if(portBox.val() == '563') {
portBox.val('119')
portBox.addClass('port-highlight')
}
}
setTimeout(function() { portBox.removeClass('port-highlight') }, 2000)
})
\$('.testServer').click(function(event){
removeObfuscation()
var theButton = \$(this)
var resultBox = theButton.parents('.col1').find('.result-box .alert');
theButton.attr("disabled", "disabled")
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')
\$.ajax({
type: "POST",
url: "../../tapi",
data: "mode=config&output=json&name=test_server&" + \$(this).parents('form:first').serialize()
}).then(function(data) {
// Let's replace the link
msg = data.value.message.replace('https://sabnzbd.org/certificate-errors', '<a href="https://sabnzbd.org/certificate-errors" class="alert-link" target="_blank">https://sabnzbd.org/certificate-errors</a>')
// Fill the box and enable the button
resultBox.removeClass('alert-success alert-danger').show()
resultBox.html(msg)
theButton.removeAttr("disabled")
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')
\$.ajax({
type: "POST",
url: "../../tapi",
data: "mode=config&output=json&name=test_server&" + \$(this).parents('form:first').serialize()
}).then(function(data) {
// Let's replace the link
msg = data.value.message.replace('https://sabnzbd.org/certificate-errors', '<a href="https://sabnzbd.org/certificate-errors" class="alert-link" target="_blank">https://sabnzbd.org/certificate-errors</a>')
msg = msg.replace('-', '<br>')
// Fill the box and enable the button
resultBox.removeClass('alert-success alert-danger').show()
resultBox.html(msg)
theButton.removeAttr("disabled")
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')
// Succes or not?
if(data.value.result) {
resultBox.addClass('alert-success')
resultBox.prepend('<span class="glyphicon glyphicon-ok-sign"></span> ')
} else {
resultBox.addClass('alert-danger')
resultBox.prepend('<span class="glyphicon glyphicon-exclamation-sign"></span> ')
}
});
});
\$('.delServer').click(function(){
if( confirm("$T('Plush-confirm')") ) {
\$(this).parents('form:first').attr('action','delServer').submit();
// Let us leave!
formWasSubmitted = true;
formHasChanged = false;
setTimeout(function() { location.reload(); }, 500)
// Succes or not?
if(data.value.result) {
resultBox.addClass('alert-success')
resultBox.prepend('<span class="glyphicon glyphicon-ok-sign"></span> ')
} else {
resultBox.addClass('alert-danger')
resultBox.prepend('<span class="glyphicon glyphicon-exclamation-sign"></span> ')
}
return false;
});
\$('.clrServer').click(function(){
if( confirm("$T('Plush-confirm')") ) {
\$(this).parents('form:first').attr('action','clrServer').submit();
// Let us leave!
formWasSubmitted = true;
formHasChanged = false;
setTimeout(function() { location.reload(); }, 500)
}
return false;
});
\$('.toggleServerCheckbox').click(function(){
var whichServer = \$(this).attr("name");
\$.ajax({
type: "POST",
url: "toggleServer",
data: {server: whichServer, session: "$session" }
}).done(function() {
// Let us leave!
formWasSubmitted = true;
formHasChanged = false;
setTimeout(function() { location.reload(); }, 100)
});
});
});
\$('.delServer').click(function(){
if( confirm("$T('Plush-confirm')") ) {
\$(this).parents('form:first').attr('action','delServer').submit();
// Let us leave!
formWasSubmitted = true;
formHasChanged = false;
setTimeout(function() { location.reload(); }, 500)
}
return false;
});
\$('.clrServer').click(function(){
if( confirm("$T('Plush-confirm')") ) {
\$(this).parents('form:first').attr('action','clrServer').submit();
// Let us leave!
formWasSubmitted = true;
formHasChanged = false;
setTimeout(function() { location.reload(); }, 500)
}
return false;
});
\$('.toggleServerCheckbox').click(function(){
var whichServer = \$(this).attr("name");
\$.ajax({
type: "POST",
url: "toggleServer",
data: {server: whichServer, session: "$session" }
}).done(function() {
// Let us leave!
formWasSubmitted = true;
formHasChanged = false;
setTimeout(function() { location.reload(); }, 100)
});
});
});
</script>
<!--#include $webdir + "/_inc_footer_uc.tmpl"#-->

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Sorting"#-->
<!--#set global $help_uri="configuration/2.3/sorting"#-->
<!--#set global $help_uri="configuration/2.1/sorting"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
@@ -40,8 +40,6 @@
<br/>
<input type="button" class="btn btn-default" onclick="tvSet('%sn/%sx%0e - %en/%sn - %sx%0e - %en.%ext')" value="$T('button-Ep1x05')" />
<input type="button" class="btn btn-default" onclick="tvSet('%sn/S%0sE%0e - %en/%sn - S%0sE%0e - %en.%ext')" value="$T('button-EpS01E05')" />
<br>
<input type="button" class="btn btn-default" onclick="tvSet('%dn.%ext')" value="$T('button-FileLikeFolder')" />
</div>
</div>
<div id="previewtv" class="example">
@@ -53,7 +51,7 @@
<label class="config">$T('sortResult')</label> <span class="desc path" id="previewtv-result">&nbsp;</span>
</div>
</div>
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<label class="config">$T('sort-legenda')</label>
<button type="button" class="btn btn-default patternKey" onclick="jQuery(this).hide(); jQuery('#Key1').show();"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span> $T('sort-legenda')</button>
<table id="Key1" class="Key">
@@ -141,9 +139,9 @@
<td>$T("sort-File")</td>
</tr>
<tr>
<td class="align-right"><b>$T('orgJobname'):</b></td>
<td class="align-right"><b>$T('orgDirname'):</b></td>
<td>%dn</td>
<td></td>
<td>$T("sort-Folder")</td>
</tr>
<tr>
<td class="align-right"><b>$T('lowercase'):</b></td>
@@ -200,8 +198,7 @@
<div class="presets float-left">
<input type="button" class="btn btn-default" onclick="movieSet('%title (%y)/%title (%y).%ext',' CD%1');movieExtraFolder(false)" value="$T('button-inFolders')" />
<input type="button" class="btn btn-default" onclick="movieSet('%title (%y).%ext',' CD%1');movieExtraFolder(true)" value="$T('button-noFolders')" />
<input type="button" class="btn btn-default" onclick="movieSet('%0decade/%title (%y).%ext',' CD%1');movieExtraFolder(true)" value="$T('decade')" />
<input type="button" class="btn btn-default" onclick="movieSet('%dn.%ext')" value="$T('button-FileLikeFolder')" />
<input type="button" class="btn btn-default" onclick="movieSet('%0decade/%title (%y).%ext',' CD%1');movieExtraFolder(true)" value="Decades 1" />
</div>
</div>
<div id="previewmovie" class="example">
@@ -213,7 +210,7 @@
<label class="config">$T('sortResult')</label> <span class="desc path" id="previewmovie-result">&nbsp;</span>
</div>
</div>
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<label class="config">$T('sort-legenda')</label>
<button type="button" class="btn btn-default patternKey" onclick="jQuery(this).hide(); jQuery('#Key2').show();"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span> $T('sort-legenda')</button>
<table id="Key2" class="Key">
@@ -266,9 +263,9 @@
<td>$T('sort-File')</td>
</tr>
<tr>
<td class="align-right"><b>$T('orgJobname'):</b></td>
<td class="align-right"><b>$T('orgDirname'):</b></td>
<td>%dn</td>
<td></td>
<td>$T("sort-Folder")</td>
</tr>
<tr>
<td class="align-right"><b>$T('lowercase'):</b></td>
@@ -331,9 +328,7 @@
<div class="presets float-left">
<input type="button" class="btn btn-default" onclick="dateSet('%t/%t - %y-%0m-%0d - %desc.%ext')" value="$T('button-ShowNameF')" />
<input type="button" class="btn btn-default" onclick="dateSet('%y-%0m/%t - %y-%0m-%0d - %desc.%ext')" value="$T('button-YMF')" />
<br>
<input type="button" class="btn btn-default" onclick="dateSet('%y-%0m-%0d/%t - %y-%0m-%0d - %desc.%ext')" value="$T('button-DailyF')" />
<input type="button" class="btn btn-default" onclick="dateSet('%dn.%ext')" value="$T('button-FileLikeFolder')" />
</div>
</div>
<div id="previewdate" class="example">
@@ -345,7 +340,7 @@
<label class="config">$T('sortResult')</label> <span class="desc path" id="previewdate-result">&nbsp;</span>
</div>
</div>
<div class="field-pair no-field-pair-bg">
<div class="field-pair">
<label class="config">$T('sort-legenda')</label>
<button type="button" class="btn btn-default patternKey" onclick="jQuery(this).hide(); jQuery('#Key3').show();"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span> $T('sort-legenda')</button>
<table id="Key3" class="Key">
@@ -412,11 +407,6 @@
<td>%fn</td>
<td>$T('sort-File')</td>
</tr>
<tr>
<td class="align-right"><b>$T('orgJobname'):</b></td>
<td>%dn</td>
<td></td>
</tr>
<tr>
<td class="align-right"><b>$T('lowercase'):</b></td>
<td>{$T('TEXT')}</td>
@@ -497,7 +487,7 @@
\$.ajax({
type: "GET",
url: "../../tapi",
data: {mode:'eval_sort', value: 'movie', name: \$('#moviesamplename').val(), title: \$moviesortstring, movieextra: \$('#movieextra').val(), apikey: '$session', output: 'json' },
data: {mode:'eval_sort', value: 'generic', name: \$('#moviesamplename').val(), title: \$moviesortstring, movieextra: \$('#movieextra').val(), apikey: '$session', output: 'json' },
success: function(data){
\$('#previewmovie-result').removeClass("loading failure").html(data.result);
},

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Special"#-->
<!--#set global $help_uri="configuration/2.3/special"#-->
<!--#set global $help_uri="configuration/2.1/special"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">

View File

@@ -1,16 +1,11 @@
<!--#set global $pane="Switches"#-->
<!--#set global $help_uri="configuration/2.3/switches"#-->
<!--#set global $help_uri="configuration/2.1/switches"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
<div class="padding alt section">
<label for="advanced-settings-button" class="form-control advanced-button ">
<input type="checkbox" id="advanced-settings-button" name="advanced-settings-button"> $T('button-advanced')
</label>
</div>
<form action="saveSwitches" method="post" name="fullform" class="fullform" autocomplete="off">
<input type="hidden" id="session" name="session" value="$session" />
<div class="section advanced-settings">
<div class="section">
<div class="col2">
<h3>$T('swtag-server') <a href="$helpuri$help_uri#toc1" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
</div><!-- /col2 -->
@@ -36,6 +31,12 @@
<input type="number" name="max_art_tries" id="max_art_tries" value="$max_art_tries" min="2" max="2000" />
<span class="desc">$T('explain-max_art_tries')</span>
</div>
<div class="field-pair">
<label class="config" for="max_art_opt">$T('opt-max_art_opt')</label>
<input type="checkbox" name="max_art_opt" id="max_art_opt" value="1" <!--#if int($max_art_opt) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-max_art_opt')</span>
</div>
<div class="field-pair">
<label class="config" for="auto_disconnect">$T('opt-auto_disconnect')</label>
<input type="checkbox" name="auto_disconnect" id="auto_disconnect" value="1" <!--#if int($auto_disconnect) > 0 then 'checked="checked"' else ""#--> />
@@ -72,12 +73,12 @@
<input type="number" name="propagation_delay" id="propagation_delay" value="$propagation_delay" /> <i>$T('minutes')</i>
<span class="desc">$T('explain-propagation_delay')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="top_only">$T('opt-top_only')</label>
<input type="checkbox" name="top_only" id="top_only" value="1" <!--#if int($top_only) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-top_only')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="pre_check">$T('opt-pre_check')</label>
<input type="checkbox" name="pre_check" id="pre_check" value="1" <!--#if int($pre_check) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-pre_check')</span>
@@ -91,7 +92,6 @@
<label class="config" for="no_dupes">$T('opt-no_dupes')</label>
<select name="no_dupes" id="no_dupes">
<option value="0" <!--#if int($no_dupes) == 0 then 'selected="selected"' else ""#--> >$T('nodupes-off')</option>
<option value="4" <!--#if int($no_dupes) == 4 then 'selected="selected"' else ""#--> >$T('nodupes-tag')</option>
<option value="2" <!--#if int($no_dupes) == 2 then 'selected="selected"' else ""#--> >$T('nodupes-pause')</option>
<option value="3" <!--#if int($no_dupes) == 3 then 'selected="selected"' else ""#--> >$T('nodupes-fail')</option>
<option value="1" <!--#if int($no_dupes) == 1 then 'selected="selected"' else ""#--> >$T('nodupes-ignore')</option>
@@ -102,18 +102,12 @@
<label class="config" for="no_series_dupes">$T('opt-no_series_dupes')</label>
<select name="no_series_dupes" id="no_series_dupes">
<option value="0" <!--#if int($no_series_dupes) == 0 then 'selected="selected"' else ""#--> >$T('nodupes-off')</option>
<option value="4" <!--#if int($no_series_dupes) == 4 then 'selected="selected"' else ""#--> >$T('nodupes-tag')</option>
<option value="2" <!--#if int($no_series_dupes) == 2 then 'selected="selected"' else ""#--> >$T('nodupes-pause')</option>
<option value="3" <!--#if int($no_series_dupes) == 3 then 'selected="selected"' else ""#--> >$T('nodupes-fail')</option>
<option value="1" <!--#if int($no_series_dupes) == 1 then 'selected="selected"' else ""#--> >$T('nodupes-ignore')</option>
</select>
<span class="desc">$T('explain-no_series_dupes')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="series_propercheck">$T('opt-series_propercheck')</label>
<input type="checkbox" name="series_propercheck" id="series_propercheck" value="1" <!--#if int($series_propercheck) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-series_propercheck')</span>
</div>
<div class="field-pair">
<label class="config" for="pause_on_pwrar">$T('opt-pause_on_pwrar')</label>
<select name="pause_on_pwrar" id="pause_on_pwrar">
@@ -123,11 +117,6 @@
</select>
<span class="desc">$T('explain-pause_on_pwrar')</span>
</div>
<div class="field-pair">
<label class="config" for="unwanted_extensions">$T('opt-unwanted_extensions')</label>
<input type="text" name="unwanted_extensions" id="unwanted_extensions" value="$unwanted_extensions"/>
<span class="desc">$T('explain-unwanted_extensions')</span>
</div>
<div class="field-pair">
<label class="config" for="action_on_unwanted_extensions">$T('opt-action_on_unwanted_extensions')</label>
<select name="action_on_unwanted_extensions" id="action_on_unwanted_extensions">
@@ -137,16 +126,16 @@
</select>
<span class="desc">$T('explain-action_on_unwanted_extensions')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="unwanted_extensions">$T('opt-unwanted_extensions')</label>
<input type="text" name="unwanted_extensions" id="unwanted_extensions" value="$unwanted_extensions"/>
<span class="desc">$T('explain-unwanted_extensions')</span>
</div>
<div class="field-pair">
<label class="config" for="auto_sort">$T('opt-auto_sort')</label>
<input type="checkbox" name="auto_sort" id="auto_sort" value="1" <!--#if int($auto_sort) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-auto_sort')</span>
</div>
<div class="field-pair">
<label class="config" for="direct_unpack">$T('opt-direct_unpack')</label>
<input type="checkbox" name="direct_unpack" id="direct_unpack" value="1" <!--#if int($direct_unpack) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-direct_unpack').replace('. ', '.<br/>')</span>
</div>
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default restoreDefaults"><span class="glyphicon glyphicon-asterisk"></span> $T('button-restoreDefaults')</button>
@@ -165,53 +154,60 @@
<input type="checkbox" name="pause_on_post_processing" id="pause_on_post_processing" value="1" <!--#if int($pause_on_post_processing) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-pause_on_post_processing')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="enable_all_par">$T('opt-enable_all_par')</label>
<input type="checkbox" name="enable_all_par" id="enable_all_par" value="1" <!--#if int($enable_all_par) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-enable_all_par').replace('. ', '.<br/>')</span>
</div>
<div class="field-pair advanced-settings">
<!--#if $nt#-->
<div class="field-pair">
<label class="config" for="multipar">$T('opt-par2_multipar')</label>
<input type="checkbox" name="multipar" id="multipar" value="1" <!--#if int($multipar) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-par2_multipar')</span>
</div>
<!--#end if#-->
<div class="field-pair">
<label class="config" for="par_option">$T('opt-par_option')</label>
<input type="text" name="par_option" id="par_option" value="$par_option" />
<span class="desc">$T('explain-par_option')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="sfv_check">$T('opt-sfv_check')</label>
<input type="checkbox" name="sfv_check" id="sfv_check" value="1" <!--#if int($sfv_check) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-sfv_check')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="safe_postproc">$T('opt-safe_postproc')</label>
<input type="checkbox" name="safe_postproc" id="safe_postproc" value="1" <!--#if int($safe_postproc) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-safe_postproc')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="enable_recursive">$T('opt-enable_recursive')</label>
<input type="checkbox" name="enable_recursive" id="enable_recursive" value="1" <!--#if int($enable_recursive) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-enable_recursive')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="flat_unpack">$T('opt-flat_unpack')</label>
<input type="checkbox" name="flat_unpack" id="flat_unpack" value="1" <!--#if int($flat_unpack) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-flat_unpack')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="script_can_fail">$T('opt-script_can_fail')</label>
<input type="checkbox" name="script_can_fail" id="script_can_fail" value="1" <!--#if int($script_can_fail) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-script_can_fail')</span>
</div>
<div class="field-pair advanced-settings">
<div class="field-pair">
<label class="config" for="new_nzb_on_failure">$T('opt-new_nzb_on_failure')</label>
<input type="checkbox" name="new_nzb_on_failure" id="new_nzb_on_failure" value="1" <!--#if int($new_nzb_on_failure) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-new_nzb_on_failure')</span>
</div>
<!--#if not $nt#-->
<div class="field-pair advanced-settings <!--#if not $have_nice then "disabled" else "" #-->">
<div class="field-pair <!--#if not $have_nice then "disabled" else "" #-->">
<label class="config" for="nice">$T('opt-nice')</label>
<input type="text" name="nice" id="nice" value="$nice" <!--#if not $have_nice then 'readonly="readonly" disabled="disabled"' else "" #--> />
<span class="desc">$T('explain-nice')</span>
</div>
<div class="field-pair advanced-settings <!--#if not $have_ionice then "disabled" else "" #-->">
<div class="field-pair <!--#if not $have_ionice then "disabled" else "" #-->">
<label class="config" for="ionice">$T('opt-ionice')</label>
<input type="text" name="ionice" id="ionice" value="$ionice" <!--#if not $have_ionice then 'readonly="readonly" disabled="disabled"' else "" #--> />
<span class="desc">$T('explain-ionice')</span>
@@ -222,28 +218,11 @@
<input type="checkbox" name="ignore_samples" id="ignore_samples" value="1" <!--#if int($ignore_samples) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-ignore_samples') $T('igsam-del').</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="enable_meta">$T('opt-enable_meta')</label>
<input type="checkbox" name="enable_meta" id="enable_meta" value="1" <!--#if int($enable_meta) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-enable_meta').replace('. ', '.<br/>')</span>
</div>
<div class="field-pair">
<label class="config" for="cleanup_list">$T('opt-cleanup_list')</label>
<input type="text" name="cleanup_list" id="cleanup_list" value="$cleanup_list"/>
<span class="desc">$T('explain-cleanup_list')</span>
</div>
<div class="field-pair">
<label class="config" for="history_retention_select">$T('opt-history_retention')</label>
<input type="hidden" name="history_retention" id="history_retention" value="$history_retention">
<select name="history_retention_select" id="history_retention_select">
<option value="0">$T('history_retention-all')</option>
<option value="n">$T('history_retention-number')</option>
<option value="d">$T('history_retention-days')</option>
<option value="-1">$T('history_retention-none')</option>
</select>
<input type="number" id="history_retention_number" name="history_retention_number" min="1">
<span class="desc">$T('explain-history_retention').replace('. ', '.<br/>')</span>
</div>
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default restoreDefaults"><span class="glyphicon glyphicon-asterisk"></span> $T('button-restoreDefaults')</button>
@@ -251,7 +230,7 @@
</fieldset>
</div><!-- /col1 -->
</div><!-- /section -->
<div class="section advanced-settings">
<div class="section">
<div class="col2">
<h3>$T('swtag-naming') <a href="$helpuri$help_uri#toc4" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
</div><!-- /col2 -->
@@ -260,7 +239,7 @@
<div class="field-pair">
<label class="config" for="folder_rename">$T('opt-folder_rename')</label>
<input type="checkbox" name="folder_rename" id="folder_rename" value="1" <!--#if int($folder_rename) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-folder_rename').replace('. ', '.<br/>')</span>
<span class="desc">$T('explain-folder_rename')</span>
</div>
<div class="field-pair">
<label class="config" for="replace_spaces">$T('opt-replace_spaces')</label>
@@ -272,6 +251,11 @@
<input type="checkbox" name="replace_dots" id="replace_dots" value="1" <!--#if int($replace_dots) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-replace_dots')</span>
</div>
<div class="field-pair">
<label class="config" for="replace_illegal">$T('opt-replace_illegal')</label>
<input type="checkbox" name="replace_illegal" id="replace_illegal" value="1" <!--#if int($replace_illegal) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-replace_illegal')</span>
</div>
<!--#if not $nt#-->
<div class="field-pair">
<label class="config" for="sanitize_safe">$T('opt-sanitize_safe')</label>
@@ -466,53 +450,6 @@
}
});
\$('#history_retention_select, #history_retention_number').on('change', updateHistoryRetention)
function updateHistoryRetention() {
var retention_setting = \$('#history_retention')
var retention_select = \$('#history_retention_select').val()
var retention_number = \$('#history_retention_number')
// Keep all or keep none
if(retention_select == "0" || retention_select == "-1") {
retention_number.hide()
retention_number.val('')
retention_number.attr('placeholder', '')
retention_setting.val(retention_select)
} else {
retention_number.show()
// Days or number?
if(retention_select.indexOf("d") !== -1) {
retention_number.attr('placeholder', '$T('days').capitalize()')
if(retention_number.val()) {
retention_setting.val(retention_number.val() + 'd')
} else if(parseInt(retention_setting.val()) > 0) {
retention_number.val(parseInt(retention_setting.val()))
}
} else {
retention_number.attr('placeholder', '$T('history_retention-limit')')
if(retention_number.val()) {
retention_setting.val(retention_number.val())
} else if(parseInt(retention_setting.val()) > 0) {
retention_number.val(parseInt(retention_setting.val()))
}
}
}
}
// Set the history-retention settig
var retention_setting_value = \$('#history_retention').val()
if(parseInt(retention_setting_value) > 0) {
// Days or number?
if(retention_setting_value.indexOf("d") !== -1) {
\$('#history_retention_select').val("d")
} else {
\$('#history_retention_select').val("n")
}
\$('#history_retention_number').val(parseInt(retention_setting_value))
} else {
// Keep all or keep none
\$('#history_retention_select').val(retention_setting_value)
\$('#history_retention_number').hide()
}
\$('.restoreDefaults').click(function(e) {
// Get section name
var sectionName = \$(this).parents('.section').find('.col2 h3').text().trim()

View File

@@ -1,9 +1,9 @@
<html lang="$active_lang">
<head>
<title>SABnzbd - $T('login')</title>
<title>SABnzbd</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="apple-mobile-web-app-title" content="SABnzbd" />
<link rel="apple-touch-icon" sizes="76x76" href="../staticcfg/ico/apple-touch-icon-76x76-precomposed.png" />
@@ -16,7 +16,7 @@
<link rel="stylesheet" type="text/css" href="../staticcfg/bootstrap/css/bootstrap.min.css?v=$version" />
<link rel="stylesheet" type="text/css" href="../staticcfg/css/login.css?v=$version" />
<script type="text/javascript" src="../staticcfg/js/jquery-3.2.1.min.js?v=$version"></script>
<script type="text/javascript" src="../staticcfg/js/jquery-3.1.1.min.js?v=$version"></script>
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
</head>
<html>

View File

File diff suppressed because one or more lines are too long

View File

@@ -23,32 +23,16 @@ body {
overflow: visible;
border: 1px solid #dfdede;
background-color: #FFF;
border: 1px solid rgba(0, 0, 0, 0.2);
width: 100%
}
.advanced-button {
float: right;
width: auto;
margin: 0 4px 0px 0px;
font-size: 13px;
color: inherit;
height: auto;
padding-bottom: 5px;
}
#advanced-settings-button {
float: left;
margin: 2px 7px 0px 0px;
}
.advanced-buttonSeperator {
width: 1px;
height: 34px;
background-color: #dfdede;
float: right;
margin: 0px 10px;
}
.section, #addFeed, #addFeedContent {
.section, .Servers form, #addFeed, #addFeedContent {
border-bottom: 1px solid #dfdede;
overflow: auto;
}
.section:last-child, .Servers form:last-child {
border: 0;
}
.col2 {
z-index: 10;
float: left;
@@ -153,8 +137,7 @@ input[type="checkbox"]+.desc {
font-style: italic;
padding: 0 1px;
}
.col2 p,
.col2-cats {
.col2 p {
font-size: 12px;
color: #666;
margin: 1em 0;
@@ -166,13 +149,13 @@ input[type="checkbox"]+.desc {
overflow: hidden;
min-width: 555px;
}
.field-pair:nth-child(odd),
.Key tr:nth-child(odd),
.tab-pane tr:nth-child(odd),
.even {
background-color: #F8F8F8;
}
.field-pair:last-child,
.no-field-pair-bg {
.field-pair:last-child {
background-color: transparent;
}
.alt,
@@ -635,6 +618,7 @@ h2.activeRSS {
padding: 0 0 .5em;
}
.feed-row div {
padding-right: 10px;
overflow:hidden;
white-space: nowrap;
text-overflow: ellipsis;
@@ -1009,50 +993,6 @@ input[type="checkbox"] {
opacity: 0.7;
}
.Servers .server-amounts-text {
width: 20%;
float: left;
}
.Servers .server-chart {
float: right;
width: calc(100% - 250px - 25%);
text-align: center;
position: relative;
}
.Servers .ct-series-a .ct-line {
stroke: #666666;
}
.Servers .ct-series-a .ct-point {
stroke: #666666;
stroke-width: 4px;
}
.Servers .ct-series-a .ct-area {
fill: #666666
}
.Servers .ct-label {
font-size: 1em;
color: black;
}
.Servers .chart-selector-container {
float: right;
}
.Servers .chart-selector-container .glyphicon {
font-size: 1.3em;
padding-right: 4px;
top: 5px;
}
.Servers .ct-grid.ct-vertical:first-of-type {
display: none;
}
.advanced-settings {
display: none;
}
@@ -1078,6 +1018,7 @@ input[type="checkbox"] {
display: none;
}
.alert-no-category,
.alert-translate {
display: none;
margin: 5px 0px 0px;
@@ -1093,13 +1034,6 @@ input[type="checkbox"] {
color: #666;
}
.host-warning {
color: #F0AD4E;
margin-left: 13px;
top: 5px;
font-size: 1.2em;
}
.fileBrowser .glyphicon {
margin-right: 2px;
top: 1px;
@@ -1184,7 +1118,7 @@ input[type="checkbox"] {
.navbar-nav {
/* For extra wide languages like Polish */
margin-right: -150px;
margin-right: -50px;
}
}
@@ -1236,17 +1170,6 @@ input[type="checkbox"] {
.Servers .col2 button:first-of-type {
margin-bottom: 0;
}
.Servers .server-chart,
.Servers .chart-selector-container,
.Servers .advanced-buttonSeperator {
display: none;
}
.Servers .server-amounts-text {
padding: 0px 15px 10px;
width: inherit;
}
}
@media screen and (max-width: 768px) {

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="608.000000pt" height="608.000000pt" viewBox="0 0 608.000000 608.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,608.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1295 5818 c-46 -17 -81 -45 -107 -86 l-23 -37 -5 -1160 -5 -1160
-386 -5 -386 -5 -40 -28 c-80 -56 -113 -173 -70 -257 9 -19 91 -113 182 -209
91 -96 165 -177 165 -180 -1 -3 -18 -18 -39 -32 -21 -15 -48 -46 -60 -71 -21
-43 -21 -54 -21 -514 0 -441 1 -472 19 -507 18 -36 18 -38 0 -74 -17 -33 -19
-61 -19 -236 0 -222 6 -245 74 -305 60 -53 76 -54 851 -50 706 3 710 3 751 25
26 13 45 18 51 12 5 -5 32 -17 60 -27 46 -15 77 -44 336 -317 157 -165 300
-309 319 -320 24 -14 51 -20 98 -20 47 0 74 6 98 20 19 11 162 155 319 320
200 211 295 304 321 315 20 8 46 20 56 26 15 9 26 7 55 -10 36 -21 39 -21 781
-21 l745 0 45 25 c24 14 58 45 75 68 l30 44 3 746 2 746 -25 51 c-14 29 -40
63 -60 77 l-35 25 170 179 c94 98 178 195 188 214 41 84 9 202 -71 257 l-40
28 -386 5 -386 5 -5 1162 -5 1161 -30 43 c-16 23 -48 52 -70 63 l-40 21 -1725
2 c-1202 1 -1734 -2 -1755 -9z m3215 -1488 l0 -1080 -284 0 -284 0 -53 -28
c-39 -20 -62 -41 -84 -77 l-30 -48 -3 -199 -4 -198 -736 0 c-703 0 -739 -1
-779 -19 -41 -19 -42 -19 -79 0 -36 18 -64 19 -510 19 l-472 0 -122 128 -122
127 239 5 c258 5 269 8 325 67 61 64 58 -6 58 1243 l0 1140 1470 0 1470 0 0
-1080z m32 -1562 l3 -273 408 -3 407 -2 0 -690 0 -690 -690 0 -690 0 0 965 0
965 280 0 280 0 2 -272z m558 155 c-19 -21 -75 -80 -125 -130 l-90 -93 -67 0
-68 0 0 130 0 130 192 0 192 0 -34 -37z m-3010 -573 l0 -140 -410 0 -410 0 0
-130 0 -130 408 -2 407 -3 0 -415 0 -415 -692 -3 -693 -2 0 145 0 145 410 0
410 0 0 130 0 130 -410 0 -410 0 0 415 0 415 695 0 695 0 0 -140z m1630 -550
l0 -690 -690 0 -690 0 0 420 0 420 410 0 410 0 0 130 0 130 -410 0 -410 0 0
140 0 140 690 0 690 0 0 -690z m-611 -967 c-35 -38 -66 -68 -69 -68 -3 0 -34
30 -69 68 l-63 67 132 0 132 0 -63 -67z"/>
<path d="M4547 2203 c-4 -3 -7 -186 -7 -405 l0 -398 130 0 130 0 0 405 0 405
-123 0 c-68 0 -127 -3 -130 -7z"/>
<path d="M2902 1533 l3 -128 128 -3 127 -3 0 131 0 130 -130 0 -131 0 3 -127z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +0,0 @@
/*
2017 Jason Mulligan <jason.mulligan@avoidwork.com>
@version 3.5.11
*/
"use strict";!function(i){function e(i){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=[],d=0,r=void 0,a=void 0,s=void 0,f=void 0,u=void 0,l=void 0,v=void 0,B=void 0,c=void 0,p=void 0,y=void 0,m=void 0,x=void 0,g=void 0;if(isNaN(i))throw new Error("Invalid arguments");return s=!0===e.bits,y=!0===e.unix,a=e.base||2,p=void 0!==e.round?e.round:y?1:2,m=void 0!==e.spacer?e.spacer:y?"":" ",g=e.symbols||e.suffixes||{},x=2===a?e.standard||"jedec":"jedec",c=e.output||"string",u=!0===e.fullform,l=e.fullforms instanceof Array?e.fullforms:[],r=void 0!==e.exponent?e.exponent:-1,B=Number(i),v=B<0,f=a>2?1e3:1024,v&&(B=-B),(-1===r||isNaN(r))&&(r=Math.floor(Math.log(B)/Math.log(f)))<0&&(r=0),r>8&&(r=8),0===B?(n[0]=0,n[1]=y?"":t[x][s?"bits":"bytes"][r]):(d=B/(2===a?Math.pow(2,10*r):Math.pow(1e3,r)),s&&(d*=8)>=f&&r<8&&(d/=f,r++),n[0]=Number(d.toFixed(r>0?p:0)),n[1]=10===a&&1===r?s?"kb":"kB":t[x][s?"bits":"bytes"][r],y&&(n[1]="jedec"===x?n[1].charAt(0):r>0?n[1].replace(/B$/,""):n[1],o.test(n[1])&&(n[0]=Math.floor(n[0]),n[1]=""))),v&&(n[0]=-n[0]),n[1]=g[n[1]]||n[1],"array"===c?n:"exponent"===c?r:"object"===c?{value:n[0],suffix:n[1],symbol:n[1]}:(u&&(n[1]=l[r]?l[r]:b[x][r]+(s?"bit":"byte")+(1===n[0]?"":"s")),n.join(m))}var o=/^(b|B)$/,t={iec:{bits:["b","Kib","Mib","Gib","Tib","Pib","Eib","Zib","Yib"],bytes:["B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"]},jedec:{bits:["b","Kb","Mb","Gb","Tb","Pb","Eb","Zb","Yb"],bytes:["B","KB","MB","GB","TB","PB","EB","ZB","YB"]}},b={iec:["","kibi","mebi","gibi","tebi","pebi","exbi","zebi","yobi"],jedec:["","kilo","mega","giga","tera","peta","exa","zetta","yotta"]};e.partial=function(i){return function(o){return e(o,i)}},"undefined"!=typeof exports?module.exports=e:"function"==typeof define&&define.amd?define(function(){return e}):i.filesize=e}("undefined"!=typeof window?window:global);
//# sourceMappingURL=filesize.min.js.map

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -47,9 +47,9 @@
<!-- ko if: historyStatus.has_rating -->
<div class="dropdown history-ratings">
<a href="#" class="name-icons hover-button" data-toggle="dropdown" onclick="keepOpen(this)">
<span class="glyphicon glyphicon-thumbs-up"></span> <span data-bind="text: historyStatus.rating_avg_vote_up"></span>
<span class="glyphicon glyphicon-thumbs-down"></span> <span data-bind="text: historyStatus.rating_avg_vote_down"></span>
<a href="#" class="name-ratings hover-button" data-toggle="dropdown" onclick="keepOpen(this)">
<span class="glyphicon glyphicon-facetime-video"></span> <span data-bind="text: historyStatus.rating_avg_video"></span>
<span class="glyphicon glyphicon-volume-up"></span> <span data-bind="text: historyStatus.rating_avg_audio"></span>
</a>
<ul class="dropdown-menu history-ratings-menu">
<li>
@@ -170,10 +170,6 @@
<div class="col-sm-2">$T('category')</div>
<div class="col-sm-10" data-bind="text: historyStatus.category() == '*' ? glitterTranslate.defaultText : historyStatus.category"></div>
</div>
<div class="row" data-bind="visible: historyStatus.password">
<div class="col-sm-2">$T('srv-password')</div>
<div class="col-sm-10" data-bind="text: historyStatus.password"></div>
</div>
<div class="row">
<div class="col-sm-2">$T('msg-path')</div>
<div class="col-sm-10" data-bind="text: historyStatus.storage() == '' ? historyStatus.path : historyStatus.storage"></div>
@@ -210,7 +206,6 @@
</ul>
<div class="multioperations-selector" id="history-options">
<a href="#" class="hover-button" title="$T('link-retryAll')" data-tooltip="true" data-placement="left" data-bind="click: history.retryAllFailed"><span class="glyphicon glyphicon-repeat"></span></a>
<a href="#" class="hover-button" title="$T('showAllHis') / $T('showFailedHis')" data-tooltip="true" data-placement="left" data-bind="click: history.toggleShowFailed, css: { 'history-options-show-failed': history.showFailed }"><span class="glyphicon glyphicon-exclamation-sign"></span></a>
<a href="#modal-purge-history" class="hover-button" title="$T('purgeHist')" data-toggle="modal" data-tooltip="true" data-placement="left"><span class="glyphicon glyphicon-trash"></span></a>
</div>

View File

@@ -89,7 +89,6 @@
</a>
<ul class="dropdown-menu menu-options">
<li><a href="#modal-help" data-toggle="modal"><span class="glyphicon glyphicon-question-sign"></span> $T('menu-help')</a></li>
<li><a href="https://sabnzbd.org/donate" target="_blank"><span class="glyphicon glyphicon-heart"></span> $T('menu-donate')</a></li>
<!--#if $have_logout or $have_quota or $have_rss_defined or $have_watched_dir or $pp_pause_event#--><li class="divider"></li><!--#end if#-->
<!--#if $have_logout#--><li><a href="./login/?logout=1"><span class="glyphicon glyphicon-log-out"></span> $T('logout')</a></li><!--#end if#-->
<!--#if $have_quota#--><li><a href="#" data-bind="click: doQueueAction" data-mode="reset_quota">$T('link-resetQuota')</a></li><!--#end if#-->

View File

@@ -18,7 +18,7 @@
<tr>
<td class="table-messages-label">
<span class="label" data-bind="css: 'label-' + css, text: type"></span>
<strong data-bind="text: displayDateTime(timestamp, \$parent.dateFormat(), 'X'), attr: { 'data-timestamp': timestamp }"></strong>
<strong data-bind="text: date, attr: { 'data-timestamp': timestamp }"></strong>
<span class="queue-message-text" data-bind="html: text"></span>
</td>
</tr>

View File

@@ -59,9 +59,15 @@
<div id="feedback-slider-inner">
<p><strong>If anything is not working as expected, or could be improved, let us know!</strong></p>
<p><strong>If you encounter an error, please include the log file (click on <span class="glyphicon glyphicon-wrench"></span> ) when contacting us.</strong></p>
<span class="glyphicon glyphicon-home"></span> <a href="https://forums.sabnzbd.org/viewforum.php?f=11" target="_blank">SABnzbd Forum</a><br />
<h4>General</h4>
<span class="glyphicon glyphicon-home"></span> <a href="http://forums.sabnzbd.org/" target="_blank">SABnzbd Forum</a><br />
<span class="glyphicon glyphicon-plane"></span> <a href="https://github.com/sabnzbd/sabnzbd/" target="_blank">SABnzbd on Github</a><br />
<span class="glyphicon glyphicon-globe"></span> <a href="https://translations.launchpad.net/sabnzbd" target="_blank">Translations of SABnzbd</a><br />
<span class="glyphicon glyphicon-envelope"></span> <a href="mailto:bugs@sabnzbd.org?body=Version:%20$version%20Skin:%20Glitter">Email bugs@sabnzbd.org</a>
<h4>Interface (Glitter)</h4>
<span class="glyphicon glyphicon-home"></span> <a href="http://forums.sabnzbd.org/viewtopic.php?f=5&amp;t=18880" target="_blank">Glitter at SABnzbd Forum</a><br />
<span class="glyphicon glyphicon-envelope"></span> <a href="mailto:safihre@sabnzbd.org?body=Version:%20$version">Email safihre@sabnzbd.org</a>
</div>
</div>
@@ -150,9 +156,7 @@
</div>
<div class="row options-function-box">
<div class="col-sm-6">
<a href="./status/showlog?session=$session" target="_blank" class="btn btn-default" data-tooltip="true" data-placement="top" title="$T('Glitter-logText')">
<span class="glyphicon glyphicon-file"></span> $T('link-showLog')
</a>
<a href="./status/showlog?session=$session" target="_blank" class="btn btn-default"><span class="glyphicon glyphicon-file"></span> $T('link-showLog')</a>
</div>
<div class="col-sm-6">
<div class="input-group" data-tooltip="true" data-placement="top" title="$T('logging')">
@@ -175,6 +179,7 @@
</div>
<div data-bind="foreach: statusInfo.servers">
<div class="options-server-box">
<div class="row">
<div class="col-sm-6">$T('swtag-server')</div>
<div class="col-sm-6">
@@ -520,14 +525,10 @@
<div class="progress-bar progress-bar-info" data-bind="attr: { 'style': 'width: '+percentage()+'; background-color: ' + \$parent.filelist.currentItem.progressColor() + ';' }">
<input type="checkbox" data-bind="attr: { 'name' : nzf_id }, disable: !canselect(), click : \$parent.filelist.checkSelectRange" title="$T('Glitter-multiSelect')" />
<strong data-bind="text: percentage"></strong>
<div class="fileDetails">
<span>
<span data-bind="truncatedTextCenter: filename"></span>
<div class="fileControls">
<a href="#" data-bind="click: \$parent.filelist.moveButton" class="hover-button buttonMoveToTop" title="$T('Glitter-top')"><span class="glyphicon glyphicon-chevron-up"></span></a>
<a href="#" data-bind="click: \$parent.filelist.moveButton" class="hover-button buttonMoveToBottom" title="$T('Glitter-bottom')"><span class="glyphicon glyphicon-chevron-down"></span></a>
</div>
<small>(<span data-bind="text: file_age"></span> - <span data-bind="text: mb"></span> MB)</small>
</div>
</span>
</div>
</div>
</td>
@@ -620,7 +621,7 @@
</tr>
<tr>
<td><strong>$T('menu-forums'):</strong></td>
<td><a href="https://forums.sabnzbd.org/" target="_blank">https://forums.sabnzbd.org/</a></td>
<td><a href="http://forums.sabnzbd.org/" target="_blank">http://forums.sabnzbd.org/</a></td>
</tr>
<tr>
<td><strong>GitHub:</strong></td>
@@ -628,7 +629,7 @@
</tr>
<tr>
<td><strong>$T('menu-irc'):</strong></td>
<td><a href="https://sabnzbd.org/live-chat" target="_blank">https://sabnzbd.org/live-chat</a></td>
<td><a href="http://www.sabnzbd.org/live-chat/" target="_blank">http://www.sabnzbd.org/live-chat/</a></td>
</tr>
</tbody>
</table>
@@ -651,6 +652,7 @@
<button type="button" class="btn btn-danger" data-bind="click: history.emptyHistory" data-action="history-purgeremove-failed"><span class="glyphicon glyphicon-floppy-remove"></span> $T('purgeFailed-Files')</button><hr />
<button type="button" class="btn btn-danger" data-bind="click: history.emptyHistory" data-action="history-purge-completed"><span class="glyphicon glyphicon-floppy-saved"></span> $T('purgeCompl')</button><hr />
<button type="button" class="btn btn-danger" data-bind="click: history.emptyHistory" data-action="history-purge-page"><span class="glyphicon glyphicon-check"></span> $T('purgePage') <span class="label label-default" data-bind="text: history.historyItems().length"></span></button>
</div>
</div>
</div>

View File

@@ -39,11 +39,6 @@
<span class="glyphicon glyphicon-hdd"></span> <span data-bind="text: diskSpaceLeft2"></span>B $T('Glitter-free')
</div>
<!-- /ko -->
<div class="info-container-box-sorting" style="display: none" data-bind="visible: refreshRate() > 2">
<a href="#" data-bind="click: refresh">
<span class="glyphicon glyphicon-repeat" data-tooltip="true" data-placement="left" title="$T('Glitter-refresh')"></span>
</a>
</div>
<div class="info-container-box-sorting dropdown" data-bind="visible: hasQueue()">
<a href="#" data-toggle="dropdown">
<span class="glyphicon glyphicon-sort-by-alphabet" data-tooltip="true" data-placement="left" title="$T('cmenu-sorting')"></span>
@@ -88,30 +83,31 @@
<tr class="queue-item">
<td>
<a href="#" data-bind="click: pauseToggle, attr: { 'title': pausedStatus() ? '$T('link-resume')' : '$T('link-pause')' }">
<span class="hover-button glyphicon" data-bind="css: queueIcon"></span>
<span class="hover-button glyphicon" data-bind="css: { 'glyphicon-play': pausedStatus(), 'glyphicon-pause': !pausedStatus() }"></span>
</a>
</td>
<td class="name">
<div class="row-wrap-text" data-bind="visible: !editingName(), css: { 'direct-unpack-text': direct_unpack }">
<span data-bind="text: name, attr: { 'title': name_title }"></span>
<div class="row-wrap-text" data-bind="visible: !editingName()">
<span data-bind="text: name, attr: { 'title': name() }"></span>
<!-- ko if: password() -->
<small class="queue-item-password">
<span class="glyphicon glyphicon-lock"></span>
<span data-bind="text: password"></span>
</small>
<!-- /ko -->
<!-- ko if: (rating_avg_video() !== false) -->
<div class="name-ratings hover-button">
<span class="glyphicon glyphicon-facetime-video"></span> <span data-bind="text: rating_avg_video"></span>
<span class="glyphicon glyphicon-volume-up"></span> <span data-bind="text: rating_avg_audio"></span>
</div>
<!-- /ko -->
</div>
<form data-bind="submit: editingNameSubmit">
<input type="text" data-bind="value: nameForEdit, visible: editingName(), hasfocus: editingName" />
</form>
<div class="name-icons direct-unpack hover-button" data-bind="visible: direct_unpack">
<span class="glyphicon glyphicon-compressed"></span> <span data-bind="text: direct_unpack"></span>
</div>
<div class="name-options" data-bind="visible: !editingName(), css: { disabled: isGrabbing() }">
<a href="#" data-bind="click: \$parent.queue.moveButton" class="hover-button buttonMoveToTop" title="$T('Glitter-top')"><span class="glyphicon glyphicon-chevron-up"></span></a>
<a href="#" data-bind="click: \$parent.queue.moveButton" class="hover-button buttonMoveToBottom" title="$T('Glitter-bottom')"><span class="glyphicon glyphicon-chevron-down"></span></a>
<a href="#" data-bind="click: editName" class="hover-button" title="$T('Glitter-rename')"><span class="glyphicon glyphicon-pencil"></span></a>
<a href="#" data-bind="click: showFiles" class="hover-button" title="$T('nzoDetails') - $T('srv-password')"><span class="glyphicon glyphicon-folder-open"></span></a>
<div class="name-options" data-bind="visible: !editingName()">
<a href="#" data-bind="click: editName, css: { disabled: isGrabbing() }" class="hover-button"><span class="glyphicon glyphicon-pencil"></span></a>
<a href="#" data-bind="click: showFiles, css: { disabled: isGrabbing() }" class="hover-button" title="$T('nzoDetails') - $T('srv-password')"><span class="glyphicon glyphicon-folder-open"></span></a>
<small data-bind="text: avg_age"></small>
</div>
</td>
@@ -121,8 +117,8 @@
</td>
<td class="progress-indicator">
<div class="progress">
<div class="progress-bar progress-bar-info" data-bind="attr: { 'style': 'width: ' + percentage() + '%; background-color: ' + progressColor() + ';' }">
<strong data-bind="text: percentage() + '%'"></strong>
<div class="progress-bar progress-bar-info" data-bind="attr: { 'style': 'width: ' + percentageRounded() + '; background-color: ' + progressColor() + ';' }">
<strong data-bind="text: percentageRounded"></strong>
<i data-bind="text: missingText"></i>
</div>
<span data-bind="text: progressText"></span>
@@ -133,8 +129,8 @@
<label data-bind="visible: parent.isMultiEditing()">
<input type="checkbox" name="multiedit" title="$T('Glitter-multiSelect')" data-bind="click: parent.addMultiEdit, attr: { 'id': 'multiedit_' + id } " />
</label>
<!-- ko if: !isGrabbing() -->
<div class="dropdown" data-bind="visible: !parent.isMultiEditing()">
<!-- ko if: !isGrabbing() -->
<div class="dropdown" data-bind="visible: !isGrabbing() && !parent.isMultiEditing()">
<a href="#" data-toggle="dropdown" data-bind="click: toggleDropdown">
<span class="caret"></span>
</a>
@@ -144,7 +140,7 @@
<span class="glyphicon glyphicon-tag"></span>
<select name="Category" class="form-control" data-bind="options: parent.categoriesList, optionsValue: 'catValue', optionsText: 'catText', value: category, event: { change: changeCat }"></select>
</li>
<!-- ko if: !isFetchingBlocks -->
<!-- ko if: (status() != 'Fetching') -->
<li title="$T('priority')" data-tooltip="true" data-placement="left">
<span class="glyphicon glyphicon-sort-by-attributes-alt"></span>
<select name="Priority" class="form-control" data-bind="options: parent.priorityOptions, optionsValue: 'value', optionsText: 'name', value: priority, event: { change: changePriority }"></select>

View File

@@ -16,7 +16,7 @@
<title data-bind="text: title">SABnzbd</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="application-name" content="SABnzbd">
<meta name="apple-mobile-web-app-capable" content="yes" />
@@ -30,14 +30,13 @@
<link rel="apple-touch-icon" sizes="152x152" href="./staticcfg/ico/apple-touch-icon-152x152-precomposed.png" />
<link rel="apple-touch-icon" sizes="180x180" href="./staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
<link rel="apple-touch-icon" sizes="192x192" href="./staticcfg/ico/android-192x192.png" />
<link rel="mask-icon" href="./staticcfg/ico/safari-pinned-tab.svg" color="#383F45">
<link rel="shortcut icon" type="image/ico" href="./staticcfg/ico/favicon.ico?v=$version" data-bind="attr: { 'href': SABIcon }" />
<link rel="shortcut icon" type="image/ico" href="./staticcfg/ico/favicon.ico?v=1.0.0" data-bind="attr: { 'href': SABIcon }" />
<link rel="stylesheet" type="text/css" href="./static/bootstrap/css/bootstrap.min.css?v=$version" />
<link rel="stylesheet" type="text/css" href="./static/stylesheets/glitter.css?v=$version" />
<link rel="stylesheet" type="text/css" href="./static/stylesheets/glitter.mobile.css?v=$version" media="all and (max-width: 768px)" />
<link rel="stylesheet" type="text/css" href="./static/bootstrap/css/bootstrap.min.css?p=$pid" />
<link rel="stylesheet" type="text/css" href="./static/stylesheets/glitter.css?p=$pid" />
<link rel="stylesheet" type="text/css" href="./static/stylesheets/glitter.mobile.css?p=$pid" media="all and (max-width: 768px)" />
<!--#if $color_scheme not in ('Default', '') #-->
<link rel="stylesheet" type="text/css" href="./static/stylesheets/colorschemes/${color_scheme}.css?v=$version"/>
<link rel="stylesheet" type="text/css" href="./static/stylesheets/colorschemes/${color_scheme}.css?p=$pid"/>
<!--#end if#-->
<!-- Make translations available in scripts -->
@@ -47,7 +46,7 @@
var sabSpeedHistory = [$bytespersec_list];
var newRelease = "$new_release";
var newReleaseUrl = "$new_rel_url";
var glitterIsBeta = ("$version".search(/[develop|Alpha|Beta|RC]/)) > 0;
var glitterIsBeta = ("$version".search(/Alpa|Beta|x/)) > 0;
var glitterPreLoadQueue = $preload_queue;
var glitterPreLoadHistory = $preload_history;
@@ -59,12 +58,10 @@
glitterTranslate.pauseFor = "$T('pauseFor')"
glitterTranslate.minutes = "$T('mins')"
glitterTranslate.shutdown = "$T('shutdownOK?')";
glitterTranslate.restart = "$T('explain-Restart') $T('explain-needNewLogin')".replace(/\<br(\s*\/|)\>/g, '\n');
glitterTranslate.restart = "$T('explain-Restart')".replace(/<br \/>/g, "\n");
glitterTranslate.repair = "$T('explain-Repair')".replace(/<br \/>/g, "\n").replace(/&quot;/g,'"');
glitterTranslate.removeDown = "$T('Glitter-confirmClearDownloads')";
glitterTranslate.removeDow1 = "$T('Glitter-confirmClear1Download')";
glitterTranslate.retryAll = "$T('link-retryAll')?";
glitterTranslate.fetch = "$T('Glitter-fetch')";
glitterTranslate.encrypted = "$T('Glitter-encrypted')";
glitterTranslate.duplicate = "$T('Glitter-duplicate')";
glitterTranslate.tooLarge = "$T('Glitter-tooLarge')";
@@ -87,7 +84,6 @@
glitterTranslate.moreText = "$T('Glitter-more')";
glitterTranslate.status = [];
glitterTranslate.status['DirectUnpack'] = "$T('opt-direct_unpack')";
glitterTranslate.status['Completed'] = "$T('post-Completed')";
glitterTranslate.status['Failed'] = "$T('post-Failed')";
glitterTranslate.status['Queued'] = "$T('post-Queued')";
@@ -106,7 +102,7 @@
glitterTranslate.status['Script'] = "$T('stage-script')";
glitterTranslate.status['Source'] = "$T('stage-source')";
glitterTranslate.status['Servers'] = "$T('stage-servers')";
glitterTranslate.status['INFO'] = "$T('log-info')".replace('+', '').toUpperCase();
glitterTranslate.status['INFO'] = "$T('log-info')".replace('+ ', '').toUpperCase();
glitterTranslate.status['WARNING'] = "$T('Glitter-warning')";
glitterTranslate.status['ERROR'] = "$T('Glitter-error')";

View File

@@ -128,13 +128,13 @@ function displayDateTime(inDate, outFormat, inFormat) {
if(inDate == '') {
var theMoment = moment()
} else {
var theMoment = moment.utc(inDate, inFormat)
var theMoment = moment(inDate, inFormat)
}
// Special format or regular format?
if(outFormat == 'fromNow') {
return theMoment.fromNow()
} else {
return theMoment.local().format(outFormat)
return theMoment.format(outFormat)
}
}

View File

@@ -35,30 +35,6 @@ function Fileslisting(parent) {
})
}
// Move to top and bottom buttons
self.moveButton = function (item,event) {
var targetRow, sourceRow, tbody;
sourceRow = $(event.currentTarget).parents("tr").filter(":first");
tbody = sourceRow.parents("tbody").filter(":first");
ko.utils.domData.set(sourceRow[0], "ko_sourceIndex", ko.utils.arrayIndexOf(sourceRow.parent().children(), sourceRow[0]));
sourceRow = sourceRow.detach();
if ($(event.currentTarget).is(".buttonMoveToTop")) {
// we are moving to the top
targetRow = tbody.children(".files-done").filter(":last");
} else {
//we are moving to the bottom
targetRow = tbody.children(".files-sortable").filter(":last");
}
if(targetRow.length < 1 ){
// we found an edge case and need to do something special
targetRow = tbody.children(".files-sortable").filter(":first");
sourceRow.insertBefore(targetRow[0]);
} else {
sourceRow.insertAfter($(targetRow[0]));
}
tbody.sortable('option', 'update').call(tbody[0],null, { item: sourceRow });
};
// Trigger update
self.triggerUpdate = function() {
// Call API
@@ -221,9 +197,9 @@ function FileslistingModel(parent, data) {
self.nzf_id = ko.observable(data.nzf_id);
self.file_age = ko.observable(data.age);
self.mb = ko.observable(data.mb);
self.percentage = ko.observable(fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)));
self.canselect = ko.observable(data.status != "finished" && data.status != "queued");
self.isdone = ko.observable(data.status == "finished");
self.percentage = ko.observable(self.isdone() ? fixPercentages(100) : fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)));
// Update internally
self.updateFromData = function(data) {
@@ -231,10 +207,9 @@ function FileslistingModel(parent, data) {
self.nzf_id(data.nzf_id)
self.file_age(data.age)
self.mb(data.mb)
self.percentage(fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)));
self.canselect(data.status != "finished" && data.status != "queued")
self.isdone(data.status == "finished")
// Data is given in MB, would always show 0% for small files even if completed
self.percentage(self.isdone() ? fixPercentages(100) : fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)))
}
}

View File

@@ -169,6 +169,7 @@ function HistoryListModel(parent) {
// Toggle showing failed
self.toggleShowFailed = function(data, event) {
// Set the loader so it doesn't flicker and then switch
self.isLoading(true)
self.showFailed(!self.showFailed())
@@ -176,20 +177,7 @@ function HistoryListModel(parent) {
$('#history-options a').tooltip('hide')
// Force refresh
self.parent.refresh(true)
}
// Retry all failed
self.retryAllFailed = function(data, event) {
// Ask to be sure
if(confirm(glitterTranslate.retryAll)) {
// Send the command
callAPI({
mode: 'retry_all'
}).then(function() {
// Force refresh
self.parent.refresh(true)
})
}
}
// Empty history options
@@ -340,14 +328,16 @@ function HistoryModel(parent, data) {
case 'speed':
// Anything to calculate?
if(self.historyStatus.bytes() > 0 && self.historyStatus.download_time() > 0) {
try {
// Extract the Download section
var downloadLog = ko.utils.arrayFirst(self.historyStatus.stage_log(), function(item) {
return item.name() == 'Download'
});
// Extract the speed
return downloadLog.actions()[0].match(/(\S*\s\S+)(?=<br\/>)/)[0]
} catch(err) { }
var theSpeed = self.historyStatus.bytes()/self.historyStatus.download_time();
theSpeed = theSpeed/1024;
// MB/s or KB/s
if(theSpeed > 1024) {
theSpeed = theSpeed/1024;
return theSpeed.toFixed(1) + ' MB/s'
} else {
return Math.round(theSpeed) + ' KB/s'
}
}
return;
case 'category':

View File

@@ -331,13 +331,6 @@ function ViewModel() {
// Split title & speed
var dataSplit = data.split('|||');
// Maybe the result is actually the login page?
if(dataSplit[0].substring(0, 11) === '<html lang=') {
// Redirect
document.location = document.location
return
}
// Set title
self.title(dataSplit[0]);
@@ -536,7 +529,6 @@ function ViewModel() {
callAPI({
mode: 'warnings'
}).then(function(response) {
// Reset it all
self.allWarnings.removeAll();
if(response) {
@@ -545,16 +537,20 @@ function ViewModel() {
// Go over all warnings and add
$.each(response.warnings, function(index, warning) {
// Split warning into parts
var warningSplit = warning.split(/\n/);
// Reformat CSS label and date
// Replaces spaces by non-breakable spaces and newlines with br's
var warningData = {
index: index,
type: glitterTranslate.status[warning.type].slice(0, -1),
text: convertHTMLtoText(warning.text).replace(/ /g, '\u00A0').replace(/(?:\r\n|\r|\n)/g, '<br />'),
timestamp: warning.time,
css: (warning.type == "ERROR" ? "danger" : warning.type == "WARNING" ? "warning" : "info"),
type: glitterTranslate.status[warningSplit[1]].slice(0, -1),
text: warningSplit.slice(2).join('<br/>').replace(/ /g, '\u00A0'), // Recombine if multiple lines
date: displayDateTime(warningSplit[0], self.dateFormat(), 'YYYY-MM-DD HH:mm'),
timestamp: moment(warningSplit[0], 'YYYY-MM-DD HH:mm').unix(),
css: (warningSplit[1] == "ERROR" ? "danger" : warningSplit[1] == "WARNING" ? "warning" : "info"),
clear: self.clearWarnings
};
self.allWarnings.push(warningData)
})
}
@@ -785,28 +781,15 @@ function ViewModel() {
}
// Update the servers
if(self.statusInfo.servers().length != data.status.servers.length) {
// Only now we can subscribe to the log-level-changes! (only at start)
if(self.statusInfo.servers().length == 0) {
self.statusInfo.loglevel.subscribe(function(newValue) {
// Update log-level
callSpecialAPI('./status/change_loglevel/', {
loglevel: newValue
});
})
}
// Empty them, in case of update
self.statusInfo.servers([])
if(self.statusInfo.servers().length == 0) {
// Initial add
$.each(data.status.servers, function() {
self.statusInfo.servers.push({
'servername': ko.observable(this.servername),
'serveroptional': ko.observable(this.serveroptional),
'serverpriority': ko.observable(this.serverpriority),
'servertotalconn': ko.observable(this.servertotalconn),
'serverssl': ko.observable(this.serverssl),
'servername': this.servername,
'serveroptional': this.serveroptional,
'serverpriority': this.serverpriority,
'servertotalconn': this.servertotalconn,
'serverssl': this.serverssl,
'serversslinfo': ko.observable(this.serversslinfo),
'serveractiveconn': ko.observable(this.serveractiveconn),
'servererror': ko.observable(this.servererror),
@@ -814,20 +797,23 @@ function ViewModel() {
'serverconnections': ko.observableArray(this.serverconnections)
})
})
// Only now we can subscribe to the log-level-changes!
self.statusInfo.loglevel.subscribe(function(newValue) {
// Update log-level
callSpecialAPI('./status/change_loglevel/', {
loglevel: newValue
});
})
} else {
// Update
$.each(data.status.servers, function(index) {
var activeServer = self.statusInfo.servers()[index];
activeServer.servername(this.servername),
activeServer.serveroptional(this.serveroptional),
activeServer.serverpriority(this.serverpriority),
activeServer.servertotalconn(this.servertotalconn),
activeServer.serverssl(this.serverssl),
activeServer.serversslinfo(this.serversslinfo),
activeServer.serveractiveconn(this.serveractiveconn),
activeServer.servererror(this.servererror),
activeServer.serveractive(this.serveractive),
activeServer.serveractiveconn(this.serveractiveconn)
activeServer.servererror(this.servererror)
activeServer.serveractive(this.serveractive)
activeServer.serverconnections(this.serverconnections)
activeServer.serversslinfo(this.serversslinfo)
})
}

View File

@@ -148,6 +148,7 @@ function QueueListModel(parent) {
// See what the actual index is of the queue-object
// This way we can see how we move up and down independent of pagination
var itemReplaced = self.queueItems()[event.targetIndex+corTerm];
callAPI({
mode: "switch",
value: itemMoved.id,
@@ -155,25 +156,6 @@ function QueueListModel(parent) {
}).then(self.parent.refresh);
};
// Move button clicked
self.moveButton = function(event,ui) {
var itemMoved = event;
var targetIndex;
if($(ui.currentTarget).is(".buttonMoveToTop")){
//we want to move to the top
targetIndex = 0;
} else {
// we want to move to the bottom
targetIndex = self.totalItems() - 1;
}
callAPI({
mode: "switch",
value: itemMoved.id,
value2: targetIndex
}).then(self.parent.refresh);
}
// Save pagination state
self.paginationLimit.subscribe(function(newValue) {
// Save in config if global
@@ -478,14 +460,11 @@ function QueueModel(parent, data) {
self.password = ko.observable(data.password);
self.index = ko.observable(data.index);
self.status = ko.observable(data.status);
self.isGrabbing = ko.observable(data.status == 'Grabbing' || data.avg_age == '-')
self.isFetchingBlocks = data.status == 'Fetching' || data.priority == 'Repair' // No need to update
self.isGrabbing = ko.observable(data.status == 'Grabbing')
self.totalMB = ko.observable(parseFloat(data.mb));
self.remainingMB = ko.observable(parseFloat(data.mbleft))
self.missingMB = ko.observable(parseFloat(data.mbmissing))
self.percentage = ko.observable(parseInt(data.percentage))
self.remainingMB = ko.observable(parseFloat(data.mbleft));
self.avg_age = ko.observable(data.avg_age)
self.direct_unpack = ko.observable(data.direct_unpack)
self.missing = ko.observable(data.missing)
self.category = ko.observable(data.cat);
self.priority = ko.observable(parent.priorityName[data.priority]);
self.script = ko.observable(data.script);
@@ -497,6 +476,8 @@ function QueueModel(parent, data) {
self.nameForEdit = ko.observable();
self.editingName = ko.observable(false);
self.hasDropdown = ko.observable(false);
self.rating_avg_video = ko.observable(false)
self.rating_avg_audio = ko.observable(false)
// Color of the progress bar
self.progressColor = ko.computed(function() {
@@ -504,8 +485,8 @@ function QueueModel(parent, data) {
if(self.status() == 'Checking') {
return '#58A9FA'
}
// Check for missing data, the value is arbitrary! (2%)
if(self.missingMB()/self.totalMB() > 0.02) {
// Check for missing data, the value is arbitrary!
if(self.missing() > 50) {
return '#F8A34E'
}
// Set to grey, only when not Force download
@@ -516,23 +497,22 @@ function QueueModel(parent, data) {
return '';
});
// MB's
// MB's and percentages
self.downloadedMB = ko.computed(function() {
return(self.totalMB() - self.remainingMB()).toFixed(0);
});
self.percentageRounded = ko.pureComputed(function() {
return fixPercentages(((self.downloadedMB() / self.totalMB()) * 100).toFixed(2))
})
self.progressText = ko.pureComputed(function() {
return (self.totalMB() - self.remainingMB()).toFixed(0) + " MB / " + (self.totalMB() * 1).toFixed(0) + " MB";
return self.downloadedMB() + " MB / " + (self.totalMB() * 1).toFixed(0) + " MB";
})
// Texts
self.name_title = ko.pureComputed(function() {
// When hovering over the job
if(self.direct_unpack()) {
return self.name() + ' - ' + glitterTranslate.status['DirectUnpack'] + ': ' + self.direct_unpack()
}
return self.name()
})
self.missingText = ko.pureComputed(function() {
// Check for missing data, the value is arbitrary! (1%)
if(self.missingMB()/self.totalMB() > 0.01) {
return self.missingMB().toFixed(0) + ' MB ' + glitterTranslate.misingArt
self.missingText= ko.pureComputed(function() {
// Check for missing data, the value is arbitrary!
if(self.missing() > 50) {
return self.missing() + ' ' + glitterTranslate.misingArt
}
return;
})
@@ -541,10 +521,6 @@ function QueueModel(parent, data) {
if(self.status() == 'Checking') {
return glitterTranslate.checking
}
// Grabbing
if(self.status() == 'Grabbing') {
return glitterTranslate.fetch
}
// Pausing status
if((self.parent.parent.downloadsPaused() && self.priority() != 2) || self.pausedStatus()) {
return glitterTranslate.paused;
@@ -553,18 +529,6 @@ function QueueModel(parent, data) {
return rewriteTime(self.timeLeft());
});
// Icon to better show force-priority
self.queueIcon = ko.computed(function() {
// Force comes first
if(self.priority() == 2) {
return 'glyphicon-forward'
}
if(self.pausedStatus()) {
return 'glyphicon-play'
}
return 'glyphicon-pause'
})
// Extra queue column
self.extraText = ko.pureComputed(function() {
// Picked anything?
@@ -597,19 +561,23 @@ function QueueModel(parent, data) {
self.password(data.password);
self.index(data.index);
self.status(data.status)
self.isGrabbing(data.status == 'Grabbing' || data.avg_age == '-')
self.isGrabbing(data.status == 'Grabbing')
self.totalMB(parseFloat(data.mb));
self.remainingMB(parseFloat(data.mbleft));
self.missingMB(parseFloat(data.mbmissing))
self.percentage(parseInt(data.percentage))
self.avg_age(data.avg_age)
self.direct_unpack(data.direct_unpack)
self.missing(data.missing)
self.category(data.cat);
self.priority(parent.priorityName[data.priority]);
self.script(data.script);
self.unpackopts(parseInt(data.unpackopts)) // UnpackOpts fails if not parseInt'd!
self.pausedStatus(data.status == 'Paused');
self.timeLeft(data.timeleft);
// If exists, otherwise false
if(data.rating_avg_video !== undefined) {
self.rating_avg_video(data.rating_avg_video === 0 ? '-' : data.rating_avg_video);
self.rating_avg_audio(data.rating_avg_audio === 0 ? '-' : data.rating_avg_audio);
}
};
// Pause individual download
@@ -700,7 +668,7 @@ function QueueModel(parent, data) {
}
self.changePriority = function(item, event) {
// Not if we are fetching extra blocks for repair!
if(item.isFetchingBlocks) return
if(item.status() == 'Fetching') return
callAPI({
mode: 'queue',
name: 'priority',

View File

@@ -1,13 +1,7 @@
// Peity jQuery plugin version 3.2.1
// (c) 2016 Ben Pickles
// Peity jQuery plugin version 3.2.0
// (c) 2015 Ben Pickles
//
// http://benpickles.github.io/peity
//
// Released under MIT license.
(function(k,w,h,v){var d=k.fn.peity=function(a,b){y&&this.each(function(){var e=k(this),c=e.data("_peity");c?(a&&(c.type=a),k.extend(c.opts,b)):(c=new x(e,a,k.extend({},d.defaults[a],e.data("peity"),b)),e.change(function(){c.draw()}).data("_peity",c));c.draw()});return this},x=function(a,b,e){this.$el=a;this.type=b;this.opts=e},o=x.prototype,q=o.svgElement=function(a,b){return k(w.createElementNS("http://www.w3.org/2000/svg",a)).attr(b)},y="createElementNS"in w&&q("svg",{})[0].createSVGRect;o.draw=
function(){var a=this.opts;d.graphers[this.type].call(this,a);a.after&&a.after.call(this,a)};o.fill=function(){var a=this.opts.fill;return k.isFunction(a)?a:function(b,e){return a[e%a.length]}};o.prepare=function(a,b){this.$svg||this.$el.hide().after(this.$svg=q("svg",{"class":"peity"}));return this.$svg.empty().data("peity",this).attr({height:b,width:a})};o.values=function(){return k.map(this.$el.text().split(this.opts.delimiter),function(a){return parseFloat(a)})};d.defaults={};d.graphers={};d.register=
function(a,b,e){this.defaults[a]=b;this.graphers[a]=e};d.register("pie",{fill:["#ff9900","#fff4dd","#ffc66e"],radius:8},function(a){if(!a.delimiter){var b=this.$el.text().match(/[^0-9\.]/);a.delimiter=b?b[0]:","}b=k.map(this.values(),function(a){return 0<a?a:0});if("/"==a.delimiter)var e=b[0],b=[e,h.max(0,b[1]-e)];for(var c=0,e=b.length,t=0;c<e;c++)t+=b[c];t||(e=2,t=1,b=[0,1]);var l=2*a.radius,l=this.prepare(a.width||l,a.height||l),c=l.width(),f=l.height(),j=c/2,d=f/2,f=h.min(j,d),a=a.innerRadius;
"donut"==this.type&&!a&&(a=0.5*f);for(var r=h.PI,s=this.fill(),g=this.scale=function(a,b){var c=a/t*r*2-r/2;return[b*h.cos(c)+j,b*h.sin(c)+d]},m=0,c=0;c<e;c++){var u=b[c],i=u/t;if(0!=i){if(1==i)if(a)var i=j-0.01,p=d-f,n=d-a,i=q("path",{d:["M",j,p,"A",f,f,0,1,1,i,p,"L",i,n,"A",a,a,0,1,0,j,n].join(" ")});else i=q("circle",{cx:j,cy:d,r:f});else p=m+u,n=["M"].concat(g(m,f),"A",f,f,0,0.5<i?1:0,1,g(p,f),"L"),a?n=n.concat(g(p,a),"A",a,a,0,0.5<i?1:0,0,g(m,a)):n.push(j,d),m+=u,i=q("path",{d:n.join(" ")});
i.attr("fill",s.call(this,u,c,b));l.append(i)}}});d.register("donut",k.extend(!0,{},d.defaults.pie),function(a){d.graphers.pie.call(this,a)});d.register("line",{delimiter:",",fill:"#c6d9fd",height:16,min:0,stroke:"#4d89f9",strokeWidth:1,width:32},function(a){var b=this.values();1==b.length&&b.push(b[0]);for(var e=h.max.apply(h,a.max==v?b:b.concat(a.max)),c=h.min.apply(h,a.min==v?b:b.concat(a.min)),d=this.prepare(a.width,a.height),l=a.strokeWidth,f=d.width(),j=d.height()-l,k=e-c,e=this.x=function(a){return a*
(f/(b.length-1))},r=this.y=function(a){var b=j;k&&(b-=(a-c)/k*j);return b+l/2},s=r(h.max(c,0)),g=[0,s],m=0;m<b.length;m++)g.push(e(m),r(b[m]));g.push(f,s);a.fill&&d.append(q("polygon",{fill:a.fill,points:g.join(" ")}));l&&d.append(q("polyline",{fill:"none",points:g.slice(2,g.length-2).join(" "),stroke:a.stroke,"stroke-width":l,"stroke-linecap":"square"}))});d.register("bar",{delimiter:",",fill:["#4D89F9"],height:16,min:0,padding:0.1,width:32},function(a){for(var b=this.values(),e=h.max.apply(h,a.max==
v?b:b.concat(a.max)),c=h.min.apply(h,a.min==v?b:b.concat(a.min)),d=this.prepare(a.width,a.height),l=d.width(),f=d.height(),j=e-c,a=a.padding,k=this.fill(),r=this.x=function(a){return a*l/b.length},s=this.y=function(a){return f-(j?(a-c)/j*f:1)},g=0;g<b.length;g++){var m=r(g+a),u=r(g+1-a)-m,i=b[g],p=s(i),n=p,o;j?0>i?n=s(h.min(e,0)):p=s(h.max(c,0)):o=1;o=p-n;0==o&&(o=1,0<e&&j&&n--);d.append(q("rect",{fill:k.call(this,i,g,b),x:m,y:n,width:u,height:o}))}})})(jQuery,document,Math);
!function(t,i,e,n){var r=t.fn.peity=function(i,e){return l&&this.each(function(){var n=t(this),s=n.data("_peity");s?(i&&(s.type=i),t.extend(s.opts,e)):(s=new a(n,i,t.extend({},r.defaults[i],n.data("peity"),e)),n.change(function(){s.draw()}).data("_peity",s)),s.draw()}),this},a=function(t,i,e){this.$el=t,this.type=i,this.opts=e},s=a.prototype,h=s.svgElement=function(e,n){return t(i.createElementNS("http://www.w3.org/2000/svg",e)).attr(n)},l="createElementNS"in i&&h("svg",{})[0].createSVGRect;s.draw=function(){var t=this.opts;r.graphers[this.type].call(this,t),t.after&&t.after.call(this,t)},s.fill=function(){var i=this.opts.fill;return t.isFunction(i)?i:function(t,e){return i[e%i.length]}},s.prepare=function(t,i){return this.$svg||this.$el.hide().after(this.$svg=h("svg",{"class":"peity"})),this.$svg.empty().data("peity",this).attr({height:i,width:t})},s.values=function(){if(this.opts.values){var i=this.opts.values;return this.opts.values="",i}return t.map(this.$el.text().split(this.opts.delimiter),function(t){return parseFloat(t)})},r.defaults={},r.graphers={},r.register=function(t,i,e){this.defaults[t]=i,this.graphers[t]=e},r.register("pie",{fill:["#ff9900","#fff4dd","#ffc66e"],radius:8},function(i){if(!i.delimiter){var n=this.$el.text().match(/[^0-9\.]/);i.delimiter=n?n[0]:","}var r=t.map(this.values(),function(t){return t>0?t:0});if("/"==i.delimiter){var a=r[0],s=r[1];r=[a,e.max(0,s-a)]}for(var l=0,p=r.length,o=0;p>l;l++)o+=r[l];o||(p=2,o=1,r=[0,1]);var f=2*i.radius,c=this.prepare(i.width||f,i.height||f),u=c.width(),d=c.height(),g=u/2,v=d/2,m=e.min(g,v),y=i.innerRadius;"donut"!=this.type||y||(y=.5*m);var w=e.PI,x=this.fill(),k=this.scale=function(t,i){var n=t/o*w*2-w/2;return[i*e.cos(n)+g,i*e.sin(n)+v]},$=0;for(l=0;p>l;l++){var j,A=r[l],E=A/o;if(0!=E){if(1==E)if(y){var F=g-.01,M=v-m,S=v-y;j=h("path",{d:["M",g,M,"A",m,m,0,1,1,F,M,"L",F,S,"A",y,y,0,1,0,g,S].join(" ")})}else j=h("circle",{cx:g,cy:v,r:m});else{var L=$+A,N=["M"].concat(k($,m),"A",m,m,0,E>.5?1:0,1,k(L,m),"L");y?N=N.concat(k(L,y),"A",y,y,0,E>.5?1:0,0,k($,y)):N.push(g,v),$+=A,j=h("path",{d:N.join(" ")})}j.attr("fill",x.call(this,A,l,r)),c.append(j)}}}),r.register("donut",t.extend(!0,{},r.defaults.pie),function(t){r.graphers.pie.call(this,t)}),r.register("line",{delimiter:",",fill:"#c6d9fd",height:16,min:0,stroke:"#4d89f9",strokeWidth:1,width:32},function(t){var i=this.values();1==i.length&&i.push(i[0]);for(var r=e.max.apply(e,t.max==n?i:i.concat(t.max)),a=e.min.apply(e,t.min==n?i:i.concat(t.min)),s=this.prepare(t.width,t.height),l=t.strokeWidth,p=s.width(),o=s.height()-l,f=r-a,c=this.x=function(t){return t*(p/(i.length-1))},u=this.y=function(t){var i=o;return f&&(i-=(t-a)/f*o),i+l/2},d=u(e.max(a,0)),g=[0,d],v=0;v<i.length;v++)g.push(c(v),u(i[v]));g.push(p,d),t.fill&&s.append(h("polygon",{fill:t.fill,points:g.join(" ")})),l&&s.append(h("polyline",{fill:"none",points:g.slice(2,g.length-2).join(" "),stroke:t.stroke,"stroke-width":l,"stroke-linecap":"square"}))}),r.register("bar",{delimiter:",",fill:["#4D89F9"],height:16,min:0,padding:.1,width:32},function(t){for(var i=this.values(),r=e.max.apply(e,t.max==n?i:i.concat(t.max)),a=e.min.apply(e,t.min==n?i:i.concat(t.min)),s=this.prepare(t.width,t.height),l=s.width(),p=s.height(),o=r-a,f=t.padding,c=this.fill(),u=this.x=function(t){return t*l/i.length},d=this.y=function(t){return p-(o?(t-a)/o*p:1)},g=0;g<i.length;g++){var v,m=u(g+f),y=u(g+1-f)-m,w=i[g],x=d(w),k=x,$=x;o?0>w?k=d(e.min(r,0)):$=d(e.max(a,0)):v=1,v=$-k,0==v&&(v=1,r>0&&o&&k--),s.append(h("rect",{fill:c.call(this,w,g,i),x:m,y:k,width:y,height:v}))}})}(jQuery,document,Math);

View File

@@ -1,124 +1,123 @@
/*!
* Knockout JavaScript library v3.4.2
* (c) The Knockout.js team - http://knockoutjs.com/
* Knockout JavaScript library v3.4.0
* (c) Steven Sanderson - http://knockoutjs.com/
* License: MIT (http://www.opensource.org/licenses/mit-license.php)
*/
(function() {(function(n){var x=this||(0,eval)("this"),t=x.document,M=x.navigator,u=x.jQuery,H=x.JSON;(function(n){"function"===typeof define&&define.amd?define(["exports","require"],n):"object"===typeof exports&&"object"===typeof module?n(module.exports||exports):n(x.ko={})})(function(N,O){function J(a,c){return null===a||typeof a in R?a===c:!1}function S(b,c){var d;return function(){d||(d=a.a.setTimeout(function(){d=n;b()},c))}}function T(b,c){var d;return function(){clearTimeout(d);d=a.a.setTimeout(b,c)}}function U(a,
c){c&&c!==E?"beforeChange"===c?this.Ob(a):this.Ja(a,c):this.Pb(a)}function V(a,c){null!==c&&c.k&&c.k()}function W(a,c){var d=this.Mc,e=d[s];e.T||(this.ob&&this.Oa[c]?(d.Sb(c,a,this.Oa[c]),this.Oa[c]=null,--this.ob):e.s[c]||d.Sb(c,a,e.t?{$:a}:d.yc(a)),a.Ha&&a.Hc())}function K(b,c,d,e){a.d[b]={init:function(b,g,h,l,m){var k,r;a.m(function(){var q=g(),p=a.a.c(q),p=!d!==!p,A=!r;if(A||c||p!==k)A&&a.xa.Ca()&&(r=a.a.wa(a.f.childNodes(b),!0)),p?(A||a.f.fa(b,a.a.wa(r)),a.hb(e?e(m,q):m,b)):a.f.za(b),k=p},null,
{i:b});return{controlsDescendantBindings:!0}}};a.h.va[b]=!1;a.f.aa[b]=!0}var a="undefined"!==typeof N?N:{};a.b=function(b,c){for(var d=b.split("."),e=a,f=0;f<d.length-1;f++)e=e[d[f]];e[d[d.length-1]]=c};a.H=function(a,c,d){a[c]=d};a.version="3.4.2";a.b("version",a.version);a.options={deferUpdates:!1,useOnlyNativeEvents:!1};a.a=function(){function b(a,b){for(var c in a)a.hasOwnProperty(c)&&b(c,a[c])}function c(a,b){if(b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function d(a,b){a.__proto__=
b;return a}function e(b,c,d,e){var m=b[c].match(r)||[];a.a.r(d.match(r),function(b){a.a.ra(m,b,e)});b[c]=m.join(" ")}var f={__proto__:[]}instanceof Array,g="function"===typeof Symbol,h={},l={};h[M&&/Firefox\/2/i.test(M.userAgent)?"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];h.MouseEvents="click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");b(h,function(a,b){if(b.length)for(var c=0,d=b.length;c<d;c++)l[b[c]]=a});var m={propertychange:!0},k=
t&&function(){for(var a=3,b=t.createElement("div"),c=b.getElementsByTagName("i");b.innerHTML="\x3c!--[if gt IE "+ ++a+"]><i></i><![endif]--\x3e",c[0];);return 4<a?a:n}(),r=/\S+/g;return{gc:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],r:function(a,b){for(var c=0,d=a.length;c<d;c++)b(a[c],c)},o:function(a,b){if("function"==typeof Array.prototype.indexOf)return Array.prototype.indexOf.call(a,b);for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},Vb:function(a,b,c){for(var d=
0,e=a.length;d<e;d++)if(b.call(c,a[d],d))return a[d];return null},Na:function(b,c){var d=a.a.o(b,c);0<d?b.splice(d,1):0===d&&b.shift()},Wb:function(b){b=b||[];for(var c=[],d=0,e=b.length;d<e;d++)0>a.a.o(c,b[d])&&c.push(b[d]);return c},ib:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;d<e;d++)c.push(b(a[d],d));return c},Ma:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;d<e;d++)b(a[d],d)&&c.push(a[d]);return c},ta:function(a,b){if(b instanceof Array)a.push.apply(a,b);else for(var c=0,d=b.length;c<
d;c++)a.push(b[c]);return a},ra:function(b,c,d){var e=a.a.o(a.a.Bb(b),c);0>e?d&&b.push(c):d||b.splice(e,1)},la:f,extend:c,$a:d,ab:f?d:c,D:b,Ea:function(a,b){if(!a)return a;var c={},d;for(d in a)a.hasOwnProperty(d)&&(c[d]=b(a[d],d,a));return c},rb:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},nc:function(b){b=a.a.W(b);for(var c=(b[0]&&b[0].ownerDocument||t).createElement("div"),d=0,e=b.length;d<e;d++)c.appendChild(a.ba(b[d]));return c},wa:function(b,c){for(var d=0,e=b.length,m=[];d<e;d++){var k=
b[d].cloneNode(!0);m.push(c?a.ba(k):k)}return m},fa:function(b,c){a.a.rb(b);if(c)for(var d=0,e=c.length;d<e;d++)b.appendChild(c[d])},uc:function(b,c){var d=b.nodeType?[b]:b;if(0<d.length){for(var e=d[0],m=e.parentNode,k=0,f=c.length;k<f;k++)m.insertBefore(c[k],e);k=0;for(f=d.length;k<f;k++)a.removeNode(d[k])}},Ba:function(a,b){if(a.length){for(b=8===b.nodeType&&b.parentNode||b;a.length&&a[0].parentNode!==b;)a.splice(0,1);for(;1<a.length&&a[a.length-1].parentNode!==b;)a.length--;if(1<a.length){var c=
a[0],d=a[a.length-1];for(a.length=0;c!==d;)a.push(c),c=c.nextSibling;a.push(d)}}return a},wc:function(a,b){7>k?a.setAttribute("selected",b):a.selected=b},cb:function(a){return null===a||a===n?"":a.trim?a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},sd:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},Rc:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(3===a.nodeType?a.parentNode:a);if(b.compareDocumentPosition)return 16==
(b.compareDocumentPosition(a)&16);for(;a&&a!=b;)a=a.parentNode;return!!a},qb:function(b){return a.a.Rc(b,b.ownerDocument.documentElement)},Tb:function(b){return!!a.a.Vb(b,a.a.qb)},A:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},Zb:function(b){return a.onError?function(){try{return b.apply(this,arguments)}catch(c){throw a.onError&&a.onError(c),c;}}:b},setTimeout:function(b,c){return setTimeout(a.a.Zb(b),c)},dc:function(b){setTimeout(function(){a.onError&&a.onError(b);throw b;},0)},q:function(b,
c,d){var e=a.a.Zb(d);d=k&&m[c];if(a.options.useOnlyNativeEvents||d||!u)if(d||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var f=function(a){e.call(b,a)},l="on"+c;b.attachEvent(l,f);a.a.G.qa(b,function(){b.detachEvent(l,f)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(c,e,!1);else u(b).bind(c,e)},Fa:function(b,c){if(!b||!b.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var d;"input"===
a.a.A(b)&&b.type&&"click"==c.toLowerCase()?(d=b.type,d="checkbox"==d||"radio"==d):d=!1;if(a.options.useOnlyNativeEvents||!u||d)if("function"==typeof t.createEvent)if("function"==typeof b.dispatchEvent)d=t.createEvent(l[c]||"HTMLEvents"),d.initEvent(c,!0,!0,x,0,0,0,0,0,!1,!1,!1,!1,0,b),b.dispatchEvent(d);else throw Error("The supplied element doesn't support dispatchEvent");else if(d&&b.click)b.click();else if("undefined"!=typeof b.fireEvent)b.fireEvent("on"+c);else throw Error("Browser doesn't support triggering events");
else u(b).trigger(c)},c:function(b){return a.I(b)?b():b},Bb:function(b){return a.I(b)?b.p():b},fb:function(b,c,d){var k;c&&("object"===typeof b.classList?(k=b.classList[d?"add":"remove"],a.a.r(c.match(r),function(a){k.call(b.classList,a)})):"string"===typeof b.className.baseVal?e(b.className,"baseVal",c,d):e(b,"className",c,d))},bb:function(b,c){var d=a.a.c(c);if(null===d||d===n)d="";var e=a.f.firstChild(b);!e||3!=e.nodeType||a.f.nextSibling(e)?a.f.fa(b,[b.ownerDocument.createTextNode(d)]):e.data=
d;a.a.Wc(b)},vc:function(a,b){a.name=b;if(7>=k)try{a.mergeAttributes(t.createElement("<input name='"+a.name+"'/>"),!1)}catch(c){}},Wc:function(a){9<=k&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},Sc:function(a){if(k){var b=a.style.width;a.style.width=0;a.style.width=b}},nd:function(b,c){b=a.a.c(b);c=a.a.c(c);for(var d=[],e=b;e<=c;e++)d.push(e);return d},W:function(a){for(var b=[],c=0,d=a.length;c<d;c++)b.push(a[c]);return b},bc:function(a){return g?Symbol(a):a},xd:6===k,
yd:7===k,C:k,ic:function(b,c){for(var d=a.a.W(b.getElementsByTagName("input")).concat(a.a.W(b.getElementsByTagName("textarea"))),e="string"==typeof c?function(a){return a.name===c}:function(a){return c.test(a.name)},k=[],m=d.length-1;0<=m;m--)e(d[m])&&k.push(d[m]);return k},kd:function(b){return"string"==typeof b&&(b=a.a.cb(b))?H&&H.parse?H.parse(b):(new Function("return "+b))():null},Gb:function(b,c,d){if(!H||!H.stringify)throw Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
return H.stringify(a.a.c(b),c,d)},ld:function(c,d,e){e=e||{};var k=e.params||{},m=e.includeFields||this.gc,f=c;if("object"==typeof c&&"form"===a.a.A(c))for(var f=c.action,l=m.length-1;0<=l;l--)for(var g=a.a.ic(c,m[l]),h=g.length-1;0<=h;h--)k[g[h].name]=g[h].value;d=a.a.c(d);var r=t.createElement("form");r.style.display="none";r.action=f;r.method="post";for(var n in d)c=t.createElement("input"),c.type="hidden",c.name=n,c.value=a.a.Gb(a.a.c(d[n])),r.appendChild(c);b(k,function(a,b){var c=t.createElement("input");
c.type="hidden";c.name=a;c.value=b;r.appendChild(c)});t.body.appendChild(r);e.submitter?e.submitter(r):r.submit();setTimeout(function(){r.parentNode.removeChild(r)},0)}}}();a.b("utils",a.a);a.b("utils.arrayForEach",a.a.r);a.b("utils.arrayFirst",a.a.Vb);a.b("utils.arrayFilter",a.a.Ma);a.b("utils.arrayGetDistinctValues",a.a.Wb);a.b("utils.arrayIndexOf",a.a.o);a.b("utils.arrayMap",a.a.ib);a.b("utils.arrayPushAll",a.a.ta);a.b("utils.arrayRemoveItem",a.a.Na);a.b("utils.extend",a.a.extend);a.b("utils.fieldsIncludedWithJsonPost",
a.a.gc);a.b("utils.getFormFields",a.a.ic);a.b("utils.peekObservable",a.a.Bb);a.b("utils.postJson",a.a.ld);a.b("utils.parseJson",a.a.kd);a.b("utils.registerEventHandler",a.a.q);a.b("utils.stringifyJson",a.a.Gb);a.b("utils.range",a.a.nd);a.b("utils.toggleDomNodeCssClass",a.a.fb);a.b("utils.triggerEvent",a.a.Fa);a.b("utils.unwrapObservable",a.a.c);a.b("utils.objectForEach",a.a.D);a.b("utils.addOrRemoveItem",a.a.ra);a.b("utils.setTextContent",a.a.bb);a.b("unwrap",a.a.c);Function.prototype.bind||(Function.prototype.bind=
function(a){var c=this;if(1===arguments.length)return function(){return c.apply(a,arguments)};var d=Array.prototype.slice.call(arguments,1);return function(){var e=d.slice(0);e.push.apply(e,arguments);return c.apply(a,e)}});a.a.e=new function(){function a(b,g){var h=b[d];if(!h||"null"===h||!e[h]){if(!g)return n;h=b[d]="ko"+c++;e[h]={}}return e[h]}var c=0,d="__ko__"+(new Date).getTime(),e={};return{get:function(c,d){var e=a(c,!1);return e===n?n:e[d]},set:function(c,d,e){if(e!==n||a(c,!1)!==n)a(c,!0)[d]=
e},clear:function(a){var b=a[d];return b?(delete e[b],a[d]=null,!0):!1},J:function(){return c++ +d}}};a.b("utils.domData",a.a.e);a.b("utils.domData.clear",a.a.e.clear);a.a.G=new function(){function b(b,c){var e=a.a.e.get(b,d);e===n&&c&&(e=[],a.a.e.set(b,d,e));return e}function c(d){var e=b(d,!1);if(e)for(var e=e.slice(0),l=0;l<e.length;l++)e[l](d);a.a.e.clear(d);a.a.G.cleanExternalData(d);if(f[d.nodeType])for(e=d.firstChild;d=e;)e=d.nextSibling,8===d.nodeType&&c(d)}var d=a.a.e.J(),e={1:!0,8:!0,9:!0},
f={1:!0,9:!0};return{qa:function(a,c){if("function"!=typeof c)throw Error("Callback must be a function");b(a,!0).push(c)},tc:function(c,e){var f=b(c,!1);f&&(a.a.Na(f,e),0==f.length&&a.a.e.set(c,d,n))},ba:function(b){if(e[b.nodeType]&&(c(b),f[b.nodeType])){var d=[];a.a.ta(d,b.getElementsByTagName("*"));for(var l=0,m=d.length;l<m;l++)c(d[l])}return b},removeNode:function(b){a.ba(b);b.parentNode&&b.parentNode.removeChild(b)},cleanExternalData:function(a){u&&"function"==typeof u.cleanData&&u.cleanData([a])}}};
a.ba=a.a.G.ba;a.removeNode=a.a.G.removeNode;a.b("cleanNode",a.ba);a.b("removeNode",a.removeNode);a.b("utils.domNodeDisposal",a.a.G);a.b("utils.domNodeDisposal.addDisposeCallback",a.a.G.qa);a.b("utils.domNodeDisposal.removeDisposeCallback",a.a.G.tc);(function(){var b=[0,"",""],c=[1,"<table>","</table>"],d=[3,"<table><tbody><tr>","</tr></tbody></table>"],e=[1,"<select multiple='multiple'>","</select>"],f={thead:c,tbody:c,tfoot:c,tr:[2,"<table><tbody>","</tbody></table>"],td:d,th:d,option:e,optgroup:e},
g=8>=a.a.C;a.a.na=function(c,d){var e;if(u)if(u.parseHTML)e=u.parseHTML(c,d)||[];else{if((e=u.clean([c],d))&&e[0]){for(var k=e[0];k.parentNode&&11!==k.parentNode.nodeType;)k=k.parentNode;k.parentNode&&k.parentNode.removeChild(k)}}else{(e=d)||(e=t);var k=e.parentWindow||e.defaultView||x,r=a.a.cb(c).toLowerCase(),q=e.createElement("div"),p;p=(r=r.match(/^<([a-z]+)[ >]/))&&f[r[1]]||b;r=p[0];p="ignored<div>"+p[1]+c+p[2]+"</div>";"function"==typeof k.innerShiv?q.appendChild(k.innerShiv(p)):(g&&e.appendChild(q),
q.innerHTML=p,g&&q.parentNode.removeChild(q));for(;r--;)q=q.lastChild;e=a.a.W(q.lastChild.childNodes)}return e};a.a.Eb=function(b,c){a.a.rb(b);c=a.a.c(c);if(null!==c&&c!==n)if("string"!=typeof c&&(c=c.toString()),u)u(b).html(c);else for(var d=a.a.na(c,b.ownerDocument),e=0;e<d.length;e++)b.appendChild(d[e])}})();a.b("utils.parseHtmlFragment",a.a.na);a.b("utils.setHtml",a.a.Eb);a.N=function(){function b(c,e){if(c)if(8==c.nodeType){var f=a.N.pc(c.nodeValue);null!=f&&e.push({Qc:c,hd:f})}else if(1==c.nodeType)for(var f=
0,g=c.childNodes,h=g.length;f<h;f++)b(g[f],e)}var c={};return{yb:function(a){if("function"!=typeof a)throw Error("You can only pass a function to ko.memoization.memoize()");var b=(4294967296*(1+Math.random())|0).toString(16).substring(1)+(4294967296*(1+Math.random())|0).toString(16).substring(1);c[b]=a;return"\x3c!--[ko_memo:"+b+"]--\x3e"},Bc:function(a,b){var f=c[a];if(f===n)throw Error("Couldn't find any memo with ID "+a+". Perhaps it's already been unmemoized.");try{return f.apply(null,b||[]),
!0}finally{delete c[a]}},Cc:function(c,e){var f=[];b(c,f);for(var g=0,h=f.length;g<h;g++){var l=f[g].Qc,m=[l];e&&a.a.ta(m,e);a.N.Bc(f[g].hd,m);l.nodeValue="";l.parentNode&&l.parentNode.removeChild(l)}},pc:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:null}}}();a.b("memoization",a.N);a.b("memoization.memoize",a.N.yb);a.b("memoization.unmemoize",a.N.Bc);a.b("memoization.parseMemoText",a.N.pc);a.b("memoization.unmemoizeDomNodeAndDescendants",a.N.Cc);a.Z=function(){function b(){if(e)for(var b=
e,c=0,m;g<e;)if(m=d[g++]){if(g>b){if(5E3<=++c){g=e;a.a.dc(Error("'Too much recursion' after processing "+c+" task groups."));break}b=e}try{m()}catch(k){a.a.dc(k)}}}function c(){b();g=e=d.length=0}var d=[],e=0,f=1,g=0;return{scheduler:x.MutationObserver?function(a){var b=t.createElement("div");(new MutationObserver(a)).observe(b,{attributes:!0});return function(){b.classList.toggle("foo")}}(c):t&&"onreadystatechange"in t.createElement("script")?function(a){var b=t.createElement("script");b.onreadystatechange=
function(){b.onreadystatechange=null;t.documentElement.removeChild(b);b=null;a()};t.documentElement.appendChild(b)}:function(a){setTimeout(a,0)},Za:function(b){e||a.Z.scheduler(c);d[e++]=b;return f++},cancel:function(a){a-=f-e;a>=g&&a<e&&(d[a]=null)},resetForTesting:function(){var a=e-g;g=e=d.length=0;return a},rd:b}}();a.b("tasks",a.Z);a.b("tasks.schedule",a.Z.Za);a.b("tasks.runEarly",a.Z.rd);a.Aa={throttle:function(b,c){b.throttleEvaluation=c;var d=null;return a.B({read:b,write:function(e){clearTimeout(d);
d=a.a.setTimeout(function(){b(e)},c)}})},rateLimit:function(a,c){var d,e,f;"number"==typeof c?d=c:(d=c.timeout,e=c.method);a.gb=!1;f="notifyWhenChangesStop"==e?T:S;a.Wa(function(a){return f(a,d)})},deferred:function(b,c){if(!0!==c)throw Error("The 'deferred' extender only accepts the value 'true', because it is not supported to turn deferral off once enabled.");b.gb||(b.gb=!0,b.Wa(function(c){var e,f=!1;return function(){if(!f){a.Z.cancel(e);e=a.Z.Za(c);try{f=!0,b.notifySubscribers(n,"dirty")}finally{f=
!1}}}}))},notify:function(a,c){a.equalityComparer="always"==c?null:J}};var R={undefined:1,"boolean":1,number:1,string:1};a.b("extenders",a.Aa);a.zc=function(b,c,d){this.$=b;this.jb=c;this.Pc=d;this.T=!1;a.H(this,"dispose",this.k)};a.zc.prototype.k=function(){this.T=!0;this.Pc()};a.K=function(){a.a.ab(this,D);D.ub(this)};var E="change",D={ub:function(a){a.F={change:[]};a.Qb=1},Y:function(b,c,d){var e=this;d=d||E;var f=new a.zc(e,c?b.bind(c):b,function(){a.a.Na(e.F[d],f);e.Ka&&e.Ka(d)});e.ua&&e.ua(d);
e.F[d]||(e.F[d]=[]);e.F[d].push(f);return f},notifySubscribers:function(b,c){c=c||E;c===E&&this.Kb();if(this.Ra(c)){var d=c===E&&this.Fc||this.F[c].slice(0);try{a.l.Xb();for(var e=0,f;f=d[e];++e)f.T||f.jb(b)}finally{a.l.end()}}},Pa:function(){return this.Qb},Zc:function(a){return this.Pa()!==a},Kb:function(){++this.Qb},Wa:function(b){var c=this,d=a.I(c),e,f,g,h;c.Ja||(c.Ja=c.notifySubscribers,c.notifySubscribers=U);var l=b(function(){c.Ha=!1;d&&h===c&&(h=c.Mb?c.Mb():c());var a=f||c.Ua(g,h);f=e=!1;
a&&c.Ja(g=h)});c.Pb=function(a){c.Fc=c.F[E].slice(0);c.Ha=e=!0;h=a;l()};c.Ob=function(a){e||(g=a,c.Ja(a,"beforeChange"))};c.Hc=function(){c.Ua(g,c.p(!0))&&(f=!0)}},Ra:function(a){return this.F[a]&&this.F[a].length},Xc:function(b){if(b)return this.F[b]&&this.F[b].length||0;var c=0;a.a.D(this.F,function(a,b){"dirty"!==a&&(c+=b.length)});return c},Ua:function(a,c){return!this.equalityComparer||!this.equalityComparer(a,c)},extend:function(b){var c=this;b&&a.a.D(b,function(b,e){var f=a.Aa[b];"function"==
typeof f&&(c=f(c,e)||c)});return c}};a.H(D,"subscribe",D.Y);a.H(D,"extend",D.extend);a.H(D,"getSubscriptionsCount",D.Xc);a.a.la&&a.a.$a(D,Function.prototype);a.K.fn=D;a.lc=function(a){return null!=a&&"function"==typeof a.Y&&"function"==typeof a.notifySubscribers};a.b("subscribable",a.K);a.b("isSubscribable",a.lc);a.xa=a.l=function(){function b(a){d.push(e);e=a}function c(){e=d.pop()}var d=[],e,f=0;return{Xb:b,end:c,sc:function(b){if(e){if(!a.lc(b))throw Error("Only subscribable things can act as dependencies");
e.jb.call(e.Lc,b,b.Gc||(b.Gc=++f))}},w:function(a,d,e){try{return b(),a.apply(d,e||[])}finally{c()}},Ca:function(){if(e)return e.m.Ca()},Va:function(){if(e)return e.Va}}}();a.b("computedContext",a.xa);a.b("computedContext.getDependenciesCount",a.xa.Ca);a.b("computedContext.isInitial",a.xa.Va);a.b("ignoreDependencies",a.wd=a.l.w);var F=a.a.bc("_latestValue");a.O=function(b){function c(){if(0<arguments.length)return c.Ua(c[F],arguments[0])&&(c.ia(),c[F]=arguments[0],c.ha()),this;a.l.sc(c);return c[F]}
c[F]=b;a.a.la||a.a.extend(c,a.K.fn);a.K.fn.ub(c);a.a.ab(c,B);a.options.deferUpdates&&a.Aa.deferred(c,!0);return c};var B={equalityComparer:J,p:function(){return this[F]},ha:function(){this.notifySubscribers(this[F])},ia:function(){this.notifySubscribers(this[F],"beforeChange")}};a.a.la&&a.a.$a(B,a.K.fn);var I=a.O.md="__ko_proto__";B[I]=a.O;a.Qa=function(b,c){return null===b||b===n||b[I]===n?!1:b[I]===c?!0:a.Qa(b[I],c)};a.I=function(b){return a.Qa(b,a.O)};a.Da=function(b){return"function"==typeof b&&
b[I]===a.O||"function"==typeof b&&b[I]===a.B&&b.$c?!0:!1};a.b("observable",a.O);a.b("isObservable",a.I);a.b("isWriteableObservable",a.Da);a.b("isWritableObservable",a.Da);a.b("observable.fn",B);a.H(B,"peek",B.p);a.H(B,"valueHasMutated",B.ha);a.H(B,"valueWillMutate",B.ia);a.ma=function(b){b=b||[];if("object"!=typeof b||!("length"in b))throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");b=a.O(b);a.a.ab(b,a.ma.fn);return b.extend({trackArrayChanges:!0})};
a.ma.fn={remove:function(b){for(var c=this.p(),d=[],e="function"!=typeof b||a.I(b)?function(a){return a===b}:b,f=0;f<c.length;f++){var g=c[f];e(g)&&(0===d.length&&this.ia(),d.push(g),c.splice(f,1),f--)}d.length&&this.ha();return d},removeAll:function(b){if(b===n){var c=this.p(),d=c.slice(0);this.ia();c.splice(0,c.length);this.ha();return d}return b?this.remove(function(c){return 0<=a.a.o(b,c)}):[]},destroy:function(b){var c=this.p(),d="function"!=typeof b||a.I(b)?function(a){return a===b}:b;this.ia();
for(var e=c.length-1;0<=e;e--)d(c[e])&&(c[e]._destroy=!0);this.ha()},destroyAll:function(b){return b===n?this.destroy(function(){return!0}):b?this.destroy(function(c){return 0<=a.a.o(b,c)}):[]},indexOf:function(b){var c=this();return a.a.o(c,b)},replace:function(a,c){var d=this.indexOf(a);0<=d&&(this.ia(),this.p()[d]=c,this.ha())}};a.a.la&&a.a.$a(a.ma.fn,a.O.fn);a.a.r("pop push reverse shift sort splice unshift".split(" "),function(b){a.ma.fn[b]=function(){var a=this.p();this.ia();this.Yb(a,b,arguments);
var d=a[b].apply(a,arguments);this.ha();return d===a?this:d}});a.a.r(["slice"],function(b){a.ma.fn[b]=function(){var a=this();return a[b].apply(a,arguments)}});a.b("observableArray",a.ma);a.Aa.trackArrayChanges=function(b,c){function d(){if(!e){e=!0;l=b.notifySubscribers;b.notifySubscribers=function(a,b){b&&b!==E||++h;return l.apply(this,arguments)};var c=[].concat(b.p()||[]);f=null;g=b.Y(function(d){d=[].concat(d||[]);if(b.Ra("arrayChange")){var e;if(!f||1<h)f=a.a.lb(c,d,b.kb);e=f}c=d;f=null;h=0;
e&&e.length&&b.notifySubscribers(e,"arrayChange")})}}b.kb={};c&&"object"==typeof c&&a.a.extend(b.kb,c);b.kb.sparse=!0;if(!b.Yb){var e=!1,f=null,g,h=0,l,m=b.ua,k=b.Ka;b.ua=function(a){m&&m.call(b,a);"arrayChange"===a&&d()};b.Ka=function(a){k&&k.call(b,a);"arrayChange"!==a||b.Ra("arrayChange")||(l&&(b.notifySubscribers=l,l=n),g.k(),e=!1)};b.Yb=function(b,c,d){function k(a,b,c){return m[m.length]={status:a,value:b,index:c}}if(e&&!h){var m=[],l=b.length,g=d.length,G=0;switch(c){case "push":G=l;case "unshift":for(c=
0;c<g;c++)k("added",d[c],G+c);break;case "pop":G=l-1;case "shift":l&&k("deleted",b[G],G);break;case "splice":c=Math.min(Math.max(0,0>d[0]?l+d[0]:d[0]),l);for(var l=1===g?l:Math.min(c+(d[1]||0),l),g=c+g-2,G=Math.max(l,g),n=[],s=[],w=2;c<G;++c,++w)c<l&&s.push(k("deleted",b[c],c)),c<g&&n.push(k("added",d[w],c));a.a.hc(s,n);break;default:return}f=m}}}};var s=a.a.bc("_state");a.m=a.B=function(b,c,d){function e(){if(0<arguments.length){if("function"===typeof f)f.apply(g.sb,arguments);else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
return this}a.l.sc(e);(g.V||g.t&&e.Sa())&&e.U();return g.M}"object"===typeof b?d=b:(d=d||{},b&&(d.read=b));if("function"!=typeof d.read)throw Error("Pass a function that returns the value of the ko.computed");var f=d.write,g={M:n,da:!0,V:!0,Ta:!1,Hb:!1,T:!1,Ya:!1,t:!1,od:d.read,sb:c||d.owner,i:d.disposeWhenNodeIsRemoved||d.i||null,ya:d.disposeWhen||d.ya,pb:null,s:{},L:0,fc:null};e[s]=g;e.$c="function"===typeof f;a.a.la||a.a.extend(e,a.K.fn);a.K.fn.ub(e);a.a.ab(e,z);d.pure?(g.Ya=!0,g.t=!0,a.a.extend(e,
Y)):d.deferEvaluation&&a.a.extend(e,Z);a.options.deferUpdates&&a.Aa.deferred(e,!0);g.i&&(g.Hb=!0,g.i.nodeType||(g.i=null));g.t||d.deferEvaluation||e.U();g.i&&e.ca()&&a.a.G.qa(g.i,g.pb=function(){e.k()});return e};var z={equalityComparer:J,Ca:function(){return this[s].L},Sb:function(a,c,d){if(this[s].Ya&&c===this)throw Error("A 'pure' computed must not be called recursively");this[s].s[a]=d;d.Ia=this[s].L++;d.pa=c.Pa()},Sa:function(){var a,c,d=this[s].s;for(a in d)if(d.hasOwnProperty(a)&&(c=d[a],this.oa&&
c.$.Ha||c.$.Zc(c.pa)))return!0},gd:function(){this.oa&&!this[s].Ta&&this.oa(!1)},ca:function(){var a=this[s];return a.V||0<a.L},qd:function(){this.Ha?this[s].V&&(this[s].da=!0):this.ec()},yc:function(a){if(a.gb&&!this[s].i){var c=a.Y(this.gd,this,"dirty"),d=a.Y(this.qd,this);return{$:a,k:function(){c.k();d.k()}}}return a.Y(this.ec,this)},ec:function(){var b=this,c=b.throttleEvaluation;c&&0<=c?(clearTimeout(this[s].fc),this[s].fc=a.a.setTimeout(function(){b.U(!0)},c)):b.oa?b.oa(!0):b.U(!0)},U:function(b){var c=
this[s],d=c.ya,e=!1;if(!c.Ta&&!c.T){if(c.i&&!a.a.qb(c.i)||d&&d()){if(!c.Hb){this.k();return}}else c.Hb=!1;c.Ta=!0;try{e=this.Vc(b)}finally{c.Ta=!1}c.L||this.k();return e}},Vc:function(b){var c=this[s],d=!1,e=c.Ya?n:!c.L,f={Mc:this,Oa:c.s,ob:c.L};a.l.Xb({Lc:f,jb:W,m:this,Va:e});c.s={};c.L=0;f=this.Uc(c,f);this.Ua(c.M,f)&&(c.t||this.notifySubscribers(c.M,"beforeChange"),c.M=f,c.t?this.Kb():b&&this.notifySubscribers(c.M),d=!0);e&&this.notifySubscribers(c.M,"awake");return d},Uc:function(b,c){try{var d=
b.od;return b.sb?d.call(b.sb):d()}finally{a.l.end(),c.ob&&!b.t&&a.a.D(c.Oa,V),b.da=b.V=!1}},p:function(a){var c=this[s];(c.V&&(a||!c.L)||c.t&&this.Sa())&&this.U();return c.M},Wa:function(b){a.K.fn.Wa.call(this,b);this.Mb=function(){this[s].da?this.U():this[s].V=!1;return this[s].M};this.oa=function(a){this.Ob(this[s].M);this[s].V=!0;a&&(this[s].da=!0);this.Pb(this)}},k:function(){var b=this[s];!b.t&&b.s&&a.a.D(b.s,function(a,b){b.k&&b.k()});b.i&&b.pb&&a.a.G.tc(b.i,b.pb);b.s=null;b.L=0;b.T=!0;b.da=
!1;b.V=!1;b.t=!1;b.i=null}},Y={ua:function(b){var c=this,d=c[s];if(!d.T&&d.t&&"change"==b){d.t=!1;if(d.da||c.Sa())d.s=null,d.L=0,c.U()&&c.Kb();else{var e=[];a.a.D(d.s,function(a,b){e[b.Ia]=a});a.a.r(e,function(a,b){var e=d.s[a],l=c.yc(e.$);l.Ia=b;l.pa=e.pa;d.s[a]=l})}d.T||c.notifySubscribers(d.M,"awake")}},Ka:function(b){var c=this[s];c.T||"change"!=b||this.Ra("change")||(a.a.D(c.s,function(a,b){b.k&&(c.s[a]={$:b.$,Ia:b.Ia,pa:b.pa},b.k())}),c.t=!0,this.notifySubscribers(n,"asleep"))},Pa:function(){var b=
this[s];b.t&&(b.da||this.Sa())&&this.U();return a.K.fn.Pa.call(this)}},Z={ua:function(a){"change"!=a&&"beforeChange"!=a||this.p()}};a.a.la&&a.a.$a(z,a.K.fn);var P=a.O.md;a.m[P]=a.O;z[P]=a.m;a.bd=function(b){return a.Qa(b,a.m)};a.cd=function(b){return a.Qa(b,a.m)&&b[s]&&b[s].Ya};a.b("computed",a.m);a.b("dependentObservable",a.m);a.b("isComputed",a.bd);a.b("isPureComputed",a.cd);a.b("computed.fn",z);a.H(z,"peek",z.p);a.H(z,"dispose",z.k);a.H(z,"isActive",z.ca);a.H(z,"getDependenciesCount",z.Ca);a.rc=
function(b,c){if("function"===typeof b)return a.m(b,c,{pure:!0});b=a.a.extend({},b);b.pure=!0;return a.m(b,c)};a.b("pureComputed",a.rc);(function(){function b(a,f,g){g=g||new d;a=f(a);if("object"!=typeof a||null===a||a===n||a instanceof RegExp||a instanceof Date||a instanceof String||a instanceof Number||a instanceof Boolean)return a;var h=a instanceof Array?[]:{};g.save(a,h);c(a,function(c){var d=f(a[c]);switch(typeof d){case "boolean":case "number":case "string":case "function":h[c]=d;break;case "object":case "undefined":var k=
g.get(d);h[c]=k!==n?k:b(d,f,g)}});return h}function c(a,b){if(a instanceof Array){for(var c=0;c<a.length;c++)b(c);"function"==typeof a.toJSON&&b("toJSON")}else for(c in a)b(c)}function d(){this.keys=[];this.Lb=[]}a.Ac=function(c){if(0==arguments.length)throw Error("When calling ko.toJS, pass the object you want to convert.");return b(c,function(b){for(var c=0;a.I(b)&&10>c;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.Ac(b);return a.a.Gb(b,c,d)};d.prototype={save:function(b,c){var d=a.a.o(this.keys,
b);0<=d?this.Lb[d]=c:(this.keys.push(b),this.Lb.push(c))},get:function(b){b=a.a.o(this.keys,b);return 0<=b?this.Lb[b]:n}}})();a.b("toJS",a.Ac);a.b("toJSON",a.toJSON);(function(){a.j={u:function(b){switch(a.a.A(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__?a.a.e.get(b,a.d.options.zb):7>=a.a.C?b.getAttributeNode("value")&&b.getAttributeNode("value").specified?b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex]):n;default:return b.value}},ja:function(b,
c,d){switch(a.a.A(b)){case "option":switch(typeof c){case "string":a.a.e.set(b,a.d.options.zb,n);"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__;b.value=c;break;default:a.a.e.set(b,a.d.options.zb,c),b.__ko__hasDomDataOptionValue__=!0,b.value="number"===typeof c?c:""}break;case "select":if(""===c||null===c)c=n;for(var e=-1,f=0,g=b.options.length,h;f<g;++f)if(h=a.j.u(b.options[f]),h==c||""==h&&c===n){e=f;break}if(d||0<=e||c===n&&1<b.size)b.selectedIndex=e;break;default:if(null===
c||c===n)c="";b.value=c}}}})();a.b("selectExtensions",a.j);a.b("selectExtensions.readValue",a.j.u);a.b("selectExtensions.writeValue",a.j.ja);a.h=function(){function b(b){b=a.a.cb(b);123===b.charCodeAt(0)&&(b=b.slice(1,-1));var c=[],d=b.match(e),r,h=[],p=0;if(d){d.push(",");for(var A=0,y;y=d[A];++A){var v=y.charCodeAt(0);if(44===v){if(0>=p){c.push(r&&h.length?{key:r,value:h.join("")}:{unknown:r||h.join("")});r=p=0;h=[];continue}}else if(58===v){if(!p&&!r&&1===h.length){r=h.pop();continue}}else 47===
v&&A&&1<y.length?(v=d[A-1].match(f))&&!g[v[0]]&&(b=b.substr(b.indexOf(y)+1),d=b.match(e),d.push(","),A=-1,y="/"):40===v||123===v||91===v?++p:41===v||125===v||93===v?--p:r||h.length||34!==v&&39!==v||(y=y.slice(1,-1));h.push(y)}}return c}var c=["true","false","null","undefined"],d=/^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i,e=RegExp("\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|/(?:[^/\\\\]|\\\\.)*/w*|[^\\s:,/][^,\"'{}()/:[\\]]*[^\\s,\"'{}()/:[\\]]|[^\\s]","g"),f=/[\])"'A-Za-z0-9_$]+$/,
g={"in":1,"return":1,"typeof":1},h={};return{va:[],ga:h,Ab:b,Xa:function(e,m){function k(b,e){var m;if(!A){var l=a.getBindingHandler(b);if(l&&l.preprocess&&!(e=l.preprocess(e,b,k)))return;if(l=h[b])m=e,0<=a.a.o(c,m)?m=!1:(l=m.match(d),m=null===l?!1:l[1]?"Object("+l[1]+")"+l[2]:m),l=m;l&&g.push("'"+b+"':function(_z){"+m+"=_z}")}p&&(e="function(){return "+e+" }");f.push("'"+b+"':"+e)}m=m||{};var f=[],g=[],p=m.valueAccessors,A=m.bindingParams,y="string"===typeof e?b(e):e;a.a.r(y,function(a){k(a.key||
a.unknown,a.value)});g.length&&k("_ko_property_writers","{"+g.join(",")+" }");return f.join(",")},fd:function(a,b){for(var c=0;c<a.length;c++)if(a[c].key==b)return!0;return!1},Ga:function(b,c,d,e,f){if(b&&a.I(b))!a.Da(b)||f&&b.p()===e||b(e);else if((b=c.get("_ko_property_writers"))&&b[d])b[d](e)}}}();a.b("expressionRewriting",a.h);a.b("expressionRewriting.bindingRewriteValidators",a.h.va);a.b("expressionRewriting.parseObjectLiteral",a.h.Ab);a.b("expressionRewriting.preProcessBindings",a.h.Xa);a.b("expressionRewriting._twoWayBindings",
a.h.ga);a.b("jsonExpressionRewriting",a.h);a.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson",a.h.Xa);(function(){function b(a){return 8==a.nodeType&&g.test(f?a.text:a.nodeValue)}function c(a){return 8==a.nodeType&&h.test(f?a.text:a.nodeValue)}function d(a,d){for(var e=a,f=1,l=[];e=e.nextSibling;){if(c(e)&&(f--,0===f))return l;l.push(e);b(e)&&f++}if(!d)throw Error("Cannot find closing comment tag to match: "+a.nodeValue);return null}function e(a,b){var c=d(a,b);return c?0<c.length?c[c.length-
1].nextSibling:a.nextSibling:null}var f=t&&"\x3c!--test--\x3e"===t.createComment("test").text,g=f?/^\x3c!--\s*ko(?:\s+([\s\S]+))?\s*--\x3e$/:/^\s*ko(?:\s+([\s\S]+))?\s*$/,h=f?/^\x3c!--\s*\/ko\s*--\x3e$/:/^\s*\/ko\s*$/,l={ul:!0,ol:!0};a.f={aa:{},childNodes:function(a){return b(a)?d(a):a.childNodes},za:function(c){if(b(c)){c=a.f.childNodes(c);for(var d=0,e=c.length;d<e;d++)a.removeNode(c[d])}else a.a.rb(c)},fa:function(c,d){if(b(c)){a.f.za(c);for(var e=c.nextSibling,f=0,l=d.length;f<l;f++)e.parentNode.insertBefore(d[f],
e)}else a.a.fa(c,d)},qc:function(a,c){b(a)?a.parentNode.insertBefore(c,a.nextSibling):a.firstChild?a.insertBefore(c,a.firstChild):a.appendChild(c)},kc:function(c,d,e){e?b(c)?c.parentNode.insertBefore(d,e.nextSibling):e.nextSibling?c.insertBefore(d,e.nextSibling):c.appendChild(d):a.f.qc(c,d)},firstChild:function(a){return b(a)?!a.nextSibling||c(a.nextSibling)?null:a.nextSibling:a.firstChild},nextSibling:function(a){b(a)&&(a=e(a));return a.nextSibling&&c(a.nextSibling)?null:a.nextSibling},Yc:b,vd:function(a){return(a=
(f?a.text:a.nodeValue).match(g))?a[1]:null},oc:function(d){if(l[a.a.A(d)]){var k=d.firstChild;if(k){do if(1===k.nodeType){var f;f=k.firstChild;var g=null;if(f){do if(g)g.push(f);else if(b(f)){var h=e(f,!0);h?f=h:g=[f]}else c(f)&&(g=[f]);while(f=f.nextSibling)}if(f=g)for(g=k.nextSibling,h=0;h<f.length;h++)g?d.insertBefore(f[h],g):d.appendChild(f[h])}while(k=k.nextSibling)}}}}})();a.b("virtualElements",a.f);a.b("virtualElements.allowedBindings",a.f.aa);a.b("virtualElements.emptyNode",a.f.za);a.b("virtualElements.insertAfter",
a.f.kc);a.b("virtualElements.prepend",a.f.qc);a.b("virtualElements.setDomNodeChildren",a.f.fa);(function(){a.S=function(){this.Kc={}};a.a.extend(a.S.prototype,{nodeHasBindings:function(b){switch(b.nodeType){case 1:return null!=b.getAttribute("data-bind")||a.g.getComponentNameForNode(b);case 8:return a.f.Yc(b);default:return!1}},getBindings:function(b,c){var d=this.getBindingsString(b,c),d=d?this.parseBindingsString(d,c,b):null;return a.g.Rb(d,b,c,!1)},getBindingAccessors:function(b,c){var d=this.getBindingsString(b,
c),d=d?this.parseBindingsString(d,c,b,{valueAccessors:!0}):null;return a.g.Rb(d,b,c,!0)},getBindingsString:function(b){switch(b.nodeType){case 1:return b.getAttribute("data-bind");case 8:return a.f.vd(b);default:return null}},parseBindingsString:function(b,c,d,e){try{var f=this.Kc,g=b+(e&&e.valueAccessors||""),h;if(!(h=f[g])){var l,m="with($context){with($data||{}){return{"+a.h.Xa(b,e)+"}}}";l=new Function("$context","$element",m);h=f[g]=l}return h(c,d)}catch(k){throw k.message="Unable to parse bindings.\nBindings value: "+
b+"\nMessage: "+k.message,k;}}});a.S.instance=new a.S})();a.b("bindingProvider",a.S);(function(){function b(a){return function(){return a}}function c(a){return a()}function d(b){return a.a.Ea(a.l.w(b),function(a,c){return function(){return b()[c]}})}function e(c,e,k){return"function"===typeof c?d(c.bind(null,e,k)):a.a.Ea(c,b)}function f(a,b){return d(this.getBindings.bind(this,a,b))}function g(b,c,d){var e,k=a.f.firstChild(c),f=a.S.instance,m=f.preprocessNode;if(m){for(;e=k;)k=a.f.nextSibling(e),
m.call(f,e);k=a.f.firstChild(c)}for(;e=k;)k=a.f.nextSibling(e),h(b,e,d)}function h(b,c,d){var e=!0,k=1===c.nodeType;k&&a.f.oc(c);if(k&&d||a.S.instance.nodeHasBindings(c))e=m(c,null,b,d).shouldBindDescendants;e&&!r[a.a.A(c)]&&g(b,c,!k)}function l(b){var c=[],d={},e=[];a.a.D(b,function X(k){if(!d[k]){var f=a.getBindingHandler(k);f&&(f.after&&(e.push(k),a.a.r(f.after,function(c){if(b[c]){if(-1!==a.a.o(e,c))throw Error("Cannot combine the following bindings, because they have a cyclic dependency: "+e.join(", "));
X(c)}}),e.length--),c.push({key:k,jc:f}));d[k]=!0}});return c}function m(b,d,e,k){var m=a.a.e.get(b,q);if(!d){if(m)throw Error("You cannot apply bindings multiple times to the same element.");a.a.e.set(b,q,!0)}!m&&k&&a.xc(b,e);var g;if(d&&"function"!==typeof d)g=d;else{var h=a.S.instance,r=h.getBindingAccessors||f,p=a.B(function(){(g=d?d(e,b):r.call(h,b,e))&&e.Q&&e.Q();return g},null,{i:b});g&&p.ca()||(p=null)}var s;if(g){var t=p?function(a){return function(){return c(p()[a])}}:function(a){return g[a]},
u=function(){return a.a.Ea(p?p():g,c)};u.get=function(a){return g[a]&&c(t(a))};u.has=function(a){return a in g};k=l(g);a.a.r(k,function(c){var d=c.jc.init,k=c.jc.update,f=c.key;if(8===b.nodeType&&!a.f.aa[f])throw Error("The binding '"+f+"' cannot be used with virtual elements");try{"function"==typeof d&&a.l.w(function(){var a=d(b,t(f),u,e.$data,e);if(a&&a.controlsDescendantBindings){if(s!==n)throw Error("Multiple bindings ("+s+" and "+f+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");
s=f}}),"function"==typeof k&&a.B(function(){k(b,t(f),u,e.$data,e)},null,{i:b})}catch(m){throw m.message='Unable to process binding "'+f+": "+g[f]+'"\nMessage: '+m.message,m;}})}return{shouldBindDescendants:s===n}}function k(b){return b&&b instanceof a.R?b:new a.R(b)}a.d={};var r={script:!0,textarea:!0,template:!0};a.getBindingHandler=function(b){return a.d[b]};a.R=function(b,c,d,e,k){function f(){var k=g?b():b,m=a.a.c(k);c?(c.Q&&c.Q(),a.a.extend(l,c),l.Q=r):(l.$parents=[],l.$root=m,l.ko=a);l.$rawData=
k;l.$data=m;d&&(l[d]=m);e&&e(l,c,m);return l.$data}function m(){return h&&!a.a.Tb(h)}var l=this,g="function"==typeof b&&!a.I(b),h,r;k&&k.exportDependencies?f():(r=a.B(f,null,{ya:m,i:!0}),r.ca()&&(l.Q=r,r.equalityComparer=null,h=[],r.Dc=function(b){h.push(b);a.a.G.qa(b,function(b){a.a.Na(h,b);h.length||(r.k(),l.Q=r=n)})}))};a.R.prototype.createChildContext=function(b,c,d,e){return new a.R(b,this,c,function(a,b){a.$parentContext=b;a.$parent=b.$data;a.$parents=(b.$parents||[]).slice(0);a.$parents.unshift(a.$parent);
d&&d(a)},e)};a.R.prototype.extend=function(b){return new a.R(this.Q||this.$data,this,null,function(c,d){c.$rawData=d.$rawData;a.a.extend(c,"function"==typeof b?b():b)})};a.R.prototype.ac=function(a,b){return this.createChildContext(a,b,null,{exportDependencies:!0})};var q=a.a.e.J(),p=a.a.e.J();a.xc=function(b,c){if(2==arguments.length)a.a.e.set(b,p,c),c.Q&&c.Q.Dc(b);else return a.a.e.get(b,p)};a.La=function(b,c,d){1===b.nodeType&&a.f.oc(b);return m(b,c,k(d),!0)};a.Ic=function(b,c,d){d=k(d);return a.La(b,
e(c,d,b),d)};a.hb=function(a,b){1!==b.nodeType&&8!==b.nodeType||g(k(a),b,!0)};a.Ub=function(a,b){!u&&x.jQuery&&(u=x.jQuery);if(b&&1!==b.nodeType&&8!==b.nodeType)throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");b=b||x.document.body;h(k(a),b,!0)};a.nb=function(b){switch(b.nodeType){case 1:case 8:var c=a.xc(b);if(c)return c;if(b.parentNode)return a.nb(b.parentNode)}return n};a.Oc=function(b){return(b=a.nb(b))?b.$data:n};a.b("bindingHandlers",
a.d);a.b("applyBindings",a.Ub);a.b("applyBindingsToDescendants",a.hb);a.b("applyBindingAccessorsToNode",a.La);a.b("applyBindingsToNode",a.Ic);a.b("contextFor",a.nb);a.b("dataFor",a.Oc)})();(function(b){function c(c,e){var m=f.hasOwnProperty(c)?f[c]:b,k;m?m.Y(e):(m=f[c]=new a.K,m.Y(e),d(c,function(b,d){var e=!(!d||!d.synchronous);g[c]={definition:b,dd:e};delete f[c];k||e?m.notifySubscribers(b):a.Z.Za(function(){m.notifySubscribers(b)})}),k=!0)}function d(a,b){e("getConfig",[a],function(c){c?e("loadComponent",
[a,c],function(a){b(a,c)}):b(null,null)})}function e(c,d,f,k){k||(k=a.g.loaders.slice(0));var g=k.shift();if(g){var q=g[c];if(q){var p=!1;if(q.apply(g,d.concat(function(a){p?f(null):null!==a?f(a):e(c,d,f,k)}))!==b&&(p=!0,!g.suppressLoaderExceptions))throw Error("Component loaders must supply values by invoking the callback, not by returning values synchronously.");}else e(c,d,f,k)}else f(null)}var f={},g={};a.g={get:function(d,e){var f=g.hasOwnProperty(d)?g[d]:b;f?f.dd?a.l.w(function(){e(f.definition)}):
a.Z.Za(function(){e(f.definition)}):c(d,e)},$b:function(a){delete g[a]},Nb:e};a.g.loaders=[];a.b("components",a.g);a.b("components.get",a.g.get);a.b("components.clearCachedDefinition",a.g.$b)})();(function(){function b(b,c,d,e){function g(){0===--y&&e(h)}var h={},y=2,v=d.template;d=d.viewModel;v?f(c,v,function(c){a.g.Nb("loadTemplate",[b,c],function(a){h.template=a;g()})}):g();d?f(c,d,function(c){a.g.Nb("loadViewModel",[b,c],function(a){h[l]=a;g()})}):g()}function c(a,b,d){if("function"===typeof b)d(function(a){return new b(a)});
else if("function"===typeof b[l])d(b[l]);else if("instance"in b){var e=b.instance;d(function(){return e})}else"viewModel"in b?c(a,b.viewModel,d):a("Unknown viewModel value: "+b)}function d(b){switch(a.a.A(b)){case "script":return a.a.na(b.text);case "textarea":return a.a.na(b.value);case "template":if(e(b.content))return a.a.wa(b.content.childNodes)}return a.a.wa(b.childNodes)}function e(a){return x.DocumentFragment?a instanceof DocumentFragment:a&&11===a.nodeType}function f(a,b,c){"string"===typeof b.require?
O||x.require?(O||x.require)([b.require],c):a("Uses require, but no AMD loader is present"):c(b)}function g(a){return function(b){throw Error("Component '"+a+"': "+b);}}var h={};a.g.register=function(b,c){if(!c)throw Error("Invalid configuration for "+b);if(a.g.wb(b))throw Error("Component "+b+" is already registered");h[b]=c};a.g.wb=function(a){return h.hasOwnProperty(a)};a.g.ud=function(b){delete h[b];a.g.$b(b)};a.g.cc={getConfig:function(a,b){b(h.hasOwnProperty(a)?h[a]:null)},loadComponent:function(a,
c,d){var e=g(a);f(e,c,function(c){b(a,e,c,d)})},loadTemplate:function(b,c,f){b=g(b);if("string"===typeof c)f(a.a.na(c));else if(c instanceof Array)f(c);else if(e(c))f(a.a.W(c.childNodes));else if(c.element)if(c=c.element,x.HTMLElement?c instanceof HTMLElement:c&&c.tagName&&1===c.nodeType)f(d(c));else if("string"===typeof c){var l=t.getElementById(c);l?f(d(l)):b("Cannot find element with ID "+c)}else b("Unknown element type: "+c);else b("Unknown template value: "+c)},loadViewModel:function(a,b,d){c(g(a),
b,d)}};var l="createViewModel";a.b("components.register",a.g.register);a.b("components.isRegistered",a.g.wb);a.b("components.unregister",a.g.ud);a.b("components.defaultLoader",a.g.cc);a.g.loaders.push(a.g.cc);a.g.Ec=h})();(function(){function b(b,e){var f=b.getAttribute("params");if(f){var f=c.parseBindingsString(f,e,b,{valueAccessors:!0,bindingParams:!0}),f=a.a.Ea(f,function(c){return a.m(c,null,{i:b})}),g=a.a.Ea(f,function(c){var e=c.p();return c.ca()?a.m({read:function(){return a.a.c(c())},write:a.Da(e)&&
function(a){c()(a)},i:b}):e});g.hasOwnProperty("$raw")||(g.$raw=f);return g}return{$raw:{}}}a.g.getComponentNameForNode=function(b){var c=a.a.A(b);if(a.g.wb(c)&&(-1!=c.indexOf("-")||"[object HTMLUnknownElement]"==""+b||8>=a.a.C&&b.tagName===c))return c};a.g.Rb=function(c,e,f,g){if(1===e.nodeType){var h=a.g.getComponentNameForNode(e);if(h){c=c||{};if(c.component)throw Error('Cannot use the "component" binding on a custom element matching a component');var l={name:h,params:b(e,f)};c.component=g?function(){return l}:
l}}return c};var c=new a.S;9>a.a.C&&(a.g.register=function(a){return function(b){t.createElement(b);return a.apply(this,arguments)}}(a.g.register),t.createDocumentFragment=function(b){return function(){var c=b(),f=a.g.Ec,g;for(g in f)f.hasOwnProperty(g)&&c.createElement(g);return c}}(t.createDocumentFragment))})();(function(b){function c(b,c,d){c=c.template;if(!c)throw Error("Component '"+b+"' has no template");b=a.a.wa(c);a.f.fa(d,b)}function d(a,b,c,d){var e=a.createViewModel;return e?e.call(a,
d,{element:b,templateNodes:c}):d}var e=0;a.d.component={init:function(f,g,h,l,m){function k(){var a=r&&r.dispose;"function"===typeof a&&a.call(r);q=r=null}var r,q,p=a.a.W(a.f.childNodes(f));a.a.G.qa(f,k);a.m(function(){var l=a.a.c(g()),h,v;"string"===typeof l?h=l:(h=a.a.c(l.name),v=a.a.c(l.params));if(!h)throw Error("No component name specified");var n=q=++e;a.g.get(h,function(e){if(q===n){k();if(!e)throw Error("Unknown component '"+h+"'");c(h,e,f);var l=d(e,f,p,v);e=m.createChildContext(l,b,function(a){a.$component=
l;a.$componentTemplateNodes=p});r=l;a.hb(e,f)}})},null,{i:f});return{controlsDescendantBindings:!0}}};a.f.aa.component=!0})();var Q={"class":"className","for":"htmlFor"};a.d.attr={update:function(b,c){var d=a.a.c(c())||{};a.a.D(d,function(c,d){d=a.a.c(d);var g=!1===d||null===d||d===n;g&&b.removeAttribute(c);8>=a.a.C&&c in Q?(c=Q[c],g?b.removeAttribute(c):b[c]=d):g||b.setAttribute(c,d.toString());"name"===c&&a.a.vc(b,g?"":d.toString())})}};(function(){a.d.checked={after:["value","attr"],init:function(b,
c,d){function e(){var e=b.checked,f=p?g():e;if(!a.xa.Va()&&(!l||e)){var h=a.l.w(c);if(k){var m=r?h.p():h;q!==f?(e&&(a.a.ra(m,f,!0),a.a.ra(m,q,!1)),q=f):a.a.ra(m,f,e);r&&a.Da(h)&&h(m)}else a.h.Ga(h,d,"checked",f,!0)}}function f(){var d=a.a.c(c());b.checked=k?0<=a.a.o(d,g()):h?d:g()===d}var g=a.rc(function(){return d.has("checkedValue")?a.a.c(d.get("checkedValue")):d.has("value")?a.a.c(d.get("value")):b.value}),h="checkbox"==b.type,l="radio"==b.type;if(h||l){var m=c(),k=h&&a.a.c(m)instanceof Array,
r=!(k&&m.push&&m.splice),q=k?g():n,p=l||k;l&&!b.name&&a.d.uniqueName.init(b,function(){return!0});a.m(e,null,{i:b});a.a.q(b,"click",e);a.m(f,null,{i:b});m=n}}};a.h.ga.checked=!0;a.d.checkedValue={update:function(b,c){b.value=a.a.c(c())}}})();a.d.css={update:function(b,c){var d=a.a.c(c());null!==d&&"object"==typeof d?a.a.D(d,function(c,d){d=a.a.c(d);a.a.fb(b,c,d)}):(d=a.a.cb(String(d||"")),a.a.fb(b,b.__ko__cssValue,!1),b.__ko__cssValue=d,a.a.fb(b,d,!0))}};a.d.enable={update:function(b,c){var d=a.a.c(c());
d&&b.disabled?b.removeAttribute("disabled"):d||b.disabled||(b.disabled=!0)}};a.d.disable={update:function(b,c){a.d.enable.update(b,function(){return!a.a.c(c())})}};a.d.event={init:function(b,c,d,e,f){var g=c()||{};a.a.D(g,function(g){"string"==typeof g&&a.a.q(b,g,function(b){var m,k=c()[g];if(k){try{var r=a.a.W(arguments);e=f.$data;r.unshift(e);m=k.apply(e,r)}finally{!0!==m&&(b.preventDefault?b.preventDefault():b.returnValue=!1)}!1===d.get(g+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};
a.d.foreach={mc:function(b){return function(){var c=b(),d=a.a.Bb(c);if(!d||"number"==typeof d.length)return{foreach:c,templateEngine:a.X.vb};a.a.c(c);return{foreach:d.data,as:d.as,includeDestroyed:d.includeDestroyed,afterAdd:d.afterAdd,beforeRemove:d.beforeRemove,afterRender:d.afterRender,beforeMove:d.beforeMove,afterMove:d.afterMove,templateEngine:a.X.vb}}},init:function(b,c){return a.d.template.init(b,a.d.foreach.mc(c))},update:function(b,c,d,e,f){return a.d.template.update(b,a.d.foreach.mc(c),
d,e,f)}};a.h.va.foreach=!1;a.f.aa.foreach=!0;a.d.hasfocus={init:function(b,c,d){function e(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(k){g=f.body}e=g===b}f=c();a.h.Ga(f,d,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var f=e.bind(null,!0),g=e.bind(null,!1);a.a.q(b,"focus",f);a.a.q(b,"focusin",f);a.a.q(b,"blur",g);a.a.q(b,"focusout",g)},update:function(b,c){var d=!!a.a.c(c());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue===
d||(d?b.focus():b.blur(),!d&&b.__ko_hasfocusLastValue&&b.ownerDocument.body.focus(),a.l.w(a.a.Fa,null,[b,d?"focusin":"focusout"]))}};a.h.ga.hasfocus=!0;a.d.hasFocus=a.d.hasfocus;a.h.ga.hasFocus=!0;a.d.html={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Eb(b,c())}};K("if");K("ifnot",!1,!0);K("with",!0,!1,function(a,c){return a.ac(c)});var L={};a.d.options={init:function(b){if("select"!==a.a.A(b))throw Error("options binding applies only to SELECT elements");for(;0<
b.length;)b.remove(0);return{controlsDescendantBindings:!0}},update:function(b,c,d){function e(){return a.a.Ma(b.options,function(a){return a.selected})}function f(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c}function g(c,e){if(A&&k)a.j.ja(b,a.a.c(d.get("value")),!0);else if(p.length){var f=0<=a.a.o(p,a.j.u(e[0]));a.a.wc(e[0],f);A&&!f&&a.l.w(a.a.Fa,null,[b,"change"])}}var h=b.multiple,l=0!=b.length&&h?b.scrollTop:null,m=a.a.c(c()),k=d.get("valueAllowUnset")&&d.has("value"),r=
d.get("optionsIncludeDestroyed");c={};var q,p=[];k||(h?p=a.a.ib(e(),a.j.u):0<=b.selectedIndex&&p.push(a.j.u(b.options[b.selectedIndex])));m&&("undefined"==typeof m.length&&(m=[m]),q=a.a.Ma(m,function(b){return r||b===n||null===b||!a.a.c(b._destroy)}),d.has("optionsCaption")&&(m=a.a.c(d.get("optionsCaption")),null!==m&&m!==n&&q.unshift(L)));var A=!1;c.beforeRemove=function(a){b.removeChild(a)};m=g;d.has("optionsAfterRender")&&"function"==typeof d.get("optionsAfterRender")&&(m=function(b,c){g(0,c);
a.l.w(d.get("optionsAfterRender"),null,[c[0],b!==L?b:n])});a.a.Db(b,q,function(c,e,g){g.length&&(p=!k&&g[0].selected?[a.j.u(g[0])]:[],A=!0);e=b.ownerDocument.createElement("option");c===L?(a.a.bb(e,d.get("optionsCaption")),a.j.ja(e,n)):(g=f(c,d.get("optionsValue"),c),a.j.ja(e,a.a.c(g)),c=f(c,d.get("optionsText"),g),a.a.bb(e,c));return[e]},c,m);a.l.w(function(){k?a.j.ja(b,a.a.c(d.get("value")),!0):(h?p.length&&e().length<p.length:p.length&&0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex])!==p[0]:
p.length||0<=b.selectedIndex)&&a.a.Fa(b,"change")});a.a.Sc(b);l&&20<Math.abs(l-b.scrollTop)&&(b.scrollTop=l)}};a.d.options.zb=a.a.e.J();a.d.selectedOptions={after:["options","foreach"],init:function(b,c,d){a.a.q(b,"change",function(){var e=c(),f=[];a.a.r(b.getElementsByTagName("option"),function(b){b.selected&&f.push(a.j.u(b))});a.h.Ga(e,d,"selectedOptions",f)})},update:function(b,c){if("select"!=a.a.A(b))throw Error("values binding applies only to SELECT elements");var d=a.a.c(c()),e=b.scrollTop;
d&&"number"==typeof d.length&&a.a.r(b.getElementsByTagName("option"),function(b){var c=0<=a.a.o(d,a.j.u(b));b.selected!=c&&a.a.wc(b,c)});b.scrollTop=e}};a.h.ga.selectedOptions=!0;a.d.style={update:function(b,c){var d=a.a.c(c()||{});a.a.D(d,function(c,d){d=a.a.c(d);if(null===d||d===n||!1===d)d="";b.style[c]=d})}};a.d.submit={init:function(b,c,d,e,f){if("function"!=typeof c())throw Error("The value for a submit binding must be a function");a.a.q(b,"submit",function(a){var d,e=c();try{d=e.call(f.$data,
b)}finally{!0!==d&&(a.preventDefault?a.preventDefault():a.returnValue=!1)}})}};a.d.text={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.bb(b,c())}};a.f.aa.text=!0;(function(){if(x&&x.navigator)var b=function(a){if(a)return parseFloat(a[1])},c=x.opera&&x.opera.version&&parseInt(x.opera.version()),d=x.navigator.userAgent,e=b(d.match(/^(?:(?!chrome).)*version\/([^ ]*) safari/i)),f=b(d.match(/Firefox\/([^ ]*)/));if(10>a.a.C)var g=a.a.e.J(),h=a.a.e.J(),l=function(b){var c=
this.activeElement;(c=c&&a.a.e.get(c,h))&&c(b)},m=function(b,c){var d=b.ownerDocument;a.a.e.get(d,g)||(a.a.e.set(d,g,!0),a.a.q(d,"selectionchange",l));a.a.e.set(b,h,c)};a.d.textInput={init:function(b,d,g){function l(c,d){a.a.q(b,c,d)}function h(){var c=a.a.c(d());if(null===c||c===n)c="";u!==n&&c===u?a.a.setTimeout(h,4):b.value!==c&&(s=c,b.value=c)}function y(){t||(u=b.value,t=a.a.setTimeout(v,4))}function v(){clearTimeout(t);u=t=n;var c=b.value;s!==c&&(s=c,a.h.Ga(d(),g,"textInput",c))}var s=b.value,
t,u,x=9==a.a.C?y:v;10>a.a.C?(l("propertychange",function(a){"value"===a.propertyName&&x(a)}),8==a.a.C&&(l("keyup",v),l("keydown",v)),8<=a.a.C&&(m(b,x),l("dragend",y))):(l("input",v),5>e&&"textarea"===a.a.A(b)?(l("keydown",y),l("paste",y),l("cut",y)):11>c?l("keydown",y):4>f&&(l("DOMAutoComplete",v),l("dragdrop",v),l("drop",v)));l("change",v);a.m(h,null,{i:b})}};a.h.ga.textInput=!0;a.d.textinput={preprocess:function(a,b,c){c("textInput",a)}}})();a.d.uniqueName={init:function(b,c){if(c()){var d="ko_unique_"+
++a.d.uniqueName.Nc;a.a.vc(b,d)}}};a.d.uniqueName.Nc=0;a.d.value={after:["options","foreach"],init:function(b,c,d){if("input"!=b.tagName.toLowerCase()||"checkbox"!=b.type&&"radio"!=b.type){var e=["change"],f=d.get("valueUpdate"),g=!1,h=null;f&&("string"==typeof f&&(f=[f]),a.a.ta(e,f),e=a.a.Wb(e));var l=function(){h=null;g=!1;var e=c(),f=a.j.u(b);a.h.Ga(e,d,"value",f)};!a.a.C||"input"!=b.tagName.toLowerCase()||"text"!=b.type||"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.o(e,"propertychange")||
(a.a.q(b,"propertychange",function(){g=!0}),a.a.q(b,"focus",function(){g=!1}),a.a.q(b,"blur",function(){g&&l()}));a.a.r(e,function(c){var d=l;a.a.sd(c,"after")&&(d=function(){h=a.j.u(b);a.a.setTimeout(l,0)},c=c.substring(5));a.a.q(b,c,d)});var m=function(){var e=a.a.c(c()),f=a.j.u(b);if(null!==h&&e===h)a.a.setTimeout(m,0);else if(e!==f)if("select"===a.a.A(b)){var g=d.get("valueAllowUnset"),f=function(){a.j.ja(b,e,g)};f();g||e===a.j.u(b)?a.a.setTimeout(f,0):a.l.w(a.a.Fa,null,[b,"change"])}else a.j.ja(b,
e)};a.m(m,null,{i:b})}else a.La(b,{checkedValue:c})},update:function(){}};a.h.ga.value=!0;a.d.visible={update:function(b,c){var d=a.a.c(c()),e="none"!=b.style.display;d&&!e?b.style.display="":!d&&e&&(b.style.display="none")}};(function(b){a.d[b]={init:function(c,d,e,f,g){return a.d.event.init.call(this,c,function(){var a={};a[b]=d();return a},e,f,g)}}})("click");a.P=function(){};a.P.prototype.renderTemplateSource=function(){throw Error("Override renderTemplateSource");};a.P.prototype.createJavaScriptEvaluatorBlock=
function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.P.prototype.makeTemplateSource=function(b,c){if("string"==typeof b){c=c||t;var d=c.getElementById(b);if(!d)throw Error("Cannot find template with ID "+b);return new a.v.n(d)}if(1==b.nodeType||8==b.nodeType)return new a.v.sa(b);throw Error("Unknown template type: "+b);};a.P.prototype.renderTemplate=function(a,c,d,e){a=this.makeTemplateSource(a,e);return this.renderTemplateSource(a,c,d,e)};a.P.prototype.isTemplateRewritten=function(a,
c){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,c).data("isRewritten")};a.P.prototype.rewriteTemplate=function(a,c,d){a=this.makeTemplateSource(a,d);c=c(a.text());a.text(c);a.data("isRewritten",!0)};a.b("templateEngine",a.P);a.Ib=function(){function b(b,c,d,h){b=a.h.Ab(b);for(var l=a.h.va,m=0;m<b.length;m++){var k=b[m].key;if(l.hasOwnProperty(k)){var r=l[k];if("function"===typeof r){if(k=r(b[m].value))throw Error(k);}else if(!r)throw Error("This template engine does not support the '"+
k+"' binding within its templates");}}d="ko.__tr_ambtns(function($context,$element){return(function(){return{ "+a.h.Xa(b,{valueAccessors:!0})+" } })()},'"+d.toLowerCase()+"')";return h.createJavaScriptEvaluatorBlock(d)+c}var c=/(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,d=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{Tc:function(b,c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.Ib.jd(b,
c)},d)},jd:function(a,f){return a.replace(c,function(a,c,d,e,k){return b(k,c,d,f)}).replace(d,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",f)})},Jc:function(b,c){return a.N.yb(function(d,h){var l=d.nextSibling;l&&l.nodeName.toLowerCase()===c&&a.La(l,b,h)})}}}();a.b("__tr_ambtns",a.Ib.Jc);(function(){a.v={};a.v.n=function(b){if(this.n=b){var c=a.a.A(b);this.eb="script"===c?1:"textarea"===c?2:"template"==c&&b.content&&11===b.content.nodeType?3:4}};a.v.n.prototype.text=function(){var b=1===
this.eb?"text":2===this.eb?"value":"innerHTML";if(0==arguments.length)return this.n[b];var c=arguments[0];"innerHTML"===b?a.a.Eb(this.n,c):this.n[b]=c};var b=a.a.e.J()+"_";a.v.n.prototype.data=function(c){if(1===arguments.length)return a.a.e.get(this.n,b+c);a.a.e.set(this.n,b+c,arguments[1])};var c=a.a.e.J();a.v.n.prototype.nodes=function(){var b=this.n;if(0==arguments.length)return(a.a.e.get(b,c)||{}).mb||(3===this.eb?b.content:4===this.eb?b:n);a.a.e.set(b,c,{mb:arguments[0]})};a.v.sa=function(a){this.n=
a};a.v.sa.prototype=new a.v.n;a.v.sa.prototype.text=function(){if(0==arguments.length){var b=a.a.e.get(this.n,c)||{};b.Jb===n&&b.mb&&(b.Jb=b.mb.innerHTML);return b.Jb}a.a.e.set(this.n,c,{Jb:arguments[0]})};a.b("templateSources",a.v);a.b("templateSources.domElement",a.v.n);a.b("templateSources.anonymousTemplate",a.v.sa)})();(function(){function b(b,c,d){var e;for(c=a.f.nextSibling(c);b&&(e=b)!==c;)b=a.f.nextSibling(e),d(e,b)}function c(c,d){if(c.length){var e=c[0],f=c[c.length-1],g=e.parentNode,h=
a.S.instance,n=h.preprocessNode;if(n){b(e,f,function(a,b){var c=a.previousSibling,d=n.call(h,a);d&&(a===e&&(e=d[0]||b),a===f&&(f=d[d.length-1]||c))});c.length=0;if(!e)return;e===f?c.push(e):(c.push(e,f),a.a.Ba(c,g))}b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.Ub(d,b)});b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.N.Cc(b,[d])});a.a.Ba(c,g)}}function d(a){return a.nodeType?a:0<a.length?a[0]:null}function e(b,e,f,h,q){q=q||{};var p=(b&&d(b)||f||{}).ownerDocument,n=q.templateEngine||g;
a.Ib.Tc(f,n,p);f=n.renderTemplate(f,h,q,p);if("number"!=typeof f.length||0<f.length&&"number"!=typeof f[0].nodeType)throw Error("Template engine must return an array of DOM nodes");p=!1;switch(e){case "replaceChildren":a.f.fa(b,f);p=!0;break;case "replaceNode":a.a.uc(b,f);p=!0;break;case "ignoreTargetNode":break;default:throw Error("Unknown renderMode: "+e);}p&&(c(f,h),q.afterRender&&a.l.w(q.afterRender,null,[f,h.$data]));return f}function f(b,c,d){return a.I(b)?b():"function"===typeof b?b(c,d):b}
var g;a.Fb=function(b){if(b!=n&&!(b instanceof a.P))throw Error("templateEngine must inherit from ko.templateEngine");g=b};a.Cb=function(b,c,k,h,q){k=k||{};if((k.templateEngine||g)==n)throw Error("Set a template engine before calling renderTemplate");q=q||"replaceChildren";if(h){var p=d(h);return a.B(function(){var g=c&&c instanceof a.R?c:new a.R(c,null,null,null,{exportDependencies:!0}),n=f(b,g.$data,g),g=e(h,q,n,g,k);"replaceNode"==q&&(h=g,p=d(h))},null,{ya:function(){return!p||!a.a.qb(p)},i:p&&
"replaceNode"==q?p.parentNode:p})}return a.N.yb(function(d){a.Cb(b,c,k,d,"replaceNode")})};a.pd=function(b,d,g,h,q){function p(a,b){c(b,t);g.afterRender&&g.afterRender(b,a);t=null}function s(a,c){t=q.createChildContext(a,g.as,function(a){a.$index=c});var d=f(b,a,t);return e(null,"ignoreTargetNode",d,t,g)}var t;return a.B(function(){var b=a.a.c(d)||[];"undefined"==typeof b.length&&(b=[b]);b=a.a.Ma(b,function(b){return g.includeDestroyed||b===n||null===b||!a.a.c(b._destroy)});a.l.w(a.a.Db,null,[h,b,
s,g,p])},null,{i:h})};var h=a.a.e.J();a.d.template={init:function(b,c){var d=a.a.c(c());if("string"==typeof d||d.name)a.f.za(b);else{if("nodes"in d){if(d=d.nodes||[],a.I(d))throw Error('The "nodes" option must be a plain, non-observable array.');}else d=a.f.childNodes(b);d=a.a.nc(d);(new a.v.sa(b)).nodes(d)}return{controlsDescendantBindings:!0}},update:function(b,c,d,e,f){var g=c();c=a.a.c(g);d=!0;e=null;"string"==typeof c?c={}:(g=c.name,"if"in c&&(d=a.a.c(c["if"])),d&&"ifnot"in c&&(d=!a.a.c(c.ifnot)));
"foreach"in c?e=a.pd(g||b,d&&c.foreach||[],c,b,f):d?(f="data"in c?f.ac(c.data,c.as):f,e=a.Cb(g||b,f,c,b)):a.f.za(b);f=e;(c=a.a.e.get(b,h))&&"function"==typeof c.k&&c.k();a.a.e.set(b,h,f&&f.ca()?f:n)}};a.h.va.template=function(b){b=a.h.Ab(b);return 1==b.length&&b[0].unknown||a.h.fd(b,"name")?null:"This template engine does not support anonymous templates nested within its templates"};a.f.aa.template=!0})();a.b("setTemplateEngine",a.Fb);a.b("renderTemplate",a.Cb);a.a.hc=function(a,c,d){if(a.length&&
c.length){var e,f,g,h,l;for(e=f=0;(!d||e<d)&&(h=a[f]);++f){for(g=0;l=c[g];++g)if(h.value===l.value){h.moved=l.index;l.moved=h.index;c.splice(g,1);e=g=0;break}e+=g}}};a.a.lb=function(){function b(b,d,e,f,g){var h=Math.min,l=Math.max,m=[],k,n=b.length,q,p=d.length,s=p-n||1,t=n+p+1,v,u,x;for(k=0;k<=n;k++)for(u=v,m.push(v=[]),x=h(p,k+s),q=l(0,k-1);q<=x;q++)v[q]=q?k?b[k-1]===d[q-1]?u[q-1]:h(u[q]||t,v[q-1]||t)+1:q+1:k+1;h=[];l=[];s=[];k=n;for(q=p;k||q;)p=m[k][q]-1,q&&p===m[k][q-1]?l.push(h[h.length]={status:e,
value:d[--q],index:q}):k&&p===m[k-1][q]?s.push(h[h.length]={status:f,value:b[--k],index:k}):(--q,--k,g.sparse||h.push({status:"retained",value:d[q]}));a.a.hc(s,l,!g.dontLimitMoves&&10*n);return h.reverse()}return function(a,d,e){e="boolean"===typeof e?{dontLimitMoves:e}:e||{};a=a||[];d=d||[];return a.length<d.length?b(a,d,"added","deleted",e):b(d,a,"deleted","added",e)}}();a.b("utils.compareArrays",a.a.lb);(function(){function b(b,c,d,h,l){var m=[],k=a.B(function(){var k=c(d,l,a.a.Ba(m,b))||[];0<
m.length&&(a.a.uc(m,k),h&&a.l.w(h,null,[d,k,l]));m.length=0;a.a.ta(m,k)},null,{i:b,ya:function(){return!a.a.Tb(m)}});return{ea:m,B:k.ca()?k:n}}var c=a.a.e.J(),d=a.a.e.J();a.a.Db=function(e,f,g,h,l){function m(b,c){w=q[c];u!==c&&(D[b]=w);w.tb(u++);a.a.Ba(w.ea,e);t.push(w);z.push(w)}function k(b,c){if(b)for(var d=0,e=c.length;d<e;d++)c[d]&&a.a.r(c[d].ea,function(a){b(a,d,c[d].ka)})}f=f||[];h=h||{};var r=a.a.e.get(e,c)===n,q=a.a.e.get(e,c)||[],p=a.a.ib(q,function(a){return a.ka}),s=a.a.lb(p,f,h.dontLimitMoves),
t=[],v=0,u=0,x=[],z=[];f=[];for(var D=[],p=[],w,C=0,B,E;B=s[C];C++)switch(E=B.moved,B.status){case "deleted":E===n&&(w=q[v],w.B&&(w.B.k(),w.B=n),a.a.Ba(w.ea,e).length&&(h.beforeRemove&&(t.push(w),z.push(w),w.ka===d?w=null:f[C]=w),w&&x.push.apply(x,w.ea)));v++;break;case "retained":m(C,v++);break;case "added":E!==n?m(C,E):(w={ka:B.value,tb:a.O(u++)},t.push(w),z.push(w),r||(p[C]=w))}a.a.e.set(e,c,t);k(h.beforeMove,D);a.a.r(x,h.beforeRemove?a.ba:a.removeNode);for(var C=0,r=a.f.firstChild(e),F;w=z[C];C++){w.ea||
a.a.extend(w,b(e,g,w.ka,l,w.tb));for(v=0;s=w.ea[v];r=s.nextSibling,F=s,v++)s!==r&&a.f.kc(e,s,F);!w.ad&&l&&(l(w.ka,w.ea,w.tb),w.ad=!0)}k(h.beforeRemove,f);for(C=0;C<f.length;++C)f[C]&&(f[C].ka=d);k(h.afterMove,D);k(h.afterAdd,p)}})();a.b("utils.setDomNodeChildrenFromArrayMapping",a.a.Db);a.X=function(){this.allowTemplateRewriting=!1};a.X.prototype=new a.P;a.X.prototype.renderTemplateSource=function(b,c,d,e){if(c=(9>a.a.C?0:b.nodes)?b.nodes():null)return a.a.W(c.cloneNode(!0).childNodes);b=b.text();
return a.a.na(b,e)};a.X.vb=new a.X;a.Fb(a.X.vb);a.b("nativeTemplateEngine",a.X);(function(){a.xb=function(){var a=this.ed=function(){if(!u||!u.tmpl)return 0;try{if(0<=u.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(b,e,f,g){g=g||t;f=f||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var h=b.data("precompiled");h||(h=b.text()||"",h=u.template(null,"{{ko_with $item.koBindingContext}}"+
h+"{{/ko_with}}"),b.data("precompiled",h));b=[e.$data];e=u.extend({koBindingContext:e},f.templateOptions);e=u.tmpl(h,b,e);e.appendTo(g.createElement("div"));u.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+a+" })()) }}"};this.addTemplate=function(a,b){t.write("<script type='text/html' id='"+a+"'>"+b+"\x3c/script>")};0<a&&(u.tmpl.tag.ko_code={open:"__.push($1 || '');"},u.tmpl.tag.ko_with={open:"with($1) {",close:"} "})};a.xb.prototype=
new a.P;var b=new a.xb;0<b.ed&&a.Fb(b);a.b("jqueryTmplTemplateEngine",a.xb)})()})})();})();
(function() {(function(n){var x=this||(0,eval)("this"),u=x.document,M=x.navigator,v=x.jQuery,F=x.JSON;(function(n){"function"===typeof define&&define.amd?define(["exports","require"],n):"object"===typeof exports&&"object"===typeof module?n(module.exports||exports):n(x.ko={})})(function(N,O){function J(a,c){return null===a||typeof a in T?a===c:!1}function U(b,c){var d;return function(){d||(d=a.a.setTimeout(function(){d=n;b()},c))}}function V(b,c){var d;return function(){clearTimeout(d);d=a.a.setTimeout(b,c)}}function W(a,
c){c&&c!==I?"beforeChange"===c?this.Kb(a):this.Ha(a,c):this.Lb(a)}function X(a,c){null!==c&&c.k&&c.k()}function Y(a,c){var d=this.Hc,e=d[s];e.R||(this.lb&&this.Ma[c]?(d.Pb(c,a,this.Ma[c]),this.Ma[c]=null,--this.lb):e.r[c]||d.Pb(c,a,e.s?{ia:a}:d.uc(a)))}function K(b,c,d,e){a.d[b]={init:function(b,g,k,l,m){var h,r;a.m(function(){var q=a.a.c(g()),p=!d!==!q,A=!r;if(A||c||p!==h)A&&a.va.Aa()&&(r=a.a.ua(a.f.childNodes(b),!0)),p?(A||a.f.da(b,a.a.ua(r)),a.eb(e?e(m,q):m,b)):a.f.xa(b),h=p},null,{i:b});return{controlsDescendantBindings:!0}}};
a.h.ta[b]=!1;a.f.Z[b]=!0}var a="undefined"!==typeof N?N:{};a.b=function(b,c){for(var d=b.split("."),e=a,f=0;f<d.length-1;f++)e=e[d[f]];e[d[d.length-1]]=c};a.G=function(a,c,d){a[c]=d};a.version="3.4.0";a.b("version",a.version);a.options={deferUpdates:!1,useOnlyNativeEvents:!1};a.a=function(){function b(a,b){for(var c in a)a.hasOwnProperty(c)&&b(c,a[c])}function c(a,b){if(b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function d(a,b){a.__proto__=b;return a}function e(b,c,d,e){var h=b[c].match(r)||
[];a.a.q(d.match(r),function(b){a.a.pa(h,b,e)});b[c]=h.join(" ")}var f={__proto__:[]}instanceof Array,g="function"===typeof Symbol,k={},l={};k[M&&/Firefox\/2/i.test(M.userAgent)?"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];k.MouseEvents="click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");b(k,function(a,b){if(b.length)for(var c=0,d=b.length;c<d;c++)l[b[c]]=a});var m={propertychange:!0},h=u&&function(){for(var a=3,b=u.createElement("div"),c=
b.getElementsByTagName("i");b.innerHTML="\x3c!--[if gt IE "+ ++a+"]><i></i><![endif]--\x3e",c[0];);return 4<a?a:n}(),r=/\S+/g;return{cc:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],q:function(a,b){for(var c=0,d=a.length;c<d;c++)b(a[c],c)},o:function(a,b){if("function"==typeof Array.prototype.indexOf)return Array.prototype.indexOf.call(a,b);for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},Sb:function(a,b,c){for(var d=0,e=a.length;d<e;d++)if(b.call(c,a[d],d))return a[d];
return null},La:function(b,c){var d=a.a.o(b,c);0<d?b.splice(d,1):0===d&&b.shift()},Tb:function(b){b=b||[];for(var c=[],d=0,e=b.length;d<e;d++)0>a.a.o(c,b[d])&&c.push(b[d]);return c},fb:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;d<e;d++)c.push(b(a[d],d));return c},Ka:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;d<e;d++)b(a[d],d)&&c.push(a[d]);return c},ra:function(a,b){if(b instanceof Array)a.push.apply(a,b);else for(var c=0,d=b.length;c<d;c++)a.push(b[c]);return a},pa:function(b,c,d){var e=
a.a.o(a.a.zb(b),c);0>e?d&&b.push(c):d||b.splice(e,1)},ka:f,extend:c,Xa:d,Ya:f?d:c,D:b,Ca:function(a,b){if(!a)return a;var c={},d;for(d in a)a.hasOwnProperty(d)&&(c[d]=b(a[d],d,a));return c},ob:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},jc:function(b){b=a.a.V(b);for(var c=(b[0]&&b[0].ownerDocument||u).createElement("div"),d=0,e=b.length;d<e;d++)c.appendChild(a.$(b[d]));return c},ua:function(b,c){for(var d=0,e=b.length,h=[];d<e;d++){var m=b[d].cloneNode(!0);h.push(c?a.$(m):m)}return h},
da:function(b,c){a.a.ob(b);if(c)for(var d=0,e=c.length;d<e;d++)b.appendChild(c[d])},qc:function(b,c){var d=b.nodeType?[b]:b;if(0<d.length){for(var e=d[0],h=e.parentNode,m=0,l=c.length;m<l;m++)h.insertBefore(c[m],e);m=0;for(l=d.length;m<l;m++)a.removeNode(d[m])}},za:function(a,b){if(a.length){for(b=8===b.nodeType&&b.parentNode||b;a.length&&a[0].parentNode!==b;)a.splice(0,1);for(;1<a.length&&a[a.length-1].parentNode!==b;)a.length--;if(1<a.length){var c=a[0],d=a[a.length-1];for(a.length=0;c!==d;)a.push(c),
c=c.nextSibling;a.push(d)}}return a},sc:function(a,b){7>h?a.setAttribute("selected",b):a.selected=b},$a:function(a){return null===a||a===n?"":a.trim?a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},nd:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},Mc:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(3===a.nodeType?a.parentNode:a);if(b.compareDocumentPosition)return 16==(b.compareDocumentPosition(a)&16);for(;a&&a!=
b;)a=a.parentNode;return!!a},nb:function(b){return a.a.Mc(b,b.ownerDocument.documentElement)},Qb:function(b){return!!a.a.Sb(b,a.a.nb)},A:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},Wb:function(b){return a.onError?function(){try{return b.apply(this,arguments)}catch(c){throw a.onError&&a.onError(c),c;}}:b},setTimeout:function(b,c){return setTimeout(a.a.Wb(b),c)},$b:function(b){setTimeout(function(){a.onError&&a.onError(b);throw b;},0)},p:function(b,c,d){var e=a.a.Wb(d);d=h&&m[c];if(a.options.useOnlyNativeEvents||
d||!v)if(d||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var l=function(a){e.call(b,a)},f="on"+c;b.attachEvent(f,l);a.a.F.oa(b,function(){b.detachEvent(f,l)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(c,e,!1);else v(b).bind(c,e)},Da:function(b,c){if(!b||!b.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var d;"input"===a.a.A(b)&&b.type&&"click"==c.toLowerCase()?(d=b.type,d="checkbox"==
d||"radio"==d):d=!1;if(a.options.useOnlyNativeEvents||!v||d)if("function"==typeof u.createEvent)if("function"==typeof b.dispatchEvent)d=u.createEvent(l[c]||"HTMLEvents"),d.initEvent(c,!0,!0,x,0,0,0,0,0,!1,!1,!1,!1,0,b),b.dispatchEvent(d);else throw Error("The supplied element doesn't support dispatchEvent");else if(d&&b.click)b.click();else if("undefined"!=typeof b.fireEvent)b.fireEvent("on"+c);else throw Error("Browser doesn't support triggering events");else v(b).trigger(c)},c:function(b){return a.H(b)?
b():b},zb:function(b){return a.H(b)?b.t():b},bb:function(b,c,d){var h;c&&("object"===typeof b.classList?(h=b.classList[d?"add":"remove"],a.a.q(c.match(r),function(a){h.call(b.classList,a)})):"string"===typeof b.className.baseVal?e(b.className,"baseVal",c,d):e(b,"className",c,d))},Za:function(b,c){var d=a.a.c(c);if(null===d||d===n)d="";var e=a.f.firstChild(b);!e||3!=e.nodeType||a.f.nextSibling(e)?a.f.da(b,[b.ownerDocument.createTextNode(d)]):e.data=d;a.a.Rc(b)},rc:function(a,b){a.name=b;if(7>=h)try{a.mergeAttributes(u.createElement("<input name='"+
a.name+"'/>"),!1)}catch(c){}},Rc:function(a){9<=h&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},Nc:function(a){if(h){var b=a.style.width;a.style.width=0;a.style.width=b}},hd:function(b,c){b=a.a.c(b);c=a.a.c(c);for(var d=[],e=b;e<=c;e++)d.push(e);return d},V:function(a){for(var b=[],c=0,d=a.length;c<d;c++)b.push(a[c]);return b},Yb:function(a){return g?Symbol(a):a},rd:6===h,sd:7===h,C:h,ec:function(b,c){for(var d=a.a.V(b.getElementsByTagName("input")).concat(a.a.V(b.getElementsByTagName("textarea"))),
e="string"==typeof c?function(a){return a.name===c}:function(a){return c.test(a.name)},h=[],m=d.length-1;0<=m;m--)e(d[m])&&h.push(d[m]);return h},ed:function(b){return"string"==typeof b&&(b=a.a.$a(b))?F&&F.parse?F.parse(b):(new Function("return "+b))():null},Eb:function(b,c,d){if(!F||!F.stringify)throw Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
return F.stringify(a.a.c(b),c,d)},fd:function(c,d,e){e=e||{};var h=e.params||{},m=e.includeFields||this.cc,l=c;if("object"==typeof c&&"form"===a.a.A(c))for(var l=c.action,f=m.length-1;0<=f;f--)for(var g=a.a.ec(c,m[f]),k=g.length-1;0<=k;k--)h[g[k].name]=g[k].value;d=a.a.c(d);var r=u.createElement("form");r.style.display="none";r.action=l;r.method="post";for(var n in d)c=u.createElement("input"),c.type="hidden",c.name=n,c.value=a.a.Eb(a.a.c(d[n])),r.appendChild(c);b(h,function(a,b){var c=u.createElement("input");
c.type="hidden";c.name=a;c.value=b;r.appendChild(c)});u.body.appendChild(r);e.submitter?e.submitter(r):r.submit();setTimeout(function(){r.parentNode.removeChild(r)},0)}}}();a.b("utils",a.a);a.b("utils.arrayForEach",a.a.q);a.b("utils.arrayFirst",a.a.Sb);a.b("utils.arrayFilter",a.a.Ka);a.b("utils.arrayGetDistinctValues",a.a.Tb);a.b("utils.arrayIndexOf",a.a.o);a.b("utils.arrayMap",a.a.fb);a.b("utils.arrayPushAll",a.a.ra);a.b("utils.arrayRemoveItem",a.a.La);a.b("utils.extend",a.a.extend);a.b("utils.fieldsIncludedWithJsonPost",
a.a.cc);a.b("utils.getFormFields",a.a.ec);a.b("utils.peekObservable",a.a.zb);a.b("utils.postJson",a.a.fd);a.b("utils.parseJson",a.a.ed);a.b("utils.registerEventHandler",a.a.p);a.b("utils.stringifyJson",a.a.Eb);a.b("utils.range",a.a.hd);a.b("utils.toggleDomNodeCssClass",a.a.bb);a.b("utils.triggerEvent",a.a.Da);a.b("utils.unwrapObservable",a.a.c);a.b("utils.objectForEach",a.a.D);a.b("utils.addOrRemoveItem",a.a.pa);a.b("utils.setTextContent",a.a.Za);a.b("unwrap",a.a.c);Function.prototype.bind||(Function.prototype.bind=
function(a){var c=this;if(1===arguments.length)return function(){return c.apply(a,arguments)};var d=Array.prototype.slice.call(arguments,1);return function(){var e=d.slice(0);e.push.apply(e,arguments);return c.apply(a,e)}});a.a.e=new function(){function a(b,g){var k=b[d];if(!k||"null"===k||!e[k]){if(!g)return n;k=b[d]="ko"+c++;e[k]={}}return e[k]}var c=0,d="__ko__"+(new Date).getTime(),e={};return{get:function(c,d){var e=a(c,!1);return e===n?n:e[d]},set:function(c,d,e){if(e!==n||a(c,!1)!==n)a(c,!0)[d]=
e},clear:function(a){var b=a[d];return b?(delete e[b],a[d]=null,!0):!1},I:function(){return c++ +d}}};a.b("utils.domData",a.a.e);a.b("utils.domData.clear",a.a.e.clear);a.a.F=new function(){function b(b,c){var e=a.a.e.get(b,d);e===n&&c&&(e=[],a.a.e.set(b,d,e));return e}function c(d){var e=b(d,!1);if(e)for(var e=e.slice(0),l=0;l<e.length;l++)e[l](d);a.a.e.clear(d);a.a.F.cleanExternalData(d);if(f[d.nodeType])for(e=d.firstChild;d=e;)e=d.nextSibling,8===d.nodeType&&c(d)}var d=a.a.e.I(),e={1:!0,8:!0,9:!0},
f={1:!0,9:!0};return{oa:function(a,c){if("function"!=typeof c)throw Error("Callback must be a function");b(a,!0).push(c)},pc:function(c,e){var l=b(c,!1);l&&(a.a.La(l,e),0==l.length&&a.a.e.set(c,d,n))},$:function(b){if(e[b.nodeType]&&(c(b),f[b.nodeType])){var d=[];a.a.ra(d,b.getElementsByTagName("*"));for(var l=0,m=d.length;l<m;l++)c(d[l])}return b},removeNode:function(b){a.$(b);b.parentNode&&b.parentNode.removeChild(b)},cleanExternalData:function(a){v&&"function"==typeof v.cleanData&&v.cleanData([a])}}};
a.$=a.a.F.$;a.removeNode=a.a.F.removeNode;a.b("cleanNode",a.$);a.b("removeNode",a.removeNode);a.b("utils.domNodeDisposal",a.a.F);a.b("utils.domNodeDisposal.addDisposeCallback",a.a.F.oa);a.b("utils.domNodeDisposal.removeDisposeCallback",a.a.F.pc);(function(){var b=[0,"",""],c=[1,"<table>","</table>"],d=[3,"<table><tbody><tr>","</tr></tbody></table>"],e=[1,"<select multiple='multiple'>","</select>"],f={thead:c,tbody:c,tfoot:c,tr:[2,"<table><tbody>","</tbody></table>"],td:d,th:d,option:e,optgroup:e},
g=8>=a.a.C;a.a.ma=function(c,d){var e;if(v)if(v.parseHTML)e=v.parseHTML(c,d)||[];else{if((e=v.clean([c],d))&&e[0]){for(var h=e[0];h.parentNode&&11!==h.parentNode.nodeType;)h=h.parentNode;h.parentNode&&h.parentNode.removeChild(h)}}else{(e=d)||(e=u);var h=e.parentWindow||e.defaultView||x,r=a.a.$a(c).toLowerCase(),q=e.createElement("div"),p;p=(r=r.match(/^<([a-z]+)[ >]/))&&f[r[1]]||b;r=p[0];p="ignored<div>"+p[1]+c+p[2]+"</div>";"function"==typeof h.innerShiv?q.appendChild(h.innerShiv(p)):(g&&e.appendChild(q),
q.innerHTML=p,g&&q.parentNode.removeChild(q));for(;r--;)q=q.lastChild;e=a.a.V(q.lastChild.childNodes)}return e};a.a.Cb=function(b,c){a.a.ob(b);c=a.a.c(c);if(null!==c&&c!==n)if("string"!=typeof c&&(c=c.toString()),v)v(b).html(c);else for(var d=a.a.ma(c,b.ownerDocument),e=0;e<d.length;e++)b.appendChild(d[e])}})();a.b("utils.parseHtmlFragment",a.a.ma);a.b("utils.setHtml",a.a.Cb);a.M=function(){function b(c,e){if(c)if(8==c.nodeType){var f=a.M.lc(c.nodeValue);null!=f&&e.push({Lc:c,cd:f})}else if(1==c.nodeType)for(var f=
0,g=c.childNodes,k=g.length;f<k;f++)b(g[f],e)}var c={};return{wb:function(a){if("function"!=typeof a)throw Error("You can only pass a function to ko.memoization.memoize()");var b=(4294967296*(1+Math.random())|0).toString(16).substring(1)+(4294967296*(1+Math.random())|0).toString(16).substring(1);c[b]=a;return"\x3c!--[ko_memo:"+b+"]--\x3e"},xc:function(a,b){var f=c[a];if(f===n)throw Error("Couldn't find any memo with ID "+a+". Perhaps it's already been unmemoized.");try{return f.apply(null,b||[]),
!0}finally{delete c[a]}},yc:function(c,e){var f=[];b(c,f);for(var g=0,k=f.length;g<k;g++){var l=f[g].Lc,m=[l];e&&a.a.ra(m,e);a.M.xc(f[g].cd,m);l.nodeValue="";l.parentNode&&l.parentNode.removeChild(l)}},lc:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:null}}}();a.b("memoization",a.M);a.b("memoization.memoize",a.M.wb);a.b("memoization.unmemoize",a.M.xc);a.b("memoization.parseMemoText",a.M.lc);a.b("memoization.unmemoizeDomNodeAndDescendants",a.M.yc);a.Y=function(){function b(){if(e)for(var b=
e,c=0,m;g<e;)if(m=d[g++]){if(g>b){if(5E3<=++c){g=e;a.a.$b(Error("'Too much recursion' after processing "+c+" task groups."));break}b=e}try{m()}catch(h){a.a.$b(h)}}}function c(){b();g=e=d.length=0}var d=[],e=0,f=1,g=0;return{scheduler:x.MutationObserver?function(a){var b=u.createElement("div");(new MutationObserver(a)).observe(b,{attributes:!0});return function(){b.classList.toggle("foo")}}(c):u&&"onreadystatechange"in u.createElement("script")?function(a){var b=u.createElement("script");b.onreadystatechange=
function(){b.onreadystatechange=null;u.documentElement.removeChild(b);b=null;a()};u.documentElement.appendChild(b)}:function(a){setTimeout(a,0)},Wa:function(b){e||a.Y.scheduler(c);d[e++]=b;return f++},cancel:function(a){a-=f-e;a>=g&&a<e&&(d[a]=null)},resetForTesting:function(){var a=e-g;g=e=d.length=0;return a},md:b}}();a.b("tasks",a.Y);a.b("tasks.schedule",a.Y.Wa);a.b("tasks.runEarly",a.Y.md);a.ya={throttle:function(b,c){b.throttleEvaluation=c;var d=null;return a.B({read:b,write:function(e){clearTimeout(d);
d=a.a.setTimeout(function(){b(e)},c)}})},rateLimit:function(a,c){var d,e,f;"number"==typeof c?d=c:(d=c.timeout,e=c.method);a.cb=!1;f="notifyWhenChangesStop"==e?V:U;a.Ta(function(a){return f(a,d)})},deferred:function(b,c){if(!0!==c)throw Error("The 'deferred' extender only accepts the value 'true', because it is not supported to turn deferral off once enabled.");b.cb||(b.cb=!0,b.Ta(function(c){var e;return function(){a.Y.cancel(e);e=a.Y.Wa(c);b.notifySubscribers(n,"dirty")}}))},notify:function(a,c){a.equalityComparer=
"always"==c?null:J}};var T={undefined:1,"boolean":1,number:1,string:1};a.b("extenders",a.ya);a.vc=function(b,c,d){this.ia=b;this.gb=c;this.Kc=d;this.R=!1;a.G(this,"dispose",this.k)};a.vc.prototype.k=function(){this.R=!0;this.Kc()};a.J=function(){a.a.Ya(this,D);D.rb(this)};var I="change",D={rb:function(a){a.K={};a.Nb=1},X:function(b,c,d){var e=this;d=d||I;var f=new a.vc(e,c?b.bind(c):b,function(){a.a.La(e.K[d],f);e.Ia&&e.Ia(d)});e.sa&&e.sa(d);e.K[d]||(e.K[d]=[]);e.K[d].push(f);return f},notifySubscribers:function(b,
c){c=c||I;c===I&&this.zc();if(this.Pa(c))try{a.l.Ub();for(var d=this.K[c].slice(0),e=0,f;f=d[e];++e)f.R||f.gb(b)}finally{a.l.end()}},Na:function(){return this.Nb},Uc:function(a){return this.Na()!==a},zc:function(){++this.Nb},Ta:function(b){var c=this,d=a.H(c),e,f,g;c.Ha||(c.Ha=c.notifySubscribers,c.notifySubscribers=W);var k=b(function(){c.Mb=!1;d&&g===c&&(g=c());e=!1;c.tb(f,g)&&c.Ha(f=g)});c.Lb=function(a){c.Mb=e=!0;g=a;k()};c.Kb=function(a){e||(f=a,c.Ha(a,"beforeChange"))}},Pa:function(a){return this.K[a]&&
this.K[a].length},Sc:function(b){if(b)return this.K[b]&&this.K[b].length||0;var c=0;a.a.D(this.K,function(a,b){"dirty"!==a&&(c+=b.length)});return c},tb:function(a,c){return!this.equalityComparer||!this.equalityComparer(a,c)},extend:function(b){var c=this;b&&a.a.D(b,function(b,e){var f=a.ya[b];"function"==typeof f&&(c=f(c,e)||c)});return c}};a.G(D,"subscribe",D.X);a.G(D,"extend",D.extend);a.G(D,"getSubscriptionsCount",D.Sc);a.a.ka&&a.a.Xa(D,Function.prototype);a.J.fn=D;a.hc=function(a){return null!=
a&&"function"==typeof a.X&&"function"==typeof a.notifySubscribers};a.b("subscribable",a.J);a.b("isSubscribable",a.hc);a.va=a.l=function(){function b(a){d.push(e);e=a}function c(){e=d.pop()}var d=[],e,f=0;return{Ub:b,end:c,oc:function(b){if(e){if(!a.hc(b))throw Error("Only subscribable things can act as dependencies");e.gb.call(e.Gc,b,b.Cc||(b.Cc=++f))}},w:function(a,d,e){try{return b(),a.apply(d,e||[])}finally{c()}},Aa:function(){if(e)return e.m.Aa()},Sa:function(){if(e)return e.Sa}}}();a.b("computedContext",
a.va);a.b("computedContext.getDependenciesCount",a.va.Aa);a.b("computedContext.isInitial",a.va.Sa);a.b("ignoreDependencies",a.qd=a.l.w);var E=a.a.Yb("_latestValue");a.N=function(b){function c(){if(0<arguments.length)return c.tb(c[E],arguments[0])&&(c.ga(),c[E]=arguments[0],c.fa()),this;a.l.oc(c);return c[E]}c[E]=b;a.a.ka||a.a.extend(c,a.J.fn);a.J.fn.rb(c);a.a.Ya(c,B);a.options.deferUpdates&&a.ya.deferred(c,!0);return c};var B={equalityComparer:J,t:function(){return this[E]},fa:function(){this.notifySubscribers(this[E])},
ga:function(){this.notifySubscribers(this[E],"beforeChange")}};a.a.ka&&a.a.Xa(B,a.J.fn);var H=a.N.gd="__ko_proto__";B[H]=a.N;a.Oa=function(b,c){return null===b||b===n||b[H]===n?!1:b[H]===c?!0:a.Oa(b[H],c)};a.H=function(b){return a.Oa(b,a.N)};a.Ba=function(b){return"function"==typeof b&&b[H]===a.N||"function"==typeof b&&b[H]===a.B&&b.Vc?!0:!1};a.b("observable",a.N);a.b("isObservable",a.H);a.b("isWriteableObservable",a.Ba);a.b("isWritableObservable",a.Ba);a.b("observable.fn",B);a.G(B,"peek",B.t);a.G(B,
"valueHasMutated",B.fa);a.G(B,"valueWillMutate",B.ga);a.la=function(b){b=b||[];if("object"!=typeof b||!("length"in b))throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");b=a.N(b);a.a.Ya(b,a.la.fn);return b.extend({trackArrayChanges:!0})};a.la.fn={remove:function(b){for(var c=this.t(),d=[],e="function"!=typeof b||a.H(b)?function(a){return a===b}:b,f=0;f<c.length;f++){var g=c[f];e(g)&&(0===d.length&&this.ga(),d.push(g),c.splice(f,1),f--)}d.length&&
this.fa();return d},removeAll:function(b){if(b===n){var c=this.t(),d=c.slice(0);this.ga();c.splice(0,c.length);this.fa();return d}return b?this.remove(function(c){return 0<=a.a.o(b,c)}):[]},destroy:function(b){var c=this.t(),d="function"!=typeof b||a.H(b)?function(a){return a===b}:b;this.ga();for(var e=c.length-1;0<=e;e--)d(c[e])&&(c[e]._destroy=!0);this.fa()},destroyAll:function(b){return b===n?this.destroy(function(){return!0}):b?this.destroy(function(c){return 0<=a.a.o(b,c)}):[]},indexOf:function(b){var c=
this();return a.a.o(c,b)},replace:function(a,c){var d=this.indexOf(a);0<=d&&(this.ga(),this.t()[d]=c,this.fa())}};a.a.ka&&a.a.Xa(a.la.fn,a.N.fn);a.a.q("pop push reverse shift sort splice unshift".split(" "),function(b){a.la.fn[b]=function(){var a=this.t();this.ga();this.Vb(a,b,arguments);var d=a[b].apply(a,arguments);this.fa();return d===a?this:d}});a.a.q(["slice"],function(b){a.la.fn[b]=function(){var a=this();return a[b].apply(a,arguments)}});a.b("observableArray",a.la);a.ya.trackArrayChanges=function(b,
c){function d(){if(!e){e=!0;var c=b.notifySubscribers;b.notifySubscribers=function(a,b){b&&b!==I||++k;return c.apply(this,arguments)};var d=[].concat(b.t()||[]);f=null;g=b.X(function(c){c=[].concat(c||[]);if(b.Pa("arrayChange")){var e;if(!f||1<k)f=a.a.ib(d,c,b.hb);e=f}d=c;f=null;k=0;e&&e.length&&b.notifySubscribers(e,"arrayChange")})}}b.hb={};c&&"object"==typeof c&&a.a.extend(b.hb,c);b.hb.sparse=!0;if(!b.Vb){var e=!1,f=null,g,k=0,l=b.sa,m=b.Ia;b.sa=function(a){l&&l.call(b,a);"arrayChange"===a&&d()};
b.Ia=function(a){m&&m.call(b,a);"arrayChange"!==a||b.Pa("arrayChange")||(g.k(),e=!1)};b.Vb=function(b,c,d){function m(a,b,c){return l[l.length]={status:a,value:b,index:c}}if(e&&!k){var l=[],g=b.length,t=d.length,G=0;switch(c){case "push":G=g;case "unshift":for(c=0;c<t;c++)m("added",d[c],G+c);break;case "pop":G=g-1;case "shift":g&&m("deleted",b[G],G);break;case "splice":c=Math.min(Math.max(0,0>d[0]?g+d[0]:d[0]),g);for(var g=1===t?g:Math.min(c+(d[1]||0),g),t=c+t-2,G=Math.max(g,t),P=[],n=[],Q=2;c<G;++c,
++Q)c<g&&n.push(m("deleted",b[c],c)),c<t&&P.push(m("added",d[Q],c));a.a.dc(n,P);break;default:return}f=l}}}};var s=a.a.Yb("_state");a.m=a.B=function(b,c,d){function e(){if(0<arguments.length){if("function"===typeof f)f.apply(g.pb,arguments);else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");return this}a.l.oc(e);(g.S||g.s&&e.Qa())&&e.aa();return g.T}"object"===typeof b?d=b:(d=d||{},b&&(d.read=
b));if("function"!=typeof d.read)throw Error("Pass a function that returns the value of the ko.computed");var f=d.write,g={T:n,S:!0,Ra:!1,Fb:!1,R:!1,Va:!1,s:!1,jd:d.read,pb:c||d.owner,i:d.disposeWhenNodeIsRemoved||d.i||null,wa:d.disposeWhen||d.wa,mb:null,r:{},L:0,bc:null};e[s]=g;e.Vc="function"===typeof f;a.a.ka||a.a.extend(e,a.J.fn);a.J.fn.rb(e);a.a.Ya(e,z);d.pure?(g.Va=!0,g.s=!0,a.a.extend(e,$)):d.deferEvaluation&&a.a.extend(e,aa);a.options.deferUpdates&&a.ya.deferred(e,!0);g.i&&(g.Fb=!0,g.i.nodeType||
(g.i=null));g.s||d.deferEvaluation||e.aa();g.i&&e.ba()&&a.a.F.oa(g.i,g.mb=function(){e.k()});return e};var z={equalityComparer:J,Aa:function(){return this[s].L},Pb:function(a,c,d){if(this[s].Va&&c===this)throw Error("A 'pure' computed must not be called recursively");this[s].r[a]=d;d.Ga=this[s].L++;d.na=c.Na()},Qa:function(){var a,c,d=this[s].r;for(a in d)if(d.hasOwnProperty(a)&&(c=d[a],c.ia.Uc(c.na)))return!0},bd:function(){this.Fa&&!this[s].Ra&&this.Fa()},ba:function(){return this[s].S||0<this[s].L},
ld:function(){this.Mb||this.ac()},uc:function(a){if(a.cb&&!this[s].i){var c=a.X(this.bd,this,"dirty"),d=a.X(this.ld,this);return{ia:a,k:function(){c.k();d.k()}}}return a.X(this.ac,this)},ac:function(){var b=this,c=b.throttleEvaluation;c&&0<=c?(clearTimeout(this[s].bc),this[s].bc=a.a.setTimeout(function(){b.aa(!0)},c)):b.Fa?b.Fa():b.aa(!0)},aa:function(b){var c=this[s],d=c.wa;if(!c.Ra&&!c.R){if(c.i&&!a.a.nb(c.i)||d&&d()){if(!c.Fb){this.k();return}}else c.Fb=!1;c.Ra=!0;try{this.Qc(b)}finally{c.Ra=!1}c.L||
this.k()}},Qc:function(b){var c=this[s],d=c.Va?n:!c.L,e={Hc:this,Ma:c.r,lb:c.L};a.l.Ub({Gc:e,gb:Y,m:this,Sa:d});c.r={};c.L=0;e=this.Pc(c,e);this.tb(c.T,e)&&(c.s||this.notifySubscribers(c.T,"beforeChange"),c.T=e,c.s?this.zc():b&&this.notifySubscribers(c.T));d&&this.notifySubscribers(c.T,"awake")},Pc:function(b,c){try{var d=b.jd;return b.pb?d.call(b.pb):d()}finally{a.l.end(),c.lb&&!b.s&&a.a.D(c.Ma,X),b.S=!1}},t:function(){var a=this[s];(a.S&&!a.L||a.s&&this.Qa())&&this.aa();return a.T},Ta:function(b){a.J.fn.Ta.call(this,
b);this.Fa=function(){this.Kb(this[s].T);this[s].S=!0;this.Lb(this)}},k:function(){var b=this[s];!b.s&&b.r&&a.a.D(b.r,function(a,b){b.k&&b.k()});b.i&&b.mb&&a.a.F.pc(b.i,b.mb);b.r=null;b.L=0;b.R=!0;b.S=!1;b.s=!1;b.i=null}},$={sa:function(b){var c=this,d=c[s];if(!d.R&&d.s&&"change"==b){d.s=!1;if(d.S||c.Qa())d.r=null,d.L=0,d.S=!0,c.aa();else{var e=[];a.a.D(d.r,function(a,b){e[b.Ga]=a});a.a.q(e,function(a,b){var e=d.r[a],l=c.uc(e.ia);l.Ga=b;l.na=e.na;d.r[a]=l})}d.R||c.notifySubscribers(d.T,"awake")}},
Ia:function(b){var c=this[s];c.R||"change"!=b||this.Pa("change")||(a.a.D(c.r,function(a,b){b.k&&(c.r[a]={ia:b.ia,Ga:b.Ga,na:b.na},b.k())}),c.s=!0,this.notifySubscribers(n,"asleep"))},Na:function(){var b=this[s];b.s&&(b.S||this.Qa())&&this.aa();return a.J.fn.Na.call(this)}},aa={sa:function(a){"change"!=a&&"beforeChange"!=a||this.t()}};a.a.ka&&a.a.Xa(z,a.J.fn);var R=a.N.gd;a.m[R]=a.N;z[R]=a.m;a.Xc=function(b){return a.Oa(b,a.m)};a.Yc=function(b){return a.Oa(b,a.m)&&b[s]&&b[s].Va};a.b("computed",a.m);
a.b("dependentObservable",a.m);a.b("isComputed",a.Xc);a.b("isPureComputed",a.Yc);a.b("computed.fn",z);a.G(z,"peek",z.t);a.G(z,"dispose",z.k);a.G(z,"isActive",z.ba);a.G(z,"getDependenciesCount",z.Aa);a.nc=function(b,c){if("function"===typeof b)return a.m(b,c,{pure:!0});b=a.a.extend({},b);b.pure=!0;return a.m(b,c)};a.b("pureComputed",a.nc);(function(){function b(a,f,g){g=g||new d;a=f(a);if("object"!=typeof a||null===a||a===n||a instanceof RegExp||a instanceof Date||a instanceof String||a instanceof
Number||a instanceof Boolean)return a;var k=a instanceof Array?[]:{};g.save(a,k);c(a,function(c){var d=f(a[c]);switch(typeof d){case "boolean":case "number":case "string":case "function":k[c]=d;break;case "object":case "undefined":var h=g.get(d);k[c]=h!==n?h:b(d,f,g)}});return k}function c(a,b){if(a instanceof Array){for(var c=0;c<a.length;c++)b(c);"function"==typeof a.toJSON&&b("toJSON")}else for(c in a)b(c)}function d(){this.keys=[];this.Ib=[]}a.wc=function(c){if(0==arguments.length)throw Error("When calling ko.toJS, pass the object you want to convert.");
return b(c,function(b){for(var c=0;a.H(b)&&10>c;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.wc(b);return a.a.Eb(b,c,d)};d.prototype={save:function(b,c){var d=a.a.o(this.keys,b);0<=d?this.Ib[d]=c:(this.keys.push(b),this.Ib.push(c))},get:function(b){b=a.a.o(this.keys,b);return 0<=b?this.Ib[b]:n}}})();a.b("toJS",a.wc);a.b("toJSON",a.toJSON);(function(){a.j={u:function(b){switch(a.a.A(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__?a.a.e.get(b,a.d.options.xb):7>=a.a.C?b.getAttributeNode("value")&&
b.getAttributeNode("value").specified?b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex]):n;default:return b.value}},ha:function(b,c,d){switch(a.a.A(b)){case "option":switch(typeof c){case "string":a.a.e.set(b,a.d.options.xb,n);"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__;b.value=c;break;default:a.a.e.set(b,a.d.options.xb,c),b.__ko__hasDomDataOptionValue__=!0,b.value="number"===typeof c?c:""}break;case "select":if(""===c||
null===c)c=n;for(var e=-1,f=0,g=b.options.length,k;f<g;++f)if(k=a.j.u(b.options[f]),k==c||""==k&&c===n){e=f;break}if(d||0<=e||c===n&&1<b.size)b.selectedIndex=e;break;default:if(null===c||c===n)c="";b.value=c}}}})();a.b("selectExtensions",a.j);a.b("selectExtensions.readValue",a.j.u);a.b("selectExtensions.writeValue",a.j.ha);a.h=function(){function b(b){b=a.a.$a(b);123===b.charCodeAt(0)&&(b=b.slice(1,-1));var c=[],d=b.match(e),r,k=[],p=0;if(d){d.push(",");for(var A=0,y;y=d[A];++A){var t=y.charCodeAt(0);
if(44===t){if(0>=p){c.push(r&&k.length?{key:r,value:k.join("")}:{unknown:r||k.join("")});r=p=0;k=[];continue}}else if(58===t){if(!p&&!r&&1===k.length){r=k.pop();continue}}else 47===t&&A&&1<y.length?(t=d[A-1].match(f))&&!g[t[0]]&&(b=b.substr(b.indexOf(y)+1),d=b.match(e),d.push(","),A=-1,y="/"):40===t||123===t||91===t?++p:41===t||125===t||93===t?--p:r||k.length||34!==t&&39!==t||(y=y.slice(1,-1));k.push(y)}}return c}var c=["true","false","null","undefined"],d=/^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i,
e=RegExp("\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|/(?:[^/\\\\]|\\\\.)*/w*|[^\\s:,/][^,\"'{}()/:[\\]]*[^\\s,\"'{}()/:[\\]]|[^\\s]","g"),f=/[\])"'A-Za-z0-9_$]+$/,g={"in":1,"return":1,"typeof":1},k={};return{ta:[],ea:k,yb:b,Ua:function(e,m){function h(b,e){var m;if(!A){var l=a.getBindingHandler(b);if(l&&l.preprocess&&!(e=l.preprocess(e,b,h)))return;if(l=k[b])m=e,0<=a.a.o(c,m)?m=!1:(l=m.match(d),m=null===l?!1:l[1]?"Object("+l[1]+")"+l[2]:m),l=m;l&&g.push("'"+b+"':function(_z){"+m+"=_z}")}p&&(e=
"function(){return "+e+" }");f.push("'"+b+"':"+e)}m=m||{};var f=[],g=[],p=m.valueAccessors,A=m.bindingParams,y="string"===typeof e?b(e):e;a.a.q(y,function(a){h(a.key||a.unknown,a.value)});g.length&&h("_ko_property_writers","{"+g.join(",")+" }");return f.join(",")},ad:function(a,b){for(var c=0;c<a.length;c++)if(a[c].key==b)return!0;return!1},Ea:function(b,c,d,e,f){if(b&&a.H(b))!a.Ba(b)||f&&b.t()===e||b(e);else if((b=c.get("_ko_property_writers"))&&b[d])b[d](e)}}}();a.b("expressionRewriting",a.h);a.b("expressionRewriting.bindingRewriteValidators",
a.h.ta);a.b("expressionRewriting.parseObjectLiteral",a.h.yb);a.b("expressionRewriting.preProcessBindings",a.h.Ua);a.b("expressionRewriting._twoWayBindings",a.h.ea);a.b("jsonExpressionRewriting",a.h);a.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson",a.h.Ua);(function(){function b(a){return 8==a.nodeType&&g.test(f?a.text:a.nodeValue)}function c(a){return 8==a.nodeType&&k.test(f?a.text:a.nodeValue)}function d(a,d){for(var e=a,f=1,l=[];e=e.nextSibling;){if(c(e)&&(f--,0===f))return l;l.push(e);
b(e)&&f++}if(!d)throw Error("Cannot find closing comment tag to match: "+a.nodeValue);return null}function e(a,b){var c=d(a,b);return c?0<c.length?c[c.length-1].nextSibling:a.nextSibling:null}var f=u&&"\x3c!--test--\x3e"===u.createComment("test").text,g=f?/^\x3c!--\s*ko(?:\s+([\s\S]+))?\s*--\x3e$/:/^\s*ko(?:\s+([\s\S]+))?\s*$/,k=f?/^\x3c!--\s*\/ko\s*--\x3e$/:/^\s*\/ko\s*$/,l={ul:!0,ol:!0};a.f={Z:{},childNodes:function(a){return b(a)?d(a):a.childNodes},xa:function(c){if(b(c)){c=a.f.childNodes(c);for(var d=
0,e=c.length;d<e;d++)a.removeNode(c[d])}else a.a.ob(c)},da:function(c,d){if(b(c)){a.f.xa(c);for(var e=c.nextSibling,f=0,l=d.length;f<l;f++)e.parentNode.insertBefore(d[f],e)}else a.a.da(c,d)},mc:function(a,c){b(a)?a.parentNode.insertBefore(c,a.nextSibling):a.firstChild?a.insertBefore(c,a.firstChild):a.appendChild(c)},gc:function(c,d,e){e?b(c)?c.parentNode.insertBefore(d,e.nextSibling):e.nextSibling?c.insertBefore(d,e.nextSibling):c.appendChild(d):a.f.mc(c,d)},firstChild:function(a){return b(a)?!a.nextSibling||
c(a.nextSibling)?null:a.nextSibling:a.firstChild},nextSibling:function(a){b(a)&&(a=e(a));return a.nextSibling&&c(a.nextSibling)?null:a.nextSibling},Tc:b,pd:function(a){return(a=(f?a.text:a.nodeValue).match(g))?a[1]:null},kc:function(d){if(l[a.a.A(d)]){var h=d.firstChild;if(h){do if(1===h.nodeType){var f;f=h.firstChild;var g=null;if(f){do if(g)g.push(f);else if(b(f)){var k=e(f,!0);k?f=k:g=[f]}else c(f)&&(g=[f]);while(f=f.nextSibling)}if(f=g)for(g=h.nextSibling,k=0;k<f.length;k++)g?d.insertBefore(f[k],
g):d.appendChild(f[k])}while(h=h.nextSibling)}}}}})();a.b("virtualElements",a.f);a.b("virtualElements.allowedBindings",a.f.Z);a.b("virtualElements.emptyNode",a.f.xa);a.b("virtualElements.insertAfter",a.f.gc);a.b("virtualElements.prepend",a.f.mc);a.b("virtualElements.setDomNodeChildren",a.f.da);(function(){a.Q=function(){this.Fc={}};a.a.extend(a.Q.prototype,{nodeHasBindings:function(b){switch(b.nodeType){case 1:return null!=b.getAttribute("data-bind")||a.g.getComponentNameForNode(b);case 8:return a.f.Tc(b);
default:return!1}},getBindings:function(b,c){var d=this.getBindingsString(b,c),d=d?this.parseBindingsString(d,c,b):null;return a.g.Ob(d,b,c,!1)},getBindingAccessors:function(b,c){var d=this.getBindingsString(b,c),d=d?this.parseBindingsString(d,c,b,{valueAccessors:!0}):null;return a.g.Ob(d,b,c,!0)},getBindingsString:function(b){switch(b.nodeType){case 1:return b.getAttribute("data-bind");case 8:return a.f.pd(b);default:return null}},parseBindingsString:function(b,c,d,e){try{var f=this.Fc,g=b+(e&&e.valueAccessors||
""),k;if(!(k=f[g])){var l,m="with($context){with($data||{}){return{"+a.h.Ua(b,e)+"}}}";l=new Function("$context","$element",m);k=f[g]=l}return k(c,d)}catch(h){throw h.message="Unable to parse bindings.\nBindings value: "+b+"\nMessage: "+h.message,h;}}});a.Q.instance=new a.Q})();a.b("bindingProvider",a.Q);(function(){function b(a){return function(){return a}}function c(a){return a()}function d(b){return a.a.Ca(a.l.w(b),function(a,c){return function(){return b()[c]}})}function e(c,e,h){return"function"===
typeof c?d(c.bind(null,e,h)):a.a.Ca(c,b)}function f(a,b){return d(this.getBindings.bind(this,a,b))}function g(b,c,d){var e,h=a.f.firstChild(c),f=a.Q.instance,m=f.preprocessNode;if(m){for(;e=h;)h=a.f.nextSibling(e),m.call(f,e);h=a.f.firstChild(c)}for(;e=h;)h=a.f.nextSibling(e),k(b,e,d)}function k(b,c,d){var e=!0,h=1===c.nodeType;h&&a.f.kc(c);if(h&&d||a.Q.instance.nodeHasBindings(c))e=m(c,null,b,d).shouldBindDescendants;e&&!r[a.a.A(c)]&&g(b,c,!h)}function l(b){var c=[],d={},e=[];a.a.D(b,function Z(h){if(!d[h]){var f=
a.getBindingHandler(h);f&&(f.after&&(e.push(h),a.a.q(f.after,function(c){if(b[c]){if(-1!==a.a.o(e,c))throw Error("Cannot combine the following bindings, because they have a cyclic dependency: "+e.join(", "));Z(c)}}),e.length--),c.push({key:h,fc:f}));d[h]=!0}});return c}function m(b,d,e,h){var m=a.a.e.get(b,q);if(!d){if(m)throw Error("You cannot apply bindings multiple times to the same element.");a.a.e.set(b,q,!0)}!m&&h&&a.tc(b,e);var g;if(d&&"function"!==typeof d)g=d;else{var k=a.Q.instance,r=k.getBindingAccessors||
f,p=a.B(function(){(g=d?d(e,b):r.call(k,b,e))&&e.P&&e.P();return g},null,{i:b});g&&p.ba()||(p=null)}var u;if(g){var v=p?function(a){return function(){return c(p()[a])}}:function(a){return g[a]},s=function(){return a.a.Ca(p?p():g,c)};s.get=function(a){return g[a]&&c(v(a))};s.has=function(a){return a in g};h=l(g);a.a.q(h,function(c){var d=c.fc.init,h=c.fc.update,f=c.key;if(8===b.nodeType&&!a.f.Z[f])throw Error("The binding '"+f+"' cannot be used with virtual elements");try{"function"==typeof d&&a.l.w(function(){var a=
d(b,v(f),s,e.$data,e);if(a&&a.controlsDescendantBindings){if(u!==n)throw Error("Multiple bindings ("+u+" and "+f+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");u=f}}),"function"==typeof h&&a.B(function(){h(b,v(f),s,e.$data,e)},null,{i:b})}catch(m){throw m.message='Unable to process binding "'+f+": "+g[f]+'"\nMessage: '+m.message,m;}})}return{shouldBindDescendants:u===n}}function h(b){return b&&b instanceof a.U?b:new a.U(b)}
a.d={};var r={script:!0,textarea:!0,template:!0};a.getBindingHandler=function(b){return a.d[b]};a.U=function(b,c,d,e){var h=this,f="function"==typeof b&&!a.H(b),m,g=a.B(function(){var m=f?b():b,l=a.a.c(m);c?(c.P&&c.P(),a.a.extend(h,c),g&&(h.P=g)):(h.$parents=[],h.$root=l,h.ko=a);h.$rawData=m;h.$data=l;d&&(h[d]=l);e&&e(h,c,l);return h.$data},null,{wa:function(){return m&&!a.a.Qb(m)},i:!0});g.ba()&&(h.P=g,g.equalityComparer=null,m=[],g.Ac=function(b){m.push(b);a.a.F.oa(b,function(b){a.a.La(m,b);m.length||
(g.k(),h.P=g=n)})})};a.U.prototype.createChildContext=function(b,c,d){return new a.U(b,this,c,function(a,b){a.$parentContext=b;a.$parent=b.$data;a.$parents=(b.$parents||[]).slice(0);a.$parents.unshift(a.$parent);d&&d(a)})};a.U.prototype.extend=function(b){return new a.U(this.P||this.$data,this,null,function(c,d){c.$rawData=d.$rawData;a.a.extend(c,"function"==typeof b?b():b)})};var q=a.a.e.I(),p=a.a.e.I();a.tc=function(b,c){if(2==arguments.length)a.a.e.set(b,p,c),c.P&&c.P.Ac(b);else return a.a.e.get(b,
p)};a.Ja=function(b,c,d){1===b.nodeType&&a.f.kc(b);return m(b,c,h(d),!0)};a.Dc=function(b,c,d){d=h(d);return a.Ja(b,e(c,d,b),d)};a.eb=function(a,b){1!==b.nodeType&&8!==b.nodeType||g(h(a),b,!0)};a.Rb=function(a,b){!v&&x.jQuery&&(v=x.jQuery);if(b&&1!==b.nodeType&&8!==b.nodeType)throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");b=b||x.document.body;k(h(a),b,!0)};a.kb=function(b){switch(b.nodeType){case 1:case 8:var c=a.tc(b);if(c)return c;
if(b.parentNode)return a.kb(b.parentNode)}return n};a.Jc=function(b){return(b=a.kb(b))?b.$data:n};a.b("bindingHandlers",a.d);a.b("applyBindings",a.Rb);a.b("applyBindingsToDescendants",a.eb);a.b("applyBindingAccessorsToNode",a.Ja);a.b("applyBindingsToNode",a.Dc);a.b("contextFor",a.kb);a.b("dataFor",a.Jc)})();(function(b){function c(c,e){var m=f.hasOwnProperty(c)?f[c]:b,h;m?m.X(e):(m=f[c]=new a.J,m.X(e),d(c,function(b,d){var e=!(!d||!d.synchronous);g[c]={definition:b,Zc:e};delete f[c];h||e?m.notifySubscribers(b):
a.Y.Wa(function(){m.notifySubscribers(b)})}),h=!0)}function d(a,b){e("getConfig",[a],function(c){c?e("loadComponent",[a,c],function(a){b(a,c)}):b(null,null)})}function e(c,d,f,h){h||(h=a.g.loaders.slice(0));var g=h.shift();if(g){var q=g[c];if(q){var p=!1;if(q.apply(g,d.concat(function(a){p?f(null):null!==a?f(a):e(c,d,f,h)}))!==b&&(p=!0,!g.suppressLoaderExceptions))throw Error("Component loaders must supply values by invoking the callback, not by returning values synchronously.");}else e(c,d,f,h)}else f(null)}
var f={},g={};a.g={get:function(d,e){var f=g.hasOwnProperty(d)?g[d]:b;f?f.Zc?a.l.w(function(){e(f.definition)}):a.Y.Wa(function(){e(f.definition)}):c(d,e)},Xb:function(a){delete g[a]},Jb:e};a.g.loaders=[];a.b("components",a.g);a.b("components.get",a.g.get);a.b("components.clearCachedDefinition",a.g.Xb)})();(function(){function b(b,c,d,e){function g(){0===--y&&e(k)}var k={},y=2,t=d.template;d=d.viewModel;t?f(c,t,function(c){a.g.Jb("loadTemplate",[b,c],function(a){k.template=a;g()})}):g();d?f(c,d,function(c){a.g.Jb("loadViewModel",
[b,c],function(a){k[l]=a;g()})}):g()}function c(a,b,d){if("function"===typeof b)d(function(a){return new b(a)});else if("function"===typeof b[l])d(b[l]);else if("instance"in b){var e=b.instance;d(function(){return e})}else"viewModel"in b?c(a,b.viewModel,d):a("Unknown viewModel value: "+b)}function d(b){switch(a.a.A(b)){case "script":return a.a.ma(b.text);case "textarea":return a.a.ma(b.value);case "template":if(e(b.content))return a.a.ua(b.content.childNodes)}return a.a.ua(b.childNodes)}function e(a){return x.DocumentFragment?
a instanceof DocumentFragment:a&&11===a.nodeType}function f(a,b,c){"string"===typeof b.require?O||x.require?(O||x.require)([b.require],c):a("Uses require, but no AMD loader is present"):c(b)}function g(a){return function(b){throw Error("Component '"+a+"': "+b);}}var k={};a.g.register=function(b,c){if(!c)throw Error("Invalid configuration for "+b);if(a.g.ub(b))throw Error("Component "+b+" is already registered");k[b]=c};a.g.ub=function(a){return k.hasOwnProperty(a)};a.g.od=function(b){delete k[b];
a.g.Xb(b)};a.g.Zb={getConfig:function(a,b){b(k.hasOwnProperty(a)?k[a]:null)},loadComponent:function(a,c,d){var e=g(a);f(e,c,function(c){b(a,e,c,d)})},loadTemplate:function(b,c,f){b=g(b);if("string"===typeof c)f(a.a.ma(c));else if(c instanceof Array)f(c);else if(e(c))f(a.a.V(c.childNodes));else if(c.element)if(c=c.element,x.HTMLElement?c instanceof HTMLElement:c&&c.tagName&&1===c.nodeType)f(d(c));else if("string"===typeof c){var l=u.getElementById(c);l?f(d(l)):b("Cannot find element with ID "+c)}else b("Unknown element type: "+
c);else b("Unknown template value: "+c)},loadViewModel:function(a,b,d){c(g(a),b,d)}};var l="createViewModel";a.b("components.register",a.g.register);a.b("components.isRegistered",a.g.ub);a.b("components.unregister",a.g.od);a.b("components.defaultLoader",a.g.Zb);a.g.loaders.push(a.g.Zb);a.g.Bc=k})();(function(){function b(b,e){var f=b.getAttribute("params");if(f){var f=c.parseBindingsString(f,e,b,{valueAccessors:!0,bindingParams:!0}),f=a.a.Ca(f,function(c){return a.m(c,null,{i:b})}),g=a.a.Ca(f,function(c){var e=
c.t();return c.ba()?a.m({read:function(){return a.a.c(c())},write:a.Ba(e)&&function(a){c()(a)},i:b}):e});g.hasOwnProperty("$raw")||(g.$raw=f);return g}return{$raw:{}}}a.g.getComponentNameForNode=function(b){var c=a.a.A(b);if(a.g.ub(c)&&(-1!=c.indexOf("-")||"[object HTMLUnknownElement]"==""+b||8>=a.a.C&&b.tagName===c))return c};a.g.Ob=function(c,e,f,g){if(1===e.nodeType){var k=a.g.getComponentNameForNode(e);if(k){c=c||{};if(c.component)throw Error('Cannot use the "component" binding on a custom element matching a component');
var l={name:k,params:b(e,f)};c.component=g?function(){return l}:l}}return c};var c=new a.Q;9>a.a.C&&(a.g.register=function(a){return function(b){u.createElement(b);return a.apply(this,arguments)}}(a.g.register),u.createDocumentFragment=function(b){return function(){var c=b(),f=a.g.Bc,g;for(g in f)f.hasOwnProperty(g)&&c.createElement(g);return c}}(u.createDocumentFragment))})();(function(b){function c(b,c,d){c=c.template;if(!c)throw Error("Component '"+b+"' has no template");b=a.a.ua(c);a.f.da(d,b)}
function d(a,b,c,d){var e=a.createViewModel;return e?e.call(a,d,{element:b,templateNodes:c}):d}var e=0;a.d.component={init:function(f,g,k,l,m){function h(){var a=r&&r.dispose;"function"===typeof a&&a.call(r);q=r=null}var r,q,p=a.a.V(a.f.childNodes(f));a.a.F.oa(f,h);a.m(function(){var l=a.a.c(g()),k,t;"string"===typeof l?k=l:(k=a.a.c(l.name),t=a.a.c(l.params));if(!k)throw Error("No component name specified");var n=q=++e;a.g.get(k,function(e){if(q===n){h();if(!e)throw Error("Unknown component '"+k+
"'");c(k,e,f);var g=d(e,f,p,t);e=m.createChildContext(g,b,function(a){a.$component=g;a.$componentTemplateNodes=p});r=g;a.eb(e,f)}})},null,{i:f});return{controlsDescendantBindings:!0}}};a.f.Z.component=!0})();var S={"class":"className","for":"htmlFor"};a.d.attr={update:function(b,c){var d=a.a.c(c())||{};a.a.D(d,function(c,d){d=a.a.c(d);var g=!1===d||null===d||d===n;g&&b.removeAttribute(c);8>=a.a.C&&c in S?(c=S[c],g?b.removeAttribute(c):b[c]=d):g||b.setAttribute(c,d.toString());"name"===c&&a.a.rc(b,
g?"":d.toString())})}};(function(){a.d.checked={after:["value","attr"],init:function(b,c,d){function e(){var e=b.checked,f=p?g():e;if(!a.va.Sa()&&(!l||e)){var m=a.l.w(c);if(h){var k=r?m.t():m;q!==f?(e&&(a.a.pa(k,f,!0),a.a.pa(k,q,!1)),q=f):a.a.pa(k,f,e);r&&a.Ba(m)&&m(k)}else a.h.Ea(m,d,"checked",f,!0)}}function f(){var d=a.a.c(c());b.checked=h?0<=a.a.o(d,g()):k?d:g()===d}var g=a.nc(function(){return d.has("checkedValue")?a.a.c(d.get("checkedValue")):d.has("value")?a.a.c(d.get("value")):b.value}),k=
"checkbox"==b.type,l="radio"==b.type;if(k||l){var m=c(),h=k&&a.a.c(m)instanceof Array,r=!(h&&m.push&&m.splice),q=h?g():n,p=l||h;l&&!b.name&&a.d.uniqueName.init(b,function(){return!0});a.m(e,null,{i:b});a.a.p(b,"click",e);a.m(f,null,{i:b});m=n}}};a.h.ea.checked=!0;a.d.checkedValue={update:function(b,c){b.value=a.a.c(c())}}})();a.d.css={update:function(b,c){var d=a.a.c(c());null!==d&&"object"==typeof d?a.a.D(d,function(c,d){d=a.a.c(d);a.a.bb(b,c,d)}):(d=a.a.$a(String(d||"")),a.a.bb(b,b.__ko__cssValue,
!1),b.__ko__cssValue=d,a.a.bb(b,d,!0))}};a.d.enable={update:function(b,c){var d=a.a.c(c());d&&b.disabled?b.removeAttribute("disabled"):d||b.disabled||(b.disabled=!0)}};a.d.disable={update:function(b,c){a.d.enable.update(b,function(){return!a.a.c(c())})}};a.d.event={init:function(b,c,d,e,f){var g=c()||{};a.a.D(g,function(g){"string"==typeof g&&a.a.p(b,g,function(b){var m,h=c()[g];if(h){try{var r=a.a.V(arguments);e=f.$data;r.unshift(e);m=h.apply(e,r)}finally{!0!==m&&(b.preventDefault?b.preventDefault():
b.returnValue=!1)}!1===d.get(g+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};a.d.foreach={ic:function(b){return function(){var c=b(),d=a.a.zb(c);if(!d||"number"==typeof d.length)return{foreach:c,templateEngine:a.W.sb};a.a.c(c);return{foreach:d.data,as:d.as,includeDestroyed:d.includeDestroyed,afterAdd:d.afterAdd,beforeRemove:d.beforeRemove,afterRender:d.afterRender,beforeMove:d.beforeMove,afterMove:d.afterMove,templateEngine:a.W.sb}}},init:function(b,c){return a.d.template.init(b,
a.d.foreach.ic(c))},update:function(b,c,d,e,f){return a.d.template.update(b,a.d.foreach.ic(c),d,e,f)}};a.h.ta.foreach=!1;a.f.Z.foreach=!0;a.d.hasfocus={init:function(b,c,d){function e(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(h){g=f.body}e=g===b}f=c();a.h.Ea(f,d,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var f=e.bind(null,!0),g=e.bind(null,!1);a.a.p(b,"focus",f);a.a.p(b,"focusin",f);a.a.p(b,"blur",g);a.a.p(b,
"focusout",g)},update:function(b,c){var d=!!a.a.c(c());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue===d||(d?b.focus():b.blur(),!d&&b.__ko_hasfocusLastValue&&b.ownerDocument.body.focus(),a.l.w(a.a.Da,null,[b,d?"focusin":"focusout"]))}};a.h.ea.hasfocus=!0;a.d.hasFocus=a.d.hasfocus;a.h.ea.hasFocus=!0;a.d.html={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Cb(b,c())}};K("if");K("ifnot",!1,!0);K("with",!0,!1,function(a,c){return a.createChildContext(c)});var L={};
a.d.options={init:function(b){if("select"!==a.a.A(b))throw Error("options binding applies only to SELECT elements");for(;0<b.length;)b.remove(0);return{controlsDescendantBindings:!0}},update:function(b,c,d){function e(){return a.a.Ka(b.options,function(a){return a.selected})}function f(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c}function g(c,e){if(A&&h)a.j.ha(b,a.a.c(d.get("value")),!0);else if(p.length){var f=0<=a.a.o(p,a.j.u(e[0]));a.a.sc(e[0],f);A&&!f&&a.l.w(a.a.Da,null,[b,
"change"])}}var k=b.multiple,l=0!=b.length&&k?b.scrollTop:null,m=a.a.c(c()),h=d.get("valueAllowUnset")&&d.has("value"),r=d.get("optionsIncludeDestroyed");c={};var q,p=[];h||(k?p=a.a.fb(e(),a.j.u):0<=b.selectedIndex&&p.push(a.j.u(b.options[b.selectedIndex])));m&&("undefined"==typeof m.length&&(m=[m]),q=a.a.Ka(m,function(b){return r||b===n||null===b||!a.a.c(b._destroy)}),d.has("optionsCaption")&&(m=a.a.c(d.get("optionsCaption")),null!==m&&m!==n&&q.unshift(L)));var A=!1;c.beforeRemove=function(a){b.removeChild(a)};
m=g;d.has("optionsAfterRender")&&"function"==typeof d.get("optionsAfterRender")&&(m=function(b,c){g(0,c);a.l.w(d.get("optionsAfterRender"),null,[c[0],b!==L?b:n])});a.a.Bb(b,q,function(c,e,g){g.length&&(p=!h&&g[0].selected?[a.j.u(g[0])]:[],A=!0);e=b.ownerDocument.createElement("option");c===L?(a.a.Za(e,d.get("optionsCaption")),a.j.ha(e,n)):(g=f(c,d.get("optionsValue"),c),a.j.ha(e,a.a.c(g)),c=f(c,d.get("optionsText"),g),a.a.Za(e,c));return[e]},c,m);a.l.w(function(){h?a.j.ha(b,a.a.c(d.get("value")),
!0):(k?p.length&&e().length<p.length:p.length&&0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex])!==p[0]:p.length||0<=b.selectedIndex)&&a.a.Da(b,"change")});a.a.Nc(b);l&&20<Math.abs(l-b.scrollTop)&&(b.scrollTop=l)}};a.d.options.xb=a.a.e.I();a.d.selectedOptions={after:["options","foreach"],init:function(b,c,d){a.a.p(b,"change",function(){var e=c(),f=[];a.a.q(b.getElementsByTagName("option"),function(b){b.selected&&f.push(a.j.u(b))});a.h.Ea(e,d,"selectedOptions",f)})},update:function(b,c){if("select"!=
a.a.A(b))throw Error("values binding applies only to SELECT elements");var d=a.a.c(c()),e=b.scrollTop;d&&"number"==typeof d.length&&a.a.q(b.getElementsByTagName("option"),function(b){var c=0<=a.a.o(d,a.j.u(b));b.selected!=c&&a.a.sc(b,c)});b.scrollTop=e}};a.h.ea.selectedOptions=!0;a.d.style={update:function(b,c){var d=a.a.c(c()||{});a.a.D(d,function(c,d){d=a.a.c(d);if(null===d||d===n||!1===d)d="";b.style[c]=d})}};a.d.submit={init:function(b,c,d,e,f){if("function"!=typeof c())throw Error("The value for a submit binding must be a function");
a.a.p(b,"submit",function(a){var d,e=c();try{d=e.call(f.$data,b)}finally{!0!==d&&(a.preventDefault?a.preventDefault():a.returnValue=!1)}})}};a.d.text={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Za(b,c())}};a.f.Z.text=!0;(function(){if(x&&x.navigator)var b=function(a){if(a)return parseFloat(a[1])},c=x.opera&&x.opera.version&&parseInt(x.opera.version()),d=x.navigator.userAgent,e=b(d.match(/^(?:(?!chrome).)*version\/([^ ]*) safari/i)),f=b(d.match(/Firefox\/([^ ]*)/));
if(10>a.a.C)var g=a.a.e.I(),k=a.a.e.I(),l=function(b){var c=this.activeElement;(c=c&&a.a.e.get(c,k))&&c(b)},m=function(b,c){var d=b.ownerDocument;a.a.e.get(d,g)||(a.a.e.set(d,g,!0),a.a.p(d,"selectionchange",l));a.a.e.set(b,k,c)};a.d.textInput={init:function(b,d,g){function l(c,d){a.a.p(b,c,d)}function k(){var c=a.a.c(d());if(null===c||c===n)c="";v!==n&&c===v?a.a.setTimeout(k,4):b.value!==c&&(u=c,b.value=c)}function y(){s||(v=b.value,s=a.a.setTimeout(t,4))}function t(){clearTimeout(s);v=s=n;var c=
b.value;u!==c&&(u=c,a.h.Ea(d(),g,"textInput",c))}var u=b.value,s,v,x=9==a.a.C?y:t;10>a.a.C?(l("propertychange",function(a){"value"===a.propertyName&&x(a)}),8==a.a.C&&(l("keyup",t),l("keydown",t)),8<=a.a.C&&(m(b,x),l("dragend",y))):(l("input",t),5>e&&"textarea"===a.a.A(b)?(l("keydown",y),l("paste",y),l("cut",y)):11>c?l("keydown",y):4>f&&(l("DOMAutoComplete",t),l("dragdrop",t),l("drop",t)));l("change",t);a.m(k,null,{i:b})}};a.h.ea.textInput=!0;a.d.textinput={preprocess:function(a,b,c){c("textInput",
a)}}})();a.d.uniqueName={init:function(b,c){if(c()){var d="ko_unique_"+ ++a.d.uniqueName.Ic;a.a.rc(b,d)}}};a.d.uniqueName.Ic=0;a.d.value={after:["options","foreach"],init:function(b,c,d){if("input"!=b.tagName.toLowerCase()||"checkbox"!=b.type&&"radio"!=b.type){var e=["change"],f=d.get("valueUpdate"),g=!1,k=null;f&&("string"==typeof f&&(f=[f]),a.a.ra(e,f),e=a.a.Tb(e));var l=function(){k=null;g=!1;var e=c(),f=a.j.u(b);a.h.Ea(e,d,"value",f)};!a.a.C||"input"!=b.tagName.toLowerCase()||"text"!=b.type||
"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.o(e,"propertychange")||(a.a.p(b,"propertychange",function(){g=!0}),a.a.p(b,"focus",function(){g=!1}),a.a.p(b,"blur",function(){g&&l()}));a.a.q(e,function(c){var d=l;a.a.nd(c,"after")&&(d=function(){k=a.j.u(b);a.a.setTimeout(l,0)},c=c.substring(5));a.a.p(b,c,d)});var m=function(){var e=a.a.c(c()),f=a.j.u(b);if(null!==k&&e===k)a.a.setTimeout(m,0);else if(e!==f)if("select"===a.a.A(b)){var g=d.get("valueAllowUnset"),f=function(){a.j.ha(b,
e,g)};f();g||e===a.j.u(b)?a.a.setTimeout(f,0):a.l.w(a.a.Da,null,[b,"change"])}else a.j.ha(b,e)};a.m(m,null,{i:b})}else a.Ja(b,{checkedValue:c})},update:function(){}};a.h.ea.value=!0;a.d.visible={update:function(b,c){var d=a.a.c(c()),e="none"!=b.style.display;d&&!e?b.style.display="":!d&&e&&(b.style.display="none")}};(function(b){a.d[b]={init:function(c,d,e,f,g){return a.d.event.init.call(this,c,function(){var a={};a[b]=d();return a},e,f,g)}}})("click");a.O=function(){};a.O.prototype.renderTemplateSource=
function(){throw Error("Override renderTemplateSource");};a.O.prototype.createJavaScriptEvaluatorBlock=function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.O.prototype.makeTemplateSource=function(b,c){if("string"==typeof b){c=c||u;var d=c.getElementById(b);if(!d)throw Error("Cannot find template with ID "+b);return new a.v.n(d)}if(1==b.nodeType||8==b.nodeType)return new a.v.qa(b);throw Error("Unknown template type: "+b);};a.O.prototype.renderTemplate=function(a,c,d,e){a=this.makeTemplateSource(a,
e);return this.renderTemplateSource(a,c,d,e)};a.O.prototype.isTemplateRewritten=function(a,c){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,c).data("isRewritten")};a.O.prototype.rewriteTemplate=function(a,c,d){a=this.makeTemplateSource(a,d);c=c(a.text());a.text(c);a.data("isRewritten",!0)};a.b("templateEngine",a.O);a.Gb=function(){function b(b,c,d,k){b=a.h.yb(b);for(var l=a.h.ta,m=0;m<b.length;m++){var h=b[m].key;if(l.hasOwnProperty(h)){var r=l[h];if("function"===typeof r){if(h=
r(b[m].value))throw Error(h);}else if(!r)throw Error("This template engine does not support the '"+h+"' binding within its templates");}}d="ko.__tr_ambtns(function($context,$element){return(function(){return{ "+a.h.Ua(b,{valueAccessors:!0})+" } })()},'"+d.toLowerCase()+"')";return k.createJavaScriptEvaluatorBlock(d)+c}var c=/(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,d=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{Oc:function(b,
c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.Gb.dd(b,c)},d)},dd:function(a,f){return a.replace(c,function(a,c,d,e,h){return b(h,c,d,f)}).replace(d,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",f)})},Ec:function(b,c){return a.M.wb(function(d,k){var l=d.nextSibling;l&&l.nodeName.toLowerCase()===c&&a.Ja(l,b,k)})}}}();a.b("__tr_ambtns",a.Gb.Ec);(function(){a.v={};a.v.n=function(b){if(this.n=b){var c=a.a.A(b);this.ab="script"===c?1:"textarea"===c?2:"template"==c&&
b.content&&11===b.content.nodeType?3:4}};a.v.n.prototype.text=function(){var b=1===this.ab?"text":2===this.ab?"value":"innerHTML";if(0==arguments.length)return this.n[b];var c=arguments[0];"innerHTML"===b?a.a.Cb(this.n,c):this.n[b]=c};var b=a.a.e.I()+"_";a.v.n.prototype.data=function(c){if(1===arguments.length)return a.a.e.get(this.n,b+c);a.a.e.set(this.n,b+c,arguments[1])};var c=a.a.e.I();a.v.n.prototype.nodes=function(){var b=this.n;if(0==arguments.length)return(a.a.e.get(b,c)||{}).jb||(3===this.ab?
b.content:4===this.ab?b:n);a.a.e.set(b,c,{jb:arguments[0]})};a.v.qa=function(a){this.n=a};a.v.qa.prototype=new a.v.n;a.v.qa.prototype.text=function(){if(0==arguments.length){var b=a.a.e.get(this.n,c)||{};b.Hb===n&&b.jb&&(b.Hb=b.jb.innerHTML);return b.Hb}a.a.e.set(this.n,c,{Hb:arguments[0]})};a.b("templateSources",a.v);a.b("templateSources.domElement",a.v.n);a.b("templateSources.anonymousTemplate",a.v.qa)})();(function(){function b(b,c,d){var e;for(c=a.f.nextSibling(c);b&&(e=b)!==c;)b=a.f.nextSibling(e),
d(e,b)}function c(c,d){if(c.length){var e=c[0],f=c[c.length-1],g=e.parentNode,k=a.Q.instance,n=k.preprocessNode;if(n){b(e,f,function(a,b){var c=a.previousSibling,d=n.call(k,a);d&&(a===e&&(e=d[0]||b),a===f&&(f=d[d.length-1]||c))});c.length=0;if(!e)return;e===f?c.push(e):(c.push(e,f),a.a.za(c,g))}b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.Rb(d,b)});b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.M.yc(b,[d])});a.a.za(c,g)}}function d(a){return a.nodeType?a:0<a.length?a[0]:null}function e(b,
e,f,k,q){q=q||{};var p=(b&&d(b)||f||{}).ownerDocument,n=q.templateEngine||g;a.Gb.Oc(f,n,p);f=n.renderTemplate(f,k,q,p);if("number"!=typeof f.length||0<f.length&&"number"!=typeof f[0].nodeType)throw Error("Template engine must return an array of DOM nodes");p=!1;switch(e){case "replaceChildren":a.f.da(b,f);p=!0;break;case "replaceNode":a.a.qc(b,f);p=!0;break;case "ignoreTargetNode":break;default:throw Error("Unknown renderMode: "+e);}p&&(c(f,k),q.afterRender&&a.l.w(q.afterRender,null,[f,k.$data]));
return f}function f(b,c,d){return a.H(b)?b():"function"===typeof b?b(c,d):b}var g;a.Db=function(b){if(b!=n&&!(b instanceof a.O))throw Error("templateEngine must inherit from ko.templateEngine");g=b};a.Ab=function(b,c,h,k,q){h=h||{};if((h.templateEngine||g)==n)throw Error("Set a template engine before calling renderTemplate");q=q||"replaceChildren";if(k){var p=d(k);return a.B(function(){var g=c&&c instanceof a.U?c:new a.U(a.a.c(c)),n=f(b,g.$data,g),g=e(k,q,n,g,h);"replaceNode"==q&&(k=g,p=d(k))},null,
{wa:function(){return!p||!a.a.nb(p)},i:p&&"replaceNode"==q?p.parentNode:p})}return a.M.wb(function(d){a.Ab(b,c,h,d,"replaceNode")})};a.kd=function(b,d,g,k,q){function p(a,b){c(b,s);g.afterRender&&g.afterRender(b,a);s=null}function u(a,c){s=q.createChildContext(a,g.as,function(a){a.$index=c});var d=f(b,a,s);return e(null,"ignoreTargetNode",d,s,g)}var s;return a.B(function(){var b=a.a.c(d)||[];"undefined"==typeof b.length&&(b=[b]);b=a.a.Ka(b,function(b){return g.includeDestroyed||b===n||null===b||!a.a.c(b._destroy)});
a.l.w(a.a.Bb,null,[k,b,u,g,p])},null,{i:k})};var k=a.a.e.I();a.d.template={init:function(b,c){var d=a.a.c(c());if("string"==typeof d||d.name)a.f.xa(b);else{if("nodes"in d){if(d=d.nodes||[],a.H(d))throw Error('The "nodes" option must be a plain, non-observable array.');}else d=a.f.childNodes(b);d=a.a.jc(d);(new a.v.qa(b)).nodes(d)}return{controlsDescendantBindings:!0}},update:function(b,c,d,e,f){var g=c(),s;c=a.a.c(g);d=!0;e=null;"string"==typeof c?c={}:(g=c.name,"if"in c&&(d=a.a.c(c["if"])),d&&"ifnot"in
c&&(d=!a.a.c(c.ifnot)),s=a.a.c(c.data));"foreach"in c?e=a.kd(g||b,d&&c.foreach||[],c,b,f):d?(f="data"in c?f.createChildContext(s,c.as):f,e=a.Ab(g||b,f,c,b)):a.f.xa(b);f=e;(s=a.a.e.get(b,k))&&"function"==typeof s.k&&s.k();a.a.e.set(b,k,f&&f.ba()?f:n)}};a.h.ta.template=function(b){b=a.h.yb(b);return 1==b.length&&b[0].unknown||a.h.ad(b,"name")?null:"This template engine does not support anonymous templates nested within its templates"};a.f.Z.template=!0})();a.b("setTemplateEngine",a.Db);a.b("renderTemplate",
a.Ab);a.a.dc=function(a,c,d){if(a.length&&c.length){var e,f,g,k,l;for(e=f=0;(!d||e<d)&&(k=a[f]);++f){for(g=0;l=c[g];++g)if(k.value===l.value){k.moved=l.index;l.moved=k.index;c.splice(g,1);e=g=0;break}e+=g}}};a.a.ib=function(){function b(b,d,e,f,g){var k=Math.min,l=Math.max,m=[],h,n=b.length,q,p=d.length,s=p-n||1,u=n+p+1,t,v,x;for(h=0;h<=n;h++)for(v=t,m.push(t=[]),x=k(p,h+s),q=l(0,h-1);q<=x;q++)t[q]=q?h?b[h-1]===d[q-1]?v[q-1]:k(v[q]||u,t[q-1]||u)+1:q+1:h+1;k=[];l=[];s=[];h=n;for(q=p;h||q;)p=m[h][q]-
1,q&&p===m[h][q-1]?l.push(k[k.length]={status:e,value:d[--q],index:q}):h&&p===m[h-1][q]?s.push(k[k.length]={status:f,value:b[--h],index:h}):(--q,--h,g.sparse||k.push({status:"retained",value:d[q]}));a.a.dc(s,l,!g.dontLimitMoves&&10*n);return k.reverse()}return function(a,d,e){e="boolean"===typeof e?{dontLimitMoves:e}:e||{};a=a||[];d=d||[];return a.length<d.length?b(a,d,"added","deleted",e):b(d,a,"deleted","added",e)}}();a.b("utils.compareArrays",a.a.ib);(function(){function b(b,c,d,k,l){var m=[],
h=a.B(function(){var h=c(d,l,a.a.za(m,b))||[];0<m.length&&(a.a.qc(m,h),k&&a.l.w(k,null,[d,h,l]));m.length=0;a.a.ra(m,h)},null,{i:b,wa:function(){return!a.a.Qb(m)}});return{ca:m,B:h.ba()?h:n}}var c=a.a.e.I(),d=a.a.e.I();a.a.Bb=function(e,f,g,k,l){function m(b,c){w=q[c];v!==c&&(D[b]=w);w.qb(v++);a.a.za(w.ca,e);u.push(w);z.push(w)}function h(b,c){if(b)for(var d=0,e=c.length;d<e;d++)c[d]&&a.a.q(c[d].ca,function(a){b(a,d,c[d].ja)})}f=f||[];k=k||{};var r=a.a.e.get(e,c)===n,q=a.a.e.get(e,c)||[],p=a.a.fb(q,
function(a){return a.ja}),s=a.a.ib(p,f,k.dontLimitMoves),u=[],t=0,v=0,x=[],z=[];f=[];for(var D=[],p=[],w,C=0,B,E;B=s[C];C++)switch(E=B.moved,B.status){case "deleted":E===n&&(w=q[t],w.B&&(w.B.k(),w.B=n),a.a.za(w.ca,e).length&&(k.beforeRemove&&(u.push(w),z.push(w),w.ja===d?w=null:f[C]=w),w&&x.push.apply(x,w.ca)));t++;break;case "retained":m(C,t++);break;case "added":E!==n?m(C,E):(w={ja:B.value,qb:a.N(v++)},u.push(w),z.push(w),r||(p[C]=w))}a.a.e.set(e,c,u);h(k.beforeMove,D);a.a.q(x,k.beforeRemove?a.$:
a.removeNode);for(var C=0,r=a.f.firstChild(e),F;w=z[C];C++){w.ca||a.a.extend(w,b(e,g,w.ja,l,w.qb));for(t=0;s=w.ca[t];r=s.nextSibling,F=s,t++)s!==r&&a.f.gc(e,s,F);!w.Wc&&l&&(l(w.ja,w.ca,w.qb),w.Wc=!0)}h(k.beforeRemove,f);for(C=0;C<f.length;++C)f[C]&&(f[C].ja=d);h(k.afterMove,D);h(k.afterAdd,p)}})();a.b("utils.setDomNodeChildrenFromArrayMapping",a.a.Bb);a.W=function(){this.allowTemplateRewriting=!1};a.W.prototype=new a.O;a.W.prototype.renderTemplateSource=function(b,c,d,e){if(c=(9>a.a.C?0:b.nodes)?
b.nodes():null)return a.a.V(c.cloneNode(!0).childNodes);b=b.text();return a.a.ma(b,e)};a.W.sb=new a.W;a.Db(a.W.sb);a.b("nativeTemplateEngine",a.W);(function(){a.vb=function(){var a=this.$c=function(){if(!v||!v.tmpl)return 0;try{if(0<=v.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(b,e,f,g){g=g||u;f=f||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var k=b.data("precompiled");
k||(k=b.text()||"",k=v.template(null,"{{ko_with $item.koBindingContext}}"+k+"{{/ko_with}}"),b.data("precompiled",k));b=[e.$data];e=v.extend({koBindingContext:e},f.templateOptions);e=v.tmpl(k,b,e);e.appendTo(g.createElement("div"));v.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+a+" })()) }}"};this.addTemplate=function(a,b){u.write("<script type='text/html' id='"+a+"'>"+b+"\x3c/script>")};0<a&&(v.tmpl.tag.ko_code={open:"__.push($1 || '');"},
v.tmpl.tag.ko_with={open:"with($1) {",close:"} "})};a.vb.prototype=new a.O;var b=new a.vb;0<b.$c&&a.Db(b);a.b("jqueryTmplTemplateEngine",a.vb)})()})})();})();

8
interfaces/Glitter/templates/static/javascripts/moment.min.js vendored Executable file → Normal file
View File

File diff suppressed because one or more lines are too long

View File

@@ -19,7 +19,7 @@ var da = moment.defineLocale('da', {
longDateFormat : {
LT : 'HH:mm',
LTS : 'HH:mm:ss',
L : 'DD.MM.YYYY',
L : 'DD/MM/YYYY',
LL : 'D. MMMM YYYY',
LLL : 'D. MMMM YYYY HH:mm',
LLLL : 'dddd [d.] D. MMMM YYYY [kl.] HH:mm'

View File

@@ -28,7 +28,7 @@ function processRelativeTime(number, withoutSuffix, key, isFuture) {
var de = moment.defineLocale('de', {
months : 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'),
monthsShort : 'Jan._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.'.split('_'),
monthsShort : 'Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'),
monthsParseExact : true,
weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'),
weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'),

View File

@@ -13,9 +13,6 @@
var monthsShortDot = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_');
var monthsShort = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_');
var monthsParse = [/^ene/i, /^feb/i, /^mar/i, /^abr/i, /^may/i, /^jun/i, /^jul/i, /^ago/i, /^sep/i, /^oct/i, /^nov/i, /^dic/i];
var monthsRegex = /^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;
var es = moment.defineLocale('es', {
months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'),
monthsShort : function (m, format) {
@@ -27,13 +24,7 @@ var es = moment.defineLocale('es', {
return monthsShortDot[m.month()];
}
},
monthsRegex : monthsRegex,
monthsShortRegex : monthsRegex,
monthsStrictRegex : /^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,
monthsShortStrictRegex : /^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,
monthsParse : monthsParse,
longMonthsParse : monthsParse,
shortMonthsParse : monthsParse,
monthsParseExact : true,
weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'),
weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'),
weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'),

View File

View File

View File

@@ -1,99 +0,0 @@
//! moment.js locale configuration
//! locale : Hebrew [he]
//! author : Tomer Cohen : https://github.com/tomer
//! author : Moshe Simantov : https://github.com/DevelopmentIL
//! author : Tal Ater : https://github.com/TalAter
;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined'
&& typeof require === 'function' ? factory(require('../moment')) :
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
factory(global.moment)
}(this, (function (moment) { 'use strict';
var he = moment.defineLocale('he', {
months : 'ינואר_פברואר_מרץ_אפריל_מאי_יוני_יוליוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר'.split('_'),
monthsShort : 'ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יוליוג׳_ספט׳וק׳וב׳_דצמ׳'.split('_'),
weekdays : 'ראשון_שני_שלישי_רביעי_חמישיישי_שבת'.split('_'),
weekdaysShort : ׳׳׳׳׳_ו׳׳'.split('_'),
weekdaysMin : 'א_ב_ג_ד_ה_ו_ש'.split('_'),
longDateFormat : {
LT : 'HH:mm',
LTS : 'HH:mm:ss',
L : 'DD/MM/YYYY',
LL : 'D [ב]MMMM YYYY',
LLL : 'D [ב]MMMM YYYY HH:mm',
LLLL : 'dddd, D [ב]MMMM YYYY HH:mm',
l : 'D/M/YYYY',
ll : 'D MMM YYYY',
lll : 'D MMM YYYY HH:mm',
llll : 'ddd, D MMM YYYY HH:mm'
},
calendar : {
sameDay : '[היום ב־]LT',
nextDay : '[מחר ב־]LT',
nextWeek : 'dddd [בשעה] LT',
lastDay : '[אתמול ב־]LT',
lastWeek : '[ביום] dddd [האחרון בשעה] LT',
sameElse : 'L'
},
relativeTime : {
future : 'בעוד %s',
past : 'לפני %s',
s : 'מספר שניות',
m : 'דקה',
mm : '%d דקות',
h : 'שעה',
hh : function (number) {
if (number === 2) {
return 'שעתיים';
}
return number + ' שעות';
},
d : 'יום',
dd : function (number) {
if (number === 2) {
return 'יומיים';
}
return number + ' ימים';
},
M : 'חודש',
MM : function (number) {
if (number === 2) {
return 'חודשיים';
}
return number + ' חודשים';
},
y : 'שנה',
yy : function (number) {
if (number === 2) {
return 'שנתיים';
} else if (number % 10 === 0 && number !== 10) {
return number + ' שנה';
}
return number + ' שנים';
}
},
meridiemParse: /אחה"צ|לפנה"צ|אחרי הצהריים|לפני הצהריים|לפנות בוקר|בבוקר|בערב/i,
isPM : function (input) {
return /^(אחה"צ|אחרי הצהריים|בערב)$/.test(input);
},
meridiem : function (hour, minute, isLower) {
if (hour < 5) {
return 'לפנות בוקר';
} else if (hour < 10) {
return 'בבוקר';
} else if (hour < 12) {
return isLower ? 'לפנה"צ' : 'לפני הצהריים';
} else if (hour < 18) {
return isLower ? 'אחה"צ' : 'אחרי הצהריים';
} else {
return 'בערב';
}
}
});
return he;
})));

View File

View File

@@ -40,7 +40,7 @@ var nl = moment.defineLocale('nl', {
weekdays : 'zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag'.split('_'),
weekdaysShort : 'zo._ma._di._wo._do._vr._za.'.split('_'),
weekdaysMin : 'zo_ma_di_wo_do_vr_za'.split('_'),
weekdaysMin : 'Zo_Ma_Di_Wo_Do_Vr_Za'.split('_'),
weekdaysParseExact : true,
longDateFormat : {
LT : 'HH:mm',

View File

@@ -63,24 +63,7 @@ var pl = moment.defineLocale('pl', {
calendar : {
sameDay: '[Dziś o] LT',
nextDay: '[Jutro o] LT',
nextWeek: function () {
switch (this.day()) {
case 0:
return '[W niedzielę o] LT';
case 2:
return '[We wtorek o] LT';
case 3:
return '[W środę o] LT';
case 6:
return '[W sobotę o] LT';
default:
return '[W] dddd [o] LT';
}
},
nextWeek: '[W] dddd [o] LT',
lastDay: '[Wczoraj o] LT',
lastWeek: function () {
switch (this.day()) {

View File

@@ -11,8 +11,8 @@
var ptBr = moment.defineLocale('pt-br', {
months : 'janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro'.split('_'),
monthsShort : 'jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez'.split('_'),
months : 'Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro'.split('_'),
monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'),
weekdays : 'Domingo_Segunda-feira_Terça-feira_Quarta-feira_Quinta-feira_Sexta-feira_Sábado'.split('_'),
weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'),
weekdaysMin : 'Do_2ª_3ª_4ª_5ª_6ª_Sá'.split('_'),
@@ -41,7 +41,6 @@ var ptBr = moment.defineLocale('pt-br', {
future : 'em %s',
past : '%s atrás',
s : 'poucos segundos',
ss : '%d segundos',
m : 'um minuto',
mm : '%d minutos',
h : 'uma hora',

View File

View File

@@ -174,7 +174,7 @@ var ru = moment.defineLocale('ru', {
},
week : {
dow : 1, // Monday is the first day of the week.
doy : 4 // The week that contains Jan 4th is the first week of the year.
doy : 7 // The week that contains Jan 1st is the first week of the year.
}
});

View File

View File

View File

View File

@@ -50,8 +50,7 @@ legend,
color: white !important;
}
.hover-button,
.fileControls a:hover {
.hover-button {
opacity: 0.7;
}
@@ -76,14 +75,13 @@ legend,
background-color: #666;
}
.navbar-collapse.in .dropdown-menu, {
.navbar-collapse.in .dropdown-menu {
border: none;
}
.max-speed-input-clear,
.max-speed-input-clear:hover,
.nav-tabs>li>a:hover,
.fileControls a {
.nav-tabs>li>a:hover {
color: black;
}
@@ -111,7 +109,6 @@ legend,
color: #EBEBEB;
}
table,
.table-striped>tbody>tr:nth-child(even)>td,
.table>tbody>tr:nth-child(even)>td,
.table th,
@@ -159,9 +156,7 @@ select.form-control,
.retry-button, .retry-button-inactive,
.history-options-show-failed,
.queue-error-info,
.options-bad-status,
.history-failed-download:hover .retry-button .glyphicon:before,
.retry-button:hover .glyphicon:before {
.options-bad-status {
color: #F95151 !important;
}
@@ -180,11 +175,10 @@ tbody .caret {
color: #D6D6D6;
}
td.name .name-icons span,
td.name .name-ratings span,
.navbar-nav .open .dropdown-menu>li>a,
.dropdown-header,
#modal-help small,
.hover-button.glyphicon-forward,
pre {
color: #EBEBEB !important;
opacity: 1 !important;
@@ -209,10 +203,6 @@ hr {
border-bottom: none !important;
}
#modal-item-files .item-files-table {
border-bottom: 1px solid black;
}
.history-queue-swicher .nav-tabs>li>a,
.history-queue-swicher .nav-tabs>li.active>a {
border-bottom: none;
@@ -230,11 +220,6 @@ hr {
box-shadow: inset 1px 0px 1px rgba(0, 0, 0, 1);
}
.modal-content {
border-color: #727272;
border-top: none;
}
/* Placeholders - Will not work if grouped! */
::-webkit-input-placeholder {
color: #EBEBEB !important;

View File

@@ -52,8 +52,8 @@ h2 {
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
background-color: #ffffff;
border: 1px solid #cccccc;
margin-left: -220px;
margin-top: 5px;
margin-left: -150px;
margin-top: 4px;
}
.navbar-collapse.in .dropdown-menu a,
@@ -360,7 +360,7 @@ li.dropdown {
#feedback-slider:hover {
left: 0px;
height: 200px;
height: 340px;
opacity: 1;
}
@@ -404,6 +404,7 @@ thead {
tr th {
border-bottom: none !important;
visibility: hidden;
padding: 0px !important;
height: 0px;
}
@@ -526,23 +527,15 @@ tbody>tr>td:last-child {
}
.hover-button.glyphicon-play,
.hover-button.glyphicon-forward,
.hover-button.glyphicon-stop {
opacity: 1;
color: #474747;
}
.hover-button.disabled,
.hover-button.disabled:hover,
.name-options.disabled .hover-button,
.name-options.disabled .hover-button:hover {
.hover-button.disabled:hover {
cursor: not-allowed !important;
opacity: 0.1 !important;
pointer-events: none;
}
.name-options.disabled {
cursor: not-allowed !important;
}
.info-container {
@@ -624,7 +617,6 @@ td.name .row-wrap-text {
}
.queue-table td.name .name-options small,
.queue-table td.name .direct-unpack,
.queue-item-password {
opacity: 0.5;
}
@@ -633,17 +625,9 @@ td.name .row-wrap-text {
margin-left: 3px;
}
.queue-table td.name .direct-unpack-text {
max-width: calc(100% - 75px);
}
.queue-table td.name:hover .row-wrap-text {
max-width: calc(100% - 125px);
/* Change for each size! */
}
.queue-table td.name:hover .direct-unpack {
display: none;
max-width: calc(100% - 85px);
/* Change for each size! */
}
.queue-table td.name:hover .name-options {
@@ -664,23 +648,19 @@ td.name .row-wrap-text {
border: 1px solid #ccc;
}
td.name .name-icons {
td.name .name-ratings {
display: inline;
margin-left: 5px;
color: black !important;
text-decoration: none !important;
}
td.name .name-icons .glyphicon {
margin-left: 2px;
top: 2px;
font-weight: bold;
.queue-table td.name:hover .name-ratings {
display: none;
}
.glyphicon-chevron-down,
.glyphicon-chevron-up {
top: 2px;
font-weight: bold;
td.name .name-ratings .glyphicon {
margin-left: 2px;
}
tbody.no-downloads tr td {
@@ -772,8 +752,8 @@ tr.queue-item>td:first-child>a {
line-height: 24px;
overflow: visible;
margin-bottom: 0;
box-shadow: inset 0 0px 2px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: inset 0 0px 2px rgba(0, 0, 0, 0.2);
box-shadow: inset 0 0px 1px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: inset 0 0px 1px rgba(0, 0, 0, 0.3);
}
.progress>span,
@@ -789,35 +769,6 @@ tr.queue-item>td:first-child>a {
padding-right: 10px;
}
.item-files-table tr .fileControls{
float:right;
display:none;
}
.item-files-table tr.files-sortable:hover .fileControls{
float:right;
display:block;
margin-left:5px;
}
.progress .progress-bar .fileDetails {
display:inline;
text-align: left;
margin-left: 70px;
line-height: 25px;
position: absolute;
top: 0;
left: 0;
z-index: 2;
font-size: 12px;
color: #404040;
padding-right: 0px;
}
.progress .progress-bar .fileDetails>span {
float: left;
}
.progress strong {
font-size: 13px;
}
@@ -1084,7 +1035,7 @@ tr.queue-item>td:first-child>a {
opacity: 1;
}
.history-ratings .name-icons {
.history-ratings .name-ratings {
float: none !important;
}
@@ -1672,11 +1623,6 @@ input[name="nzbURL"] {
#modal-item-files .item-files-table .progress small {
color: #727272 !important;
margin-left: 5px;
}
#modal-item-files .item-files-table tr.files-sortable:hover .progress small {
display:none;
}
#modal-item-files .item-files-table td {
@@ -1864,9 +1810,10 @@ input[name="nzbURL"] {
}
@media screen and (max-width: 1200px) {
td.name .name-icons {
td.name .name-ratings {
margin-left: 0px;
margin-right: -5px;
display: block;
}
td.name .row-wrap-text {
@@ -1894,6 +1841,7 @@ input[name="nzbURL"] {
.history-queue-swicher .nav-tabs>li>a {
border-bottom: inherit;
}
}
@media screen and (min-width: 768px) {
@@ -1905,14 +1853,10 @@ input[name="nzbURL"] {
min-width: 715px;
}
.queue .sortable-placeholder td {
padding: 9px 0px 8px !important;
}
.queue-table .buttonMoveToBottom,
.queue-table .buttonMoveToTop {
display: inline;
}
}
@media screen and (min-height: 800px) {
@@ -1977,6 +1921,49 @@ input[name="nzbURL"] {
}
}
/***
SPECIAL FOR FIREFOX
It uses very high CPU for anything animated (Sep 2015)
Disable animations on progress-bar and make the History-'processing' a block animation
Can be removed if it's performance gets better in the future..
***/
@supports (-moz-transform: translate(0, 0)) {
.progress-bar {
transform: none !important;
animation: none !important;
transition: none !important;
}
@keyframes stretchdelay {
0%, 60% {
transform: scaleY(0.4);
}
61%, 100% {
transform: scaleY(1.0);
}
}
.processing-download > div {
animation: stretchdelay 2s infinite linear;
}
.processing-download .loader-bar-two {
animation-delay: 0.2s;
}
.processing-download .loader-bar-three {
animation-delay: 0.4s;
}
.processing-download .loader-bar-four {
animation-delay: 0.6s;
}
.queue-table td.name input {
margin-left: -2px;
}
}
/***
Bootstrap overwrites

View File

@@ -132,21 +132,12 @@ h2 {
max-width: calc(100% - 45px);
}
.queue-table .buttonMoveToBottom,
.queue-table .buttonMoveToTop {
display: none;
}
tr.queue-item>td:first-child>a {
margin-top: 3px;
}
.queue-table td.name .direct-unpack {
display: none;
}
.queue-table td.name .name-options {
display: inline-block;
display: block;
}
.queue-table td.name .name-options small {

View File

@@ -1,7 +1,7 @@
Plush for SABnzbd 0.6.x | Feb. 21 2010
assembled by pairofdimes - see LICENSE-CC.txt
https://forums.sabnzbd.org contributions welcome
http://forums.sabnzbd.org contributions welcome
======================
THANKS TO CONTRIBUTORS

View File

@@ -28,7 +28,6 @@
<link rel="apple-touch-icon" sizes="152x152" href="${path}staticcfg/ico/apple-touch-icon-152x152-precomposed.png" />
<link rel="apple-touch-icon" sizes="180x180" href="${path}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
<link rel="apple-touch-icon" sizes="192x192" href="${path}staticcfg/ico/android-192x192.png" />
<link rel="mask-icon" href="${path}staticcfg/ico/safari-pinned-tab.svg" color="#383F45">
<script type="text/javascript" src="${path}static/javascripts/lib.js?$version"></script>
@@ -100,7 +99,7 @@
#if $loadavg#$T('ft-sysload'):&nbsp;<span id="loadavg">$loadavg</span>#end if#
</div>
<div id="nav_text_left">
<span id="warning_box"><b><a href="${path}status/#tabs-warnings" id="last_warning"><span id="have_warnings">$have_warnings</span> $T('warnings')</a></b></span>
<span id="warning_box"><b><a href="${path}status/#tabs-warnings" id="last_warning" title="#echo $last_warning.replace("\n"," ").replace('"',"'") #"><span id="have_warnings">$have_warnings</span> $T('warnings')</a></b></span>
#if $pane=="Main"#
#if $new_release#&sdot; <a href="$new_rel_url" id="new_release" target="_blank">$T('Plush-updateAvailable').replace(' ','&nbsp;')</a>#end if#
#end if#

View File

@@ -23,8 +23,8 @@
<div id="help_modal">
<table>
<tr><td><strong>$T('menu-wiki'):</strong></td><td><a href="$helpuri$help_uri" target="_blank">$helpuri$help_uri</a></td></tr>
<tr><td><strong>$T('menu-forums'):</strong></td><td><a href="https://forums.sabnzbd.org/" target="_blank">https://forums.sabnzbd.org/</a></td></tr>
<tr><td><strong>$T('menu-irc'):</strong></td><td><a href="https://sabnzbd.org/live-chat.html" target="_blank">https://sabnzbd.org/live-chat.html</a></td></tr>
<tr><td><strong>$T('menu-forums'):</strong></td><td><a href="http://forums.sabnzbd.org/" target="_blank">http://forums.sabnzbd.org/</a></td></tr>
<tr><td><strong>$T('menu-irc'):</strong></td><td><a href="http://www.sabnzbd.org/live-chat/" target="_blank">http://www.sabnzbd.org/live-chat/</a></td></tr>
</table>
<div class="sabnzbd_logo main_sprite_container sprite_sabnzbdplus_logo"></div>
<p><strong>SABnzbd $T('version'):</strong> $version</p>

View File

@@ -6,7 +6,7 @@
$.plush.queuenoofslots = $noofslots;
$.plush.SetQueueSpeedLimit('$speedlimit', '$speedlimit_abs');
$.plush.SetQueueFinishAction('$finishaction');
$.plush.SetWarnings($have_warnings,"");
$.plush.SetWarnings($have_warnings,"<!--#echo $last_warning.replace("\n"," ").replace('"',"'").replace('\\','\\\\') #-->");
$.plush.SetQueuePauseInfo(<!--#if $paused#-->true<!--#else#-->false<!--#end if#-->,'$pause_int');
$.plush.SetQueueETAStats("<!--#if float($kbpersec) > 1023 #-->$speed<!--#else#--><!--#echo "%.0f" % float($kbpersec)#--> K<!--#end if#-->",<!--#echo "%.0f" % float($kbpersec)#-->,'$timeleft','$T('eta'): $eta');
@@ -56,7 +56,7 @@
</td>
<td class="download-title" <!--#if $rating_enable#-->style="width:35%"<!--#end if#-->>
<a href="nzb/$slot.nzo_id/" title="$T('status'): $T('post-'+$slot.status)<br/>$T('nzo-age'): $slot.avg_age<br/><!--#if $slot.mbmissing!="0.00"#-->$T('missingArt'): $slot.mbmissing $T('MB')<!--#end if#-->">$slot.filename.replace('.', '.&#8203;').replace('_', '_&#8203;')<!--#if $slot.password#--> / $slot.password<!--#end if#--></a>
<a href="nzb/$slot.nzo_id/" title="$T('status'): $T('post-'+$slot.status)<br/>$T('nzo-age'): $slot.avg_age<br/><!--#if $slot.missing#-->$T('missingArt'): $slot.missing<!--#end if#-->">$slot.filename.replace('.', '.&#8203;').replace('_', '_&#8203;')<!--#if $slot.password#--> / $slot.password<!--#end if#--></a>
</td>
<!--#if $rating_enable#-->

View File

@@ -146,7 +146,7 @@ jQuery(function($){
$("#plush_options").colorbox({ inline:true, href:"#plush_options_modal", title:$("#plush_options").text(),
innerWidth:"375px", innerHeight:"350px", initialWidth:"375px", initialHeight:"350px", speed:0, opacity:0.7
});
// Save the type of speedlimit display
$('#maxSpeed-label').change(function() {
$.plush.speedLimitType = $(this).val();
@@ -159,19 +159,19 @@ jQuery(function($){
$('#maxSpeed-label').val($.plush.speedLimitType)
// Max Speed main menu input -- don't change value on refresh when focused
$("#maxSpeed-option").focus(function(){
$.plush.focusedOnSpeedChanger = true;
}).blur(function(){
$.plush.focusedOnSpeedChanger = false;
$("#maxSpeed-option").focus(function(){
$.plush.focusedOnSpeedChanger = true;
}).blur(function(){
$.plush.focusedOnSpeedChanger = false;
}).keyup(function (e) {
// Catch the enter
if (e.keyCode == 13) {
$("#maxSpeed-enable").click()
}
})
// Submit the new speedlimit
$("#maxSpeed-enable, #maxSpeed-disable").click( function(e) {
$("#maxSpeed-enable, #maxSpeed-disable").click( function(e) {
// Remove
if ($(e.target).attr('id')=="maxSpeed-disable") {
$('#maxSpeed-option').val('');
@@ -496,7 +496,7 @@ jQuery(function($){
};
// static-element tooltips
$('body').delegate('#pausefor_title, #time-left, #multi_delete, #explain-blockRefresh, #pause_resume, #hist_purge, #queueTable td.download-title a, #queueTable td.eta span, #queueTable td.options .icon_nzb_remove, #historyTable td.options .icon_nzb_remove, #historyTable td div.icon_history_verbose', 'mouseover mouseout mousemove', function(event) {
$('body').delegate('#pausefor_title, #last_warning, #time-left, #multi_delete, #explain-blockRefresh, #pause_resume, #hist_purge, #queueTable td.download-title a, #queueTable td.eta span, #queueTable td.options .icon_nzb_remove, #historyTable td.options .icon_nzb_remove, #historyTable td div.icon_history_verbose', 'mouseover mouseout mousemove', function(event) {
var link = this,
$link = $(this);
@@ -1095,7 +1095,7 @@ $("a","#multiops_inputs").click(function(e){
title:function(){return $(this).text().replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");},
innerWidth:"80%", innerHeight:"300px", initialWidth:"80%", initialHeight:"300px", speed:0, opacity:0.7 });
// modal for reporting issues
// modal for reporting issues
$("#historyTable .modal-report").colorbox({ inline:true,
href: function(){return "#report-"+$(this).parent().parent().parent().attr('id');},
title:function(){return $(this).text();},
@@ -1252,7 +1252,7 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
$('#queue-pagination span').removeClass('loading'); // Remove spinner graphic from pagination
$('#manual_refresh_wrapper').removeClass('refreshing'); // Refresh state notification
},
error: function(xhr){
error: function(xhr){
// Only reason for a 404 error could be a login failure -> redirect
if(xhr.status == 404) {
document.location=document.location;
@@ -1320,7 +1320,7 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
$('#history .avg_rate').rateit({readonly: true, resetable: false, step: 0.5});
$('#history .avg_rate').each(function() { $(this).rateit('value', $(this).attr('value') / 2); });
$('#history .user_combo option').filter(function() {
return $(this).attr('value') == $(this).parent().parent().find('input.user_combo').attr('value');
return $(this).attr('value') == $(this).parent().parent().find('input.user_combo').attr('value');
}).attr('selected', true);
$('#history-pagination span').removeClass('loading'); // Remove spinner graphic from pagination
}
@@ -1342,11 +1342,11 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
// For switching using the select
if(!speedLimit) speedLimit = $.plush.speedLimit;
if(speedLimitAbs == undefined) speedLimitAbs = $.plush.speedLimitAbs;
// Save
// Save
$.plush.speedLimit = speedLimit;
$.plush.speedLimitAbs = speedLimitAbs;
// How do we format?
switch($.plush.speedLimitType) {
case '%':
@@ -1360,11 +1360,11 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
speedlimitDisplay = speedLimitAbs/1024/1024;
break;
}
// In case nothing and we make the displaying of the float more pretty
speedlimitDisplay = (isNaN(speedlimitDisplay) || speedlimitDisplay == '0') ? '' : speedlimitDisplay;
speedlimitDisplay = Math.round(speedlimitDisplay*10)/10;
// Update
if ($("#maxSpeed-option").val() != speedlimitDisplay && !$.plush.focusedOnSpeedChanger)
$("#maxSpeed-option").val(speedlimitDisplay);

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 726 B

After

Width:  |  Height:  |  Size: 729 B

View File

@@ -1,7 +1,7 @@
<!--#set global $pane="Status"#-->
<!--#set global $help_uri="GUI+Status-0-7"#-->
<!--#include $webdir + "/_inc_header.tmpl"#-->
<!--#import datetime#-->
<div class="tabs">
<ul>
<li><a href="#tabs-warnings">$T('lastWarnings')</a></li>
@@ -33,11 +33,7 @@
<!--#for $warn in $warnings#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<!--#set when = datetime.datetime.fromtimestamp($warn.time) #-->
<td>$when</td>
<td>$warn.type</td>
<td>$warn.text</td>
</tr>
<td>$warn.replace("\n","</td><td>", 2)</td></tr>
<!--#end for#-->
</table>
<!--#else#-->

View File

@@ -20,7 +20,6 @@
<link rel="apple-touch-icon" sizes="152x152" href="./staticcfg/ico/apple-touch-icon-152x152-precomposed.png" />
<link rel="apple-touch-icon" sizes="180x180" href="./staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
<link rel="apple-touch-icon" sizes="192x192" href="./staticcfg/ico/android-192x192.png" />
<link rel="mask-icon" href="./staticcfg/ico/safari-pinned-tab.svg" color="#383F45">
<script type="text/javascript" src="static/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="static/PlotKit/PlotKit.js"></script>
@@ -79,6 +78,7 @@
input = input.replace(/%0e/g,'05');
input = input.replace(/%e/g,'5');
input = input.replace(/%fn/g,'$T("sort-File")');
input = input.replace(/%dn/g,'$T("sort-Folder")');
document.getElementById('previewtv').innerHTML = getOutput(input);
}
@@ -1134,7 +1134,7 @@ function loadingJSON(){
<li><a style="text-decoration:underline;cursor:pointer;" onclick="if(confirm('$T('shutdownOK?')')){shutdown()}">$T('link-shutdown')</a></li>
<br/>
<li><a href="$helpuri" target="_blank">$T('menu-wiki')</a></li>
<li><a href="https://forums.sabnzbd.org" target="_blank">$T('menu-forums')</a></li>
<li><a href="http://forums.sabnzbd.org" target="_blank">$T('menu-forums')</a></li>
<li><a href="http://sabnzbd.org/live-chat/" target="_blank">$T('menu-irc')</a></li>
</ul>
<!--<input type="checkbox" name="enable_speedlimit" />-->

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +1,2 @@
dojo.hostenv.conditionalLoadModule({"common": ["MochiKit.MochiKit"]});
dojo.hostenv.moduleLoaded("MochiKit.*");
dojo.hostenv.conditionalLoadModule({"common": ["MochiKit.MochiKit"]});
dojo.hostenv.moduleLoaded("MochiKit.*");

View File

@@ -1,4 +1,3 @@
<!--#import datetime#-->
<script type="text/javascript">
window.onload = function() {
location = "../#/status"
@@ -37,10 +36,7 @@ $T('logging'):
<!--#if $warnings#-->
<h2>$T('lastWarnings') (<a class="config" onClick="lr('status/clearwarnings','', '-1', this.parentNode.id);">$T('clearWarnings')</a>)</h2>
<!--#for $warn in $warnings#-->
<!--#set when = datetime.datetime.fromtimestamp($warn.time) #-->
$when<br>
$warn.type<br>
$warn.text<br>
$warn<br/>
<!--#end for#-->
<!--#end if#-->

View File

@@ -6,7 +6,7 @@
<link rel="stylesheet" type="text/css" href="../staticcfg/bootstrap/css/bootstrap.min.css?v=$version"/>
<link rel="stylesheet" type="text/css" href="static/style.css?v=$version"/>
<link rel="shortcut icon" href="../staticcfg/ico/favicon.ico?v=$version" />
<script type="text/javascript" src="../staticcfg/js/jquery-3.2.1.min.js?v=$version"></script>
<script type="text/javascript" src="../staticcfg/js/jquery-3.1.1.min.js?v=$version"></script>
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
</head>
<body>

View File

@@ -16,10 +16,6 @@
<input type="radio" name="lang" id="$l" value="$l" <!--#if $lang == $l then 'checked="checked"' else ''#--> />
</label>
<!--#end for#-->
<!--#if not $languages#-->
<hr>
No language files detected. Please run <code>python tools/make_mo.py</code> once and restart SABnzbd, or contact your package provider.
<!--#end if#-->
<div class="spacer"></div>
</div>
</div>

View File

@@ -87,7 +87,7 @@
</div>
<div class="col-md-5">
<div class="clearfix"></div>
<iframe style="float: right; width: 325px; height: 325px;" frameborder="0" src="https://sabnzbd.org/wizard#$language"></iframe>
<iframe style="float: right; width: 315px; height: 315px;" frameborder="0" src="https://resources.sabnzbd.org/wizard/ad/$language"></iframe>
</div>
</div>
<input type="hidden" name="session" value="$session" />

View File

@@ -1,11 +1,11 @@
The original author of SABnzbd based his work on Pynewsleecher by Freddy@madcowdesease.org.
Few parts of Pynewsleecher have survived the generations of SABnzbd in a
recognizable form.
Still, we wish to thank Freddy for his inspiration.
The home of the Pynewsleecher project:
http://www.madcowdisease.org/mcd/pynewsleecher
The software does not carry any license information.
The original author of SABnzbd based his work on Pynewsleecher by Freddy@madcowdesease.org.
Few parts of Pynewsleecher have survived the generations of SABnzbd in a
recognizable form.
Still, we wish to thank Freddy for his inspiration.
The home of the Pynewsleecher project:
http://www.madcowdisease.org/mcd/pynewsleecher
The software does not carry any license information.

View File

@@ -1,8 +1,8 @@
On http://www.brunningonline.net/simon/blog/archives/001835.html,
the author licensed SysTrayIcon.py under a variant of the WTFPL:
> Any road up, help yourself. Consider SysTrayIcon.py to be under an
> "Aleister Crowley" style license - "Do what thou wilt shall be the
> only law".
>
> Err, but don't sue me if it doesn't work. ;-)
On http://www.brunningonline.net/simon/blog/archives/001835.html,
the author licensed SysTrayIcon.py under a variant of the WTFPL:
> Any road up, help yourself. Consider SysTrayIcon.py to be under an
> "Aleister Crowley" style license - "Do what thou wilt shall be the
> only law".
>
> Err, but don't sue me if it doesn't work. ;-)

BIN
osx/par2/par2-classic Executable file
View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -1,11 +1,11 @@
#
# SABnzbd Translation Template file EMAIL
# Copyright 2011-2017 The SABnzbd-Team
# Copyright (C) 2011-2015 by the SABnzbd Team
# team@sabnzbd.org
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-2.2.0-develop\n"
"Project-Id-Version: SABnzbd-0.8.x\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: shypike@sabnzbd.org\n"
"Language-Team: LANGUAGE <LL@li.org>\n"

View File

@@ -7,15 +7,15 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: shypike <Unknown>\n"
"Language-Team: Danish <da@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
"X-Generator: Launchpad (build 18416)\n"
"X-Launchpad-Export-Date: 2015-04-26 05:45+0000\n"
"X-Generator: Launchpad (build 17430)\n"
#: email/email.tmpl:1
msgid ""

View File

@@ -7,15 +7,15 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: Thomas Lucke (Lucky) <Unknown>\n"
"Language-Team: German <de@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
"X-Generator: Launchpad (build 18416)\n"
"X-Launchpad-Export-Date: 2015-04-26 05:45+0000\n"
"X-Generator: Launchpad (build 17430)\n"
#: email/email.tmpl:1
msgid ""

View File

@@ -7,15 +7,15 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: shypike <Unknown>\n"
"Language-Team: Spanish <es@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
"X-Generator: Launchpad (build 18416)\n"
"X-Launchpad-Export-Date: 2015-04-26 05:45+0000\n"
"X-Generator: Launchpad (build 17430)\n"
#: email/email.tmpl:1
msgid ""

View File

@@ -7,15 +7,15 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: Matti Ylönen <Unknown>\n"
"Language-Team: Finnish <fi@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
"X-Generator: Launchpad (build 18416)\n"
"X-Launchpad-Export-Date: 2015-04-26 05:45+0000\n"
"X-Generator: Launchpad (build 17430)\n"
#: email/email.tmpl:1
msgid ""

View File

@@ -7,15 +7,15 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: Fox Ace <Unknown>\n"
"Language-Team: French <fr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
"X-Generator: Launchpad (build 18416)\n"
"X-Launchpad-Export-Date: 2015-04-26 05:45+0000\n"
"X-Generator: Launchpad (build 17430)\n"
#: email/email.tmpl:1
msgid ""

Some files were not shown because too many files have changed in this diff Show More