Compare commits
634 Commits
1.2.x
...
2.3.0Alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
a724f6a979 | ||
|
|
715b25b52f | ||
|
|
4d3f370b3a | ||
|
|
07f6717728 | ||
|
|
35b4aa6b7a | ||
|
|
a7a04d912c | ||
|
|
ce558b0850 | ||
|
|
8ab7c294ee | ||
|
|
306228462e | ||
|
|
9e7a8468e2 | ||
|
|
4c2445485a | ||
|
|
103c46e2b4 | ||
|
|
b4922d69a2 | ||
|
|
110a06a3cd | ||
|
|
6f0f67110f | ||
|
|
848721da84 | ||
|
|
127d7ab40c | ||
|
|
4fb7246082 | ||
|
|
8c42237d51 | ||
|
|
6a87f0c4e4 | ||
|
|
f8630a878c | ||
|
|
7f6ef5e204 | ||
|
|
547d4dbf0a | ||
|
|
65e70a431c | ||
|
|
f85f4de5ff | ||
|
|
97644dea16 | ||
|
|
0a6105ebc1 | ||
|
|
de3d4f8d14 | ||
|
|
f337053aea | ||
|
|
2a4b49a679 | ||
|
|
ebe526f8cf | ||
|
|
926cd7b132 | ||
|
|
99667aa410 | ||
|
|
67cab3465e | ||
|
|
ce68a0654b | ||
|
|
dffdc3ae1f | ||
|
|
28e5311c6c | ||
|
|
ebf0526420 | ||
|
|
a2f73ca1f0 | ||
|
|
8ca150c48d | ||
|
|
d765fa09f1 | ||
|
|
878d68d343 | ||
|
|
fc7bd78dfa | ||
|
|
aca6ed360c | ||
|
|
53672d1d73 | ||
|
|
07d316ed4f | ||
|
|
8aa57bf406 | ||
|
|
d369097573 | ||
|
|
f4960715fa | ||
|
|
83ba676c43 | ||
|
|
12cca9dea1 | ||
|
|
5f52535c44 | ||
|
|
2f95410ab4 | ||
|
|
5749c0c008 | ||
|
|
73c71ef4bf | ||
|
|
6bc1c51013 | ||
|
|
bcdd3302a6 | ||
|
|
0fc3b60054 | ||
|
|
e9b0d4d691 | ||
|
|
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 | ||
|
|
8483e4ab8a | ||
|
|
f6c163b505 | ||
|
|
8f30173db0 | ||
|
|
0372ff95bb | ||
|
|
6fa29c7877 | ||
|
|
d4c9121593 | ||
|
|
76a8df0282 | ||
|
|
0b6d8309a0 | ||
|
|
10a9bc0817 | ||
|
|
2a14af4ffa | ||
|
|
d1a4a292e3 | ||
|
|
14c0efa151 | ||
|
|
4fc03f2581 | ||
|
|
3205b9fda9 | ||
|
|
953e0d6c22 | ||
|
|
b50ce54ca9 | ||
|
|
5e7558ce4a | ||
|
|
8aa6362432 | ||
|
|
02ebb97a8b | ||
|
|
b36063403d | ||
|
|
526ffa2afb | ||
|
|
5b3fd812d8 | ||
|
|
af6dac9cdc | ||
|
|
bc25d936bb | ||
|
|
b497fe1444 | ||
|
|
3f456cce05 | ||
|
|
4dd2f089ec | ||
|
|
b1b1bc248d | ||
|
|
d9e675469c | ||
|
|
ede0ca1772 | ||
|
|
2d098a1477 | ||
|
|
e5f014b68e | ||
|
|
b3a9dc9eeb | ||
|
|
2a06cec27c | ||
|
|
19230c889d | ||
|
|
c969ce552c | ||
|
|
2def600d21 | ||
|
|
02aa8f18c8 | ||
|
|
fcd9522dae | ||
|
|
72d3ce885e | ||
|
|
b428996eb7 | ||
|
|
2b4eb58fad | ||
|
|
240e8dff60 | ||
|
|
1c286afde6 | ||
|
|
2eeb908540 | ||
|
|
562e6ecce9 | ||
|
|
4bd0d32508 | ||
|
|
6f2ccbef80 | ||
|
|
61a6cb6d96 | ||
|
|
443efb5eda | ||
|
|
ba3c731fee | ||
|
|
e55f72dd1d | ||
|
|
b28c0a60a1 | ||
|
|
b7a80bf026 | ||
|
|
fe7218e64b | ||
|
|
0857a9046d | ||
|
|
354131b78a | ||
|
|
e3ae91a4f8 | ||
|
|
52cc5e2e4f | ||
|
|
0c04451442 | ||
|
|
f5ab4a2253 | ||
|
|
1303dfe17a | ||
|
|
55d80f26fa | ||
|
|
7aee585748 | ||
|
|
196858409c | ||
|
|
21467dd62f | ||
|
|
bb30eb7d11 | ||
|
|
fb9f4a7373 | ||
|
|
ccd5c1c75e | ||
|
|
015c578cdd | ||
|
|
8eb4ce2914 | ||
|
|
a6b8108ee6 | ||
|
|
181a56218a | ||
|
|
24feaaebd6 | ||
|
|
e1945e7a35 | ||
|
|
072f65dd9c | ||
|
|
bde03ecc63 | ||
|
|
73c2e23da4 | ||
|
|
e6233831d1 | ||
|
|
82ccbdaa7b | ||
|
|
bf5212a81c | ||
|
|
5c6cc932cf | ||
|
|
ed4430a7e0 | ||
|
|
eb73f78b1f | ||
|
|
314aad0009 | ||
|
|
98d0c5c52f | ||
|
|
4d4da889ec | ||
|
|
4a622f59ba | ||
|
|
913b92088a | ||
|
|
80f4690df8 | ||
|
|
f552531703 | ||
|
|
707d4a7a0c | ||
|
|
be5bebb574 | ||
|
|
4ca2a7a65e | ||
|
|
eed8c9bf50 | ||
|
|
11d5855430 | ||
|
|
b13413b1e5 | ||
|
|
9809474615 | ||
|
|
9ee3a61ae9 | ||
|
|
c71ffa02f8 | ||
|
|
2e862da292 | ||
|
|
08d762c6c9 | ||
|
|
2ef6f9e0e7 | ||
|
|
b8f84cf18d | ||
|
|
7e88af7047 | ||
|
|
2fc365dd57 | ||
|
|
434170862c | ||
|
|
1eb6c426fd | ||
|
|
e2c46d73e4 | ||
|
|
eef02ac7ce | ||
|
|
0d9614755e | ||
|
|
aa0557656c | ||
|
|
2c750f98cb | ||
|
|
82cf2b33cf | ||
|
|
b9cfe0d6f0 | ||
|
|
a0166a4011 | ||
|
|
c9f765813c | ||
|
|
2f52590587 | ||
|
|
fd581fffa5 | ||
|
|
fcdac1cb32 | ||
|
|
a824fa617b | ||
|
|
1fd0c23e55 | ||
|
|
884d4ee91b | ||
|
|
0c7d303568 | ||
|
|
a8ac8609d6 | ||
|
|
0d98a24c67 | ||
|
|
31eeb5e539 | ||
|
|
82dacda359 | ||
|
|
c3f82e49bf | ||
|
|
86a0e734b1 | ||
|
|
934db752bf | ||
|
|
ed379da657 | ||
|
|
55c4bef524 | ||
|
|
ccb329160d | ||
|
|
424626d64b | ||
|
|
026893e10d | ||
|
|
840b03c875 | ||
|
|
325f876010 | ||
|
|
91606a24b8 | ||
|
|
f001d8b749 | ||
|
|
31c0c239f9 | ||
|
|
918c4dbfce | ||
|
|
f587319ef0 | ||
|
|
b4dd942899 | ||
|
|
97a6720fba | ||
|
|
f05c1ef9e8 | ||
|
|
91c1ea97fd | ||
|
|
74faa159e7 | ||
|
|
7e9892bb8d | ||
|
|
0a9e54e5c5 | ||
|
|
0f0d16a104 | ||
|
|
7d7ee6ca6a | ||
|
|
0d96cd3fe8 | ||
|
|
596244543c | ||
|
|
237d6b9414 | ||
|
|
a7c42779f8 | ||
|
|
c49e5f2054 | ||
|
|
6ca6037aa0 | ||
|
|
cc7f360c04 | ||
|
|
75991bffea | ||
|
|
afd1b1968c | ||
|
|
746e9d2a6d | ||
|
|
36b5b5d0f3 | ||
|
|
dd603cfcc8 | ||
|
|
cefce9913a | ||
|
|
0b29f27fcd | ||
|
|
f3f3e27bfe | ||
|
|
519c44a72a | ||
|
|
7e28da0530 | ||
|
|
af70d98b50 | ||
|
|
3f0b84ea22 | ||
|
|
e58abd45ec | ||
|
|
abeee263f0 | ||
|
|
30f68bd7b9 | ||
|
|
f13394d27f | ||
|
|
88e0617429 | ||
|
|
ab94ffc055 | ||
|
|
265ab99cc7 | ||
|
|
738adbe38e | ||
|
|
4e6862cef9 | ||
|
|
7aff60b24d | ||
|
|
8819e38073 | ||
|
|
2bbac91436 | ||
|
|
cf4dea432b | ||
|
|
bd70df1f05 | ||
|
|
b2638c1fac | ||
|
|
4aa9409f5d | ||
|
|
dfa863a54a | ||
|
|
626c04df48 | ||
|
|
6026fa57f0 | ||
|
|
0db28fb5e2 | ||
|
|
c9bf8ced99 | ||
|
|
18a55db245 | ||
|
|
efb9664761 | ||
|
|
3ee412c7a5 | ||
|
|
5afc00a502 | ||
|
|
e5ca0e6415 | ||
|
|
98f121258c | ||
|
|
b2474c51fd | ||
|
|
1bd6ebdb41 | ||
|
|
20ef99326d | ||
|
|
171a1b9ae3 | ||
|
|
cd2c9d151a | ||
|
|
8fbfe9a76a | ||
|
|
63cf0d4f97 | ||
|
|
faa98126f8 | ||
|
|
0dd70249b9 | ||
|
|
aadd99cac3 | ||
|
|
5bede842ba | ||
|
|
1f2ac77b5e | ||
|
|
3f4f35c6d1 | ||
|
|
9575ddbdb4 | ||
|
|
3ed918d98d | ||
|
|
89d5af3372 | ||
|
|
fa8f40eee3 | ||
|
|
8f06035500 | ||
|
|
8f7d969099 | ||
|
|
5422785feb | ||
|
|
911f82c00b | ||
|
|
fbff8c991b | ||
|
|
f1b139d55d | ||
|
|
9c7f196e20 | ||
|
|
4d8a37006e | ||
|
|
bc9d3d561f | ||
|
|
fd9e80bdf5 | ||
|
|
aee2f71170 | ||
|
|
102160d651 | ||
|
|
e33f26d33c | ||
|
|
57113fa02f | ||
|
|
2be7575f98 | ||
|
|
c5647b46e1 | ||
|
|
3450cff92f | ||
|
|
13a11411a9 | ||
|
|
6e7eb9dec4 | ||
|
|
c74eed7c0e | ||
|
|
2fc6811495 | ||
|
|
57e0dac45b | ||
|
|
537e31000e | ||
|
|
e0872f4536 | ||
|
|
9dddf6dd2e | ||
|
|
7a0c5feed3 | ||
|
|
42d154f0b7 | ||
|
|
44b0ab2203 | ||
|
|
4af59b50ad | ||
|
|
b309099f0b | ||
|
|
affea99cb1 | ||
|
|
9bc35a3026 | ||
|
|
57e9d499fb | ||
|
|
d53cf598a4 | ||
|
|
bdaca2bd37 | ||
|
|
53e3af9b30 | ||
|
|
9cfef895f8 | ||
|
|
56fa9644a5 | ||
|
|
8eff51a96b | ||
|
|
d130a1d44a | ||
|
|
98316fd282 | ||
|
|
59f1ea3073 | ||
|
|
270757f3bd | ||
|
|
843c6b36a8 | ||
|
|
f71a2a8fc2 | ||
|
|
63fc763958 | ||
|
|
9bc0aac63d | ||
|
|
ff529da874 | ||
|
|
ff40944f00 | ||
|
|
1d0ac46c7e | ||
|
|
d62bb1e5b6 | ||
|
|
89f91e46b7 | ||
|
|
bc2daa5f8b | ||
|
|
0fbf240a58 | ||
|
|
83d57b33f7 | ||
|
|
59ae23e315 | ||
|
|
df26adfe89 | ||
|
|
d1a92aeb36 | ||
|
|
c09f1b2f1c | ||
|
|
a70a1e6290 | ||
|
|
dde5258b59 | ||
|
|
dc438e6eb7 | ||
|
|
37a9a97f4f | ||
|
|
4fb6e3fe7b | ||
|
|
7884848c78 | ||
|
|
2ece328e50 | ||
|
|
b8ac3cd22f | ||
|
|
9b4bd7a3f0 | ||
|
|
facefc5c58 | ||
|
|
5d6a6b1af7 | ||
|
|
916e0ead99 | ||
|
|
aff7b07f33 | ||
|
|
8267b429ca | ||
|
|
5759bee1df | ||
|
|
f2648ec85c | ||
|
|
e083722f0b | ||
|
|
f03e63fa54 | ||
|
|
f33c3e30eb | ||
|
|
c9ee0b0fcb | ||
|
|
7aaa8036bc | ||
|
|
00f2410d2d | ||
|
|
be77a494db | ||
|
|
787a95bdd2 | ||
|
|
720ce591b7 |
@@ -1,5 +1,5 @@
|
||||
*******************************************
|
||||
*** This is SABnzbd 1.2.x ***
|
||||
*** This is SABnzbd 2.2.0 ***
|
||||
*******************************************
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
@@ -12,9 +12,6 @@ and offers a complete API for third-party applications to hook into.
|
||||
There is an extensive Wiki on the use of SABnzbd.
|
||||
https://sabnzbd.org/wiki/
|
||||
|
||||
IMPORTANT INFORMATION about release 1.x.x:
|
||||
https://sabnzbd.org/wiki/new-features-and-changes
|
||||
|
||||
Please also read the file "ISSUES.txt"
|
||||
|
||||
The organization of the download queue is different from 0.7.x (and older).
|
||||
|
||||
48
INSTALL.txt
@@ -1,4 +1,4 @@
|
||||
SABnzbd 1.2.1
|
||||
SABnzbd 2.3.0
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
0) LICENSE
|
||||
@@ -21,67 +21,62 @@ along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
1) INSTALL with the Win32 installer
|
||||
1) INSTALL with the Windows installer
|
||||
-------------------------------------------------------------------------------
|
||||
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 Win32 binaries
|
||||
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 OSX binaries
|
||||
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 OSX 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/OSX
|
||||
All platforms
|
||||
Python-2.7.latest http://www.python.org (2.7.9+ recommended)
|
||||
|
||||
OSX Mavericks or newer
|
||||
Apple Python 2.7 Included in OSX (default)
|
||||
|
||||
Windows
|
||||
Python-2.7.latest http://www.python.org (2.7.9+ recommended)
|
||||
PyWin32 use "pip install pypiwin32"
|
||||
|
||||
Essential modules
|
||||
cheetah-2.0.1+ use "pip install cheetah"
|
||||
par2cmdline >= 0.4 http://parchive.sourceforge.net/
|
||||
Note: https://sabnzbd.org/wiki/configuration/1.2/switches#par2cmdline
|
||||
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/
|
||||
yenc module >= 0.4 use "pip install yenc"
|
||||
https://sabnzbd.org/wiki/installation/yenc-0.4_py2.7.rar (Win32-only)
|
||||
openssl => 1.0.0 http://www.openssl.org/
|
||||
v0.9.8 will work, but limits certificate validation
|
||||
sabyenc == 3.0.2 use "pip install sabyenc"
|
||||
More information: https://sabnzbd.org/sabyenc
|
||||
cryptography >= 1.0 use "pip install cryptography"
|
||||
Enables certificate generation and detection of encrypted RAR-files
|
||||
|
||||
Optional modules Unix/Linux/OSX
|
||||
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.
|
||||
@@ -91,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
|
||||
@@ -98,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.
|
||||
|
||||
|
||||
@@ -116,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.
|
||||
|
||||
@@ -124,7 +120,7 @@ may help you solve problems easier.
|
||||
6) MORE INFORMATION
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Visit the WIKI site:
|
||||
Visit our wiki:
|
||||
https://sabnzbd.org/wiki/
|
||||
|
||||
|
||||
@@ -134,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.
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
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/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.2/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/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.2/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 +41,7 @@
|
||||
You will see this only when downloaded files contain accented characters.
|
||||
You need to fix it yourself by running the convmv utility (available for most Linux platforms).
|
||||
Possible the file system override setting 'fsys_type' might be solve things:
|
||||
See: https://sabnzbd.org/wiki/configuration/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.2/special
|
||||
|
||||
- The "Watched Folder" sometimes fails to delete the NZB files it has
|
||||
processed. This happens when other software still accesses these files.
|
||||
@@ -81,4 +81,4 @@
|
||||
- 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/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.2/special
|
||||
|
||||
6
PKG-INFO
@@ -1,8 +1,8 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 1.2.RC1
|
||||
Summary: SABnzbd-1.2.1RC1
|
||||
Home-page: http://sabnzbd.org
|
||||
Version: 2.3.0Alpha2
|
||||
Summary: SABnzbd-2.3.0Alpha1
|
||||
Home-page: https://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
License: GNU General Public License 2 (GPL2 or later)
|
||||
|
||||
28
README.md
@@ -1,37 +1,25 @@
|
||||
SABnzbd - The automated Usenet download tool
|
||||
============================================
|
||||
|
||||
This Unicode release is not compatible with 0.7.x queues!
|
||||
|
||||
There is also an issue with upgrading of the "sabnzbd.ini" file.
|
||||
Make sure that you have a backup!
|
||||
|
||||
Saved queues may not be compatible after updates.
|
||||
|
||||
----
|
||||
|
||||
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.
|
||||
SABnzbd makes Usenet as simple and streamlined as possible by automating everything we can. All you have to do is add an `.nzb`. SABnzbd takes over from there, where it will be automatically downloaded, verified, repaired, extracted and filed away with zero human interaction.
|
||||
If you want to know more you can head over to our website: https://sabnzbd.org.
|
||||
|
||||
## Resolving Dependencies
|
||||
|
||||
SABnzbd has a good deal of dependencies you'll need before you can get running. If you've previously run SABnzbd from one of the various Linux packages floating around (Ubuntu, Debian, Fedora, etc), then you likely already have all the needed dependencies. If not, here's what you're looking for:
|
||||
SABnzbd has a good deal of dependencies you'll need before you can get running. If you've previously run SABnzbd from one of the various Linux packages, then you likely already have all the needed dependencies. If not, here's what you're looking for:
|
||||
|
||||
- `python` (only 2.7.x and higher, but not 3.x.x)
|
||||
- `python-cheetah`
|
||||
- `python-support`
|
||||
- `par2` (Multi-threaded par2 installation guide can be found [here](https://forums.sabnzbd.org/viewtopic.php?f=16&t=18793#p99702))
|
||||
- `par2` (Multi-threaded par2 installation guide can be found [here](https://sabnzbd.org/wiki/installation/multicore-par2))
|
||||
- `unrar` (Make sure you get the "official" non-free version of unrar)
|
||||
- `sabyenc` (installation guide can be found [here](https://sabnzbd.org/sabyenc))
|
||||
|
||||
Optional:
|
||||
|
||||
- `python-cryptography` (enables certificate generation and detection of encrypted RAR-files during download)
|
||||
- `python-yenc`
|
||||
- `python-dbus` (enable option to Shutdown/Restart/Standby PC on queue finish)
|
||||
- `7zip`
|
||||
- `unzip`
|
||||
@@ -43,13 +31,13 @@ Your package manager should supply these. If not, we've got links in our more in
|
||||
Once you've sorted out all the dependencies, simply run:
|
||||
|
||||
```
|
||||
python SABnzbd.py
|
||||
python -OO SABnzbd.py
|
||||
```
|
||||
|
||||
Or, if you want to run in the background:
|
||||
|
||||
```
|
||||
python SABnzbd.py -d -f /path/to/sabnzbd.ini
|
||||
python -OO SABnzbd.py -d -f /path/to/sabnzbd.ini
|
||||
```
|
||||
|
||||
If you want multi-language support, run:
|
||||
@@ -68,7 +56,7 @@ Basically:
|
||||
- `develop` is the target for integration and is **not** intended for end-users.
|
||||
- `1.1.x` is a release and maintenance branch for 1.1.x (1.1.0 -> 1.1.1 -> 1.1.2) and is **not** intended for end-users.
|
||||
- `feature/my_feature` is a temporary feature branch based on `develop`.
|
||||
- `hotfix/my_hotfix` is an optional temporary branch for bugfix(es) based on `develop`.
|
||||
- `bugfix/my_bugfix` is an optional temporary branch for bugfix(es) based on `develop`.
|
||||
|
||||
Conditions:
|
||||
- Merging of a stable release into `master` will be simple: the release branch is always right.
|
||||
|
||||
110
README.mkd
@@ -1,63 +1,67 @@
|
||||
Release Notes - SABnzbd 1.2.1RC1
|
||||
==============================================
|
||||
Release Notes - SABnzbd 2.3.0 Alpha 2
|
||||
=========================================================
|
||||
|
||||
## What's new in 1.2.1 RC1
|
||||
- On Windows paths passed to scripts were in \\?\ notation
|
||||
- New self-signed certificates now list local IP in SAN-list
|
||||
- Retry rename 3x before falling back to copy during "Moving"
|
||||
- Fix RSS page problems with missing fields
|
||||
- Passwords were not parsed correctly from filenames
|
||||
- Catch several SSL errors of the webserver
|
||||
- Multi-feed RSS will not stop if only 1 feed is not functioning
|
||||
- More incorrectly labeled some downloads as Encrypted
|
||||
## Changes and bugfixes since 2.3.0 Alpha 1
|
||||
- Specials Config page could not be loaded
|
||||
- Crash when adding new jobs
|
||||
- Further stalling-detection improvements
|
||||
- Crash when a job was set to only Download
|
||||
- Display of download progress and missing data improved
|
||||
- Retried jobs would show incorrect download progress
|
||||
- Different icon for downloads with Force priority
|
||||
- Show progress during verification of extra files
|
||||
- API: 'missing' field removed, use 'mbmissing'
|
||||
|
||||
|
||||
## What's new in 1.2.1 Beta 1
|
||||
- QuickCheck will perform fast rename of obfuscated posts
|
||||
- RSS Downloaded page now shows icon to indicate source
|
||||
- HTML tags are filtered from single-line script output
|
||||
## 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
|
||||
- RSS-feed title 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
|
||||
- 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
|
||||
|
||||
## Bug fixes in 1.2.1 Beta 1
|
||||
- Fix crashing Assembler
|
||||
- Cloaked files (RAR within RAR) were not detected anymore
|
||||
- 'Only Download Top of Queue' was broken for a long time
|
||||
- Incorrectly labeled some downloads as Encrypted
|
||||
- RSS reading could fail on newznab attributes
|
||||
- Incorrectly marking jobs with folders inside as failed
|
||||
- Handle jobs on Windows with forbidden names (Con.*, Aux.*,..)
|
||||
- Categories were not matched properly if a list of tags was set
|
||||
- PostProcessing-script was not called on Accept&Fail or Dupe detect
|
||||
- Support for newer par2cmdline(-mt) versions that need -B parameter
|
||||
- Some newsservers would timeout when connecting
|
||||
- Duplicate detection set to Fail would not work for RSS feeds
|
||||
- More robust detection of execute permissions for scripts
|
||||
- CPU type reporting on Windows and macOS
|
||||
- Failed to start with some localhost configs
|
||||
- Removed some more stalling issues
|
||||
## Bugfixes since 2.2.1
|
||||
- Reduce CPU usage with multiple servers
|
||||
- Fix yet another potential stalling issue
|
||||
- Remove Timeout tracebacks
|
||||
- Only warn if number of actual passwords is larger than 30
|
||||
- Unexpected behavior when diskspace becomes critically low
|
||||
- MacOS: Direct Unpack could hang in case of special charters in names
|
||||
- API: Correct listing of downloaded and queued files in get_files
|
||||
- API: Jobs with Force priority should always have status 'Downloading'
|
||||
|
||||
## About
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
thanks to its web-based user interface and advanced
|
||||
built-in post-processing options that automatically verify, repair,
|
||||
extract and clean up posts downloaded from Usenet.
|
||||
|
||||
(c) Copyright 2007-2017 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
|
||||
### IMPORTANT INFORMATION about release 1.x.x
|
||||
<https://sabnzbd.org/wiki/new-features-and-changes>
|
||||
|
||||
### Known problems and solutions
|
||||
- Read the file "ISSUES.txt"
|
||||
|
||||
### Upgrading from 0.7.x and older
|
||||
## Upgrading from 2.1.x and older
|
||||
- Finish queue
|
||||
- Stop SABnzbd
|
||||
- Install new version
|
||||
- Start SABnzbd
|
||||
|
||||
The organization of the download queue is different from older versions.
|
||||
1.x.x will not see the existing queue, but you can go to
|
||||
Status->QueueRepair and "Repair" the old queue.
|
||||
Also, your sabnzbd.ini file will be upgraded, making it
|
||||
incompatible with releases older than 0.7.9
|
||||
## 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.
|
||||
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"
|
||||
|
||||
## About
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically, thanks
|
||||
to its web-based user interface and advanced built-in post-processing options
|
||||
that automatically verify, repair, extract and clean up posts downloaded
|
||||
from Usenet.
|
||||
|
||||
(c) Copyright 2007-2017 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
|
||||
359
SABnzbd.py
@@ -37,6 +37,7 @@ import getopt
|
||||
import signal
|
||||
import socket
|
||||
import platform
|
||||
import ssl
|
||||
import time
|
||||
import re
|
||||
|
||||
@@ -56,8 +57,6 @@ if [int(n) for n in cherrypy.__version__.split('.')] < [8, 1, 2]:
|
||||
print 'Sorry, requires Python module Cherrypy 8.1.2+ (use the included version)'
|
||||
sys.exit(1)
|
||||
|
||||
from cherrypy import _cpserver
|
||||
|
||||
SQLITE_DLL = True
|
||||
try:
|
||||
from sqlite3 import version as sqlite3_version
|
||||
@@ -90,8 +89,8 @@ from sabnzbd.misc import real_path, \
|
||||
check_latest_version, exit_sab, \
|
||||
split_host, get_ext, create_https_certificates, \
|
||||
windows_variant, ip_extract, set_serv_parms, get_serv_parms, globber_full
|
||||
from sabnzbd.panic import panic_tmpl, panic_port, panic_host, panic_fwall, \
|
||||
panic_sqlite, panic, launch_a_browser, panic_xport
|
||||
from sabnzbd.panic import panic_tmpl, panic_port, panic_host, \
|
||||
panic_sqlite, panic, launch_a_browser
|
||||
import sabnzbd.scheduler as scheduler
|
||||
import sabnzbd.config as config
|
||||
import sabnzbd.cfg
|
||||
@@ -99,7 +98,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
|
||||
|
||||
@@ -130,24 +128,6 @@ def guard_loglevel():
|
||||
LOG_FLAG = True
|
||||
|
||||
|
||||
class FilterCP3:
|
||||
# Filter out all CherryPy3-Access logging that we receive,
|
||||
# because we have the root logger
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def filter(self, record):
|
||||
_cplogging = record.module == '_cplogging'
|
||||
# Python2.4 fix
|
||||
# record has no attribute called funcName under python 2.4
|
||||
if hasattr(record, 'funcName'):
|
||||
access = record.funcName == 'access'
|
||||
else:
|
||||
access = True
|
||||
return not (_cplogging and access)
|
||||
|
||||
|
||||
class guiHandler(logging.Handler):
|
||||
""" Logging handler collects the last warnings/errors/exceptions
|
||||
to be displayed in the web-gui
|
||||
@@ -202,10 +182,9 @@ def print_help():
|
||||
print " -f --config-file <ini> Location of config file"
|
||||
print " -s --server <srv:port> Listen on server:port [*]"
|
||||
print " -t --templates <templ> Template directory [*]"
|
||||
print " -2 --template2 <templ> Secondary template dir [*]"
|
||||
print
|
||||
print " -l --logging <0..2> Set logging level (-1=off, 0= least, 2= most) [*]"
|
||||
print " -w --weblogging <0..2> Set cherrypy logging (0= off, 1= on, 2= file-only) [*]"
|
||||
print " -w --weblogging Enable cherrypy access logging"
|
||||
print
|
||||
print " -b --browser <0..1> Auto browser launch (0= off, 1= on) [*]"
|
||||
if sabnzbd.WIN32:
|
||||
@@ -215,7 +194,6 @@ def print_help():
|
||||
print " --pid <path> Create a PID file in the given folder (full path)"
|
||||
print " --pidfile <path> Create a PID file with the given name (full path)"
|
||||
print
|
||||
print " --force Discard web-port timeout (see Wiki!)"
|
||||
print " -h --help Print this message"
|
||||
print " -v --version Print version information"
|
||||
print " -c --clean Remove queue, cache and logs"
|
||||
@@ -224,6 +202,7 @@ 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 " --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"
|
||||
@@ -274,9 +253,7 @@ def Bail_Out(browserhost, cherryport, err=''):
|
||||
""" Abort program because of CherryPy troubles """
|
||||
logging.error(T('Failed to start web-interface') + ' : ' + str(err))
|
||||
if not sabnzbd.DAEMON:
|
||||
if '13' in err:
|
||||
panic_xport(browserhost, cherryport)
|
||||
elif '49' in err:
|
||||
if '49' in err:
|
||||
panic_host(browserhost, cherryport)
|
||||
else:
|
||||
panic_port(browserhost, cherryport)
|
||||
@@ -304,9 +281,6 @@ def Web_Template(key, defweb, wdir):
|
||||
logging.info("Web dir is %s", full_dir)
|
||||
|
||||
if not os.path.exists(full_main):
|
||||
# Temporarily fix that allows missing Config
|
||||
if defweb == DEF_STDCONFIG:
|
||||
return ''
|
||||
# end temp fix
|
||||
logging.warning(T('Cannot find web template: %s, trying standard template'), full_main)
|
||||
full_dir = real_path(sabnzbd.DIR_INTERFACES, DEF_STDINTF)
|
||||
@@ -316,8 +290,6 @@ def Web_Template(key, defweb, wdir):
|
||||
panic_tmpl(full_dir)
|
||||
exit_sab(1)
|
||||
|
||||
# sabnzbd.lang.install_language(real_path(full_dir, DEF_INT_LANGUAGE), sabnzbd.cfg.language(), wdir)
|
||||
|
||||
return real_path(full_dir, "templates")
|
||||
|
||||
|
||||
@@ -428,10 +400,23 @@ def GetProfileInfo(vista_plus):
|
||||
|
||||
def print_modules():
|
||||
""" Log all detected optional or external modules """
|
||||
if sabnzbd.decoder.HAVE_YENC:
|
||||
logging.info("_yenc module... found!")
|
||||
if sabnzbd.decoder.SABYENC_ENABLED:
|
||||
# Yes, we have SABYenc, and it's the correct version, so it's enabled
|
||||
logging.info("SABYenc module (v%s)... found!", sabnzbd.constants.SABYENC_VERSION_REQUIRED)
|
||||
else:
|
||||
logging.warning(T('_yenc module... NOT found!'))
|
||||
# Something wrong with SABYenc, so let's determine and print what:
|
||||
if sabnzbd.decoder.SABYENC_VERSION:
|
||||
# We have a VERSION, thus a SABYenc module, but it's not the correct version
|
||||
logging.warning(T("SABYenc disabled: no correct version found! (Found v%s, expecting v%s)") % (sabnzbd.decoder.SABYENC_VERSION, sabnzbd.constants.SABYENC_VERSION_REQUIRED))
|
||||
else:
|
||||
# No SABYenc module at all
|
||||
logging.warning(T("SABYenc module... NOT found! Expecting v%s - https://sabnzbd.org/sabyenc") % sabnzbd.constants.SABYENC_VERSION_REQUIRED)
|
||||
|
||||
# No correct SABYenc version or no SABYenc at all, so now we care about old-yEnc
|
||||
if sabnzbd.decoder.HAVE_YENC:
|
||||
logging.info("_yenc module... found!")
|
||||
else:
|
||||
logging.error(T('_yenc module... NOT found!'))
|
||||
|
||||
if sabnzbd.HAVE_CRYPTOGRAPHY:
|
||||
logging.info('Cryptography module (v%s)... found!', sabnzbd.HAVE_CRYPTOGRAPHY)
|
||||
@@ -441,16 +426,25 @@ 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!'))
|
||||
logging.error('%s %s' % (T('par2 binary... NOT found!'), T('Verification and repair will not be possible.')))
|
||||
|
||||
if sabnzbd.newsunpack.PAR2C_COMMAND:
|
||||
logging.info("par2cmdline binary... found (%s)", sabnzbd.newsunpack.PAR2C_COMMAND)
|
||||
if sabnzbd.newsunpack.MULTIPAR_COMMAND:
|
||||
logging.info("MultiPar binary... found (%s)", sabnzbd.newsunpack.MULTIPAR_COMMAND)
|
||||
elif sabnzbd.WIN32:
|
||||
logging.error('%s %s' % (T('MultiPar binary... NOT found!'), T('Verification and repair will not be possible.')))
|
||||
|
||||
if sabnzbd.newsunpack.RAR_COMMAND:
|
||||
logging.info("UNRAR binary... found (%s)", sabnzbd.newsunpack.RAR_COMMAND)
|
||||
|
||||
# Report problematic unrar
|
||||
if sabnzbd.newsunpack.RAR_PROBLEM and not sabnzbd.cfg.ignore_wrong_unrar():
|
||||
have_str = '%.2f' % (float(sabnzbd.newsunpack.RAR_VERSION) / 100)
|
||||
want_str = '%.2f' % (float(sabnzbd.constants.REC_RAR_VERSION) / 100)
|
||||
logging.warning(T('Your UNRAR version is %s, we recommend version %s or higher.<br />') % (have_str, want_str))
|
||||
elif not (sabnzbd.WIN32 or sabnzbd.DARWIN):
|
||||
logging.info('UNRAR binary version %.2f', (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
|
||||
else:
|
||||
logging.warning(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)
|
||||
@@ -651,7 +645,7 @@ def attach_server(host, port, cert=None, key=None, chain=None):
|
||||
http_server = cherrypy._cpserver.Server()
|
||||
http_server.bind_addr = (host, port)
|
||||
if cert and key:
|
||||
http_server.ssl_provider = 'builtin'
|
||||
http_server.ssl_module = 'builtin'
|
||||
http_server.ssl_certificate = cert
|
||||
http_server.ssl_private_key = key
|
||||
http_server.ssl_certificate_chain = chain
|
||||
@@ -730,24 +724,6 @@ def evaluate_inipath(path):
|
||||
return path
|
||||
|
||||
|
||||
def cherrypy_logging(log_path, log_handler):
|
||||
""" Setup CherryPy logging """
|
||||
log = cherrypy.log
|
||||
log.access_file = ''
|
||||
log.error_file = ''
|
||||
# Max size of 512KB
|
||||
maxBytes = getattr(log, "rot_maxBytes", 524288)
|
||||
# cherrypy.log cherrypy.log.1 cherrypy.log.2
|
||||
backupCount = getattr(log, "rot_backupCount", 3)
|
||||
|
||||
# Make a new RotatingFileHandler for the error log.
|
||||
fname = getattr(log, "rot_error_file", log_path)
|
||||
h = log_handler(fname, 'a', maxBytes, backupCount)
|
||||
h.setLevel(logging.DEBUG)
|
||||
h.setFormatter(cherrypy._cplogging.logfmt)
|
||||
log.error_log.addHandler(h)
|
||||
|
||||
|
||||
def commandline_handler(frozen=True):
|
||||
""" Split win32-service commands are true parameters
|
||||
Returns:
|
||||
@@ -778,9 +754,9 @@ def commandline_handler(frozen=True):
|
||||
info.extend(sys.argv[slice:])
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(info, "phdvncw:l:s:f:t:b:2:",
|
||||
opts, args = getopt.getopt(info, "phdvncwl:s:f:t:b:2:",
|
||||
['pause', 'help', 'daemon', 'nobrowser', 'clean', 'logging=',
|
||||
'weblogging=', 'server=', 'templates', 'ipv6_hosting=',
|
||||
'weblogging', 'server=', 'templates', 'ipv6_hosting=',
|
||||
'template2', 'browser=', 'config-file=', 'force',
|
||||
'version', 'https=', 'autorestarted', 'repair', 'repair-all',
|
||||
'log-all', 'no-login', 'pid=', 'new', 'console', 'pidfile=',
|
||||
@@ -845,10 +821,8 @@ def main():
|
||||
clean_up = False
|
||||
logging_level = None
|
||||
web_dir = None
|
||||
web_dir2 = None
|
||||
vista_plus = False
|
||||
vista64 = False
|
||||
force_web = False
|
||||
win64 = False
|
||||
repair = 0
|
||||
api_url = None
|
||||
no_login = False
|
||||
@@ -879,8 +853,6 @@ def main():
|
||||
exit_sab(0)
|
||||
elif opt in ('-t', '--templates'):
|
||||
web_dir = arg
|
||||
elif opt in ('-2', '--template2'):
|
||||
web_dir2 = arg
|
||||
elif opt in ('-s', '--server'):
|
||||
(cherryhost, cherryport) = split_host(arg)
|
||||
elif opt in ('-n', '--nobrowser'):
|
||||
@@ -895,13 +867,7 @@ def main():
|
||||
elif opt in ('-c', '--clean'):
|
||||
clean_up = True
|
||||
elif opt in ('-w', '--weblogging'):
|
||||
try:
|
||||
cherrypylogging = int(arg)
|
||||
except:
|
||||
cherrypylogging = -1
|
||||
if cherrypylogging < 0 or cherrypylogging > 2:
|
||||
print_help()
|
||||
exit_sab(1)
|
||||
cherrypylogging = True
|
||||
elif opt in ('-l', '--logging'):
|
||||
try:
|
||||
logging_level = int(arg)
|
||||
@@ -915,9 +881,6 @@ def main():
|
||||
exit_sab(0)
|
||||
elif opt in ('-p', '--pause'):
|
||||
pause = True
|
||||
elif opt in ('--force',):
|
||||
force_web = True
|
||||
sabnzbd.RESTART_ARGS.append(opt)
|
||||
elif opt in ('--https',):
|
||||
https_port = int(arg)
|
||||
sabnzbd.RESTART_ARGS.append(opt)
|
||||
@@ -984,8 +947,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)
|
||||
@@ -1035,13 +998,13 @@ def main():
|
||||
if sabnzbd.DAEMON:
|
||||
if enable_https and https_port:
|
||||
try:
|
||||
cherrypy.process.servers.check_port(cherryhost, https_port, timeout=0.025)
|
||||
cherrypy.process.servers.check_port(cherryhost, https_port, timeout=0.05)
|
||||
except IOError, error:
|
||||
Bail_Out(browserhost, cherryport)
|
||||
except:
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
try:
|
||||
cherrypy.process.servers.check_port(cherryhost, cherryport, timeout=0.025)
|
||||
cherrypy.process.servers.check_port(cherryhost, cherryport, timeout=0.05)
|
||||
except IOError, error:
|
||||
Bail_Out(browserhost, cherryport)
|
||||
except:
|
||||
@@ -1054,58 +1017,62 @@ def main():
|
||||
if url and check_for_sabnzbd(url, upload_nzbs, autobrowser):
|
||||
exit_sab(0)
|
||||
|
||||
# If an instance of sabnzbd(same version) is already running on this port, launch the browser
|
||||
# If another program or sabnzbd version is on this port, try 10 other ports going up in a step of 5
|
||||
# If 'Port is not bound' (firewall) do not do anything (let the script further down deal with that).
|
||||
notify_port_change = False
|
||||
|
||||
# SSL
|
||||
if enable_https:
|
||||
port = https_port or cherryport
|
||||
try:
|
||||
cherrypy.process.servers.check_port(browserhost, port, timeout=0.025)
|
||||
cherrypy.process.servers.check_port(browserhost, port, timeout=0.05)
|
||||
except IOError, error:
|
||||
if str(error) == 'Port not bound.':
|
||||
pass
|
||||
else:
|
||||
if not url:
|
||||
url = 'https://%s:%s/sabnzbd/api?' % (browserhost, port)
|
||||
if not sabnzbd.cfg.fixed_ports():
|
||||
if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
|
||||
newport = find_free_port(browserhost, port)
|
||||
if newport > 0:
|
||||
notify_port_change = True
|
||||
# Save the new port
|
||||
if https_port:
|
||||
https_port = newport
|
||||
sabnzbd.cfg.https_port.set(newport)
|
||||
else:
|
||||
# In case HTTPS == HTTP port
|
||||
http_port = newport
|
||||
sabnzbd.cfg.port.set(newport)
|
||||
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():
|
||||
Bail_Out(browserhost, cherryport)
|
||||
# Find free port to bind
|
||||
newport = find_free_port(browserhost, port)
|
||||
if newport > 0:
|
||||
# Save the new port
|
||||
if https_port:
|
||||
https_port = newport
|
||||
sabnzbd.cfg.https_port.set(newport)
|
||||
else:
|
||||
# In case HTTPS == HTTP port
|
||||
cherryport = newport
|
||||
sabnzbd.cfg.port.set(newport)
|
||||
except:
|
||||
# Something else wrong, probably badly specified host
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
|
||||
# NonSSL check if there's no HTTPS or we only use 1 port
|
||||
if not (enable_https and not https_port):
|
||||
try:
|
||||
cherrypy.process.servers.check_port(browserhost, cherryport, timeout=0.025)
|
||||
cherrypy.process.servers.check_port(browserhost, cherryport, timeout=0.05)
|
||||
except IOError, error:
|
||||
if str(error) == 'Port not bound.':
|
||||
pass
|
||||
else:
|
||||
if not url:
|
||||
url = 'http://%s:%s/sabnzbd/api?' % (browserhost, cherryport)
|
||||
if not sabnzbd.cfg.fixed_ports():
|
||||
if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
|
||||
port = find_free_port(browserhost, cherryport)
|
||||
if port > 0:
|
||||
sabnzbd.cfg.cherryport.set(port)
|
||||
notify_port_change = True
|
||||
cherryport = port
|
||||
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():
|
||||
Bail_Out(browserhost, cherryport)
|
||||
# Find free port to bind
|
||||
port = find_free_port(browserhost, cherryport)
|
||||
if port > 0:
|
||||
sabnzbd.cfg.cherryport.set(port)
|
||||
cherryport = port
|
||||
except:
|
||||
# Something else wrong, probably badly specified host
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
|
||||
# We found a port, now we never check again
|
||||
sabnzbd.cfg.fixed_ports.set(True)
|
||||
|
||||
if logging_level is None:
|
||||
logging_level = sabnzbd.cfg.log_level()
|
||||
else:
|
||||
@@ -1139,7 +1106,6 @@ def main():
|
||||
|
||||
logformat = '%(asctime)s::%(levelname)s::[%(module)s:%(lineno)d] %(message)s'
|
||||
rollover_log.setFormatter(logging.Formatter(logformat))
|
||||
rollover_log.addFilter(FilterCP3())
|
||||
sabnzbd.LOGHANDLER = rollover_log
|
||||
logger.addHandler(rollover_log)
|
||||
logger.setLevel(LOGLEVELS[logging_level + 1])
|
||||
@@ -1169,7 +1135,6 @@ def main():
|
||||
|
||||
if consoleLogging:
|
||||
console = logging.StreamHandler()
|
||||
console.addFilter(FilterCP3())
|
||||
console.setLevel(LOGLEVELS[logging_level + 1])
|
||||
console.setFormatter(logging.Formatter(logformat))
|
||||
logger.addHandler(console)
|
||||
@@ -1186,25 +1151,47 @@ 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)
|
||||
logging.info('Arguments = %s', sabnzbd.CMDLINE)
|
||||
|
||||
# Find encoding; relevant for unrar activities
|
||||
try:
|
||||
logging.info('Preferred encoding = %s', locale.getpreferredencoding())
|
||||
preferredencoding = locale.getpreferredencoding()
|
||||
logging.info('Preferred encoding = %s', preferredencoding)
|
||||
except:
|
||||
logging.info('Preferred encoding = ERROR')
|
||||
preferredencoding = ''
|
||||
|
||||
# On Linux/FreeBSD/Unix "UTF-8" is strongly, strongly adviced:
|
||||
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()
|
||||
@@ -1229,23 +1216,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)
|
||||
|
||||
# OSX 10.5 I/O priority setting
|
||||
if sabnzbd.DARWIN:
|
||||
logging.info('[osx] IO priority setting')
|
||||
try:
|
||||
from ctypes import cdll
|
||||
libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
|
||||
boolSetResult = libc.setiopolicy_np(0, 1, 3)
|
||||
logging.info('[osx] IO priority set to throttle for process scope')
|
||||
except:
|
||||
logging.info('[osx] IO priority setting not supported')
|
||||
logging.debug('CPU model = %s', cpumodel)
|
||||
|
||||
logging.info('Read INI file %s', inifile)
|
||||
|
||||
@@ -1262,21 +1238,12 @@ def main():
|
||||
|
||||
os.chdir(sabnzbd.DIR_PROG)
|
||||
|
||||
web_dir = Web_Template(sabnzbd.cfg.web_dir, DEF_STDINTF, fix_webname(web_dir))
|
||||
web_dir2 = Web_Template(sabnzbd.cfg.web_dir2, '', fix_webname(web_dir2))
|
||||
web_dirc = Web_Template(None, DEF_STDCONFIG, '')
|
||||
sabnzbd.WEB_DIR = Web_Template(sabnzbd.cfg.web_dir, DEF_STDINTF, fix_webname(web_dir))
|
||||
sabnzbd.WEB_DIR_CONFIG = Web_Template(None, DEF_STDCONFIG, '')
|
||||
sabnzbd.WIZARD_DIR = os.path.join(sabnzbd.DIR_INTERFACES, 'wizard')
|
||||
|
||||
wizard_dir = os.path.join(sabnzbd.DIR_INTERFACES, 'wizard')
|
||||
|
||||
sabnzbd.WEB_DIR = web_dir
|
||||
sabnzbd.WEB_DIR2 = web_dir2
|
||||
sabnzbd.WEB_DIRC = web_dirc
|
||||
sabnzbd.WIZARD_DIR = wizard_dir
|
||||
|
||||
sabnzbd.WEB_COLOR = CheckColor(sabnzbd.cfg.web_color(), web_dir)
|
||||
sabnzbd.WEB_COLOR = CheckColor(sabnzbd.cfg.web_color(), sabnzbd.WEB_DIR)
|
||||
sabnzbd.cfg.web_color.set(sabnzbd.WEB_COLOR)
|
||||
sabnzbd.WEB_COLOR2 = CheckColor(sabnzbd.cfg.web_color2(), web_dir2)
|
||||
sabnzbd.cfg.web_color2.set(sabnzbd.WEB_COLOR2)
|
||||
|
||||
if fork and not sabnzbd.WIN32:
|
||||
daemonize()
|
||||
@@ -1298,29 +1265,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()))
|
||||
|
||||
cherrylogtoscreen = False
|
||||
sabnzbd.WEBLOGFILE = None
|
||||
|
||||
if cherrypylogging:
|
||||
if logdir:
|
||||
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
|
||||
# Define our custom logger for cherrypy errors
|
||||
cherrypy_logging(sabnzbd.WEBLOGFILE, logging.handlers.RotatingFileHandler)
|
||||
if not fork:
|
||||
try:
|
||||
x = sys.stderr.fileno
|
||||
x = sys.stdout.fileno
|
||||
if cherrypylogging == 1:
|
||||
cherrylogtoscreen = True
|
||||
except:
|
||||
pass
|
||||
|
||||
# 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()
|
||||
@@ -1336,6 +1283,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')
|
||||
@@ -1360,7 +1308,7 @@ def main():
|
||||
cherryport = https_port
|
||||
elif multilocal:
|
||||
# Extra HTTPS port for secondary localhost
|
||||
attach_server(hosts[1], cherryport, https_cert, https_key)
|
||||
attach_server(hosts[1], cherryport, https_cert, https_key, https_chain)
|
||||
|
||||
cherrypy.config.update({'server.ssl_module': 'builtin',
|
||||
'server.ssl_certificate': https_cert,
|
||||
@@ -1387,60 +1335,49 @@ def main():
|
||||
'server.socket_host': cherryhost,
|
||||
'server.socket_port': cherryport,
|
||||
'server.shutdown_timeout': 0,
|
||||
'log.screen': cherrylogtoscreen,
|
||||
'log.screen': False,
|
||||
'engine.timeout_monitor.on': False,
|
||||
'engine.autoreload.on': False,
|
||||
'tools.encode.on': True,
|
||||
'tools.gzip.on': True,
|
||||
'tools.gzip.mime_types': mime_gzip,
|
||||
'request.show_tracebacks': True,
|
||||
'checker.check_localhost': bool(consoleLogging),
|
||||
'error_page.401': sabnzbd.panic.error_page_401,
|
||||
'error_page.404': sabnzbd.panic.error_page_404
|
||||
})
|
||||
|
||||
forced_mime_types = {'css': 'text/css', 'js': 'application/javascript'}
|
||||
static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
staticcfg = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dirc, 'staticcfg'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
wizard_static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(wizard_dir, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
# Do we want CherryPy Logging? Cannot be done via the config
|
||||
if cherrypylogging:
|
||||
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
|
||||
cherrypy.log.screen = True
|
||||
cherrypy.log.access_log.propagate = True
|
||||
cherrypy.log.access_file = str(sabnzbd.WEBLOGFILE)
|
||||
else:
|
||||
cherrypy.log.access_log.propagate = False
|
||||
|
||||
appconfig = {'/sabnzbd/api': {'tools.basic_auth.on': False},
|
||||
'/api': {'tools.basic_auth.on': False},
|
||||
'/m/api': {'tools.basic_auth.on': False},
|
||||
# Force mimetypes (OS might overwrite them)
|
||||
forced_mime_types = {'css': 'text/css', 'js': 'application/javascript'}
|
||||
|
||||
static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WEB_DIR, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
staticcfg = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WEB_DIR_CONFIG, 'staticcfg'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
wizard_static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WIZARD_DIR, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
|
||||
appconfig = {'/api': {'tools.basic_auth.on': False},
|
||||
'/rss': {'tools.basic_auth.on': False},
|
||||
'/sabnzbd/rss': {'tools.basic_auth.on': False},
|
||||
'/m/rss': {'tools.basic_auth.on': False},
|
||||
'/sabnzbd/shutdown': {'streamResponse': True},
|
||||
'/sabnzbd/static': static,
|
||||
'/static': static,
|
||||
'/sabnzbd/wizard/static': wizard_static,
|
||||
'/wizard/static': wizard_static,
|
||||
'/favicon.ico': {'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(web_dirc, 'staticcfg', 'ico', 'favicon.ico')},
|
||||
'/sabnzbd/staticcfg': staticcfg,
|
||||
'/favicon.ico': {'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(sabnzbd.WEB_DIR_CONFIG, 'staticcfg', 'ico', 'favicon.ico')},
|
||||
'/staticcfg': staticcfg
|
||||
}
|
||||
|
||||
if web_dir2:
|
||||
static2 = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir2, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
appconfig['/sabnzbd/m/api'] = {'tools.basic_auth.on': False}
|
||||
appconfig['/sabnzbd/m/rss'] = {'tools.basic_auth.on': False}
|
||||
appconfig['/sabnzbd/m/shutdown'] = {'streamResponse': True}
|
||||
appconfig['/sabnzbd/m/static'] = static2
|
||||
appconfig['/m/static'] = static2
|
||||
appconfig['/sabnzbd/m/wizard/static'] = wizard_static
|
||||
appconfig['/m/wizard/static'] = wizard_static
|
||||
appconfig['/sabnzbd/m/staticcfg'] = staticcfg
|
||||
appconfig['/m/staticcfg'] = staticcfg
|
||||
|
||||
login_page = sabnzbd.interface.MainPage(web_dir, '/', web_dir2, '/m/', web_dirc, first=2)
|
||||
cherrypy.tree.mount(login_page, '/', config=appconfig)
|
||||
# Make available from both URLs
|
||||
main_page = sabnzbd.interface.MainPage()
|
||||
cherrypy.tree.mount(main_page, '/', config=appconfig)
|
||||
cherrypy.tree.mount(main_page, sabnzbd.cfg.url_base(), config=appconfig)
|
||||
|
||||
# Set authentication for CherryPy
|
||||
sabnzbd.interface.set_auth(cherrypy.config)
|
||||
|
||||
# Notify if port was changed
|
||||
if notify_port_change:
|
||||
logging.warning(T('Could not bind to configured port. Port changed to %s') % cherryport)
|
||||
|
||||
logging.info('Starting web-interface on %s:%s', cherryhost, cherryport)
|
||||
|
||||
sabnzbd.cfg.log_level.callback(guard_loglevel)
|
||||
@@ -1453,15 +1390,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()
|
||||
@@ -1494,9 +1431,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:
|
||||
@@ -1510,6 +1447,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:
|
||||
@@ -1758,9 +1702,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
|
||||
|
||||
@@ -1785,9 +1728,7 @@ if __name__ == '__main__':
|
||||
sabApp = startApp()
|
||||
sabApp.start()
|
||||
AppHelper.runEventLoop()
|
||||
|
||||
except:
|
||||
main()
|
||||
|
||||
else:
|
||||
main()
|
||||
|
||||
@@ -214,18 +214,7 @@ class HTTPRedirect(CherryPyException):
|
||||
if isinstance(urls, text_or_bytes):
|
||||
urls = [urls]
|
||||
|
||||
abs_urls = []
|
||||
for url in urls:
|
||||
url = tonative(url, encoding or self.encoding)
|
||||
|
||||
# Note that urljoin will "do the right thing" whether url is:
|
||||
# 1. a complete URL with host (e.g. "http://www.example.com/test")
|
||||
# 2. a URL relative to root (e.g. "/dummy")
|
||||
# 3. a URL relative to the current path
|
||||
# Note that any query string in cherrypy.request is discarded.
|
||||
url = _urljoin(cherrypy.url(), url)
|
||||
abs_urls.append(url)
|
||||
self.urls = abs_urls
|
||||
self.urls = [tonative(url, encoding or self.encoding) for url in urls]
|
||||
|
||||
# RFC 2616 indicates a 301 response code fits our goal; however,
|
||||
# browser support for 301 is quite messy. Do 302/303 instead. See
|
||||
@@ -241,7 +230,7 @@ class HTTPRedirect(CherryPyException):
|
||||
raise ValueError('status must be between 300 and 399.')
|
||||
|
||||
self.status = status
|
||||
CherryPyException.__init__(self, abs_urls, status)
|
||||
CherryPyException.__init__(self, self.urls, status)
|
||||
|
||||
def set_response(self):
|
||||
"""Modify cherrypy.response status, headers, and body to represent
|
||||
|
||||
@@ -196,12 +196,13 @@ socket_errors_to_ignore = plat_specific_errors(
|
||||
)
|
||||
socket_errors_to_ignore.append('timed out')
|
||||
socket_errors_to_ignore.append('The read operation timed out')
|
||||
if sys.platform == 'darwin':
|
||||
socket_errors_to_ignore.append(plat_specific_errors('EPROTOTYPE'))
|
||||
|
||||
socket_errors_nonblocking = plat_specific_errors(
|
||||
'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
socket_errors_to_ignore.extend(plat_specific_errors('EPROTOTYPE'))
|
||||
socket_errors_nonblocking.extend(plat_specific_errors('EPROTOTYPE'))
|
||||
|
||||
comma_separated_headers = [
|
||||
ntob(h) for h in
|
||||
['Accept', 'Accept-Charset', 'Accept-Encoding',
|
||||
|
||||
@@ -85,8 +85,8 @@ class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
|
||||
|
||||
# Check if it's one of the known errors
|
||||
# Errors that are caught by PyOpenSSL, but thrown by built-in ssl
|
||||
_block_errors = ('unknown protocol', 'unknown ca', 'unknown_ca',
|
||||
'inappropriate fallback', 'wrong version number',
|
||||
_block_errors = ('unknown protocol', 'unknown ca', 'unknown_ca', 'unknown error',
|
||||
'https proxy request', 'inappropriate fallback', 'wrong version number',
|
||||
'no shared cipher', 'certificate unknown', 'ccs received early')
|
||||
for error_text in _block_errors:
|
||||
if error_text in e.args[1].lower():
|
||||
@@ -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
|
||||
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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/1.2/configure"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/configure"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#from locale import getpreferredencoding#-->
|
||||
@@ -19,10 +19,6 @@
|
||||
<th scope="row">$T('confgFile'): </th>
|
||||
<td>$configfn</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">$T('cache').capitalize(): </th>
|
||||
<td><!--#set $msg=$T('ft-buffer@2')%($cache_art, $cache_size)#-->$msg</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">$T('parameters'): </th>
|
||||
<td>$cmdline</td>
|
||||
@@ -34,12 +30,7 @@
|
||||
<tr>
|
||||
<th scope="row">OpenSSL:</th>
|
||||
<td>
|
||||
<!--#if $have_ssl#-->
|
||||
$ssl_version [$ssl_protocols]
|
||||
<!--#else#-->
|
||||
<span class="label label-danger">$T('notAvailable')</span>
|
||||
<a href="$helpuri$help_uri#no_ssl" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<!--#end if#-->
|
||||
$ssl_version
|
||||
</td>
|
||||
</tr>
|
||||
<!--#if not $have_ssl_context#-->
|
||||
@@ -50,6 +41,19 @@
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $nt and not $darwin#-->
|
||||
<tr>
|
||||
<th scope="row">$T('opt-multicore-par2')</th>
|
||||
<td>
|
||||
<!--#if $have_mt_par2#-->
|
||||
<span class="glyphicon glyphicon-ok"></span>
|
||||
<!--#else#-->
|
||||
<span class="label label-warning">$T('notAvailable')</span> $T('explain-getpar2mt')
|
||||
<a href="${helpuri}installation/multicore-par2" target="_blank">${helpuri}installation/multicore-par2</a>
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_cryptography #-->
|
||||
<tr>
|
||||
<th scope="row">Python Cryptography:</th>
|
||||
@@ -59,7 +63,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_yenc#-->
|
||||
<!--#if not $have_yenc and not $have_sabyenc#-->
|
||||
<tr>
|
||||
<th scope="row">yEnc:</th>
|
||||
<td>
|
||||
@@ -68,6 +72,15 @@
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_sabyenc#-->
|
||||
<tr>
|
||||
<th scope="row">SABYenc:</th>
|
||||
<td>
|
||||
<span class="label label-danger">$T('notAvailable')</span>
|
||||
<a href="$helpuri$help_uri#no_sabyenc" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_unzip #-->
|
||||
<tr>
|
||||
<th scope="row">$T('opt-enable_unzip'):</th>
|
||||
@@ -96,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>
|
||||
@@ -104,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>
|
||||
@@ -118,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/1.2/categories"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/categories"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<div class="section">
|
||||
@@ -17,9 +17,7 @@
|
||||
<th>$T('category')</th>
|
||||
<th>$T('priority')</th>
|
||||
<th>$T('mode')</th>
|
||||
<!--#if $script_list#-->
|
||||
<th>$T('script')</th>
|
||||
<!--#end if#-->
|
||||
<th>$T('catFolderPath')</th>
|
||||
<th colspan="2">$T('catTags')</th>
|
||||
</tr>
|
||||
@@ -61,17 +59,21 @@
|
||||
<option value="3" <!--#if $slot.pp=="3" then 'selected="selected"' else ""#-->>$T('pp-delete')</option>
|
||||
</select>
|
||||
</td>
|
||||
<!--#if $script_list#-->
|
||||
<td>
|
||||
<!--#if $scripts#-->
|
||||
<select name="script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<!--#if not ($sc == 'Default' and $slot.name == '*')#-->
|
||||
<option value="$sc" <!--#if $slot.script.lower()==$sc.lower() then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<!--#else#-->
|
||||
<select name="script" disabled>
|
||||
|
||||
</select>
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
<!--#end if#-->
|
||||
<td class="nowrap">
|
||||
<input type="text" name="dir" class="fileBrowserSmall" value="$slot.dir" size="20" data-initialdir="$defdir" data-title="$T('catFolderPath')" title="$T('explain-catTags2')" />
|
||||
</td>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Folders"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/folders"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/folders"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="General"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/general"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/general"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -23,9 +23,9 @@
|
||||
<input type="number" name="port" id="port" value="$port" size="8" data-original="$port" />
|
||||
<span class="desc">$T('explain-port')</span>
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<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 ""#--> <!--#if int($have_ssl) == 0 then "disabled" 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">
|
||||
@@ -39,21 +39,7 @@
|
||||
<!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('explain-web_dir') <a href="$caller_url1">$caller_url1</a></span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="web_dir2">$T('opt-web_dir2')</label>
|
||||
<select name="web_dir2" id="web_dir2">
|
||||
<option value="None" selected="selected">$T("None")</option>
|
||||
<!--#for $webline in $web_list#-->
|
||||
<!--#if $webline.lower() == $web_dir2.lower()#-->
|
||||
<option value="$webline" selected="selected">$webline</option>
|
||||
<!--#else#-->
|
||||
<option value="$webline">$webline</option>
|
||||
<!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('explain-web_dir2') <a href="$caller_url2">$caller_url2</a></span>
|
||||
<span class="desc">$T('explain-web_dir') <a href="$caller_url">$caller_url</a></span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="language">$T('opt-language')</label>
|
||||
@@ -254,7 +240,17 @@
|
||||
\$('.alert-translate').show()
|
||||
}
|
||||
}
|
||||
\$('#language').on('change', hideOrShowTranslate)
|
||||
\$('#language').on('change', function() {
|
||||
// Show message
|
||||
hideOrShowTranslate()
|
||||
// Re-load page on submit
|
||||
\$('.fullform').submit(function() {
|
||||
// Skip the fancy stuff, just submit
|
||||
this.submit()
|
||||
})
|
||||
// No JSON reponse
|
||||
\$('#ajax').val('')
|
||||
})
|
||||
hideOrShowTranslate()
|
||||
|
||||
\$('#apikey, #nzbkey').click(function () { \$(this).select() });
|
||||
@@ -328,14 +324,20 @@
|
||||
if(bandwidthLimit) {
|
||||
var bandwithLimitNumber = parseFloat(bandwidthLimit)
|
||||
var bandwithLimitText = bandwidthLimit.replace(/[^a-zA-Z]+/g, '');
|
||||
\$('#bandwidth_max_value').val(bandwithLimitNumber)
|
||||
\$('#bandwidth_max_dropdown').val(bandwithLimitText)
|
||||
if(bandwithLimitNumber) {
|
||||
\$('#bandwidth_max_value').val(bandwithLimitNumber)
|
||||
\$('#bandwidth_max_dropdown').val(bandwithLimitText)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update the value
|
||||
\$('#bandwidth_max_value, #bandwidth_max_dropdown').on('change', function() {
|
||||
\$('#bandwidth_max').val(\$('#bandwidth_max_value').val() + \$('#bandwidth_max_dropdown').val())
|
||||
if(\$('#bandwidth_max_value').val()) {
|
||||
\$('#bandwidth_max').val(\$('#bandwidth_max_value').val() + \$('#bandwidth_max_dropdown').val())
|
||||
} else {
|
||||
\$('#bandwidth_max').val('')
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Email"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/notifications"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/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 $script_list#-->
|
||||
<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/1.2/rss"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/rss"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<!--#if not $active_feed#-->
|
||||
@@ -163,7 +163,7 @@
|
||||
<!--#if $rss[$feed]['pick_cat']#-->
|
||||
<td>
|
||||
<select name="cat">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct==$rss[$feed]['cat'] then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -191,7 +191,7 @@
|
||||
<!--#if $rss[$feed]['pick_script']#-->
|
||||
<td>
|
||||
<select name="script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $sc==$rss[$feed]['script'] then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -236,7 +236,7 @@
|
||||
<!--#if $rss[$feed]['pick_cat']#-->
|
||||
<td>
|
||||
<select name="cat">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct=='Default' then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -264,7 +264,7 @@
|
||||
<!--#if $rss[$feed]['pick_script']#-->
|
||||
<td>
|
||||
<select name="script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $sc=='Default' then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -312,7 +312,7 @@
|
||||
<!--#if $rss[$feed]['pick_cat']#-->
|
||||
<td>
|
||||
<select name="cat">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct==$filter[0] then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -340,7 +340,7 @@
|
||||
<!--#if $rss[$feed]['pick_script']#-->
|
||||
<td>
|
||||
<select name="script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $sc==$filter[2] then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/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">
|
||||
@@ -80,21 +87,20 @@ else:
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
<!--#if $schedlines#-->
|
||||
<!--#set $schednum = 0#-->
|
||||
<!--#set $odd = True#-->
|
||||
<!--#for $line in $schedlines#-->
|
||||
<!--#for $schednum, $line in enumerate($schedlines)#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<form action="delSchedule" method="post">
|
||||
<input type="hidden" name="session" value="$session"/>
|
||||
<input type="hidden" name="line" id="line" value="$line"/>
|
||||
<div class="field-pair infoTableSeperator <!--#if $odd then "" else " alt"#-->">
|
||||
<input type="checkbox" name="schedenabled" value="$line" <!--#if int($taskinfo[$schednum][5]) > 0 then 'checked="checked"' else ""#-->>
|
||||
<button class="btn btn-default float-left"><span class="glyphicon glyphicon-trash"></span></button>
|
||||
<div class="scheduleEntry">
|
||||
<span class="time">$taskinfo[$schednum][1]:$taskinfo[$schednum][2]</span><span class="frequency">$taskinfo[$schednum][3]</span> <span class="darkred">$taskinfo[$schednum][4]</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!--#set $schednum = $schednum+1#-->
|
||||
<!--#end for#-->
|
||||
<!--#else#-->
|
||||
<div class="field-pair">
|
||||
@@ -126,5 +132,18 @@ else:
|
||||
\$('#hidden_arguments').show()
|
||||
}*/
|
||||
})
|
||||
|
||||
\$('[name="schedenabled"]').click(function() {
|
||||
\$.ajax({
|
||||
type: "POST",
|
||||
url: "toggleSchedule",
|
||||
data: {line: \$(this).val(), session: "$session" }
|
||||
}).done(function() {
|
||||
// Let us leave!
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!--#include $webdir + "/_inc_footer_uc.tmpl"#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Servers"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/servers"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/servers"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -29,9 +29,9 @@
|
||||
<label class="config" for="port">$T('srv-port')</label>
|
||||
<input type="number" name="port" id="port" size="8" value="119" />
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl">$T('srv-ssl')</label>
|
||||
<input type="checkbox" name="ssl" id="ssl" value="1" <!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
|
||||
<input type="checkbox" name="ssl" id="ssl" value="1" />
|
||||
<span class="desc">$T('explain-ssl')</span>
|
||||
</div>
|
||||
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
|
||||
@@ -62,9 +62,9 @@
|
||||
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#--> advanced-settings">
|
||||
<label class="config" for="ssl_verify">$T('opt-ssl_verify')</label>
|
||||
<select name="ssl_verify" id="ssl_verify" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
|
||||
<option value="0">$T('ssl_verify-disabled')</option>
|
||||
<option value="1" selected>$T('ssl_verify-normal')</option>
|
||||
<option value="2">$T('ssl_verify-strict')</option>
|
||||
<option value="2" selected>$T('ssl_verify-strict')</option>
|
||||
<option value="1">$T('ssl_verify-normal')</option>
|
||||
<option value="0">$T('ssl_verify-disabled')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
@@ -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 #-->
|
||||
@@ -149,9 +191,9 @@
|
||||
<label class="config" for="port$cur">$T('srv-port')</label>
|
||||
<input type="number" name="port" id="port$cur" value="$server['port']" size="8" />
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl$cur">$T('srv-ssl')</label>
|
||||
<input type="checkbox" name="ssl" id="ssl$cur" value="1" <!--#if int($server['ssl']) != 0 and int($have_ssl) == 1 then 'checked="checked"' else ""#--> <!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
|
||||
<input type="checkbox" name="ssl" id="ssl$cur" value="1" <!--#if int($server['ssl']) != 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-ssl')</span>
|
||||
</div>
|
||||
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
|
||||
@@ -183,9 +225,9 @@
|
||||
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#--> advanced-settings">
|
||||
<label class="config" for="ssl_verify$cur">$T('opt-ssl_verify')</label>
|
||||
<select name="ssl_verify" id="ssl_verify$cur" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
|
||||
<option value="0" <!--#if $server['ssl_verify'] == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
|
||||
<option value="1" <!--#if $server['ssl_verify'] == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
|
||||
<option value="2" <!--#if $server['ssl_verify'] == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
|
||||
<option value="2" <!--#if $server['ssl_verify'] == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
|
||||
<option value="1" <!--#if $server['ssl_verify'] == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
|
||||
<option value="0" <!--#if $server['ssl_verify'] == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
@@ -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,165 +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>')
|
||||
// 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).attr("value") == "$T('showDetails')") {
|
||||
\$(this).attr("value", "$T('hideDetails')");
|
||||
} else {
|
||||
resultBox.addClass('alert-danger')
|
||||
resultBox.prepend('<span class="glyphicon glyphicon-exclamation-sign"></span> ')
|
||||
\$(this).attr("value", "$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/1.2/sorting"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/sorting"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -12,7 +12,7 @@
|
||||
<p>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="tv_cat" multiple="multiple" class="multiple_cats">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct in $tv_categories then 'selected="selected"' else ""#--> >$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -165,7 +165,7 @@
|
||||
<p>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="movie_cat" multiple="multiple" class="multiple_cats">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct in $movie_categories then 'selected="selected"' else ""#--> >$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -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">
|
||||
@@ -303,7 +303,7 @@
|
||||
<p>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="date_cat" multiple="multiple" class="multiple_cats">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct in $date_categories then 'selected="selected"' else ""#--> >$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Special"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/special"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/special"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Switches"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/switches"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/switches"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -20,9 +20,9 @@
|
||||
</select>
|
||||
<span class="desc">$T('explain-load_balancing')</span>
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl_ciphers">$T('opt-ssl_ciphers')</label>
|
||||
<input type="text" name="ssl_ciphers" id="ssl_ciphers" value="$ssl_ciphers"<!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
|
||||
<input type="text" name="ssl_ciphers" id="ssl_ciphers" value="$ssl_ciphers" />
|
||||
<span class="desc">$T('explain-ssl_ciphers') <br>$T('readwiki')
|
||||
<a href="${helpuri}advanced/ssl-ciphers" target="_blank">${helpuri}advanced/ssl-ciphers</a></span>
|
||||
</div>
|
||||
@@ -31,12 +31,6 @@
|
||||
<input type="number" name="max_art_tries" id="max_art_tries" value="$max_art_tries" min="2" max="2000" />
|
||||
<span class="desc">$T('explain-max_art_tries')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="max_art_opt">$T('opt-max_art_opt')</label>
|
||||
<input type="checkbox" name="max_art_opt" id="max_art_opt" value="1" <!--#if int($max_art_opt) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-max_art_opt')</span>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label class="config" for="auto_disconnect">$T('opt-auto_disconnect')</label>
|
||||
<input type="checkbox" name="auto_disconnect" id="auto_disconnect" value="1" <!--#if int($auto_disconnect) > 0 then 'checked="checked"' else ""#--> />
|
||||
@@ -58,7 +52,7 @@
|
||||
<div class="field-pair">
|
||||
<label class="config" for="pre_script">$T('opt-pre_script')</label>
|
||||
<select name="pre_script" id="pre_script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<!--#if $sc.lower() == $pre_script.lower()#-->
|
||||
<option value="$sc" selected="selected">$Tspec($sc)</option>
|
||||
<!--#else#-->
|
||||
@@ -92,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>
|
||||
@@ -102,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">
|
||||
@@ -136,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>
|
||||
@@ -159,18 +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>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="quick_check">$T('opt-quick_check')</label>
|
||||
<input type="checkbox" name="quick_check" id="quick_check" value="1" <!--#if int($quick_check) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-quick_check')</span>
|
||||
</div>
|
||||
<!--#if $have_multicore#-->
|
||||
<div class="field-pair">
|
||||
<label class="config" for="par2_multicore">$T('opt-par2_multicore')</label>
|
||||
<input type="checkbox" name="par2_multicore" id="par2_multicore" value="1" <!--#if int($par2_multicore) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-par2_multicore')</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" />
|
||||
@@ -196,11 +190,6 @@
|
||||
<input type="checkbox" name="flat_unpack" id="flat_unpack" value="1" <!--#if int($flat_unpack) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-flat_unpack')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="overwrite_files">$T('opt-overwrite_files')</label>
|
||||
<input type="checkbox" name="overwrite_files" id="overwrite_files" value="1" <!--#if int($overwrite_files) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-overwrite_files')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="script_can_fail">$T('opt-script_can_fail')</label>
|
||||
<input type="checkbox" name="script_can_fail" id="script_can_fail" value="1" <!--#if int($script_can_fail) > 0 then 'checked="checked"' else ""#--> />
|
||||
@@ -228,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>
|
||||
@@ -249,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>
|
||||
@@ -261,11 +267,6 @@
|
||||
<input type="checkbox" name="replace_dots" id="replace_dots" value="1" <!--#if int($replace_dots) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-replace_dots')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="replace_illegal">$T('opt-replace_illegal')</label>
|
||||
<input type="checkbox" name="replace_illegal" id="replace_illegal" value="1" <!--#if int($replace_illegal) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-replace_illegal')</span>
|
||||
</div>
|
||||
<!--#if not $nt#-->
|
||||
<div class="field-pair">
|
||||
<label class="config" for="sanitize_safe">$T('opt-sanitize_safe')</label>
|
||||
@@ -273,11 +274,6 @@
|
||||
<span class="desc">$T('explain-sanitize_safe')</span>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<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')</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>
|
||||
@@ -331,16 +327,7 @@
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_enable">$T('opt-rating_enable')</label>
|
||||
<input type="checkbox" name="rating_enable" id="rating_enable" value="1" <!--#if int($rating_enable) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-rating_enable')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_feedback">$T('opt-rating_feedback')</label>
|
||||
<input type="checkbox" name="rating_feedback" id="rating_feedback" value="1" <!--#if int($rating_feedback) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-rating_feedback')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_host">$T('opt-rating_host')</label>
|
||||
<input type="text" name="rating_host" id="rating_host" value="$rating_host" />
|
||||
<span class="desc">$T('explain-rating_enable').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_api_key">$T('opt-rating_api_key')</label>
|
||||
@@ -474,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
@@ -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;
|
||||
@@ -865,11 +865,17 @@ input[type="checkbox"] {
|
||||
max-width: 150px !important;
|
||||
}
|
||||
|
||||
.Scheduling input[type="checkbox"] {
|
||||
.Scheduling form[action="addSchedule"] input[type="checkbox"] {
|
||||
margin-top: 0px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
.Scheduling form[action="delSchedule"] input[type="checkbox"] {
|
||||
position: initial;
|
||||
float: left;
|
||||
margin: 9px 10px 0px 5px;
|
||||
}
|
||||
|
||||
.navbar .container {
|
||||
padding-right: 0;
|
||||
}
|
||||
@@ -987,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: 120px;
|
||||
margin-left: -40px;
|
||||
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;
|
||||
}
|
||||
@@ -1012,7 +1067,6 @@ input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alert-no-category,
|
||||
.alert-translate {
|
||||
display: none;
|
||||
margin: 5px 0px 0px;
|
||||
@@ -1112,7 +1166,7 @@ input[type="checkbox"] {
|
||||
|
||||
.navbar-nav {
|
||||
/* For extra wide languages like Polish */
|
||||
margin-right: -50px;
|
||||
margin-right: -150px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1164,6 +1218,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) {
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 982 B |
10
interfaces/Config/templates/staticcfg/js/chartist.min.js
vendored
Normal file
4
interfaces/Config/templates/staticcfg/js/jquery-3.2.1.min.js
vendored
Normal file
@@ -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
|
||||
@@ -263,8 +258,13 @@ function do_restart() {
|
||||
// Keep counter of failures
|
||||
var failureCounter = 0;
|
||||
|
||||
// Now we try untill we can connect
|
||||
// Now we try until we can connect
|
||||
var refreshInterval = setInterval(function() {
|
||||
// We skip the first one
|
||||
if(failureCounter == 0) {
|
||||
failureCounter = failureCounter+1;
|
||||
return
|
||||
}
|
||||
$.ajax({ url: urlTotal,
|
||||
success: function() {
|
||||
// Back to base
|
||||
@@ -273,7 +273,7 @@ function do_restart() {
|
||||
error: function(status, text) {
|
||||
failureCounter = failureCounter+1;
|
||||
// Too many failuers and we give up
|
||||
if(failureCounter >= 7) {
|
||||
if(failureCounter >= 6) {
|
||||
// If the port has changed 'Access-Control-Allow-Origin' header will not allow
|
||||
// us to check if the server is back up. So after 7 failures we redirect
|
||||
// anyway in the hopes it works anyway..
|
||||
@@ -281,7 +281,7 @@ function do_restart() {
|
||||
}
|
||||
}
|
||||
})
|
||||
}, 3000)
|
||||
}, 4000)
|
||||
|
||||
// Exception if we go from HTTPS to HTTP
|
||||
// (this is not allowed by browsers and all of the above will be ignored)
|
||||
@@ -404,6 +404,9 @@ $(document).ready(function () {
|
||||
$('#enable_https').on('change', function() {
|
||||
$('.enable_https_options').toggle()
|
||||
})
|
||||
if(!$('#enable_https').is(':checked')) {
|
||||
$('.enable_https_options').hide()
|
||||
}
|
||||
|
||||
$('.advancedButton').click(function(event){
|
||||
$('.advanced-settings').toggle()
|
||||
|
||||
@@ -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#-->
|
||||
@@ -101,7 +102,7 @@
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header"><span class="glyphicon glyphicon-off"></span> $T('Glitter-onFinish'):</li>
|
||||
<li>
|
||||
<select data-bind="value: onQueueFinish" class="form-control">
|
||||
<select data-bind="value: onQueueFinish, event: { change: setOnQueueFinish }" class="form-control">
|
||||
<option value=""></option>
|
||||
<optgroup label="$T('eoq-actions')">
|
||||
<option value="shutdown_program">$T('shutdownSab')</option>
|
||||
@@ -111,9 +112,11 @@
|
||||
<option value="hibernate_pc">$T('hibernatePc')</option>
|
||||
<!--#end if#-->
|
||||
</optgroup>
|
||||
<optgroup label="$T('eoq-scripts')">
|
||||
<optgroup label="$T('eoq-scripts')" data-bind="visible: queue.scriptsList().length > 1">
|
||||
<!-- ko foreach: queue.scriptsList -->
|
||||
<option data-bind="text: \$data, attr: { value: 'script_'+\$data } " ></option>
|
||||
<!-- ko if: \$data != glitterTranslate.noneText -->
|
||||
<option data-bind="text: \$data, attr: { value: 'script_'+\$data } " ></option>
|
||||
<!-- /ko -->
|
||||
<!-- /ko -->
|
||||
</optgroup>
|
||||
</select>
|
||||
|
||||
@@ -60,13 +60,13 @@
|
||||
<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/" 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-home"></span> <a href="https://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>
|
||||
@@ -112,37 +112,36 @@
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('cache')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasStatusInfo">
|
||||
<span data-bind="text: statusInfo.cache_size"></span> (<span data-bind="text: statusInfo.cache_art"></span> $T('Glitter-articles'))
|
||||
<div class="col-sm-6">
|
||||
<span data-bind="text: cacheSize"></span> (<span data-bind="text: cacheArticles"></span> $T('Glitter-articles'))
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('dashboard-systemPerformance')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasStatusInfo">
|
||||
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.pystone"></span>
|
||||
<a href="#" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<a href="#" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small data-bind="truncatedText: statusInfo.cpumodel, length: 25, attr: { 'data-original-title': statusInfo.cpumodel }" data-tooltip="true"></small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('dashboard-downloadDirSpeed')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasDiskStatusInfo">
|
||||
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.downloaddirspeed()"></span> MB/s
|
||||
<a href="#" class="diskspeed-button" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small>(<span data-bind="truncatedText: statusInfo.downloaddir, length: 24"></span>)</small>
|
||||
<small>(<span data-bind="truncatedText: statusInfo.downloaddir, length: 24, attr: { 'data-original-title': statusInfo.downloaddir }" data-tooltip="true"></span>)</small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasDiskStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('dashboard-completeDirSpeed')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasDiskStatusInfo">
|
||||
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.completedirspeed()"></span> MB/s
|
||||
<a href="#" class="diskspeed-button" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small>(<span data-bind="truncatedText: statusInfo.completedir, length: 24"></span>)</small>
|
||||
<small>(<span data-bind="truncatedText: statusInfo.completedir, length: 24, attr: { 'data-original-title': statusInfo.completedir }" data-tooltip="true"></span>)</small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasDiskStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="row options-function-box">
|
||||
@@ -185,7 +184,7 @@
|
||||
<div class="col-sm-6">$T('swtag-server')</div>
|
||||
<div class="col-sm-6">
|
||||
<span data-bind="text: servername"></span>
|
||||
<span class="label label-default" data-bind="visible: serveroptional">$T('optional').capitalize()</span>
|
||||
<span class="label label-warning" data-bind="visible: serveroptional">$T('optional').capitalize()</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -208,7 +207,7 @@
|
||||
<div class="row" data-bind="visible: servererror()">
|
||||
<div class="col-sm-12">
|
||||
<div class="alert alert-danger">
|
||||
<a href="#" data-bind="visible: !serveractive(), click: function() { \$parent.unblockServer(servername()) }" class="btn btn-default"><span class="glyphicon glyphicon-share-alt"></span> $T('Glitter-unblockServer')</a>
|
||||
<a href="#" data-bind="visible: !serveractive(), click: function() { \$parent.unblockServer(servername) }" class="btn btn-default"><span class="glyphicon glyphicon-share-alt"></span> $T('Glitter-unblockServer')</a>
|
||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||
<span data-bind="text: servererror()"></span>
|
||||
</div>
|
||||
@@ -526,10 +525,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>
|
||||
@@ -622,7 +625,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>
|
||||
@@ -630,7 +633,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>
|
||||
@@ -152,7 +156,7 @@
|
||||
</li>
|
||||
<li title="$T('eoq-scripts')" data-tooltip="true" data-placement="left">
|
||||
<span class="glyphicon glyphicon-flash"></span>
|
||||
<select name="Post-processing" class="form-control" data-bind="options: parent.scriptsList, value: script, event: { change: changeScript }, enable: (parent.scriptsList().length > 0)"></select>
|
||||
<select name="Post-processing" class="form-control" data-bind="options: parent.scriptsList, value: script, event: { change: changeScript }, enable: (parent.scriptsList().length > 1)"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- /ko -->
|
||||
|
||||
@@ -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 -->
|
||||
@@ -58,10 +58,11 @@
|
||||
glitterTranslate.pauseFor = "$T('pauseFor')"
|
||||
glitterTranslate.minutes = "$T('mins')"
|
||||
glitterTranslate.shutdown = "$T('shutdownOK?')";
|
||||
glitterTranslate.restart = "$T('explain-Restart')".replace(/<br \/>/g, "\n");
|
||||
glitterTranslate.repair = "$T('explain-Repair')".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.encrypted = "$T('Glitter-encrypted')";
|
||||
glitterTranslate.duplicate = "$T('Glitter-duplicate')";
|
||||
glitterTranslate.tooLarge = "$T('Glitter-tooLarge')";
|
||||
@@ -102,7 +103,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')";
|
||||
|
||||
@@ -125,7 +126,7 @@
|
||||
<!--#include raw $webdir + "/static/javascripts/jquery.min.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/jquery-ui.min.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/jquery.peity.min.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/moment.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/moment.min.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/knockout-latest.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/knockout-extensions.js"#-->
|
||||
<!--#include raw $webdir + "/static/bootstrap/js/bootstrap.min.js"#-->
|
||||
|
||||
@@ -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
|
||||
@@ -55,15 +79,10 @@ function Fileslisting(parent) {
|
||||
$.each(response.files, function(index, slot) {
|
||||
// Existing or updating?
|
||||
var existingItem = ko.utils.arrayFirst(self.fileItems(), function(i) {
|
||||
return i.filename() == slot.filename;
|
||||
return i.nzf_id() == slot.nzf_id;
|
||||
});
|
||||
|
||||
if(existingItem) {
|
||||
// We skip queued files!
|
||||
// They cause problems because they can have the same filename
|
||||
// as files that we do want to be updated.. The slot.id is not unique!
|
||||
if(slot.status == "queued") return false;
|
||||
|
||||
// Update the rest
|
||||
existingItem.updateFromData(slot);
|
||||
} else {
|
||||
@@ -202,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.nzf_id !== undefined);
|
||||
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) {
|
||||
@@ -212,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.nzf_id !== undefined)
|
||||
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
|
||||
@@ -297,11 +309,11 @@ function HistoryModel(parent, data) {
|
||||
self.processingDownload = ko.pureComputed(function() {
|
||||
var status = self.status();
|
||||
// When we can cancel
|
||||
if (status === 'Extracting' || status === 'Verifying' || status == 'Repairing') {
|
||||
if (status === 'Extracting' || status === 'Verifying' || status == 'Repairing' || status === 'Running') {
|
||||
return 2
|
||||
}
|
||||
// These cannot be cancelled
|
||||
if(status === 'Moving' || status === 'Running') {
|
||||
if(status === 'Moving') {
|
||||
return 1
|
||||
}
|
||||
return false;
|
||||
@@ -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':
|
||||
@@ -356,10 +366,17 @@ function HistoryModel(parent, data) {
|
||||
return displayDateTime(self.completed(), parent.parent.dateFormat(), 'X')
|
||||
});
|
||||
|
||||
// Subscribe to retryEvent so we can load the password
|
||||
self.canRetry.subscribe(function() {
|
||||
self.updateAllHistory = true;
|
||||
})
|
||||
|
||||
// Re-try button
|
||||
self.retry = function() {
|
||||
// Set JOB-id
|
||||
$('#modal-retry-job input[name="retry_job_id"]').val(self.nzo_id)
|
||||
// Set password
|
||||
$('#retry_job_password').val(self.historyStatus.password())
|
||||
// Open modal
|
||||
$('#modal-retry-job').modal("show")
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ function ViewModel() {
|
||||
self.isRestarting = ko.observable(false);
|
||||
self.useGlobalOptions = ko.observable(true).extend({ persist: 'useGlobalOptions' });
|
||||
self.refreshRate = ko.observable(1).extend({ persist: 'pageRefreshRate' });
|
||||
self.dateFormat = ko.observable('DD/MM/YYYY HH:mm').extend({ persist: 'pageDateFormat' });
|
||||
self.dateFormat = ko.observable('fromNow').extend({ persist: 'pageDateFormat' });
|
||||
self.displayTabbed = ko.observable().extend({ persist: 'displayTabbed' });
|
||||
self.displayCompact = ko.observable(false).extend({ persist: 'displayCompact' });
|
||||
self.confirmDeleteQueue = ko.observable(true).extend({ persist: 'confirmDeleteQueue' });
|
||||
@@ -40,6 +40,8 @@ function ViewModel() {
|
||||
self.quotaLimit = ko.observable();
|
||||
self.quotaLimitLeft = ko.observable();
|
||||
self.systemLoad = ko.observable();
|
||||
self.cacheSize = ko.observable();
|
||||
self.cacheArticles = ko.observable();
|
||||
self.nrWarnings = ko.observable(0);
|
||||
self.allWarnings = ko.observableArray([]);
|
||||
self.allMessages = ko.observableArray([]);
|
||||
@@ -48,7 +50,7 @@ function ViewModel() {
|
||||
|
||||
// Statusinfo container
|
||||
self.hasStatusInfo = ko.observable(false);
|
||||
self.hasDiskStatusInfo = ko.observable(false);
|
||||
self.hasPerformanceInfo = ko.observable(false);
|
||||
self.statusInfo = {};
|
||||
self.statusInfo.folders = ko.observableArray([]);
|
||||
self.statusInfo.servers = ko.observableArray([]);
|
||||
@@ -59,8 +61,6 @@ function ViewModel() {
|
||||
self.statusInfo.pystone = ko.observable();
|
||||
self.statusInfo.cpumodel = ko.observable();
|
||||
self.statusInfo.loglevel = ko.observable();
|
||||
self.statusInfo.cache_size = ko.observable();
|
||||
self.statusInfo.cache_art = ko.observable();
|
||||
self.statusInfo.downloaddir = ko.observable();
|
||||
self.statusInfo.downloaddirspeed = ko.observable();
|
||||
self.statusInfo.completedir = ko.observable();
|
||||
@@ -173,8 +173,8 @@ function ViewModel() {
|
||||
}
|
||||
|
||||
// Did we exceed the space?
|
||||
self.diskSpaceExceeded1(parseInt(response.queue.mbleft)/1024 > parseInt(response.queue.diskspace1))
|
||||
self.diskSpaceExceeded2(parseInt(response.queue.mbleft)/1024 > parseInt(response.queue.diskspace2))
|
||||
self.diskSpaceExceeded1(parseInt(response.queue.mbleft)/1024 > parseFloat(response.queue.diskspace1))
|
||||
self.diskSpaceExceeded2(parseInt(response.queue.mbleft)/1024 > parseFloat(response.queue.diskspace2))
|
||||
|
||||
// Quota
|
||||
self.quotaLimit(response.queue.quota)
|
||||
@@ -183,6 +183,10 @@ function ViewModel() {
|
||||
// System load
|
||||
self.systemLoad(response.queue.loadavg)
|
||||
|
||||
// Cache
|
||||
self.cacheSize(response.queue.cache_size)
|
||||
self.cacheArticles(response.queue.cache_art)
|
||||
|
||||
// Warnings (new warnings will trigger an update of allMessages)
|
||||
self.nrWarnings(response.queue.have_warnings)
|
||||
|
||||
@@ -327,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]);
|
||||
|
||||
@@ -467,6 +478,12 @@ function ViewModel() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fix DateJS bug it has some strange problem with the current day-of-month + 1
|
||||
// Removing the space makes DateJS work properly
|
||||
newValue = newValue.replace(/\s*h|\s*m|\s*d/g, function(match) {
|
||||
return match.trim()
|
||||
});
|
||||
|
||||
// Parse
|
||||
var pauseParsed = Date.parse(newValue);
|
||||
|
||||
@@ -528,7 +545,7 @@ function ViewModel() {
|
||||
// Go over all warnings and add
|
||||
$.each(response.warnings, function(index, warning) {
|
||||
// Split warning into parts
|
||||
var warningSplit = warning.split(/\n/);
|
||||
var warningSplit = convertHTMLtoText(warning).split(/\n/);
|
||||
|
||||
// Reformat CSS label and date
|
||||
var warningData = {
|
||||
@@ -589,7 +606,7 @@ function ViewModel() {
|
||||
}
|
||||
|
||||
// Shutdown options
|
||||
self.onQueueFinish.subscribe(function(newValue) {
|
||||
self.setOnQueueFinish = function(model, event) {
|
||||
// Ignore updates before the page is done
|
||||
if(!self.hasStatusInfo()) return;
|
||||
|
||||
@@ -597,9 +614,12 @@ function ViewModel() {
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: 'change_complete_action',
|
||||
value: newValue
|
||||
value: $(event.target).val()
|
||||
})
|
||||
})
|
||||
|
||||
// Top stop blinking while the API is calling
|
||||
self.onQueueFinish($(event.target).val())
|
||||
}
|
||||
|
||||
// Use global settings or device-specific?
|
||||
self.useGlobalOptions.subscribe(function(newValue) {
|
||||
@@ -749,8 +769,6 @@ function ViewModel() {
|
||||
callAPI({ mode: 'fullstatus', skip_dashboard: (!statusFullRefresh)*1 }).then(function(data) {
|
||||
// Update basic
|
||||
self.statusInfo.loglevel(data.status.loglevel)
|
||||
self.statusInfo.cache_art(data.status.cache_art)
|
||||
self.statusInfo.cache_size(data.status.cache_size)
|
||||
self.statusInfo.folders(data.status.folders)
|
||||
|
||||
// Update the full set
|
||||
@@ -766,7 +784,7 @@ function ViewModel() {
|
||||
self.statusInfo.publicipv4(data.status.publicipv4)
|
||||
self.statusInfo.ipv6(data.status.ipv6 || glitterTranslate.noneText)
|
||||
// Loaded disk info
|
||||
self.hasDiskStatusInfo(true)
|
||||
self.hasPerformanceInfo(true)
|
||||
}
|
||||
|
||||
// Update the servers
|
||||
@@ -816,7 +834,7 @@ function ViewModel() {
|
||||
|
||||
// Do a disk-speedtest
|
||||
self.testDiskSpeed = function(item, event) {
|
||||
self.hasDiskStatusInfo(false)
|
||||
self.hasPerformanceInfo(false)
|
||||
|
||||
// Run it and then display it
|
||||
callSpecialAPI('./status/dashrefresh/').then(function() {
|
||||
|
||||
@@ -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
|
||||
@@ -171,7 +189,7 @@ function QueueListModel(parent) {
|
||||
|
||||
// Do we show search box. So it doesn't dissapear when nothing is found
|
||||
self.hasQueueSearch = ko.pureComputed(function() {
|
||||
return (self.pagination.hasPagination() || self.searchTerm())
|
||||
return (self.pagination.hasPagination() || self.searchTerm() || (self.parent.hasQueue() && self.isMultiEditing()))
|
||||
})
|
||||
|
||||
// Searching in queue (rate-limited in decleration)
|
||||
@@ -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;
|
||||
})
|
||||
@@ -529,6 +541,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 +585,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
|
||||
@@ -652,7 +672,7 @@ function QueueModel(parent, data) {
|
||||
}
|
||||
self.changeScript = function(item) {
|
||||
// Not on empty handlers
|
||||
if(!item.script()) return;
|
||||
if(!item.script() || parent.scriptsList().length <= 1) return;
|
||||
callAPI({
|
||||
mode: 'change_script',
|
||||
value: item.id,
|
||||
|
||||
@@ -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)})()})})();})();
|
||||
|
||||
7
interfaces/Glitter/templates/static/javascripts/moment.min.js
vendored
Normal file
@@ -1,59 +1,60 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : danish (da)
|
||||
//! locale : Danish [da]
|
||||
//! author : Ulrik Nielsen : https://github.com/mrbase
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var da = moment.defineLocale('da', {
|
||||
months : 'januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december'.split('_'),
|
||||
monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'),
|
||||
weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'),
|
||||
weekdaysShort : 'søn_man_tir_ons_tor_fre_lør'.split('_'),
|
||||
weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD/MM/YYYY',
|
||||
LL : 'D. MMMM YYYY',
|
||||
LLL : 'D. MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd [d.] D. MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay : '[I dag kl.] LT',
|
||||
nextDay : '[I morgen kl.] LT',
|
||||
nextWeek : 'dddd [kl.] LT',
|
||||
lastDay : '[I går kl.] LT',
|
||||
lastWeek : '[sidste] dddd [kl] LT',
|
||||
sameElse : 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'om %s',
|
||||
past : '%s siden',
|
||||
s : 'få sekunder',
|
||||
m : 'et minut',
|
||||
mm : '%d minutter',
|
||||
h : 'en time',
|
||||
hh : '%d timer',
|
||||
d : 'en dag',
|
||||
dd : '%d dage',
|
||||
M : 'en måned',
|
||||
MM : '%d måneder',
|
||||
y : 'et år',
|
||||
yy : '%d år'
|
||||
},
|
||||
ordinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
var da = moment.defineLocale('da', {
|
||||
months : 'januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december'.split('_'),
|
||||
monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'),
|
||||
weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'),
|
||||
weekdaysShort : 'søn_man_tir_ons_tor_fre_lør'.split('_'),
|
||||
weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD/MM/YYYY',
|
||||
LL : 'D. MMMM YYYY',
|
||||
LLL : 'D. MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd [d.] D. MMMM YYYY [kl.] HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay : '[i dag kl.] LT',
|
||||
nextDay : '[i morgen kl.] LT',
|
||||
nextWeek : 'på dddd [kl.] LT',
|
||||
lastDay : '[i går kl.] LT',
|
||||
lastWeek : '[i] dddd[s kl.] LT',
|
||||
sameElse : 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'om %s',
|
||||
past : '%s siden',
|
||||
s : 'få sekunder',
|
||||
m : 'et minut',
|
||||
mm : '%d minutter',
|
||||
h : 'en time',
|
||||
hh : '%d timer',
|
||||
d : 'en dag',
|
||||
dd : '%d dage',
|
||||
M : 'en måned',
|
||||
MM : '%d måneder',
|
||||
y : 'et år',
|
||||
yy : '%d år'
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return da;
|
||||
return da;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,74 +1,78 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : german (de)
|
||||
//! locale : German [de]
|
||||
//! author : lluchs : https://github.com/lluchs
|
||||
//! author: Menelion Elensúle: https://github.com/Oire
|
||||
//! author : Mikolaj Dadela : https://github.com/mik01aj
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
function processRelativeTime(number, withoutSuffix, key, isFuture) {
|
||||
var format = {
|
||||
'm': ['eine Minute', 'einer Minute'],
|
||||
'h': ['eine Stunde', 'einer Stunde'],
|
||||
'd': ['ein Tag', 'einem Tag'],
|
||||
'dd': [number + ' Tage', number + ' Tagen'],
|
||||
'M': ['ein Monat', 'einem Monat'],
|
||||
'MM': [number + ' Monate', number + ' Monaten'],
|
||||
'y': ['ein Jahr', 'einem Jahr'],
|
||||
'yy': [number + ' Jahre', number + ' Jahren']
|
||||
};
|
||||
return withoutSuffix ? format[key][0] : format[key][1];
|
||||
function processRelativeTime(number, withoutSuffix, key, isFuture) {
|
||||
var format = {
|
||||
'm': ['eine Minute', 'einer Minute'],
|
||||
'h': ['eine Stunde', 'einer Stunde'],
|
||||
'd': ['ein Tag', 'einem Tag'],
|
||||
'dd': [number + ' Tage', number + ' Tagen'],
|
||||
'M': ['ein Monat', 'einem Monat'],
|
||||
'MM': [number + ' Monate', number + ' Monaten'],
|
||||
'y': ['ein Jahr', 'einem Jahr'],
|
||||
'yy': [number + ' Jahre', number + ' Jahren']
|
||||
};
|
||||
return withoutSuffix ? format[key][0] : format[key][1];
|
||||
}
|
||||
|
||||
var de = moment.defineLocale('de', {
|
||||
months : 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'),
|
||||
monthsShort : 'Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'),
|
||||
monthsParseExact : true,
|
||||
weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'),
|
||||
weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'),
|
||||
weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'),
|
||||
weekdaysParseExact : true,
|
||||
longDateFormat : {
|
||||
LT: 'HH:mm',
|
||||
LTS: 'HH:mm:ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'D. MMMM YYYY',
|
||||
LLL : 'D. MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd, D. MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[heute um] LT [Uhr]',
|
||||
sameElse: 'L',
|
||||
nextDay: '[morgen um] LT [Uhr]',
|
||||
nextWeek: 'dddd [um] LT [Uhr]',
|
||||
lastDay: '[gestern um] LT [Uhr]',
|
||||
lastWeek: '[letzten] dddd [um] LT [Uhr]'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'in %s',
|
||||
past : 'vor %s',
|
||||
s : 'ein paar Sekunden',
|
||||
m : processRelativeTime,
|
||||
mm : '%d Minuten',
|
||||
h : processRelativeTime,
|
||||
hh : '%d Stunden',
|
||||
d : processRelativeTime,
|
||||
dd : processRelativeTime,
|
||||
M : processRelativeTime,
|
||||
MM : processRelativeTime,
|
||||
y : processRelativeTime,
|
||||
yy : processRelativeTime
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
var de = moment.defineLocale('de', {
|
||||
months : 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'),
|
||||
monthsShort : 'Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'),
|
||||
weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'),
|
||||
weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'),
|
||||
weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'),
|
||||
longDateFormat : {
|
||||
LT: 'HH:mm',
|
||||
LTS: 'HH:mm:ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'D. MMMM YYYY',
|
||||
LLL : 'D. MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd, D. MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[Heute um] LT [Uhr]',
|
||||
sameElse: 'L',
|
||||
nextDay: '[Morgen um] LT [Uhr]',
|
||||
nextWeek: 'dddd [um] LT [Uhr]',
|
||||
lastDay: '[Gestern um] LT [Uhr]',
|
||||
lastWeek: '[letzten] dddd [um] LT [Uhr]'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'in %s',
|
||||
past : 'vor %s',
|
||||
s : 'ein paar Sekunden',
|
||||
m : processRelativeTime,
|
||||
mm : '%d Minuten',
|
||||
h : processRelativeTime,
|
||||
hh : '%d Stunden',
|
||||
d : processRelativeTime,
|
||||
dd : processRelativeTime,
|
||||
M : processRelativeTime,
|
||||
MM : processRelativeTime,
|
||||
y : processRelativeTime,
|
||||
yy : processRelativeTime
|
||||
},
|
||||
ordinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
return de;
|
||||
|
||||
return de;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,78 +1,83 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : spanish (es)
|
||||
//! locale : Spanish [es]
|
||||
//! author : Julio Napurí : https://github.com/julionc
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var monthsShortDot = 'Ene._Feb._Mar._Abr._May._Jun._Jul._Ago._Sep._Oct._Nov._Dic.'.split('_'),
|
||||
monthsShort = 'Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Sep_Oct_Nov_Dic'.split('_');
|
||||
var monthsShortDot = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_');
|
||||
var monthsShort = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_');
|
||||
|
||||
var es = moment.defineLocale('es', {
|
||||
months : 'Enero_Febrero_Marzo_Abril_Mayo_Junio_Julio_Agosto_Septiembre_Octubre_Noviembre_Diciembre'.split('_'),
|
||||
monthsShort : function (m, format) {
|
||||
if (/-MMM-/.test(format)) {
|
||||
return monthsShort[m.month()];
|
||||
} else {
|
||||
return monthsShortDot[m.month()];
|
||||
}
|
||||
},
|
||||
weekdays : 'Domingo_Lunes_Martes_Miércoles_Jueves_Viernes_Sábado'.split('_'),
|
||||
weekdaysShort : 'Dom._Lun._Mar._Mié._Jue._Vie._Sáb.'.split('_'),
|
||||
weekdaysMin : 'Do_Lu_Ma_Mi_Ju_Vi_Sá'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'H:mm',
|
||||
LTS : 'H:mm:ss',
|
||||
L : 'DD/MM/YYYY',
|
||||
LL : 'D [de] MMMM [de] YYYY',
|
||||
LLL : 'D [de] MMMM [de] YYYY H:mm',
|
||||
LLLL : 'dddd, D [de] MMMM [de] YYYY H:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay : function () {
|
||||
return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
|
||||
},
|
||||
nextDay : function () {
|
||||
return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
|
||||
},
|
||||
nextWeek : function () {
|
||||
return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
|
||||
},
|
||||
lastDay : function () {
|
||||
return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
|
||||
},
|
||||
lastWeek : function () {
|
||||
return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
|
||||
},
|
||||
sameElse : 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'en %s',
|
||||
past : 'hace %s',
|
||||
s : 'unos segundos',
|
||||
m : 'un minuto',
|
||||
mm : '%d minutos',
|
||||
h : 'una hora',
|
||||
hh : '%d horas',
|
||||
d : 'un día',
|
||||
dd : '%d días',
|
||||
M : 'un mes',
|
||||
MM : '%d meses',
|
||||
y : 'un año',
|
||||
yy : '%d años'
|
||||
},
|
||||
ordinalParse : /\d{1,2}º/,
|
||||
ordinal : '%dº',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
var es = moment.defineLocale('es', {
|
||||
months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'),
|
||||
monthsShort : function (m, format) {
|
||||
if (!m) {
|
||||
return monthsShortDot;
|
||||
} else if (/-MMM-/.test(format)) {
|
||||
return monthsShort[m.month()];
|
||||
} else {
|
||||
return monthsShortDot[m.month()];
|
||||
}
|
||||
});
|
||||
},
|
||||
monthsParseExact : true,
|
||||
weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'),
|
||||
weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'),
|
||||
weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'),
|
||||
weekdaysParseExact : true,
|
||||
longDateFormat : {
|
||||
LT : 'H:mm',
|
||||
LTS : 'H:mm:ss',
|
||||
L : 'DD/MM/YYYY',
|
||||
LL : 'D [de] MMMM [de] YYYY',
|
||||
LLL : 'D [de] MMMM [de] YYYY H:mm',
|
||||
LLLL : 'dddd, D [de] MMMM [de] YYYY H:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay : function () {
|
||||
return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
|
||||
},
|
||||
nextDay : function () {
|
||||
return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
|
||||
},
|
||||
nextWeek : function () {
|
||||
return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
|
||||
},
|
||||
lastDay : function () {
|
||||
return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
|
||||
},
|
||||
lastWeek : function () {
|
||||
return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
|
||||
},
|
||||
sameElse : 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'en %s',
|
||||
past : 'hace %s',
|
||||
s : 'unos segundos',
|
||||
m : 'un minuto',
|
||||
mm : '%d minutos',
|
||||
h : 'una hora',
|
||||
hh : '%d horas',
|
||||
d : 'un día',
|
||||
dd : '%d días',
|
||||
M : 'un mes',
|
||||
MM : '%d meses',
|
||||
y : 'un año',
|
||||
yy : '%d años'
|
||||
},
|
||||
dayOfMonthOrdinalParse : /\d{1,2}º/,
|
||||
ordinal : '%dº',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return es;
|
||||
return es;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : finnish (fi)
|
||||
//! locale : Finnish [fi]
|
||||
//! author : Tarmo Aidantausta : https://github.com/bleadof
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var numbersPast = 'nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän'.split(' '),
|
||||
numbersFuture = [
|
||||
'nolla', 'yhden', 'kahden', 'kolmen', 'neljän', 'viiden', 'kuuden',
|
||||
numbersPast[7], numbersPast[8], numbersPast[9]
|
||||
];
|
||||
function translate(number, withoutSuffix, key, isFuture) {
|
||||
var result = '';
|
||||
switch (key) {
|
||||
var numbersPast = 'nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän'.split(' ');
|
||||
var numbersFuture = [
|
||||
'nolla', 'yhden', 'kahden', 'kolmen', 'neljän', 'viiden', 'kuuden',
|
||||
numbersPast[7], numbersPast[8], numbersPast[9]
|
||||
];
|
||||
function translate(number, withoutSuffix, key, isFuture) {
|
||||
var result = '';
|
||||
switch (key) {
|
||||
case 's':
|
||||
return isFuture ? 'muutaman sekunnin' : 'muutama sekunti';
|
||||
case 'm':
|
||||
@@ -44,63 +45,63 @@
|
||||
case 'yy':
|
||||
result = isFuture ? 'vuoden' : 'vuotta';
|
||||
break;
|
||||
}
|
||||
result = verbalNumber(number, isFuture) + ' ' + result;
|
||||
return result;
|
||||
}
|
||||
function verbalNumber(number, isFuture) {
|
||||
return number < 10 ? (isFuture ? numbersFuture[number] : numbersPast[number]) : number;
|
||||
result = verbalNumber(number, isFuture) + ' ' + result;
|
||||
return result;
|
||||
}
|
||||
function verbalNumber(number, isFuture) {
|
||||
return number < 10 ? (isFuture ? numbersFuture[number] : numbersPast[number]) : number;
|
||||
}
|
||||
|
||||
var fi = moment.defineLocale('fi', {
|
||||
months : 'tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu'.split('_'),
|
||||
monthsShort : 'tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu'.split('_'),
|
||||
weekdays : 'sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai'.split('_'),
|
||||
weekdaysShort : 'su_ma_ti_ke_to_pe_la'.split('_'),
|
||||
weekdaysMin : 'su_ma_ti_ke_to_pe_la'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH.mm',
|
||||
LTS : 'HH.mm.ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'Do MMMM[ta] YYYY',
|
||||
LLL : 'Do MMMM[ta] YYYY, [klo] HH.mm',
|
||||
LLLL : 'dddd, Do MMMM[ta] YYYY, [klo] HH.mm',
|
||||
l : 'D.M.YYYY',
|
||||
ll : 'Do MMM YYYY',
|
||||
lll : 'Do MMM YYYY, [klo] HH.mm',
|
||||
llll : 'ddd, Do MMM YYYY, [klo] HH.mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay : '[tänään] [klo] LT',
|
||||
nextDay : '[huomenna] [klo] LT',
|
||||
nextWeek : 'dddd [klo] LT',
|
||||
lastDay : '[eilen] [klo] LT',
|
||||
lastWeek : '[viime] dddd[na] [klo] LT',
|
||||
sameElse : 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : '%s päästä',
|
||||
past : '%s sitten',
|
||||
s : translate,
|
||||
m : translate,
|
||||
mm : translate,
|
||||
h : translate,
|
||||
hh : translate,
|
||||
d : translate,
|
||||
dd : translate,
|
||||
M : translate,
|
||||
MM : translate,
|
||||
y : translate,
|
||||
yy : translate
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
var fi = moment.defineLocale('fi', {
|
||||
months : 'tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu'.split('_'),
|
||||
monthsShort : 'tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu'.split('_'),
|
||||
weekdays : 'sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai'.split('_'),
|
||||
weekdaysShort : 'su_ma_ti_ke_to_pe_la'.split('_'),
|
||||
weekdaysMin : 'su_ma_ti_ke_to_pe_la'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH.mm',
|
||||
LTS : 'HH.mm.ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'Do MMMM[ta] YYYY',
|
||||
LLL : 'Do MMMM[ta] YYYY, [klo] HH.mm',
|
||||
LLLL : 'dddd, Do MMMM[ta] YYYY, [klo] HH.mm',
|
||||
l : 'D.M.YYYY',
|
||||
ll : 'Do MMM YYYY',
|
||||
lll : 'Do MMM YYYY, [klo] HH.mm',
|
||||
llll : 'ddd, Do MMM YYYY, [klo] HH.mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay : '[tänään] [klo] LT',
|
||||
nextDay : '[huomenna] [klo] LT',
|
||||
nextWeek : 'dddd [klo] LT',
|
||||
lastDay : '[eilen] [klo] LT',
|
||||
lastWeek : '[viime] dddd[na] [klo] LT',
|
||||
sameElse : 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : '%s päästä',
|
||||
past : '%s sitten',
|
||||
s : translate,
|
||||
m : translate,
|
||||
mm : translate,
|
||||
h : translate,
|
||||
hh : translate,
|
||||
d : translate,
|
||||
dd : translate,
|
||||
M : translate,
|
||||
MM : translate,
|
||||
y : translate,
|
||||
yy : translate
|
||||
},
|
||||
ordinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
return fi;
|
||||
|
||||
return fi;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,61 +1,83 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : french (fr)
|
||||
//! locale : French [fr]
|
||||
//! author : John Fischer : https://github.com/jfroffice
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var fr = moment.defineLocale('fr', {
|
||||
months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
|
||||
monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
|
||||
weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
|
||||
weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
|
||||
weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD/MM/YYYY',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd D MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[Aujourd\'hui à] LT',
|
||||
nextDay: '[Demain à] LT',
|
||||
nextWeek: 'dddd [à] LT',
|
||||
lastDay: '[Hier à] LT',
|
||||
lastWeek: 'dddd [dernier à] LT',
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'dans %s',
|
||||
past : 'il y a %s',
|
||||
s : 'quelques secondes',
|
||||
m : 'une minute',
|
||||
mm : '%d minutes',
|
||||
h : 'une heure',
|
||||
hh : '%d heures',
|
||||
d : 'un jour',
|
||||
dd : '%d jours',
|
||||
M : 'un mois',
|
||||
MM : '%d mois',
|
||||
y : 'un an',
|
||||
yy : '%d ans'
|
||||
},
|
||||
ordinalParse: /\d{1,2}(er|)/,
|
||||
ordinal : function (number) {
|
||||
return number + (number === 1 ? 'er' : '');
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
var fr = moment.defineLocale('fr', {
|
||||
months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
|
||||
monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
|
||||
monthsParseExact : true,
|
||||
weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
|
||||
weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
|
||||
weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),
|
||||
weekdaysParseExact : true,
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD/MM/YYYY',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd D MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay : '[Aujourd’hui à] LT',
|
||||
nextDay : '[Demain à] LT',
|
||||
nextWeek : 'dddd [à] LT',
|
||||
lastDay : '[Hier à] LT',
|
||||
lastWeek : 'dddd [dernier à] LT',
|
||||
sameElse : 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'dans %s',
|
||||
past : 'il y a %s',
|
||||
s : 'quelques secondes',
|
||||
m : 'une minute',
|
||||
mm : '%d minutes',
|
||||
h : 'une heure',
|
||||
hh : '%d heures',
|
||||
d : 'un jour',
|
||||
dd : '%d jours',
|
||||
M : 'un mois',
|
||||
MM : '%d mois',
|
||||
y : 'un an',
|
||||
yy : '%d ans'
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}(er|)/,
|
||||
ordinal : function (number, period) {
|
||||
switch (period) {
|
||||
// TODO: Return 'e' when day of month > 1. Move this case inside
|
||||
// block for masculine words below.
|
||||
// See https://github.com/moment/moment/issues/3375
|
||||
case 'D':
|
||||
return number + (number === 1 ? 'er' : '');
|
||||
|
||||
// Words with masculine grammatical gender: mois, trimestre, jour
|
||||
default:
|
||||
case 'M':
|
||||
case 'Q':
|
||||
case 'DDD':
|
||||
case 'd':
|
||||
return number + (number === 1 ? 'er' : 'e');
|
||||
|
||||
// Words with feminine grammatical gender: semaine
|
||||
case 'w':
|
||||
case 'W':
|
||||
return number + (number === 1 ? 're' : 'e');
|
||||
}
|
||||
});
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return fr;
|
||||
return fr;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,60 +1,63 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : norwegian bokmål (nb)
|
||||
//! locale : Norwegian Bokmål [nb]
|
||||
//! authors : Espen Hovlandsdal : https://github.com/rexxars
|
||||
//! Sigurd Gartmann : https://github.com/sigurdga
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var nb = moment.defineLocale('nb', {
|
||||
months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'),
|
||||
monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'),
|
||||
weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'),
|
||||
weekdaysShort : 'søn_man_tirs_ons_tors_fre_lør'.split('_'),
|
||||
weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'H.mm',
|
||||
LTS : 'H.mm.ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'D. MMMM YYYY',
|
||||
LLL : 'D. MMMM YYYY [kl.] H.mm',
|
||||
LLLL : 'dddd D. MMMM YYYY [kl.] H.mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[i dag kl.] LT',
|
||||
nextDay: '[i morgen kl.] LT',
|
||||
nextWeek: 'dddd [kl.] LT',
|
||||
lastDay: '[i går kl.] LT',
|
||||
lastWeek: '[forrige] dddd [kl.] LT',
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'om %s',
|
||||
past : 'for %s siden',
|
||||
s : 'noen sekunder',
|
||||
m : 'ett minutt',
|
||||
mm : '%d minutter',
|
||||
h : 'en time',
|
||||
hh : '%d timer',
|
||||
d : 'en dag',
|
||||
dd : '%d dager',
|
||||
M : 'en måned',
|
||||
MM : '%d måneder',
|
||||
y : 'ett år',
|
||||
yy : '%d år'
|
||||
},
|
||||
ordinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
var nb = moment.defineLocale('nb', {
|
||||
months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'),
|
||||
monthsShort : 'jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.'.split('_'),
|
||||
monthsParseExact : true,
|
||||
weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'),
|
||||
weekdaysShort : 'sø._ma._ti._on._to._fr._lø.'.split('_'),
|
||||
weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'),
|
||||
weekdaysParseExact : true,
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'D. MMMM YYYY',
|
||||
LLL : 'D. MMMM YYYY [kl.] HH:mm',
|
||||
LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[i dag kl.] LT',
|
||||
nextDay: '[i morgen kl.] LT',
|
||||
nextWeek: 'dddd [kl.] LT',
|
||||
lastDay: '[i går kl.] LT',
|
||||
lastWeek: '[forrige] dddd [kl.] LT',
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'om %s',
|
||||
past : '%s siden',
|
||||
s : 'noen sekunder',
|
||||
m : 'ett minutt',
|
||||
mm : '%d minutter',
|
||||
h : 'en time',
|
||||
hh : '%d timer',
|
||||
d : 'en dag',
|
||||
dd : '%d dager',
|
||||
M : 'en måned',
|
||||
MM : '%d måneder',
|
||||
y : 'ett år',
|
||||
yy : '%d år'
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return nb;
|
||||
return nb;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,70 +1,88 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : dutch (nl)
|
||||
//! author : Joris Röling : https://github.com/jjupiter
|
||||
//! locale : Dutch [nl]
|
||||
//! author : Joris Röling : https://github.com/jorisroling
|
||||
//! author : Jacob Middag : https://github.com/middagj
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var monthsShortWithDots = 'jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.'.split('_'),
|
||||
monthsShortWithoutDots = 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_');
|
||||
var monthsShortWithDots = 'jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.'.split('_');
|
||||
var monthsShortWithoutDots = 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_');
|
||||
|
||||
var nl = moment.defineLocale('nl', {
|
||||
months : 'januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december'.split('_'),
|
||||
monthsShort : function (m, format) {
|
||||
if (/-MMM-/.test(format)) {
|
||||
return monthsShortWithoutDots[m.month()];
|
||||
} else {
|
||||
return monthsShortWithDots[m.month()];
|
||||
}
|
||||
},
|
||||
weekdays : 'zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag'.split('_'),
|
||||
weekdaysShort : 'zo._ma._di._wo._do._vr._za.'.split('_'),
|
||||
weekdaysMin : 'Zo_Ma_Di_Wo_Do_Vr_Za'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD-MM-YYYY',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd D MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[vandaag om] LT',
|
||||
nextDay: '[morgen om] LT',
|
||||
nextWeek: 'dddd [om] LT',
|
||||
lastDay: '[gisteren om] LT',
|
||||
lastWeek: '[afgelopen] dddd [om] LT',
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'over %s',
|
||||
past : '%s geleden',
|
||||
s : 'een paar seconden',
|
||||
m : 'één minuut',
|
||||
mm : '%d minuten',
|
||||
h : 'één uur',
|
||||
hh : '%d uur',
|
||||
d : 'één dag',
|
||||
dd : '%d dagen',
|
||||
M : 'één maand',
|
||||
MM : '%d maanden',
|
||||
y : 'één jaar',
|
||||
yy : '%d jaar'
|
||||
},
|
||||
ordinalParse: /\d{1,2}(ste|de)/,
|
||||
ordinal : function (number) {
|
||||
return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de');
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
var monthsParse = [/^jan/i, /^feb/i, /^maart|mrt.?$/i, /^apr/i, /^mei$/i, /^jun[i.]?$/i, /^jul[i.]?$/i, /^aug/i, /^sep/i, /^okt/i, /^nov/i, /^dec/i];
|
||||
var monthsRegex = /^(januari|februari|maart|april|mei|april|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i;
|
||||
|
||||
var nl = moment.defineLocale('nl', {
|
||||
months : 'januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december'.split('_'),
|
||||
monthsShort : function (m, format) {
|
||||
if (!m) {
|
||||
return monthsShortWithDots;
|
||||
} else if (/-MMM-/.test(format)) {
|
||||
return monthsShortWithoutDots[m.month()];
|
||||
} else {
|
||||
return monthsShortWithDots[m.month()];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
return nl;
|
||||
monthsRegex: monthsRegex,
|
||||
monthsShortRegex: monthsRegex,
|
||||
monthsStrictRegex: /^(januari|februari|maart|mei|ju[nl]i|april|augustus|september|oktober|november|december)/i,
|
||||
monthsShortStrictRegex: /^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,
|
||||
|
||||
}));
|
||||
monthsParse : monthsParse,
|
||||
longMonthsParse : monthsParse,
|
||||
shortMonthsParse : monthsParse,
|
||||
|
||||
weekdays : 'zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag'.split('_'),
|
||||
weekdaysShort : 'zo._ma._di._wo._do._vr._za.'.split('_'),
|
||||
weekdaysMin : 'Zo_Ma_Di_Wo_Do_Vr_Za'.split('_'),
|
||||
weekdaysParseExact : true,
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD-MM-YYYY',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd D MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[vandaag om] LT',
|
||||
nextDay: '[morgen om] LT',
|
||||
nextWeek: 'dddd [om] LT',
|
||||
lastDay: '[gisteren om] LT',
|
||||
lastWeek: '[afgelopen] dddd [om] LT',
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'over %s',
|
||||
past : '%s geleden',
|
||||
s : 'een paar seconden',
|
||||
m : 'één minuut',
|
||||
mm : '%d minuten',
|
||||
h : 'één uur',
|
||||
hh : '%d uur',
|
||||
d : 'één dag',
|
||||
dd : '%d dagen',
|
||||
M : 'één maand',
|
||||
MM : '%d maanden',
|
||||
y : 'één jaar',
|
||||
yy : '%d jaar'
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/,
|
||||
ordinal : function (number) {
|
||||
return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de');
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return nl;
|
||||
|
||||
})));
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : polish (pl)
|
||||
//! locale : Polish [pl]
|
||||
//! author : Rafal Hirsz : https://github.com/evoL
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var monthsNominative = 'styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień'.split('_'),
|
||||
monthsSubjective = 'stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia'.split('_');
|
||||
function plural(n) {
|
||||
return (n % 10 < 5) && (n % 10 > 1) && ((~~(n / 10) % 10) !== 1);
|
||||
}
|
||||
function translate(number, withoutSuffix, key) {
|
||||
var result = number + ' ';
|
||||
switch (key) {
|
||||
var monthsNominative = 'styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień'.split('_');
|
||||
var monthsSubjective = 'stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia'.split('_');
|
||||
function plural(n) {
|
||||
return (n % 10 < 5) && (n % 10 > 1) && ((~~(n / 10) % 10) !== 1);
|
||||
}
|
||||
function translate(number, withoutSuffix, key) {
|
||||
var result = number + ' ';
|
||||
switch (key) {
|
||||
case 'm':
|
||||
return withoutSuffix ? 'minuta' : 'minutę';
|
||||
case 'mm':
|
||||
@@ -29,41 +30,43 @@
|
||||
return result + (plural(number) ? 'miesiące' : 'miesięcy');
|
||||
case 'yy':
|
||||
return result + (plural(number) ? 'lata' : 'lat');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pl = moment.defineLocale('pl', {
|
||||
months : function (momentToFormat, format) {
|
||||
if (format === '') {
|
||||
// Hack: if format empty we know this is used to generate
|
||||
// RegExp by moment. Give then back both valid forms of months
|
||||
// in RegExp ready format.
|
||||
return '(' + monthsSubjective[momentToFormat.month()] + '|' + monthsNominative[momentToFormat.month()] + ')';
|
||||
} else if (/D MMMM/.test(format)) {
|
||||
return monthsSubjective[momentToFormat.month()];
|
||||
} else {
|
||||
return monthsNominative[momentToFormat.month()];
|
||||
}
|
||||
},
|
||||
monthsShort : 'sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru'.split('_'),
|
||||
weekdays : 'niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota'.split('_'),
|
||||
weekdaysShort : 'nie_pon_wt_śr_czw_pt_sb'.split('_'),
|
||||
weekdaysMin : 'N_Pn_Wt_Śr_Cz_Pt_So'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd, D MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[Dziś o] LT',
|
||||
nextDay: '[Jutro o] LT',
|
||||
nextWeek: '[W] dddd [o] LT',
|
||||
lastDay: '[Wczoraj o] LT',
|
||||
lastWeek: function () {
|
||||
switch (this.day()) {
|
||||
var pl = moment.defineLocale('pl', {
|
||||
months : function (momentToFormat, format) {
|
||||
if (!momentToFormat) {
|
||||
return monthsNominative;
|
||||
} else if (format === '') {
|
||||
// Hack: if format empty we know this is used to generate
|
||||
// RegExp by moment. Give then back both valid forms of months
|
||||
// in RegExp ready format.
|
||||
return '(' + monthsSubjective[momentToFormat.month()] + '|' + monthsNominative[momentToFormat.month()] + ')';
|
||||
} else if (/D MMMM/.test(format)) {
|
||||
return monthsSubjective[momentToFormat.month()];
|
||||
} else {
|
||||
return monthsNominative[momentToFormat.month()];
|
||||
}
|
||||
},
|
||||
monthsShort : 'sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru'.split('_'),
|
||||
weekdays : 'niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota'.split('_'),
|
||||
weekdaysShort : 'ndz_pon_wt_śr_czw_pt_sob'.split('_'),
|
||||
weekdaysMin : 'Nd_Pn_Wt_Śr_Cz_Pt_So'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd, D MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[Dziś o] LT',
|
||||
nextDay: '[Jutro o] LT',
|
||||
nextWeek: '[W] dddd [o] LT',
|
||||
lastDay: '[Wczoraj o] LT',
|
||||
lastWeek: function () {
|
||||
switch (this.day()) {
|
||||
case 0:
|
||||
return '[W zeszłą niedzielę o] LT';
|
||||
case 3:
|
||||
@@ -72,33 +75,33 @@
|
||||
return '[W zeszłą sobotę o] LT';
|
||||
default:
|
||||
return '[W zeszły] dddd [o] LT';
|
||||
}
|
||||
},
|
||||
sameElse: 'L'
|
||||
}
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'za %s',
|
||||
past : '%s temu',
|
||||
s : 'kilka sekund',
|
||||
m : translate,
|
||||
mm : translate,
|
||||
h : translate,
|
||||
hh : translate,
|
||||
d : '1 dzień',
|
||||
dd : '%d dni',
|
||||
M : 'miesiąc',
|
||||
MM : translate,
|
||||
y : 'rok',
|
||||
yy : translate
|
||||
},
|
||||
ordinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'za %s',
|
||||
past : '%s temu',
|
||||
s : 'kilka sekund',
|
||||
m : translate,
|
||||
mm : translate,
|
||||
h : translate,
|
||||
hh : translate,
|
||||
d : '1 dzień',
|
||||
dd : '%d dni',
|
||||
M : 'miesiąc',
|
||||
MM : translate,
|
||||
y : 'rok',
|
||||
yy : translate
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return pl;
|
||||
return pl;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,59 +1,61 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : brazilian portuguese (pt-br)
|
||||
//! locale : Portuguese (Brazil) [pt-br]
|
||||
//! author : Caio Ribeiro Pereira : https://github.com/caio-ribeiro-pereira
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var pt_br = moment.defineLocale('pt-br', {
|
||||
months : 'Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro'.split('_'),
|
||||
monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'),
|
||||
weekdays : 'Domingo_Segunda-Feira_Terça-Feira_Quarta-Feira_Quinta-Feira_Sexta-Feira_Sábado'.split('_'),
|
||||
weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'),
|
||||
weekdaysMin : 'Dom_2ª_3ª_4ª_5ª_6ª_Sáb'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD/MM/YYYY',
|
||||
LL : 'D [de] MMMM [de] YYYY',
|
||||
LLL : 'D [de] MMMM [de] YYYY [às] HH:mm',
|
||||
LLLL : 'dddd, D [de] MMMM [de] YYYY [às] HH:mm'
|
||||
var ptBr = moment.defineLocale('pt-br', {
|
||||
months : 'Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro'.split('_'),
|
||||
monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'),
|
||||
weekdays : 'Domingo_Segunda-feira_Terça-feira_Quarta-feira_Quinta-feira_Sexta-feira_Sábado'.split('_'),
|
||||
weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'),
|
||||
weekdaysMin : 'Do_2ª_3ª_4ª_5ª_6ª_Sá'.split('_'),
|
||||
weekdaysParseExact : true,
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD/MM/YYYY',
|
||||
LL : 'D [de] MMMM [de] YYYY',
|
||||
LLL : 'D [de] MMMM [de] YYYY [às] HH:mm',
|
||||
LLLL : 'dddd, D [de] MMMM [de] YYYY [às] HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[Hoje às] LT',
|
||||
nextDay: '[Amanhã às] LT',
|
||||
nextWeek: 'dddd [às] LT',
|
||||
lastDay: '[Ontem às] LT',
|
||||
lastWeek: function () {
|
||||
return (this.day() === 0 || this.day() === 6) ?
|
||||
'[Último] dddd [às] LT' : // Saturday + Sunday
|
||||
'[Última] dddd [às] LT'; // Monday - Friday
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[Hoje às] LT',
|
||||
nextDay: '[Amanhã às] LT',
|
||||
nextWeek: 'dddd [às] LT',
|
||||
lastDay: '[Ontem às] LT',
|
||||
lastWeek: function () {
|
||||
return (this.day() === 0 || this.day() === 6) ?
|
||||
'[Último] dddd [às] LT' : // Saturday + Sunday
|
||||
'[Última] dddd [às] LT'; // Monday - Friday
|
||||
},
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'em %s',
|
||||
past : '%s atrás',
|
||||
s : 'poucos segundos',
|
||||
m : 'um minuto',
|
||||
mm : '%d minutos',
|
||||
h : 'uma hora',
|
||||
hh : '%d horas',
|
||||
d : 'um dia',
|
||||
dd : '%d dias',
|
||||
M : 'um mês',
|
||||
MM : '%d meses',
|
||||
y : 'um ano',
|
||||
yy : '%d anos'
|
||||
},
|
||||
ordinalParse: /\d{1,2}º/,
|
||||
ordinal : '%dº'
|
||||
});
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'em %s',
|
||||
past : '%s atrás',
|
||||
s : 'poucos segundos',
|
||||
m : 'um minuto',
|
||||
mm : '%d minutos',
|
||||
h : 'uma hora',
|
||||
hh : '%d horas',
|
||||
d : 'um dia',
|
||||
dd : '%d dias',
|
||||
M : 'um mês',
|
||||
MM : '%d meses',
|
||||
y : 'um ano',
|
||||
yy : '%d anos'
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}º/,
|
||||
ordinal : '%dº'
|
||||
});
|
||||
|
||||
return pt_br;
|
||||
return ptBr;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,73 +1,75 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : romanian (ro)
|
||||
//! locale : Romanian [ro]
|
||||
//! author : Vlad Gurdiga : https://github.com/gurdiga
|
||||
//! author : Valentin Agachi : https://github.com/avaly
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
function relativeTimeWithPlural(number, withoutSuffix, key) {
|
||||
var format = {
|
||||
'mm': 'minute',
|
||||
'hh': 'ore',
|
||||
'dd': 'zile',
|
||||
'MM': 'luni',
|
||||
'yy': 'ani'
|
||||
},
|
||||
separator = ' ';
|
||||
if (number % 100 >= 20 || (number >= 100 && number % 100 === 0)) {
|
||||
separator = ' de ';
|
||||
}
|
||||
return number + separator + format[key];
|
||||
function relativeTimeWithPlural(number, withoutSuffix, key) {
|
||||
var format = {
|
||||
'mm': 'minute',
|
||||
'hh': 'ore',
|
||||
'dd': 'zile',
|
||||
'MM': 'luni',
|
||||
'yy': 'ani'
|
||||
},
|
||||
separator = ' ';
|
||||
if (number % 100 >= 20 || (number >= 100 && number % 100 === 0)) {
|
||||
separator = ' de ';
|
||||
}
|
||||
return number + separator + format[key];
|
||||
}
|
||||
|
||||
var ro = moment.defineLocale('ro', {
|
||||
months : 'ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie'.split('_'),
|
||||
monthsShort : 'ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.'.split('_'),
|
||||
weekdays : 'duminică_luni_marți_miercuri_joi_vineri_sâmbătă'.split('_'),
|
||||
weekdaysShort : 'Dum_Lun_Mar_Mie_Joi_Vin_Sâm'.split('_'),
|
||||
weekdaysMin : 'Du_Lu_Ma_Mi_Jo_Vi_Sâ'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'H:mm',
|
||||
LTS : 'H:mm:ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY H:mm',
|
||||
LLLL : 'dddd, D MMMM YYYY H:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[azi la] LT',
|
||||
nextDay: '[mâine la] LT',
|
||||
nextWeek: 'dddd [la] LT',
|
||||
lastDay: '[ieri la] LT',
|
||||
lastWeek: '[fosta] dddd [la] LT',
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'peste %s',
|
||||
past : '%s în urmă',
|
||||
s : 'câteva secunde',
|
||||
m : 'un minut',
|
||||
mm : relativeTimeWithPlural,
|
||||
h : 'o oră',
|
||||
hh : relativeTimeWithPlural,
|
||||
d : 'o zi',
|
||||
dd : relativeTimeWithPlural,
|
||||
M : 'o lună',
|
||||
MM : relativeTimeWithPlural,
|
||||
y : 'un an',
|
||||
yy : relativeTimeWithPlural
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 7 // The week that contains Jan 1st is the first week of the year.
|
||||
}
|
||||
});
|
||||
var ro = moment.defineLocale('ro', {
|
||||
months : 'ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie'.split('_'),
|
||||
monthsShort : 'ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.'.split('_'),
|
||||
monthsParseExact: true,
|
||||
weekdays : 'duminică_luni_marți_miercuri_joi_vineri_sâmbătă'.split('_'),
|
||||
weekdaysShort : 'Dum_Lun_Mar_Mie_Joi_Vin_Sâm'.split('_'),
|
||||
weekdaysMin : 'Du_Lu_Ma_Mi_Jo_Vi_Sâ'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'H:mm',
|
||||
LTS : 'H:mm:ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY H:mm',
|
||||
LLLL : 'dddd, D MMMM YYYY H:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[azi la] LT',
|
||||
nextDay: '[mâine la] LT',
|
||||
nextWeek: 'dddd [la] LT',
|
||||
lastDay: '[ieri la] LT',
|
||||
lastWeek: '[fosta] dddd [la] LT',
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'peste %s',
|
||||
past : '%s în urmă',
|
||||
s : 'câteva secunde',
|
||||
m : 'un minut',
|
||||
mm : relativeTimeWithPlural,
|
||||
h : 'o oră',
|
||||
hh : relativeTimeWithPlural,
|
||||
d : 'o zi',
|
||||
dd : relativeTimeWithPlural,
|
||||
M : 'o lună',
|
||||
MM : relativeTimeWithPlural,
|
||||
y : 'un an',
|
||||
yy : relativeTimeWithPlural
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 7 // The week that contains Jan 1st is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return ro;
|
||||
return ro;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,90 +1,110 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : russian (ru)
|
||||
//! locale : Russian [ru]
|
||||
//! author : Viktorminator : https://github.com/Viktorminator
|
||||
//! Author : Menelion Elensúle : https://github.com/Oire
|
||||
//! author : Коренберг Марк : https://github.com/socketpair
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
function plural(word, num) {
|
||||
var forms = word.split('_');
|
||||
return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]);
|
||||
function plural(word, num) {
|
||||
var forms = word.split('_');
|
||||
return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]);
|
||||
}
|
||||
function relativeTimeWithPlural(number, withoutSuffix, key) {
|
||||
var format = {
|
||||
'mm': withoutSuffix ? 'минута_минуты_минут' : 'минуту_минуты_минут',
|
||||
'hh': 'час_часа_часов',
|
||||
'dd': 'день_дня_дней',
|
||||
'MM': 'месяц_месяца_месяцев',
|
||||
'yy': 'год_года_лет'
|
||||
};
|
||||
if (key === 'm') {
|
||||
return withoutSuffix ? 'минута' : 'минуту';
|
||||
}
|
||||
function relativeTimeWithPlural(number, withoutSuffix, key) {
|
||||
var format = {
|
||||
'mm': withoutSuffix ? 'минута_минуты_минут' : 'минуту_минуты_минут',
|
||||
'hh': 'час_часа_часов',
|
||||
'dd': 'день_дня_дней',
|
||||
'MM': 'месяц_месяца_месяцев',
|
||||
'yy': 'год_года_лет'
|
||||
};
|
||||
if (key === 'm') {
|
||||
return withoutSuffix ? 'минута' : 'минуту';
|
||||
}
|
||||
else {
|
||||
return number + ' ' + plural(format[key], +number);
|
||||
}
|
||||
}
|
||||
function monthsCaseReplace(m, format) {
|
||||
var months = {
|
||||
'nominative': 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_'),
|
||||
'accusative': 'января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря'.split('_')
|
||||
},
|
||||
nounCase = (/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/).test(format) ?
|
||||
'accusative' :
|
||||
'nominative';
|
||||
return months[nounCase][m.month()];
|
||||
}
|
||||
function monthsShortCaseReplace(m, format) {
|
||||
var monthsShort = {
|
||||
'nominative': 'янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек'.split('_'),
|
||||
'accusative': 'янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек'.split('_')
|
||||
},
|
||||
nounCase = (/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/).test(format) ?
|
||||
'accusative' :
|
||||
'nominative';
|
||||
return monthsShort[nounCase][m.month()];
|
||||
}
|
||||
function weekdaysCaseReplace(m, format) {
|
||||
var weekdays = {
|
||||
'nominative': 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'),
|
||||
'accusative': 'воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу'.split('_')
|
||||
},
|
||||
nounCase = (/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/).test(format) ?
|
||||
'accusative' :
|
||||
'nominative';
|
||||
return weekdays[nounCase][m.day()];
|
||||
else {
|
||||
return number + ' ' + plural(format[key], +number);
|
||||
}
|
||||
}
|
||||
var monthsParse = [/^янв/i, /^фев/i, /^мар/i, /^апр/i, /^ма[йя]/i, /^июн/i, /^июл/i, /^авг/i, /^сен/i, /^окт/i, /^ноя/i, /^дек/i];
|
||||
|
||||
var ru = moment.defineLocale('ru', {
|
||||
months : monthsCaseReplace,
|
||||
monthsShort : monthsShortCaseReplace,
|
||||
weekdays : weekdaysCaseReplace,
|
||||
weekdaysShort : 'вс_пн_вт_ср_чт_пт_сб'.split('_'),
|
||||
weekdaysMin : 'вс_пн_вт_ср_чт_пт_сб'.split('_'),
|
||||
monthsParse : [/^янв/i, /^фев/i, /^мар/i, /^апр/i, /^ма[й|я]/i, /^июн/i, /^июл/i, /^авг/i, /^сен/i, /^окт/i, /^ноя/i, /^дек/i],
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'D MMMM YYYY г.',
|
||||
LLL : 'D MMMM YYYY г., HH:mm',
|
||||
LLLL : 'dddd, D MMMM YYYY г., HH:mm'
|
||||
// http://new.gramota.ru/spravka/rules/139-prop : § 103
|
||||
// Сокращения месяцев: http://new.gramota.ru/spravka/buro/search-answer?s=242637
|
||||
// CLDR data: http://www.unicode.org/cldr/charts/28/summary/ru.html#1753
|
||||
var ru = moment.defineLocale('ru', {
|
||||
months : {
|
||||
format: 'января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря'.split('_'),
|
||||
standalone: 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_')
|
||||
},
|
||||
monthsShort : {
|
||||
// по CLDR именно "июл." и "июн.", но какой смысл менять букву на точку ?
|
||||
format: 'янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.'.split('_'),
|
||||
standalone: 'янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.'.split('_')
|
||||
},
|
||||
weekdays : {
|
||||
standalone: 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'),
|
||||
format: 'воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу'.split('_'),
|
||||
isFormat: /\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/
|
||||
},
|
||||
weekdaysShort : 'вс_пн_вт_ср_чт_пт_сб'.split('_'),
|
||||
weekdaysMin : 'вс_пн_вт_ср_чт_пт_сб'.split('_'),
|
||||
monthsParse : monthsParse,
|
||||
longMonthsParse : monthsParse,
|
||||
shortMonthsParse : monthsParse,
|
||||
|
||||
// полные названия с падежами, по три буквы, для некоторых, по 4 буквы, сокращения с точкой и без точки
|
||||
monthsRegex: /^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,
|
||||
|
||||
// копия предыдущего
|
||||
monthsShortRegex: /^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,
|
||||
|
||||
// полные названия с падежами
|
||||
monthsStrictRegex: /^(январ[яь]|феврал[яь]|марта?|апрел[яь]|ма[яй]|июн[яь]|июл[яь]|августа?|сентябр[яь]|октябр[яь]|ноябр[яь]|декабр[яь])/i,
|
||||
|
||||
// Выражение, которое соотвествует только сокращённым формам
|
||||
monthsShortStrictRegex: /^(янв\.|февр?\.|мар[т.]|апр\.|ма[яй]|июн[ья.]|июл[ья.]|авг\.|сент?\.|окт\.|нояб?\.|дек\.)/i,
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD.MM.YYYY',
|
||||
LL : 'D MMMM YYYY г.',
|
||||
LLL : 'D MMMM YYYY г., HH:mm',
|
||||
LLLL : 'dddd, D MMMM YYYY г., HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[Сегодня в] LT',
|
||||
nextDay: '[Завтра в] LT',
|
||||
lastDay: '[Вчера в] LT',
|
||||
nextWeek: function (now) {
|
||||
if (now.week() !== this.week()) {
|
||||
switch (this.day()) {
|
||||
case 0:
|
||||
return '[В следующее] dddd [в] LT';
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
return '[В следующий] dddd [в] LT';
|
||||
case 3:
|
||||
case 5:
|
||||
case 6:
|
||||
return '[В следующую] dddd [в] LT';
|
||||
}
|
||||
} else {
|
||||
if (this.day() === 2) {
|
||||
return '[Во] dddd [в] LT';
|
||||
} else {
|
||||
return '[В] dddd [в] LT';
|
||||
}
|
||||
}
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[Сегодня в] LT',
|
||||
nextDay: '[Завтра в] LT',
|
||||
lastDay: '[Вчера в] LT',
|
||||
nextWeek: function () {
|
||||
return this.day() === 2 ? '[Во] dddd [в] LT' : '[В] dddd [в] LT';
|
||||
},
|
||||
lastWeek: function (now) {
|
||||
if (now.week() !== this.week()) {
|
||||
switch (this.day()) {
|
||||
lastWeek: function (now) {
|
||||
if (now.week() !== this.week()) {
|
||||
switch (this.day()) {
|
||||
case 0:
|
||||
return '[В прошлое] dddd [в] LT';
|
||||
case 1:
|
||||
@@ -95,50 +115,50 @@
|
||||
case 5:
|
||||
case 6:
|
||||
return '[В прошлую] dddd [в] LT';
|
||||
}
|
||||
} else {
|
||||
if (this.day() === 2) {
|
||||
return '[Во] dddd [в] LT';
|
||||
} else {
|
||||
return '[В] dddd [в] LT';
|
||||
}
|
||||
}
|
||||
},
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'через %s',
|
||||
past : '%s назад',
|
||||
s : 'несколько секунд',
|
||||
m : relativeTimeWithPlural,
|
||||
mm : relativeTimeWithPlural,
|
||||
h : 'час',
|
||||
hh : relativeTimeWithPlural,
|
||||
d : 'день',
|
||||
dd : relativeTimeWithPlural,
|
||||
M : 'месяц',
|
||||
MM : relativeTimeWithPlural,
|
||||
y : 'год',
|
||||
yy : relativeTimeWithPlural
|
||||
},
|
||||
meridiemParse: /ночи|утра|дня|вечера/i,
|
||||
isPM : function (input) {
|
||||
return /^(дня|вечера)$/.test(input);
|
||||
},
|
||||
meridiem : function (hour, minute, isLower) {
|
||||
if (hour < 4) {
|
||||
return 'ночи';
|
||||
} else if (hour < 12) {
|
||||
return 'утра';
|
||||
} else if (hour < 17) {
|
||||
return 'дня';
|
||||
} else {
|
||||
return 'вечера';
|
||||
if (this.day() === 2) {
|
||||
return '[Во] dddd [в] LT';
|
||||
} else {
|
||||
return '[В] dddd [в] LT';
|
||||
}
|
||||
}
|
||||
},
|
||||
ordinalParse: /\d{1,2}-(й|го|я)/,
|
||||
ordinal: function (number, period) {
|
||||
switch (period) {
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'через %s',
|
||||
past : '%s назад',
|
||||
s : 'несколько секунд',
|
||||
m : relativeTimeWithPlural,
|
||||
mm : relativeTimeWithPlural,
|
||||
h : 'час',
|
||||
hh : relativeTimeWithPlural,
|
||||
d : 'день',
|
||||
dd : relativeTimeWithPlural,
|
||||
M : 'месяц',
|
||||
MM : relativeTimeWithPlural,
|
||||
y : 'год',
|
||||
yy : relativeTimeWithPlural
|
||||
},
|
||||
meridiemParse: /ночи|утра|дня|вечера/i,
|
||||
isPM : function (input) {
|
||||
return /^(дня|вечера)$/.test(input);
|
||||
},
|
||||
meridiem : function (hour, minute, isLower) {
|
||||
if (hour < 4) {
|
||||
return 'ночи';
|
||||
} else if (hour < 12) {
|
||||
return 'утра';
|
||||
} else if (hour < 17) {
|
||||
return 'дня';
|
||||
} else {
|
||||
return 'вечера';
|
||||
}
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}-(й|го|я)/,
|
||||
ordinal: function (number, period) {
|
||||
switch (period) {
|
||||
case 'M':
|
||||
case 'd':
|
||||
case 'DDD':
|
||||
@@ -150,14 +170,14 @@
|
||||
return number + '-я';
|
||||
default:
|
||||
return number;
|
||||
}
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 7 // The week that contains Jan 1st is the first week of the year.
|
||||
}
|
||||
});
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 7 // The week that contains Jan 1st is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return ru;
|
||||
return ru;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,107 +1,110 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : Serbian-cyrillic (sr-cyrl)
|
||||
//! locale : Serbian [sr]
|
||||
//! author : Milan Janačković<milanjanackovic@gmail.com> : https://github.com/milan-j
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var translator = {
|
||||
words: { //Different grammatical cases
|
||||
m: ['један минут', 'једне минуте'],
|
||||
mm: ['минут', 'минуте', 'минута'],
|
||||
h: ['један сат', 'једног сата'],
|
||||
hh: ['сат', 'сата', 'сати'],
|
||||
dd: ['дан', 'дана', 'дана'],
|
||||
MM: ['месец', 'месеца', 'месеци'],
|
||||
yy: ['година', 'године', 'година']
|
||||
},
|
||||
correctGrammaticalCase: function (number, wordKey) {
|
||||
return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]);
|
||||
},
|
||||
translate: function (number, withoutSuffix, key) {
|
||||
var wordKey = translator.words[key];
|
||||
if (key.length === 1) {
|
||||
return withoutSuffix ? wordKey[0] : wordKey[1];
|
||||
} else {
|
||||
return number + ' ' + translator.correctGrammaticalCase(number, wordKey);
|
||||
}
|
||||
var translator = {
|
||||
words: { //Different grammatical cases
|
||||
m: ['jedan minut', 'jedne minute'],
|
||||
mm: ['minut', 'minute', 'minuta'],
|
||||
h: ['jedan sat', 'jednog sata'],
|
||||
hh: ['sat', 'sata', 'sati'],
|
||||
dd: ['dan', 'dana', 'dana'],
|
||||
MM: ['mesec', 'meseca', 'meseci'],
|
||||
yy: ['godina', 'godine', 'godina']
|
||||
},
|
||||
correctGrammaticalCase: function (number, wordKey) {
|
||||
return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]);
|
||||
},
|
||||
translate: function (number, withoutSuffix, key) {
|
||||
var wordKey = translator.words[key];
|
||||
if (key.length === 1) {
|
||||
return withoutSuffix ? wordKey[0] : wordKey[1];
|
||||
} else {
|
||||
return number + ' ' + translator.correctGrammaticalCase(number, wordKey);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var sr_cyrl = moment.defineLocale('sr-cyrl', {
|
||||
months: ['јануар', 'фебруар', 'март', 'април', 'мај', 'јун', 'јул', 'август', 'септембар', 'октобар', 'новембар', 'децембар'],
|
||||
monthsShort: ['јан.', 'феб.', 'мар.', 'апр.', 'мај', 'јун', 'јул', 'авг.', 'сеп.', 'окт.', 'нов.', 'дец.'],
|
||||
weekdays: ['недеља', 'понедељак', 'уторак', 'среда', 'четвртак', 'петак', 'субота'],
|
||||
weekdaysShort: ['нед.', 'пон.', 'уто.', 'сре.', 'чет.', 'пет.', 'суб.'],
|
||||
weekdaysMin: ['не', 'по', 'ут', 'ср', 'че', 'пе', 'су'],
|
||||
longDateFormat: {
|
||||
LT: 'H:mm',
|
||||
LTS : 'H:mm:ss',
|
||||
L: 'DD. MM. YYYY',
|
||||
LL: 'D. MMMM YYYY',
|
||||
LLL: 'D. MMMM YYYY H:mm',
|
||||
LLLL: 'dddd, D. MMMM YYYY H:mm'
|
||||
},
|
||||
calendar: {
|
||||
sameDay: '[данас у] LT',
|
||||
nextDay: '[сутра у] LT',
|
||||
nextWeek: function () {
|
||||
switch (this.day()) {
|
||||
var sr = moment.defineLocale('sr', {
|
||||
months: 'januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar'.split('_'),
|
||||
monthsShort: 'jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.'.split('_'),
|
||||
monthsParseExact: true,
|
||||
weekdays: 'nedelja_ponedeljak_utorak_sreda_četvrtak_petak_subota'.split('_'),
|
||||
weekdaysShort: 'ned._pon._uto._sre._čet._pet._sub.'.split('_'),
|
||||
weekdaysMin: 'ne_po_ut_sr_če_pe_su'.split('_'),
|
||||
weekdaysParseExact : true,
|
||||
longDateFormat: {
|
||||
LT: 'H:mm',
|
||||
LTS : 'H:mm:ss',
|
||||
L: 'DD.MM.YYYY',
|
||||
LL: 'D. MMMM YYYY',
|
||||
LLL: 'D. MMMM YYYY H:mm',
|
||||
LLLL: 'dddd, D. MMMM YYYY H:mm'
|
||||
},
|
||||
calendar: {
|
||||
sameDay: '[danas u] LT',
|
||||
nextDay: '[sutra u] LT',
|
||||
nextWeek: function () {
|
||||
switch (this.day()) {
|
||||
case 0:
|
||||
return '[у] [недељу] [у] LT';
|
||||
return '[u] [nedelju] [u] LT';
|
||||
case 3:
|
||||
return '[у] [среду] [у] LT';
|
||||
return '[u] [sredu] [u] LT';
|
||||
case 6:
|
||||
return '[у] [суботу] [у] LT';
|
||||
return '[u] [subotu] [u] LT';
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 5:
|
||||
return '[у] dddd [у] LT';
|
||||
}
|
||||
},
|
||||
lastDay : '[јуче у] LT',
|
||||
lastWeek : function () {
|
||||
var lastWeekDays = [
|
||||
'[прошле] [недеље] [у] LT',
|
||||
'[прошлог] [понедељка] [у] LT',
|
||||
'[прошлог] [уторка] [у] LT',
|
||||
'[прошле] [среде] [у] LT',
|
||||
'[прошлог] [четвртка] [у] LT',
|
||||
'[прошлог] [петка] [у] LT',
|
||||
'[прошле] [суботе] [у] LT'
|
||||
];
|
||||
return lastWeekDays[this.day()];
|
||||
},
|
||||
sameElse : 'L'
|
||||
return '[u] dddd [u] LT';
|
||||
}
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'за %s',
|
||||
past : 'пре %s',
|
||||
s : 'неколико секунди',
|
||||
m : translator.translate,
|
||||
mm : translator.translate,
|
||||
h : translator.translate,
|
||||
hh : translator.translate,
|
||||
d : 'дан',
|
||||
dd : translator.translate,
|
||||
M : 'месец',
|
||||
MM : translator.translate,
|
||||
y : 'годину',
|
||||
yy : translator.translate
|
||||
lastDay : '[juče u] LT',
|
||||
lastWeek : function () {
|
||||
var lastWeekDays = [
|
||||
'[prošle] [nedelje] [u] LT',
|
||||
'[prošlog] [ponedeljka] [u] LT',
|
||||
'[prošlog] [utorka] [u] LT',
|
||||
'[prošle] [srede] [u] LT',
|
||||
'[prošlog] [četvrtka] [u] LT',
|
||||
'[prošlog] [petka] [u] LT',
|
||||
'[prošle] [subote] [u] LT'
|
||||
];
|
||||
return lastWeekDays[this.day()];
|
||||
},
|
||||
ordinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 7 // The week that contains Jan 1st is the first week of the year.
|
||||
}
|
||||
});
|
||||
sameElse : 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'za %s',
|
||||
past : 'pre %s',
|
||||
s : 'nekoliko sekundi',
|
||||
m : translator.translate,
|
||||
mm : translator.translate,
|
||||
h : translator.translate,
|
||||
hh : translator.translate,
|
||||
d : 'dan',
|
||||
dd : translator.translate,
|
||||
M : 'mesec',
|
||||
MM : translator.translate,
|
||||
y : 'godinu',
|
||||
yy : translator.translate
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}\./,
|
||||
ordinal : '%d.',
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 7 // The week that contains Jan 1st is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return sr_cyrl;
|
||||
return sr;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,66 +1,69 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : swedish (sv)
|
||||
//! locale : Swedish [sv]
|
||||
//! author : Jens Alm : https://github.com/ulmus
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var sv = moment.defineLocale('sv', {
|
||||
months : 'januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december'.split('_'),
|
||||
monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'),
|
||||
weekdays : 'söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag'.split('_'),
|
||||
weekdaysShort : 'sön_mån_tis_ons_tor_fre_lör'.split('_'),
|
||||
weekdaysMin : 'sö_må_ti_on_to_fr_lö'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'YYYY-MM-DD',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd D MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[Idag] LT',
|
||||
nextDay: '[Imorgon] LT',
|
||||
lastDay: '[Igår] LT',
|
||||
nextWeek: '[På] dddd LT',
|
||||
lastWeek: '[I] dddd[s] LT',
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'om %s',
|
||||
past : 'för %s sedan',
|
||||
s : 'några sekunder',
|
||||
m : 'en minut',
|
||||
mm : '%d minuter',
|
||||
h : 'en timme',
|
||||
hh : '%d timmar',
|
||||
d : 'en dag',
|
||||
dd : '%d dagar',
|
||||
M : 'en månad',
|
||||
MM : '%d månader',
|
||||
y : 'ett år',
|
||||
yy : '%d år'
|
||||
},
|
||||
ordinalParse: /\d{1,2}(e|a)/,
|
||||
ordinal : function (number) {
|
||||
var b = number % 10,
|
||||
output = (~~(number % 100 / 10) === 1) ? 'e' :
|
||||
(b === 1) ? 'a' :
|
||||
(b === 2) ? 'a' :
|
||||
(b === 3) ? 'e' : 'e';
|
||||
return number + output;
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
var sv = moment.defineLocale('sv', {
|
||||
months : 'januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december'.split('_'),
|
||||
monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'),
|
||||
weekdays : 'söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag'.split('_'),
|
||||
weekdaysShort : 'sön_mån_tis_ons_tor_fre_lör'.split('_'),
|
||||
weekdaysMin : 'sö_må_ti_on_to_fr_lö'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'YYYY-MM-DD',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY [kl.] HH:mm',
|
||||
LLLL : 'dddd D MMMM YYYY [kl.] HH:mm',
|
||||
lll : 'D MMM YYYY HH:mm',
|
||||
llll : 'ddd D MMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay: '[Idag] LT',
|
||||
nextDay: '[Imorgon] LT',
|
||||
lastDay: '[Igår] LT',
|
||||
nextWeek: '[På] dddd LT',
|
||||
lastWeek: '[I] dddd[s] LT',
|
||||
sameElse: 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'om %s',
|
||||
past : 'för %s sedan',
|
||||
s : 'några sekunder',
|
||||
m : 'en minut',
|
||||
mm : '%d minuter',
|
||||
h : 'en timme',
|
||||
hh : '%d timmar',
|
||||
d : 'en dag',
|
||||
dd : '%d dagar',
|
||||
M : 'en månad',
|
||||
MM : '%d månader',
|
||||
y : 'ett år',
|
||||
yy : '%d år'
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}(e|a)/,
|
||||
ordinal : function (number) {
|
||||
var b = number % 10,
|
||||
output = (~~(number % 100 / 10) === 1) ? 'e' :
|
||||
(b === 1) ? 'a' :
|
||||
(b === 2) ? 'a' :
|
||||
(b === 3) ? 'e' : 'e';
|
||||
return number + output;
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return sv;
|
||||
return sv;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -1,91 +1,76 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : chinese (zh-cn)
|
||||
//! locale : Chinese (China) [zh-cn]
|
||||
//! author : suupic : https://github.com/suupic
|
||||
//! author : Zeno Zeng : https://github.com/zenozeng
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment'], factory) :
|
||||
;(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
&& typeof require === 'function' ? factory(require('../moment')) :
|
||||
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
|
||||
factory(global.moment)
|
||||
}(this, function (moment) { 'use strict';
|
||||
}(this, (function (moment) { 'use strict';
|
||||
|
||||
|
||||
var zh_cn = moment.defineLocale('zh-cn', {
|
||||
months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
|
||||
monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
|
||||
weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
|
||||
weekdaysShort : '周日_周一_周二_周三_周四_周五_周六'.split('_'),
|
||||
weekdaysMin : '日_一_二_三_四_五_六'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'Ah点mm分',
|
||||
LTS : 'Ah点m分s秒',
|
||||
L : 'YYYY-MM-DD',
|
||||
LL : 'YYYY年MMMD日',
|
||||
LLL : 'YYYY年MMMD日Ah点mm分',
|
||||
LLLL : 'YYYY年MMMD日ddddAh点mm分',
|
||||
l : 'YYYY-MM-DD',
|
||||
ll : 'YYYY年MMMD日',
|
||||
lll : 'YYYY年MMMD日Ah点mm分',
|
||||
llll : 'YYYY年MMMD日ddddAh点mm分'
|
||||
},
|
||||
meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
|
||||
meridiemHour: function (hour, meridiem) {
|
||||
if (hour === 12) {
|
||||
hour = 0;
|
||||
}
|
||||
if (meridiem === '凌晨' || meridiem === '早上' ||
|
||||
meridiem === '上午') {
|
||||
return hour;
|
||||
} else if (meridiem === '下午' || meridiem === '晚上') {
|
||||
return hour + 12;
|
||||
} else {
|
||||
// '中午'
|
||||
return hour >= 11 ? hour : hour + 12;
|
||||
}
|
||||
},
|
||||
meridiem : function (hour, minute, isLower) {
|
||||
var hm = hour * 100 + minute;
|
||||
if (hm < 600) {
|
||||
return '凌晨';
|
||||
} else if (hm < 900) {
|
||||
return '早上';
|
||||
} else if (hm < 1130) {
|
||||
return '上午';
|
||||
} else if (hm < 1230) {
|
||||
return '中午';
|
||||
} else if (hm < 1800) {
|
||||
return '下午';
|
||||
} else {
|
||||
return '晚上';
|
||||
}
|
||||
},
|
||||
calendar : {
|
||||
sameDay : function () {
|
||||
return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT';
|
||||
},
|
||||
nextDay : function () {
|
||||
return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT';
|
||||
},
|
||||
lastDay : function () {
|
||||
return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT';
|
||||
},
|
||||
nextWeek : function () {
|
||||
var startOfWeek, prefix;
|
||||
startOfWeek = moment().startOf('week');
|
||||
prefix = this.unix() - startOfWeek.unix() >= 7 * 24 * 3600 ? '[下]' : '[本]';
|
||||
return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
|
||||
},
|
||||
lastWeek : function () {
|
||||
var startOfWeek, prefix;
|
||||
startOfWeek = moment().startOf('week');
|
||||
prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]';
|
||||
return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
|
||||
},
|
||||
sameElse : 'LL'
|
||||
},
|
||||
ordinalParse: /\d{1,2}(日|月|周)/,
|
||||
ordinal : function (number, period) {
|
||||
switch (period) {
|
||||
var zhCn = moment.defineLocale('zh-cn', {
|
||||
months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
|
||||
monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
|
||||
weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
|
||||
weekdaysShort : '周日_周一_周二_周三_周四_周五_周六'.split('_'),
|
||||
weekdaysMin : '日_一_二_三_四_五_六'.split('_'),
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'YYYY年MMMD日',
|
||||
LL : 'YYYY年MMMD日',
|
||||
LLL : 'YYYY年MMMD日Ah点mm分',
|
||||
LLLL : 'YYYY年MMMD日ddddAh点mm分',
|
||||
l : 'YYYY年MMMD日',
|
||||
ll : 'YYYY年MMMD日',
|
||||
lll : 'YYYY年MMMD日 HH:mm',
|
||||
llll : 'YYYY年MMMD日dddd HH:mm'
|
||||
},
|
||||
meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
|
||||
meridiemHour: function (hour, meridiem) {
|
||||
if (hour === 12) {
|
||||
hour = 0;
|
||||
}
|
||||
if (meridiem === '凌晨' || meridiem === '早上' ||
|
||||
meridiem === '上午') {
|
||||
return hour;
|
||||
} else if (meridiem === '下午' || meridiem === '晚上') {
|
||||
return hour + 12;
|
||||
} else {
|
||||
// '中午'
|
||||
return hour >= 11 ? hour : hour + 12;
|
||||
}
|
||||
},
|
||||
meridiem : function (hour, minute, isLower) {
|
||||
var hm = hour * 100 + minute;
|
||||
if (hm < 600) {
|
||||
return '凌晨';
|
||||
} else if (hm < 900) {
|
||||
return '早上';
|
||||
} else if (hm < 1130) {
|
||||
return '上午';
|
||||
} else if (hm < 1230) {
|
||||
return '中午';
|
||||
} else if (hm < 1800) {
|
||||
return '下午';
|
||||
} else {
|
||||
return '晚上';
|
||||
}
|
||||
},
|
||||
calendar : {
|
||||
sameDay : '[今天]LT',
|
||||
nextDay : '[明天]LT',
|
||||
nextWeek : '[下]ddddLT',
|
||||
lastDay : '[昨天]LT',
|
||||
lastWeek : '[上]ddddLT',
|
||||
sameElse : 'L'
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}(日|月|周)/,
|
||||
ordinal : function (number, period) {
|
||||
switch (period) {
|
||||
case 'd':
|
||||
case 'D':
|
||||
case 'DDD':
|
||||
@@ -97,30 +82,30 @@
|
||||
return number + '周';
|
||||
default:
|
||||
return number;
|
||||
}
|
||||
},
|
||||
relativeTime : {
|
||||
future : '%s内',
|
||||
past : '%s前',
|
||||
s : '几秒',
|
||||
m : '1 分钟',
|
||||
mm : '%d 分钟',
|
||||
h : '1 小时',
|
||||
hh : '%d 小时',
|
||||
d : '1 天',
|
||||
dd : '%d 天',
|
||||
M : '1 个月',
|
||||
MM : '%d 个月',
|
||||
y : '1 年',
|
||||
yy : '%d 年'
|
||||
},
|
||||
week : {
|
||||
// GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
},
|
||||
relativeTime : {
|
||||
future : '%s内',
|
||||
past : '%s前',
|
||||
s : '几秒',
|
||||
m : '1 分钟',
|
||||
mm : '%d 分钟',
|
||||
h : '1 小时',
|
||||
hh : '%d 小时',
|
||||
d : '1 天',
|
||||
dd : '%d 天',
|
||||
M : '1 个月',
|
||||
MM : '%d 个月',
|
||||
y : '1 年',
|
||||
yy : '%d 年'
|
||||
},
|
||||
week : {
|
||||
// GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
return zh_cn;
|
||||
return zhCn;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
@@ -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,
|
||||
@@ -527,15 +527,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 +625,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 +635,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 +657,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 +782,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 +1077,7 @@ tr.queue-item>td:first-child>a {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.history-ratings .name-ratings {
|
||||
.history-ratings .name-icons {
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
@@ -1623,6 +1665,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 +1857,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 +1904,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) {
|
||||
@@ -1921,49 +1973,6 @@ input[name="nzbURL"] {
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
SPECIAL FOR FIREFOX
|
||||
It uses very high CPU for anything animated (Sep 2015)
|
||||
Disable animations on progress-bar and make the History-'processing' a block animation
|
||||
Can be removed if it's performance gets better in the future..
|
||||
***/
|
||||
@supports (-moz-transform: translate(0, 0)) {
|
||||
.progress-bar {
|
||||
transform: none !important;
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
@keyframes stretchdelay {
|
||||
0%, 60% {
|
||||
transform: scaleY(0.4);
|
||||
}
|
||||
|
||||
61%, 100% {
|
||||
transform: scaleY(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
.processing-download > div {
|
||||
animation: stretchdelay 2s infinite linear;
|
||||
}
|
||||
|
||||
.processing-download .loader-bar-two {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.processing-download .loader-bar-three {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.processing-download .loader-bar-four {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.queue-table td.name input {
|
||||
margin-left: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
Bootstrap overwrites
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="${path}staticcfg/ico/apple-touch-icon-76x76-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="${path}staticcfg/ico/apple-touch-icon-120x120-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="${path}staticcfg/ico/apple-touch-icon-152x152-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${path}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${path}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="${path}staticcfg/ico/android-192x192.png" />
|
||||
|
||||
|
||||
<script type="text/javascript" src="${path}static/javascripts/lib.js?$version"></script>
|
||||
|
||||
#if $pane=="Main"#
|
||||
@@ -101,8 +101,8 @@
|
||||
<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>
|
||||
#if $pane=="Main"#
|
||||
⋅ <a href="${path}config/general#web_dir">#echo $T('useGlitter').split('.')[0]#.</a>
|
||||
#if $new_release#⋅ <a href="$new_rel_url" id="new_release" target="_blank">$T('Plush-updateAvailable').replace(' ',' ')</a>#end if#
|
||||
#if $warning#⋅ <a id="warning_message">$warning.replace(' ',' ')</a>#end if#
|
||||
#end if#
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
@@ -84,10 +84,10 @@ $T('Plush-containerWidth'):
|
||||
<div id="add_nzb_hr"><hr></div>
|
||||
<i>$T('pp'):</i>
|
||||
<table id="add_nzb_pp">
|
||||
#if $cat_list#
|
||||
#if $categories#
|
||||
<tr><td style="text-align:right">$T('category'):</td>
|
||||
<td><select id="addID_cat" name="cat">
|
||||
#for $ct in $cat_list#
|
||||
#for $ct in $categories#
|
||||
<option value="$ct">$Tspec($ct)</option>
|
||||
#end for#
|
||||
</select>
|
||||
@@ -111,10 +111,10 @@ $T('Plush-containerWidth'):
|
||||
<option value="3">$T('pp-delete')</option>
|
||||
</select>
|
||||
</td></tr>
|
||||
#if $script_list#
|
||||
#if $scripts#
|
||||
<tr><td style="text-align:right">$T('script'):</td>
|
||||
<td><select id="addID_script" name="script">
|
||||
#for $sc in $script_list#
|
||||
#for $sc in $scripts#
|
||||
<option value="$sc">$Tspec($sc)</option>
|
||||
#end for#
|
||||
</select>
|
||||
@@ -186,7 +186,7 @@ $T('Plush-containerWidth'):
|
||||
<option value='www.newshosting.com'>NewsHosting</option>
|
||||
<option value='www.readnews.com'>Readnews</option>
|
||||
<option value='www.supernews.com'>SuperNews</option>
|
||||
<option value='www.thundernews.com'>ThunderNews</option>
|
||||
<option value='www.thundernews.com'>ThunderNews</option>
|
||||
<option value='www.tweaknews.eu'>Tweaknews</option>
|
||||
<option value='www.usenetserver.com'>UsenetServer</option>
|
||||
<option value='www.xentech.net'>XenTech</option>
|
||||
|
||||
@@ -46,9 +46,9 @@
|
||||
<option value="hibernate_pc" <!--#if $finishaction == 'hibernate_pc' then 'selected' else ''#-->>$T('hibernatePc')</option>
|
||||
<!--#end if#-->
|
||||
</optgroup>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<optgroup label="$T('eoq-scripts')">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<!--#if $sc != 'None'#-->
|
||||
<option value="script_$sc" <!--#if $finishaction == 'script_'+$sc then 'selected' else ''#-->>$sc</option>
|
||||
<!--#end if#-->
|
||||
@@ -85,7 +85,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
$T('Plush-maxSpeed'):
|
||||
<input type="text" id="maxSpeed-option" size="4" />
|
||||
<input type="text" id="maxSpeed-option" size="4" />
|
||||
<select id="maxSpeed-label">
|
||||
<option value="%">%</option>
|
||||
<option value="K">KB/s</option>
|
||||
@@ -136,10 +136,10 @@
|
||||
<option value="pause">$T('sch-pause')</option>
|
||||
<option value="resume">$T('sch-resume')</option>
|
||||
</optgroup></select>
|
||||
<!--#if $cat_list#-->
|
||||
<!--#if $categories#-->
|
||||
<select id="multi_cat"><optgroup label="$T('category')">
|
||||
<option value="">$T('category')</option>
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct">$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
@@ -158,10 +158,10 @@
|
||||
<option value="2">$T('pp-unpack')</option>
|
||||
<option value="3">$T('pp-delete')</option>
|
||||
</optgroup></select>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<select id="multi_script"><optgroup label="$T('script')">
|
||||
<option value="">$T('script')</option>
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<!--#if $sc != "Default"#--><option value="$sc">$Tspec($sc)</option><!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<option value=$i <!--#if $i == $index then "selected" else ""#-->>$i</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<!--#if $cat_list#-->
|
||||
<!--#if $categories#-->
|
||||
<select name="cat"><optgroup label="$T('category')">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $slot.cat.lower() == $ct.lower() then "selected" else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
@@ -35,9 +35,9 @@
|
||||
<option value="2" <!--#if $slot.unpackopts == "2" then "selected" else ""#-->>$T('pp-unpack')</option>
|
||||
<option value="3" <!--#if $slot.unpackopts == "3" then "selected" else ""#-->>$T('pp-delete')</option>
|
||||
</optgroup></select>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<select name="script"><optgroup label="$T('script')">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $slot.script.lower() == $sc.lower() then "selected" else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
|
||||
@@ -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#-->
|
||||
@@ -75,7 +75,7 @@
|
||||
<td></td>
|
||||
<!--#end if#-->
|
||||
<!--#end if#-->
|
||||
|
||||
|
||||
<td>
|
||||
<div class="main_sprite_container sprite_progressbar_bg">
|
||||
<div class="main_sprite_container sprite_progress_done" style="background-position: -<!--#if $slot.mb == "0.00" then "120" else int(120 - 120.0 / 100.0 * int(100 - float($slot.mbleft) / float($slot.mb) * 100))#-->px -401px">
|
||||
@@ -94,9 +94,9 @@
|
||||
</td>
|
||||
|
||||
<td class="options nowrap">
|
||||
<!--#if $cat_list#-->
|
||||
<!--#if $categories#-->
|
||||
<select class="change_cat"><optgroup label="$T('category')">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $slot.cat.lower() == $ct.lower() then "selected" else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
@@ -114,9 +114,9 @@
|
||||
<option value="2" <!--#if $slot.unpackopts == "2" then "selected " else ""#-->>$T('pp-unpack')</option>
|
||||
<option value="3" <!--#if $slot.unpackopts == "3" then "selected " else ""#-->>$T('pp-delete')</option>
|
||||
</optgroup></select>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<select class="change_script"><optgroup label="$T('script')">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $slot.script == $sc then "selected" else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
|
||||
@@ -109,11 +109,6 @@ body {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#new_release {
|
||||
color: green;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#livetip {
|
||||
position: absolute;
|
||||
background-color: #cfc;
|
||||
@@ -343,6 +338,7 @@ body {
|
||||
clear:left;
|
||||
padding: 0 20px 0 25px;
|
||||
font-size:90%;
|
||||
font-weight: bold;
|
||||
}
|
||||
#nav_text_left a, #nav_text_right a {
|
||||
color:#000;
|
||||
@@ -1083,7 +1079,7 @@ tr:hover .history_added { color: black; }
|
||||
color: red;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
.rating_icon_vision {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
|
Before Width: | Height: | Size: 112 B After Width: | Height: | Size: 78 B |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 729 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 932 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 256 B |
|
Before Width: | Height: | Size: 508 B After Width: | Height: | Size: 405 B |
|
Before Width: | Height: | Size: 980 B After Width: | Height: | Size: 618 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 454 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 854 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 276 B |
|
Before Width: | Height: | Size: 698 B After Width: | Height: | Size: 524 B |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 446 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 809 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 811 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 479 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 710 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 363 B |
|
Before Width: | Height: | Size: 764 B After Width: | Height: | Size: 617 B |
|
Before Width: | Height: | Size: 319 B After Width: | Height: | Size: 195 B |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 610 B After Width: | Height: | Size: 472 B |