mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2026-01-03 21:20:24 -05:00
Compare commits
588 Commits
2.2.0Alpha
...
2.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d69796d351 | ||
|
|
a2d5713477 | ||
|
|
ba7d906bea | ||
|
|
68f78b0e71 | ||
|
|
d5cd0180d8 | ||
|
|
10265bdfb4 | ||
|
|
3d0d67bffc | ||
|
|
11bd16a653 | ||
|
|
248f2da8a6 | ||
|
|
7b7aaaf467 | ||
|
|
0030e4dd36 | ||
|
|
c3013d67b4 | ||
|
|
ddb7f2a40c | ||
|
|
167a94736e | ||
|
|
8b3e30f0a1 | ||
|
|
c383fa88fb | ||
|
|
ee72c1e4d5 | ||
|
|
a80bd826d6 | ||
|
|
63e6d45bb1 | ||
|
|
61300db1fb | ||
|
|
23d005fc36 | ||
|
|
871b351656 | ||
|
|
6b53b9934e | ||
|
|
e038e08a60 | ||
|
|
8300cb7762 | ||
|
|
ec8302717f | ||
|
|
6b872b44db | ||
|
|
6e48ebccc7 | ||
|
|
1cd24d1fd0 | ||
|
|
fa6bb79f53 | ||
|
|
22e9086e09 | ||
|
|
408b84e02d | ||
|
|
16ac4dce21 | ||
|
|
5ce1554a46 | ||
|
|
2d84ed6813 | ||
|
|
06dc5b181e | ||
|
|
37b759fece | ||
|
|
3f8430780d | ||
|
|
9e9bc5f3b0 | ||
|
|
85204105c2 | ||
|
|
f13339d64d | ||
|
|
fa5b44be99 | ||
|
|
08bc7c5b9d | ||
|
|
d9b5dd549a | ||
|
|
8ec53a3bce | ||
|
|
0aac9a5e5c | ||
|
|
11a880d040 | ||
|
|
67b66beb13 | ||
|
|
1da633442b | ||
|
|
13de40881e | ||
|
|
1c6419ea65 | ||
|
|
a2adeffc1a | ||
|
|
71fa3c544a | ||
|
|
b739fb7f07 | ||
|
|
860728beae | ||
|
|
1bdbf1c6a8 | ||
|
|
abbed4cd77 | ||
|
|
d06c11673f | ||
|
|
67d67f5ff6 | ||
|
|
2386d65b84 | ||
|
|
be638ecca1 | ||
|
|
c32bcea3f6 | ||
|
|
bf0aa6569b | ||
|
|
e64df8ed60 | ||
|
|
f7c3a4381d | ||
|
|
55efb34f03 | ||
|
|
ae8e9d83f1 | ||
|
|
c4406df73f | ||
|
|
12004802b6 | ||
|
|
6068ca6376 | ||
|
|
613ba49165 | ||
|
|
9cd21d84ee | ||
|
|
8d813f125e | ||
|
|
cb66bc28ab | ||
|
|
30b13b1856 | ||
|
|
67a133068c | ||
|
|
731a3bcb22 | ||
|
|
724ec8ca9f | ||
|
|
d28f775c71 | ||
|
|
a834c1c7a7 | ||
|
|
04e595e706 | ||
|
|
46c28dbf68 | ||
|
|
8594bfe817 | ||
|
|
e61a01512b | ||
|
|
657e3bb594 | ||
|
|
bbbdca6a00 | ||
|
|
a95c705e4c | ||
|
|
446c5ba80f | ||
|
|
7641c0e1cc | ||
|
|
42fdd9c890 | ||
|
|
dca63878db | ||
|
|
dc67fc414c | ||
|
|
90be3cc5a0 | ||
|
|
42cdba5ce3 | ||
|
|
9c069cfb2c | ||
|
|
544b420baa | ||
|
|
094c96f270 | ||
|
|
aa7bad56f0 | ||
|
|
977f4e1036 | ||
|
|
e05a98d22b | ||
|
|
452e955a1e | ||
|
|
29ec4d9a23 | ||
|
|
22517a7cd7 | ||
|
|
a724f6a979 | ||
|
|
715b25b52f | ||
|
|
bcc4dd75cf | ||
|
|
97711ca82e | ||
|
|
e782237f27 | ||
|
|
4d3f370b3a | ||
|
|
07f6717728 | ||
|
|
35b4aa6b7a | ||
|
|
52bb156c08 | ||
|
|
4361d82ddd | ||
|
|
a7a04d912c | ||
|
|
ce558b0850 | ||
|
|
8ab7c294ee | ||
|
|
306228462e | ||
|
|
017cf8f285 | ||
|
|
03cdf6ed5d | ||
|
|
cf347a8e90 | ||
|
|
9e7a8468e2 | ||
|
|
f06afe43e1 | ||
|
|
4c2445485a | ||
|
|
103c46e2b4 | ||
|
|
b4922d69a2 | ||
|
|
110a06a3cd | ||
|
|
fb301eb5c8 | ||
|
|
6f0f67110f | ||
|
|
1562c3560b | ||
|
|
848721da84 | ||
|
|
9813bc237f | ||
|
|
b39fe059c6 | ||
|
|
a56c770a8b | ||
|
|
127d7ab40c | ||
|
|
4fb7246082 | ||
|
|
8c42237d51 | ||
|
|
6a87f0c4e4 | ||
|
|
e3bf0edad8 | ||
|
|
e35d9e4db3 | ||
|
|
c617d4321a | ||
|
|
0fd3a2881f | ||
|
|
f8630a878c | ||
|
|
7f6ef5e204 | ||
|
|
0c1f7633de | ||
|
|
b7d5d49c84 | ||
|
|
9911b93ece | ||
|
|
eeaad00968 | ||
|
|
e1bb8459e3 | ||
|
|
65c3ac0cc0 | ||
|
|
413c02a80f | ||
|
|
547d4dbf0a | ||
|
|
65e70a431c | ||
|
|
f85f4de5ff | ||
|
|
97644dea16 | ||
|
|
0a6105ebc1 | ||
|
|
de3d4f8d14 | ||
|
|
80f118f304 | ||
|
|
f337053aea | ||
|
|
2a4b49a679 | ||
|
|
ebe526f8cf | ||
|
|
926cd7b132 | ||
|
|
99667aa410 | ||
|
|
67cab3465e | ||
|
|
ce68a0654b | ||
|
|
dffdc3ae1f | ||
|
|
28e5311c6c | ||
|
|
ebf0526420 | ||
|
|
a2f73ca1f0 | ||
|
|
8ca150c48d | ||
|
|
d765fa09f1 | ||
|
|
878d68d343 | ||
|
|
fc7bd78dfa | ||
|
|
aca6ed360c | ||
|
|
5c0a10e16b | ||
|
|
d9b32261e7 | ||
|
|
8d8ce52193 | ||
|
|
53672d1d73 | ||
|
|
07d316ed4f | ||
|
|
8aa57bf406 | ||
|
|
d369097573 | ||
|
|
f4960715fa | ||
|
|
83ba676c43 | ||
|
|
12cca9dea1 | ||
|
|
5f52535c44 | ||
|
|
2f95410ab4 | ||
|
|
5749c0c008 | ||
|
|
73c71ef4bf | ||
|
|
6bc1c51013 | ||
|
|
bcdd3302a6 | ||
|
|
0fc3b60054 | ||
|
|
e9b0d4d691 | ||
|
|
1cc2e25cda | ||
|
|
0dc2c6687d | ||
|
|
b061e582b6 | ||
|
|
690731fd79 | ||
|
|
068b7ed7f5 | ||
|
|
aae2fdcd32 | ||
|
|
d3628a1eb7 | ||
|
|
9cc8176d87 | ||
|
|
27f83f21be | ||
|
|
5e31a31a21 | ||
|
|
a077012478 | ||
|
|
fed0e0f765 | ||
|
|
fbdbf7ab22 | ||
|
|
f013d38d00 | ||
|
|
93b9c8a6da | ||
|
|
e3a779bbc6 | ||
|
|
adfce8c8b6 | ||
|
|
a49d68c0db | ||
|
|
e4156e76d1 | ||
|
|
35b66eea0e | ||
|
|
4d0cf8d45f | ||
|
|
ad9fef5f41 | ||
|
|
6235174995 | ||
|
|
4b9ca989c4 | ||
|
|
4d54aecceb | ||
|
|
11eeb6f2e9 | ||
|
|
00364b1317 | ||
|
|
6666663f78 | ||
|
|
3d6dfec47a | ||
|
|
0f3d44aa4b | ||
|
|
d2d2471950 | ||
|
|
b71343e8ab | ||
|
|
489f3f4ba0 | ||
|
|
3765e8c350 | ||
|
|
28d4f527b8 | ||
|
|
1d8af8f97d | ||
|
|
829ef4bee8 | ||
|
|
7e40c12e47 | ||
|
|
37d8d659f5 | ||
|
|
0a29291be2 | ||
|
|
7f3a5f309b | ||
|
|
60ec5f9191 | ||
|
|
d03e801e74 | ||
|
|
56bf484e77 | ||
|
|
66674469d5 | ||
|
|
09a86683e5 | ||
|
|
fc9a13879e | ||
|
|
73f0885566 | ||
|
|
090b22f193 | ||
|
|
f9c092ae8f | ||
|
|
4246bc2aea | ||
|
|
c4fa047393 | ||
|
|
22e4d24a71 | ||
|
|
96fccff63b | ||
|
|
6b46a15b49 | ||
|
|
98cc0dad55 | ||
|
|
6fdeab6948 | ||
|
|
553dd04cea | ||
|
|
0baa316a72 | ||
|
|
3ac209f9a9 | ||
|
|
ec55f64a8a | ||
|
|
932e1e577b | ||
|
|
56d5b1d9f8 | ||
|
|
28bdebb147 | ||
|
|
827fc7b64e | ||
|
|
f5dde93644 | ||
|
|
60c574828e | ||
|
|
14ca8342f9 | ||
|
|
5d5b1bf053 | ||
|
|
ea4cdba3eb | ||
|
|
d6ecebc75a | ||
|
|
b0af6a1761 | ||
|
|
6e350f30fc | ||
|
|
169137c631 | ||
|
|
6393dc0dca | ||
|
|
1a27b4824b | ||
|
|
2b59a383cf | ||
|
|
efbaaade22 | ||
|
|
cf7e7b1f62 | ||
|
|
2c1746a92d | ||
|
|
a2e57fd3d8 | ||
|
|
932f8d9176 | ||
|
|
5ffd82da89 | ||
|
|
8b3de191d9 | ||
|
|
83d8a23e2c | ||
|
|
58b107a4b5 | ||
|
|
a40609b39d | ||
|
|
0faa5d3dff | ||
|
|
374239777e | ||
|
|
9a7701d7e6 | ||
|
|
01ff04f338 | ||
|
|
eac39767dd | ||
|
|
0d0adf99fa | ||
|
|
16905ce34f | ||
|
|
5287fa8a0c | ||
|
|
b72ab4fb8e | ||
|
|
81054c675c | ||
|
|
7362be8748 | ||
|
|
b4ba2b3463 | ||
|
|
8bed6938c1 | ||
|
|
ecf16f6201 | ||
|
|
bf240357df | ||
|
|
ddcf447957 | ||
|
|
d9642611e2 | ||
|
|
0018c6f263 | ||
|
|
6398bfa12f | ||
|
|
01dfb7538d | ||
|
|
3f0d4675b6 | ||
|
|
f23c5caf80 | ||
|
|
bd22430b26 | ||
|
|
1189a7fdbc | ||
|
|
f3aa4f84fc | ||
|
|
ea26ce4700 | ||
|
|
a1e649b7e2 | ||
|
|
3b9f2b2cf0 | ||
|
|
7333d19e1c | ||
|
|
232d537d23 | ||
|
|
c6e17e7bcb | ||
|
|
54c6fd55dd | ||
|
|
0625aa1ca8 | ||
|
|
83643f3298 | ||
|
|
ff3c46fe1f | ||
|
|
0930f0dcee | ||
|
|
3221257310 | ||
|
|
8048a73156 | ||
|
|
ea552cd402 | ||
|
|
dcb925f621 | ||
|
|
cce91e1985 | ||
|
|
e17d417c2e | ||
|
|
a69f5bd2df | ||
|
|
97e53eb4d3 | ||
|
|
a6da2b7bee | ||
|
|
4a21e7c217 | ||
|
|
9bd3c7be44 | ||
|
|
434f5c4b2d | ||
|
|
d3cc4f9f07 | ||
|
|
a16aa17c17 | ||
|
|
68445d0409 | ||
|
|
32b68a45cc | ||
|
|
345f8359cc | ||
|
|
81f9886584 | ||
|
|
adbc618808 | ||
|
|
41eafc6b4b | ||
|
|
9f18d8e8c1 | ||
|
|
8c2c853166 | ||
|
|
97914906a0 | ||
|
|
f1ce4ed19b | ||
|
|
99185d8151 | ||
|
|
385b6b7ade | ||
|
|
81ea513f8c | ||
|
|
336b1ddba3 | ||
|
|
7274973322 | ||
|
|
af132965de | ||
|
|
5586742886 | ||
|
|
5868b51490 | ||
|
|
7f17a38b9b | ||
|
|
415e843ebb | ||
|
|
7ffc1192bb | ||
|
|
945e769a03 | ||
|
|
86c7fb86cc | ||
|
|
ff20f3f620 | ||
|
|
e8bef94706 | ||
|
|
d05fe2d680 | ||
|
|
4f8cc3f697 | ||
|
|
6fa619fa37 | ||
|
|
a43f5369ea | ||
|
|
2040173dc2 | ||
|
|
a15b7ec7ac | ||
|
|
6adcf2ce10 | ||
|
|
e756b9b5c1 | ||
|
|
b3de745849 | ||
|
|
77f3dc18b5 | ||
|
|
6b2f15f82e | ||
|
|
570e58611d | ||
|
|
6b69010aec | ||
|
|
e3e2fb7057 | ||
|
|
ece04909e7 | ||
|
|
963920eb88 | ||
|
|
cf5fa542b6 | ||
|
|
1be7e99754 | ||
|
|
14e3334682 | ||
|
|
b1e033dd55 | ||
|
|
111feb1b57 | ||
|
|
886b23d034 | ||
|
|
f2590792b3 | ||
|
|
02a497ed74 | ||
|
|
48df0eed84 | ||
|
|
0f58cbb671 | ||
|
|
9d71670f59 | ||
|
|
7f838ebb38 | ||
|
|
ef1cb05bc8 | ||
|
|
c14b3ed82a | ||
|
|
792e337936 | ||
|
|
6cd2e66052 | ||
|
|
728022b86d | ||
|
|
7718446313 | ||
|
|
66dea54053 | ||
|
|
f19b60bd41 | ||
|
|
09f1c92856 | ||
|
|
589715901d | ||
|
|
3f1a5ff5e0 | ||
|
|
49cd956d4c | ||
|
|
f9acde862f | ||
|
|
503e1dd899 | ||
|
|
c8e12b948d | ||
|
|
18949d68c0 | ||
|
|
0c51b6c016 | ||
|
|
63a5c22c1f | ||
|
|
f76e2a7b56 | ||
|
|
bab151d6f5 | ||
|
|
d43fec088b | ||
|
|
a8ca1cbcd7 | ||
|
|
ada3494483 | ||
|
|
43c238b7f1 | ||
|
|
128d10c51e | ||
|
|
1a1e01f9f6 | ||
|
|
4605c3fd30 | ||
|
|
ed7dc3f827 | ||
|
|
e69eeebdd8 | ||
|
|
5da5f1adc1 | ||
|
|
f47e92dec0 | ||
|
|
a894ca5171 | ||
|
|
5abe1140ae | ||
|
|
d34e14370c | ||
|
|
c4f4a3131c | ||
|
|
dcbd9b57f3 | ||
|
|
aad3b54a17 | ||
|
|
cde142a371 | ||
|
|
8bfc98ffc6 | ||
|
|
e46f21d566 | ||
|
|
0e45fdcdfd | ||
|
|
eec7af16d7 | ||
|
|
6532425902 | ||
|
|
44b896522c | ||
|
|
1b16ee44cb | ||
|
|
d5f608c28c | ||
|
|
555d8418e7 | ||
|
|
8c22e35da4 | ||
|
|
95a7924b31 | ||
|
|
5830bebd95 | ||
|
|
d32cf57c75 | ||
|
|
6d9242ebc5 | ||
|
|
cbc4f6a964 | ||
|
|
2a3b2b9556 | ||
|
|
53a219f12b | ||
|
|
48519dcfa0 | ||
|
|
92542c58fe | ||
|
|
7eafe730f9 | ||
|
|
494e72a996 | ||
|
|
84cc86f1d3 | ||
|
|
64479e2e5d | ||
|
|
13b523d9bd | ||
|
|
181881a21b | ||
|
|
86d11095ac | ||
|
|
927ba3cd9d | ||
|
|
6296fc1762 | ||
|
|
60fbe44724 | ||
|
|
29e45da431 | ||
|
|
d82e69eef4 | ||
|
|
8c7d557252 | ||
|
|
a56d6e5517 | ||
|
|
7548d9e975 | ||
|
|
b7e2bd9684 | ||
|
|
f0a243e3d3 | ||
|
|
6e108c9ef2 | ||
|
|
89edcc1924 | ||
|
|
8a6aca47a1 | ||
|
|
d03e5780b8 | ||
|
|
209d8f9b40 | ||
|
|
c257b1be3d | ||
|
|
2c48c8de2e | ||
|
|
a767ef6aed | ||
|
|
ad61d1dd03 | ||
|
|
33c3d187a0 | ||
|
|
4eb486d4e2 | ||
|
|
bfb6c167a4 | ||
|
|
44abf3bdf6 | ||
|
|
c950572592 | ||
|
|
3999cb13fd | ||
|
|
af65075f0c | ||
|
|
de2a2b465b | ||
|
|
cd7a77f02d | ||
|
|
f4a5394b63 | ||
|
|
3fb6a8dedb | ||
|
|
50c8f84eba | ||
|
|
2c7ecdee92 | ||
|
|
72390a793a | ||
|
|
04ad4e5d3e | ||
|
|
5ef9c6a433 | ||
|
|
e6baffc839 | ||
|
|
e361eb25a5 | ||
|
|
9b420e91c9 | ||
|
|
3a4bf971b2 | ||
|
|
1128691c5d | ||
|
|
15043aef3f | ||
|
|
2a3b4afa03 | ||
|
|
00a98efa81 | ||
|
|
f013dd7f0d | ||
|
|
7b91b1c769 | ||
|
|
5583cce322 | ||
|
|
b995c5f992 | ||
|
|
214ac4a53d | ||
|
|
fc7e87f0df | ||
|
|
c0f2f59fc1 | ||
|
|
b90a847a6f | ||
|
|
a58bb385f5 | ||
|
|
9754baeb1c | ||
|
|
ffcd154966 | ||
|
|
97cfe9488c | ||
|
|
374b6f616a | ||
|
|
e2f51595b6 | ||
|
|
04091a16aa | ||
|
|
9d9d2fd9a2 | ||
|
|
5746115331 | ||
|
|
42f1a4926c | ||
|
|
7d87fd461b | ||
|
|
1ba9976979 | ||
|
|
659c199043 | ||
|
|
81a3f53226 | ||
|
|
1cbff28f67 | ||
|
|
8e15acbf30 | ||
|
|
e07be60db6 | ||
|
|
539c9662ff | ||
|
|
b396014f8d | ||
|
|
1db32415b6 | ||
|
|
b24629db6b | ||
|
|
9b5cdcf8fb | ||
|
|
4831415d14 | ||
|
|
a4c51f0b20 | ||
|
|
ec3ba1fb93 | ||
|
|
61966f7036 | ||
|
|
4f69e81841 | ||
|
|
d0d90581df | ||
|
|
8ea5c27633 | ||
|
|
517500fdf3 | ||
|
|
c4c1c9b6ab | ||
|
|
2388889ede | ||
|
|
55cfe878d7 | ||
|
|
a2daaee468 | ||
|
|
2c360e395e | ||
|
|
399cfee594 | ||
|
|
be646ae6ab | ||
|
|
b470253d9f | ||
|
|
b83c493492 | ||
|
|
991277bb01 | ||
|
|
5626013b81 | ||
|
|
2810d37758 | ||
|
|
c2f08f01e0 | ||
|
|
17ff087e06 | ||
|
|
77de565b7c | ||
|
|
54d238aa4d | ||
|
|
379d09f8cc | ||
|
|
00de72b127 | ||
|
|
f9c84fa7dd | ||
|
|
c8e46691bb | ||
|
|
df1bb636e5 | ||
|
|
ff886fad0d | ||
|
|
6dbee7a413 | ||
|
|
3f8fcd7172 | ||
|
|
d94f7388e6 | ||
|
|
ad8b49fea8 | ||
|
|
ce00270c12 | ||
|
|
8c501f8f58 | ||
|
|
ce313ebc65 | ||
|
|
887ad881a2 | ||
|
|
ce40827552 | ||
|
|
2777d89482 | ||
|
|
727b300a0e | ||
|
|
652b021a8e | ||
|
|
fdf33acfbb | ||
|
|
b001bc9b6f | ||
|
|
8802cb1d8c | ||
|
|
e19a2fbae7 | ||
|
|
53e38f98f9 | ||
|
|
e783e227f6 | ||
|
|
f3dfbe4181 | ||
|
|
bcd8ca8bc4 | ||
|
|
816d6a63cd | ||
|
|
88d3f25700 | ||
|
|
80f69b11db | ||
|
|
81a11f20c8 | ||
|
|
9e2a839953 | ||
|
|
3cefcde270 | ||
|
|
87a1eacfe7 | ||
|
|
7cbc1a8419 | ||
|
|
7b5570eb0b | ||
|
|
1a43a4dcf0 | ||
|
|
2c2a6592c7 | ||
|
|
f31de6ee4e | ||
|
|
8fcd1f6b6c | ||
|
|
d7f3a473d7 | ||
|
|
ab2eb0c94e | ||
|
|
e51f4fc45a | ||
|
|
65278120e2 | ||
|
|
2eed355e9c | ||
|
|
018955f4d5 | ||
|
|
12fd63c1cf |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -19,6 +19,10 @@ SABnzbd*.dmg
|
||||
# WingIDE project files
|
||||
*.wp[ru]
|
||||
|
||||
# Testing folders
|
||||
.cache
|
||||
.xprocess
|
||||
|
||||
# General junk
|
||||
*.keep
|
||||
*.bak
|
||||
|
||||
15
.travis.yml
Normal file
15
.travis.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
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
|
||||
@@ -1,5 +1,5 @@
|
||||
*******************************************
|
||||
*** This is SABnzbd 2.2.0 ***
|
||||
*** This is SABnzbd 2.3.0 ***
|
||||
*******************************************
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
|
||||
31
INSTALL.txt
31
INSTALL.txt
@@ -1,4 +1,4 @@
|
||||
SABnzbd 2.2.0
|
||||
SABnzbd 2.3.0
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
0) LICENSE
|
||||
@@ -27,39 +27,38 @@ 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 5-10 seconds your web browser will start and show the user interface.
|
||||
Within a few 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 5-10 seconds your web browser will start and show the user interface.
|
||||
Within a few 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.
|
||||
|
||||
Unix/Linux/macOS
|
||||
All platforms
|
||||
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"
|
||||
|
||||
Essential modules
|
||||
@@ -67,18 +66,17 @@ Essential modules
|
||||
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.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 Unix/Linux/macOS
|
||||
Optional modules Linux
|
||||
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.
|
||||
@@ -88,6 +86,7 @@ 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
|
||||
@@ -95,7 +94,7 @@ Start this from a shell terminal (or command prompt):
|
||||
Start this from a shell terminal (or command prompt):
|
||||
python SABnzbd.py
|
||||
|
||||
Within 5-10 seconds your web browser will start and show the user interface.
|
||||
Within a few 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.
|
||||
|
||||
|
||||
@@ -113,7 +112,7 @@ or
|
||||
|
||||
You may of course try other port numbers too.
|
||||
|
||||
For troubleshooting you can use the program SABnzbd-console.exe.
|
||||
For troubleshooting on Windows 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.
|
||||
|
||||
@@ -121,7 +120,7 @@ may help you solve problems easier.
|
||||
6) MORE INFORMATION
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Visit the WIKI site:
|
||||
Visit our wiki:
|
||||
https://sabnzbd.org/wiki/
|
||||
|
||||
|
||||
@@ -131,4 +130,4 @@ Visit the WIKI site:
|
||||
|
||||
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.
|
||||
|
||||
30
ISSUES.txt
30
ISSUES.txt
@@ -9,28 +9,18 @@
|
||||
- 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.1/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.3/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.1/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.3/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.
|
||||
@@ -41,7 +31,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.1/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.3/special
|
||||
|
||||
- The "Watched Folder" sometimes fails to delete the NZB files it has
|
||||
processed. This happens when other software still accesses these files.
|
||||
@@ -51,6 +41,10 @@
|
||||
|
||||
- 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
|
||||
@@ -72,13 +66,3 @@
|
||||
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
|
||||
|
||||
6
PKG-INFO
6
PKG-INFO
@@ -1,8 +1,8 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 2.2.0Alpha1
|
||||
Summary: SABnzbd-2.2.0Alpha1
|
||||
Home-page: http://sabnzbd.org
|
||||
Version: 2.3.0
|
||||
Summary: SABnzbd-2.3.0
|
||||
Home-page: https://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
License: GNU General Public License 2 (GPL2 or later)
|
||||
|
||||
@@ -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: http://sabnzbd.org.
|
||||
If you want to know more you can head over to our website: https://sabnzbd.org.
|
||||
|
||||
## Resolving Dependencies
|
||||
|
||||
|
||||
79
README.mkd
79
README.mkd
@@ -1,43 +1,64 @@
|
||||
Release Notes - SABnzbd 2.2.0 Alpha 1
|
||||
Release Notes - SABnzbd 2.3.0
|
||||
=========================================================
|
||||
|
||||
NOTE: Due to changes in this release, the queue will be converted when 2.2.0
|
||||
is started for the first time. Job order, settings and data will be preserved,
|
||||
but URL's that did finished fetching before the upgrade will be lost!
|
||||
## SABYenc updated
|
||||
- Improved download performance 10-15% on CPU limited devices
|
||||
by optimizations and by removing redundant article verification.
|
||||
Linux/NAS: Update of SABYenc to version 3.3.1 is required.
|
||||
Update your package or see https://sabnzbd.org/sabyenc
|
||||
Windows/macOS: Update is included in the installers.
|
||||
|
||||
## Changes in 2.2.0
|
||||
- Reduced memory usage, especially with larger queues
|
||||
- Slight improvement in download performance by removing internal locks
|
||||
- Smoother animations in Firefox (disabled previously due to FF high-CPU usage)
|
||||
- If enabled, replace dots in filenames also when there are spaces already
|
||||
- Jobs outside server retention are processed faster
|
||||
- max_art_opt and replace_illegal moved from Switches to Specials
|
||||
## Changes since 2.2.1
|
||||
- Option to limit Servers to specific Categories removed
|
||||
- Improved par2 handling and obfuscated files detection
|
||||
- Duplicate filenames in NZB's no longer rejected by default
|
||||
- Set custom URL instead of /sabnzbd/ (in Config > Specials)
|
||||
- Article-state (which servers are tried) is preserved after restart
|
||||
- Auto disconnect (if enabled) only after verification of last item
|
||||
- Slight performance improvement when fetching RSS-feeds
|
||||
- Job title from RSS-feed is shown for URLs being grabbed
|
||||
- URL grabbing can now be individually paused
|
||||
- Scheduler can pause/unpause jobs in specific category
|
||||
- Series Duplicate Checker can allow PROPER/REAL/REPACK versions
|
||||
- Refresh-icon in Glitter when refresh rate is above 2 seconds
|
||||
- Different icon for downloads with Force priority
|
||||
- Show progress during verification of extra files
|
||||
- All dates and times are now time zone independent
|
||||
- API: 'missing' field removed from 'queue', use 'mbmissing'
|
||||
- API: 'warnings' method now returns array for better handling
|
||||
- macOS: Bundled new OpenSSL version with support for TLSv1.2
|
||||
- macOS: No longer linked to system certificate store
|
||||
- macOS and Windows: Installers include Mozilla CA certificates
|
||||
|
||||
## Bugfixes in 2.2.0
|
||||
- Shutdown/suspend did not work on some Linux systems
|
||||
- Deleting a job could result in write errors
|
||||
- Display warning if custom par2 parameters are wrong
|
||||
- macOS: Catch 'Protocol wrong type for socket' errors
|
||||
- Windows: Fix error in MultiPar-code when first par2-file was damaged
|
||||
## Bugfixes since 2.2.1
|
||||
- Reduce CPU usage with multiple servers
|
||||
- Fix yet another potential stalling issue
|
||||
- Remove Timeout tracebacks
|
||||
- Handle locked history database better
|
||||
- Only warn if number of actual passwords is larger than 30
|
||||
- Unexpected behavior when diskspace becomes critically low
|
||||
- Retried jobs would show incorrect download progress
|
||||
- Password file was loaded from disk excessively
|
||||
- API: Correct listing of downloaded and queued files in 'get_files'
|
||||
- API: Number of bytes missing and downloaded could be slightly off
|
||||
- API: Jobs with Force priority should always have status 'Downloading'
|
||||
- MacOS: Direct Unpack could hang in case of special characters in names
|
||||
- Windows: Unpack could fail if started instantly after previous one
|
||||
- Windows: Download with many par2 sets could get listed as failed
|
||||
|
||||
## Translations
|
||||
- Added Hebrew translation by ION IL, many other languages updated.
|
||||
|
||||
## Upgrading from 0.7.x and older
|
||||
## Upgrading from 2.1.x and older
|
||||
- Finish queue
|
||||
- Stop SABnzbd
|
||||
- Install new version
|
||||
- Start SABnzbd
|
||||
|
||||
## 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.
|
||||
## Upgrade notices
|
||||
- When upgrading from 2.1.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.
|
||||
- The organization of the download queue is different from 0.7.x releases.
|
||||
So 2.x.x will not see the existing queue, but you can go to Status->Queue Repair
|
||||
and "Repair" the old queue.
|
||||
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.
|
||||
|
||||
## Known problems and solutions
|
||||
- Read the file "ISSUES.txt"
|
||||
|
||||
98
SABnzbd.py
98
SABnzbd.py
@@ -32,11 +32,13 @@ except:
|
||||
|
||||
import logging
|
||||
import logging.handlers
|
||||
import traceback
|
||||
import os
|
||||
import getopt
|
||||
import signal
|
||||
import socket
|
||||
import platform
|
||||
import ssl
|
||||
import time
|
||||
import re
|
||||
|
||||
@@ -97,7 +99,6 @@ 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
|
||||
|
||||
@@ -150,7 +151,11 @@ class guiHandler(logging.Handler):
|
||||
# Loose the oldest record
|
||||
self.store.pop(0)
|
||||
try:
|
||||
self.store.append(self.format(record))
|
||||
# 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)
|
||||
except UnicodeDecodeError:
|
||||
# Catch elusive Unicode conversion problems
|
||||
pass
|
||||
@@ -161,12 +166,6 @@ 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
|
||||
@@ -202,11 +201,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():
|
||||
@@ -426,13 +425,12 @@ def print_modules():
|
||||
if sabnzbd.newsunpack.PAR2_COMMAND:
|
||||
logging.info("par2 binary... found (%s)", sabnzbd.newsunpack.PAR2_COMMAND)
|
||||
else:
|
||||
logging.error(T('par2 binary... NOT found!'))
|
||||
|
||||
if sabnzbd.newsunpack.PAR2C_COMMAND:
|
||||
logging.info("par2cmdline binary... found (%s)", sabnzbd.newsunpack.PAR2C_COMMAND)
|
||||
logging.error('%s %s' % (T('par2 binary... NOT found!'), T('Verification and repair will not be possible.')))
|
||||
|
||||
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)
|
||||
@@ -443,9 +441,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.debug('UNRAR binary version %.2f', (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
|
||||
logging.info('UNRAR binary version %.2f', (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
|
||||
else:
|
||||
logging.error(T('unrar binary... NOT found'))
|
||||
logging.error('%s %s' % (T('unrar binary... NOT found'), T('Downloads will not unpacked.')))
|
||||
|
||||
if sabnzbd.newsunpack.ZIP_COMMAND:
|
||||
logging.info("unzip binary... found (%s)", sabnzbd.newsunpack.ZIP_COMMAND)
|
||||
@@ -823,7 +821,7 @@ def main():
|
||||
logging_level = None
|
||||
web_dir = None
|
||||
vista_plus = False
|
||||
vista64 = False
|
||||
win64 = False
|
||||
repair = 0
|
||||
api_url = None
|
||||
no_login = False
|
||||
@@ -948,8 +946,8 @@ def main():
|
||||
|
||||
# Detect Windows variant
|
||||
if sabnzbd.WIN32:
|
||||
vista_plus, vista64 = windows_variant()
|
||||
sabnzbd.WIN64 = vista64
|
||||
vista_plus, win64 = windows_variant()
|
||||
sabnzbd.WIN64 = win64
|
||||
|
||||
if not SQLITE_DLL:
|
||||
panic_sqlite(sabnzbd.MY_FULLNAME)
|
||||
@@ -1028,7 +1026,7 @@ def main():
|
||||
pass
|
||||
else:
|
||||
if not url:
|
||||
url = 'https://%s:%s/sabnzbd/api?' % (browserhost, port)
|
||||
url = 'https://%s:%s%s/api?' % (browserhost, port, sabnzbd.cfg.url_base())
|
||||
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():
|
||||
@@ -1057,7 +1055,7 @@ def main():
|
||||
pass
|
||||
else:
|
||||
if not url:
|
||||
url = 'http://%s:%s/sabnzbd/api?' % (browserhost, cherryport)
|
||||
url = 'http://%s:%s%s/api?' % (browserhost, cherryport, sabnzbd.cfg.url_base())
|
||||
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():
|
||||
@@ -1152,14 +1150,12 @@ def main():
|
||||
logging.info('Full executable path = %s', sabnzbd.MY_FULLNAME)
|
||||
if sabnzbd.WIN32:
|
||||
suffix = ''
|
||||
if vista_plus:
|
||||
suffix = ' (=Vista+)'
|
||||
if vista64:
|
||||
suffix = ' (=Vista+ x64)'
|
||||
if win64:
|
||||
suffix = '(win64)'
|
||||
try:
|
||||
logging.info('Platform=%s%s Class=%s', platform.platform(), suffix, os.name)
|
||||
logging.info('Platform = %s %s', platform.platform(), suffix)
|
||||
except:
|
||||
logging.info('Platform=%s <unknown> Class=%s', suffix, os.name)
|
||||
logging.info('Platform = %s <unknown>', suffix)
|
||||
else:
|
||||
logging.info('Platform = %s', os.name)
|
||||
logging.info('Python-version = %s', sys.version)
|
||||
@@ -1177,7 +1173,24 @@ 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()
|
||||
@@ -1202,12 +1215,12 @@ def main():
|
||||
from sabnzbd.utils.getperformance import getpystone, getcpu
|
||||
pystoneperf = getpystone()
|
||||
if pystoneperf:
|
||||
logging.debug('CPU Pystone available performance is %s', pystoneperf)
|
||||
logging.debug('CPU Pystone available performance = %s', pystoneperf)
|
||||
else:
|
||||
logging.debug('CPU Pystone available performance could not be calculated')
|
||||
cpumodel = getcpu() # Linux only
|
||||
if cpumodel:
|
||||
logging.debug('CPU model name is %s', cpumodel)
|
||||
logging.debug('CPU model = %s', cpumodel)
|
||||
|
||||
logging.info('Read INI file %s', inifile)
|
||||
|
||||
@@ -1251,12 +1264,9 @@ def main():
|
||||
|
||||
# Find external programs
|
||||
sabnzbd.newsunpack.find_programs(sabnzbd.DIR_PROG)
|
||||
|
||||
print_modules()
|
||||
|
||||
logging.info("SSL version %s", sabnzbd.utils.sslinfo.ssl_version())
|
||||
logging.info("SSL supported protocols %s", str(sabnzbd.utils.sslinfo.ssl_protocols_labels()))
|
||||
|
||||
# HTTPS certificate generation
|
||||
https_cert = sabnzbd.cfg.https_cert.get_path()
|
||||
https_key = sabnzbd.cfg.https_key.get_path()
|
||||
https_chain = sabnzbd.cfg.https_chain.get_path()
|
||||
@@ -1272,6 +1282,7 @@ 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')
|
||||
@@ -1324,6 +1335,7 @@ 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,
|
||||
@@ -1360,7 +1372,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/', config=appconfig)
|
||||
cherrypy.tree.mount(main_page, sabnzbd.cfg.url_base(), config=appconfig)
|
||||
|
||||
# Set authentication for CherryPy
|
||||
sabnzbd.interface.set_auth(cherrypy.config)
|
||||
@@ -1377,15 +1389,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/sabnzbd/api?apikey=%s' % (mode, browserhost, cherryport, sabnzbd.cfg.api_key())
|
||||
api_url = 'http%s://%s:%s%s/api?apikey=%s' % (mode, browserhost, cherryport, sabnzbd.cfg.url_base(), sabnzbd.cfg.api_key())
|
||||
|
||||
if sabnzbd.WIN_SERVICE:
|
||||
mail = MailSlot()
|
||||
@@ -1418,9 +1430,9 @@ def main():
|
||||
|
||||
# Set URL for browser
|
||||
if enable_https:
|
||||
browser_url = "https://%s:%s/sabnzbd" % (browserhost, cherryport)
|
||||
browser_url = "https://%s:%s%s" % (browserhost, cherryport, sabnzbd.cfg.url_base())
|
||||
else:
|
||||
browser_url = "http://%s:%s/sabnzbd" % (browserhost, cherryport)
|
||||
browser_url = "http://%s:%s%s" % (browserhost, cherryport, sabnzbd.cfg.url_base())
|
||||
sabnzbd.BROWSER_URL = browser_url
|
||||
|
||||
if not autorestarted:
|
||||
@@ -1434,6 +1446,13 @@ 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:
|
||||
@@ -1682,9 +1701,8 @@ 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
|
||||
|
||||
@@ -1709,9 +1727,7 @@ if __name__ == '__main__':
|
||||
sabApp = startApp()
|
||||
sabApp.start()
|
||||
AppHelper.runEventLoop()
|
||||
|
||||
except:
|
||||
main()
|
||||
|
||||
else:
|
||||
main()
|
||||
|
||||
6
appveyor.yml
Normal file
6
appveyor.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
install:
|
||||
- pip install --upgrade -r tests/requirements.txt
|
||||
- pip install pypiwin32
|
||||
|
||||
build_script:
|
||||
- pytest
|
||||
@@ -200,8 +200,8 @@ socket_errors_nonblocking = plat_specific_errors(
|
||||
'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
socket_errors_to_ignore.append(plat_specific_errors('EPROTOTYPE'))
|
||||
socket_errors_nonblocking.append(plat_specific_errors('EPROTOTYPE'))
|
||||
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
|
||||
|
||||
@@ -98,6 +98,12 @@ 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
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
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)
|
||||
509
gntp/__init__.py
509
gntp/__init__.py
@@ -1,509 +0,0 @@
|
||||
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')
|
||||
|
||||
141
gntp/cli.py
Normal file
141
gntp/cli.py
Normal file
@@ -0,0 +1,141 @@
|
||||
# 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()
|
||||
77
gntp/config.py
Normal file
77
gntp/config.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# 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')
|
||||
518
gntp/core.py
Normal file
518
gntp/core.py
Normal file
@@ -0,0 +1,518 @@
|
||||
# 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')
|
||||
25
gntp/errors.py
Normal file
25
gntp/errors.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# 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"
|
||||
161
gntp/notifier.py
161
gntp/notifier.py
@@ -1,3 +1,6 @@
|
||||
# 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
|
||||
@@ -9,10 +12,15 @@ 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',
|
||||
@@ -22,45 +30,6 @@ __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
|
||||
|
||||
@@ -99,8 +68,9 @@ 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.debug('Checking icon')
|
||||
return data.startswith('http')
|
||||
logger.info('Checking icon')
|
||||
|
||||
return gntp.shim.u(data)[:4] in ['http', 'file']
|
||||
|
||||
def register(self):
|
||||
"""Send GNTP Registration
|
||||
@@ -109,8 +79,8 @@ class GrowlNotifier(object):
|
||||
Before sending notifications to Growl, you need to have
|
||||
sent a registration message at least once
|
||||
"""
|
||||
logger.debug('Sending registration to %s:%s', self.hostname, self.port)
|
||||
register = gntp.GNTPRegister()
|
||||
logger.info('Sending registration to %s:%s', self.hostname, self.port)
|
||||
register = gntp.core.GNTPRegister()
|
||||
register.add_header('Application-Name', self.applicationName)
|
||||
for notification in self.notifications:
|
||||
enabled = notification in self.defaultNotifications
|
||||
@@ -119,8 +89,8 @@ class GrowlNotifier(object):
|
||||
if self._checkIcon(self.applicationIcon):
|
||||
register.add_header('Application-Icon', self.applicationIcon)
|
||||
else:
|
||||
id = register.add_resource(self.applicationIcon)
|
||||
register.add_header('Application-Icon', id)
|
||||
resource = register.add_resource(self.applicationIcon)
|
||||
register.add_header('Application-Icon', resource)
|
||||
if self.password:
|
||||
register.set_password(self.password, self.passwordHash)
|
||||
self.add_origin_info(register)
|
||||
@@ -128,7 +98,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):
|
||||
priority=None, callback=None, identifier=None, custom={}):
|
||||
"""Send a GNTP notifications
|
||||
|
||||
.. warning::
|
||||
@@ -141,14 +111,16 @@ 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.debug('Sending notification [%s] to %s:%s', noteType, self.hostname, self.port)
|
||||
logger.info('Sending notification [%s] to %s:%s', noteType, self.hostname, self.port)
|
||||
assert noteType in self.notifications
|
||||
notice = gntp.GNTPNotice()
|
||||
notice = gntp.core.GNTPNotice()
|
||||
notice.add_header('Application-Name', self.applicationName)
|
||||
notice.add_header('Notification-Name', noteType)
|
||||
notice.add_header('Notification-Title', title)
|
||||
@@ -162,8 +134,8 @@ class GrowlNotifier(object):
|
||||
if self._checkIcon(icon):
|
||||
notice.add_header('Notification-Icon', icon)
|
||||
else:
|
||||
id = notice.add_resource(icon)
|
||||
notice.add_header('Notification-Icon', id)
|
||||
resource = notice.add_resource(icon)
|
||||
notice.add_header('Notification-Icon', resource)
|
||||
|
||||
if description:
|
||||
notice.add_header('Notification-Text', description)
|
||||
@@ -172,6 +144,9 @@ 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)
|
||||
|
||||
@@ -179,7 +154,7 @@ class GrowlNotifier(object):
|
||||
|
||||
def subscribe(self, id, name, port):
|
||||
"""Send a Subscribe request to a remote machine"""
|
||||
sub = gntp.GNTPSubscribe()
|
||||
sub = gntp.core.GNTPSubscribe()
|
||||
sub.add_header('Subscriber-ID', id)
|
||||
sub.add_header('Subscriber-Name', name)
|
||||
sub.add_header('Subscriber-Port', port)
|
||||
@@ -195,7 +170,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', gntp.__version__)
|
||||
packet.add_header('Origin-Software-Version', __version__)
|
||||
packet.add_header('Origin-Platform-Name', platform.system())
|
||||
packet.add_header('Origin-Platform-Version', platform.platform())
|
||||
|
||||
@@ -214,34 +189,78 @@ class GrowlNotifier(object):
|
||||
packet.validate()
|
||||
data = packet.encode()
|
||||
|
||||
#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__)
|
||||
logger.debug('To : %s:%s <%s>\n%s', self.hostname, self.port, packet.__class__, data)
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(self.socketTimeout)
|
||||
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)
|
||||
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.close()
|
||||
|
||||
#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__)
|
||||
logger.debug('From : %s:%s <%s>\n%s', self.hostname, self.port, response.__class__, response)
|
||||
|
||||
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
|
||||
if type(response) == gntp.core.GNTPOK:
|
||||
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.DEBUG)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
mini('Testing mini notification')
|
||||
|
||||
46
gntp/shim.py
Normal file
46
gntp/shim.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# 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"
|
||||
4
gntp/version.py
Normal file
4
gntp/version.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# Copyright: 2013 Paul Traylor
|
||||
# These sources are released under the terms of the MIT license: see LICENSE
|
||||
|
||||
__version__ = '1.0.3'
|
||||
@@ -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" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-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,7 +32,9 @@
|
||||
<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/style.css?p=$pid" />
|
||||
<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="shortcut icon" href="${root}staticcfg/ico/favicon.ico?v=$version" />
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -43,6 +45,7 @@
|
||||
// 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 '/'#'
|
||||
|
||||
@@ -59,7 +62,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.1.1.min.js?v=$version"></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/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">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Config"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/configure"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/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_protocols]
|
||||
$ssl_version
|
||||
</td>
|
||||
</tr>
|
||||
<!--#if not $have_ssl_context#-->
|
||||
@@ -109,7 +109,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">$T('homePage') </th>
|
||||
<td><a href="http://sabnzbd.org/" target="_blank">http://sabnzbd.org/</a></td>
|
||||
<td><a href="https://sabnzbd.org/" target="_blank">https://sabnzbd.org/</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">$T('menu-wiki') </th>
|
||||
@@ -117,7 +117,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">$T('menu-forums') </th>
|
||||
<td><a href="http://forums.sabnzbd.org/" target="_blank">http://forums.sabnzbd.org/</a></td>
|
||||
<td><a href="https://forums.sabnzbd.org/" target="_blank">https://forums.sabnzbd.org/</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">$T('source') </th>
|
||||
@@ -131,6 +131,10 @@
|
||||
<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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Categories"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/categories"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/categories"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<div class="section">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Folders"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/folders"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/folders"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="General"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/general"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/general"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -25,7 +25,7 @@
|
||||
</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"' else ""#-->/>
|
||||
<input type="checkbox" name="enable_https" id="enable_https" value="1" <!--#if int($enable_https) > 0 then 'checked="checked" data-original="1"' else ""#-->/>
|
||||
<span class="desc">$T('explain-enable_https')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
@@ -253,6 +253,32 @@
|
||||
})
|
||||
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 () {
|
||||
@@ -330,7 +356,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update the value
|
||||
\$('#bandwidth_max_value, #bandwidth_max_dropdown').on('change', function() {
|
||||
if(\$('#bandwidth_max_value').val()) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Email"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/notifications"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/notifications"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#def show_notify_checkboxes($section_label)#-->
|
||||
@@ -13,6 +13,18 @@
|
||||
<!--#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" novalidate>
|
||||
<input type="hidden" id="session" name="session" value="$session" />
|
||||
@@ -20,7 +32,15 @@
|
||||
<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><!-- /col2 -->
|
||||
<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 class="col1">
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
@@ -79,8 +99,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<!--#if $have_ncenter#-->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
@@ -91,7 +111,7 @@
|
||||
<td><label for="ncenter_enable"> $T('opt-ncenter_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- /col2 -->
|
||||
</div>
|
||||
<div class="col1" <!--#if int($ncenter_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
$show_notify_checkboxes('ncenter')
|
||||
@@ -103,8 +123,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<!--#if $nt#-->
|
||||
<div class="section">
|
||||
@@ -116,7 +136,8 @@
|
||||
<td><label for="acenter_enable"> $T('opt-acenter_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('acenter')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($acenter_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
$show_notify_checkboxes('acenter')
|
||||
@@ -128,8 +149,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<!--#if $have_ntfosd#-->
|
||||
<div class="section">
|
||||
@@ -141,7 +162,8 @@
|
||||
<td><label for="ntfosd_enable"> $T('opt-ntfosd_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('ntfosd')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($ntfosd_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
$show_notify_checkboxes('ntfosd')
|
||||
@@ -153,9 +175,48 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<!--#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">
|
||||
<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>
|
||||
@@ -165,7 +226,8 @@
|
||||
<td><label for="growl_enable"> $T('opt-growl_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('growl')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($growl_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
@@ -187,8 +249,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="prowl">
|
||||
<div class="col2">
|
||||
<h3>$T('section-Prowl')</h3>
|
||||
@@ -199,7 +261,8 @@
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-prowl_enable')</em>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('prowl')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($prowl_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
@@ -231,8 +294,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section" id="pushover">
|
||||
<div class="col2">
|
||||
@@ -244,7 +307,8 @@
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-pushover_enable')</em>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('pushover')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($pushover_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
@@ -286,8 +350,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="pushbullet">
|
||||
<div class="col2">
|
||||
<h3>$T('section-Pushbullet')</h3>
|
||||
@@ -298,7 +362,8 @@
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-pushbullet_enable')</em>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('pushbullet')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($pushbullet_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
@@ -322,46 +387,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</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 -->
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /colmask -->
|
||||
|
||||
@@ -374,11 +401,20 @@
|
||||
\$('.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()
|
||||
})
|
||||
\$('#email_endjob').change(function() {
|
||||
if(\$(this).val() > 0) {
|
||||
\$(this).parents('.section').find('.col2-cats').show()
|
||||
} else {
|
||||
\$(this).parents('.section').find('.col2-cats').hide()
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
Testing functions
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="RSS"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/rss"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/rss"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<!--#if not $active_feed#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/scheduling"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<%
|
||||
@@ -59,6 +59,13 @@ 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">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Servers"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/servers"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/servers"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -7,7 +7,7 @@
|
||||
<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>
|
||||
<button type="button" class="btn btn-default" id="addServerButton"><span class="glyphicon glyphicon-plus"></span> $T('button-addServer')</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="addServerContent" style="display: none;">
|
||||
@@ -78,17 +78,6 @@
|
||||
<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" />
|
||||
@@ -110,6 +99,59 @@
|
||||
</div><!-- /section -->
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" src="${root}staticcfg/js/chartist.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
// Define variables needed for the server-plots
|
||||
var serverData = {}
|
||||
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
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
|
||||
<!--#import json#-->
|
||||
<!--#import datetime#-->
|
||||
<!--#import sabnzbd.misc#-->
|
||||
|
||||
<!--#def show_date_selector($server, $id)#-->
|
||||
<!--#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()#-->
|
||||
<!--#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))#-->
|
||||
<!--#end for#-->
|
||||
<!--#set months_recorded = list(sabnzbd.misc.monthrange(min_date, datetime.date.today()))#-->
|
||||
<!--#$months_recorded.reverse()#-->
|
||||
<select class="chart-selector" name="chart-selector-${id}" id="chart-selector-${id}" data-id="${id}">
|
||||
<!--#for $cur_date in months_recorded#-->
|
||||
<option value="<!--#echo '%d-%02d' % ($cur_date.year, $cur_date.month)#-->">$month_names[$cur_date.month-1] $cur_date.year</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<!--#end def#-->
|
||||
|
||||
<!--#set $prio_colors = ["#59cc33", "#3366cc","#7f33cc", "#cc33a6", "#cc3333"] #-->
|
||||
<!--#set $cur_prio_color = -1 #-->
|
||||
<!--#set $last_prio = -1 #-->
|
||||
@@ -199,21 +241,6 @@
|
||||
<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>
|
||||
<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']" />
|
||||
@@ -232,166 +259,252 @@
|
||||
<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><!-- /section -->
|
||||
<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
|
||||
</div>
|
||||
<div class="server-chart">
|
||||
$show_date_selector($server, $cur)
|
||||
<div id="server-chart-${cur}" class="ct-chart"></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
// Server data
|
||||
serverData[${cur}] = <!--#echo json.dumps($server['amounts'][4])#-->
|
||||
\$(document).ready(function() {
|
||||
showChart(${cur}, \$('#chart-selector-${cur}').val())
|
||||
})
|
||||
</script>
|
||||
<!--#end if#-->
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!--#end for#-->
|
||||
|
||||
</div><!-- /colmask -->
|
||||
|
||||
<script type="text/javascript">
|
||||
\$(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()
|
||||
})
|
||||
})
|
||||
function showChart(server_id, month) {
|
||||
// This month
|
||||
var thisDay = new Date()
|
||||
|
||||
/**
|
||||
Message on no Default category selected
|
||||
**/
|
||||
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
|
||||
}
|
||||
// What month are we doing?
|
||||
var inputDate = new Date(month+'-01')
|
||||
var baseDate = new Date(inputDate.getUTCFullYear(), inputDate.getUTCMonth(), 1)
|
||||
var maxDaysInMonth = new Date(baseDate.getFullYear(), baseDate.getMonth()+1, 0).getDate()
|
||||
|
||||
// Fill the data array
|
||||
var data = {
|
||||
labels: [],
|
||||
series: [[]]
|
||||
};
|
||||
var largestVal = 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])
|
||||
largestVal = Math.max(largestVal, 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)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we should shrink the Y-axis values
|
||||
var devideBy = 1024
|
||||
var axisLabel = 'KB'
|
||||
if(largestVal > 1024*1024) {
|
||||
devideBy = 1024*1024
|
||||
axisLabel = 'MB'
|
||||
}
|
||||
if(largestVal > 1024*1024*1024) {
|
||||
devideBy = 1024*1024*1024
|
||||
axisLabel = 'GB'
|
||||
}
|
||||
if(largestVal > 1024*1024*1024*1024) {
|
||||
devideBy = 1024*1024*1024*1024
|
||||
axisLabel = 'TB'
|
||||
}
|
||||
|
||||
// Shrink the value
|
||||
data['series'][0] = data['series'][0].map(function(num) {
|
||||
return num / devideBy;
|
||||
})
|
||||
// We found nothing.. Let's show a warning
|
||||
if(!hasDefault) \$('.alert-no-category').show()
|
||||
|
||||
// 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 += axisLabel
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
\$('select[name="categories"]').on('change', checkServerCats)
|
||||
checkServerCats()
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
Click events
|
||||
When finished loading
|
||||
**/
|
||||
\$('.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')");
|
||||
}
|
||||
});
|
||||
\$(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()
|
||||
})
|
||||
})
|
||||
|
||||
\$('#addServerButton').click(function(){
|
||||
\$('#addServer').hide();
|
||||
\$('#addServerContent').show();
|
||||
});
|
||||
/**
|
||||
Update charts when changed
|
||||
**/
|
||||
\$('.chart-selector').on('change', function(elemn) {
|
||||
showChart(\$(elemn.target).data('id'), \$(elemn.target).val())
|
||||
// Lets us leave (needs to be called after the change event)
|
||||
setTimeout(function() {
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
}, 100)
|
||||
})
|
||||
|
||||
\$('[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')
|
||||
/**
|
||||
Click events
|
||||
**/
|
||||
\$('.showserver').click(function () {
|
||||
if(\$(this).parent().hasClass('server-disabled')) {
|
||||
\$(this).parent().parent().toggleClass('server-disabled')
|
||||
}
|
||||
} 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>')
|
||||
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> ')
|
||||
\$(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 {
|
||||
resultBox.addClass('alert-danger')
|
||||
resultBox.prepend('<span class="glyphicon glyphicon-exclamation-sign"></span> ')
|
||||
\$(this).html(\$(this).html().replace("$T('hideDetails')", "$T('showDetails')"));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
\$('.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;
|
||||
});
|
||||
\$('#addServerButton').click(function(){
|
||||
\$('#addServer').hide();
|
||||
\$('#addServerContent').show();
|
||||
});
|
||||
|
||||
\$('.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;
|
||||
});
|
||||
\$('[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)
|
||||
})
|
||||
|
||||
\$('.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)
|
||||
\$('.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>')
|
||||
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)
|
||||
}
|
||||
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"#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Sorting"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/sorting"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/sorting"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -198,7 +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="Decades 1" />
|
||||
<input type="button" class="btn btn-default" onclick="movieSet('%0decade/%title (%y).%ext',' CD%1');movieExtraFolder(true)" value="$T('decade')" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="previewmovie" class="example">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Special"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/special"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/special"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Switches"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/switches"#-->
|
||||
<!--#set global $help_uri="configuration/2.3/switches"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -86,6 +86,7 @@
|
||||
<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>
|
||||
@@ -96,12 +97,18 @@
|
||||
<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">
|
||||
<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">
|
||||
@@ -130,6 +137,11 @@
|
||||
<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>
|
||||
@@ -153,13 +165,6 @@
|
||||
<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>
|
||||
<!--#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" />
|
||||
@@ -212,11 +217,28 @@
|
||||
<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">
|
||||
<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>
|
||||
@@ -233,7 +255,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')</span>
|
||||
<span class="desc">$T('explain-folder_rename').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="replace_spaces">$T('opt-replace_spaces')</label>
|
||||
@@ -439,6 +461,53 @@
|
||||
}
|
||||
});
|
||||
|
||||
\$('#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()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<html lang="$active_lang">
|
||||
<head>
|
||||
<title>SABnzbd</title>
|
||||
<title>SABnzbd - $T('login')</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-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.1.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="../staticcfg/js/jquery-3.2.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
|
||||
</head>
|
||||
<html>
|
||||
|
||||
1
interfaces/Config/templates/staticcfg/css/chartist.min.css
vendored
Normal file
1
interfaces/Config/templates/staticcfg/css/chartist.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -137,7 +137,8 @@ input[type="checkbox"]+.desc {
|
||||
font-style: italic;
|
||||
padding: 0 1px;
|
||||
}
|
||||
.col2 p {
|
||||
.col2 p,
|
||||
.col2-cats {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin: 1em 0;
|
||||
@@ -618,7 +619,6 @@ h2.activeRSS {
|
||||
padding: 0 0 .5em;
|
||||
}
|
||||
.feed-row div {
|
||||
padding-right: 10px;
|
||||
overflow:hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
@@ -993,6 +993,55 @@ 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 {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: -7px;
|
||||
left: 50%;
|
||||
width: 150px;
|
||||
margin-left: -50px;
|
||||
min-width: initial;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.Servers .chart-selector:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.Servers .ct-grid.ct-vertical:first-of-type {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.advanced-settings {
|
||||
display: none;
|
||||
}
|
||||
@@ -1018,7 +1067,6 @@ input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alert-no-category,
|
||||
.alert-translate {
|
||||
display: none;
|
||||
margin: 5px 0px 0px;
|
||||
@@ -1034,6 +1082,12 @@ input[type="checkbox"] {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.host-warning {
|
||||
color: #404040;
|
||||
margin-left: 7px;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
.fileBrowser .glyphicon {
|
||||
margin-right: 2px;
|
||||
top: 1px;
|
||||
@@ -1118,7 +1172,7 @@ input[type="checkbox"] {
|
||||
|
||||
.navbar-nav {
|
||||
/* For extra wide languages like Polish */
|
||||
margin-right: -50px;
|
||||
margin-right: -150px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1170,6 +1224,15 @@ input[type="checkbox"] {
|
||||
.Servers .col2 button:first-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.Servers .server-chart {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.Servers .server-amounts-text {
|
||||
padding: 0px 15px 10px;
|
||||
width: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
|
||||
10
interfaces/Config/templates/staticcfg/js/chartist.min.js
vendored
Normal file
10
interfaces/Config/templates/staticcfg/js/chartist.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
interfaces/Config/templates/staticcfg/js/jquery-3.2.1.min.js
vendored
Normal file
4
interfaces/Config/templates/staticcfg/js/jquery-3.2.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -226,18 +226,13 @@ function do_restart() {
|
||||
$('.main-restarting').show()
|
||||
|
||||
// What template
|
||||
var arrPath = window.location.pathname.split('/');
|
||||
var urlPath = (arrPath[1] == "m" || arrPath[2] == "m") ? '/sabnzbd/m/' : '/sabnzbd/';
|
||||
var switchedHTTPS = !$('#enable_https').is(':checked') && window.location.protocol == 'https:'
|
||||
var switchedHTTPS = ($('#enable_https').is(':checked') == ($('#enable_https').data('original') === undefined))
|
||||
var portsUnchanged = ($('#port').val() == $('#port').data('original')) && ($('#https_port').val() == $('#https_port').data('original'))
|
||||
|
||||
// Are we on settings page?
|
||||
if(!$('body').hasClass('General')) {
|
||||
// Same as before, with fall-back in case location.origin is not supported (<IE9)
|
||||
var urlTotal = window.location.origin ? (window.location.origin + urlPath) : window.location;
|
||||
} else if (!switchedHTTPS && ($('#port').val() == $('#port').data('original')) && ($('#https_port').val() == $('#https_port').data('original'))) {
|
||||
// If the http/https port config didn't change, don't try and guess the URL/port to redirect to
|
||||
// This solves some incorrect behavior if running behind a reverse proxy
|
||||
var urlTotal = window.location.origin ? (window.location.origin + urlPath) : window.location;
|
||||
// Are we on settings page or did nothing change?
|
||||
if(!$('body').hasClass('General') || (!switchedHTTPS && portsUnchanged)) {
|
||||
// Same as before
|
||||
var urlTotal = window.location.origin + urlBase
|
||||
} else {
|
||||
// Protocol and port depend on http(s) setting
|
||||
if($('#enable_https').is(':checked') && (window.location.protocol == 'https:' || !$('#https_port').val())) {
|
||||
@@ -251,7 +246,7 @@ function do_restart() {
|
||||
}
|
||||
|
||||
// We cannot make a good guess for the IP, so at least we assume that stays the same
|
||||
var urlTotal = urlProtocol + '//' + window.location.hostname + ':' + urlPort + urlPath;
|
||||
var urlTotal = urlProtocol + '//' + window.location.hostname + ':' + urlPort + urlBase;
|
||||
}
|
||||
|
||||
// Show where we are going to connect
|
||||
@@ -401,7 +396,7 @@ $(document).ready(function () {
|
||||
$(checkDisabled).on('change', function() {
|
||||
$(this).parent().nextAll().toggleClass('disabled')
|
||||
})
|
||||
if($(checkDisabled).is(':checked')) {
|
||||
if(!$(checkDisabled).is(':checked')) {
|
||||
$(checkDisabled).parent().nextAll().addClass('disabled')
|
||||
}
|
||||
|
||||
|
||||
@@ -47,9 +47,9 @@
|
||||
|
||||
<!-- ko if: historyStatus.has_rating -->
|
||||
<div class="dropdown history-ratings">
|
||||
<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 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>
|
||||
<ul class="dropdown-menu history-ratings-menu">
|
||||
<li>
|
||||
@@ -206,6 +206,7 @@
|
||||
</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>
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
</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#-->
|
||||
|
||||
@@ -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: date, attr: { 'data-timestamp': timestamp }"></strong>
|
||||
<strong data-bind="text: displayDateTime(timestamp, \$parent.dateFormat(), 'X'), attr: { 'data-timestamp': timestamp }"></strong>
|
||||
<span class="queue-message-text" data-bind="html: text"></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -59,15 +59,9 @@
|
||||
<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>
|
||||
<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-home"></span> <a href="https://forums.sabnzbd.org/viewforum.php?f=11" 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&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>
|
||||
|
||||
@@ -525,10 +519,14 @@
|
||||
<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>
|
||||
<span>
|
||||
<div class="fileDetails">
|
||||
<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>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -621,7 +619,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>$T('menu-forums'):</strong></td>
|
||||
<td><a href="http://forums.sabnzbd.org/" target="_blank">http://forums.sabnzbd.org/</a></td>
|
||||
<td><a href="https://forums.sabnzbd.org/" target="_blank">https://forums.sabnzbd.org/</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>GitHub:</strong></td>
|
||||
@@ -629,7 +627,7 @@
|
||||
</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>
|
||||
<td><a href="https://sabnzbd.org/live-chat" target="_blank">https://sabnzbd.org/live-chat</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -39,6 +39,11 @@
|
||||
<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>
|
||||
@@ -83,7 +88,7 @@
|
||||
<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: { 'glyphicon-play': pausedStatus(), 'glyphicon-pause': !pausedStatus() }"></span>
|
||||
<span class="hover-button glyphicon" data-bind="css: queueIcon"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td class="name">
|
||||
@@ -95,19 +100,18 @@
|
||||
<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 class="name-icons direct-unpack hover-button" data-bind="visible: direct_unpack" title="$T('opt-direct_unpack')">
|
||||
<span class="glyphicon glyphicon-compressed"></span> <span data-bind="text: direct_unpack"></span>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
<form data-bind="submit: editingNameSubmit">
|
||||
<input type="text" data-bind="value: nameForEdit, visible: editingName(), hasfocus: editingName" />
|
||||
</form>
|
||||
<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>
|
||||
<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>
|
||||
<small data-bind="text: avg_age"></small>
|
||||
</div>
|
||||
</td>
|
||||
@@ -117,8 +121,8 @@
|
||||
</td>
|
||||
<td class="progress-indicator">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-info" data-bind="attr: { 'style': 'width: ' + percentageRounded() + '; background-color: ' + progressColor() + ';' }">
|
||||
<strong data-bind="text: percentageRounded"></strong>
|
||||
<div class="progress-bar progress-bar-info" data-bind="attr: { 'style': 'width: ' + percentage() + '%; background-color: ' + progressColor() + ';' }">
|
||||
<strong data-bind="text: percentage() + '%'"></strong>
|
||||
<i data-bind="text: missingText"></i>
|
||||
</div>
|
||||
<span data-bind="text: progressText"></span>
|
||||
|
||||
@@ -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" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-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,13 +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="shortcut icon" type="image/ico" href="./staticcfg/ico/favicon.ico?v=1.0.0" data-bind="attr: { 'href': SABIcon }" />
|
||||
<link rel="shortcut icon" type="image/ico" href="./staticcfg/ico/favicon.ico?v=$version" data-bind="attr: { 'href': SABIcon }" />
|
||||
|
||||
<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)" />
|
||||
<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)" />
|
||||
<!--#if $color_scheme not in ('Default', '') #-->
|
||||
<link rel="stylesheet" type="text/css" href="./static/stylesheets/colorschemes/${color_scheme}.css?p=$pid"/>
|
||||
<link rel="stylesheet" type="text/css" href="./static/stylesheets/colorschemes/${color_scheme}.css?v=$version"/>
|
||||
<!--#end if#-->
|
||||
|
||||
<!-- Make translations available in scripts -->
|
||||
@@ -46,7 +46,7 @@
|
||||
var sabSpeedHistory = [$bytespersec_list];
|
||||
var newRelease = "$new_release";
|
||||
var newReleaseUrl = "$new_rel_url";
|
||||
var glitterIsBeta = ("$version".search(/Alpa|Beta|x/)) > 0;
|
||||
var glitterIsBeta = ("$version".search(/[develop|Alpha|Beta|RC]/)) > 0;
|
||||
var glitterPreLoadQueue = $preload_queue;
|
||||
var glitterPreLoadHistory = $preload_history;
|
||||
|
||||
@@ -58,10 +58,12 @@
|
||||
glitterTranslate.pauseFor = "$T('pauseFor')"
|
||||
glitterTranslate.minutes = "$T('mins')"
|
||||
glitterTranslate.shutdown = "$T('shutdownOK?')";
|
||||
glitterTranslate.restart = "$T('explain-Restart')".replace(/<br \/>/g, "\n");
|
||||
glitterTranslate.restart = "$T('explain-Restart') $T('explain-needNewLogin')".replace(/\<br(\s*\/|)\>/g, '\n');
|
||||
glitterTranslate.repair = "$T('explain-Repair')".replace(/<br \/>/g, "\n").replace(/"/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')";
|
||||
@@ -102,7 +104,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')";
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ function displayDateTime(inDate, outFormat, inFormat) {
|
||||
if(inDate == '') {
|
||||
var theMoment = moment()
|
||||
} else {
|
||||
var theMoment = moment(inDate, inFormat)
|
||||
var theMoment = moment.utc(inDate, inFormat)
|
||||
}
|
||||
// Special format or regular format?
|
||||
if(outFormat == 'fromNow') {
|
||||
|
||||
@@ -35,6 +35,30 @@ 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
|
||||
@@ -197,9 +221,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) {
|
||||
@@ -207,9 +231,10 @@ 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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,6 @@ 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())
|
||||
@@ -177,7 +176,20 @@ 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
|
||||
@@ -328,16 +340,14 @@ function HistoryModel(parent, data) {
|
||||
case 'speed':
|
||||
// Anything to calculate?
|
||||
if(self.historyStatus.bytes() > 0 && self.historyStatus.download_time() > 0) {
|
||||
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'
|
||||
}
|
||||
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) { }
|
||||
}
|
||||
return;
|
||||
case 'category':
|
||||
|
||||
@@ -331,6 +331,13 @@ 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]);
|
||||
|
||||
@@ -529,6 +536,7 @@ function ViewModel() {
|
||||
callAPI({
|
||||
mode: 'warnings'
|
||||
}).then(function(response) {
|
||||
|
||||
// Reset it all
|
||||
self.allWarnings.removeAll();
|
||||
if(response) {
|
||||
@@ -537,20 +545,16 @@ 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[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"),
|
||||
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"),
|
||||
clear: self.clearWarnings
|
||||
};
|
||||
|
||||
self.allWarnings.push(warningData)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -148,7 +148,6 @@ 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,
|
||||
@@ -156,6 +155,25 @@ 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
|
||||
@@ -460,11 +478,13 @@ 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')
|
||||
self.isGrabbing = ko.observable(data.status == 'Grabbing' || data.avg_age == '-')
|
||||
self.totalMB = ko.observable(parseFloat(data.mb));
|
||||
self.remainingMB = ko.observable(parseFloat(data.mbleft));
|
||||
self.remainingMB = ko.observable(parseFloat(data.mbleft))
|
||||
self.missingMB = ko.observable(parseFloat(data.mbmissing))
|
||||
self.percentage = ko.observable(parseInt(data.percentage))
|
||||
self.avg_age = ko.observable(data.avg_age)
|
||||
self.missing = ko.observable(data.missing)
|
||||
self.direct_unpack = ko.observable(data.direct_unpack)
|
||||
self.category = ko.observable(data.cat);
|
||||
self.priority = ko.observable(parent.priorityName[data.priority]);
|
||||
self.script = ko.observable(data.script);
|
||||
@@ -476,8 +496,6 @@ 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() {
|
||||
@@ -485,8 +503,8 @@ function QueueModel(parent, data) {
|
||||
if(self.status() == 'Checking') {
|
||||
return '#58A9FA'
|
||||
}
|
||||
// Check for missing data, the value is arbitrary!
|
||||
if(self.missing() > 50) {
|
||||
// Check for missing data, the value is arbitrary! (2%)
|
||||
if(self.missingMB()/self.totalMB() > 0.02) {
|
||||
return '#F8A34E'
|
||||
}
|
||||
// Set to grey, only when not Force download
|
||||
@@ -497,22 +515,16 @@ function QueueModel(parent, data) {
|
||||
return '';
|
||||
});
|
||||
|
||||
// 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))
|
||||
})
|
||||
// MB's
|
||||
self.progressText = ko.pureComputed(function() {
|
||||
return self.downloadedMB() + " MB / " + (self.totalMB() * 1).toFixed(0) + " MB";
|
||||
return (self.totalMB() - self.remainingMB()).toFixed(0) + " MB / " + (self.totalMB() * 1).toFixed(0) + " MB";
|
||||
})
|
||||
|
||||
// Texts
|
||||
self.missingText= ko.pureComputed(function() {
|
||||
// Check for missing data, the value is arbitrary!
|
||||
if(self.missing() > 50) {
|
||||
return self.missing() + ' ' + glitterTranslate.misingArt
|
||||
// Check for missing data, the value is arbitrary! (1%)
|
||||
if(self.missingMB()/self.totalMB() > 0.01) {
|
||||
return self.missingMB().toFixed(0) + ' MB ' + glitterTranslate.misingArt
|
||||
}
|
||||
return;
|
||||
})
|
||||
@@ -521,6 +533,10 @@ 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;
|
||||
@@ -529,6 +545,18 @@ 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?
|
||||
@@ -561,23 +589,19 @@ function QueueModel(parent, data) {
|
||||
self.password(data.password);
|
||||
self.index(data.index);
|
||||
self.status(data.status)
|
||||
self.isGrabbing(data.status == 'Grabbing')
|
||||
self.isGrabbing(data.status == 'Grabbing' || data.avg_age == '-')
|
||||
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.missing(data.missing)
|
||||
self.direct_unpack(data.direct_unpack)
|
||||
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
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
// Peity jQuery plugin version 3.2.0
|
||||
// (c) 2015 Ben Pickles
|
||||
// Peity jQuery plugin version 3.2.1
|
||||
// (c) 2016 Ben Pickles
|
||||
//
|
||||
// http://benpickles.github.io/peity
|
||||
//
|
||||
// Released under MIT license.
|
||||
!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);
|
||||
(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);
|
||||
@@ -1,123 +1,124 @@
|
||||
/*!
|
||||
* Knockout JavaScript library v3.4.0
|
||||
* (c) Steven Sanderson - http://knockoutjs.com/
|
||||
* Knockout JavaScript library v3.4.2
|
||||
* (c) The Knockout.js team - http://knockoutjs.com/
|
||||
* License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
|
||||
(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)})()})})();})();
|
||||
(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)})()})})();})();
|
||||
|
||||
@@ -50,7 +50,8 @@ legend,
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.hover-button {
|
||||
.hover-button,
|
||||
.fileControls a:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
@@ -75,13 +76,14 @@ 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 {
|
||||
.nav-tabs>li>a:hover,
|
||||
.fileControls a {
|
||||
color: black;
|
||||
}
|
||||
|
||||
@@ -109,6 +111,7 @@ legend,
|
||||
color: #EBEBEB;
|
||||
}
|
||||
|
||||
table,
|
||||
.table-striped>tbody>tr:nth-child(even)>td,
|
||||
.table>tbody>tr:nth-child(even)>td,
|
||||
.table th,
|
||||
@@ -156,7 +159,9 @@ select.form-control,
|
||||
.retry-button, .retry-button-inactive,
|
||||
.history-options-show-failed,
|
||||
.queue-error-info,
|
||||
.options-bad-status {
|
||||
.options-bad-status,
|
||||
.history-failed-download:hover .retry-button .glyphicon:before,
|
||||
.retry-button:hover .glyphicon:before {
|
||||
color: #F95151 !important;
|
||||
}
|
||||
|
||||
@@ -175,7 +180,7 @@ tbody .caret {
|
||||
color: #D6D6D6;
|
||||
}
|
||||
|
||||
td.name .name-ratings span,
|
||||
td.name .name-icons span,
|
||||
.navbar-nav .open .dropdown-menu>li>a,
|
||||
.dropdown-header,
|
||||
#modal-help small,
|
||||
@@ -203,6 +208,10 @@ 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;
|
||||
@@ -220,6 +229,11 @@ 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;
|
||||
|
||||
@@ -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: -150px;
|
||||
margin-top: 4px;
|
||||
margin-left: -220px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.navbar-collapse.in .dropdown-menu a,
|
||||
@@ -360,7 +360,7 @@ li.dropdown {
|
||||
|
||||
#feedback-slider:hover {
|
||||
left: 0px;
|
||||
height: 340px;
|
||||
height: 200px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -404,7 +404,6 @@ thead {
|
||||
|
||||
tr th {
|
||||
border-bottom: none !important;
|
||||
visibility: hidden;
|
||||
padding: 0px !important;
|
||||
height: 0px;
|
||||
}
|
||||
@@ -527,15 +526,23 @@ 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 {
|
||||
.hover-button.disabled:hover,
|
||||
.name-options.disabled .hover-button,
|
||||
.name-options.disabled .hover-button:hover {
|
||||
cursor: not-allowed !important;
|
||||
opacity: 0.1 !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.name-options.disabled {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.info-container {
|
||||
@@ -617,6 +624,7 @@ td.name .row-wrap-text {
|
||||
}
|
||||
|
||||
.queue-table td.name .name-options small,
|
||||
.queue-table td.name .direct-unpack,
|
||||
.queue-item-password {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -626,7 +634,7 @@ td.name .row-wrap-text {
|
||||
}
|
||||
|
||||
.queue-table td.name:hover .row-wrap-text {
|
||||
max-width: calc(100% - 85px);
|
||||
max-width: calc(100% - 125px);
|
||||
/* Change for each size! */
|
||||
}
|
||||
|
||||
@@ -648,19 +656,23 @@ td.name .row-wrap-text {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
td.name .name-ratings {
|
||||
td.name .name-icons {
|
||||
display: inline;
|
||||
margin-left: 5px;
|
||||
color: black !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.queue-table td.name:hover .name-ratings {
|
||||
display: none;
|
||||
td.name .name-icons .glyphicon {
|
||||
margin-left: 2px;
|
||||
top: 2px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td.name .name-ratings .glyphicon {
|
||||
margin-left: 2px;
|
||||
.glyphicon-chevron-down,
|
||||
.glyphicon-chevron-up {
|
||||
top: 2px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tbody.no-downloads tr td {
|
||||
@@ -769,6 +781,35 @@ 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;
|
||||
}
|
||||
@@ -1035,7 +1076,7 @@ tr.queue-item>td:first-child>a {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.history-ratings .name-ratings {
|
||||
.history-ratings .name-icons {
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
@@ -1623,6 +1664,11 @@ 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 {
|
||||
@@ -1810,7 +1856,7 @@ input[name="nzbURL"] {
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
td.name .name-ratings {
|
||||
td.name .name-icons {
|
||||
margin-left: 0px;
|
||||
margin-right: -5px;
|
||||
display: block;
|
||||
@@ -1857,6 +1903,11 @@ input[name="nzbURL"] {
|
||||
.queue .sortable-placeholder td {
|
||||
padding: 9px 0px 8px !important;
|
||||
}
|
||||
|
||||
.queue-table .buttonMoveToBottom,
|
||||
.queue-table .buttonMoveToTop {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-height: 800px) {
|
||||
|
||||
@@ -132,6 +132,11 @@ h2 {
|
||||
max-width: calc(100% - 45px);
|
||||
}
|
||||
|
||||
.queue-table .buttonMoveToBottom,
|
||||
.queue-table .buttonMoveToTop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
tr.queue-item>td:first-child>a {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
Plush for SABnzbd 0.6.x | Feb. 21 2010
|
||||
assembled by pairofdimes - see LICENSE-CC.txt
|
||||
http://forums.sabnzbd.org contributions welcome
|
||||
https://forums.sabnzbd.org contributions welcome
|
||||
|
||||
======================
|
||||
THANKS TO CONTRIBUTORS
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
#if $loadavg#$T('ft-sysload'): <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" title="#echo $last_warning.replace("\n"," ").replace('"',"'") #"><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"><span id="have_warnings">$have_warnings</span> $T('warnings')</a></b></span>
|
||||
#if $pane=="Main"#
|
||||
#if $new_release#⋅ <a href="$new_rel_url" id="new_release" target="_blank">$T('Plush-updateAvailable').replace(' ',' ')</a>#end if#
|
||||
#end if#
|
||||
|
||||
@@ -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="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>
|
||||
<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>
|
||||
</table>
|
||||
<div class="sabnzbd_logo main_sprite_container sprite_sabnzbdplus_logo"></div>
|
||||
<p><strong>SABnzbd $T('version'):</strong> $version</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
$.plush.queuenoofslots = $noofslots;
|
||||
$.plush.SetQueueSpeedLimit('$speedlimit', '$speedlimit_abs');
|
||||
$.plush.SetQueueFinishAction('$finishaction');
|
||||
$.plush.SetWarnings($have_warnings,"<!--#echo $last_warning.replace("\n"," ").replace('"',"'").replace('\\','\\\\') #-->");
|
||||
$.plush.SetWarnings($have_warnings,"");
|
||||
$.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.missing#-->$T('missingArt'): $slot.missing<!--#end if#-->">$slot.filename.replace('.', '.​').replace('_', '_​')<!--#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.mbmissing!="0.00"#-->$T('missingArt'): $slot.mbmissing $T('MB')<!--#end if#-->">$slot.filename.replace('.', '.​').replace('_', '_​')<!--#if $slot.password#--> / $slot.password<!--#end if#--></a>
|
||||
</td>
|
||||
|
||||
<!--#if $rating_enable#-->
|
||||
|
||||
@@ -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, #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) {
|
||||
$('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) {
|
||||
var link = this,
|
||||
$link = $(this);
|
||||
|
||||
@@ -1095,7 +1095,7 @@ $("a","#multiops_inputs").click(function(e){
|
||||
title:function(){return $(this).text().replace("&", "&").replace("<", "<").replace(">", ">");},
|
||||
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);
|
||||
|
||||
@@ -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,7 +33,11 @@
|
||||
<!--#for $warn in $warnings#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$warn.replace("\n","</td><td>", 2)</td></tr>
|
||||
<!--#set when = datetime.datetime.fromtimestamp($warn.time) #-->
|
||||
<td>$when</td>
|
||||
<td>$warn.type</td>
|
||||
<td>$warn.text</td>
|
||||
</tr>
|
||||
<!--#end for#-->
|
||||
</table>
|
||||
<!--#else#-->
|
||||
|
||||
@@ -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="http://forums.sabnzbd.org" target="_blank">$T('menu-forums')</a></li>
|
||||
<li><a href="https://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" />-->
|
||||
|
||||
16146
interfaces/smpl/templates/static/MochiKit/MochiKit.js
vendored
16146
interfaces/smpl/templates/static/MochiKit/MochiKit.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,2 @@
|
||||
dojo.hostenv.conditionalLoadModule({"common": ["MochiKit.MochiKit"]});
|
||||
dojo.hostenv.moduleLoaded("MochiKit.*");
|
||||
dojo.hostenv.conditionalLoadModule({"common": ["MochiKit.MochiKit"]});
|
||||
dojo.hostenv.moduleLoaded("MochiKit.*");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!--#import datetime#-->
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
location = "../#/status"
|
||||
@@ -36,7 +37,10 @@ $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#-->
|
||||
$warn<br/>
|
||||
<!--#set when = datetime.datetime.fromtimestamp($warn.time) #-->
|
||||
$when<br>
|
||||
$warn.type<br>
|
||||
$warn.text<br>
|
||||
<!--#end for#-->
|
||||
<!--#end if#-->
|
||||
|
||||
|
||||
@@ -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.1.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="../staticcfg/js/jquery-3.2.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="clearfix"></div>
|
||||
<iframe style="float: right; width: 315px; height: 315px;" frameborder="0" src="https://resources.sabnzbd.org/wizard/ad/$language"></iframe>
|
||||
<iframe style="float: right; width: 325px; height: 325px;" frameborder="0" src="https://sabnzbd.org/wizard#$language"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="session" value="$session" />
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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. ;-)
|
||||
|
||||
Binary file not shown.
BIN
osx/unrar/unrar
BIN
osx/unrar/unrar
Binary file not shown.
@@ -1,11 +1,11 @@
|
||||
#
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright (C) 2011-2015 by the SABnzbd Team
|
||||
# Copyright 2011-2017 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-0.8.x\n"
|
||||
"Project-Id-Version: SABnzbd-2.2.0-develop\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: shypike@sabnzbd.org\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
206
po/email/he.po
Normal file
206
po/email/he.po
Normal file
@@ -0,0 +1,206 @@
|
||||
# Hebrew translation for sabnzbd
|
||||
# Copyright (c) 2017 Rosetta Contributors and Canonical Ltd 2017
|
||||
# This file is distributed under the same license as the sabnzbd package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
|
||||
#
|
||||
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"
|
||||
"PO-Revision-Date: 2017-08-01 16:45+0000\n"
|
||||
"Last-Translator: ION IL <Unknown>\n"
|
||||
"Language-Team: Hebrew <he@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-08-02 06:03+0000\n"
|
||||
"X-Generator: Launchpad (build 18441)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd has <!--#if $status then \"completed\" else \"failed\" #--> "
|
||||
"job $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"\n"
|
||||
"Hi,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd has downloaded \"$name\" <!--#if $msgid==\"\" then \"\" else "
|
||||
"\"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd has failed to download \"$name\" <!--#if $msgid==\"\" then \"\" else "
|
||||
"\"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Finished at $end_time\n"
|
||||
"Downloaded $size\n"
|
||||
"\n"
|
||||
"Results of the job:\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Stage $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Output from user script \"$script\" (Exit code = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Enjoy!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## SABnzbd תבנית דוא\"ל ברירת מחדל עבור\n"
|
||||
"## זאת תבנית ברדלס\n"
|
||||
"## http://sabnzbd.wikidot.com/email-templates :תיעוד\n"
|
||||
"##\n"
|
||||
"## !שורות חדשות ורווחים לבנים הם משמעותיים\n"
|
||||
"##\n"
|
||||
"## אלו כותרות הדוא\"ל\n"
|
||||
"$to :אל\n"
|
||||
"$from :מאת\n"
|
||||
"תאריך: $date\n"
|
||||
"<!--#if $status then \"completed\" else \"failed\" #--> $name יש עבודה אשר "
|
||||
"SABnzbd-נושא: ל\n"
|
||||
"## !אחרי זה בא הגוף, השורה הריקה דרושה\n"
|
||||
"\n"
|
||||
",היי\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd הוריד את \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin "
|
||||
"#\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd נכשל להוריד את \"$name\" <!--#if $msgid==\"\" then \"\" else "
|
||||
"\"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"הסתיים ב-$end_time\n"
|
||||
"הורדו $size\n"
|
||||
"\n"
|
||||
":תוצאות העבודה\n"
|
||||
"<!--#for $stage ב $stages #-->\n"
|
||||
"שלב $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result ב $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
":(קוד יציאה = $script_ret) \"$script\" פלט מתסריט משתמש\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"!תהנה\n"
|
||||
"<!--#else#-->\n"
|
||||
"!סליחה\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd has added $amount jobs to the queue\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"\n"
|
||||
"Hi,\n"
|
||||
"\n"
|
||||
"SABnzbd has added $amount job(s) to the queue.\n"
|
||||
"They are from RSS feed \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## SABnzbd עבור RSS תבנית דוא\"ל\n"
|
||||
"## זאת תבנית ברדלס\n"
|
||||
"## http://sabnzbd.wikidot.com/email-templates :תיעוד\n"
|
||||
"##\n"
|
||||
"## !שורות חדשות ורווחים לבנים הם משמעותיים\n"
|
||||
"##\n"
|
||||
"## אלו כותרות הדוא\"ל\n"
|
||||
"$to :אל\n"
|
||||
"$from :מאת\n"
|
||||
"תאריך: $date\n"
|
||||
"הוסיף $amount עבודות לתור SABnzbd :נושא\n"
|
||||
"## !אחרי זה בא הגוף, השורה הריקה דרושה\n"
|
||||
"\n"
|
||||
",היי\n"
|
||||
"\n"
|
||||
".הוסיף $amount עבודות לתור SABnzbd\n"
|
||||
".\"$feed\" בשם RSS הם מהזנת\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"ביי\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd failed to fetch an NZB\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"\n"
|
||||
"Hi,\n"
|
||||
"\n"
|
||||
"SABnzbd has failed to retrieve the NZB from $url.\n"
|
||||
"The error message was: $msg\n"
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## SABnzbd רעה עבור URL תבנית דוא\"ל של משיכת\n"
|
||||
"## זאת תבנית ברדלס\n"
|
||||
"## http://sabnzbd.wikidot.com/email-templates :תיעוד\n"
|
||||
"##\n"
|
||||
"## !שורות חדשות ורווחים לבנים הם משמעותיים\n"
|
||||
"##\n"
|
||||
"## אלו כותרות הדוא\"ל\n"
|
||||
"$to :אל\n"
|
||||
"$from :מאת\n"
|
||||
"תאריך: $date\n"
|
||||
"NZB נכשל במשיכת SABnzbd :נושא\n"
|
||||
"## !אחרי זה בא הגוף, השורה הריקה דרושה\n"
|
||||
"\n"
|
||||
",היי\n"
|
||||
"\n"
|
||||
".$url מתוך NZB-נכשל לאחזר את ה SABnzbd\n"
|
||||
"הודעת השגיאה הייתה: $msg\n"
|
||||
"\n"
|
||||
"ביי\n"
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Norwegian Bokmal <nb@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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"Language-Team: Dutch <nl@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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Tomasz 'Zen' Napierala <tomasz@napierala.org>\n"
|
||||
"Language-Team: Polish <pl@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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: lrrosa <Unknown>\n"
|
||||
"Language-Team: Brazilian Portuguese <pt_BR@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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: nicusor <Unknown>\n"
|
||||
"Language-Team: Romanian <ro@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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Pavel Maryanov <Unknown>\n"
|
||||
"Language-Team: Russian <gnu@mx.ru>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Ozzii <Unknown>\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
|
||||
"PO-Revision-Date: 2017-06-24 19:51+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Launchpad Serbian Translators\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:00+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"Language: sr\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
@@ -66,20 +66,20 @@ msgid ""
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Шаблон основне е-поште за САБнзбд\n"
|
||||
"## Основни шаблон ел. поште за САБнзбд\n"
|
||||
"## Ово је Гепард шаблон\n"
|
||||
"## Документација: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Нови редови и размаци су важни!\n"
|
||||
"##\n"
|
||||
"## Ово су заглавља е-поруке\n"
|
||||
"Прима: $to\n"
|
||||
"Шаље: $from\n"
|
||||
"Датум: $date\n"
|
||||
"Тема: САБнзбд је <!--#if $status then \"завршио\" else \"није обавио\" #--> "
|
||||
"посао $name\n"
|
||||
"Х-приоритет: 5\n"
|
||||
"Х-МС-приоритет: 5\n"
|
||||
"## Ово су заглавља ел. поште\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: САБнзбд је <!--#if $status then \"completed\" else \"failed\" #--> "
|
||||
"посао „$name“\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## После тога долази разрада, празни редови су потребни!\n"
|
||||
"\n"
|
||||
"Здраво,\n"
|
||||
@@ -101,7 +101,7 @@ msgstr ""
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Излаз корисничке скрипте „$script“ (Код излаза = $script_ret):\n"
|
||||
"Излаз корисничке скрипте „$script“ (Шифра излаза = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
@@ -139,19 +139,19 @@ msgid ""
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Шаблон РСС е-поруке за САБнзбд\n"
|
||||
"## РСС шаблон ел. поште за САБнзбд\n"
|
||||
"## Ово је Гепард шаблон\n"
|
||||
"## Документација: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Нови редови и размаци су важни!\n"
|
||||
"##\n"
|
||||
"## Ово су заглавља е-поруке\n"
|
||||
"Прима: $to\n"
|
||||
"Шаље: $from\n"
|
||||
"Датум: $date\n"
|
||||
"Тема: САБнзбд је додао $amount посла у ред\n"
|
||||
"Х-приоритет: 5\n"
|
||||
"Х-МС-приоритет: 5\n"
|
||||
"## Ово су заглавља ел. поште\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject САБнзбд је додао $amount посла у ред\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## После тога долази разрада, празни редови су потребни!\n"
|
||||
"\n"
|
||||
"Здраво,\n"
|
||||
@@ -190,24 +190,24 @@ msgid ""
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"## Шаблон ел. поште лошег набављања адресе за САБнзбд\n"
|
||||
"## Ово је Гепард шаблон\n"
|
||||
"## Документација: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"## Нови редови и размаци су важни!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"За: $to\n"
|
||||
"Од: $from\n"
|
||||
"Датум: $date\n"
|
||||
"Сујјекат: SABnzbd није успео да преузме НЗБ\n"
|
||||
"## Ово су заглавља ел. поште\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: САБнзбд није успео да преузме НЗБ\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"## После тога долази разрада, празни редови су потребни!\n"
|
||||
"\n"
|
||||
"Здраво,\n"
|
||||
"\n"
|
||||
"SABnzbd није успео да преузме НЗБ од $url.\n"
|
||||
"САБнзбд није успео да преузме НЗБ са „$url“.\n"
|
||||
"Порука грешке је: $msg\n"
|
||||
"\n"
|
||||
"Поздрав\n"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Andreas Lindberg <andypandyswe@gmail.com>\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
|
||||
"PO-Revision-Date: 2017-06-24 19:50+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Swedish <sv@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: 2015-04-26 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:00+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
@@ -72,10 +72,10 @@ msgstr ""
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"Till: $to\n"
|
||||
"Från: $from\n"
|
||||
"Datum: $date\n"
|
||||
"Ämne: SABnzbd has <!--#if $status then \"completed\" else \"failed\" #--> "
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd has <!--#if $status then \"completed\" else \"failed\" #--> "
|
||||
"job $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
@@ -145,10 +145,10 @@ msgstr ""
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"Till: $to\n"
|
||||
"Från: $from\n"
|
||||
"Datum: $date\n"
|
||||
"Ämne: SABnzbd har lagt till $amount jobb i kön\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd har lagt till $amount jobb i kön\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
@@ -196,10 +196,10 @@ msgstr ""
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"Till: $to\n"
|
||||
"Från: $from\n"
|
||||
"Datum: $date\n"
|
||||
"Ämne: SABnzbd misslyckades med att hämta en NZB -fil\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd misslyckades med att hämta en NZB -fil\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:21+0000\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
|
||||
"PO-Revision-Date: 2015-10-24 11:05+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"Language-Team: Chinese (Simplified) <zh_CN@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: 2015-10-25 05:43+0000\n"
|
||||
"X-Generator: Launchpad (build 17812)\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
5571
po/main/SABnzbd.pot
5571
po/main/SABnzbd.pot
File diff suppressed because it is too large
Load Diff
2883
po/main/da.po
2883
po/main/da.po
File diff suppressed because it is too large
Load Diff
2930
po/main/de.po
2930
po/main/de.po
File diff suppressed because it is too large
Load Diff
@@ -15,27 +15,21 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: sabnzbd/skintext.py:67 [Config->Scheduler]
|
||||
msgid "Pause low prioirty jobs"
|
||||
msgstr "Pause low priority jobs"
|
||||
|
||||
#: sabnzbd/skintext.py:68 [Config->Scheduler]
|
||||
msgid "Pause normal prioirty jobs"
|
||||
msgstr "Pause normal priority jobs"
|
||||
|
||||
#: sabnzbd/skintext.py:69 [Config->Scheduler]
|
||||
msgid "Pause high prioirty jobs"
|
||||
msgstr "Pause high priority jobs"
|
||||
|
||||
#: sabnzbd/skintext.py:70 [Config->Scheduler]
|
||||
msgid "Resume low prioirty jobs"
|
||||
msgstr "Resume low priority jobs"
|
||||
|
||||
#: sabnzbd/skintext.py:71 [Config->Scheduler]
|
||||
msgid "Resume normal prioirty jobs"
|
||||
msgstr "Resume normal priority jobs"
|
||||
|
||||
#: sabnzbd/skintext.py:72 [Config->Scheduler]
|
||||
msgid "Resume high prioirty jobs"
|
||||
msgstr "Resume high priority jobs"
|
||||
|
||||
@@ -48,88 +42,78 @@ msgstr ""
|
||||
"click the button below.<br /><br /><strong><a "
|
||||
"href=\"..\">Refresh</a></strong><br />"
|
||||
|
||||
#: sabnzbd/skintext.py:251 [Retry all failed jobs in History]
|
||||
msgid "Retry all failed"
|
||||
msgstr "Retry All Failed"
|
||||
|
||||
#: sabnzbd/skintext.py:54 [#: Config->Scheduler]
|
||||
msgid "disable server"
|
||||
msgstr "Disable server:"
|
||||
|
||||
#: sabnzbd/skintext.py:55 [#: Config->Scheduler]
|
||||
msgid "enable server"
|
||||
msgstr "Enable server:"
|
||||
|
||||
#: sabnzbd/emailer.py:117
|
||||
msgid "The server didn't reply properly to the helo greeting"
|
||||
msgstr "The server didn't reply properly to the hello greeting"
|
||||
|
||||
#: sabnzbd/interface.py:226
|
||||
msgid "API Key missing, please enter the api key from Config->General into your 3rd party program:"
|
||||
msgstr "API key missing, please enter the API key from Config->General into your 3rd party program:"
|
||||
|
||||
#: sabnzbd/interface.py:233
|
||||
msgid "API Key incorrect, Use the api key from Config->General in your 3rd party program:"
|
||||
msgstr "API key incorrect, Use the API key from Config->General in your 3rd party program:"
|
||||
|
||||
#: sabnzbd/skintext.py:330
|
||||
msgid "HTTPS Chain Certifcates"
|
||||
msgstr "HTTPS Chain Certificates"
|
||||
|
||||
#: sabnzbd/skintext.py:465
|
||||
msgid "Replace Spaces in Foldername"
|
||||
msgstr "Replace spaces in folder name"
|
||||
|
||||
#: sabnzbd/skintext.py:467
|
||||
msgid "Replace dots in Foldername"
|
||||
msgstr "Replace dots in folder name"
|
||||
|
||||
#: sabnzbd/skintext.py:693
|
||||
msgid "Original Foldername"
|
||||
msgstr "Original folder name"
|
||||
|
||||
#: sabnzbd/skintext.py:808
|
||||
msgid "How long or untill when do you want to pause? (in English!)"
|
||||
msgstr "How long or until when do you want to pause? (in English!)"
|
||||
|
||||
#: sabnzbd/skintext.py:917
|
||||
msgid "Timeleft"
|
||||
msgstr "Time left"
|
||||
|
||||
#: sabnzbd/skintext.py:791 # sabnzbd/skintext.py:871
|
||||
msgid "Optionally specify a filename"
|
||||
msgstr "Optionally specify a name"
|
||||
|
||||
#: sabnzbd/skintext.py:819 # sabnzbd/skintext.py:880
|
||||
msgid "Confirm Queue Deletions"
|
||||
msgstr "Confirm queue deletions"
|
||||
|
||||
#: sabnzbd/skintext.py:820 # sabnzbd/skintext.py:881
|
||||
msgid "Confirm History Deletions"
|
||||
msgstr "Confirm history deletions"
|
||||
|
||||
#: sabnzbd/skintext.py:288 [Do not translate Pystone]
|
||||
msgid "System Performance (Pystone)"
|
||||
msgstr "System performance (Pystone)"
|
||||
|
||||
#: sabnzbd/skintext.py:317 # sabnzbd/skintext.py:779
|
||||
msgid "Web Interface"
|
||||
msgstr "Web interface"
|
||||
|
||||
#: sabnzbd/notifier.py
|
||||
msgid "Script returned exit code %s and output \"%s\""
|
||||
msgstr "Notification script returned exit code %s and output \"%s\""
|
||||
|
||||
#: sabnzbd/skintext.py:333
|
||||
msgid "If empty, the standard port will only listen to HTTPS."
|
||||
msgstr "If empty, the SABnzbd Port set above will listen to HTTPS."
|
||||
|
||||
#: sabnzbd/skintext.py:466
|
||||
msgid "Posts will be paused untill they are at least this age. Setting job priority to Force will skip the delay."
|
||||
msgstr "Posts will be paused until they are at least this age. Setting job priority to Force will skip the delay."
|
||||
|
||||
#: sabnzbd/skintext.py:841
|
||||
msgid "How long or until when do you want to pause? (in English!)"
|
||||
msgstr ""
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Support the project, donate!"
|
||||
|
||||
msgid "User script can flag job as failed"
|
||||
msgstr "Post-processing script can flag job as failed"
|
||||
|
||||
msgid "When the user script returns a non-zero exit code, the job will be flagged as failed."
|
||||
msgstr "When the post-processing script returns a non-zero exit code, the job will be flagged as failed."
|
||||
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "unrar binary... NOT found!"
|
||||
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr "Downloads will not be unpacked."
|
||||
|
||||
|
||||
2886
po/main/es.po
2886
po/main/es.po
File diff suppressed because it is too large
Load Diff
2882
po/main/fi.po
2882
po/main/fi.po
File diff suppressed because it is too large
Load Diff
2916
po/main/fr.po
2916
po/main/fr.po
File diff suppressed because it is too large
Load Diff
3381
po/main/he.po
3381
po/main/he.po
File diff suppressed because it is too large
Load Diff
2882
po/main/nb.po
2882
po/main/nb.po
File diff suppressed because it is too large
Load Diff
3227
po/main/nl.po
3227
po/main/nl.po
File diff suppressed because it is too large
Load Diff
2878
po/main/pl.po
2878
po/main/pl.po
File diff suppressed because it is too large
Load Diff
2876
po/main/pt_BR.po
2876
po/main/pt_BR.po
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user