Compare commits
2,197 commits
publicatio
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ac5982f905 | |||
| 961542283d | |||
| 7f38911bbb | |||
| c2996b9f0a | |||
| 8723f500cc | |||
| b55151623c | |||
| c7d1d7ce4c | |||
| c5d0616896 | |||
| e6f6686a39 | |||
| 56efb4ae94 | |||
| 7d775fe26d | |||
| b713eba2a5 | |||
| f3641a7c8f | |||
| 21752d1ca2 | |||
| f6713c768b | |||
| 38e3a4efdf | |||
| 9f25bc54d3 | |||
| a0cb395c79 | |||
| 532b3eccdc | |||
| 08afde34a8 | |||
| 3c31a9d4b4 | |||
| 7930391ac0 | |||
| bc94d0c649 | |||
| fbc84f9d08 | |||
| 4973f7ac4a | |||
| 0f2dafa3b1 | |||
| 404f29e6ea | |||
| 7da127ecb0 | |||
| f05664e2e3 | |||
| eaca60e5e0 | |||
| 42b9e54ec7 | |||
| 698e69d132 | |||
| 7af23ed297 | |||
| ac5772008b | |||
| 1ec71728de | |||
| 24fa72eb8a | |||
| 0edf71107a | |||
| f841d9c11c | |||
| bf2be00f15 | |||
| 71120c1c89 | |||
| 5ba86d0c5f | |||
| 8e196136c3 | |||
| 4ca2bc106a | |||
| 74f388a2b9 | |||
| 5e262b75a3 | |||
| d26333c5e2 | |||
| cb0e0e2c24 | |||
| 6100f33e7c | |||
| 47f2004a4c | |||
| faf74ec808 | |||
| e42545416f | |||
| 3f5d6bb04b | |||
| 99c436ba9a | |||
| a7d521fbdd | |||
| 0c53372618 | |||
| 84be750ce6 | |||
| 3881385c9e | |||
| e44cac32ac | |||
| 485e6b0173 | |||
| f1ada8ce99 | |||
| 7e301b8ecb | |||
| 4dcf1218d8 | |||
| 3f5b7b9ed7 | |||
| bc0570c2c7 | |||
| cd50a4b9d3 | |||
| 4734a8f047 | |||
| dadb84e8f9 | |||
| 801042e5cf | |||
| fca27b07fe | |||
| 3fc765ccfa | |||
| 590a55c395 | |||
| b62369f89f | |||
| cb4ceecbf5 | |||
| 98d9f2daf3 | |||
| db1e2603fc | |||
| 0730a22daa | |||
| 3467ca6db5 | |||
| 910adb123a | |||
| 1551c11a00 | |||
| ed3e6b66de | |||
| c21fd098a0 | |||
| 7df675346c | |||
| 526d693ffd | |||
| df8a759134 | |||
| 03e48b749c | |||
| 97f7e3fa59 | |||
| e421c91ac2 | |||
| baccc54d02 | |||
| e8e87c9958 | |||
| 08a31898df | |||
| b409fa6806 | |||
| 63b4cdc622 | |||
| 650f933993 | |||
| 603b226955 | |||
| 55e829fa64 | |||
| 45a0504c44 | |||
| ad7489e558 | |||
| 57c3cd8fd6 | |||
| 24686a6a24 | |||
| 407b67f4c2 | |||
| c28d974105 | |||
| ffb69663b6 | |||
| 4ec4f47951 | |||
| 96707e3a29 | |||
| a4001759f6 | |||
| f15cd29f78 | |||
| 7e41ddd664 | |||
| 89b7710544 | |||
| 79ec20d11c | |||
| 09206df20a | |||
| a14c151b04 | |||
| 68dad00930 | |||
| c74eadc801 | |||
|
|
376e112130 | ||
|
|
1d2a09f612 | ||
|
|
6a35bd6345 | ||
|
|
a463e88a90 | ||
|
|
3d066fbdeb | ||
|
|
7dd3f64a08 | ||
| bd5050b24a | |||
| 0ab453811c | |||
| e71dc24a27 | |||
| fb5147fac3 | |||
| f073ea0fd0 | |||
| 34cf1789f3 | |||
| 9e75386038 | |||
| a1b0e6a79b | |||
| 092d2256f7 | |||
| 28b4e7e529 | |||
| 32632322d4 | |||
| 4473166ee7 | |||
| 12feb91d48 | |||
| 03d02669ea | |||
| c1924c0e92 | |||
| 7692f92aa4 | |||
| 724f985770 | |||
| fa1b21e49f | |||
| 6b9283f7ca | |||
| 2a0a0dc9d4 | |||
| ad6e59d8eb | |||
| 6a1120898b | |||
| e178f7a80f | |||
| 895e34fef4 | |||
| 148deb77ec | |||
| 5321d499b2 | |||
| f6d2794fbd | |||
| ba37309ee5 | |||
| 6dfe4115c3 | |||
| 0296c9cc85 | |||
| 180fcbfa33 | |||
| 5168d875d5 | |||
| 53095d40a9 | |||
| a15532f8b8 | |||
| 70882779e8 | |||
| c97897f2a4 | |||
| d955dc1b3b | |||
| 2597bf2a14 | |||
| 681fb44462 | |||
| be6448971b | |||
| 24dd190299 | |||
| d14936fd29 | |||
| f5e2d91c1b | |||
| 8a91d4b7fa | |||
| 07043bd692 | |||
| 629b450f89 | |||
| 696cbae7fa | |||
| 0eb50e0cbc | |||
| 4265d6ab92 | |||
| 0a8a36d73d | |||
| ea8ad1d6db | |||
| e08dd2f2e8 | |||
| ac8f704062 | |||
| 371eb2fe68 | |||
| b2200ad8f2 | |||
| 5b4514a254 | |||
| 005c13196b | |||
| 60170390ca | |||
| 626a5b0981 | |||
| 106fddeb2e | |||
| 0c61fa29cd | |||
| 83cdee759a | |||
| 1adb1807b5 | |||
| 0f9d56fcbf | |||
| 8c6db30c52 | |||
|
|
3316375cbb | ||
| 3d2606ab9c | |||
| 2f6c7ecd8b | |||
| 1f295c3411 | |||
| 0bf367bd3b | |||
| a82e3642a8 | |||
| 38fa6ec1de | |||
| 051d62a5fa | |||
| 4c3b07db1e | |||
| c293b58a94 | |||
| a630075116 | |||
| 7ae1517a59 | |||
| 5a4960f9ad | |||
| 0669f74395 | |||
| 5981240280 | |||
| 180ec5e29d | |||
| 3d1b318091 | |||
| c8f70c48fa | |||
| 81958ef4b9 | |||
| 8bb8cb18e3 | |||
| caae846bc7 | |||
| bf71a40f49 | |||
| e86e50fcd0 | |||
| bd901abf56 | |||
| 6cbfea1494 | |||
| 8802fe80d7 | |||
| cf623c7a47 | |||
| ba9a0aee42 | |||
| c2c138b6e3 | |||
| 46cdd304e8 | |||
| 86b0ff1669 | |||
| 517cfdd4d2 | |||
| 88cc25e8a2 | |||
| 65435ceeb4 | |||
| 84b5702da8 | |||
| 8f14814335 | |||
| aaad54d643 | |||
| c26617607d | |||
| cc552db7cf | |||
| b828b26162 | |||
| e64acba944 | |||
| 9105285235 | |||
| 4c93a94334 | |||
| 3d221f3ab2 | |||
| c39e6a6ef4 | |||
| 93ac43183d | |||
| a13c055bf3 | |||
| 34c11bf731 | |||
| afde4acc2c | |||
| 914698a38c | |||
| e68fd1c0d8 | |||
| 462dffe9ab | |||
| 0eb6934474 | |||
| efb374a573 | |||
| 3052e2af6d | |||
| 7f62fd7205 | |||
| 1796ecc25e | |||
| f21dc21193 | |||
| 1204b9df18 | |||
| db0e49a8cb | |||
| 43ffc36557 | |||
| 125662bc8c | |||
| 3393122dbc | |||
| a8b8a4cc31 | |||
| 9ee910514e | |||
| ea7bfee5ea | |||
| 40b9c713dc | |||
| 08b27eeba9 | |||
| ed478eaf67 | |||
| 49239045e2 | |||
| 91c7ef9785 | |||
| 3e828f9bb7 | |||
| 8bbd0e643b | |||
| e2db847f70 | |||
| 0b7fa570db | |||
| f0a253245d | |||
| 83778129d3 | |||
| 3c457015eb | |||
| eeced21be8 | |||
| 651d428223 | |||
| b5065df4c3 | |||
| 5ece912ec9 | |||
| 77cdfdb355 | |||
| 0b45855842 | |||
| ade20a0410 | |||
| 74fb73a268 | |||
| 84e218a672 | |||
| f0377f5f5d | |||
| 9172f36be7 | |||
| 298d09f346 | |||
| 18cab3dc6b | |||
| d98aa1c269 | |||
| c0188e6d52 | |||
| 66ab0dfc78 | |||
| 76606b3c80 | |||
| c53140b88e | |||
| 59cf98ead2 | |||
| c547c45d31 | |||
| ecb815666e | |||
| 373bd83640 | |||
| 59344893cb | |||
| 9dbf34f4d3 | |||
| 9d87f70bc8 | |||
| 725e015478 | |||
| adb18a6a7c | |||
| 382417b9ff | |||
| b4ec736948 | |||
| 3f0e0536b9 | |||
| f4d0e0001c | |||
| 9e6a03c681 | |||
| 122e919daf | |||
| df08e1ec72 | |||
| 239e8ae88d | |||
| 0092170dbd | |||
| e3e55c579a | |||
| ed217b5d72 | |||
| a0a62a808d | |||
| 45395e399d | |||
| a01380730c | |||
| ada16f4ce7 | |||
| db603676a8 | |||
| 5974fe8cd4 | |||
| 59af4103b8 | |||
| 48c7a42922 | |||
| d049e0f18e | |||
| 24e825d500 | |||
| 81d272c5b2 | |||
| dc5350c20f | |||
| c3e6cadb70 | |||
| 1889447b34 | |||
| cf4ff0245f | |||
| 52bc7b6650 | |||
| 18fb11360b | |||
| 052b1a5949 | |||
| d8d95027af | |||
| f585d75a66 | |||
| d9483ee98c | |||
| 0808af7ded | |||
| fa5aee89c8 | |||
| dbf22f668b | |||
| c9449b2338 | |||
| 65b5bf8c16 | |||
|
|
1404aa2c0f | ||
| f815bff8ca | |||
| 21bf188691 | |||
| 1e24d0ea25 | |||
| 3c8ba3ecc2 | |||
| bed79b947b | |||
| b74d49aae7 | |||
| c0017d7cbb | |||
| f157d9c3bd | |||
| ac966f9023 | |||
| 5a6d9047c2 | |||
| e9dc522a81 | |||
| 989dce2aed | |||
| 26c282138e | |||
| 35d07c1aa4 | |||
| 85ab183bed | |||
| ba388780d8 | |||
| 7fbe2f3f8e | |||
| 09c1111135 | |||
| b3c207d07d | |||
| 6e684436d1 | |||
| 0db9e9b539 | |||
| 357944564b | |||
| d323bf9ee9 | |||
| a0cd651dae | |||
| c082ee43d0 | |||
| 6e5fd70156 | |||
| b9ded53920 | |||
| c638789b61 | |||
| 977caccc1f | |||
| ae5068f8b8 | |||
| cc147a9819 | |||
| f32873f307 | |||
| 0ca7aa568d | |||
| 84b9e352ee | |||
| 398de21793 | |||
| a1ce2df131 | |||
| 516ebf9c5a | |||
| 5e48ab0928 | |||
| 13a11269a8 | |||
| d234bbf272 | |||
| 4c29f2e53e | |||
| a9635ca8ac | |||
| 5edaf2cf3d | |||
| adb0e36dd4 | |||
| 76f830b332 | |||
| daae6f4f07 | |||
| 79afaa8fb2 | |||
| 5592fabefa | |||
| d44fc4f715 | |||
| ad72eb0b95 | |||
| 19481962d5 | |||
| 0c45b52e04 | |||
| c7fc18bfb4 | |||
| a0bc832910 | |||
| 3519f7416d | |||
| e4f404d8d6 | |||
| b0cb03bbe1 | |||
| 97f73cbadb | |||
| 79d78bd358 | |||
| 9a8cb32719 | |||
| f646eb41be | |||
| 55e3b35198 | |||
| 71da41b7cd | |||
| a4fe5bbedd | |||
| 177c87eeb7 | |||
| c09f2b9ad7 | |||
| 9e3ed09c5c | |||
| aa2650f247 | |||
| 9732f7c7bd | |||
| b2c37b7fff | |||
| 756412bafd | |||
| 508ee458cd | |||
| 0f28a175b5 | |||
| 7e00529aa8 | |||
| ef76edf9e0 | |||
| e7e4ab35dd | |||
| cb1cb391d1 | |||
| eac4d717ff | |||
| 697e55fd74 | |||
| 632eb62f1f | |||
| 0b04185933 | |||
| 904ef661ba | |||
| fb6a5d8063 | |||
| 81cd6fe3a1 | |||
| 7798420d56 | |||
| 37bd268470 | |||
| c674e1b621 | |||
| b93c57f704 | |||
| e3762ffb1d | |||
| 7cde7c05a1 | |||
| 3bd6ecf11e | |||
| 5eb1f66ba7 | |||
| 017df69896 | |||
| b1ca990537 | |||
| afcc7f2de0 | |||
| 48fe1e7711 | |||
| 954cf84f0f | |||
| 04e938ff73 | |||
| 636cc2b55b | |||
|
|
55f7d0826e | ||
| 0de881d23e | |||
| b049930da9 | |||
| 2feb2d47b5 | |||
| a043138a63 | |||
| 4dedcfc420 | |||
| fe786e8b93 | |||
| bff01deaff | |||
| e9359b9a5e | |||
| a10a93f336 | |||
| efd571b36d | |||
| 3ef2db319f | |||
| 631b2ff990 | |||
| 75bf99ed7b | |||
| 82d4cb3e38 | |||
| 0591f81255 | |||
| e611424ba3 | |||
| a5c6cdeb7e | |||
| cc2eee22f5 | |||
| add006b2c2 | |||
| 834ab0f5c4 | |||
| ac45800326 | |||
| 1a6209e58d | |||
| d4cad767eb | |||
| c31f76e9c3 | |||
| 9a5347b8ef | |||
| 334512ef0d | |||
| 563cf14adc | |||
| 34fd07ba4e | |||
| c9e3a9ac3c | |||
| c3353572e6 | |||
| f3cdf1afca | |||
| 1f833d39fc | |||
| 092256d9e5 | |||
| 60a34d3ced | |||
|
|
0a22d09947 | ||
| b52622c772 | |||
| 9896445e00 | |||
| 130bb0c092 | |||
| ff83468fef | |||
| 83ceded133 | |||
| ecf5cac9c9 | |||
| b6966d47ce | |||
| 9f49a689fd | |||
| e651a7495f | |||
| 50f668c229 | |||
| f1a662b11f | |||
| 3dd5127145 | |||
| 16e331586b | |||
| 790e5b20f5 | |||
| 4e07dba966 | |||
| 4cd784765a | |||
| 520c0a86c7 | |||
| e9ed3a42f6 | |||
| c82e09a1c4 | |||
| 1378d5bd71 | |||
| df90c5c72f | |||
| c70eb7f582 | |||
| 84a771a4a2 | |||
| 6fd14306e1 | |||
| 20272e7bad | |||
| f6b94b33e5 | |||
| a5234e401e | |||
| 7e5e93ed35 | |||
| 4ca709a11d | |||
| ceb3ff0b6a | |||
| 190fdfe422 | |||
| d6ff46ca7f | |||
| a0c34018cf | |||
| ac39fe2493 | |||
| 5a9d2226e4 | |||
| e957fccaca | |||
| 9cca20c6f8 | |||
| f328261ea2 | |||
| 08107931ea | |||
| 9a34e393f3 | |||
| a109c6d341 | |||
| 0c7f5b88df | |||
| ec61b7ed1d | |||
| a80dd34d1b | |||
| e8c5b540d1 | |||
| 01281adf28 | |||
| e65c54ea37 | |||
| a7432b070d | |||
| b08039c997 | |||
| 598b34eb4f | |||
|
|
f6bb741070 | ||
|
|
1afbab32dd | ||
| b3e221a15b | |||
| a2d2457811 | |||
| 3f8750990e | |||
| d996c12452 | |||
| ba26dd6bb1 | |||
| 106f1a5e30 | |||
| e74b5b267b | |||
| 7573717f71 | |||
| 4b03f0befd | |||
| 0ab321bb87 | |||
| 7c8ab2567a | |||
| 5e5114bbd3 | |||
| 5db06e688a | |||
| 41ff4b9f50 | |||
| 49aa1682d7 | |||
| 46db6bbf20 | |||
| 27a69b321b | |||
| dfd8580afd | |||
| 43de6a6b71 | |||
| 2097be2e21 | |||
| 54590087ab | |||
| 6163d51e5b | |||
| 3e1c01031f | |||
| 8b50029f4d | |||
| 1d4b79bf90 | |||
| ec3f818c30 | |||
| 7cc076b31e | |||
| 5a484abaa8 | |||
| ba8dcc1ddb | |||
| 28edb55131 | |||
| 59e52f3020 | |||
| 5811cc2c2f | |||
| 2dbe79930a | |||
| 682a5f933f | |||
| 289393e9c3 | |||
| 3b1fd53ba6 | |||
| 22bd4e68c4 | |||
| 81b9649c5d | |||
| bb7ded3619 | |||
| 02edce9df7 | |||
| 04a3a64ffa | |||
| f4f42b1303 | |||
| a905f56e42 | |||
| 6578cddf02 | |||
| d65026edd0 | |||
| 3c97f91a46 | |||
| 60d88cd317 | |||
| 3ef5a562c9 | |||
| 430e5f5cb8 | |||
| 55e6700c45 | |||
| 1aaf2105a5 | |||
| 6eab49510c | |||
| 705047f770 | |||
| 4b97d20ff6 | |||
| 0fcdcfaa7a | |||
| f27acf3830 | |||
| d12e6597bd | |||
| 23a982f083 | |||
| bf61b0184b | |||
| 4ee950cca2 | |||
| 11662d8c5e | |||
| 17d140d7cc | |||
| cb2cd7f4c0 | |||
| 51e3bfde90 | |||
| 08002751bf | |||
| 19c447d4e4 | |||
| 7bfab1ad64 | |||
| 9aee418889 | |||
| 06cb10a0c7 | |||
| a058679829 | |||
| c13da8b574 | |||
| 859b6a318e | |||
| cd64fc90bf | |||
| b94beb363b | |||
| 7114ece593 | |||
| d4990916b5 | |||
| d2f409db7a | |||
| e000778696 | |||
| bbe2072b4f | |||
| 57720f156e | |||
| dd594a8d08 | |||
| 4fb0c11736 | |||
| 3e5e8c9ba4 | |||
| c69a335a91 | |||
| 89334ce57c | |||
| 1ace4394b5 | |||
| 3e828ebbfc | |||
| 0c9ba50fcc | |||
| 25f2b5827a | |||
| 89d687cd94 | |||
|
|
a5699b6cce | ||
|
|
a431b75e69 | ||
| 8717fc24fd | |||
| 6caf8c53b9 | |||
| 8132bc6b17 | |||
| 1d66450240 | |||
| 97bdad21a2 | |||
| acf909ab1e | |||
| 472e3a8cba | |||
| 27d2121337 | |||
| d91d514ed6 | |||
| c86349bc72 | |||
| dfccde82cf | |||
| 0aff2a3151 | |||
| 55817f265b | |||
| 3478ffca03 | |||
| bbb23e08ec | |||
| b6bc7f5736 | |||
| 3d2a601580 | |||
| 3502e3f6b8 | |||
| c08386b0ca | |||
| 6acc752bd9 | |||
| 5dd92a6603 | |||
| 6d450d3667 | |||
| f097c029f3 | |||
| 499e251796 | |||
|
|
b3b102b2f4 | ||
|
|
eb67674da0 | ||
|
|
0200dce71b | ||
|
|
4ef8589330 | ||
|
|
9c656c92fe | ||
|
|
7999464384 | ||
|
|
5cf894031c | ||
|
|
4856a2ce2d | ||
|
|
979f64845c | ||
| 50adfa9536 | |||
| 934493f77a | |||
| 1769938205 | |||
| ec98e521dc | |||
| 4df1948069 | |||
| ed091e761c | |||
| f755d7c998 | |||
| 1ca5452707 | |||
| dc83efa868 | |||
| edbb867f62 | |||
| 6fd1856dd9 | |||
| 7a2b1bdede | |||
| b9a456e8f7 | |||
| 1e2db62c74 | |||
| 2116253e9d | |||
| ad57faf0c1 | |||
| 240ecd269d | |||
| fd09d1bbbc | |||
| 064d291c2a | |||
| 144ae6d5a2 | |||
| 073258e067 | |||
| e787a48f76 | |||
| ca3b5460ae | |||
| 2137c8c243 | |||
| d5642d7888 | |||
| b3ec909f96 | |||
| d475365b43 | |||
| b86a6ebc0c | |||
| 34f175e57b | |||
| 61ec7a56f5 | |||
| 60243dd486 | |||
| 28ad0fa791 | |||
| 1a8ebcb8bf | |||
| ab23ef8f71 | |||
| abe5ad61d4 | |||
| f366d6b8c1 | |||
| c06d667088 | |||
| 5cd285f6d0 | |||
| 1051291b84 | |||
| 3a2fce7376 | |||
| 232e7ae879 | |||
| 033cb1fe18 | |||
| ecd5f25ec9 | |||
| 620f744c0c | |||
| 3c5b4a7d9f | |||
| d85417b925 | |||
| 31bccddc49 | |||
| 3a38a75e25 | |||
| 3cf3a03ab8 | |||
| d9abf90e84 | |||
| 5e712dc4a4 | |||
| 290a445e41 | |||
| 4a19893480 | |||
| 81b7dea92d | |||
| 9a9dff84ee | |||
| 1ece65ff9b | |||
| 1c87b9ce55 | |||
| 6943c66c25 | |||
| 7712419a9f | |||
| edc5c25a29 | |||
| 5b102ad8ea | |||
| 0a627b9c6d | |||
| 1c78cefb09 | |||
| d4c6a1ccbb | |||
| 6fa262eb7f | |||
| bd0b1d28a1 | |||
|
|
0a7fc6fa47 | ||
| d8458e5b49 | |||
| 78189aab37 | |||
| e472b482d6 | |||
| 2d6fd8eb45 | |||
| 4a603dcaf7 | |||
| 890a532d01 | |||
| acabe41e07 | |||
| b7344f2b73 | |||
| 20dc1f65dc | |||
| 37dde01444 | |||
| aad95f1e53 | |||
| d4f69059bf | |||
| 34889a949f | |||
| 532cb6e489 | |||
| 5f31b85556 | |||
| fa57099f45 | |||
| 541741760b | |||
| 5fb85c22dc | |||
| ac64db277a | |||
| 6407970dfa | |||
| 2ca3d1471f | |||
| 902d9195ac | |||
| 79a5d251aa | |||
| f6a251e2ec | |||
| 2140939364 | |||
| 20c41ec573 | |||
| 75eae43f60 | |||
| 63cf665f2d | |||
| abd91012f8 | |||
| 3103dc1029 | |||
| e261c77c79 | |||
| f5529ff72d | |||
| f623699f56 | |||
| c5a059bd3b | |||
| 5cf4565573 | |||
| f1d96089ce | |||
| 55de68f428 | |||
| 8f18dbbf81 | |||
| c8f59ce706 | |||
| dd8781ea24 | |||
| 4fd8d3880a | |||
| 1f16cabe19 | |||
| 5db62e45d4 | |||
| 6faae3e48d | |||
| 3b84537430 | |||
| fb64472f2f | |||
| 375f1da071 | |||
| d8462cf58e | |||
| 089e604679 | |||
| 0d5b87b3f7 | |||
| 268925db0d | |||
| 3344e05e0d | |||
| 18b8f0f722 | |||
| b79fe47f00 | |||
| bf758e7fe5 | |||
| 0d596ccb8c | |||
| cc0e26ef1f | |||
| a9ba784e58 | |||
| 094deeab51 | |||
| 37bfd8f2b9 | |||
| 9ac09278f6 | |||
| 00f7399170 | |||
| 217c85aed5 | |||
| 848eb913e4 | |||
| 32a3414ff1 | |||
| d84040bda9 | |||
| 7faec773d6 | |||
| 0fef44a542 | |||
| 6ba309e5e5 | |||
| 3f9e0d851d | |||
| 8f8f22b5bc | |||
| 802e7b4f53 | |||
| 6bd23ee9f0 | |||
| a585a6338e | |||
| 47aea1de66 | |||
| 5163ce4c8d | |||
| c84d39ca27 | |||
| 6a0b0545d7 | |||
| 1a21115503 | |||
| 0a7b40abd7 | |||
| 4d7161281d | |||
| 0c7eecf315 | |||
| af1fe1e6d8 | |||
| c2104a642d | |||
| 71d7ac3cbf | |||
| 1bd30632d3 | |||
| bd9d9e9402 | |||
| d98d846cd6 | |||
| da6f8acbbd | |||
| 938ee1d172 | |||
| 7de41b78a7 | |||
| d17e0d17a9 | |||
| 8ffa7a2cba | |||
| 769024517f | |||
| 159b8bda50 | |||
| 54a96ab501 | |||
| f263712185 | |||
| 5fac0d4b30 | |||
| 3772af4965 | |||
| 4451e41285 | |||
| 838ce2fb3f | |||
| d4ce0dd474 | |||
| b8c5ec6725 | |||
| fd7f6d0c63 | |||
| e636763597 | |||
| 38c0a5ceee | |||
| ef1eafb789 | |||
| 3fcf705dcf | |||
| eb85b28f5b | |||
| 0f41e44e13 | |||
| 0e9d0f4933 | |||
| f24d3ea5e8 | |||
| 3d89159d0c | |||
| ade363cab1 | |||
| d2aa336bf2 | |||
| 5427d3cbf1 | |||
| 6855318e42 | |||
| 66ddca30fb | |||
| a882a4a302 | |||
| f1c0ffd679 | |||
| 1c1f5a8485 | |||
| 444d6c3523 | |||
| f7dfc5a510 | |||
| 7333fbeb71 | |||
| 26911181a2 | |||
| 3fb7c6e772 | |||
| f7540ee5cf | |||
| f92116c2ba | |||
| 83f30319b3 | |||
| 394e11a742 | |||
| 4b7181c857 | |||
| b879bb3aca | |||
| 75145dc5cd | |||
| 847a42700f | |||
| 1d3a75ff82 | |||
| 0f65babdf4 | |||
| 1720906ec8 | |||
| a7309b6a00 | |||
| 6b74674123 | |||
| 81ea38e2c1 | |||
| 3cbb20a985 | |||
| aa0e7406c1 | |||
| 99cc79421f | |||
| ab2eb7f1cc | |||
| 935f2c0c24 | |||
| 87a0d1ccaa | |||
| 9b3087c8b1 | |||
| 9f7b4e5498 | |||
| 0acafa24ac | |||
| cad2bc09e4 | |||
| ef194d2cf3 | |||
| 7cc8c65ab2 | |||
| dbe0984560 | |||
| 4b09efa9fd | |||
| 2df227c25b | |||
| baa410e654 | |||
| 7edbd0f9de | |||
| 30a39fa9ef | |||
| 4b9e796341 | |||
| 8db701889f | |||
| 93eed8d5e4 | |||
| ecc9ae6ef1 | |||
| f079ecd9e3 | |||
| f087213f0a | |||
| ec9a3a408d | |||
| 84f85d631a | |||
| fee1ab2a26 | |||
| 2381dfe4f5 | |||
| ccc2c5d1d7 | |||
| ea02fa4617 | |||
| f0e6183c21 | |||
| 3421286c9b | |||
| 80422daffb | |||
| 1f3f0fd55b | |||
| 20f5656a74 | |||
| bd19d31577 | |||
| c28ad9533b | |||
| 14f10c91db | |||
| 257c594dbe | |||
| 60d790f8d3 | |||
| abb277210c | |||
| 9bcf4a481e | |||
| 6ca71230c1 | |||
| 541e32e10b | |||
| f4c3f1e15e | |||
| 24a1e86c19 | |||
| 8cd15bc1d8 | |||
| de245a14f4 | |||
| d89f2b5b0f | |||
| 85027166d7 | |||
| 0834bc4d13 | |||
| 3a4003670f | |||
|
|
643ecb1e14 | ||
| 83be5595ba | |||
| f1a2e6c360 | |||
| 7eb56999a3 | |||
| 95a7986e94 | |||
| 072637c8cd | |||
| 26fb0c5554 | |||
| 22716fc51c | |||
| a8fa58e111 | |||
| b784c448a0 | |||
| 0ffa126950 | |||
| 3049989ac6 | |||
| cb489396ab | |||
| 9044260a71 | |||
| cd51246d9b | |||
| 5560a526b1 | |||
| 0e19b59452 | |||
| 13588fc634 | |||
| 67f129ce4c | |||
| 8758effc99 | |||
| 1aa82bb2ef | |||
| 00f84e43ca | |||
| d002c2a4c5 | |||
| f49ff5aeda | |||
| ee080c0666 | |||
| 0fe037d7f5 | |||
| abdf146fea | |||
| 9fd5564410 | |||
| 7c2e97740f | |||
| cd07bec05b | |||
| 8e1b3bede0 | |||
| 0e0b50d439 | |||
| c13fd3d0b1 | |||
| 2ace5e1e52 | |||
| ad7ad37e7f | |||
| ef999999ea | |||
| c415e06237 | |||
| 4b8e447b1b | |||
| ae1378780f | |||
| 5b47d1c250 | |||
| f9e9bfcb75 | |||
| 057ce22fb9 | |||
| 7a800b10de | |||
| 76ee40b7f1 | |||
| b334122707 | |||
| 19daf69482 | |||
| cc37348aaa | |||
| 6a5119cb6a | |||
| 79c251d85f | |||
| 7b2603afb0 | |||
| 3d35cee67d | |||
| ffd43ac8e1 | |||
| 9d171cfe89 | |||
| 50b3e4c739 | |||
| 98939e9d61 | |||
| 960122dfb6 | |||
| a28f108b8a | |||
| 6b7ed273b7 | |||
| 91b2daea2e | |||
| 38a4e21e28 | |||
| 5d716106c4 | |||
| acdf0a6261 | |||
| 23ac512ce6 | |||
| ac25202024 | |||
| d7ff10762e | |||
| 5d07634d0d | |||
| d48dd2647c | |||
| 4647ff033c | |||
| 070684d618 | |||
| a4701af619 | |||
| a790ced236 | |||
| 56e41cad6a | |||
| adebdd180d | |||
| cea3c13369 | |||
| fb368d79d1 | |||
| edde9f885d | |||
| 721908ee18 | |||
| 2b26b2b819 | |||
| d791e74a2a | |||
| 47776eeeb4 | |||
| 3a6daa3d04 | |||
| 3cf92b4798 | |||
| ca12b3dde5 | |||
| e86a2c9be2 | |||
| 92d5c0bd64 | |||
| 9a6dd6e360 | |||
| 9ed4a918d4 | |||
| d028a2e50a | |||
| b5193d5bac | |||
| ad5d74cfe3 | |||
| 06c7067b7a | |||
| be4cfb8a38 | |||
| 503da6cc4f | |||
| eaa73fcb85 | |||
| 0f97e07649 | |||
| 16168df123 | |||
| 1297fe2a39 | |||
| 64feef8b95 | |||
| 6aead541d6 | |||
| 85b47afa6a | |||
| 044e38172b | |||
| 0accd98e5c | |||
| e0b34c8043 | |||
| 1b3a32b65e | |||
| 7d31bb3312 | |||
| 7daf4e07cc | |||
| c982576625 | |||
| 5678efa5c4 | |||
| 60aafc5997 | |||
| ad993e4e4c | |||
| 32582f576b | |||
| 198314c2f5 | |||
| 26fb064a81 | |||
| 817f43ee7a | |||
| 8e4e60d9d0 | |||
| 737207d59a | |||
| 51ff2ef3d6 | |||
| 1dd41fcb3a | |||
| b5fff619b8 | |||
| a3f229d56e | |||
| 211facf9d0 | |||
| e3e8e86b4c | |||
| e2b47e744d | |||
| 41fb5a1cd0 | |||
| 9bc9851b12 | |||
| 95c992555c | |||
| 86d9a039c8 | |||
| a85c2efa45 | |||
| db9d7edf6b | |||
| 8aba067d05 | |||
| 95aadffb2e | |||
| c78545c18b | |||
| 08ea1bac0d | |||
| a0013947dd | |||
| c7968fb256 | |||
| c34fe51641 | |||
| e84b1d67cb | |||
| b0129e5239 | |||
| d8943ba1f3 | |||
|
|
8afbacd654 | ||
|
|
af388dbfff | ||
|
|
04a9e3b3b6 | ||
|
|
a3144fac45 | ||
| 05f396f5c9 | |||
| e0034a1fc1 | |||
| b26ef1c0ce | |||
| 223f44572e | |||
| dfe62e0b97 | |||
| cf75367b5b | |||
| 01a9bb2e94 | |||
| 4cce95245e | |||
| d69c062d40 | |||
| 9f45f10775 | |||
| 0f75b71f5f | |||
| 7b300d4ffe | |||
| 32d003f7b7 | |||
| 4b1b5445f7 | |||
| 0a8d0dad30 | |||
| e922171f17 | |||
| 7a5c1eeba7 | |||
| 38857054ba | |||
| 36af72d616 | |||
| 498e3c5b63 | |||
| 30a665ff72 | |||
| 750db69b06 | |||
| e9dd35f8ac | |||
| 329bd246c7 | |||
| 116c061715 | |||
| 9ea415b857 | |||
| 159672ec47 | |||
| cfde1689cc | |||
| af6e86d4ef | |||
| 0831ea6088 | |||
| a82defe2a7 | |||
| a7dda3a999 | |||
| cdc342bea3 | |||
| d2d7b35623 | |||
| cc1b212cca | |||
| 367e686e8a | |||
| 6d9fd1ff12 | |||
| 6aa0f4da95 | |||
| ba096c0af1 | |||
| 9a2fd85d57 | |||
| 83a579fbd2 | |||
| 1591ec4376 | |||
| 11a12e1d44 | |||
| f4188ec289 | |||
| 39acdee6b2 | |||
| 46d1bb21f7 | |||
| bfdb1c2bf7 | |||
| cf502bd9d5 | |||
| cf7482a14a | |||
| f2e7b30ace | |||
| 0e6a6893f1 | |||
| 437ed4d393 | |||
| 635e67c224 | |||
| 8cb7bf8b96 | |||
| 046b38cc32 | |||
| 58af047a26 | |||
| d09c1741a2 | |||
| 2ce95ccafc | |||
| 59de4f66d8 | |||
| 722295989c | |||
| b92b107efe | |||
| d883926647 | |||
| 68bd43c7ce | |||
| 8d8d8d7c82 | |||
| 152bbe178f | |||
| f61b0a8e47 | |||
| 68fb332ed1 | |||
| 595318e7b1 | |||
| b7a36f906c | |||
| a414cd22c8 | |||
| e581630d5e | |||
| f0902dc023 | |||
| 549f96535c | |||
| eca15b394a | |||
| fafb778c9d | |||
| cd03b99f9b | |||
| c0260da035 | |||
| f65375b01f | |||
| 4c84038b28 | |||
| 1856a78d10 | |||
| fbeb2cc42b | |||
| 70891bf0e9 | |||
| 6c31820178 | |||
| 8fd2a70894 | |||
| ad41513654 | |||
| 48895af3e8 | |||
| 5d25024481 | |||
| 5bfd1574f2 | |||
| 565c55754d | |||
| 243f16d2b8 | |||
| bd35705f58 | |||
| 3c237819c3 | |||
| 465a48c1c0 | |||
| eb07eadae0 | |||
| 4b2625c47d | |||
| 123467f3eb | |||
| 58217d1d8a | |||
| aab66bf612 | |||
| 8ed9415c68 | |||
| 560110ba5e | |||
| d24b1c5d4d | |||
| 2c76b5c7a3 | |||
| 80917ae436 | |||
| a6adc1ac8c | |||
| 45a9240834 | |||
| 3bf0fc69ee | |||
| 4a190f51c5 | |||
| b92381f007 | |||
| 8eb2bda539 | |||
| 9fe66c563b | |||
| 70bad90756 | |||
| 630c065825 | |||
| 7cdca440e6 | |||
| f690a4e1c8 | |||
| 72add55723 | |||
| 8b3fbdb64a | |||
| 83468ad723 | |||
| 9d639a0315 | |||
| 3c0751a78a | |||
| c25c11e70a | |||
| 5a5e02e533 | |||
| a9b3a45fb1 | |||
| 9cf33ee755 | |||
| 3fa53bc877 | |||
| 1313f2caf9 | |||
| 53e70b1eba | |||
| c525acff20 | |||
| 48ee5321a8 | |||
| c713a0a25d | |||
| dff4f4eb63 | |||
| e8f6a03cd9 | |||
| 457cd307dd | |||
| 15afbb8b87 | |||
| dc64eb549f | |||
| de4e97f033 | |||
| 86aaf5f519 | |||
| 14281b5485 | |||
| fa7dc0b9b7 | |||
| b5cb3b92ec | |||
| 840e9c3534 | |||
| c11eb8a315 | |||
| b302de9218 | |||
| 85b2703d4a | |||
| cb0e503b1f | |||
| 2539882b48 | |||
| c8fb852157 | |||
| 18b5aca547 | |||
| 2a85c55e18 | |||
| d7247d6854 | |||
| 70cd7ab571 | |||
| d97187af93 | |||
| 45672db8b5 | |||
| ad39e0fff5 | |||
| 63e775308f | |||
| 2f748dc3f7 | |||
| 38eaec6edc | |||
| 1613b1780d | |||
| 7a7f90eeda | |||
| 40a9b0d187 | |||
| 25e2e60092 | |||
| 0355ec1a54 | |||
| 5e4c14c634 | |||
| 2cd40e64ab | |||
| 02bd5f316a | |||
| 2ca2018485 | |||
| 277258814b | |||
| 1dcde1ba10 | |||
| 2b87449475 | |||
| 5d36c8a2c2 | |||
| 1d36de028d | |||
| 59ff2e5a3a | |||
| b1961c7102 | |||
| 3e8ab0bd1a | |||
| 86748b36c8 | |||
| d4a81aa660 | |||
| 01b05aaed0 | |||
| b98e23d060 | |||
| 892bb99461 | |||
| 63b0ad4885 | |||
| 76ccd25ae3 | |||
| ce41ab76eb | |||
| b1315a9eeb | |||
| 15c85c8f59 | |||
| 252ff33b83 | |||
| 5a79343af8 | |||
| 081ad1f928 | |||
| 40a9078b70 | |||
| 63d8ae4ecd | |||
| 4b82987bbb | |||
| 2645109839 | |||
| 92bb409764 | |||
| 0a15bd9756 | |||
| eb7697ed50 | |||
| f8001653cd | |||
| 0cc72712a4 | |||
| e6d8f2db1b | |||
| 7896579189 | |||
| c7569b5e54 | |||
| 6999b4e728 | |||
| 8a6d480d17 | |||
| 63423c9c69 | |||
| 969c61017f | |||
| 2d5b34ee01 | |||
| 57d351c6c1 | |||
| 43be59b97d | |||
| 23c43ad667 | |||
| 9fe1374a77 | |||
| 3625af47d9 | |||
| 724b00b825 | |||
| 038abe450d | |||
| a06602a7e8 | |||
| 1a1343596a | |||
| 995740e275 | |||
| 49664c3dfe | |||
| 41565729fd | |||
| 61fccca070 | |||
| c3742ade4e | |||
| 30d0afe43f | |||
| 495b08463f | |||
| 89ca192890 | |||
| aebfb7bf96 | |||
| 281056a723 | |||
| c85c1f0354 | |||
| 87583fbd17 | |||
| 315fb1efae | |||
| 744aabf821 | |||
| e29802b731 | |||
| 8d9269c635 | |||
|
|
2e4513c00f | ||
| 56d8d49304 | |||
| d010b86fa0 | |||
| 2875d3eb59 | |||
| 48b65e0d39 | |||
| 7c2bb0df23 | |||
| 08da42e273 | |||
| 105034ec8c | |||
| cd73622cae | |||
| b887288c78 | |||
| 203450df32 | |||
| f6fec437f9 | |||
| debfd2a894 | |||
| 22e25a384a | |||
| 1488ee60e5 | |||
| 82c2af57cb | |||
| bd8db24997 | |||
| de03863f1b | |||
| 89979eac8f | |||
| 71fa7f67ea | |||
| cf1d8d9516 | |||
| e6aadfdd8b | |||
| b6ed4fd966 | |||
| ac4fc633ce | |||
| 8a383719b4 | |||
| b9a220c359 | |||
| 5eeb1a6297 | |||
| ed69dc6ba4 | |||
| 75d288000f | |||
| e48ee589e5 | |||
| 86e15bd80b | |||
| 9367d99a05 | |||
| 5fa94ecbed | |||
| fb53c9a4f1 | |||
| a0a1a717ee | |||
| b2c8b735f4 | |||
| 63de5d64b1 | |||
| 6395eaaf5f | |||
| 84115b89f7 | |||
| dc6d7152f9 | |||
| 25116cca59 | |||
| 12a6fcf461 | |||
| f2bc4b015f | |||
| 5c12963da8 | |||
| 342d216b3e | |||
| abf0715dbf | |||
| f5941dcece | |||
| a812a6a5c6 | |||
| f5f450f456 | |||
| 2b58e707ca | |||
| 864f78e8fa | |||
| 986fbb8f74 | |||
| b0a7daf1f4 | |||
| 9dcd43664a | |||
| 46261af751 | |||
| 2a9d2cacda | |||
| 8683e78213 | |||
| eca6a4238d | |||
| a5bf9d2600 | |||
| ab11446be2 | |||
| dedfab1c7d | |||
| d319587dab | |||
| 2246e00948 | |||
| 17839474e1 | |||
| 74d77dce9f | |||
| f2bf07fd28 | |||
| 83a47af391 | |||
| e6557c8c06 | |||
| 941e1c16d5 | |||
| e3057726e8 | |||
| 815f4b9037 | |||
| 3c42bef298 | |||
| a255480195 | |||
| 23d5ea7c97 | |||
| 451b678e73 | |||
| dcb0cb315b | |||
| 6223d2be36 | |||
| 1def2c97c1 | |||
| ef899ee99b | |||
| 102a0878ac | |||
| 74e8c3801a | |||
| 867e9bb345 | |||
| 7e13cf28bd | |||
| 9fa1ede69c | |||
| 234ce066cf | |||
| 4fd4fb01b4 | |||
| 993af78fd1 | |||
| 25ba010987 | |||
| 96c629f27c | |||
| 688f97016a | |||
| 0681c1ebe3 | |||
| 66344fa0db | |||
| 77c71c5364 | |||
| 7e9c2ccbe9 | |||
| 357035deba | |||
| 9a9d742e21 | |||
| d701331436 | |||
| 29607981e4 | |||
| 8f1b44e3dd | |||
| 74ae52ef41 | |||
| c2fe14c0d7 | |||
| 8e95cec104 | |||
| e0dd5ea789 | |||
| 7fc860edec | |||
| eb26879c74 | |||
| 57fe1a7517 | |||
| 8b261011b6 | |||
| d458ac963a | |||
| 5c990de2a0 | |||
| 9fa89e0793 | |||
| 9dc1f401b7 | |||
| 99862b6daa | |||
| 5d13cfe01e | |||
| 10c408eda6 | |||
| 0d792dcd8f | |||
| f4dcaa23a3 | |||
| 2cf9723c6c | |||
| 3434535f51 | |||
| 1445917fec | |||
| f7c15925c6 | |||
| 8e8fa7c61c | |||
| f53a5dbcf9 | |||
| c10becba91 | |||
| a93d6c8c49 | |||
| 38c18ef1aa | |||
| 3bc8f0bf95 | |||
| 1ad4382e97 | |||
| 1a3e14040c | |||
| ea334a8a2f | |||
| 911bcb032e | |||
| 1436d9ca81 | |||
| a8f25471f1 | |||
| 74c3599b5d | |||
| 95ca255d75 | |||
| 4490eb7036 | |||
| ce2f42cdc8 | |||
| 70afa61814 | |||
| 7c51ce7c4f | |||
| 5aa21d2679 | |||
| 940b32debc | |||
| 42d594ccac | |||
| a237936feb | |||
| a0155c6deb | |||
| eca814ca4b | |||
| 7ad36f6141 | |||
| c9932cdaf6 | |||
| 2d6d09852b | |||
| 90151ce498 | |||
| 130bb92dc8 | |||
| 5b84e4bfdb | |||
| 64b9e9a251 | |||
| 5d3ef96f3f | |||
| 16abc95b4f | |||
| 21cc875cc0 | |||
| adb424ea03 | |||
| 45069d4fbb | |||
| 3bc8d7064b | |||
| d45a6841db | |||
| ed2a38511e | |||
| ff56933a93 | |||
| 56c6f282c4 | |||
| 0c8bc261d9 | |||
| bb9dd10f00 | |||
| 429cd3010c | |||
| ca8bac1ac8 | |||
| 0536da2472 | |||
| a82a0fb170 | |||
| f1c5681a37 | |||
| e017e11f68 | |||
| 5849648c70 | |||
| 83ba3b88a5 | |||
| 66a72633d6 | |||
| b9fa5accff | |||
| d66de6fb3c | |||
| 0ab637faed | |||
| 35bd908374 | |||
| 7c84301c04 | |||
| cb97af2f8a | |||
| 4f237677e2 | |||
| 5df1cc6e93 | |||
| 007efc6118 | |||
| a0b19f6184 | |||
| 6f17fc0760 | |||
| 05a795ad49 | |||
| 23b6b2b005 | |||
| 32fe61f557 | |||
| 7f691779f7 | |||
| d093f3670b | |||
| 3f692984c7 | |||
| 15ae32090f | |||
| 0bc42282aa | |||
| b387f011d8 | |||
| f4c74f57d6 | |||
| 3cb4e98bd1 | |||
| d944be349a | |||
| edbac43423 | |||
| 73eb3ab1c0 | |||
| d8584a8a31 | |||
| 83b7df7e69 | |||
| e45a674937 | |||
| e945071a10 | |||
| 590522e7ed | |||
| a3ffdeae17 | |||
| 7bec409ab8 | |||
| 9d93331868 | |||
| ac9361b4ce | |||
| caea02bb4d | |||
| 4820d42327 | |||
| 6d5e2bcb65 | |||
| 34a2370236 | |||
| 5c17dd4605 | |||
| 99e53ccfe6 | |||
| 6921431a77 | |||
| e937073588 | |||
| 823328ead2 | |||
| 4e258cb30d | |||
| 0937b4a2b8 | |||
| f3fdb36929 | |||
| c9cacb80a7 | |||
| 6f64eaed95 | |||
| 9186bbc229 | |||
| 32dc9c1a8c | |||
| 2e3f7c6894 | |||
| 56b79cae2d | |||
| 80a4192cb4 | |||
| a4c87b92a5 | |||
| f7762c0828 | |||
| 2bae30a841 | |||
| 084d39f6cf | |||
| 08aa7d278c | |||
| 22c4835875 | |||
| ccf32f8a48 | |||
| 91cc8b9314 | |||
| 4f6480d7f8 | |||
| a6e799a635 | |||
| f251d30162 | |||
| 9c9d4edd74 | |||
| b35a8a9200 | |||
| 769158a9d7 | |||
| aee3500fdf | |||
| 16c337c2bc | |||
| 1833a7550d | |||
| 04345b33a2 | |||
| e7c1812cb0 | |||
| aba311aebd | |||
| 47ba134b55 | |||
| 8f998485bb | |||
| a8b6e9fba5 | |||
| 0cfa695873 | |||
| 9983542653 | |||
| 546cae869b | |||
| 141c5dd33d | |||
| 104cb067ea | |||
| bea31faa8e | |||
| f078919459 | |||
| 5e1f314822 | |||
| 778c30035b | |||
| 3465406b6f | |||
| e00a67832e | |||
| e4b740b5bc | |||
| 572082cd5f | |||
| 225f6d2c99 | |||
| 5ffbeabf5b | |||
| 13548d913f | |||
| 91e40c1e1a | |||
| f3f14dcd25 | |||
| a475617657 | |||
| b4fa57f9c9 | |||
| a545112cb2 | |||
| 11a66346e7 | |||
| 56053f3350 | |||
| f3a34c00db | |||
| 6ad11e49d5 | |||
| 9693940d8c | |||
| d97ecde3fb | |||
| 4a490b1a33 | |||
| 14f5cf29b7 | |||
| 1db035e050 | |||
| 698c2f1a47 | |||
| fbae34ee4f | |||
| 26eab7ed67 | |||
| 5dcb13629a | |||
| fa33fac003 | |||
| 6740256a32 | |||
| 20f2597248 | |||
| cefed3bf23 | |||
| c2c5cf4ce3 | |||
| 97a3aa713f | |||
| 0766fbe480 | |||
|
|
cb7f3326c4 | ||
| 48a941e2c5 | |||
| 0c8099a639 | |||
| f2fc142869 | |||
| ded583008a | |||
| ca891cd9b2 | |||
| 6265f85149 | |||
| 99fcc99e82 | |||
| 33f7d104e4 | |||
| d7f0425d8a | |||
| 4e01377a29 | |||
| 6f7bd3b785 | |||
| 168be0f7cc | |||
| 846f2ce8a4 | |||
| 8e618565ad | |||
| 0cbd6390ba | |||
| 6d72e6b970 | |||
| 41a3279bf8 | |||
| 8131fda0e7 | |||
| c8ece39cb2 | |||
| 936ef09e33 | |||
| 973363b3da | |||
| 3e5b4ebad2 | |||
| ba5642da8f | |||
| 2b75287d16 | |||
| 3bcac39f5f | |||
| 4c7fd839b6 | |||
| a3d473983d | |||
| 444ba85f11 | |||
| 32f1c86519 | |||
| 9a9675bcf5 | |||
| eb95d861d3 | |||
| 4039a394b5 | |||
| 3f55845374 | |||
| 3104bf4e65 | |||
| 3f99771910 | |||
| 682598fdbb | |||
| 536dc0eb6b | |||
| 15d108497e | |||
| ff7c89af9f | |||
| 771627a0da | |||
| b778d29dd9 | |||
| 703eaef880 | |||
| 14d31737e0 | |||
| 2b95995104 | |||
| 921644deb4 | |||
| a35aa7be70 | |||
| 6a1f73c895 | |||
| 60ec9704e3 | |||
| 2381fb490b | |||
| 34d2054e04 | |||
| 2ab9cb2eaa | |||
| 6715fb10a9 | |||
| c4aa220b2c | |||
| 473332e101 | |||
| 73db9da682 | |||
| 650f1f4d59 | |||
| 41ef7f2555 | |||
| 1757b40a9c | |||
| af73b2b872 | |||
| e97cf884ce | |||
| db22c4af1b | |||
| 85d326db6b | |||
| c9347f45e2 | |||
| 5c5dadbee0 | |||
| 5714e8f41b | |||
| 42a17ab450 | |||
| 17a9d39556 | |||
| fa2d514bbc | |||
| c1bc86d3c9 | |||
| 547184a40a | |||
| 8abe57ffb6 | |||
| 3f7e217316 | |||
| eee2cd6a2f | |||
| b205409679 | |||
| 8c754fe265 | |||
| f983da9815 | |||
| 088c2402cd | |||
| 48fcfec0d0 | |||
| bf426d2ed2 | |||
| 525b3d6b56 | |||
| 56faf7b8db | |||
| f32e46c699 | |||
| 19b57f5908 | |||
| 58dbd9499b | |||
| 50a51ba628 | |||
| 65908f8880 | |||
| 7d9ad18f42 | |||
| 09b24cd401 | |||
| 0812fe5000 | |||
| e59e02e4fc | |||
| 9784310dc0 | |||
| 6e612df2e9 | |||
| 1fd8f4ee42 | |||
| 9a3d3bf038 | |||
| dfffb18de1 | |||
| ef35879dde | |||
| 24989c4cfa | |||
| a4e0a90adf | |||
| 196f10dc9f | |||
| 8190bbfdc0 | |||
| 6042f9b477 | |||
| 2ac205bf83 | |||
| 4ee70a8781 | |||
| 5d432cdcfc | |||
| cfb06009c9 | |||
| 7227c7109e | |||
| d9fb261232 | |||
| 41f815f54d | |||
| 6be7ba09a5 | |||
| 3b15fda470 | |||
| 4f98536f91 | |||
| 07ec6cb613 | |||
| c1eeb382f8 | |||
| b6769086c2 | |||
| 2879b697c0 | |||
| e57ff1be8d | |||
| 19bd8cca0d | |||
| ad9ab881dd | |||
| cf3c4b998f | |||
| 108814b8b7 | |||
| f4c3f9b511 | |||
| 12eddadc07 | |||
| 8749a7c164 | |||
| 75463dcebb | |||
| af1cecd3ce | |||
| 891b2ea6bf | |||
| 024ae04f45 | |||
| 4a4d0f634a | |||
| 42e6a4d386 | |||
| 9be56fb9a2 | |||
| 8e6b8829ea | |||
| c5f8288f39 | |||
| 2623d9dd61 | |||
| 6925614f49 | |||
| 322c53b086 | |||
| f79c0ad254 | |||
| 9263946c88 | |||
| 47006d76fe | |||
| e5a9a2ecba | |||
| dd2f7b0bd5 | |||
| ff5bd63eb0 | |||
| 9466b1d7e6 | |||
| 5a144a26f9 | |||
| 5e9e45da03 | |||
| 5516dfc3f5 | |||
| 7cbd7b6eeb | |||
| 5d644fa366 | |||
| bcbf5b35cf | |||
| 2582b9e208 | |||
| 5d31ac6e04 | |||
| ff3dec059c | |||
| 1e183f60ee | |||
| 0f9cc39cc7 | |||
| a66d6885e7 | |||
| e029ec5414 | |||
| 93f36faafe | |||
| 20df137eeb | |||
| a499d23149 | |||
| d60e9264e3 | |||
| 25b23e7ae0 | |||
| ba9bf4ef45 | |||
| d1e98fc4f9 | |||
| 03e3bb8118 | |||
| 6f5d7828db | |||
| 0d8505131e | |||
| 0075bdeb52 | |||
| 6df8ee8eb7 | |||
| 3a372b85c5 | |||
| ab67146c0f | |||
| 8c87451d80 | |||
| f3eabd74fc | |||
| 1b75547308 | |||
| 99ef5046db | |||
| aa3750bb68 | |||
| 83ad6340b2 | |||
| 81ce648b5d | |||
| 78b6211b94 | |||
| 69979ced1d | |||
| e2fdce10ef | |||
| f2f94a399b | |||
| a5eb6ca285 | |||
| 8d5504205e | |||
| 2b0d16aa0d | |||
| 0c5aa65092 | |||
| 85658bb3c6 | |||
| 4de0c64672 | |||
| a1c94d582d | |||
| 5dbf60eaa2 | |||
| d89cd2f0ca | |||
| 910ec94fd8 | |||
| 8dc460b507 | |||
| 2c5144aac0 | |||
| 74550f8907 | |||
| 7edd70c3c0 | |||
| 8edc8e697c | |||
| 93519e5f62 | |||
| 2daa04bb5f | |||
| d6dfdbc238 | |||
| 7970b552e9 | |||
| 485ffafc9a | |||
| 2c5325c507 | |||
| 3f9e5f887a | |||
| d1ce2a0740 | |||
| 2402097012 | |||
| 819614278f | |||
| 4ea34e0136 | |||
| c02b30409b | |||
| c877da1161 | |||
| 07dcc1804b | |||
| f2e1268398 | |||
| 6aacce23ca | |||
| 4dba8dc882 | |||
| b2e639697f | |||
| 5ad7d208b3 | |||
| d1a41bbcb7 | |||
| f27072db16 | |||
| ff3e83e9ee | |||
| 2ccc59b4fa | |||
| dc4a4925e3 | |||
| 5b53fbda0b | |||
| c8cbbcb84d | |||
| 598f4a5076 | |||
| dff8431e8b | |||
| 255a567e5c | |||
| 9d18d0733b | |||
| 9ac3fc7e35 | |||
| 7f2ae673d0 | |||
| c05609f85f | |||
| 4f088d1cdb | |||
| 592db2dbba | |||
| 0e36a850cf | |||
| dbf1985d25 | |||
| 3056a19d09 | |||
| b9f1822a65 | |||
| f9237d2dcf | |||
| 46aaa7cb4a | |||
| e9fd9c4e9a | |||
| 3df8d24e33 | |||
| deb12052b5 | |||
| 99024ee5ce | |||
| d25462177e | |||
| 8463993581 | |||
| 3ad7976e4b | |||
| 476f0f553c | |||
| f9abdd23c6 | |||
| 711db60a4c | |||
| 21697f01ca | |||
| 87471acf98 | |||
| 8c95782eff | |||
| abd7fc6bef | |||
| 614003a7cd | |||
| c5b65289d3 | |||
| 11e0b46034 | |||
| 024d34f0e4 | |||
| 7b0e8195ff | |||
| 87b41ab3cc | |||
| 63a55a8a0b | |||
| cf290732dc | |||
| a06a256c21 | |||
| 0f48b27a04 | |||
| 8702db568c | |||
| 0414c392bf | |||
| 07cea2e04a | |||
| 5f660702eb | |||
| d40922629b | |||
| 69a866bbbf | |||
| ad6fe0394f | |||
| 3838f7645d | |||
| 3dcb233c3f | |||
| 01368dd6f4 | |||
| c9152c90e6 | |||
| 5c742834ea | |||
| 8ac2776cca | |||
| 195490484c | |||
| d6ae1551ba | |||
| ef26e46ac9 | |||
| bb33572b19 | |||
| 17ef0b0a32 | |||
| 521507b8e3 | |||
| c11f2403d2 | |||
| d0bd722c92 | |||
| c43bafa21b | |||
| 3a0c892148 | |||
| 0effdbcf5e | |||
| 26295dd978 | |||
| ea56219fa1 | |||
| 9e2c0b2610 | |||
| bc2d09e14e | |||
| 168e7cd636 | |||
| a5dbde7fb5 | |||
| bd0416eede | |||
| 129baaacee | |||
| 2259c78730 | |||
| d7553f0392 | |||
| 488a032eba | |||
| 333bb408e1 | |||
| 44d335bc9f | |||
| 0654033721 | |||
| 6b54704d59 | |||
| 1166a925fe | |||
| e85a41d713 | |||
| f183985982 | |||
| 665fd301c6 | |||
| 1c09ae2fa8 | |||
| 06dcd0c2b7 | |||
| 5eaf1926c1 | |||
| ff56ec9fe3 | |||
| c2558fe0ec | |||
| c5017c83bd | |||
| 232327e89e | |||
| d21f3b0b83 | |||
| f36e1c4e4d | |||
| 3146e75ead | |||
| 2a6fbd4e32 | |||
| 971273a185 | |||
| 1e2a74f3ca | |||
| dcfb34c6fd | |||
| f9e1cf6691 | |||
| da2a88a3a6 | |||
| 2a941a4fc7 | |||
| 20dfd99ec0 | |||
| 5b30788cff | |||
| af2fe21d73 | |||
| 7da6f5cd0c | |||
| 6034246015 | |||
| c33390fa80 | |||
| be7a159815 | |||
| baf12f87a3 | |||
| 9ab5738cff | |||
| cb02fa98dd | |||
| 66391baeef | |||
| a6bc0727b2 | |||
| 3a65363ebb | |||
| 92ba880006 | |||
| e6b1b932f4 | |||
| d05c211a7c | |||
|
|
3b7d9a2a75 | ||
|
|
af55c5af9f | ||
|
|
d303ecfa38 | ||
|
|
12cb4e95f4 | ||
|
|
76c10e92a4 | ||
|
|
351cc5943a | ||
| 0697bede3e | |||
| 492ab72dcd | |||
| e126743d69 | |||
| f70fb20a70 | |||
| dcb67fba63 | |||
| 12a85ee804 | |||
| 72db1c92e7 | |||
| 3466f4956a | |||
| 5377d15313 | |||
| df4bcd9786 | |||
| 0d125071ef | |||
| 1a959862f1 | |||
| 156a87abc0 | |||
| 73eb04bcf0 | |||
| b0f81c59d4 | |||
| 2fbe7a327e | |||
| bcc598ebd5 | |||
| c460bb7bf5 | |||
| 0ec90b14c6 | |||
| 4b538cdea8 | |||
| a78973be29 | |||
| 4077431ef0 | |||
| faab83e037 | |||
| b8cbb9f758 | |||
| bd51d177b5 | |||
| 25d242b76c | |||
| 00688cb996 | |||
| 62aea5413e | |||
| 1dcebc4eca | |||
| 68e5c4cd2b | |||
| 3ed8c619b1 | |||
| fc456a41f2 | |||
| 4b21931ff0 | |||
| eeaff28b31 | |||
| a5111aa2fb | |||
| 0b2e61faef | |||
| 9d36e55227 | |||
| fb89ca5938 | |||
| ff98bfa79f | |||
| fdafbed237 | |||
| 167a6ae1cb | |||
| 45620ba4c2 | |||
| 3aadab40b0 | |||
| e53bcadc05 | |||
| 1110afa058 | |||
| 1e36eb8b2f | |||
| bd924150eb | |||
| 91663f55af | |||
| 4bf9262e79 | |||
| 1b6587de24 | |||
| 3c8f9e55b6 | |||
| e02e98f8f3 | |||
| 0047c48e72 | |||
| 1c18b797e0 | |||
| 627c28cd94 | |||
| 6d1ef0f51c | |||
| 8ae4d1c42f | |||
| 2e3b262a78 | |||
| 3f1f60030c | |||
| 72bb5acc0a | |||
| 4dbfffef7b | |||
| 59beafb314 | |||
| c18465d498 | |||
| c118035c33 | |||
| 5b558bcf00 | |||
| 992221a6da | |||
| e083da2f72 | |||
| 191c89f7ad | |||
| b8f573ce86 | |||
| bbaf0ed9d9 | |||
| 0c540a39eb | |||
| bd75157a79 | |||
| 1eef71923a | |||
| 3932bba83d | |||
| 5b2dc909e2 | |||
| 9d4c048f0e | |||
| 2bf88089b3 | |||
| ca41e4edee | |||
| 332aa90931 | |||
| e7d3e34c0b | |||
| db4cc9ce85 | |||
| 494ccb740b | |||
| 5edccf21cd | |||
| 1b852f255e | |||
| beba0a615f | |||
| 11d0fe8d1f | |||
| 6cc40be36a | |||
| db9077a85c | |||
| 57758cd018 | |||
| 24f527ab8a | |||
| 935b1666ac | |||
| 809d166a2d | |||
| fc902e1063 | |||
| 2aa1d6eeca | |||
| 5e2e03f5e9 | |||
| 0f9c8e0335 | |||
| 87428909b2 | |||
| 9a1a64c41c | |||
| 184714aeeb | |||
| bc135d00c5 | |||
| ea3f3b709d | |||
| 39b57119fe | |||
| e9910fe827 | |||
| a0737d91b9 | |||
| 76597280f5 | |||
| bc9d27aa94 | |||
| baf992bccb | |||
| ba88129580 | |||
| 55f87f7a67 | |||
| 4052969304 | |||
| eee1558dd9 | |||
| a0a2313924 | |||
| e630bc3d75 | |||
| 6329f44d42 | |||
| 11c8a56f14 | |||
| edc6ca9b7a | |||
| 838918da66 | |||
| 48e6ba7861 | |||
| 830dacd6f5 | |||
| b079f7891c | |||
| d6012dfffb | |||
| 6903c91df2 | |||
| 037f27c62c | |||
| b9182786bf | |||
| c36cd202e8 | |||
| 31b80a5b2a | |||
| 33bf5a0f34 | |||
| 73080d7d0d | |||
| 978f260c64 | |||
| 292fef7d12 | |||
| 68bb0e6b21 | |||
| 1ab94862d0 | |||
| de3c78b2ee | |||
| bb3e4547bb | |||
| 09d6acc65a | |||
| 92c7de942b | |||
| 8ed23ddc7a | |||
| d81f068eba | |||
| a543be0255 | |||
| b4057c1a2c | |||
| 3d59042802 | |||
| 9a9d5fcda4 | |||
| a1c6eadbe5 | |||
| 6ef91a92e5 | |||
| 9225038ffa | |||
| 993b83f8e7 | |||
| 762d3a5222 | |||
| a033f81f5f | |||
| ae7e1ede14 | |||
| 38d7cb00b6 | |||
| f97e114a81 | |||
| 38a0f4c9b5 | |||
| bfd7126e1e | |||
| 4d1dde4528 | |||
| 38606f28c7 | |||
| 8f7de926d3 | |||
| 6237f7755a | |||
| b84fe87f48 | |||
| 99975d9df4 | |||
| cd5a9d06ea | |||
| bf86e40db0 | |||
| 07a372ab79 | |||
| e6e6e6c206 | |||
| 9325419002 | |||
| 21590655cb | |||
| 4bd8d5f93e | |||
| ddd1773777 | |||
| 557b576da5 | |||
| 3f13d81eb3 | |||
| f17541e252 | |||
| 31d98285a4 | |||
| fb1d8f90ed | |||
| b31f009d2e | |||
| 76a4c09f37 | |||
| eefac93091 | |||
| f2089c4d96 | |||
| 41400a8710 | |||
| e362700031 | |||
| 7b2fdaf0ad | |||
| 070807b485 | |||
| def822cd45 | |||
| df5c9532cd | |||
| 1458c71cfa | |||
| 88ef2f64c0 | |||
| 17d983221d | |||
| 510e25e351 | |||
| 4a97b06520 | |||
| a15b285090 | |||
| 82ecd0d6dd | |||
| d03350f6b3 | |||
| ce46313dd1 | |||
| 410ab529ae | |||
| 8a93f4bdd1 | |||
| d0f588e47d | |||
| fad2534267 | |||
| e824f4982e | |||
| db210ebc5e | |||
| 2235470d9d | |||
| 2e8d28542e | |||
| 5a6b27ff18 | |||
| 31c079701f | |||
| 6148897dac | |||
| 963c6ff4f2 | |||
| dea178b7ba | |||
| 1ebcdd7687 | |||
| 2eb94c8ddb | |||
| 60192a0a02 | |||
| cc3892463a | |||
| 50ec3df2d6 | |||
| 4f6d4a82b0 | |||
| 7597fcfe5b | |||
| 7478051425 | |||
| da0e7facfd | |||
| c1c84ba3d1 | |||
| cb1fe0847b | |||
| cab95b7985 | |||
| 4fe641a9f5 | |||
| 17f51f5e7b | |||
| 318bc4bc4d | |||
| 544bbb745c | |||
| 416ad65c87 | |||
| 7240cbb414 | |||
| d4177f6228 | |||
| b8b1f14806 | |||
| 8e91e7edbe | |||
| 51815862f7 | |||
| 49933059f3 | |||
| 4550f653ea | |||
| 8fd2cd66c1 | |||
| 67f27d3d8b | |||
| 8d03a08717 | |||
| b1c4ebfe45 | |||
| ef4a738672 | |||
| b42016c74a | |||
| 78ce24f3f7 | |||
| 21e4b04c19 | |||
| b772a22705 | |||
| 8c2e8a19d1 | |||
| 31af092203 | |||
| 9dd376ba22 | |||
| 91182b1877 | |||
| 1c879fe50e | |||
| da29071ad1 | |||
| 3f80b89a4c | |||
| 09d1a397c0 | |||
| 0cde350c5e | |||
| e1d1a8d1b1 | |||
| 10fe40e4a8 | |||
| 37310e41f5 | |||
| 80d06f237c | |||
| f3a484fb67 | |||
| 1bd403cd8c | |||
| 0b4e8a233c | |||
| 119280d814 | |||
| 63931f73ba | |||
| c57b612205 | |||
| f0621fa191 | |||
| b6782962f1 | |||
| 863070c037 | |||
| 6a4868b9b3 | |||
| 27ca960b2a | |||
| 2e718b22b6 | |||
| 4b4c6881c7 | |||
| 3b320469b5 | |||
| 5a0b81ba32 | |||
| ac27893a01 | |||
| 234e0460d8 | |||
| 19e73dcaa1 | |||
| d1c5a545d9 | |||
| 220c26d9c5 | |||
| 7fe35c5f1c | |||
| e76d055bdb | |||
| 25bf34e82c | |||
| 22e8937879 | |||
| 1054dd7086 | |||
| 0e30259b7e | |||
| 5c5f7c4a9e | |||
| 9f78b6bd79 | |||
| 636ab65990 | |||
| 384b6cd212 | |||
| 1caa5784dd | |||
| 10bd687ecc | |||
| 7367e7cc5d | |||
| 9894a91948 | |||
| 5fa19223a9 | |||
| aa7afe1a6a | |||
| 018282d798 | |||
| e0a1aeb053 | |||
| 535a8b91de | |||
| c137969b6a | |||
| 347f317dd9 | |||
| 017adfb2b1 | |||
| 5e74b3f7ce | |||
| 29b8244796 | |||
| 3a6df40e07 | |||
| 9f4ea89c51 |
600 changed files with 63525 additions and 5781 deletions
33
.dockerignore
Normal file
33
.dockerignore
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
admin/admin
|
||||
checker/checker
|
||||
dashboard/dashboard
|
||||
evdist/evdist
|
||||
generator/generator
|
||||
receiver/receiver
|
||||
repochecker/repochecker
|
||||
frontend/fic/build
|
||||
frontend/fic/node_modules
|
||||
qa/ui/build
|
||||
qa/ui/node_modules
|
||||
fickit-backend-initrd.img
|
||||
fickit-backend-kernel
|
||||
fickit-backend-squashfs.img
|
||||
fickit-backend-state
|
||||
fickit-frontend-initrd.img
|
||||
fickit-frontend-kernel
|
||||
fickit-frontend-squashfs.img
|
||||
fickit-frontend-state
|
||||
fickit-prepare-initrd.img
|
||||
fickit-prepare-kernel
|
||||
fickit-update-initrd.img
|
||||
fickit-update-kernel
|
||||
DASHBOARD
|
||||
FILES
|
||||
PKI
|
||||
REMOTE
|
||||
repochecker/*.so
|
||||
SETTINGS
|
||||
SETTINGSDIST
|
||||
submissions
|
||||
TEAMS
|
||||
vendor
|
||||
22
.drone-manifest-fic-admin.yml
Normal file
22
.drone-manifest-fic-admin.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-admin:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-admin:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-admin:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-admin:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fic-checker.yml
Normal file
22
.drone-manifest-fic-checker.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fic-dashboard.yml
Normal file
22
.drone-manifest-fic-dashboard.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-dashboard:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-dashboard:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-dashboard:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-dashboard:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fic-evdist.yml
Normal file
22
.drone-manifest-fic-evdist.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-evdist:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-evdist:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-evdist:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-evdist:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fic-frontend-ui.yml
Normal file
22
.drone-manifest-fic-frontend-ui.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-frontend-ui:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-frontend-ui:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-frontend-ui:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-frontend-ui:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fic-generator.yml
Normal file
22
.drone-manifest-fic-generator.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fic-get-remote-files.yml
Normal file
22
.drone-manifest-fic-get-remote-files.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-get-remote-files:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-get-remote-files:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-get-remote-files:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-get-remote-files:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fic-nginx.yml
Normal file
22
.drone-manifest-fic-nginx.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-nginx:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-nginx:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-nginx:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-nginx:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fic-qa.yml
Normal file
22
.drone-manifest-fic-qa.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-qa:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-qa:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-qa:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-qa:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fic-receiver.yml
Normal file
22
.drone-manifest-fic-receiver.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-receiver:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-receiver:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-receiver:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-receiver:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fic-repochecker.yml
Normal file
22
.drone-manifest-fic-repochecker.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fic-repochecker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fic-repochecker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fic-repochecker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fic-repochecker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
22
.drone-manifest-fickit-deploy.yml
Normal file
22
.drone-manifest-fickit-deploy.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
image: nemunaire/fickit-deploy:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: nemunaire/fickit-deploy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: nemunaire/fickit-deploy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
- image: nemunaire/fickit-deploy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
||||
816
.drone.yml
Normal file
816
.drone.yml
Normal file
|
|
@ -0,0 +1,816 @@
|
|||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-amd64
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/srs.epita.fr/fic-server
|
||||
|
||||
steps:
|
||||
- name: get deps
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- apk --no-cache add git
|
||||
- go get -v -d ./...
|
||||
- mkdir deploy
|
||||
|
||||
- name: build qa ui
|
||||
image: node:23-alpine
|
||||
commands:
|
||||
- cd qa/ui
|
||||
- npm install --network-timeout=100000
|
||||
- npm run build
|
||||
- tar chjf ../../deploy/htdocs-qa.tar.bz2 build
|
||||
|
||||
- name: vet and tests
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- apk --no-cache add build-base
|
||||
- go vet -buildvcs=false -tags gitgo ./...
|
||||
- go vet -buildvcs=false ./...
|
||||
- go test ./...
|
||||
|
||||
- name: build admin
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -tags gitgo -o deploy/admin-gitgo-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin
|
||||
- go build -buildvcs=false -o deploy/admin-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin
|
||||
- tar chjf deploy/htdocs-admin.tar.bz2 htdocs-admin
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build checker
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/checker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/checker
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build evdist
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/evdist-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/evdist
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build generator
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/generator-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/generator
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build receiver
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/receiver-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/receiver
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build frontend fic ui
|
||||
image: node:23-alpine
|
||||
commands:
|
||||
- cd frontend/fic
|
||||
- npm install --network-timeout=100000
|
||||
- npm run build
|
||||
- tar chjf ../../deploy/htdocs-frontend-fic.tar.bz2 build
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build dashboard
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard
|
||||
- tar chjf deploy/htdocs-dashboard.tar.bz2 htdocs-dashboard
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build repochecker
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- apk --no-cache add build-base
|
||||
- go build -buildvcs=false --tags checkupdate -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker
|
||||
- go build -buildvcs=false -buildmode=plugin -o deploy/repochecker-epita-rules-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.so srs.epita.fr/fic-server/repochecker/epita
|
||||
- go build -buildvcs=false -buildmode=plugin -o deploy/repochecker-file-inspector-rules-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.so srs.epita.fr/fic-server/repochecker/file-inspector
|
||||
- go build -buildvcs=false -buildmode=plugin -o deploy/repochecker-grammalecte-rules-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.so srs.epita.fr/fic-server/repochecker/grammalecte
|
||||
- go build -buildvcs=false -buildmode=plugin -o deploy/repochecker-pcap-inspector-rules-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.so srs.epita.fr/fic-server/repochecker/pcap-inspector
|
||||
- go build -buildvcs=false -buildmode=plugin -o deploy/repochecker-videos-rules-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.so srs.epita.fr/fic-server/repochecker/videos
|
||||
- grep "const version" repochecker/update.go | sed -r 's/^.*=\s*(\S.*)$/\1/' > deploy/repochecker.version
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build qa
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/qa-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/qa
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: docker admin
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-admin
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-admin
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker checker
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-checker
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-checker
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker evdist
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-evdist
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-evdist
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker generator
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-generator
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-generator
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker receiver
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-receiver
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-receiver
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker frontend nginx
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-nginx
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-nginx
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker frontend ui
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-frontend-ui
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-frontend-ui
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker dashboard
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-dashboard
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-dashboard
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker qa
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-qa
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-qa
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker repochecker
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-repochecker
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-repochecker
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker remote-scores-sync-zqds
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-remote-scores-sync-zqds
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-remote-scores-sync-zqds
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker remote-challenge-sync-airbus
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-remote-challenge-sync-airbus
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-remote-challenge-sync-airbus
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker fic-get-remote-files
|
||||
failure: ignore
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-get-remote-files
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-get-remote-files
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker fickit-deploy
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fickit-deploy
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-deploy
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- cron
|
||||
- push
|
||||
- tag
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-arm64
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/srs.epita.fr/fic-server
|
||||
|
||||
steps:
|
||||
- name: get deps
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- apk --no-cache add git
|
||||
- go get -d ./...
|
||||
- mkdir deploy
|
||||
|
||||
- name: build admin
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- apk --no-cache add build-base
|
||||
- go build -buildvcs=false -o deploy/admin-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build checker
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/checker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/checker
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build evdist
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/evdist-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/evdist
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build generator
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/generator-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/generator
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build receiver
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/receiver-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/receiver
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build frontend fic ui
|
||||
image: node:23-alpine
|
||||
commands:
|
||||
- cd frontend/fic
|
||||
- npm install --network-timeout=100000
|
||||
- npm run build
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build dashboard
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build repochecker
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- apk --no-cache add build-base
|
||||
- go build -buildvcs=false --tags checkupdate -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build repochecker for macOS
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- apk --no-cache add build-base
|
||||
- go build -buildvcs=false --tags checkupdate -o deploy/repochecker-darwin-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
GOOS: darwin
|
||||
GOARCH: arm64
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build qa ui
|
||||
image: node:23-alpine
|
||||
commands:
|
||||
- cd qa/ui
|
||||
- npm install --network-timeout=100000
|
||||
- npm run build
|
||||
- tar chjf ../../deploy/htdocs-qa.tar.bz2 build
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: build qa
|
||||
image: golang:alpine
|
||||
commands:
|
||||
- go build -buildvcs=false -o deploy/qa-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/qa
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
when:
|
||||
branch:
|
||||
exclude:
|
||||
- master
|
||||
|
||||
- name: docker admin
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-admin
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-admin
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker checker
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-checker
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-checker
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker evdist
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-evdist
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-evdist
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker generator
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-generator
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-generator
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker fic-get-remote-files
|
||||
failure: ignore
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-get-remote-files
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-get-remote-files
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker receiver
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-receiver
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-receiver
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker frontend nginx
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-nginx
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-nginx
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker dashboard
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-dashboard
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-dashboard
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker qa
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-qa
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-qa
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: docker repochecker
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-repochecker
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-repochecker
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- cron
|
||||
- push
|
||||
- tag
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: docker-manifest
|
||||
steps:
|
||||
- name: publish admin
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fic-admin.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: publish checker
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fic-checker.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: publish evdist
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fic-evdist.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: publish generator
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fic-generator.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: publish receiver
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fic-receiver.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: publish frontend nginx
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fic-nginx.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: publish frontend ui
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fic-frontend-ui.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: publish dashboard
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fic-dashboard.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: publish repochecker
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fic-repochecker.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: publish qa
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fic-qa.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: docker fic-get-remote-files
|
||||
failure: ignore
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: nemunaire/fic-get-remote-files
|
||||
auto_tag: true
|
||||
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
dockerfile: Dockerfile-get-remote-files
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
- name: publish fickit-deploy
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: .drone-manifest-fickit-deploy.yml
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- tag
|
||||
|
||||
depends_on:
|
||||
- build-amd64
|
||||
- build-arm64
|
||||
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
vendor/
|
||||
DASHBOARD/
|
||||
FILES/
|
||||
PKI/
|
||||
REMOTE/
|
||||
SETTINGS/
|
||||
SETTINGSDIST/
|
||||
TEAMS/
|
||||
submissions/
|
||||
admin/sync/README.html
|
||||
fickit-boot-cmdline
|
||||
fickit-boot-initrd.img
|
||||
fickit-boot-kernel
|
||||
fickit-backend-cmdline
|
||||
fickit-backend-initrd.img
|
||||
fickit-backend-squashfs.img
|
||||
fickit-backend-kernel
|
||||
fickit-backend-state
|
||||
fickit-frontend-cmdline
|
||||
fickit-frontend-initrd.img
|
||||
fickit-frontend-squashfs.img
|
||||
fickit-frontend-kernel
|
||||
fickit-frontend-state
|
||||
fickit-prepare-bios.img
|
||||
fickit-prepare-cmdline
|
||||
fickit-prepare-initrd.img
|
||||
fickit-prepare-kernel
|
||||
fickit-prepare-state
|
||||
fickit-update-cmdline
|
||||
fickit-update-initrd.img
|
||||
fickit-update-kernel
|
||||
fickit-update-squashfs.img
|
||||
result
|
||||
started
|
||||
|
||||
# Standalone binaries
|
||||
admin/get-remote-files/get-remote-files
|
||||
fic-admin
|
||||
fic-backend
|
||||
fic-dashboard
|
||||
fic-frontend
|
||||
fic-qa
|
||||
fic-repochecker
|
||||
122
.gitlab-ci.yml
Normal file
122
.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
---
|
||||
|
||||
stages:
|
||||
- deps
|
||||
- build
|
||||
- fickit
|
||||
- sast
|
||||
- qa
|
||||
- image
|
||||
- container_scanning
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .go/pkg/mod/
|
||||
- qa/ui/node_modules/
|
||||
- frontend/ui/node_modules/
|
||||
|
||||
include:
|
||||
- '.gitlab-ci/build.yml'
|
||||
- '.gitlab-ci/image.yml'
|
||||
- template: SAST.gitlab-ci.yml
|
||||
- template: Security/Dependency-Scanning.gitlab-ci.yml
|
||||
- template: Security/Secret-Detection.gitlab-ci.yml
|
||||
- template: Security/Container-Scanning.gitlab-ci.yml
|
||||
|
||||
.scanners-matrix:
|
||||
parallel:
|
||||
matrix:
|
||||
- IMAGE_NAME: [checker, admin, evdist, frontend-ui, nginx, dashboard, repochecker, qa, receiver, generator, remote-challenge-sync-airbus]
|
||||
|
||||
container_scanning:
|
||||
stage: container_scanning
|
||||
extends:
|
||||
- .scanners-matrix
|
||||
variables:
|
||||
DOCKER_SERVICE: localhost
|
||||
DOCKERFILE_PATH: Dockerfile-${IMAGE_NAME}
|
||||
CI_APPLICATION_REPOSITORY: ${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}/${IMAGE_NAME}
|
||||
CI_APPLICATION_TAG: latest
|
||||
GIT_STRATEGY: fetch
|
||||
before_script:
|
||||
- 'echo "Scanning: ${IMAGE_NAME}"'
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "master"'
|
||||
|
||||
sast:
|
||||
stage: sast
|
||||
interruptible: true
|
||||
needs: []
|
||||
before_script:
|
||||
- rm -rf .go/
|
||||
|
||||
secret_detection:
|
||||
stage: sast
|
||||
interruptible: true
|
||||
needs: []
|
||||
|
||||
dependency_scanning:
|
||||
stage: qa
|
||||
interruptible: true
|
||||
needs: []
|
||||
|
||||
get-deps:
|
||||
stage: deps
|
||||
image: golang:1-alpine
|
||||
before_script:
|
||||
- export GOPATH="$CI_PROJECT_DIR/.go"
|
||||
- mkdir -p .go
|
||||
script:
|
||||
- apk --no-cache add git
|
||||
- go get -v -d ./...
|
||||
|
||||
vet:
|
||||
stage: sast
|
||||
needs: ["build-qa-ui"]
|
||||
dependencies:
|
||||
- build-qa-ui
|
||||
image: golang:1-alpine
|
||||
before_script:
|
||||
- export GOPATH="$CI_PROJECT_DIR/.go"
|
||||
- mkdir -p .go
|
||||
script:
|
||||
- apk --no-cache add build-base
|
||||
- go vet -v -buildvcs=false -tags gitgo ./...
|
||||
- go vet -v -buildvcs=false ./...
|
||||
|
||||
fickit:
|
||||
stage: fickit
|
||||
interruptible: true
|
||||
needs: ["build-admin","build-checker","build-dashboard","build-evdist","build-generator","build-qa","build-receiver","build-repochecker"]
|
||||
image: nemunaire/linuxkit
|
||||
tags: ['docker']
|
||||
before_script:
|
||||
- mkdir -p ~/.docker
|
||||
- echo "{\"auths\":{\"${CI_REGISTRY}\":{\"username\":\"${CI_REGISTRY_USER}\",\"password\":\"${CI_REGISTRY_PASSWORD}\"}}}" > ~/.docker/config.json
|
||||
script:
|
||||
- dockerd & sleep 5
|
||||
|
||||
- linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/boot/
|
||||
- linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/kexec/
|
||||
- linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/mariadb-client/
|
||||
- linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/mdadm/
|
||||
- linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/rsync/
|
||||
- linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/syslinux/
|
||||
- linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/unbound/
|
||||
|
||||
- sed -i "s@nemunaire/fic-@${CI_REGISTRY_IMAGE}/master/@;s@nemunaire/@${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}/@" fickit-backend.yml fickit-boot.yml fickit-frontend.yml fickit-prepare.yml fickit-update.yml
|
||||
|
||||
- linuxkit build -format kernel+squashfs fickit-backend.yml
|
||||
- linuxkit build -format kernel+squashfs fickit-frontend.yml
|
||||
- linuxkit build -format kernel+initrd fickit-boot.yml
|
||||
- linuxkit build -format kernel+initrd fickit-prepare.yml
|
||||
- linuxkit build -format kernel+initrd fickit-update.yml
|
||||
artifacts:
|
||||
expire_in: 8 hours
|
||||
paths:
|
||||
- fickit-backend-squashfs.img
|
||||
- fickit-frontend-squashfs.img
|
||||
- fickit-boot-kernel
|
||||
- fickit-boot-initrd.img
|
||||
- fickit-prepare-initrd.img
|
||||
- fickit-update-initrd.img
|
||||
93
.gitlab-ci/build.yml
Normal file
93
.gitlab-ci/build.yml
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
|
||||
.build:
|
||||
stage: build
|
||||
image: golang:1-alpine
|
||||
before_script:
|
||||
- export GOPATH="$CI_PROJECT_DIR/.go"
|
||||
- mkdir -p .go
|
||||
variables:
|
||||
CGO_ENABLED: 0
|
||||
|
||||
build-qa-ui:
|
||||
stage: build
|
||||
image: node:21-alpine
|
||||
before_script:
|
||||
script:
|
||||
- cd qa/ui
|
||||
- npm install --network-timeout=100000
|
||||
- npm run build
|
||||
artifacts:
|
||||
paths:
|
||||
- qa/ui/build/
|
||||
when: on_success
|
||||
|
||||
build-checker:
|
||||
extends:
|
||||
- .build
|
||||
script:
|
||||
- go build -v -buildvcs=false -o deploy/checker srs.epita.fr/fic-server/checker
|
||||
|
||||
build-generator:
|
||||
extends:
|
||||
- .build
|
||||
script:
|
||||
- go build -v -buildvcs=false -o deploy/generator srs.epita.fr/fic-server/generator
|
||||
|
||||
build-receiver:
|
||||
extends:
|
||||
- .build
|
||||
script:
|
||||
- go build -v -buildvcs=false -o deploy/receiver srs.epita.fr/fic-server/receiver
|
||||
|
||||
build-admin:
|
||||
extends:
|
||||
- .build
|
||||
script:
|
||||
- go build -v -buildvcs=false -tags gitgo -o deploy/admin-gitgo srs.epita.fr/fic-server/admin
|
||||
- go build -v -buildvcs=false -o deploy/admin srs.epita.fr/fic-server/admin
|
||||
|
||||
build-evdist:
|
||||
extends:
|
||||
- .build
|
||||
script:
|
||||
- go build -v -buildvcs=false -o deploy/evdist srs.epita.fr/fic-server/evdist
|
||||
|
||||
build-frontend-ui:
|
||||
stage: build
|
||||
image: node:21-alpine
|
||||
before_script:
|
||||
script:
|
||||
- cd frontend/fic
|
||||
- npm install --network-timeout=100000
|
||||
- npm run build
|
||||
|
||||
build-dashboard:
|
||||
extends:
|
||||
- .build
|
||||
script:
|
||||
- go build -v -buildvcs=false -o deploy/dashboard srs.epita.fr/fic-server/dashboard
|
||||
|
||||
build-repochecker:
|
||||
extends:
|
||||
- .build
|
||||
variables:
|
||||
CGO_ENABLED: 1
|
||||
script:
|
||||
- apk --no-cache add build-base
|
||||
- go build -buildvcs=false --tags checkupdate -v -o deploy/repochecker srs.epita.fr/fic-server/repochecker
|
||||
- go build -buildvcs=false -buildmode=plugin -v -o deploy/repochecker-epita-rules.so srs.epita.fr/fic-server/repochecker/epita
|
||||
- go build -buildvcs=false -buildmode=plugin -v -o deploy/repochecker-file-inspector-rules.so srs.epita.fr/fic-server/repochecker/file-inspector
|
||||
- go build -buildvcs=false -buildmode=plugin -v -o deploy/repochecker-grammalecte-rules.so srs.epita.fr/fic-server/repochecker/grammalecte
|
||||
- go build -buildvcs=false -buildmode=plugin -v -o deploy/repochecker-pcap-inspector-rules.so srs.epita.fr/fic-server/repochecker/pcap-inspector
|
||||
- go build -buildvcs=false -buildmode=plugin -v -o deploy/repochecker-videos-rules.so srs.epita.fr/fic-server/repochecker/videos
|
||||
- grep "const version" repochecker/update.go | sed -r 's/^.*=\s*(\S.*)$/\1/' > deploy/repochecker.version
|
||||
|
||||
build-qa:
|
||||
extends:
|
||||
- .build
|
||||
needs: ["build-qa-ui"]
|
||||
dependencies:
|
||||
- build-qa-ui
|
||||
script:
|
||||
- go build -v -buildvcs=false -o deploy/qa srs.epita.fr/fic-server/qa
|
||||
99
.gitlab-ci/image.yml
Normal file
99
.gitlab-ci/image.yml
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
|
||||
.push:
|
||||
stage: image
|
||||
interruptible: true
|
||||
needs: []
|
||||
image:
|
||||
name: gcr.io/kaniko-project/executor:v1.9.0-debug
|
||||
entrypoint: [""]
|
||||
before_script:
|
||||
- mkdir -p /kaniko/.docker
|
||||
- echo "{\"auths\":{\"${CI_REGISTRY}\":{\"username\":\"${CI_REGISTRY_USER}\",\"password\":\"${CI_REGISTRY_PASSWORD}\"}}}" > /kaniko/.docker/config.json
|
||||
script:
|
||||
- |
|
||||
/kaniko/executor \
|
||||
--context . \
|
||||
--dockerfile "${DOCKERFILE}" \
|
||||
--destination "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}/${CI_JOB_NAME}:${CI_COMMIT_SHA}" \
|
||||
--destination "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}/${CI_JOB_NAME}:latest"
|
||||
only:
|
||||
- master
|
||||
|
||||
checker:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-checker
|
||||
|
||||
receiver:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-receiver
|
||||
|
||||
generator:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-generator
|
||||
|
||||
admin:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-admin
|
||||
|
||||
fickit-deploy:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-deploy
|
||||
|
||||
get-remote-files:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-get-remote-files
|
||||
|
||||
evdist:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-evdist
|
||||
|
||||
frontend-ui:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-frontend-ui
|
||||
|
||||
nginx:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-nginx
|
||||
|
||||
dashboard:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-dashboard
|
||||
|
||||
repochecker:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-repochecker
|
||||
|
||||
qa:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-qa
|
||||
|
||||
remote-challenge-sync-airbus:
|
||||
extends:
|
||||
- .push
|
||||
variables:
|
||||
DOCKERFILE: Dockerfile-remote-challenge-sync-airbus
|
||||
42
Dockerfile-admin
Normal file
42
Dockerfile-admin
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
RUN apk add --no-cache binutils-gold build-base
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY settings settings/
|
||||
COPY libfic ./libfic/
|
||||
COPY admin ./admin/
|
||||
COPY repochecker ./repochecker/
|
||||
|
||||
RUN go get -d -v ./admin && \
|
||||
go build -v -o admin/admin ./admin && \
|
||||
go build -v -buildmode=plugin -o repochecker/epita-rules.so ./repochecker/epita && \
|
||||
go build -v -buildmode=plugin -o repochecker/file-inspector.so ./repochecker/file-inspector && \
|
||||
go build -v -buildmode=plugin -o repochecker/grammalecte-rules.so ./repochecker/grammalecte && \
|
||||
go build -v -buildmode=plugin -o repochecker/videos-rules.so ./repochecker/videos
|
||||
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates \
|
||||
git \
|
||||
git-lfs \
|
||||
openssh-client-default \
|
||||
openssl
|
||||
|
||||
EXPOSE 8081
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
ENTRYPOINT ["/srv/admin", "-bind=:8081", "-baseurl=/admin/"]
|
||||
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/admin/admin /srv/admin
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/epita-rules.so /srv/epita-rules.so
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/file-inspector.so /usr/lib/file-inspector.so
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/grammalecte-rules.so /usr/lib/grammalecte-rules.so
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/videos-rules.so /usr/lib/videos-rules.so
|
||||
22
Dockerfile-checker
Normal file
22
Dockerfile-checker
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY settings settings/
|
||||
COPY libfic ./libfic/
|
||||
COPY checker ./checker/
|
||||
|
||||
RUN go get -d -v ./checker && \
|
||||
go build -v -buildvcs=false -o checker/checker ./checker
|
||||
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
ENTRYPOINT ["/srv/checker"]
|
||||
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/checker/checker /srv/checker
|
||||
32
Dockerfile-dashboard
Normal file
32
Dockerfile-dashboard
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY settings settings/
|
||||
COPY libfic ./libfic/
|
||||
COPY dashboard ./dashboard/
|
||||
|
||||
RUN go get -d -v ./dashboard && \
|
||||
go build -v -buildvcs=false -o dashboard/dashboard ./dashboard
|
||||
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
EXPOSE 8082
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
ENTRYPOINT ["/srv/dashboard", "--bind=:8082"]
|
||||
|
||||
VOLUME /srv/htdocs-dashboard/
|
||||
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/dashboard/dashboard /srv/dashboard
|
||||
COPY dashboard/static/index.html /srv/htdocs-dashboard/
|
||||
COPY admin/static/css/bootstrap.min.css dashboard/static/css/fic.css admin/static/css/glyphicon.css /srv/htdocs-dashboard/css/
|
||||
COPY admin/static/fonts /srv/htdocs-dashboard/fonts
|
||||
COPY dashboard/static/img/srs.png /srv/htdocs-dashboard/img/
|
||||
COPY dashboard/static/js/dashboard.js admin/static/js/angular.min.js dashboard/static/js/angular-animate.min.js admin/static/js/angular-route.min.js admin/static/js/angular-sanitize.min.js admin/static/js/bootstrap.min.js admin/static/js/common.js admin/static/js/d3.v3.min.js admin/static/js/jquery.min.js /srv/htdocs-dashboard/js/
|
||||
COPY admin/static/js/i18n/* /srv/htdocs-dashboard/js/i18n/
|
||||
24
Dockerfile-deploy
Normal file
24
Dockerfile-deploy
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
FROM alpine:3.21
|
||||
|
||||
EXPOSE 67/udp
|
||||
EXPOSE 69/udp
|
||||
EXPOSE 80/tcp
|
||||
|
||||
ENTRYPOINT ["/usr/sbin/initial-config.sh"]
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||
|
||||
WORKDIR /srv/s
|
||||
|
||||
RUN apk add --no-cache \
|
||||
busybox-extras \
|
||||
supervisor \
|
||||
syslinux \
|
||||
tftp-hpa
|
||||
|
||||
RUN touch /var/lib/udhcpd/udhcpd.leases && \
|
||||
mv /usr/share/syslinux/* /srv
|
||||
|
||||
COPY configs/deploy-initial-config.sh /usr/sbin/initial-config.sh
|
||||
COPY configs/deploy-supervisord.conf /etc/supervisord.conf
|
||||
COPY configs/udhcpd-sample.conf /etc/udhcpd.conf
|
||||
COPY configs/pxelinux.cfg /srv/pxelinux.cfg/default
|
||||
21
Dockerfile-evdist
Normal file
21
Dockerfile-evdist
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY settings settings/
|
||||
COPY evdist ./evdist/
|
||||
|
||||
RUN go get -d -v ./evdist && \
|
||||
go build -v -buildvcs=false -o evdist/evdist ./evdist
|
||||
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
ENTRYPOINT ["/srv/evdist"]
|
||||
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/evdist/evdist /srv/evdist
|
||||
13
Dockerfile-frontend-ui
Normal file
13
Dockerfile-frontend-ui
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
FROM node:23-alpine AS nodebuild
|
||||
|
||||
WORKDIR /ui
|
||||
|
||||
COPY frontend/fic/ .
|
||||
|
||||
RUN npm install --network-timeout=100000 && \
|
||||
npm run build
|
||||
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=nodebuild /ui/build/ /www/htdocs-frontend
|
||||
22
Dockerfile-generator
Normal file
22
Dockerfile-generator
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY settings settings/
|
||||
COPY libfic ./libfic/
|
||||
COPY generator ./generator/
|
||||
|
||||
RUN go get -d -v ./generator && \
|
||||
go build -v -buildvcs=false -o generator/generator ./generator
|
||||
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
ENTRYPOINT ["/srv/generator"]
|
||||
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/generator/generator /srv/generator
|
||||
27
Dockerfile-get-remote-files
Normal file
27
Dockerfile-get-remote-files
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
RUN apk add --no-cache build-base
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY settings settings/
|
||||
COPY libfic ./libfic/
|
||||
COPY admin ./admin/
|
||||
|
||||
RUN go get -d -v ./admin && \
|
||||
go build -v -o get-remote-files ./admin/get-remote-files
|
||||
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
ENTRYPOINT ["/srv/get-remote-files", "/mnt/fic/"]
|
||||
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/get-remote-files /srv/get-remote-files
|
||||
32
Dockerfile-nginx
Normal file
32
Dockerfile-nginx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
FROM node:23-alpine AS nodebuild
|
||||
|
||||
WORKDIR /ui
|
||||
|
||||
COPY frontend/fic/ .
|
||||
|
||||
RUN npm install --network-timeout=100000 && \
|
||||
npm run build
|
||||
|
||||
|
||||
FROM nginx:stable-alpine-slim
|
||||
|
||||
ENV FIC_BASEURL /
|
||||
ENV HOST_RECEIVER receiver:8080
|
||||
ENV HOST_ADMIN admin:8081
|
||||
ENV HOST_DASHBOARD dashboard:8082
|
||||
ENV HOST_QA qa:8083
|
||||
ENV PATH_FILES /srv/FILES
|
||||
ENV PATH_STARTINGBLOCK /srv/STARTINGBLOCK
|
||||
ENV PATH_STATIC /srv/htdocs-frontend
|
||||
ENV PATH_SETTINGS /srv/SETTINGSDIST
|
||||
ENV PATH_TEAMS /srv/TEAMS
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
COPY configs/nginx-chbase.sh /docker-entrypoint.d/40-update-baseurl.sh
|
||||
|
||||
COPY configs/nginx/get-team/upstream.conf /etc/nginx/fic-get-team.conf
|
||||
COPY configs/nginx/auth/none.conf /etc/nginx/fic-auth.conf
|
||||
COPY configs/nginx/base/docker.conf /etc/nginx/templates/default.conf.template
|
||||
|
||||
COPY --from=nodebuild /ui/build/ /srv/htdocs-frontend
|
||||
38
Dockerfile-qa
Normal file
38
Dockerfile-qa
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
FROM node:23-alpine AS nodebuild
|
||||
|
||||
WORKDIR /ui
|
||||
|
||||
COPY qa/ui/ .
|
||||
|
||||
RUN npm install --network-timeout=100000 && \
|
||||
npm run build
|
||||
|
||||
|
||||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY settings settings/
|
||||
COPY libfic ./libfic/
|
||||
COPY --from=nodebuild /ui ./qa/ui
|
||||
COPY qa ./qa/
|
||||
COPY admin ./admin/
|
||||
|
||||
RUN go get -d -v ./qa && \
|
||||
go build -v -buildvcs=false -o qa/qa ./qa
|
||||
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
EXPOSE 8083
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
ENTRYPOINT ["/srv/qa", "--bind=:8083"]
|
||||
|
||||
VOLUME /srv/htdocs-qa/
|
||||
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/qa/qa /srv/qa
|
||||
27
Dockerfile-receiver
Normal file
27
Dockerfile-receiver
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY settings settings/
|
||||
COPY libfic ./libfic/
|
||||
COPY receiver ./receiver/
|
||||
|
||||
RUN go get -d -v ./receiver && \
|
||||
go build -v -buildvcs=false -o ./receiver/receiver ./receiver
|
||||
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
ENTRYPOINT ["/usr/sbin/entrypoint.sh"]
|
||||
CMD ["--bind=:8080"]
|
||||
|
||||
COPY entrypoint-receiver.sh /usr/sbin/entrypoint.sh
|
||||
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/receiver/receiver /srv/receiver
|
||||
24
Dockerfile-remote-challenge-sync-airbus
Normal file
24
Dockerfile-remote-challenge-sync-airbus
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY libfic ./libfic/
|
||||
COPY settings ./settings/
|
||||
COPY remote/challenge-sync-airbus ./remote/challenge-sync-airbus/
|
||||
|
||||
RUN go get -d -v ./remote/challenge-sync-airbus && \
|
||||
go build -v -buildvcs=false -o ./challenge-sync-airbus ./remote/challenge-sync-airbus
|
||||
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk add --no-cache openssl ca-certificates
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
ENTRYPOINT ["/srv/challenge-sync-airbus"]
|
||||
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/challenge-sync-airbus /srv/challenge-sync-airbus
|
||||
24
Dockerfile-remote-scores-sync-zqds
Normal file
24
Dockerfile-remote-scores-sync-zqds
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY libfic ./libfic/
|
||||
COPY settings ./settings/
|
||||
COPY remote/scores-sync-zqds ./remote/scores-sync-zqds/
|
||||
|
||||
RUN go get -d -v ./remote/scores-sync-zqds && \
|
||||
go build -v -buildvcs=false -o ./scores-sync-zqds ./remote/scores-sync-zqds
|
||||
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk add --no-cache openssl ca-certificates
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
ENTRYPOINT ["/srv/scores-sync-zqds"]
|
||||
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/scores-sync-zqds /srv/scores-sync-zqds
|
||||
42
Dockerfile-repochecker
Normal file
42
Dockerfile-repochecker
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
FROM golang:1-alpine AS gobuild
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
WORKDIR /go/src/srs.epita.fr/fic-server/
|
||||
|
||||
RUN apk add --no-cache binutils-gold build-base
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY settings settings/
|
||||
COPY libfic ./libfic/
|
||||
COPY admin ./admin/
|
||||
COPY repochecker ./repochecker/
|
||||
|
||||
RUN go get -d -v ./repochecker && \
|
||||
go build -v -o repochecker/repochecker ./repochecker && \
|
||||
go build -v -buildmode=plugin -o repochecker/epita-rules.so ./repochecker/epita && \
|
||||
go build -v -buildmode=plugin -o repochecker/file-inspector.so ./repochecker/file-inspector && \
|
||||
go build -v -buildmode=plugin -o repochecker/grammalecte-rules.so ./repochecker/grammalecte && \
|
||||
go build -v -buildmode=plugin -o repochecker/pcap-inspector.so ./repochecker/pcap-inspector && \
|
||||
go build -v -buildmode=plugin -o repochecker/videos-rules.so ./repochecker/videos
|
||||
|
||||
|
||||
ENV GRAMMALECTE_VERSION 2.1.1
|
||||
|
||||
ADD https://web.archive.org/web/20240926154729if_/https://grammalecte.net/zip/Grammalecte-fr-v$GRAMMALECTE_VERSION.zip /srv/grammalecte.zip
|
||||
|
||||
RUN mkdir /srv/grammalecte && cd /srv/grammalecte && unzip /srv/grammalecte.zip && sed -i 's/if sys.version_info.major < (3, 7):/if False:/' /srv/grammalecte/grammalecte-server.py
|
||||
|
||||
FROM alpine:3.19
|
||||
|
||||
ENTRYPOINT ["/usr/bin/repochecker", "--rules-plugins=/usr/lib/epita-rules.so", "--rules-plugins=/usr/lib/file-inspector.so", "--rules-plugins=/usr/lib/grammalecte-rules.so", "--rules-plugins=/usr/lib/pcap-inspector.so", "--rules-plugins=/usr/lib/videos-rules.so"]
|
||||
|
||||
RUN apk add --no-cache git python3 ffmpeg
|
||||
|
||||
COPY --from=gobuild /srv/grammalecte /srv/grammalecte
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/repochecker /usr/bin/repochecker
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/epita-rules.so /usr/lib/epita-rules.so
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/file-inspector.so /usr/lib/file-inspector.so
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/grammalecte-rules.so /usr/lib/grammalecte-rules.so
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/pcap-inspector.so /usr/lib/pcap-inspector.so
|
||||
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/videos-rules.so /usr/lib/videos-rules.so
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016-2018 Pierre-Olivier Mercier <nemunaire@nemunai.re>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
238
README.md
Normal file
238
README.md
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
FIC Forensic CTF Platform
|
||||
=========================
|
||||
|
||||
This is a CTF server for distributing and validating challenges. It is design
|
||||
to be robust, so it uses some uncommon technics like client certificate for
|
||||
authentication, lots of state of the art cryptographic methods and aims to be
|
||||
deployed in a DMZ network architecture.
|
||||
|
||||
## Features
|
||||
|
||||
- **Collaborative Challenge Design and Review:** Facilitates large team collaboration for challenge creation and review.
|
||||
- **Versatile Flag Formats:** Supports flags as strings, numbers, multiple-choice questions, unique-choice questions, selects, multiline inputs, and strings with capture regexp.
|
||||
- **Engaging Challenge Interface:** A visually appealing interface that incorporates images to illustrate exercises.
|
||||
- **Public Dashboard:** Allow spectators to follow the competition alongside players.
|
||||
- **Archival Mode:** Preserve past challenges and data in a static form, with no code. Your archive can lied on a S3 bucket.
|
||||
- **Export Capabilities:** Export challenges to other CTF platforms.
|
||||
- **Security-Focused:** Designed with security as a top priority. Each service aims to be isolated with right restrictions. Answers are not stored in the database, ...
|
||||
- **Choose your Authentication:** Authentication is not part of this project, integrate your own authentication methods.
|
||||
- **Extensible:** Easily extend and customize the platform. The main codebase in Golang is highly documented, each frontend part can be recreated in another language with ease.
|
||||
- **Comprehensive Settings:** A wide range of settings for challenge customization. You can have first blood or not, dynamic exercice gain, evenemential bonus, ...
|
||||
- **Git Integration:** Seamless verification and integration with Git.
|
||||
- **Infrastructure as Code (IaC):** Ensure read-only and reproducible infrastructure.
|
||||
- **Last-Minute Checks:** Ensure your challenge is ready with a comprehensive set of checks that can be performed anytime, verifying that downloadable files are as expected by the challenge creators.
|
||||
- **Lightweight:** Optimized for minimal resource consumption, supporting features like serving gzipped files directly to browsers without CPU usage.
|
||||
- **Scalable:** Designed to handle large-scale competitions with multiple receivers and frontend servers, smoothly queuing activity peaks on the backend.
|
||||
- **Offline Capability:** Run your challenges offline.
|
||||
- **Integrated Exercise Issue Ticketing System:** Manage and track issues related to exercises during the competition directly with teams. During designing phase, this transform in a complete dedicated QA platform.
|
||||
- **Detailed Statistics:** Provide administrators with insights into exercise preferences and complexity.
|
||||
- **Change Planning:** Schedule events in advance, such as new exercise availability or ephemeral bonuses, with second-by-second precision.
|
||||
- **Frontend Time Synchronization:** Ensure accurate remaining time and event synchronization between servers and players.
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
This is a [monorepo](https://danluu.com/monorepo/), containing several
|
||||
micro-services :
|
||||
|
||||
- `admin` is the web interface and API used to control the challenge
|
||||
and doing synchronization.
|
||||
- `checker` is an inotify reacting service that handles submissions
|
||||
checking.
|
||||
- `dashboard` is a public interface to explain and follow the
|
||||
conquest, aims to animate the challenge for visitors.
|
||||
- `evdist` is an inotify reacting service that handles settings
|
||||
changes during the challenge (eg. a 30 minutes event where hints are
|
||||
free, ...).
|
||||
- `generator` takes care of global and team's files generation.
|
||||
- `qa` is an interface dedicated to challenge development, it stores
|
||||
reports to be treated by challenges creators.
|
||||
- `receiver` is only responsible for receiving submissions. It is the
|
||||
only dynamic part accessibe to players, so it's codebase is reduce
|
||||
to the minimum. It does not parse or try to understand players
|
||||
submissions, it just write it down to a file in the file
|
||||
system. Parsing and treatment is made by the `checker`.
|
||||
- `remote/challenge-sync-airbus` is an inotify reacting service that
|
||||
allows us to synchronize scores and exercice validations with the
|
||||
Airbus scoring platform.
|
||||
- `remote/scores-sync-zqds` is an inotify reacting service that allows
|
||||
us to synchronize scores with the ZQDS scoring platform.
|
||||
- `repochecker` is a side project to check offline for synchronization
|
||||
issues.
|
||||
|
||||
Here is how thoses services speak to each others:
|
||||
|
||||

|
||||
|
||||
In the production setup, each micro-service runs in a dedicated
|
||||
container, isolated from each other. Moreover, two physical machines
|
||||
should be used:
|
||||
|
||||
- `phobos` communicates with players: displaying the web interface,
|
||||
authenticate teams and players, storing contest files and handling
|
||||
submissions retrieval without understanding them. It can't access
|
||||
`deimos` so its job stops after writing requests on the filesystem.
|
||||
- `deimos` is hidden from players, isolated from the network. It can
|
||||
only access `phobos` via a restricted ssh connection, to retrieve
|
||||
requests from `phobos` filesystem and pushing to it newly generated
|
||||
static files.
|
||||
|
||||
Concretely, the L2 looks like this:
|
||||
|
||||

|
||||
|
||||
So, the general filesystem is organized this way:
|
||||
|
||||
- `DASHBOARD` contains files structuring the content of the dashboard
|
||||
screen(s).
|
||||
- `FILES` stores the contest file to be downloaded by players. To be
|
||||
accessible without authentication and to avoid bruteforce, each file
|
||||
is placed into a directory with a hashed name (the original file
|
||||
name is preserved). It's rsynced as is to `deimos`.
|
||||
- `GENERATOR` contains a socket to allow other services to communicate
|
||||
with the `generator`.
|
||||
- `PKI` takes care of the PKI used for the client certiciate
|
||||
authorization process, and more generaly, all authentication related
|
||||
files (htpasswd, dexidp config, ...). Only the `shared` subdirectory
|
||||
is shared with `deimos`, private key and teams P12 don't go out.
|
||||
- `SETTINGS` stores the challenge config as wanted by admins. It's not
|
||||
always the config in use: it uses can be delayed waiting for a
|
||||
trigger.
|
||||
- `SETTINGSDIST` is the challenge configuration in use. It is the one
|
||||
shared with players.
|
||||
- `startingblock` keep the `started` state of the challenge. This
|
||||
helps `nginx` to know when it can start distributing exercices
|
||||
related files.
|
||||
- `TEAMS` stores the static files generated by the `generator`, there is
|
||||
one subdirectory by team (id of the team), plus some files at the
|
||||
root, which are common to all teams. There is also symlink pointing
|
||||
to team directory, each symlink represent an authentication
|
||||
association (certificate ID, OpenID username, htpasswd user, ...).
|
||||
- `submissions` is the directory where the `receiver` writes
|
||||
requests. It creates subdirectories at the name of the
|
||||
authentication association, as seen in `TEAMS`, `checker` then
|
||||
resolve the association regarding `TEAMS` directory. There is also a
|
||||
special directory to handle team registration.
|
||||
|
||||
Here is a diagram showing how each micro-service uses directories it has access to (blue for read access, red for write access):
|
||||
|
||||

|
||||
|
||||
Local developer setup
|
||||
---------------------
|
||||
|
||||
### Using Docker
|
||||
|
||||
Use `docker-compose build`, then `docker-compose up` to launch the infrastructure.
|
||||
|
||||
After booting, you'll be able to reach the main interface at:
|
||||
<http://localhost:8042/> and the admin one at: <http://localhost:8081/> (or at <http://localhost:8042/admin/>).
|
||||
The dashboard is available at <http://localhost:8042/dashboard/> and the QA service at <http://localhost:8042/qa/>.
|
||||
|
||||
In this setup, there is no authentication. You are identfied [as a team](./configs/nginx/get-team/team-1.conf). On first use you'll need to register.
|
||||
|
||||
#### Import folder
|
||||
|
||||
##### Local import folder
|
||||
The following changes is only required if your are trying to change the local import folder `~/fic` location.
|
||||
|
||||
Make the following changes inside this file `docker-compose.yml`:
|
||||
|
||||
23 volumes:
|
||||
24 - - ~/fic:/mnt/fic:ro
|
||||
24 + - <custom-path-to-import-folder>/fic:/mnt/fic:ro
|
||||
|
||||
##### Git import
|
||||
A git repository can be used:
|
||||
|
||||
29 - command: --baseurl /admin/ -localimport /mnt/fic -localimportsymlink
|
||||
29 + command: --baseurl /admin/ -localimport /mnt/fic -localimportsymlink -git-import-remote git@gitlab.cri.epita.fr:ing/majeures/srs/fic/2042/challenges.git
|
||||
|
||||
##### Owncloud import folder
|
||||
If your are trying to use the folder available with the Owncloud service, make the following changes inside this file `docker-compose.yml`:
|
||||
|
||||
29 - command: --baseurl /admin/ -localimport /mnt/fic -localimportsymlink
|
||||
29 + command: --baseurl /admin/ -clouddav=https://owncloud.srs.epita.fr/remote.php/webdav/FIC%202019/ -clouduser <login_x> -cloudpass '<passwd>'
|
||||
|
||||
### Manual builds
|
||||
|
||||
Running this project requires a web server (configuration is given for nginx),
|
||||
a database (currently supporting only MySQL/MariaDB), a Go compiler for the
|
||||
revision 1.18 at least and a `inotify`-aware system. You'll also need NodeJS to
|
||||
compile some user interfaces.
|
||||
|
||||
1. Above all, you need to build Node projects:
|
||||
|
||||
cd frontend/fic; npm install && npm run build
|
||||
cd qa/ui; npm install && npm run build
|
||||
|
||||
2. First, you'll need to retrieve the dependencies:
|
||||
|
||||
go mod vendor
|
||||
|
||||
2. Then, build the three Go projects:
|
||||
|
||||
go build -o fic-admin ./admin
|
||||
go build -o fic-checker ./checker
|
||||
go build -o fic-dashboard ./dashboard
|
||||
go build -o fic-generator ./generator
|
||||
go build -o fic-qa ./qa
|
||||
go build -o fic-receiver ./receiver
|
||||
go build -o fic-repochecker ./repochecker
|
||||
...
|
||||
|
||||
3. Before launching anything, you need to create a database:
|
||||
|
||||
mysql -u root -p <<EOF
|
||||
CREATE DATABASE fic;
|
||||
CREATE USER fic@localhost IDENTIFIED BY 'fic';
|
||||
GRANT ALL ON fic.* TO fic@localhost;
|
||||
EOF
|
||||
|
||||
By default, expected credentials for development purpose is `fic`,
|
||||
for both username, password and database name. If you want to use
|
||||
other credentials, define the corresponding environment variable:
|
||||
`MYSQL_HOST`, `MYSQL_USER`, `MYSQL_PASSWORD` and
|
||||
`MYSQL_DATABASE`. Those variables are the one used by the `mysql`
|
||||
docker image, so just link them together if you use containers.
|
||||
|
||||
4. Launch it!
|
||||
|
||||
./fic-admin &
|
||||
|
||||
After initializing the database, the server will listen on
|
||||
<http://localhost:8081/>: this is the administration part.
|
||||
|
||||
./fic-generator &
|
||||
|
||||
This daemon generates static and team related files and then waits
|
||||
another process to tell it to regenerate some files.
|
||||
|
||||
./fic-receiver &
|
||||
|
||||
This one exposes an API that gives time synchronization to clients and
|
||||
handle submission reception (but without treating them).
|
||||
|
||||
./fic-checker &
|
||||
|
||||
This service waits for new submissions (expected in `submissions`
|
||||
directory). It only watchs modifications on the file system, it has no web
|
||||
interface.
|
||||
|
||||
./fic-dashboard &
|
||||
|
||||
This last server runs the public dashboard. It serves all file, without the
|
||||
need of a webserver. It listens on port 8082 by default.
|
||||
|
||||
./fic-qa &
|
||||
|
||||
If you need it, this will launch a web interface on the port 8083 by
|
||||
default, to perform quality control.
|
||||
|
||||
For the moment, a web server is mandatory to serve static files, look
|
||||
at the samples given in the `configs/nginx` directory. You need to
|
||||
pick one base configation flavor in the `configs/nginx/base`
|
||||
directory, and associated with an authentication mechanism in
|
||||
`configs/nginx/auth` (named the file `fic-auth.conf` in `/etc/nginx`),
|
||||
and also pick the corresponding `configs/nginx/get-team` file, you
|
||||
named `fic-get-team.conf`.
|
||||
1
admin/.gitignore
vendored
1
admin/.gitignore
vendored
|
|
@ -2,3 +2,4 @@ admin
|
|||
fic.db
|
||||
PKI/
|
||||
FILES/
|
||||
static/full_import_report.json
|
||||
|
|
|
|||
102
admin/api.go
102
admin/api.go
|
|
@ -1,102 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DispatchFunction func([]string, []byte) (interface{}, error)
|
||||
|
||||
var apiRoutes = map[string]*(map[string]DispatchFunction){
|
||||
"version": &ApiVersionRouting,
|
||||
"ca": &ApiCARouting,
|
||||
"events": &ApiEventsRouting,
|
||||
"exercices": &ApiExercicesRouting,
|
||||
"themes": &ApiThemesRouting,
|
||||
"teams": &ApiTeamsRouting,
|
||||
}
|
||||
|
||||
type apiRouting struct{}
|
||||
|
||||
func ApiHandler() http.Handler {
|
||||
return apiRouting{}
|
||||
}
|
||||
|
||||
func (a apiRouting) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Handling %s request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
|
||||
// Extract URL arguments
|
||||
var sURL = strings.Split(r.URL.Path, "/")[1:]
|
||||
if len(sURL) > 1 && sURL[len(sURL)-1] == "" {
|
||||
// Remove trailing /
|
||||
sURL = sURL[:len(sURL)-1]
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
var ret interface{}
|
||||
var err error = nil
|
||||
|
||||
// Read the body
|
||||
if r.ContentLength < 0 || r.ContentLength > 6553600 {
|
||||
http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}", err), http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
var body []byte
|
||||
if r.ContentLength > 0 {
|
||||
tmp := make([]byte, 1024)
|
||||
for {
|
||||
n, err := r.Body.Read(tmp)
|
||||
for j := 0; j < n; j++ {
|
||||
body = append(body, tmp[j])
|
||||
}
|
||||
if err != nil || n <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Route request
|
||||
if len(sURL) > 0 {
|
||||
if h, ok := apiRoutes[sURL[0]]; ok {
|
||||
if f, ok := (*h)[r.Method]; ok {
|
||||
ret, err = f(sURL[1:], body)
|
||||
} else {
|
||||
err = errors.New(fmt.Sprintf("Invalid action (%s) provided for %s.", r.Method, sURL[0]))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = errors.New("No action provided.")
|
||||
}
|
||||
|
||||
// Format response
|
||||
resStatus := http.StatusOK
|
||||
if err != nil {
|
||||
ret = map[string]string{"errmsg": err.Error()}
|
||||
resStatus = http.StatusBadRequest
|
||||
log.Println(r.RemoteAddr, resStatus, err.Error())
|
||||
}
|
||||
|
||||
if ret == nil {
|
||||
ret = map[string]string{"errmsg": "Page not found"}
|
||||
resStatus = http.StatusNotFound
|
||||
}
|
||||
|
||||
if str, found := ret.(string); found {
|
||||
w.WriteHeader(resStatus)
|
||||
io.WriteString(w, str)
|
||||
} else if bts, found := ret.([]byte); found {
|
||||
w.WriteHeader(resStatus)
|
||||
w.Write(bts)
|
||||
} else if j, err := json.Marshal(ret); err != nil {
|
||||
http.Error(w, fmt.Sprintf("{\"errmsg\":\"%q\"}", err), http.StatusInternalServerError)
|
||||
} else {
|
||||
w.WriteHeader(resStatus)
|
||||
w.Write(j)
|
||||
}
|
||||
}
|
||||
479
admin/api/certificate.go
Normal file
479
admin/api/certificate.go
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/pki"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var TeamsDir string
|
||||
|
||||
func declareCertificateRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/htpasswd", func(c *gin.Context) {
|
||||
ret, err := genHtpasswd(true)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, ret)
|
||||
})
|
||||
router.POST("/htpasswd", func(c *gin.Context) {
|
||||
if htpasswd, err := genHtpasswd(true); err != nil {
|
||||
log.Println("Unable to generate htpasswd:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
} else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "ficpasswd"), []byte(htpasswd), 0644); err != nil {
|
||||
log.Println("Unable to write htpasswd:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
c.AbortWithStatus(http.StatusOK)
|
||||
})
|
||||
router.DELETE("/htpasswd", func(c *gin.Context) {
|
||||
if err := os.Remove(path.Join(pki.PKIDir, "shared", "ficpasswd")); err != nil {
|
||||
log.Println("Unable to remove htpasswd:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
c.AbortWithStatus(http.StatusOK)
|
||||
})
|
||||
router.GET("/htpasswd.apr1", func(c *gin.Context) {
|
||||
ret, err := genHtpasswd(false)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, ret)
|
||||
})
|
||||
router.GET("/ca", infoCA)
|
||||
router.GET("/ca.pem", getCAPEM)
|
||||
router.POST("/ca/new", func(c *gin.Context) {
|
||||
var upki PKISettings
|
||||
err := c.ShouldBindJSON(&upki)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := pki.GenerateCA(upki.NotBefore, upki.NotAfter); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, true)
|
||||
})
|
||||
|
||||
router.GET("/certs", getCertificates)
|
||||
router.POST("/certs", generateClientCert)
|
||||
router.DELETE("/certs", func(c *gin.Context) {
|
||||
v, err := fic.ClearCertificates()
|
||||
if err != nil {
|
||||
log.Println("Unable to ClearCertificates:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, v)
|
||||
})
|
||||
|
||||
apiCertificatesRoutes := router.Group("/certs/:certid")
|
||||
apiCertificatesRoutes.Use(CertificateHandler)
|
||||
apiCertificatesRoutes.HEAD("", getTeamP12File)
|
||||
apiCertificatesRoutes.GET("", getTeamP12File)
|
||||
apiCertificatesRoutes.PUT("", updateCertificateAssociation)
|
||||
apiCertificatesRoutes.DELETE("", func(c *gin.Context) {
|
||||
cert := c.MustGet("cert").(*fic.Certificate)
|
||||
|
||||
v, err := cert.Revoke()
|
||||
if err != nil {
|
||||
log.Println("Unable to Revoke:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, v)
|
||||
})
|
||||
}
|
||||
|
||||
func declareTeamCertificateRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/certificates", func(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
if serials, err := pki.GetTeamSerials(TeamsDir, team.Id); err != nil {
|
||||
log.Println("Unable to GetTeamSerials:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
} else {
|
||||
var certs []CertExported
|
||||
for _, serial := range serials {
|
||||
if cert, err := fic.GetCertificate(serial); err == nil {
|
||||
certs = append(certs, CertExported{fmt.Sprintf("%0[2]*[1]X", cert.Id, int(math.Ceil(math.Log2(float64(cert.Id))/8)*2)), cert.Creation, cert.Password, &team.Id, cert.Revoked})
|
||||
} else {
|
||||
log.Println("Unable to get back certificate, whereas an association exists on disk: ", err)
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, certs)
|
||||
}
|
||||
})
|
||||
|
||||
router.GET("/associations", func(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
assocs, err := pki.GetTeamAssociations(TeamsDir, team.Id)
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeamAssociations:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, assocs)
|
||||
})
|
||||
|
||||
apiTeamAssociationsRoutes := router.Group("/associations/:assoc")
|
||||
apiTeamAssociationsRoutes.POST("", func(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
if err := os.Symlink(fmt.Sprintf("%d", team.Id), path.Join(TeamsDir, c.Params.ByName("assoc"))); err != nil {
|
||||
log.Println("Unable to create association symlink:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to create association symlink: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, c.Params.ByName("assoc"))
|
||||
})
|
||||
apiTeamAssociationsRoutes.DELETE("", func(c *gin.Context) {
|
||||
err := pki.DeleteTeamAssociation(TeamsDir, c.Params.ByName("assoc"))
|
||||
if err != nil {
|
||||
log.Printf("Unable to DeleteTeamAssociation(%s): %s", c.Params.ByName("assoc"), err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to delete association symlink: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, nil)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func CertificateHandler(c *gin.Context) {
|
||||
var certid uint64
|
||||
var err error
|
||||
|
||||
cid := strings.TrimSuffix(string(c.Params.ByName("certid")), ".p12")
|
||||
if certid, err = strconv.ParseUint(cid, 10, 64); err != nil {
|
||||
if certid, err = strconv.ParseUint(cid, 16, 64); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid certficate identifier"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cert, err := fic.GetCertificate(certid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Certificate not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("cert", cert)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func genHtpasswd(ssha bool) (ret string, err error) {
|
||||
var teams []*fic.Team
|
||||
teams, err = fic.GetTeams()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, team := range teams {
|
||||
var serials []uint64
|
||||
serials, err = pki.GetTeamSerials(TeamsDir, team.Id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(serials) == 0 {
|
||||
// Don't include teams that don't have associated certificates
|
||||
continue
|
||||
}
|
||||
|
||||
for _, serial := range serials {
|
||||
var cert *fic.Certificate
|
||||
cert, err = fic.GetCertificate(serial)
|
||||
if err != nil {
|
||||
// Ignore invalid/incorrect/non-existant certificates
|
||||
continue
|
||||
}
|
||||
|
||||
if cert.Revoked != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
salt := make([]byte, 5)
|
||||
if _, err = rand.Read(salt); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if ssha {
|
||||
hash := sha1.New()
|
||||
hash.Write([]byte(cert.Password))
|
||||
hash.Write([]byte(salt))
|
||||
|
||||
passwdline := fmt.Sprintf(":{SSHA}%s\n", base64.StdEncoding.EncodeToString(append(hash.Sum(nil), salt...)))
|
||||
|
||||
ret += strings.ToLower(team.Name) + passwdline
|
||||
ret += fmt.Sprintf("%0[2]*[1]x", cert.Id, int(math.Ceil(math.Log2(float64(cert.Id))/8)*2)) + passwdline
|
||||
ret += fmt.Sprintf("%0[2]*[1]X", cert.Id, int(math.Ceil(math.Log2(float64(cert.Id))/8)*2)) + passwdline
|
||||
teamAssociations, _ := pki.GetTeamAssociations(TeamsDir, team.Id)
|
||||
log.Println(path.Join(TeamsDir, fmt.Sprintf("%d", team.Id)), teamAssociations)
|
||||
for _, ta := range teamAssociations {
|
||||
ret += strings.Replace(ta, ":", "", -1) + passwdline
|
||||
}
|
||||
} else {
|
||||
salt32 := base32.StdEncoding.EncodeToString(salt)
|
||||
ret += fmt.Sprintf(
|
||||
"%s:$apr1$%s$%s\n",
|
||||
strings.ToLower(team.Name),
|
||||
salt32,
|
||||
fic.Apr1Md5(cert.Password, salt32),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type PKISettings struct {
|
||||
Version int `json:"version"`
|
||||
SerialNumber *big.Int `json:"serialnumber"`
|
||||
Issuer pkix.Name `json:"issuer"`
|
||||
Subject pkix.Name `json:"subject"`
|
||||
NotBefore time.Time `json:"notbefore"`
|
||||
NotAfter time.Time `json:"notafter"`
|
||||
SignatureAlgorithm x509.SignatureAlgorithm `json:"signatureAlgorithm,"`
|
||||
PublicKeyAlgorithm x509.PublicKeyAlgorithm `json:"publicKeyAlgorithm"`
|
||||
}
|
||||
|
||||
func infoCA(c *gin.Context) {
|
||||
_, cacert, err := pki.LoadCA()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "CA not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, PKISettings{
|
||||
Version: cacert.Version,
|
||||
SerialNumber: cacert.SerialNumber,
|
||||
Issuer: cacert.Issuer,
|
||||
Subject: cacert.Subject,
|
||||
NotBefore: cacert.NotBefore,
|
||||
NotAfter: cacert.NotAfter,
|
||||
SignatureAlgorithm: cacert.SignatureAlgorithm,
|
||||
PublicKeyAlgorithm: cacert.PublicKeyAlgorithm,
|
||||
})
|
||||
}
|
||||
|
||||
func getCAPEM(c *gin.Context) {
|
||||
if _, err := os.Stat(pki.CACertPath()); os.IsNotExist(err) {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Unable to locate the CA root certificate. Have you generated it?"})
|
||||
return
|
||||
} else if fd, err := os.Open(pki.CACertPath()); err != nil {
|
||||
log.Println("Unable to open CA root certificate:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
} else {
|
||||
defer fd.Close()
|
||||
|
||||
cnt, err := ioutil.ReadAll(fd)
|
||||
if err != nil {
|
||||
log.Println("Unable to read CA root certificate:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, string(cnt))
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamP12File(c *gin.Context) {
|
||||
cert := c.MustGet("cert").(*fic.Certificate)
|
||||
|
||||
// Create p12 if necessary
|
||||
if _, err := os.Stat(pki.ClientP12Path(cert.Id)); os.IsNotExist(err) {
|
||||
if err := pki.WriteP12(cert.Id, cert.Password); err != nil {
|
||||
log.Println("Unable to WriteP12:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(pki.ClientP12Path(cert.Id)); os.IsNotExist(err) {
|
||||
log.Println("Unable to compute ClientP12Path:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, errors.New("Unable to locate the p12. Have you generated it?"))
|
||||
return
|
||||
} else if fd, err := os.Open(pki.ClientP12Path(cert.Id)); err != nil {
|
||||
log.Println("Unable to open ClientP12Path:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("Unable to open the p12: %w", err))
|
||||
return
|
||||
} else {
|
||||
defer fd.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(fd)
|
||||
if err != nil {
|
||||
log.Println("Unable to open ClientP12Path:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("Unable to open the p12: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
c.Data(http.StatusOK, "application/x-pkcs12", data)
|
||||
}
|
||||
}
|
||||
|
||||
func generateClientCert(c *gin.Context) {
|
||||
// First, generate a new, unique, serial
|
||||
var serial_gen [8]byte
|
||||
if _, err := rand.Read(serial_gen[:]); err != nil {
|
||||
log.Println("Unable to read enough entropy to generate client certificate:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to read enough entropy"})
|
||||
return
|
||||
}
|
||||
for fic.ExistingCertSerial(serial_gen) {
|
||||
if _, err := rand.Read(serial_gen[:]); err != nil {
|
||||
log.Println("Unable to read enough entropy to generate client certificate:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to read enough entropy"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var serial_b big.Int
|
||||
serial_b.SetBytes(serial_gen[:])
|
||||
serial := serial_b.Uint64()
|
||||
|
||||
// Let's pick a random password
|
||||
password, err := fic.GeneratePassword()
|
||||
if err != nil {
|
||||
log.Println("Unable to generate password:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to generate password: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Ok, now load CA
|
||||
capriv, cacert, err := pki.LoadCA()
|
||||
if err != nil {
|
||||
log.Println("Unable to load the CA:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to load the CA"})
|
||||
return
|
||||
}
|
||||
|
||||
// Generate our privkey
|
||||
if err := pki.GenerateClient(serial, cacert.NotBefore, cacert.NotAfter, &cacert, &capriv); err != nil {
|
||||
log.Println("Unable to generate private key:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to generate private key: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Save in DB
|
||||
cert, err := fic.RegisterCertificate(serial, password)
|
||||
if err != nil {
|
||||
log.Println("Unable to register certificate:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to register certificate."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, CertExported{fmt.Sprintf("%0[2]*[1]X", cert.Id, int(math.Ceil(math.Log2(float64(cert.Id))/8)*2)), cert.Creation, cert.Password, nil, cert.Revoked})
|
||||
}
|
||||
|
||||
type CertExported struct {
|
||||
Id string `json:"id"`
|
||||
Creation time.Time `json:"creation"`
|
||||
Password string `json:"password,omitempty"`
|
||||
IdTeam *int64 `json:"id_team"`
|
||||
Revoked *time.Time `json:"revoked"`
|
||||
}
|
||||
|
||||
func getCertificates(c *gin.Context) {
|
||||
certificates, err := fic.GetCertificates()
|
||||
if err != nil {
|
||||
log.Println("Unable to retrieve certificates list:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during certificates retrieval."})
|
||||
return
|
||||
}
|
||||
ret := make([]CertExported, 0)
|
||||
for _, cert := range certificates {
|
||||
dstLinkPath := path.Join(TeamsDir, pki.GetCertificateAssociation(cert.Id))
|
||||
|
||||
var idTeam *int64 = nil
|
||||
if lnk, err := os.Readlink(dstLinkPath); err == nil {
|
||||
if tid, err := strconv.ParseInt(lnk, 10, 64); err == nil {
|
||||
idTeam = &tid
|
||||
}
|
||||
}
|
||||
|
||||
ret = append(ret, CertExported{fmt.Sprintf("%0[2]*[1]X", cert.Id, int(math.Ceil(math.Log2(float64(cert.Id))/8)*2)), cert.Creation, "", idTeam, cert.Revoked})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
type CertUploaded struct {
|
||||
Team *int64 `json:"id_team"`
|
||||
}
|
||||
|
||||
func updateCertificateAssociation(c *gin.Context) {
|
||||
cert := c.MustGet("cert").(*fic.Certificate)
|
||||
|
||||
var uc CertUploaded
|
||||
err := c.ShouldBindJSON(&uc)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
dstLinkPath := path.Join(TeamsDir, pki.GetCertificateAssociation(cert.Id))
|
||||
|
||||
if uc.Team != nil {
|
||||
srcLinkPath := fmt.Sprintf("%d", *uc.Team)
|
||||
if err := os.Symlink(srcLinkPath, dstLinkPath); err != nil {
|
||||
log.Println("Unable to create certificate symlink:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to create certificate symlink: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
// Mark team as active to ensure it'll be generated
|
||||
if ut, err := fic.GetTeam(*uc.Team); err != nil {
|
||||
log.Println("Unable to GetTeam:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team retrieval."})
|
||||
return
|
||||
} else if !ut.Active {
|
||||
ut.Active = true
|
||||
_, err := ut.Update()
|
||||
if err != nil {
|
||||
log.Println("Unable to UpdateTeam after updateCertificateAssociation:", err.Error())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
os.Remove(dstLinkPath)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, cert)
|
||||
}
|
||||
499
admin/api/claim.go
Normal file
499
admin/api/claim.go
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/generation"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func declareClaimsRoutes(router *gin.RouterGroup) {
|
||||
// Tasks
|
||||
router.GET("/claims", getClaims)
|
||||
router.POST("/claims", newClaim)
|
||||
router.DELETE("/claims", clearClaims)
|
||||
|
||||
apiClaimsRoutes := router.Group("/claims/:cid")
|
||||
apiClaimsRoutes.Use(ClaimHandler)
|
||||
apiClaimsRoutes.GET("", showClaim)
|
||||
apiClaimsRoutes.PUT("", updateClaim)
|
||||
apiClaimsRoutes.POST("", addClaimDescription)
|
||||
apiClaimsRoutes.DELETE("", deleteClaim)
|
||||
|
||||
apiClaimsRoutes.GET("/last_update", getClaimLastUpdate)
|
||||
apiClaimsRoutes.PUT("/descriptions", updateClaimDescription)
|
||||
|
||||
// Assignees
|
||||
router.GET("/claims-assignees", getAssignees)
|
||||
router.POST("/claims-assignees", newAssignee)
|
||||
|
||||
apiClaimAssigneesRoutes := router.Group("/claims-assignees/:aid")
|
||||
apiClaimAssigneesRoutes.Use(ClaimAssigneeHandler)
|
||||
router.GET("/claims-assignees/:aid", showClaimAssignee)
|
||||
router.PUT("/claims-assignees/:aid", updateClaimAssignee)
|
||||
router.DELETE("/claims-assignees/:aid", deleteClaimAssignee)
|
||||
}
|
||||
|
||||
func declareExerciceClaimsRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/claims", getExerciceClaims)
|
||||
}
|
||||
|
||||
func declareTeamClaimsRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/api/teams/:tid/issue.json", func(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
issues, err := team.MyIssueFile()
|
||||
if err != nil {
|
||||
log.Printf("Unable to MyIssueFile(tid=%d): %s", team.Id, err.Error())
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to generate issues.json."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, issues)
|
||||
})
|
||||
|
||||
router.GET("/claims", getTeamClaims)
|
||||
}
|
||||
|
||||
func ClaimHandler(c *gin.Context) {
|
||||
cid, err := strconv.ParseInt(string(c.Params.ByName("cid")), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid claim identifier"})
|
||||
return
|
||||
}
|
||||
|
||||
claim, err := fic.GetClaim(cid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Requested claim not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("claim", claim)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func ClaimAssigneeHandler(c *gin.Context) {
|
||||
aid, err := strconv.ParseInt(string(c.Params.ByName("aid")), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid claim assignee identifier"})
|
||||
return
|
||||
}
|
||||
|
||||
assignee, err := fic.GetAssignee(aid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Requested claim-assignee not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("claim-assignee", assignee)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func getClaims(c *gin.Context) {
|
||||
claims, err := fic.GetClaims()
|
||||
|
||||
if err != nil {
|
||||
log.Println("Unable to getClaims:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claims retrieval."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, claims)
|
||||
}
|
||||
|
||||
func getTeamClaims(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
claims, err := team.GetClaims()
|
||||
if err != nil {
|
||||
log.Printf("Unable to GetClaims(tid=%d): %s", team.Id, err.Error())
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve claim list."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, claims)
|
||||
}
|
||||
|
||||
func getExerciceClaims(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
|
||||
claims, err := exercice.GetClaims()
|
||||
if err != nil {
|
||||
log.Printf("Unable to GetClaims(eid=%d): %s", exercice.Id, err.Error())
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve claim list."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, claims)
|
||||
}
|
||||
|
||||
func getClaimLastUpdate(c *gin.Context) {
|
||||
claim := c.MustGet("claim").(*fic.Claim)
|
||||
|
||||
v, err := claim.GetLastUpdate()
|
||||
if err != nil {
|
||||
log.Printf("Unable to GetLastUpdate: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim last update retrieval."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, v)
|
||||
}
|
||||
|
||||
type ClaimExported struct {
|
||||
Id int64 `json:"id"`
|
||||
Subject string `json:"subject"`
|
||||
IdTeam *int64 `json:"id_team"`
|
||||
Team *fic.Team `json:"team"`
|
||||
IdExercice *int64 `json:"id_exercice"`
|
||||
Exercice *fic.Exercice `json:"exercice"`
|
||||
IdAssignee *int64 `json:"id_assignee"`
|
||||
Assignee *fic.ClaimAssignee `json:"assignee"`
|
||||
Creation time.Time `json:"creation"`
|
||||
LastUpdate time.Time `json:"last_update"`
|
||||
State string `json:"state"`
|
||||
Priority string `json:"priority"`
|
||||
Descriptions []*fic.ClaimDescription `json:"descriptions"`
|
||||
}
|
||||
|
||||
func showClaim(c *gin.Context) {
|
||||
claim := c.MustGet("claim").(*fic.Claim)
|
||||
|
||||
var e ClaimExported
|
||||
var err error
|
||||
if e.Team, err = claim.GetTeam(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find associated team: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
if e.Exercice, err = claim.GetExercice(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find associated exercice: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
if e.Assignee, err = claim.GetAssignee(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find associated assignee: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
if e.Descriptions, err = claim.GetDescriptions(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find claim's descriptions: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
e.LastUpdate = e.Creation
|
||||
for _, d := range e.Descriptions {
|
||||
if d.Date.After(e.LastUpdate) {
|
||||
e.LastUpdate = d.Date
|
||||
}
|
||||
}
|
||||
|
||||
e.Id = claim.Id
|
||||
e.IdAssignee = claim.IdAssignee
|
||||
e.IdTeam = claim.IdTeam
|
||||
e.IdExercice = claim.IdExercice
|
||||
e.Subject = claim.Subject
|
||||
e.Creation = claim.Creation
|
||||
e.State = claim.State
|
||||
e.Priority = claim.Priority
|
||||
|
||||
c.JSON(http.StatusOK, e)
|
||||
}
|
||||
|
||||
type ClaimUploaded struct {
|
||||
fic.Claim
|
||||
Whoami *int64 `json:"whoami"`
|
||||
}
|
||||
|
||||
func newClaim(c *gin.Context) {
|
||||
var uc ClaimUploaded
|
||||
err := c.ShouldBindJSON(&uc)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if uc.Subject == "" {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Claim's subject cannot be empty."})
|
||||
return
|
||||
}
|
||||
|
||||
var t *fic.Team
|
||||
if uc.IdTeam != nil {
|
||||
if team, err := fic.GetTeam(*uc.IdTeam); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated team: %s", err.Error())})
|
||||
return
|
||||
} else {
|
||||
t = team
|
||||
}
|
||||
} else {
|
||||
t = nil
|
||||
}
|
||||
|
||||
var e *fic.Exercice
|
||||
if uc.IdExercice != nil {
|
||||
if exercice, err := fic.GetExercice(*uc.IdExercice); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated exercice: %s", err.Error())})
|
||||
return
|
||||
} else {
|
||||
e = exercice
|
||||
}
|
||||
} else {
|
||||
e = nil
|
||||
}
|
||||
|
||||
var a *fic.ClaimAssignee
|
||||
if uc.IdAssignee != nil {
|
||||
if assignee, err := fic.GetAssignee(*uc.IdAssignee); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated assignee: %s", err.Error())})
|
||||
return
|
||||
} else {
|
||||
a = assignee
|
||||
}
|
||||
} else {
|
||||
a = nil
|
||||
}
|
||||
|
||||
if uc.Priority == "" {
|
||||
uc.Priority = "medium"
|
||||
}
|
||||
|
||||
claim, err := fic.NewClaim(uc.Subject, t, e, a, uc.Priority)
|
||||
if err != nil {
|
||||
log.Println("Unable to newClaim:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to register new claim"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, claim)
|
||||
}
|
||||
|
||||
func clearClaims(c *gin.Context) {
|
||||
nb, err := fic.ClearClaims()
|
||||
if err != nil {
|
||||
log.Printf("Unable to clearClaims: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claims clearing."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, nb)
|
||||
}
|
||||
|
||||
func generateTeamIssuesFile(team fic.Team) error {
|
||||
if generation.GeneratorSocket == "" {
|
||||
if my, err := team.MyIssueFile(); err != nil {
|
||||
return fmt.Errorf("Unable to generate issue FILE (tid=%d): %w", team.Id, err)
|
||||
} else if j, err := json.Marshal(my); err != nil {
|
||||
return fmt.Errorf("Unable to encode issues' file JSON: %w", err)
|
||||
} else if err = ioutil.WriteFile(path.Join(TeamsDir, fmt.Sprintf("%d", team.Id), "issues.json"), j, 0644); err != nil {
|
||||
return fmt.Errorf("Unable to write issues' file: %w", err)
|
||||
}
|
||||
} else {
|
||||
resp, err := generation.PerformGeneration(fic.GenStruct{Type: fic.GenTeamIssues, TeamId: team.Id})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
v, _ := ioutil.ReadAll(resp.Body)
|
||||
return fmt.Errorf("%s", string(v))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addClaimDescription(c *gin.Context) {
|
||||
claim := c.MustGet("claim").(*fic.Claim)
|
||||
|
||||
var ud fic.ClaimDescription
|
||||
err := c.ShouldBindJSON(&ud)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
assignee, err := fic.GetAssignee(ud.IdAssignee)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated assignee: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
description, err := claim.AddDescription(ud.Content, assignee, ud.Publish)
|
||||
if err != nil {
|
||||
log.Println("Unable to addClaimDescription:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to add description"})
|
||||
return
|
||||
}
|
||||
|
||||
if team, _ := claim.GetTeam(); team != nil {
|
||||
err = generateTeamIssuesFile(*team)
|
||||
if err != nil {
|
||||
log.Println("Unable to generateTeamIssuesFile after addClaimDescription:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, description)
|
||||
}
|
||||
|
||||
func updateClaimDescription(c *gin.Context) {
|
||||
claim := c.MustGet("claim").(*fic.Claim)
|
||||
|
||||
var ud fic.ClaimDescription
|
||||
err := c.ShouldBindJSON(&ud)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := ud.Update(); err != nil {
|
||||
log.Println("Unable to updateClaimDescription:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during claim description updating."})
|
||||
return
|
||||
}
|
||||
if team, _ := claim.GetTeam(); team != nil {
|
||||
err = generateTeamIssuesFile(*team)
|
||||
if err != nil {
|
||||
log.Println("Unable to generateTeamIssuesFile:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ud)
|
||||
}
|
||||
|
||||
func updateClaim(c *gin.Context) {
|
||||
claim := c.MustGet("claim").(*fic.Claim)
|
||||
|
||||
var uc ClaimUploaded
|
||||
err := c.ShouldBindJSON(&uc)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
uc.Id = claim.Id
|
||||
|
||||
_, err = uc.Update()
|
||||
if err != nil {
|
||||
log.Printf("Unable to updateClaim: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim update."})
|
||||
return
|
||||
}
|
||||
|
||||
if claim.State != uc.State {
|
||||
if uc.Whoami != nil {
|
||||
if assignee, err := fic.GetAssignee(*uc.Whoami); err == nil {
|
||||
claim.AddDescription(fmt.Sprintf("%s a changé l'état de la tâche vers %q (était %q).", assignee.Name, uc.State, claim.State), assignee, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if claim.IdAssignee != uc.IdAssignee {
|
||||
if uc.Whoami != nil {
|
||||
if whoami, err := fic.GetAssignee(*uc.Whoami); err == nil {
|
||||
if uc.IdAssignee != nil {
|
||||
if assignee, err := fic.GetAssignee(*uc.IdAssignee); err == nil {
|
||||
if assignee.Id != whoami.Id {
|
||||
claim.AddDescription(fmt.Sprintf("%s a assigné la tâche à %s.", whoami.Name, assignee.Name), whoami, false)
|
||||
} else {
|
||||
claim.AddDescription(fmt.Sprintf("%s s'est assigné la tâche.", assignee.Name), whoami, false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
claim.AddDescription(fmt.Sprintf("%s a retiré l'attribution de la tâche.", whoami.Name), whoami, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if team, _ := claim.GetTeam(); team != nil {
|
||||
err = generateTeamIssuesFile(*team)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, uc)
|
||||
}
|
||||
|
||||
func deleteClaim(c *gin.Context) {
|
||||
claim := c.MustGet("claim").(*fic.Claim)
|
||||
|
||||
if nb, err := claim.Delete(); err != nil {
|
||||
log.Println("Unable to deleteClaim:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim deletion."})
|
||||
return
|
||||
} else {
|
||||
c.JSON(http.StatusOK, nb)
|
||||
}
|
||||
}
|
||||
|
||||
func getAssignees(c *gin.Context) {
|
||||
assignees, err := fic.GetAssignees()
|
||||
if err != nil {
|
||||
log.Println("Unable to getAssignees:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during assignees retrieval."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, assignees)
|
||||
}
|
||||
|
||||
func showClaimAssignee(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, c.MustGet("claim-assignee").(*fic.ClaimAssignee))
|
||||
}
|
||||
func newAssignee(c *gin.Context) {
|
||||
var ua fic.ClaimAssignee
|
||||
err := c.ShouldBindJSON(&ua)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
assignee, err := fic.NewClaimAssignee(ua.Name)
|
||||
if err != nil {
|
||||
log.Println("Unable to newAssignee:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during assignee creation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, assignee)
|
||||
}
|
||||
|
||||
func updateClaimAssignee(c *gin.Context) {
|
||||
assignee := c.MustGet("claim-assignee").(*fic.ClaimAssignee)
|
||||
|
||||
var ua fic.ClaimAssignee
|
||||
err := c.ShouldBindJSON(&ua)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ua.Id = assignee.Id
|
||||
|
||||
if _, err := ua.Update(); err != nil {
|
||||
log.Println("Unable to updateClaimAssignee:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim assignee update."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ua)
|
||||
}
|
||||
|
||||
func deleteClaimAssignee(c *gin.Context) {
|
||||
assignee := c.MustGet("claim-assignee").(*fic.ClaimAssignee)
|
||||
|
||||
if _, err := assignee.Delete(); err != nil {
|
||||
log.Println("Unable to deleteClaimAssignee:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during claim assignee deletion: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
154
admin/api/events.go
Normal file
154
admin/api/events.go
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func declareEventsRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/events", getEvents)
|
||||
router.GET("/events.json", getLastEvents)
|
||||
router.POST("/events", newEvent)
|
||||
router.DELETE("/events", clearEvents)
|
||||
|
||||
apiEventsRoutes := router.Group("/events/:evid")
|
||||
apiEventsRoutes.Use(EventHandler)
|
||||
apiEventsRoutes.GET("", showEvent)
|
||||
apiEventsRoutes.PUT("", updateEvent)
|
||||
apiEventsRoutes.DELETE("", deleteEvent)
|
||||
}
|
||||
|
||||
func EventHandler(c *gin.Context) {
|
||||
evid, err := strconv.ParseInt(string(c.Params.ByName("evid")), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid event identifier"})
|
||||
return
|
||||
}
|
||||
|
||||
event, err := fic.GetEvent(evid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Event not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("event", event)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func genEventsFile() error {
|
||||
if evts, err := fic.GetLastEvents(); err != nil {
|
||||
return err
|
||||
} else if j, err := json.Marshal(evts); err != nil {
|
||||
return err
|
||||
} else if err := ioutil.WriteFile(path.Join(TeamsDir, "events.json"), j, 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEvents(c *gin.Context) {
|
||||
evts, err := fic.GetEvents()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetEvents:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve events list"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, evts)
|
||||
}
|
||||
|
||||
func getLastEvents(c *gin.Context) {
|
||||
evts, err := fic.GetLastEvents()
|
||||
|
||||
if err != nil {
|
||||
log.Println("Unable to GetLastEvents:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve last events list"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, evts)
|
||||
}
|
||||
|
||||
func newEvent(c *gin.Context) {
|
||||
var ue fic.Event
|
||||
err := c.ShouldBindJSON(&ue)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
event, err := fic.NewEvent(ue.Text, ue.Kind)
|
||||
if err != nil {
|
||||
log.Printf("Unable to newEvent: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during event creation."})
|
||||
return
|
||||
}
|
||||
|
||||
genEventsFile()
|
||||
|
||||
c.JSON(http.StatusOK, event)
|
||||
}
|
||||
|
||||
func clearEvents(c *gin.Context) {
|
||||
nb, err := fic.ClearEvents()
|
||||
if err != nil {
|
||||
log.Printf("Unable to clearEvent: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during event clearing."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, nb)
|
||||
}
|
||||
|
||||
func showEvent(c *gin.Context) {
|
||||
event := c.MustGet("event").(*fic.Event)
|
||||
c.JSON(http.StatusOK, event)
|
||||
}
|
||||
|
||||
func updateEvent(c *gin.Context) {
|
||||
event := c.MustGet("event").(*fic.Event)
|
||||
|
||||
var ue fic.Event
|
||||
err := c.ShouldBindJSON(&ue)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ue.Id = event.Id
|
||||
|
||||
if _, err := ue.Update(); err != nil {
|
||||
log.Printf("Unable to updateEvent: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during event update."})
|
||||
return
|
||||
}
|
||||
|
||||
genEventsFile()
|
||||
|
||||
c.JSON(http.StatusOK, ue)
|
||||
}
|
||||
|
||||
func deleteEvent(c *gin.Context) {
|
||||
event := c.MustGet("event").(*fic.Event)
|
||||
|
||||
_, err := event.Delete()
|
||||
if err != nil {
|
||||
log.Printf("Unable to deleteEvent: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during event deletion."})
|
||||
return
|
||||
}
|
||||
|
||||
genEventsFile()
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
1749
admin/api/exercice.go
Normal file
1749
admin/api/exercice.go
Normal file
File diff suppressed because it is too large
Load diff
126
admin/api/export.go
Normal file
126
admin/api/export.go
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
"srs.epita.fr/fic-server/settings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func declareExportRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/archive.zip", func(c *gin.Context) {
|
||||
challengeinfo, err := GetChallengeInfo()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
my, err := fic.MyJSONTeam(nil, true)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
s, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile))
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
s.End = nil
|
||||
s.NextChangeTime = nil
|
||||
s.DelegatedQA = []string{}
|
||||
|
||||
teams, err := fic.ExportTeams(false)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
themes, err := fic.ExportThemes()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.Writer.WriteHeader(http.StatusOK)
|
||||
c.Header("Content-Disposition", "attachment; filename=archive.zip")
|
||||
c.Header("Content-Type", "application/zip")
|
||||
|
||||
w := zip.NewWriter(c.Writer)
|
||||
|
||||
// challenge.json
|
||||
f, err := w.Create("challenge.json")
|
||||
if err == nil {
|
||||
json.NewEncoder(f).Encode(challengeinfo)
|
||||
}
|
||||
|
||||
// Include partners' logos from challenge.json
|
||||
if sync.GlobalImporter != nil {
|
||||
if len(challengeinfo.MainLogo) > 0 {
|
||||
for _, logo := range challengeinfo.MainLogo {
|
||||
fd, closer, err := sync.OpenOrGetFile(sync.GlobalImporter, logo)
|
||||
if err != nil {
|
||||
log.Printf("Unable to archive main logo %q: %s", logo, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
f, err := w.Create(path.Join("logo", path.Base(logo)))
|
||||
if err == nil {
|
||||
io.Copy(f, fd)
|
||||
}
|
||||
closer()
|
||||
}
|
||||
}
|
||||
|
||||
if len(challengeinfo.Partners) > 0 {
|
||||
for _, partner := range challengeinfo.Partners {
|
||||
fd, closer, err := sync.OpenOrGetFile(sync.GlobalImporter, partner.Src)
|
||||
if err != nil {
|
||||
log.Printf("Unable to archive partner logo %q: %s", partner.Src, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
f, err := w.Create(path.Join("partner", path.Base(partner.Src)))
|
||||
if err == nil {
|
||||
io.Copy(f, fd)
|
||||
}
|
||||
closer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// my.json
|
||||
f, err = w.Create("my.json")
|
||||
if err == nil {
|
||||
json.NewEncoder(f).Encode(my)
|
||||
}
|
||||
|
||||
// settings.json
|
||||
f, err = w.Create("settings.json")
|
||||
if err == nil {
|
||||
json.NewEncoder(f).Encode(s)
|
||||
}
|
||||
|
||||
// teams.json
|
||||
f, err = w.Create("teams.json")
|
||||
if err == nil {
|
||||
json.NewEncoder(f).Encode(teams)
|
||||
}
|
||||
|
||||
// themes.json
|
||||
f, err = w.Create("themes.json")
|
||||
if err == nil {
|
||||
json.NewEncoder(f).Encode(themes)
|
||||
}
|
||||
|
||||
w.Close()
|
||||
})
|
||||
}
|
||||
297
admin/api/file.go
Normal file
297
admin/api/file.go
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func declareFilesGlobalRoutes(router *gin.RouterGroup) {
|
||||
router.DELETE("/files/", clearFiles)
|
||||
|
||||
// Remote
|
||||
router.GET("/remote/themes/:thid/exercices/:exid/files", sync.ApiGetRemoteExerciceFiles)
|
||||
}
|
||||
|
||||
func declareFilesRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/files", listFiles)
|
||||
router.POST("/files", createExerciceFile)
|
||||
|
||||
apiFilesRoutes := router.Group("/files/:fileid")
|
||||
apiFilesRoutes.Use(FileHandler)
|
||||
apiFilesRoutes.GET("", showFile)
|
||||
apiFilesRoutes.PUT("", updateFile)
|
||||
apiFilesRoutes.DELETE("", deleteFile)
|
||||
|
||||
apiFileDepsRoutes := apiFilesRoutes.Group("/dependancies/:depid")
|
||||
apiFileDepsRoutes.Use(FileDepHandler)
|
||||
apiFileDepsRoutes.DELETE("", deleteFileDep)
|
||||
|
||||
// Check
|
||||
apiFilesRoutes.POST("/check", checkFile)
|
||||
apiFilesRoutes.POST("/gunzip", gunzipFile)
|
||||
}
|
||||
|
||||
func FileHandler(c *gin.Context) {
|
||||
fileid, err := strconv.ParseInt(string(c.Params.ByName("fileid")), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid file identifier"})
|
||||
return
|
||||
}
|
||||
|
||||
var file *fic.EFile
|
||||
if exercice, exists := c.Get("exercice"); exists {
|
||||
file, err = exercice.(*fic.Exercice).GetFile(fileid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "File not found"})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
file, err = fic.GetFile(fileid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "File not found"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.Set("file", file)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func FileDepHandler(c *gin.Context) {
|
||||
depid, err := strconv.ParseInt(string(c.Params.ByName("depid")), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid dependency identifier"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("file-depid", depid)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
type APIFile struct {
|
||||
*fic.EFile
|
||||
Depends []fic.Flag `json:"depends,omitempty"`
|
||||
}
|
||||
|
||||
func genFileList(in []*fic.EFile, e error) (out []APIFile, err error) {
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
for _, f := range in {
|
||||
g := APIFile{EFile: f}
|
||||
|
||||
var deps []fic.Flag
|
||||
deps, err = f.GetDepends()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, d := range deps {
|
||||
if k, ok := d.(*fic.FlagKey); ok {
|
||||
k, err = fic.GetFlagKey(k.Id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
g.Depends = append(g.Depends, k)
|
||||
} else if m, ok := d.(*fic.MCQ); ok {
|
||||
m, err = fic.GetMCQ(m.Id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
g.Depends = append(g.Depends, m)
|
||||
} else {
|
||||
err = fmt.Errorf("Unknown type %T to handle file dependancy", k)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
out = append(out, g)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func listFiles(c *gin.Context) {
|
||||
var files []APIFile
|
||||
var err error
|
||||
|
||||
if exercice, exists := c.Get("exercice"); exists {
|
||||
files, err = genFileList(exercice.(*fic.Exercice).GetFiles())
|
||||
} else {
|
||||
files, err = genFileList(fic.GetFiles())
|
||||
}
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, files)
|
||||
}
|
||||
|
||||
func clearFiles(c *gin.Context) {
|
||||
err := os.RemoveAll(fic.FilesDir)
|
||||
if err != nil {
|
||||
log.Println("Unable to remove files:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = os.MkdirAll(fic.FilesDir, 0751)
|
||||
if err != nil {
|
||||
log.Println("Unable to create FILES:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
_, err = fic.ClearFiles()
|
||||
if err != nil {
|
||||
log.Println("Unable to clean DB files:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Les fichiers ont bien été effacés. Mais il n'a pas été possible d'effacer la base de données. Refaites une synchronisation maintenant. " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func showFile(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, c.MustGet("file").(*fic.EFile))
|
||||
}
|
||||
|
||||
type uploadedFile struct {
|
||||
URI string
|
||||
Digest string
|
||||
}
|
||||
|
||||
func createExerciceFile(c *gin.Context) {
|
||||
exercice, exists := c.Get("exercice")
|
||||
if !exists {
|
||||
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": "File can only be added inside an exercice."})
|
||||
return
|
||||
}
|
||||
|
||||
paramsFiles, err := sync.GetExerciceFilesParams(sync.GlobalImporter, exercice.(*fic.Exercice))
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var uf uploadedFile
|
||||
err = c.ShouldBindJSON(&uf)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ret, err := sync.ImportFile(sync.GlobalImporter, uf.URI,
|
||||
func(filePath string, origin string) (interface{}, error) {
|
||||
if digest, err := hex.DecodeString(uf.Digest); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
published := true
|
||||
disclaimer := ""
|
||||
|
||||
if f, exists := paramsFiles[filepath.Base(filePath)]; exists {
|
||||
published = !f.Hidden
|
||||
|
||||
if disclaimer, err = sync.ProcessMarkdown(sync.GlobalImporter, f.Disclaimer, exercice.(*fic.Exercice).Path); err != nil {
|
||||
return nil, fmt.Errorf("error during markdown formating of disclaimer: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return exercice.(*fic.Exercice).ImportFile(filePath, origin, digest, nil, disclaimer, published)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
func updateFile(c *gin.Context) {
|
||||
file := c.MustGet("file").(*fic.EFile)
|
||||
|
||||
var uf fic.EFile
|
||||
err := c.ShouldBindJSON(&uf)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
uf.Id = file.Id
|
||||
|
||||
if _, err := uf.Update(); err != nil {
|
||||
log.Println("Unable to updateFile:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update file."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, uf)
|
||||
}
|
||||
|
||||
func deleteFile(c *gin.Context) {
|
||||
file := c.MustGet("file").(*fic.EFile)
|
||||
|
||||
_, err := file.Delete()
|
||||
if err != nil {
|
||||
log.Println("Unable to updateFile:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update file."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func deleteFileDep(c *gin.Context) {
|
||||
file := c.MustGet("file").(*fic.EFile)
|
||||
depid := c.MustGet("file-depid").(int64)
|
||||
|
||||
err := file.DeleteDepend(&fic.FlagKey{Id: int(depid)})
|
||||
if err != nil {
|
||||
log.Println("Unable to deleteFileDep:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to delete file dependency."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func checkFile(c *gin.Context) {
|
||||
file := c.MustGet("file").(*fic.EFile)
|
||||
|
||||
err := file.CheckFileOnDisk()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func gunzipFile(c *gin.Context) {
|
||||
file := c.MustGet("file").(*fic.EFile)
|
||||
|
||||
err := file.GunzipFileOnDisk()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
156
admin/api/health.go
Normal file
156
admin/api/health.go
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/pki"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var TimestampCheck = "submissions"
|
||||
|
||||
func declareHealthRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/timestamps.json", func(c *gin.Context) {
|
||||
stat, err := os.Stat(TimestampCheck)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("timestamp.json: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"frontend": stat.ModTime().UTC(),
|
||||
"backend": now,
|
||||
"diffFB": now.Sub(stat.ModTime()),
|
||||
})
|
||||
})
|
||||
router.GET("/health.json", GetHealth)
|
||||
router.GET("/submissions-stats.json", GetSubmissionsStats)
|
||||
router.GET("/validations-stats.json", GetValidationsStats)
|
||||
|
||||
router.DELETE("/submissions/*path", func(c *gin.Context) {
|
||||
err := os.Remove(path.Join(TimestampCheck, c.Params.ByName("path")))
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusOK)
|
||||
})
|
||||
}
|
||||
|
||||
type healthFileReport struct {
|
||||
IdTeam string `json:"id_team,omitempty"`
|
||||
Path string `json:"path"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func getHealth(pathname string) (ret []healthFileReport) {
|
||||
if ds, err := ioutil.ReadDir(pathname); err != nil {
|
||||
ret = append(ret, healthFileReport{
|
||||
Path: strings.TrimPrefix(pathname, TimestampCheck),
|
||||
Error: fmt.Sprintf("unable to ReadDir: %s", err),
|
||||
})
|
||||
return
|
||||
} else {
|
||||
for _, d := range ds {
|
||||
p := path.Join(pathname, d.Name())
|
||||
if d.IsDir() && d.Name() != ".tmp" && d.Mode()&os.ModeSymlink == 0 {
|
||||
ret = append(ret, getHealth(p)...)
|
||||
} else if !d.IsDir() && d.Mode()&os.ModeSymlink == 0 && time.Since(d.ModTime()) > 2*time.Second {
|
||||
if d.Name() == ".locked" {
|
||||
continue
|
||||
}
|
||||
|
||||
teamDir := strings.TrimPrefix(pathname, TimestampCheck)
|
||||
idteam, _ := pki.GetAssociation(path.Join(TeamsDir, teamDir))
|
||||
ret = append(ret, healthFileReport{
|
||||
IdTeam: idteam,
|
||||
Path: path.Join(teamDir, d.Name()),
|
||||
Error: "existing untreated file",
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func GetHealth(c *gin.Context) {
|
||||
if _, err := os.Stat(TimestampCheck); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("health.json: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, getHealth(TimestampCheck))
|
||||
}
|
||||
|
||||
type SubmissionsStats struct {
|
||||
NbSubmissionLastMinute uint `json:"nbsubminute"`
|
||||
NbSubmissionLast5Minute uint `json:"nbsub5minute"`
|
||||
NbSubmissionLastQuarter uint `json:"nbsubquarter"`
|
||||
NbSubmissionLastHour uint `json:"nbsubhour"`
|
||||
NbSubmissionLastDay uint `json:"nbsubday"`
|
||||
}
|
||||
|
||||
func calcSubmissionsStats(tries []time.Time) (stats SubmissionsStats) {
|
||||
lastMinute := time.Now().Add(-1 * time.Minute)
|
||||
last5Minute := time.Now().Add(-5 * time.Minute)
|
||||
lastQuarter := time.Now().Add(-15 * time.Minute)
|
||||
lastHour := time.Now().Add(-1 * time.Hour)
|
||||
lastDay := time.Now().Add(-24 * time.Hour)
|
||||
|
||||
for _, t := range tries {
|
||||
if lastMinute.Before(t) {
|
||||
stats.NbSubmissionLastMinute += 1
|
||||
stats.NbSubmissionLast5Minute += 1
|
||||
stats.NbSubmissionLastQuarter += 1
|
||||
stats.NbSubmissionLastHour += 1
|
||||
stats.NbSubmissionLastDay += 1
|
||||
} else if last5Minute.Before(t) {
|
||||
stats.NbSubmissionLast5Minute += 1
|
||||
stats.NbSubmissionLastQuarter += 1
|
||||
stats.NbSubmissionLastHour += 1
|
||||
stats.NbSubmissionLastDay += 1
|
||||
} else if lastQuarter.Before(t) {
|
||||
stats.NbSubmissionLastQuarter += 1
|
||||
stats.NbSubmissionLastHour += 1
|
||||
stats.NbSubmissionLastDay += 1
|
||||
} else if lastHour.Before(t) {
|
||||
stats.NbSubmissionLastHour += 1
|
||||
stats.NbSubmissionLastDay += 1
|
||||
} else if lastDay.Before(t) {
|
||||
stats.NbSubmissionLastDay += 1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func GetSubmissionsStats(c *gin.Context) {
|
||||
tries, err := fic.GetTries(nil, nil)
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTries:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieves tries."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, calcSubmissionsStats(tries))
|
||||
}
|
||||
|
||||
func GetValidationsStats(c *gin.Context) {
|
||||
tries, err := fic.GetValidations(nil, nil)
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTries:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieves tries."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, calcSubmissionsStats(tries))
|
||||
}
|
||||
98
admin/api/monitor.go
Normal file
98
admin/api/monitor.go
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func declareMonitorRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/monitor", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"localhost": genLocalConstants(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func readLoadAvg(fd *os.File) (ret map[string]float64) {
|
||||
if s, err := ioutil.ReadAll(fd); err == nil {
|
||||
f := strings.Fields(strings.TrimSpace(string(s)))
|
||||
if len(f) >= 3 {
|
||||
ret = map[string]float64{}
|
||||
ret["1m"], _ = strconv.ParseFloat(f[0], 64)
|
||||
ret["5m"], _ = strconv.ParseFloat(f[1], 64)
|
||||
ret["15m"], _ = strconv.ParseFloat(f[2], 64)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func readMeminfo(fd *os.File) (ret map[string]uint64) {
|
||||
ret = map[string]uint64{}
|
||||
|
||||
scanner := bufio.NewScanner(fd)
|
||||
for scanner.Scan() {
|
||||
f := strings.Fields(strings.TrimSpace(scanner.Text()))
|
||||
if len(f) >= 2 {
|
||||
if v, err := strconv.ParseUint(f[1], 10, 64); err == nil {
|
||||
ret[strings.ToLower(strings.TrimSuffix(f[0], ":"))] = v * 1024
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func readCPUStats(fd *os.File) (ret map[string]map[string]uint64) {
|
||||
ret = map[string]map[string]uint64{}
|
||||
|
||||
scanner := bufio.NewScanner(fd)
|
||||
for scanner.Scan() {
|
||||
f := strings.Fields(strings.TrimSpace(scanner.Text()))
|
||||
if len(f[0]) >= 4 && f[0][0:3] == "cpu" && len(f) >= 8 {
|
||||
ret[f[0]] = map[string]uint64{}
|
||||
var total uint64 = 0
|
||||
for i, k := range []string{"user", "nice", "system", "idle", "iowait", "irq", "softirq"} {
|
||||
if v, err := strconv.ParseUint(f[i+1], 10, 64); err == nil {
|
||||
ret[f[0]][k] = v
|
||||
total += v
|
||||
}
|
||||
}
|
||||
ret[f[0]]["total"] = total
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func genLocalConstants() interface{} {
|
||||
ret := map[string]interface{}{}
|
||||
|
||||
fi, err := os.Open("/proc/loadavg")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
ret["loadavg"] = readLoadAvg(fi)
|
||||
|
||||
fi, err = os.Open("/proc/meminfo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
ret["meminfo"] = readMeminfo(fi)
|
||||
|
||||
fi, err = os.Open("/proc/stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
ret["cpustat"] = readCPUStats(fi)
|
||||
|
||||
return ret
|
||||
}
|
||||
360
admin/api/password.go
Normal file
360
admin/api/password.go
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"text/template"
|
||||
"unicode"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/pki"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
OidcIssuer = "live.fic.srs.epita.fr"
|
||||
OidcClientId = "epita-challenge"
|
||||
OidcSecret = ""
|
||||
)
|
||||
|
||||
func declarePasswordRoutes(router *gin.RouterGroup) {
|
||||
router.POST("/password", func(c *gin.Context) {
|
||||
passwd, err := fic.GeneratePassword()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"password": passwd})
|
||||
})
|
||||
router.GET("/oauth-status", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"secret_defined": OidcSecret != "",
|
||||
})
|
||||
})
|
||||
router.GET("/dex.yaml", func(c *gin.Context) {
|
||||
cfg, err := genDexConfig()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, string(cfg))
|
||||
})
|
||||
router.POST("/dex.yaml", func(c *gin.Context) {
|
||||
if dexcfg, err := genDexConfig(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
} else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "dex-config.yaml"), []byte(dexcfg), 0644); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
})
|
||||
router.GET("/dex-password.tpl", func(c *gin.Context) {
|
||||
passtpl, err := genDexPasswordTpl()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, string(passtpl))
|
||||
})
|
||||
router.POST("/dex-password.tpl", func(c *gin.Context) {
|
||||
if dexcfg, err := genDexPasswordTpl(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
} else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "dex-password.tpl"), []byte(dexcfg), 0644); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
})
|
||||
router.GET("/vouch-proxy.yaml", func(c *gin.Context) {
|
||||
cfg, err := genVouchProxyConfig()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, string(cfg))
|
||||
})
|
||||
router.POST("/vouch-proxy.yaml", func(c *gin.Context) {
|
||||
if dexcfg, err := genVouchProxyConfig(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
} else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "vouch-config.yaml"), []byte(dexcfg), 0644); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
})
|
||||
}
|
||||
|
||||
func declareTeamsPasswordRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/password", func(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
if team.Password != nil {
|
||||
c.String(http.StatusOK, *team.Password)
|
||||
} else {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, nil)
|
||||
}
|
||||
})
|
||||
router.POST("/password", func(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
if passwd, err := fic.GeneratePassword(); err != nil {
|
||||
log.Println("Unable to GeneratePassword:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something went wrong when generating the new team password"})
|
||||
return
|
||||
} else {
|
||||
team.Password = &passwd
|
||||
|
||||
_, err := team.Update()
|
||||
if err != nil {
|
||||
log.Println("Unable to Update Team:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something went wrong when updating the new team password"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, team)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const dexcfgtpl = `issuer: {{ .Issuer }}
|
||||
storage:
|
||||
type: sqlite3
|
||||
config:
|
||||
file: /var/dex/dex.db
|
||||
web:
|
||||
http: 0.0.0.0:5556
|
||||
frontend:
|
||||
issuer: {{ .Name }}
|
||||
logoURL: {{ .LogoPath }}
|
||||
dir: /srv/dex/web/
|
||||
oauth2:
|
||||
skipApprovalScreen: true
|
||||
staticClients:
|
||||
{{ range $c := .Clients }}
|
||||
- id: {{ $c.Id }}
|
||||
name: {{ $c.Name }}
|
||||
redirectURIs: [{{ range $u := $c.RedirectURIs }}'{{ $u }}'{{ end }}]
|
||||
secret: {{ $c.Secret }}
|
||||
{{ end }}
|
||||
enablePasswordDB: true
|
||||
staticPasswords:
|
||||
{{ range $t := .Teams }}
|
||||
- email: "team{{ printf "%02d" $t.Id }}"
|
||||
hash: "{{with $t }}{{ .HashedPassword }}{{end}}"
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
const dexpasswdtpl = `{{ "{{" }} template "header.html" . {{ "}}" }}
|
||||
|
||||
<div class="theme-panel">
|
||||
<h2 class="theme-heading">
|
||||
Bienvenue au {{ .Name }} !
|
||||
</h2>
|
||||
<form method="post" action="{{ "{{" }} .PostURL {{ "}}" }}">
|
||||
<div class="theme-form-row">
|
||||
<div class="theme-form-label">
|
||||
<label for="userid">Votre équipe</label>
|
||||
</div>
|
||||
<select tabindex="1" required id="login" name="login" class="theme-form-input" autofocus>
|
||||
{{ range $t := .Teams }} <option value="team{{ printf "%02d" $t.Id }}">{{ $t.Name }}</option>
|
||||
{{ end }} </select>
|
||||
</div>
|
||||
<div class="theme-form-row">
|
||||
<div class="theme-form-label">
|
||||
<label for="password">Mot de passe</label>
|
||||
</div>
|
||||
<input tabindex="2" required id="password" name="password" type="password" class="theme-form-input" placeholder="mot de passe" {{ "{{" }} if .Invalid {{ "}}" }} autofocus {{ "{{" }} end {{ "}}" }}/>
|
||||
</div>
|
||||
|
||||
{{ "{{" }} if .Invalid {{ "}}" }}
|
||||
<div id="login-error" class="dex-error-box">
|
||||
Identifiants incorrects.
|
||||
</div>
|
||||
{{ "{{" }} end {{ "}}" }}
|
||||
|
||||
<button tabindex="3" id="submit-login" type="submit" class="dex-btn theme-btn--primary">C'est parti !</button>
|
||||
|
||||
</form>
|
||||
{{ "{{" }} if .BackLink {{ "}}" }}
|
||||
<div class="theme-link-back">
|
||||
<a class="dex-subtle-text" href="{{ "{{" }} .BackLink {{ "}}" }}">Sélectionner une autre méthode d'authentification.</a>
|
||||
</div>
|
||||
{{ "{{" }} end {{ "}}" }}
|
||||
</div>
|
||||
|
||||
{{ "{{" }} template "footer.html" . {{ "}}" }}
|
||||
`
|
||||
|
||||
type dexConfigClient struct {
|
||||
Id string
|
||||
Name string
|
||||
RedirectURIs []string
|
||||
Secret string
|
||||
}
|
||||
|
||||
type dexConfig struct {
|
||||
Name string
|
||||
Issuer string
|
||||
Clients []dexConfigClient
|
||||
Teams []*fic.Team
|
||||
LogoPath string
|
||||
}
|
||||
|
||||
func genDexConfig() ([]byte, error) {
|
||||
if OidcSecret == "" {
|
||||
return nil, fmt.Errorf("Unable to generate dex configuration: OIDC Secret not defined. Please define FICOIDC_SECRET in your environment.")
|
||||
}
|
||||
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := bytes.NewBufferString("")
|
||||
|
||||
challengeInfo, err := GetChallengeInfo()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cannot create template: %w", err)
|
||||
}
|
||||
|
||||
// Lower the first letter to be included in a sentence.
|
||||
name := []rune(challengeInfo.Title)
|
||||
if len(name) > 0 {
|
||||
name[0] = unicode.ToLower(name[0])
|
||||
}
|
||||
|
||||
logoPath := ""
|
||||
if len(challengeInfo.MainLogo) > 0 {
|
||||
logoPath = path.Join("../../files", "logo", path.Base(challengeInfo.MainLogo[len(challengeInfo.MainLogo)-1]))
|
||||
}
|
||||
|
||||
dexTmpl, err := template.New("dexcfg").Parse(dexcfgtpl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cannot create template: %w", err)
|
||||
}
|
||||
|
||||
err = dexTmpl.Execute(b, dexConfig{
|
||||
Name: string(name),
|
||||
Issuer: "https://" + OidcIssuer,
|
||||
Clients: []dexConfigClient{
|
||||
dexConfigClient{
|
||||
Id: OidcClientId,
|
||||
Name: challengeInfo.Title,
|
||||
RedirectURIs: []string{"https://" + OidcIssuer + "/challenge_access/auth"},
|
||||
Secret: OidcSecret,
|
||||
},
|
||||
},
|
||||
Teams: teams,
|
||||
LogoPath: logoPath,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("An error occurs during template execution: %w", err)
|
||||
}
|
||||
|
||||
// Also generate team associations
|
||||
for _, team := range teams {
|
||||
if _, err := os.Stat(path.Join(TeamsDir, fmt.Sprintf("team%02d", team.Id))); err == nil {
|
||||
if err = os.Remove(path.Join(TeamsDir, fmt.Sprintf("team%02d", team.Id))); err != nil {
|
||||
log.Println("Unable to remove existing association symlink:", err.Error())
|
||||
return nil, fmt.Errorf("Unable to remove existing association symlink: %s", err.Error())
|
||||
}
|
||||
}
|
||||
if err := os.Symlink(fmt.Sprintf("%d", team.Id), path.Join(TeamsDir, fmt.Sprintf("team%02d", team.Id))); err != nil {
|
||||
log.Println("Unable to create association symlink:", err.Error())
|
||||
return nil, fmt.Errorf("Unable to create association symlink: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func genDexPasswordTpl() ([]byte, error) {
|
||||
challengeInfo, err := GetChallengeInfo()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cannot create template: %w", err)
|
||||
}
|
||||
|
||||
if teams, err := fic.GetTeams(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
b := bytes.NewBufferString("")
|
||||
|
||||
if dexTmpl, err := template.New("dexpasswd").Parse(dexpasswdtpl); err != nil {
|
||||
return nil, fmt.Errorf("Cannot create template: %w", err)
|
||||
} else if err = dexTmpl.Execute(b, dexConfig{
|
||||
Teams: teams,
|
||||
Name: challengeInfo.Title,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("An error occurs during template execution: %w", err)
|
||||
} else {
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const vouchcfgtpl = `# CONFIGURATION FILE HANDLED BY fic-admin
|
||||
# DO NOT MODIFY IT BY HAND
|
||||
|
||||
vouch:
|
||||
logLevel: debug
|
||||
allowAllUsers: true
|
||||
document_root: /challenge_access
|
||||
|
||||
cookie:
|
||||
domain: {{ .Domain }}
|
||||
|
||||
oauth:
|
||||
provider: oidc
|
||||
client_id: {{ .ClientId }}
|
||||
client_secret: {{ .ClientSecret }}
|
||||
callback_urls:
|
||||
- https://{{ .Domain }}/challenge_access/auth
|
||||
auth_url: https://{{ .Domain }}/auth
|
||||
token_url: http://127.0.0.1:5556/token
|
||||
user_info_url: http://127.0.0.1:5556/userinfo
|
||||
scopes:
|
||||
- openid
|
||||
- email
|
||||
`
|
||||
|
||||
type vouchProxyConfig struct {
|
||||
Domain string
|
||||
ClientId string
|
||||
ClientSecret string
|
||||
}
|
||||
|
||||
func genVouchProxyConfig() ([]byte, error) {
|
||||
if OidcSecret == "" {
|
||||
return nil, fmt.Errorf("Unable to generate vouch proxy configuration: OIDC Secret not defined. Please define FICOIDC_SECRET in your environment.")
|
||||
}
|
||||
|
||||
b := bytes.NewBufferString("")
|
||||
|
||||
if vouchTmpl, err := template.New("vouchcfg").Parse(vouchcfgtpl); err != nil {
|
||||
return nil, fmt.Errorf("Cannot create template: %w", err)
|
||||
} else if err = vouchTmpl.Execute(b, vouchProxyConfig{
|
||||
Domain: OidcIssuer,
|
||||
ClientId: OidcClientId,
|
||||
ClientSecret: OidcSecret,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("An error occurs during template execution: %w", err)
|
||||
} else {
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
}
|
||||
206
admin/api/public.go
Normal file
206
admin/api/public.go
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var DashboardDir string
|
||||
|
||||
func declarePublicRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/public/", listPublic)
|
||||
router.GET("/public/:sid", getPublic)
|
||||
router.DELETE("/public/:sid", deletePublic)
|
||||
router.PUT("/public/:sid", savePublic)
|
||||
}
|
||||
|
||||
type FICPublicScene struct {
|
||||
Type string `json:"type"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
}
|
||||
|
||||
type FICPublicDisplay struct {
|
||||
Scenes []FICPublicScene `json:"scenes"`
|
||||
Side []FICPublicScene `json:"side"`
|
||||
CustomCountdown map[string]interface{} `json:"customCountdown"`
|
||||
HideEvents bool `json:"hideEvents"`
|
||||
HideCountdown bool `json:"hideCountdown"`
|
||||
HideCarousel bool `json:"hideCarousel"`
|
||||
PropagationTime *time.Time `json:"propagationTime,omitempty"`
|
||||
}
|
||||
|
||||
func InitDashboardPresets(dir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func readPublic(path string) (FICPublicDisplay, error) {
|
||||
var s FICPublicDisplay
|
||||
if fd, err := os.Open(path); err != nil {
|
||||
return s, err
|
||||
} else {
|
||||
defer fd.Close()
|
||||
jdec := json.NewDecoder(fd)
|
||||
|
||||
if err := jdec.Decode(&s); err != nil {
|
||||
return s, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
|
||||
func savePublicTo(path string, s FICPublicDisplay) error {
|
||||
if fd, err := os.Create(path); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer fd.Close()
|
||||
jenc := json.NewEncoder(fd)
|
||||
|
||||
if err := jenc.Encode(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type DashboardFiles struct {
|
||||
Presets []string `json:"presets"`
|
||||
Nexts []*NextDashboardFile `json:"nexts"`
|
||||
}
|
||||
|
||||
type NextDashboardFile struct {
|
||||
Name string `json:"name"`
|
||||
Screen int `json:"screen"`
|
||||
Date time.Time `json:"date"`
|
||||
}
|
||||
|
||||
func listPublic(c *gin.Context) {
|
||||
files, err := os.ReadDir(DashboardDir)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var ret DashboardFiles
|
||||
for _, file := range files {
|
||||
if strings.HasPrefix(file.Name(), "preset-") {
|
||||
ret.Presets = append(ret.Presets, strings.TrimSuffix(strings.TrimPrefix(file.Name(), "preset-"), ".json"))
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(file.Name(), "public") || len(file.Name()) < 18 {
|
||||
continue
|
||||
}
|
||||
|
||||
ts, err := strconv.ParseInt(file.Name()[8:18], 10, 64)
|
||||
if err == nil {
|
||||
s, _ := strconv.Atoi(file.Name()[6:7])
|
||||
ret.Nexts = append(ret.Nexts, &NextDashboardFile{
|
||||
Name: file.Name()[6:18],
|
||||
Screen: s,
|
||||
Date: time.Unix(ts, 0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
func getPublic(c *gin.Context) {
|
||||
if strings.Contains(c.Params.ByName("sid"), "/") {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "sid cannot contains /"})
|
||||
return
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("public%s.json", c.Params.ByName("sid"))
|
||||
if strings.HasPrefix(c.Params.ByName("sid"), "preset-") {
|
||||
filename = fmt.Sprintf("%s.json", c.Params.ByName("sid"))
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path.Join(DashboardDir, filename)); !os.IsNotExist(err) {
|
||||
p, err := readPublic(path.Join(DashboardDir, filename))
|
||||
if err != nil {
|
||||
log.Println("Unable to readPublic in getPublic:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene retrieval."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, p)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, FICPublicDisplay{Scenes: []FICPublicScene{}, Side: []FICPublicScene{}})
|
||||
}
|
||||
|
||||
func deletePublic(c *gin.Context) {
|
||||
if strings.Contains(c.Params.ByName("sid"), "/") {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "sid cannot contains /"})
|
||||
return
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("public%s.json", c.Params.ByName("sid"))
|
||||
if strings.HasPrefix(c.Params.ByName("sid"), "preset-") {
|
||||
filename = fmt.Sprintf("%s.json", c.Params.ByName("sid"))
|
||||
}
|
||||
|
||||
if len(filename) == 12 {
|
||||
if err := savePublicTo(path.Join(DashboardDir, filename), FICPublicDisplay{}); err != nil {
|
||||
log.Println("Unable to deletePublic:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene deletion."})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := os.Remove(path.Join(DashboardDir, filename)); err != nil {
|
||||
log.Println("Unable to deletePublic:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene deletion."})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, FICPublicDisplay{Scenes: []FICPublicScene{}, Side: []FICPublicScene{}})
|
||||
}
|
||||
|
||||
func savePublic(c *gin.Context) {
|
||||
if strings.Contains(c.Params.ByName("sid"), "/") {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "sid cannot contains /"})
|
||||
return
|
||||
}
|
||||
|
||||
var scenes FICPublicDisplay
|
||||
err := c.ShouldBindJSON(&scenes)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("public%s.json", c.Params.ByName("sid"))
|
||||
if c.Request.URL.Query().Has("t") {
|
||||
t, err := time.Parse(time.RFC3339, c.Request.URL.Query().Get("t"))
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
filename = fmt.Sprintf("public%s-%d.json", c.Params.ByName("sid"), t.Unix())
|
||||
} else if c.Request.URL.Query().Has("p") {
|
||||
filename = fmt.Sprintf("preset-%s.json", c.Request.URL.Query().Get("p"))
|
||||
}
|
||||
|
||||
if err := savePublicTo(path.Join(DashboardDir, filename), scenes); err != nil {
|
||||
log.Println("Unable to savePublicTo:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene saving."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, scenes)
|
||||
}
|
||||
119
admin/api/qa.go
Normal file
119
admin/api/qa.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func declareQARoutes(router *gin.RouterGroup) {
|
||||
router.POST("/qa/", importExerciceQA)
|
||||
|
||||
apiQARoutes := router.Group("/qa/:qid")
|
||||
apiQARoutes.Use(QAHandler)
|
||||
apiQARoutes.POST("/comments", importQAComment)
|
||||
}
|
||||
|
||||
func QAHandler(c *gin.Context) {
|
||||
qid, err := strconv.ParseInt(string(c.Params.ByName("qid")), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid QA identifier"})
|
||||
return
|
||||
}
|
||||
|
||||
qa, err := fic.GetQAQuery(qid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "QA query not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("qa-query", qa)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func importExerciceQA(c *gin.Context) {
|
||||
// Create a new query
|
||||
var uq fic.QAQuery
|
||||
err := c.ShouldBindJSON(&uq)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var exercice *fic.Exercice
|
||||
if uq.IdExercice == 0 {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "id_exercice not filled"})
|
||||
return
|
||||
} else if exercice, err = fic.GetExercice(uq.IdExercice); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Unable to find requested exercice"})
|
||||
return
|
||||
}
|
||||
|
||||
if len(uq.State) == 0 {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "State not filled"})
|
||||
return
|
||||
}
|
||||
|
||||
if len(uq.Subject) == 0 {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Subject not filled"})
|
||||
return
|
||||
}
|
||||
|
||||
if qa, err := exercice.NewQAQuery(uq.Subject, uq.IdTeam, uq.User, uq.State, nil); err != nil {
|
||||
log.Println("Unable to importExerciceQA:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during query creation."})
|
||||
return
|
||||
} else {
|
||||
qa.Creation = uq.Creation
|
||||
qa.Solved = uq.Solved
|
||||
qa.Closed = uq.Closed
|
||||
|
||||
_, err = qa.Update()
|
||||
if err != nil {
|
||||
log.Println("Unable to update in importExerciceQA:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during query updating."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, qa)
|
||||
}
|
||||
}
|
||||
|
||||
func importQAComment(c *gin.Context) {
|
||||
query := c.MustGet("qa-query").(*fic.QAQuery)
|
||||
|
||||
// Create a new query
|
||||
var uc fic.QAComment
|
||||
err := c.ShouldBindJSON(&uc)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if len(uc.Content) == 0 {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Empty comment"})
|
||||
return
|
||||
}
|
||||
|
||||
if qac, err := query.AddComment(uc.Content, uc.IdTeam, uc.User); err != nil {
|
||||
log.Println("Unable to AddComment in importQAComment:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during comment creation."})
|
||||
return
|
||||
} else {
|
||||
qac.Date = uc.Date
|
||||
|
||||
_, err = qac.Update()
|
||||
if err != nil {
|
||||
log.Println("Unable to Update comment in importQAComment")
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during comment creation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, qac)
|
||||
}
|
||||
}
|
||||
67
admin/api/repositories.go
Normal file
67
admin/api/repositories.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func declareRepositoriesRoutes(router *gin.RouterGroup) {
|
||||
if gi, ok := sync.GlobalImporter.(sync.GitImporter); ok {
|
||||
router.GET("/repositories", func(c *gin.Context) {
|
||||
mod, err := gi.GetSubmodules()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"repositories": mod})
|
||||
})
|
||||
|
||||
router.GET("/repositories/*repopath", func(c *gin.Context) {
|
||||
repopath := strings.TrimPrefix(c.Param("repopath"), "/")
|
||||
|
||||
mod, err := gi.GetSubmodule(repopath)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, mod)
|
||||
})
|
||||
|
||||
router.POST("/repositories/*repopath", func(c *gin.Context) {
|
||||
repopath := strings.TrimPrefix(c.Param("repopath"), "/")
|
||||
|
||||
mod, err := gi.IsRepositoryUptodate(repopath)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, mod)
|
||||
})
|
||||
|
||||
router.DELETE("/repositories/*repopath", func(c *gin.Context) {
|
||||
di, ok := sync.GlobalImporter.(sync.DeletableImporter)
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(http.StatusNotImplemented, gin.H{"errmsg": "Not implemented"})
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(c.Param("repopath"), "..") {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Repopath contains invalid characters"})
|
||||
return
|
||||
}
|
||||
|
||||
repopath := strings.TrimPrefix(c.Param("repopath"), "/")
|
||||
|
||||
err := di.DeleteDir(repopath)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, true)
|
||||
})
|
||||
}
|
||||
}
|
||||
29
admin/api/router.go
Normal file
29
admin/api/router.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func DeclareRoutes(router *gin.RouterGroup) {
|
||||
apiRoutes := router.Group("/api")
|
||||
|
||||
declareCertificateRoutes(apiRoutes)
|
||||
declareClaimsRoutes(apiRoutes)
|
||||
declareEventsRoutes(apiRoutes)
|
||||
declareExercicesRoutes(apiRoutes)
|
||||
declareExportRoutes(apiRoutes)
|
||||
declareFilesGlobalRoutes(apiRoutes)
|
||||
declareFilesRoutes(apiRoutes)
|
||||
declareGlobalExercicesRoutes(apiRoutes)
|
||||
declareHealthRoutes(apiRoutes)
|
||||
declareMonitorRoutes(apiRoutes)
|
||||
declarePasswordRoutes(apiRoutes)
|
||||
declarePublicRoutes(apiRoutes)
|
||||
declareQARoutes(apiRoutes)
|
||||
declareRepositoriesRoutes(apiRoutes)
|
||||
declareTeamsRoutes(apiRoutes)
|
||||
declareThemesRoutes(apiRoutes)
|
||||
declareSettingsRoutes(apiRoutes)
|
||||
declareSyncRoutes(apiRoutes)
|
||||
DeclareVersionRoutes(apiRoutes)
|
||||
}
|
||||
425
admin/api/settings.go
Normal file
425
admin/api/settings.go
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/generation"
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
"srs.epita.fr/fic-server/settings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var IsProductionEnv = false
|
||||
|
||||
func declareSettingsRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/challenge.json", getChallengeInfo)
|
||||
router.PUT("/challenge.json", saveChallengeInfo)
|
||||
|
||||
router.GET("/settings.json", getSettings)
|
||||
router.PUT("/settings.json", saveSettings)
|
||||
router.DELETE("/settings.json", func(c *gin.Context) {
|
||||
err := ResetSettings()
|
||||
if err != nil {
|
||||
log.Println("Unable to ResetSettings:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during setting reset."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
})
|
||||
|
||||
router.GET("/settings-next", listNextSettings)
|
||||
|
||||
apiNextSettingsRoutes := router.Group("/settings-next/:ts")
|
||||
apiNextSettingsRoutes.Use(NextSettingsHandler)
|
||||
apiNextSettingsRoutes.GET("", getNextSettings)
|
||||
apiNextSettingsRoutes.DELETE("", deleteNextSettings)
|
||||
|
||||
router.POST("/reset", reset)
|
||||
router.POST("/full-generation", fullGeneration)
|
||||
|
||||
router.GET("/prod", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, IsProductionEnv)
|
||||
})
|
||||
router.PUT("/prod", func(c *gin.Context) {
|
||||
err := c.ShouldBindJSON(&IsProductionEnv)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, IsProductionEnv)
|
||||
})
|
||||
}
|
||||
|
||||
func NextSettingsHandler(c *gin.Context) {
|
||||
ts, err := strconv.ParseInt(string(c.Params.ByName("ts")), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid next settings identifier"})
|
||||
return
|
||||
}
|
||||
|
||||
nsf, err := settings.ReadNextSettingsFile(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", ts)), ts)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Next settings not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("next-settings", nsf)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func fullGeneration(c *gin.Context) {
|
||||
resp, err := generation.FullGeneration()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
|
||||
"errmsg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
v, _ := io.ReadAll(resp.Body)
|
||||
c.JSON(resp.StatusCode, gin.H{
|
||||
"errmsg": string(v),
|
||||
})
|
||||
}
|
||||
|
||||
func GetChallengeInfo() (*settings.ChallengeInfo, error) {
|
||||
var challengeinfo string
|
||||
var err error
|
||||
if sync.GlobalImporter == nil {
|
||||
if fd, err := os.Open(path.Join(settings.SettingsDir, settings.ChallengeFile)); err == nil {
|
||||
defer fd.Close()
|
||||
var buf []byte
|
||||
buf, err = io.ReadAll(fd)
|
||||
if err == nil {
|
||||
challengeinfo = string(buf)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
challengeinfo, err = sync.GetFileContent(sync.GlobalImporter, settings.ChallengeFile)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println("Unable to retrieve challenge.json:", err.Error())
|
||||
return nil, fmt.Errorf("Unable to retrive challenge.json: %w", err)
|
||||
}
|
||||
|
||||
s, err := settings.ReadChallengeInfo(challengeinfo)
|
||||
if err != nil {
|
||||
log.Println("Unable to ReadChallengeInfo:", err.Error())
|
||||
return nil, fmt.Errorf("Unable to read challenge info: %w", err)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func getChallengeInfo(c *gin.Context) {
|
||||
if s, err := GetChallengeInfo(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, s)
|
||||
}
|
||||
}
|
||||
|
||||
func saveChallengeInfo(c *gin.Context) {
|
||||
var info *settings.ChallengeInfo
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if sync.GlobalImporter != nil {
|
||||
jenc, err := json.Marshal(info)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = sync.WriteFileContent(sync.GlobalImporter, "challenge.json", jenc)
|
||||
if err != nil {
|
||||
log.Println("Unable to SaveChallengeInfo:", err.Error())
|
||||
// Ignore the error, try to continue
|
||||
}
|
||||
|
||||
err = sync.ImportChallengeInfo(info, DashboardDir)
|
||||
if err != nil {
|
||||
log.Println("Unable to ImportChallengeInfo:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Something goes wrong when trying to import related files: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := settings.SaveChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile), info); err != nil {
|
||||
log.Println("Unable to SaveChallengeInfo:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to save distributed challenge info: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, info)
|
||||
}
|
||||
|
||||
func getSettings(c *gin.Context) {
|
||||
s, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile))
|
||||
if err != nil {
|
||||
log.Println("Unable to ReadSettings:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to read settings: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
s.WorkInProgress = !IsProductionEnv
|
||||
c.Writer.Header().Add("X-FIC-Time", fmt.Sprintf("%d", time.Now().Unix()))
|
||||
c.JSON(http.StatusOK, s)
|
||||
}
|
||||
|
||||
func saveSettings(c *gin.Context) {
|
||||
var config *settings.Settings
|
||||
err := c.ShouldBindJSON(&config)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Is this a future setting?
|
||||
if c.Request.URL.Query().Has("t") {
|
||||
t, err := time.Parse(time.RFC3339, c.Request.URL.Query().Get("t"))
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Load current settings to perform diff later
|
||||
init_settings, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile))
|
||||
if err != nil {
|
||||
log.Println("Unable to ReadSettings:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to read settings: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
current_settings := init_settings
|
||||
// Apply already registered settings
|
||||
nsu, err := settings.MergeNextSettingsUntil(&t)
|
||||
if err == nil {
|
||||
current_settings = settings.MergeSettings(*init_settings, nsu)
|
||||
} else {
|
||||
log.Println("Unable to MergeNextSettingsUntil:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to merge next settings: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
// Keep only diff
|
||||
diff := settings.DiffSettings(current_settings, config)
|
||||
|
||||
hasItems := false
|
||||
for _, _ = range diff {
|
||||
hasItems = true
|
||||
break
|
||||
}
|
||||
|
||||
if !hasItems {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "No difference to apply."})
|
||||
return
|
||||
}
|
||||
|
||||
if !c.Request.URL.Query().Has("erase") {
|
||||
// Check if there is already diff to apply at the given time
|
||||
if nsf, err := settings.ReadNextSettingsFile(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", t.Unix())), t.Unix()); err == nil {
|
||||
for k, v := range nsf.Values {
|
||||
if _, ok := diff[k]; !ok {
|
||||
diff[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save the diff
|
||||
settings.SaveSettings(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", t.Unix())), diff)
|
||||
|
||||
// Return current settings
|
||||
c.JSON(http.StatusOK, current_settings)
|
||||
} else {
|
||||
// Just apply settings right now!
|
||||
if err := settings.SaveSettings(path.Join(settings.SettingsDir, settings.SettingsFile), config); err != nil {
|
||||
log.Println("Unable to SaveSettings:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to save settings: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
ApplySettings(config)
|
||||
c.JSON(http.StatusOK, config)
|
||||
}
|
||||
}
|
||||
|
||||
func listNextSettings(c *gin.Context) {
|
||||
nsf, err := settings.ListNextSettingsFiles()
|
||||
if err != nil {
|
||||
log.Println("Unable to ListNextSettingsFiles:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list next settings files: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, nsf)
|
||||
}
|
||||
|
||||
func getNextSettings(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, c.MustGet("next-settings").(*settings.NextSettingsFile))
|
||||
}
|
||||
|
||||
func deleteNextSettings(c *gin.Context) {
|
||||
nsf := c.MustGet("next-settings").(*settings.NextSettingsFile)
|
||||
|
||||
err := os.Remove(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", nsf.Id)))
|
||||
if err != nil {
|
||||
log.Println("Unable to remove the file:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to remove the file: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func ApplySettings(config *settings.Settings) {
|
||||
fic.PartialValidation = config.PartialValidation
|
||||
fic.UnlockedChallengeDepth = config.UnlockedChallengeDepth
|
||||
fic.UnlockedChallengeUpTo = config.UnlockedChallengeUpTo
|
||||
fic.DisplayAllFlags = config.DisplayAllFlags
|
||||
fic.HideCaseSensitivity = config.HideCaseSensitivity
|
||||
fic.UnlockedStandaloneExercices = config.UnlockedStandaloneExercices
|
||||
fic.UnlockedStandaloneExercicesByThemeStepValidation = config.UnlockedStandaloneExercicesByThemeStepValidation
|
||||
fic.UnlockedStandaloneExercicesByStandaloneExerciceValidation = config.UnlockedStandaloneExercicesByStandaloneExerciceValidation
|
||||
fic.DisplayMCQBadCount = config.DisplayMCQBadCount
|
||||
fic.FirstBlood = config.FirstBlood
|
||||
fic.SubmissionCostBase = config.SubmissionCostBase
|
||||
fic.HintCoefficient = config.HintCurCoefficient
|
||||
fic.WChoiceCoefficient = config.WChoiceCurCoefficient
|
||||
fic.ExerciceCurrentCoefficient = config.ExerciceCurCoefficient
|
||||
fic.GlobalScoreCoefficient = config.GlobalScoreCoefficient
|
||||
fic.SubmissionCostBase = config.SubmissionCostBase
|
||||
fic.SubmissionUniqueness = config.SubmissionUniqueness
|
||||
fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries
|
||||
fic.QuestionGainRatio = config.QuestionGainRatio
|
||||
|
||||
if config.DiscountedFactor != fic.DiscountedFactor {
|
||||
fic.DiscountedFactor = config.DiscountedFactor
|
||||
if err := fic.DBRecreateDiscountedView(); err != nil {
|
||||
log.Println("Unable to recreate exercices_discounted view:", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ResetSettings() error {
|
||||
return settings.SaveSettings(path.Join(settings.SettingsDir, settings.SettingsFile), &settings.Settings{
|
||||
WorkInProgress: IsProductionEnv,
|
||||
FirstBlood: fic.FirstBlood,
|
||||
SubmissionCostBase: fic.SubmissionCostBase,
|
||||
ExerciceCurCoefficient: 1,
|
||||
HintCurCoefficient: 1,
|
||||
WChoiceCurCoefficient: 1,
|
||||
GlobalScoreCoefficient: 1,
|
||||
DiscountedFactor: 0,
|
||||
QuestionGainRatio: 0,
|
||||
UnlockedStandaloneExercices: 10,
|
||||
UnlockedStandaloneExercicesByThemeStepValidation: 1,
|
||||
UnlockedStandaloneExercicesByStandaloneExerciceValidation: 0,
|
||||
AllowRegistration: false,
|
||||
CanJoinTeam: false,
|
||||
DenyTeamCreation: false,
|
||||
DenyNameChange: false,
|
||||
AcceptNewIssue: true,
|
||||
QAenabled: false,
|
||||
EnableResolutionRoute: false,
|
||||
PartialValidation: true,
|
||||
UnlockedChallengeDepth: 0,
|
||||
SubmissionUniqueness: true,
|
||||
CountOnlyNotGoodTries: true,
|
||||
DisplayAllFlags: false,
|
||||
DisplayMCQBadCount: false,
|
||||
EventKindness: false,
|
||||
})
|
||||
}
|
||||
|
||||
func ResetChallengeInfo() error {
|
||||
return settings.SaveChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile), &settings.ChallengeInfo{
|
||||
Title: "Challenge forensic",
|
||||
SubTitle: "sous le patronage du commandement de la cyberdéfense",
|
||||
Authors: "Laboratoire SRS, ÉPITA",
|
||||
VideosLink: "",
|
||||
Description: `<p>Le challenge <em>forensic</em> vous place dans la peau de <strong>spécialistes en investigation numérique</strong>. Nous mettons à votre disposition une <strong>vingtaine de scénarios différents</strong>, dans lesquels vous devrez faire les différentes étapes <strong>de la caractérisation d’une réponse à incident</strong> proposées.</p>
|
||||
<p>Chaque scénario met en scène un contexte d’<strong>entreprise</strong>, ayant découvert récemment qu’elle a été <strong>victime d’une cyberattaque</strong>. Elle vous demande alors de l’aider à <strong>caractériser</strong>, afin de mieux comprendre <strong>la situation</strong>, notamment le <strong>mode opératoire de l’adversaire</strong>, les <strong>impacts</strong> de la cyberattaque, le <strong>périmètre technique compromis</strong>, etc. Il faudra parfois aussi l’éclairer sur les premières étapes de la réaction.</p>`,
|
||||
Rules: `<h3>Déroulement</h3>
|
||||
<p>Pendant toute la durée du challenge, vous aurez <strong>accès à tous les scénarios</strong>, mais seulement à la première des 5 étapes. <strong>Chaque étape</strong> supplémentaire <strong>est débloquée lorsque vous validez l’intégralité de l’étape précédente</strong>. Toutefois, pour dynamiser le challenge toutes les étapes et tous les scénarios seront débloquées pour la dernière heure du challenge.</p>
|
||||
<p>Nous mettons à votre disposition une <strong>plateforme</strong> sur laquelle vous pourrez <strong>obtenir les informations sur le contexte</strong> de l’entreprise et, généralement, une <strong>série de fichiers</strong> qui semblent appropriés pour avancer dans l’investigation.</p>
|
||||
<p>La <strong>validation d’une étape</strong> se fait sur la plateforme, après avoir analysé les informations fournies, en <strong>répondant à des questions</strong> plus ou moins précises. Il s’agit le plus souvent des <strong>mots-clefs</strong> que l’on placerait dans un <strong>rapport</strong>.</p>
|
||||
<p>Pour vous débloquer ou accélérer votre investigation, vous pouvez accéder à quelques <strong><em>indices</em></strong>, en échange d’une décote sur votre score d’un certain nombre de points préalablement affichés.</p>
|
||||
<h3>Calcul des points, bonus, malus et classement</h3>
|
||||
<p>Chaque équipe dispose d’un <strong>compteur de points</strong> dans l’intervalle ]-∞;+∞[ (aux détails techniques près), à partir duquel <strong>le classement est établi</strong>.</p>
|
||||
<p>Vous <strong>perdez des points</strong> en <strong>dévoilant des indices</strong>, en <strong>demandant des propositions de réponses</strong> en remplacement de certains champs de texte, ou en <strong>essayant un trop grand nombre de fois une réponse</strong>.</p>
|
||||
<p>Le nombre de points que vous fait perdre un indice dépend habituellement de l’aide qu’il vous apportera et est indiqué avant de le dévoiler, car il peut fluctuer en fonction de l’avancement du challenge.</p>
|
||||
<p>Pour chaque champ de texte, vous disposez de 10 tentatives avant de perdre des points (vous perdez les points même si vous ne validez pas l’étape) pour chaque tentative supplémentaire : -0,25 point entre 11 et 20, -0,5 entre 21 et 30, -0,75 entre 31 et 40, …</p>
|
||||
<p>La seule manière de <strong>gagner des points</strong> est de <strong>valider une étape d’un scénario dans son intégralité</strong>. Le nombre de points gagnés <strong>dépend de la difficulté théorique</strong> de l’étape ainsi que <strong>d’éventuels bonus</strong>. Un bonus de <strong>10 %</strong> est accordé à la première équipe qui valide une étape. D’<strong>autres bonus</strong> peuvent ponctuer le challenge, détaillé dans la partie suivante.</p>
|
||||
<p>Le classement est établi par équipe, selon le nombre de points récoltés et perdus par tous les membres. En cas d’égalité au score, les équipes sont départagées en fonction de leur ordre d’arrivée à ce score.</p>
|
||||
<h3>Temps forts</h3>
|
||||
<p>Le challenge <em>forensic</em> est jalonné de plusieurs temps forts durant lesquels <strong>certains calculs</strong> détaillés dans la partie précédente <strong>peuvent être altérés</strong>. L’équipe d’animation du challenge vous <strong>avertira</strong> environ <strong>15 minutes avant</strong> le début de la modification.</p>
|
||||
<p>Chaque modification se répercute instantanément dans votre interface, attendez simplement qu’elle apparaisse afin d’être certain d’en bénéficier. Un compte à rebours est généralement affiché sur les écrans pour indiquer la fin d’un temps fort. La fin d’application d’un bonus est déterminé par l’heure d’arrivée de votre demande sur nos serveurs.</p>
|
||||
<p>Sans y être limité ou assuré, sachez que durant les précédentes éditions du challenge <em>forensic</em>, nous avons par exemple : <strong>doublé les points</strong> de défis peu tentés, <strong>doublé les points de tous les défis</strong> pendant 30 minutes, <strong>réduit le coût des indices</strong> pendant 15 minutes, etc.</p>
|
||||
<p></p>
|
||||
<p>Tous les étudiants de la majeure Système, Réseaux et Sécurité de l’ÉPITA, son équipe enseignante ainsi que le commandement de la cyberdéfense vous souhaitent bon courage pour cette nouvelle éditions du challenge !</p>`,
|
||||
YourMission: `<h4>Bienvenue au challenge forensic !</h4>
|
||||
<p>Vous voici aujourd'hui dans la peau de <strong>spécialistes en investigation numérique</strong>. Vous avez à votre disposition une vingtaine de scénarios différents dans lesquels vous devrez faire les différentes étapes <strong>de la caractérisation d’une réponse à incident</strong>.</p>
|
||||
<p>Chaque scénario est découpé en 5 grandes <strong>étapes de difficulté croissante</strong>. Un certain nombre de points est attribué à chaque étape, avec un processus de validation automatique.</p>
|
||||
<p>Un classement est établi en temps réel, tenant compte des différents bonus, en fonction du nombre de points de chaque équipe.</p>`,
|
||||
})
|
||||
}
|
||||
|
||||
func reset(c *gin.Context) {
|
||||
var m map[string]string
|
||||
err := c.ShouldBindJSON(&m)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
t, ok := m["type"]
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Field type not found"})
|
||||
}
|
||||
|
||||
switch t {
|
||||
case "teams":
|
||||
err = fic.ResetTeams()
|
||||
case "challenges":
|
||||
err = fic.ResetExercices()
|
||||
case "game":
|
||||
err = fic.ResetGame()
|
||||
case "annexes":
|
||||
err = fic.ResetAnnexes()
|
||||
case "settings":
|
||||
err = ResetSettings()
|
||||
case "challengeInfo":
|
||||
err = ResetChallengeInfo()
|
||||
default:
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Unknown reset type"})
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Unable to reset (type=%q): %s", t, err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to performe the reset: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
411
admin/api/sync.go
Normal file
411
admin/api/sync.go
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/generation"
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
var lastSyncError = ""
|
||||
|
||||
func flatifySyncErrors(errs error) (ret []string) {
|
||||
for _, err := range multierr.Errors(errs) {
|
||||
ret = append(ret, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func declareSyncRoutes(router *gin.RouterGroup) {
|
||||
apiSyncRoutes := router.Group("/sync")
|
||||
|
||||
// Return the global sync status
|
||||
apiSyncRoutes.GET("/status", func(c *gin.Context) {
|
||||
syncMtd := "Disabled"
|
||||
if sync.GlobalImporter != nil {
|
||||
syncMtd = sync.GlobalImporter.Kind()
|
||||
}
|
||||
|
||||
var syncId *string
|
||||
if sync.GlobalImporter != nil {
|
||||
syncId = sync.GlobalImporter.Id()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"sync-type": reflect.TypeOf(sync.GlobalImporter).Name(),
|
||||
"sync-id": syncId,
|
||||
"sync": syncMtd,
|
||||
"pullMutex": !sync.OneGitPullStatus(),
|
||||
"syncMutex": !sync.OneDeepSyncStatus() && !sync.OneThemeDeepSyncStatus(),
|
||||
"progress": sync.DeepSyncProgress,
|
||||
"lastError": lastSyncError,
|
||||
})
|
||||
})
|
||||
|
||||
// Base sync checks if the local directory is in sync with remote one.
|
||||
apiSyncRoutes.POST("/base", func(c *gin.Context) {
|
||||
err := sync.GlobalImporter.Sync()
|
||||
if err != nil {
|
||||
lastSyncError = err.Error()
|
||||
c.JSON(http.StatusExpectationFailed, gin.H{"errmsg": err.Error()})
|
||||
} else {
|
||||
lastSyncError = ""
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
})
|
||||
|
||||
// Speedy sync performs a recursive synchronization without importing files.
|
||||
apiSyncRoutes.POST("/speed", func(c *gin.Context) {
|
||||
st := sync.SpeedySyncDeep(sync.GlobalImporter)
|
||||
sync.EditDeepReport(&st, false)
|
||||
c.JSON(http.StatusOK, st)
|
||||
})
|
||||
|
||||
// Deep sync: a fully recursive synchronization (can be limited by theme).
|
||||
apiSyncRoutes.POST("/deep", func(c *gin.Context) {
|
||||
r := sync.SyncDeep(sync.GlobalImporter)
|
||||
lastSyncError = ""
|
||||
c.JSON(http.StatusOK, r)
|
||||
})
|
||||
|
||||
apiSyncRoutes.POST("/local-diff", APIDiffDBWithRemote)
|
||||
|
||||
apiSyncDeepRoutes := apiSyncRoutes.Group("/deep/:thid")
|
||||
apiSyncDeepRoutes.Use(ThemeHandler)
|
||||
// Special route to handle standalone exercices
|
||||
apiSyncRoutes.POST("/deep/0", func(c *gin.Context) {
|
||||
var st []string
|
||||
for _, se := range multierr.Errors(sync.SyncThemeDeep(sync.GlobalImporter, &fic.StandaloneExercicesTheme, 0, 250, nil)) {
|
||||
st = append(st, se.Error())
|
||||
}
|
||||
sync.EditDeepReport(&sync.SyncReport{Exercices: st}, false)
|
||||
sync.DeepSyncProgress = 255
|
||||
lastSyncError = ""
|
||||
c.JSON(http.StatusOK, st)
|
||||
})
|
||||
apiSyncDeepRoutes.POST("", func(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
exceptions := sync.LoadThemeException(sync.GlobalImporter, theme)
|
||||
|
||||
var st []string
|
||||
for _, se := range multierr.Errors(sync.SyncThemeDeep(sync.GlobalImporter, theme, 0, 250, exceptions)) {
|
||||
st = append(st, se.Error())
|
||||
}
|
||||
sync.EditDeepReport(&sync.SyncReport{Themes: map[string][]string{theme.Name: st}}, false)
|
||||
sync.DeepSyncProgress = 255
|
||||
lastSyncError = ""
|
||||
c.JSON(http.StatusOK, st)
|
||||
})
|
||||
|
||||
// Auto sync: to use with continuous deployment, in a development env
|
||||
apiSyncRoutes.POST("/auto/*p", autoSync)
|
||||
|
||||
// Themes
|
||||
apiSyncRoutes.POST("/fixurlids", fixAllURLIds)
|
||||
|
||||
apiSyncRoutes.POST("/themes", func(c *gin.Context) {
|
||||
_, errs := sync.SyncThemes(sync.GlobalImporter)
|
||||
lastSyncError = ""
|
||||
c.JSON(http.StatusOK, flatifySyncErrors(errs))
|
||||
})
|
||||
|
||||
apiSyncThemesRoutes := apiSyncRoutes.Group("/themes/:thid")
|
||||
apiSyncThemesRoutes.Use(ThemeHandler)
|
||||
apiSyncThemesRoutes.POST("/fixurlid", func(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
if theme.FixURLId() {
|
||||
v, err := theme.Update()
|
||||
if err != nil {
|
||||
log.Println("Unable to UpdateTheme after fixurlid:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when saving the theme."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, v)
|
||||
} else {
|
||||
c.AbortWithStatusJSON(http.StatusOK, 0)
|
||||
}
|
||||
})
|
||||
|
||||
// Exercices
|
||||
declareSyncExercicesRoutes(apiSyncRoutes)
|
||||
declareSyncExercicesRoutes(apiSyncThemesRoutes)
|
||||
|
||||
// Videos sync imports resolution.mp4 from path stored in database.
|
||||
apiSyncRoutes.POST("/videos", func(c *gin.Context) {
|
||||
exercices, err := fic.GetExercices()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetExercices:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve exercices list."})
|
||||
return
|
||||
}
|
||||
|
||||
for _, e := range exercices {
|
||||
if len(e.VideoURI) == 0 || !strings.HasPrefix(e.VideoURI, "$RFILES$/") {
|
||||
continue
|
||||
}
|
||||
|
||||
vpath, err := url.PathUnescape(strings.TrimPrefix(e.VideoURI, "$RFILES$/"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusExpectationFailed, gin.H{"errmsg": fmt.Sprintf("Unable to perform URL unescape: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
_, err = sync.ImportFile(sync.GlobalImporter, vpath, func(filePath, URI string) (interface{}, error) {
|
||||
e.VideoURI = path.Join("$FILES$", strings.TrimPrefix(filePath, fic.FilesDir))
|
||||
return e.Update()
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusExpectationFailed, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
})
|
||||
|
||||
// Remove soluces from the database.
|
||||
apiSyncRoutes.POST("/drop_soluces", func(c *gin.Context) {
|
||||
exercices, err := fic.GetExercices()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetExercices:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve exercices list."})
|
||||
return
|
||||
}
|
||||
|
||||
var errs error
|
||||
for _, e := range exercices {
|
||||
// Remove any published video
|
||||
if len(e.VideoURI) > 0 && strings.HasPrefix(e.VideoURI, "$FILES$") {
|
||||
vpath := path.Join(fic.FilesDir, strings.TrimPrefix(e.VideoURI, "$FILES$/"))
|
||||
err = os.Remove(vpath)
|
||||
if err != nil {
|
||||
errs = multierr.Append(errs, fmt.Errorf("unable to delete published video (%q): %w", e.VideoURI, err))
|
||||
}
|
||||
}
|
||||
|
||||
// Clean the database
|
||||
if len(e.VideoURI) > 0 || len(e.Resolution) > 0 {
|
||||
e.VideoURI = ""
|
||||
e.Resolution = ""
|
||||
|
||||
_, err = e.Update()
|
||||
if err != nil {
|
||||
errs = multierr.Append(errs, fmt.Errorf("unable to update exercice (%d: %s): %w", e.Id, e.Title, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": flatifySyncErrors(err)})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func declareSyncExercicesRoutes(router *gin.RouterGroup) {
|
||||
router.POST("/exercices", func(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
exceptions := sync.LoadThemeException(sync.GlobalImporter, theme)
|
||||
|
||||
_, errs := sync.SyncExercices(sync.GlobalImporter, theme, exceptions)
|
||||
c.JSON(http.StatusOK, flatifySyncErrors(errs))
|
||||
})
|
||||
apiSyncExercicesRoutes := router.Group("/exercices/:eid")
|
||||
apiSyncExercicesRoutes.Use(ExerciceHandler)
|
||||
apiSyncExercicesRoutes.POST("", func(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
|
||||
exceptions := sync.LoadExerciceException(sync.GlobalImporter, theme, exercice, nil)
|
||||
|
||||
_, _, _, errs := sync.SyncExercice(sync.GlobalImporter, theme, exercice.Path, nil, exceptions)
|
||||
c.JSON(http.StatusOK, flatifySyncErrors(errs))
|
||||
})
|
||||
apiSyncExercicesRoutes.POST("/files", func(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
exceptions := sync.LoadExerciceException(sync.GlobalImporter, theme, exercice, nil)
|
||||
|
||||
c.JSON(http.StatusOK, flatifySyncErrors(sync.ImportExerciceFiles(sync.GlobalImporter, exercice, exceptions)))
|
||||
})
|
||||
apiSyncExercicesRoutes.POST("/fixurlid", func(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
if exercice.FixURLId() {
|
||||
v, err := exercice.Update()
|
||||
if err != nil {
|
||||
log.Println("Unable to UpdateExercice after fixurlid:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when saving the exercice."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, v)
|
||||
} else {
|
||||
c.AbortWithStatusJSON(http.StatusOK, 0)
|
||||
}
|
||||
})
|
||||
apiSyncExercicesRoutes.POST("/hints", func(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
exceptions := sync.LoadExerciceException(sync.GlobalImporter, theme, exercice, nil)
|
||||
|
||||
_, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice), exceptions)
|
||||
c.JSON(http.StatusOK, flatifySyncErrors(errs))
|
||||
})
|
||||
apiSyncExercicesRoutes.POST("/flags", func(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
exceptions := sync.LoadExerciceException(sync.GlobalImporter, theme, exercice, nil)
|
||||
_, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice, exceptions)
|
||||
_, herrs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice), exceptions)
|
||||
c.JSON(http.StatusOK, flatifySyncErrors(multierr.Append(errs, herrs)))
|
||||
})
|
||||
}
|
||||
|
||||
// autoSync tries to performs a smart synchronization, when in development environment.
|
||||
// It'll sync most of modified things, and will delete out of sync data.
|
||||
// Avoid using it in a production environment.
|
||||
func autoSync(c *gin.Context) {
|
||||
p := strings.Split(strings.TrimPrefix(c.Params.ByName("p"), "/"), "/")
|
||||
|
||||
if !IsProductionEnv {
|
||||
if err := sync.GlobalImporter.Sync(); err != nil {
|
||||
lastSyncError = err.Error()
|
||||
log.Println("Unable to sync.GI.Sync:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to perform the pull."})
|
||||
return
|
||||
}
|
||||
lastSyncError = ""
|
||||
}
|
||||
|
||||
themes, err := fic.GetThemes()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetThemes:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve theme list."})
|
||||
return
|
||||
}
|
||||
|
||||
// No argument, do a deep sync
|
||||
if len(p) == 0 {
|
||||
if !IsProductionEnv {
|
||||
for _, theme := range themes {
|
||||
theme.DeleteDeep()
|
||||
}
|
||||
}
|
||||
|
||||
st := sync.SyncDeep(sync.GlobalImporter)
|
||||
c.JSON(http.StatusOK, st)
|
||||
return
|
||||
}
|
||||
|
||||
var theTheme *fic.Theme
|
||||
|
||||
// Find the given theme
|
||||
for _, theme := range themes {
|
||||
if theme.Path == p[0] {
|
||||
theTheme = theme
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if theTheme == nil {
|
||||
// The theme doesn't exists locally, perhaps it has not been imported already?
|
||||
rThemes, err := sync.GetThemes(sync.GlobalImporter)
|
||||
if err == nil {
|
||||
for _, theme := range rThemes {
|
||||
if theme == p[0] {
|
||||
sync.SyncThemes(sync.GlobalImporter)
|
||||
|
||||
themes, err := fic.GetThemes()
|
||||
if err == nil {
|
||||
for _, theme := range themes {
|
||||
if theme.Path == p[0] {
|
||||
theTheme = theme
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if theTheme == nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("Theme not found %q", p[0])})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !IsProductionEnv {
|
||||
exercices, err := theTheme.GetExercices()
|
||||
if err == nil {
|
||||
for _, exercice := range exercices {
|
||||
if len(p) <= 1 || exercice.Path == path.Join(p[0], p[1]) {
|
||||
exercice.DeleteDeep()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exceptions := sync.LoadThemeException(sync.GlobalImporter, theTheme)
|
||||
|
||||
var st []string
|
||||
for _, se := range multierr.Errors(sync.SyncThemeDeep(sync.GlobalImporter, theTheme, 0, 250, exceptions)) {
|
||||
st = append(st, se.Error())
|
||||
}
|
||||
sync.EditDeepReport(&sync.SyncReport{Themes: map[string][]string{theTheme.Name: st}}, false)
|
||||
sync.DeepSyncProgress = 255
|
||||
|
||||
resp, err := generation.FullGeneration()
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, st)
|
||||
}
|
||||
|
||||
func diffDBWithRemote() (map[string][]syncDiff, error) {
|
||||
diffs := map[string][]syncDiff{}
|
||||
|
||||
themes, err := fic.GetThemesExtended()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Compare inner themes
|
||||
for _, theme := range themes {
|
||||
diffs[theme.Name], err = diffThemeWithRemote(theme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to diffThemeWithRemote: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return diffs, err
|
||||
}
|
||||
|
||||
func APIDiffDBWithRemote(c *gin.Context) {
|
||||
diffs, err := diffDBWithRemote()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, diffs)
|
||||
}
|
||||
641
admin/api/team.go
Normal file
641
admin/api/team.go
Normal file
|
|
@ -0,0 +1,641 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/pki"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func declareTeamsRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/teams.json", func(c *gin.Context) {
|
||||
teams, err := fic.ExportTeams(false)
|
||||
if err != nil {
|
||||
log.Println("Unable to ExportTeams:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during teams export."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, teams)
|
||||
})
|
||||
router.GET("/teams-members.json", func(c *gin.Context) {
|
||||
teams, err := fic.ExportTeams(true)
|
||||
if err != nil {
|
||||
log.Println("Unable to ExportTeams:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during teams export."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, teams)
|
||||
})
|
||||
router.GET("/teams-associations.json", allAssociations)
|
||||
router.GET("/teams-binding", bindingTeams)
|
||||
router.GET("/teams-nginx", nginxGenTeams)
|
||||
router.POST("/refine_colors", refineTeamsColors)
|
||||
router.POST("/disableinactiveteams", disableInactiveTeams)
|
||||
router.POST("/enableallteams", enableAllTeams)
|
||||
router.GET("/teams-members-nginx", nginxGenMember)
|
||||
router.GET("/teams-tries.json", func(c *gin.Context) {
|
||||
tries, err := fic.GetTries(nil, nil)
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTries:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieves tries."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, tries)
|
||||
})
|
||||
|
||||
router.GET("/teams", func(c *gin.Context) {
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeams:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during teams listing."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, teams)
|
||||
})
|
||||
router.POST("/teams", createTeam)
|
||||
|
||||
apiTeamsRoutes := router.Group("/teams/:tid")
|
||||
apiTeamsRoutes.Use(TeamHandler)
|
||||
apiTeamsRoutes.GET("/", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, c.MustGet("team").(*fic.Team))
|
||||
})
|
||||
apiTeamsRoutes.PUT("/", updateTeam)
|
||||
apiTeamsRoutes.POST("/", addTeamMember)
|
||||
apiTeamsRoutes.DELETE("/", deleteTeam)
|
||||
apiTeamsRoutes.GET("/score-grid.json", func(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
sg, err := team.ScoreGrid()
|
||||
if err != nil {
|
||||
log.Printf("Unable to get ScoreGrid(tid=%d): %s", team.Id, err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during score grid calculation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, sg)
|
||||
})
|
||||
|
||||
apiTeamsPublicRoutes := router.Group("/teams/:tid")
|
||||
apiTeamsPublicRoutes.Use(TeamPublicHandler)
|
||||
apiTeamsPublicRoutes.GET("/my.json", func(c *gin.Context) {
|
||||
var team *fic.Team
|
||||
if t, ok := c.Get("team"); ok && t != nil {
|
||||
team = t.(*fic.Team)
|
||||
}
|
||||
tfile, err := fic.MyJSONTeam(team, true)
|
||||
if err != nil {
|
||||
log.Println("Unable to get MyJSONTeam:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team JSON generation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, tfile)
|
||||
})
|
||||
apiTeamsPublicRoutes.GET("/wait.json", func(c *gin.Context) {
|
||||
var team *fic.Team
|
||||
if t, ok := c.Get("team"); ok && t != nil {
|
||||
team = t.(*fic.Team)
|
||||
}
|
||||
tfile, err := fic.MyJSONTeam(team, false)
|
||||
if err != nil {
|
||||
log.Println("Unable to get MyJSONTeam:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team JSON generation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, tfile)
|
||||
})
|
||||
apiTeamsPublicRoutes.GET("/stats.json", func(c *gin.Context) {
|
||||
var team *fic.Team
|
||||
if t, ok := c.Get("team"); ok && t != nil {
|
||||
team = t.(*fic.Team)
|
||||
}
|
||||
if team != nil {
|
||||
stats, err := team.GetStats()
|
||||
if err != nil {
|
||||
log.Println("Unable to get GetStats:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during stats calculation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, stats)
|
||||
} else {
|
||||
stats, err := fic.GetTeamsStats(nil)
|
||||
if err != nil {
|
||||
log.Println("Unable to get GetTeamsStats:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during global stats calculation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, stats)
|
||||
}
|
||||
})
|
||||
apiTeamsRoutes.GET("/history.json", func(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
history, err := team.GetHistory()
|
||||
if err != nil {
|
||||
log.Println("Unable to get GetHistory:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during history calculation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, history)
|
||||
})
|
||||
apiTeamsRoutes.PATCH("/history.json", updateHistory)
|
||||
apiTeamsRoutes.DELETE("/history.json", delHistory)
|
||||
apiTeamsPublicRoutes.GET("/tries", func(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
tries, err := fic.GetTries(team, nil)
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTries:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during tries calculation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, tries)
|
||||
})
|
||||
apiTeamsRoutes.GET("/members", func(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
members, err := team.GetMembers()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetMembers:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during members retrieval."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, members)
|
||||
})
|
||||
apiTeamsRoutes.POST("/members", addTeamMember)
|
||||
apiTeamsRoutes.PUT("/members", setTeamMember)
|
||||
|
||||
declareTeamsPasswordRoutes(apiTeamsRoutes)
|
||||
declareTeamClaimsRoutes(apiTeamsRoutes)
|
||||
declareTeamCertificateRoutes(apiTeamsRoutes)
|
||||
|
||||
// Import teams from cyberrange
|
||||
router.POST("/cyberrange-teams.json", importTeamsFromCyberrange)
|
||||
}
|
||||
|
||||
func TeamHandler(c *gin.Context) {
|
||||
tid, err := strconv.ParseInt(string(c.Params.ByName("tid")), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid team identifier"})
|
||||
return
|
||||
}
|
||||
|
||||
team, err := fic.GetTeam(tid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Team not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("team", team)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func TeamPublicHandler(c *gin.Context) {
|
||||
tid, err := strconv.ParseInt(string(c.Params.ByName("tid")), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid team identifier"})
|
||||
return
|
||||
}
|
||||
|
||||
if tid != 0 {
|
||||
team, err := fic.GetTeam(tid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Team not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("team", team)
|
||||
} else {
|
||||
c.Set("team", nil)
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func nginxGenTeams(c *gin.Context) {
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeams:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
ret := ""
|
||||
for _, team := range teams {
|
||||
ret += fmt.Sprintf(" if ($remote_user = \"%s\") { set $team \"%d\"; }\n", strings.ToLower(team.Name), team.Id)
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
func nginxGenMember(c *gin.Context) {
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeams:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
ret := ""
|
||||
for _, team := range teams {
|
||||
if members, err := team.GetMembers(); err == nil {
|
||||
for _, member := range members {
|
||||
ret += fmt.Sprintf(" if ($remote_user = \"%s\") { set $team \"%d\"; }\n", member.Nickname, team.Id)
|
||||
}
|
||||
} else {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
func bindingTeams(c *gin.Context) {
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeams:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
ret := ""
|
||||
for _, team := range teams {
|
||||
if members, err := team.GetMembers(); err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
} else {
|
||||
var mbs []string
|
||||
for _, member := range members {
|
||||
mbs = append(mbs, fmt.Sprintf("%s %s", member.Firstname, member.Lastname))
|
||||
}
|
||||
ret += fmt.Sprintf("%d;%s;%s\n", team.Id, team.Name, strings.Join(mbs, ";"))
|
||||
}
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
type teamAssociation struct {
|
||||
Association string `json:"association"`
|
||||
TeamId int64 `json:"team_id"`
|
||||
}
|
||||
|
||||
func allAssociations(c *gin.Context) {
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeams:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
var ret []teamAssociation
|
||||
|
||||
for _, team := range teams {
|
||||
assocs, err := pki.GetTeamAssociations(TeamsDir, team.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, a := range assocs {
|
||||
ret = append(ret, teamAssociation{a, team.Id})
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
func importTeamsFromCyberrange(c *gin.Context) {
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"errmsg": "Failed to get file: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Failed to open file: " + err.Error()})
|
||||
return
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
var ut []fic.CyberrangeTeamBase
|
||||
err = json.NewDecoder(src).Decode(&fic.CyberrangeAPIResponse{Data: &ut})
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible de récupérer la liste des équipes actuelles: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
for _, crteam := range ut {
|
||||
var exist_team *fic.Team
|
||||
for _, team := range teams {
|
||||
if team.Name == crteam.Name || team.ExternalId == crteam.UUID {
|
||||
exist_team = team
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if exist_team != nil {
|
||||
exist_team.Name = crteam.Name
|
||||
exist_team.ExternalId = crteam.UUID
|
||||
_, err = exist_team.Update()
|
||||
} else {
|
||||
exist_team, err = fic.CreateTeam(crteam.Name, fic.RandomColor().ToRGB(), crteam.UUID)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible d'ajouter/de modifier l'équipe %v: %s", crteam, err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
// Import members
|
||||
if c.DefaultQuery("nomembers", "0") != "" && len(crteam.Members) > 0 {
|
||||
exist_team.ClearMembers()
|
||||
|
||||
for _, member := range crteam.Members {
|
||||
_, err = exist_team.AddMember(member.Name, "", member.Nickname, exist_team.Name)
|
||||
if err != nil {
|
||||
log.Printf("Unable to add member %q to team %s (tid=%d): %s", member.UUID, exist_team.Name, exist_team.Id, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
teams, err = fic.GetTeams()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible de récupérer la liste des équipes après import: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, teams)
|
||||
}
|
||||
|
||||
func createTeam(c *gin.Context) {
|
||||
var ut fic.Team
|
||||
err := c.ShouldBindJSON(&ut)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if ut.Color == 0 {
|
||||
ut.Color = fic.RandomColor().ToRGB()
|
||||
}
|
||||
|
||||
team, err := fic.CreateTeam(strings.TrimSpace(ut.Name), ut.Color, ut.ExternalId)
|
||||
if err != nil {
|
||||
log.Println("Unable to CreateTeam:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team creation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, team)
|
||||
}
|
||||
|
||||
func updateTeam(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
var ut fic.Team
|
||||
err := c.ShouldBindJSON(&ut)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ut.Id = team.Id
|
||||
|
||||
if ut.Password != nil && *ut.Password == "" {
|
||||
ut.Password = nil
|
||||
}
|
||||
|
||||
_, err = ut.Update()
|
||||
if err != nil {
|
||||
log.Println("Unable to updateTeam:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team updating."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ut)
|
||||
}
|
||||
|
||||
func refineTeamsColors(c *gin.Context) {
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeams:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
for i, team := range teams {
|
||||
team.Color = fic.HSL{
|
||||
H: float64(i)/float64(len(teams)) - 0.2,
|
||||
S: float64(1) / float64(1+i%2),
|
||||
L: 0.25 + float64(0.5)/float64(1+i%3),
|
||||
}.ToRGB()
|
||||
|
||||
_, err = team.Update()
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, teams)
|
||||
}
|
||||
|
||||
func disableInactiveTeams(c *gin.Context) {
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeams:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, team := range teams {
|
||||
var serials []uint64
|
||||
serials, err = pki.GetTeamSerials(TeamsDir, team.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
var assocs []string
|
||||
assocs, err = pki.GetTeamAssociations(TeamsDir, team.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(serials) == 0 && len(assocs) == 0 {
|
||||
if team.Active {
|
||||
team.Active = false
|
||||
team.Update()
|
||||
}
|
||||
} else if !team.Active {
|
||||
team.Active = true
|
||||
team.Update()
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func enableAllTeams(c *gin.Context) {
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeams:", err.Error())
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, team := range teams {
|
||||
if !team.Active {
|
||||
team.Active = true
|
||||
team.Update()
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func deleteTeam(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
assocs, err := pki.GetTeamAssociations(TeamsDir, team.Id)
|
||||
if err != nil {
|
||||
log.Printf("Unable to GetTeamAssociations(tid=%d): %s", team.Id, err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to retrieve team association."})
|
||||
return
|
||||
}
|
||||
|
||||
for _, assoc := range assocs {
|
||||
err = pki.DeleteTeamAssociation(TeamsDir, assoc)
|
||||
if err != nil {
|
||||
log.Printf("Unable to DeleteTeamAssociation(assoc=%s): %s", assoc, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = team.Delete()
|
||||
if err != nil {
|
||||
log.Println("Unable to deleteTeam:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team deletion."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func addTeamMember(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
var members []fic.Member
|
||||
err := c.ShouldBindJSON(&members)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
for _, member := range members {
|
||||
_, err := team.AddMember(strings.TrimSpace(member.Firstname), strings.TrimSpace(member.Lastname), strings.TrimSpace(member.Nickname), strings.TrimSpace(member.Company))
|
||||
if err != nil {
|
||||
log.Println("Unable to AddMember:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during member creation."})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
mmbrs, err := team.GetMembers()
|
||||
if err != nil {
|
||||
log.Println("Unable to retrieve members list:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve members list."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, mmbrs)
|
||||
}
|
||||
|
||||
func setTeamMember(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
team.ClearMembers()
|
||||
addTeamMember(c)
|
||||
}
|
||||
|
||||
type uploadedHistory struct {
|
||||
Kind string
|
||||
Time time.Time
|
||||
Primary *int64
|
||||
Secondary *int64
|
||||
Coefficient float32
|
||||
}
|
||||
|
||||
func updateHistory(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
var uh uploadedHistory
|
||||
err := c.ShouldBindJSON(&uh)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var givenId int64
|
||||
if uh.Secondary != nil {
|
||||
givenId = *uh.Secondary
|
||||
} else if uh.Primary != nil {
|
||||
givenId = *uh.Primary
|
||||
}
|
||||
|
||||
_, err = team.UpdateHistoryCoeff(uh.Kind, uh.Time, givenId, uh.Coefficient)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to update this history line: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func delHistory(c *gin.Context) {
|
||||
team := c.MustGet("team").(*fic.Team)
|
||||
|
||||
var uh uploadedHistory
|
||||
err := c.ShouldBindJSON(&uh)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
_, err = team.DelHistoryItem(uh.Kind, uh.Time, uh.Primary, uh.Secondary)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to delete this history line: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
376
admin/api/theme.go
Normal file
376
admin/api/theme.go
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
"srs.epita.fr/fic-server/settings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func declareThemesRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/themes", listThemes)
|
||||
router.POST("/themes", createTheme)
|
||||
router.GET("/themes.json", exportThemes)
|
||||
router.GET("/session-forensic.yaml", func(c *gin.Context) {
|
||||
if s, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)); err != nil {
|
||||
log.Printf("Unable to ReadSettings: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during settings reading."})
|
||||
return
|
||||
|
||||
} else if challengeinfo, err := sync.GetFileContent(sync.GlobalImporter, "challenge.json"); err != nil {
|
||||
log.Println("Unable to retrieve challenge.json:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to retrive challenge.json: %s", err.Error())})
|
||||
return
|
||||
} else if ch, err := settings.ReadChallengeInfo(challengeinfo); err != nil {
|
||||
log.Printf("Unable to ReadChallengeInfo: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during challenge info reading."})
|
||||
return
|
||||
} else if sf, err := fic.GenZQDSSessionFile(ch, s); err != nil {
|
||||
log.Printf("Unable to GenZQDSSessionFile: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during session file generation."})
|
||||
return
|
||||
} else {
|
||||
c.JSON(http.StatusOK, sf)
|
||||
}
|
||||
})
|
||||
router.GET("/files-bindings", bindingFiles)
|
||||
|
||||
apiThemesRoutes := router.Group("/themes/:thid")
|
||||
apiThemesRoutes.Use(ThemeHandler)
|
||||
apiThemesRoutes.GET("", showTheme)
|
||||
apiThemesRoutes.PUT("", updateTheme)
|
||||
apiThemesRoutes.DELETE("", deleteTheme)
|
||||
|
||||
apiThemesRoutes.POST("/diff-sync", APIDiffThemeWithRemote)
|
||||
|
||||
apiThemesRoutes.GET("/exercices_stats.json", getThemedExercicesStats)
|
||||
|
||||
declareExercicesRoutes(apiThemesRoutes)
|
||||
|
||||
// Remote
|
||||
router.GET("/remote/themes", sync.ApiListRemoteThemes)
|
||||
router.GET("/remote/themes/:thid", sync.ApiGetRemoteTheme)
|
||||
router.GET("/remote/themes/:thid/exercices", sync.ApiListRemoteExercices)
|
||||
}
|
||||
|
||||
type Theme struct {
|
||||
*fic.Theme
|
||||
ForgeLink string `json:"forge_link,omitempty"`
|
||||
}
|
||||
|
||||
func ThemeHandler(c *gin.Context) {
|
||||
thid, err := strconv.ParseInt(string(c.Params.ByName("thid")), 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid theme identifier"})
|
||||
return
|
||||
}
|
||||
|
||||
if thid == 0 {
|
||||
c.Set("theme", &fic.StandaloneExercicesTheme)
|
||||
} else {
|
||||
theme, err := fic.GetTheme(thid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Theme not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("theme", theme)
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func fixAllURLIds(c *gin.Context) {
|
||||
nbFix := 0
|
||||
if themes, err := fic.GetThemes(); err == nil {
|
||||
for _, theme := range themes {
|
||||
if theme.FixURLId() {
|
||||
theme.Update()
|
||||
nbFix += 1
|
||||
}
|
||||
|
||||
if exercices, err := theme.GetExercices(); err == nil {
|
||||
for _, exercice := range exercices {
|
||||
if exercice.FixURLId() {
|
||||
exercice.Update()
|
||||
nbFix += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, nbFix)
|
||||
}
|
||||
|
||||
func bindingFiles(c *gin.Context) {
|
||||
files, err := fic.GetFiles()
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
ret := ""
|
||||
for _, file := range files {
|
||||
ret += fmt.Sprintf("%s;%s\n", file.GetOrigin(), file.Path)
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
func listThemes(c *gin.Context) {
|
||||
themes, err := fic.GetThemes()
|
||||
if err != nil {
|
||||
log.Println("Unable to listThemes:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to list themes."})
|
||||
return
|
||||
}
|
||||
|
||||
if has, _ := fic.HasStandaloneExercice(); has {
|
||||
themes = append([]*fic.Theme{&fic.StandaloneExercicesTheme}, themes...)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, themes)
|
||||
}
|
||||
|
||||
func exportThemes(c *gin.Context) {
|
||||
themes, err := fic.ExportThemes()
|
||||
if err != nil {
|
||||
log.Println("Unable to exportthemes:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to export themes."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, themes)
|
||||
}
|
||||
|
||||
func showTheme(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
var forgelink string
|
||||
if fli, ok := sync.GlobalImporter.(sync.ForgeLinkedImporter); ok {
|
||||
if u, _ := fli.GetThemeLink(theme); u != nil {
|
||||
forgelink = u.String()
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, Theme{theme, forgelink})
|
||||
}
|
||||
|
||||
func createTheme(c *gin.Context) {
|
||||
var ut fic.Theme
|
||||
err := c.ShouldBindJSON(&ut)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if len(ut.Name) == 0 {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Theme's name not filled"})
|
||||
return
|
||||
}
|
||||
|
||||
th, err := fic.CreateTheme(&ut)
|
||||
if err != nil {
|
||||
log.Println("Unable to createTheme:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during theme creation."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, th)
|
||||
}
|
||||
|
||||
func updateTheme(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
var ut fic.Theme
|
||||
err := c.ShouldBindJSON(&ut)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ut.Id = theme.Id
|
||||
|
||||
if len(ut.Name) == 0 {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Theme's name not filled"})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := ut.Update(); err != nil {
|
||||
log.Println("Unable to updateTheme:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during theme update."})
|
||||
return
|
||||
}
|
||||
|
||||
if theme.Locked != ut.Locked {
|
||||
exercices, err := theme.GetExercices()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
for _, exercice := range exercices {
|
||||
if exercice.Disabled != ut.Locked {
|
||||
exercice.Disabled = ut.Locked
|
||||
_, err = exercice.Update()
|
||||
if err != nil {
|
||||
log.Println("Unable to enable/disable exercice: ", exercice.Id, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ut)
|
||||
}
|
||||
|
||||
func deleteTheme(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
_, err := theme.Delete()
|
||||
if err != nil {
|
||||
log.Println("Unable to deleteTheme:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during theme deletion."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func getThemedExercicesStats(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
exercices, err := theme.GetExercices()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to fetch exercices: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
ret := []exerciceStats{}
|
||||
for _, e := range exercices {
|
||||
ret = append(ret, exerciceStats{
|
||||
IdExercice: e.Id,
|
||||
TeamTries: e.TriedTeamCount(),
|
||||
TotalTries: e.TriedCount(),
|
||||
SolvedCount: e.SolvedCount(),
|
||||
FlagSolved: e.FlagSolved(),
|
||||
MCQSolved: e.MCQSolved(),
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
func diffThemeWithRemote(theme *fic.Theme) ([]syncDiff, error) {
|
||||
var diffs []syncDiff
|
||||
|
||||
// Compare theme attributes
|
||||
theme_remote, err := sync.GetRemoteTheme(theme.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, field := range reflect.VisibleFields(reflect.TypeOf(*theme)) {
|
||||
if ((field.Name == "Image") && path.Base(reflect.ValueOf(*theme_remote).FieldByName(field.Name).String()) != path.Base(reflect.ValueOf(*theme).FieldByName(field.Name).String())) || (field.Name != "Image" && !reflect.ValueOf(*theme_remote).FieldByName(field.Name).Equal(reflect.ValueOf(*theme).FieldByName(field.Name))) {
|
||||
if !field.IsExported() || field.Name == "Id" || field.Name == "IdTheme" || field.Name == "IssueKind" || field.Name == "BackgroundColor" {
|
||||
continue
|
||||
}
|
||||
|
||||
diffs = append(diffs, syncDiff{
|
||||
Field: field.Name,
|
||||
Link: fmt.Sprintf("themes/%d", theme.Id),
|
||||
Before: reflect.ValueOf(*theme).FieldByName(field.Name).Interface(),
|
||||
After: reflect.ValueOf(*theme_remote).FieldByName(field.Name).Interface(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Compare exercices list
|
||||
exercices, err := theme.GetExercices()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to GetExercices: %w", err)
|
||||
}
|
||||
|
||||
exercices_remote, err := sync.ListRemoteExercices(theme.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to ListRemoteExercices: %w", err)
|
||||
}
|
||||
|
||||
var not_found []string
|
||||
var extra_found []string
|
||||
|
||||
for _, exercice_remote := range exercices_remote {
|
||||
found := false
|
||||
for _, exercice := range exercices {
|
||||
if exercice.Path[strings.Index(exercice.Path, "/")+1:] == exercice_remote {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
not_found = append(not_found, exercice_remote)
|
||||
}
|
||||
}
|
||||
|
||||
for _, exercice := range exercices {
|
||||
found := false
|
||||
for _, exercice_remote := range exercices_remote {
|
||||
if exercice.Path[strings.Index(exercice.Path, "/")+1:] == exercice_remote {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
extra_found = append(extra_found, exercice.Path[strings.Index(exercice.Path, "/")+1:])
|
||||
}
|
||||
}
|
||||
|
||||
if len(not_found) > 0 || len(extra_found) > 0 {
|
||||
diffs = append(diffs, syncDiff{
|
||||
Field: "theme.Exercices",
|
||||
Link: fmt.Sprintf("themes/%d", theme.Id),
|
||||
Before: strings.Join(extra_found, ", "),
|
||||
After: strings.Join(not_found, ", "),
|
||||
})
|
||||
}
|
||||
|
||||
// Compare inner exercices
|
||||
for i, exercice := range exercices {
|
||||
exdiffs, err := diffExerciceWithRemote(exercice, theme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to diffExerciceWithRemote: %w", err)
|
||||
}
|
||||
|
||||
for _, exdiff := range exdiffs {
|
||||
if theme.Id == 0 {
|
||||
exdiff.Field = fmt.Sprintf("exercices[%d].%s", exercice.Id, exdiff.Field)
|
||||
} else {
|
||||
exdiff.Field = fmt.Sprintf("exercices[%d].%s", i, exdiff.Field)
|
||||
}
|
||||
diffs = append(diffs, exdiff)
|
||||
}
|
||||
}
|
||||
|
||||
return diffs, err
|
||||
}
|
||||
|
||||
func APIDiffThemeWithRemote(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
diffs, err := diffThemeWithRemote(theme)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, diffs)
|
||||
}
|
||||
15
admin/api/version.go
Normal file
15
admin/api/version.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func DeclareVersionRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/version", showVersion)
|
||||
}
|
||||
|
||||
func showVersion(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"version": 1.0})
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
func CertificateAPI(team fic.Team, args []string) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
if args[0] == "generate" {
|
||||
return team.GenerateCert(), nil
|
||||
} else if args[0] == "revoke" {
|
||||
return team.RevokeCert(), nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
} else if fd, err := os.Open("../PKI/pkcs/" + team.Name + ".p12"); err == nil {
|
||||
return ioutil.ReadAll(fd)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var ApiCARouting = map[string]DispatchFunction{
|
||||
"GET": genCA,
|
||||
}
|
||||
|
||||
func genCA(args []string, body []byte) (interface{}, error) {
|
||||
return fic.GenerateCA(), nil
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var ApiEventsRouting = map[string]DispatchFunction{
|
||||
"GET": getEvents,
|
||||
}
|
||||
|
||||
func getEvents(args []string, body []byte) (interface{}, error) {
|
||||
if evts, err := fic.GetEvents(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return evts, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var ApiExercicesRouting = map[string]DispatchFunction{
|
||||
"GET": listExercice,
|
||||
"PATCH": updateExercice,
|
||||
"DELETE": deletionExercice,
|
||||
}
|
||||
|
||||
func listExercice(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
if eid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return fic.GetExercice(int64(eid))
|
||||
}
|
||||
} else {
|
||||
// List all exercices
|
||||
return fic.GetExercices()
|
||||
}
|
||||
}
|
||||
|
||||
func deletionExercice(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
if eid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return exercice.Delete()
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
type uploadedExercice struct {
|
||||
Title string
|
||||
Statement string
|
||||
Hint string
|
||||
Depend *int64
|
||||
Gain int
|
||||
VideoURI string
|
||||
}
|
||||
|
||||
func updateExercice(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
if eid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// Update an exercice
|
||||
var ue uploadedExercice
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ue.Title) == 0 {
|
||||
return nil, errors.New("Exercice's title not filled")
|
||||
}
|
||||
|
||||
if ue.Depend != nil {
|
||||
if _, err := fic.GetExercice(*ue.Depend); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
exercice.Title = ue.Title
|
||||
exercice.Statement = ue.Statement
|
||||
exercice.Hint = ue.Hint
|
||||
exercice.Depend = ue.Depend
|
||||
exercice.Gain = int64(ue.Gain)
|
||||
exercice.VideoURI = ue.VideoURI
|
||||
|
||||
return exercice.Update()
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func createExercice(theme fic.Theme, args []string, body []byte) (interface{}, error) {
|
||||
if len(args) >= 1 {
|
||||
if eid, err := strconv.Atoi(args[0]); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := theme.GetExercice(eid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if args[1] == "files" {
|
||||
return createExerciceFile(theme, exercice, args[2:], body)
|
||||
} else if args[1] == "keys" {
|
||||
return createExerciceKey(theme, exercice, args[2:], body)
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
} else {
|
||||
// Create a new exercice
|
||||
var ue uploadedExercice
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ue.Title) == 0 {
|
||||
return nil, errors.New("Title not filled")
|
||||
}
|
||||
|
||||
var depend *fic.Exercice = nil
|
||||
if ue.Depend != nil {
|
||||
if d, err := fic.GetExercice(*ue.Depend); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
depend = &d
|
||||
}
|
||||
}
|
||||
|
||||
return theme.AddExercice(ue.Title, ue.Statement, ue.Hint, depend, ue.Gain, ue.VideoURI)
|
||||
}
|
||||
}
|
||||
|
||||
type uploadedKey struct {
|
||||
Name string
|
||||
Key string
|
||||
}
|
||||
|
||||
func createExerciceKey(theme fic.Theme, exercice fic.Exercice, args []string, body []byte) (interface{}, error) {
|
||||
var uk uploadedKey
|
||||
if err := json.Unmarshal(body, &uk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(uk.Key) == 0 {
|
||||
return nil, errors.New("Key not filled")
|
||||
}
|
||||
|
||||
return exercice.AddRawKey(uk.Name, uk.Key)
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha512"
|
||||
"encoding/base32"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
type uploadedFile struct {
|
||||
URI string
|
||||
}
|
||||
|
||||
func createExerciceFile(theme fic.Theme, exercice fic.Exercice, args []string, body []byte) (interface{}, error) {
|
||||
var uf uploadedFile
|
||||
if err := json.Unmarshal(body, &uf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(uf.URI) == 0 {
|
||||
return nil, errors.New("URI not filled")
|
||||
}
|
||||
|
||||
hash := sha512.Sum512([]byte(uf.URI))
|
||||
pathname := path.Join(fic.FilesDir, strings.ToLower(base32.StdEncoding.EncodeToString(hash[:])), path.Base(uf.URI))
|
||||
|
||||
if _, err := os.Stat(pathname); os.IsNotExist(err) {
|
||||
log.Println("Import file from Cloud:", uf.URI, "=>", pathname)
|
||||
if err := os.MkdirAll(path.Dir(pathname), 0777); err != nil {
|
||||
return nil, err
|
||||
} else if err := getCloudFile(uf.URI, pathname); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return exercice.ImportFile(pathname, uf.URI)
|
||||
}
|
||||
|
||||
func getCloudFile(pathname string, dest string) error {
|
||||
client := http.Client{}
|
||||
if req, err := http.NewRequest("GET", CloudDAVBase+pathname, nil); err != nil {
|
||||
return err
|
||||
} else {
|
||||
req.SetBasicAuth(CloudUsername, CloudPassword)
|
||||
if resp, err := client.Do(req); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
|
||||
if fd, err := os.Create(dest); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer fd.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(resp.Status)
|
||||
} else {
|
||||
writer := bufio.NewWriter(fd)
|
||||
reader := bufio.NewReader(resp.Body)
|
||||
reader.WriteTo(writer)
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
type statsTheme struct {
|
||||
SolvedByLevel []int `json:"solvedByLevel"`
|
||||
}
|
||||
|
||||
type stats struct {
|
||||
Themes map[string]statsTheme `json:"themes"`
|
||||
TryRank []int64 `json:"tryRank"`
|
||||
}
|
||||
|
||||
func genStats() (interface{}, error) {
|
||||
ret := map[string]statsTheme{}
|
||||
|
||||
if themes, err := fic.GetThemes(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, theme := range themes {
|
||||
if exercices, err := theme.GetExercices(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
exos := map[string]fic.ExportedExercice{}
|
||||
for _, exercice := range exercices {
|
||||
exos[fmt.Sprintf("%d", exercice.Id)] = fic.ExportedExercice{
|
||||
exercice.Title,
|
||||
exercice.Gain,
|
||||
exercice.SolvedCount(),
|
||||
exercice.TriedTeamCount(),
|
||||
}
|
||||
}
|
||||
ret[fmt.Sprintf("%d", theme.Id)] = statsTheme{}
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var ApiTeamsRouting = map[string]DispatchFunction{
|
||||
"GET": listTeam,
|
||||
"PUT": creationTeamMembers,
|
||||
"POST": creationTeam,
|
||||
"DELETE": deletionTeam,
|
||||
}
|
||||
|
||||
func nginxGenTeam() (string, error) {
|
||||
if teams, err := fic.GetTeams(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ret := ""
|
||||
for _, team := range teams {
|
||||
ret += fmt.Sprintf(" if ($ssl_client_s_dn ~ \"/C=FR/ST=France/O=Epita/OU=SRS/CN=%s\") { set $team \"%s\"; }\n", team.InitialName, team.InitialName)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func bindingTeams() (string, error) {
|
||||
if teams, err := fic.GetTeams(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ret := ""
|
||||
for _, team := range teams {
|
||||
if members, err := team.GetMembers(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
var mbs []string
|
||||
for _, member := range members {
|
||||
mbs = append(mbs, fmt.Sprintf("%s %s", member.Firstname, member.Lastname))
|
||||
}
|
||||
ret += fmt.Sprintf("%d;%s;%s\n", team.Id, team.Name, strings.Join(mbs, ";"))
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
type uploadedTeam struct {
|
||||
Name string
|
||||
Color uint32
|
||||
}
|
||||
|
||||
type uploadedMember struct {
|
||||
Firstname string
|
||||
Lastname string
|
||||
Nickname string
|
||||
Company string
|
||||
}
|
||||
|
||||
func listTeam(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) >= 2 {
|
||||
var team *fic.Team
|
||||
if tid, err := strconv.Atoi(args[0]); err != nil {
|
||||
if t, err := fic.GetTeamByInitialName(args[0]); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
team = &t
|
||||
}
|
||||
} else {
|
||||
if tid == 0 {
|
||||
team = nil
|
||||
} else if t, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
team = &t
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if args[1] == "my.json" {
|
||||
return fic.MyJSONTeam(team, true)
|
||||
} else if args[1] == "wait.json" {
|
||||
return fic.MyJSONTeam(team, false)
|
||||
} else if args[1] == "stats.json" {
|
||||
if team != nil {
|
||||
return team.GetStats()
|
||||
} else {
|
||||
return fic.GetTeamsStats(nil)
|
||||
}
|
||||
} else if args[1] == "tries" {
|
||||
return fic.GetTries(team, nil)
|
||||
} else if team != nil && args[1] == "members" {
|
||||
return team.GetMembers()
|
||||
} else if args[1] == "certificate" && team != nil {
|
||||
return CertificateAPI(*team, args[2:])
|
||||
} else if team != nil && args[1] == "name" {
|
||||
return team.Name, nil
|
||||
}
|
||||
} else if len(args) == 1 {
|
||||
if args[0] == "teams.json" {
|
||||
return fic.ExportTeams()
|
||||
} else if args[0] == "tries" {
|
||||
return fic.GetTries(nil, nil)
|
||||
} else if args[0] == "nginx" {
|
||||
return nginxGenTeam()
|
||||
} else if args[0] == "binding" {
|
||||
return bindingTeams()
|
||||
} else if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return fic.GetTeamByInitialName(args[0])
|
||||
} else if team, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return team, nil
|
||||
}
|
||||
} else if len(args) == 0 {
|
||||
// List all teams
|
||||
return fic.GetTeams()
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func creationTeam(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
// List given team
|
||||
if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else if team, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
var members []uploadedMember
|
||||
if err := json.Unmarshal(body, &members); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, member := range members {
|
||||
team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company)
|
||||
}
|
||||
|
||||
return team.GetMembers()
|
||||
}
|
||||
} else if len(args) == 0 {
|
||||
// Create a new team
|
||||
var ut uploadedTeam
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fic.CreateTeam(ut.Name, ut.Color)
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func creationTeamMembers(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
// List given team
|
||||
if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else if team, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
var member uploadedMember
|
||||
if err := json.Unmarshal(body, &member); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company)
|
||||
|
||||
return team.GetMembers()
|
||||
}
|
||||
} else if len(args) == 0 {
|
||||
// Create a new team
|
||||
var members []uploadedMember
|
||||
if err := json.Unmarshal(body, &members); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if team, err := fic.CreateTeam("", 0); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, member := range members {
|
||||
if _, err := team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return team, nil
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func deletionTeam(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else if team, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return team.Delete()
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var ApiThemesRouting = map[string]DispatchFunction{
|
||||
"GET": listTheme,
|
||||
"PATCH": updateTheme,
|
||||
"POST": creationTheme,
|
||||
"DELETE": deletionTheme,
|
||||
}
|
||||
|
||||
func bindingFiles() (string, error) {
|
||||
if files, err := fic.GetFiles(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ret := ""
|
||||
for _, file := range files {
|
||||
ret += fmt.Sprintf("%s;%s\n", file.GetOrigin(), file.Path)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getTheme(args []string) (fic.Theme, error) {
|
||||
if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return fic.Theme{}, err
|
||||
} else {
|
||||
return fic.GetTheme(tid)
|
||||
}
|
||||
}
|
||||
|
||||
func getExercice(args []string) (fic.Exercice, error) {
|
||||
if theme, err := getTheme(args); err != nil {
|
||||
return fic.Exercice{}, err
|
||||
} else if eid, err := strconv.Atoi(string(args[1])); err != nil {
|
||||
return fic.Exercice{}, err
|
||||
} else {
|
||||
return theme.GetExercice(eid)
|
||||
}
|
||||
}
|
||||
|
||||
func listTheme(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 3 {
|
||||
if e, err := getExercice(args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if args[2] == "files" {
|
||||
return e.GetFiles()
|
||||
} else if args[2] == "keys" {
|
||||
return e.GetKeys()
|
||||
}
|
||||
}
|
||||
} else if len(args) == 2 {
|
||||
if args[1] == "exercices" {
|
||||
if theme, err := getTheme(args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return theme.GetExercices()
|
||||
}
|
||||
} else {
|
||||
return getExercice(args)
|
||||
}
|
||||
} else if len(args) == 1 {
|
||||
if args[0] == "files-bindings" {
|
||||
return bindingFiles()
|
||||
} else if args[0] == "themes.json" {
|
||||
return fic.ExportThemes()
|
||||
} else {
|
||||
return getTheme(args)
|
||||
}
|
||||
} else if len(args) == 0 {
|
||||
// List all themes
|
||||
return fic.GetThemes()
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type uploadedTheme struct {
|
||||
Name string
|
||||
Authors string
|
||||
}
|
||||
|
||||
func creationTheme(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) >= 1 {
|
||||
if theme, err := getTheme(args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return createExercice(theme, args[1:], body)
|
||||
}
|
||||
} else if len(args) == 0 {
|
||||
// Create a new theme
|
||||
var ut uploadedTheme
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ut.Name) == 0 {
|
||||
return nil, errors.New("Theme's name not filled")
|
||||
}
|
||||
|
||||
return fic.CreateTheme(ut.Name, ut.Authors)
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateTheme(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 2 {
|
||||
// Update an exercice
|
||||
var ue fic.Exercice
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ue.Title) == 0 {
|
||||
return nil, errors.New("Exercice's title not filled")
|
||||
}
|
||||
|
||||
if _, err := ue.Update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ue, nil
|
||||
} else if len(args) == 1 {
|
||||
// Update a theme
|
||||
var ut fic.Theme
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ut.Name) == 0 {
|
||||
return nil, errors.New("Theme's name not filled")
|
||||
}
|
||||
|
||||
return ut.Update()
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func deletionTheme(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 2 {
|
||||
if exercice, err := getExercice(args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return exercice.Delete()
|
||||
}
|
||||
} else if len(args) == 1 {
|
||||
if theme, err := getTheme(args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return theme.Delete()
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package main
|
||||
|
||||
import ()
|
||||
|
||||
var ApiVersionRouting = map[string]DispatchFunction{
|
||||
"GET": showVersion,
|
||||
}
|
||||
|
||||
func showVersion(args []string, body []byte) (interface{}, error) {
|
||||
return map[string]interface{}{"version": 0.1}, nil
|
||||
}
|
||||
81
admin/app.go
Normal file
81
admin/app.go
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/api"
|
||||
"srs.epita.fr/fic-server/settings"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
router *gin.Engine
|
||||
srv *http.Server
|
||||
cfg *settings.Settings
|
||||
bind string
|
||||
}
|
||||
|
||||
func NewApp(cfg *settings.Settings, baseURL string, bind string) App {
|
||||
if !cfg.WorkInProgress {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
gin.ForceConsoleColor()
|
||||
router := gin.Default()
|
||||
|
||||
api.DeclareRoutes(router.Group(""))
|
||||
|
||||
var baserouter *gin.RouterGroup
|
||||
if len(baseURL) > 0 {
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, baseURL)
|
||||
})
|
||||
router.GET(filepath.Dir(baseURL)+"/files/*_", func(c *gin.Context) {
|
||||
path := c.Request.URL.Path
|
||||
c.Redirect(http.StatusFound, filepath.Join(baseURL, strings.TrimPrefix(path, filepath.Dir(baseURL))))
|
||||
})
|
||||
|
||||
baserouter = router.Group(baseURL)
|
||||
|
||||
api.DeclareRoutes(baserouter)
|
||||
declareStaticRoutes(baserouter, cfg, baseURL)
|
||||
} else {
|
||||
declareStaticRoutes(router.Group(""), cfg, "")
|
||||
}
|
||||
|
||||
app := App{
|
||||
router: router,
|
||||
bind: bind,
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func (app *App) Start() {
|
||||
app.srv = &http.Server{
|
||||
Addr: app.bind,
|
||||
Handler: app.router,
|
||||
ReadHeaderTimeout: 15 * time.Second,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
IdleTimeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
log.Printf("Ready, listening on %s\n", app.bind)
|
||||
if err := app.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("listen: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *App) Stop() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := app.srv.Shutdown(ctx); err != nil {
|
||||
log.Fatal("Server Shutdown:", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
BASEURL="http://localhost:8081"
|
||||
BASEURI="https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016"
|
||||
BASEFILE="/files"
|
||||
CLOUDPASS=fic:'f>t\nV33R|(+?$i*'
|
||||
|
||||
new_theme() {
|
||||
NAME=`echo $1 | sed 's/"/\\\\"/g'`
|
||||
AUTHORS=`echo $2 | sed 's/"/\\\\"/g'`
|
||||
curl -f -s -d "{\"name\": \"$NAME\", \"authors\": \"$AUTHORS\"}" "${BASEURL}/api/themes/" |
|
||||
grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||
}
|
||||
|
||||
new_exercice() {
|
||||
THEME="$1"
|
||||
TITLE=`echo "$2" | sed 's/"/\\\\"/g'`
|
||||
STATEMENT=`echo "$3" | sed 's/"/\\\\"/g' | sed ':a;N;$!ba;s/\n/<br>/g'`
|
||||
HINT=`echo "$4" | sed 's/"/\\\\"/g' | sed ':a;N;$!ba;s/\n/<br>/g'`
|
||||
DEPEND="$5"
|
||||
GAIN="$6"
|
||||
VIDEO="$7"
|
||||
|
||||
curl -f -s -d "{\"title\": \"$TITLE\", \"statement\": \"$STATEMENT\", \"hint\": \"$HINT\", \"depend\": $DEPEND, \"gain\": $GAIN, \"videoURI\": \"$VIDEO\"}" "${BASEURL}/api/themes/$THEME" |
|
||||
grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||
}
|
||||
|
||||
new_file() {
|
||||
THEME="$1"
|
||||
EXERCICE="$2"
|
||||
URI="$3"
|
||||
|
||||
curl -f -s -d "{\"URI\": \"${BASEFILE}${URI}\"}" "${BASEURL}/api/themes/$THEME/$EXERCICE/files" |
|
||||
grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||
}
|
||||
|
||||
new_key() {
|
||||
THEME="$1"
|
||||
EXERCICE="$2"
|
||||
NAME="$3"
|
||||
KEY=`echo $4 | sed 's/"/\\\\"/g' | sed 's#\\\\#\\\\\\\\#g'`
|
||||
|
||||
curl -f -s -d "{\"name\": \"$NAME\", \"key\": \"$KEY\"}" "${BASEURL}/api/themes/$THEME/$EXERCICE/keys" |
|
||||
grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||
}
|
||||
|
||||
# Theme
|
||||
curl -f -s -X PROPFIND -u "${CLOUDPASS}" "${BASEURI}" | xmllint --format - | grep 'd:href' | sed -E 's/^.*>(.*)<.*$/\1/' | sed 1d | tac | while read f; do basename "$f"; done | while read THEME_URI
|
||||
do
|
||||
THM_BASEURI="/${THEME_URI}/"
|
||||
THEME_NAME=$(echo "${THEME_URI}" | sed -E 's/%20/ /g' | sed -E 's/%c3%a9/é/g' | sed -E 's/%c3%a8/è/g')
|
||||
THEME_AUTHORS=$(curl -f -s -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}/AUTHORS.txt" | sed 's/$/,/' | xargs)
|
||||
THEME_ID=`new_theme "$THEME_NAME" "$THEME_AUTHORS"`
|
||||
if [ -z "$THEME_ID" ]; then
|
||||
echo -e "\e[31;01m!!! An error occured during theme add\e[00m"
|
||||
continue
|
||||
else
|
||||
echo -e "\e[33m>>> New theme created:\e[00m $THEME_ID - $THEME_NAME"
|
||||
fi
|
||||
|
||||
LAST=null
|
||||
EXO_NUM=0
|
||||
curl -f -s -X PROPFIND -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}" | xmllint --format - | grep 'd:href' | sed -E 's/^.*>(.*)<.*$/\1/' | sed -E 's/%20/ /g' | sed -E 's/%c3%a9/é/g' | sed -E 's/%c3%a8/è/g' | sed 1d | while read f; do basename "$f"; done | while read EXO_NAME
|
||||
do
|
||||
if ! echo $EXO_NAME | grep Exercice > /dev/null
|
||||
then
|
||||
continue
|
||||
fi
|
||||
|
||||
EXO_NUM=$((EXO_NUM + 1))
|
||||
echo
|
||||
echo -e "\e[36m--- Filling exercice ${EXO_NUM} in theme ${THEME_NAME}\e[00m"
|
||||
|
||||
EXO_BASEURI="${EXO_NAME}/"
|
||||
|
||||
FILES=$(curl -f -s -X PROPFIND -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}${EXO_BASEURI}" | xmllint --format - | grep 'd:href' | sed -E 's/^.*>(.*)<.*$/\1/' | sed 1d | while read f; do basename $f; done)
|
||||
|
||||
EXO_VIDEO=$(echo "$FILES" | grep -E "\.(mov|mkv|mp4|avi|flv|ogv|webm)$" | tail -1)
|
||||
|
||||
if [ "${LAST}" = "null" ]; then
|
||||
echo ">>> Assuming this exercice has no dependency"
|
||||
else
|
||||
echo ">>> Assuming this exercice depends on the last entry (id=${LAST})"
|
||||
fi
|
||||
|
||||
EXO_GAIN=$((3 * (2 ** $EXO_NUM) - 1))
|
||||
echo ">>> Using default gain: ${EXO_GAIN} points"
|
||||
|
||||
EXO_DESC=$(curl -f -s -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}${EXO_BASEURI}/description.txt")
|
||||
EXO_HINT=$(curl -f -s -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}${EXO_BASEURI}/hint.txt")
|
||||
|
||||
EXO_ID=`new_exercice "${THEME_ID}" "${EXO_NAME}" "${EXO_DESC}" "${EXO_HINT}" "${LAST}" "${EXO_GAIN}" "${THM_BASEURI}${EXO_BASEURI}${EXO_VIDEO}"`
|
||||
if [ -z "$EXO_ID" ]; then
|
||||
echo -e "\e[31;01m!!! An error occured during exercice add.\e[00m"
|
||||
continue
|
||||
else
|
||||
echo -e "\e[32m>>> New exercice created:\e[00m $EXO_ID - $EXO_NAME"
|
||||
fi
|
||||
|
||||
|
||||
# Keys
|
||||
curl -f -s -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}${EXO_BASEURI}/keys.txt" | while read KEYLINE
|
||||
do
|
||||
KEY_NAME=$(echo "$KEYLINE" | cut -d : -f 1)
|
||||
KEY_RAW=$(echo "$KEYLINE" | cut -d : -f 2-)
|
||||
|
||||
if [ -z "${KEY_NAME}" ]; then
|
||||
KEY_NAME="Flag"
|
||||
fi
|
||||
|
||||
KEY_ID=`new_key "${THEME_ID}" "${EXO_ID}" "${KEY_NAME}" "${KEY_RAW}"`
|
||||
if [ -z "$KEY_ID" ]; then
|
||||
echo -e "\e[31;01m!!! An error occured during key import!\e[00m (name=${KEYNAME};raw=${KEY_RAW})"
|
||||
else
|
||||
echo -e "\e[32m>>> New key added:\e[00m $KEY_ID - $KEY_NAME"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
# Files
|
||||
for f in $FILES; do echo $f; done | grep -vEi "(ressources|readme|description.txt|hint.txt|keys.txt|${EXO_VIDEO})" |
|
||||
while read FBASE
|
||||
do
|
||||
echo "Import file ${BASEURI}${THM_BASEURI}${EXO_BASEURI}${FBASE}"
|
||||
FILE_ID=`new_file "${THEME_ID}" "${EXO_ID}" "${THM_BASEURI}${EXO_BASEURI}${FBASE}"`
|
||||
if [ -z "$FILE_ID" ]; then
|
||||
echo -e "\e[31;01m!!! An error occured during file import! Please check path.\e[00m"
|
||||
else
|
||||
echo -e "\e[32m>>> New file added:\e[00m $FILE_ID - $FBASE"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
LAST=$EXO_ID
|
||||
done
|
||||
echo
|
||||
done
|
||||
|
|
@ -1,7 +1,51 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
BASEURL="http://localhost:8081"
|
||||
PART_FILE="Challenge_Liste des participants.csv"
|
||||
BASEURL="http://127.0.0.1:8081/admin"
|
||||
GEN_CERTS=0
|
||||
GEN_PASSWD=0
|
||||
EXTRA_TEAMS=0
|
||||
CSV_SPLITER=","
|
||||
CSV_COL_LASTNAME=2
|
||||
CSV_COL_FIRSTNAME=3
|
||||
CSV_COL_NICKNAME=5
|
||||
CSV_COL_COMPANY=6
|
||||
CSV_COL_TEAM=1
|
||||
|
||||
usage() {
|
||||
echo "$0 [options] csv_file"
|
||||
echo " -B -baseurl BASEURL URL to administration endpoint (default: $BASEURL)"
|
||||
echo " -S -csv-spliter SEP CSV separator (default: $CSV_SPLITER)"
|
||||
echo " -e -extra-teams NBS Number of extra teams to generate (default: ${EXTRA_TEAMS})"
|
||||
echo " -c -generate-certificate Should team certificates be generated? (default: no)"
|
||||
echo " -p -generate-password Should generate team password to teams.pass? (default: no)"
|
||||
}
|
||||
|
||||
# Parse options
|
||||
while [ "${1:0:1}" = "-" ]
|
||||
do
|
||||
case "$1" in
|
||||
-B|-baseurl)
|
||||
BASEURL=$2
|
||||
shift;;
|
||||
-S|-csv-spliter)
|
||||
CSV_SPLITER=$2
|
||||
shift;;
|
||||
-e|-extra-teams)
|
||||
EXTRA_TEAMS=$2
|
||||
shift;;
|
||||
-c|-generate-certificates)
|
||||
GEN_CERTS=1;;
|
||||
-p|-generate-password)
|
||||
GEN_PASSWD=1;;
|
||||
*)
|
||||
echo "Unknown option '$1'"
|
||||
usage
|
||||
exit 1;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ "$#" -lt 1 ] && [ "${EXTRA_TEAMS}" -eq 0 ] && { usage; exit 1; }
|
||||
|
||||
new_team() {
|
||||
head -n "$1" team-names.txt | tail -1 | sed -E 's/^.*\|\[\[([^|]+\|)?([^|]+)\]\][^|]*\|([A-Fa-f0-9]{1,2})\|([A-Fa-f0-9]{1,2})\|([A-Fa-f0-9]{1,2})\|([0-9]{1,3})\|([0-9]{1,3})\|([0-9]{1,3})\|.*$/\6 \7 \8 \2/' |
|
||||
|
|
@ -10,17 +54,21 @@ new_team() {
|
|||
R=`echo $line | cut -d " " -f 1`
|
||||
G=`echo $line | cut -d " " -f 2`
|
||||
B=`echo $line | cut -d " " -f 3`
|
||||
N=`echo $line | cut -d " " -f 4`
|
||||
if [ -z "$2" ]; then
|
||||
N=`echo $line | cut -d " " -f 4`
|
||||
else
|
||||
N=`echo -n $2 | tr -d '\r\n'`
|
||||
fi
|
||||
|
||||
COLOR=$((($R*256 + $G) * 256 + $B))
|
||||
|
||||
curl -s -d "{\"name\": \"$N\",\"color\": $COLOR}" "${BASEURL}/api/teams/"
|
||||
curl -s -d "{\"name\": \"$N\",\"color\": $COLOR}" "${BASEURL}/api/teams"
|
||||
done | grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||
}
|
||||
|
||||
TNUM=0
|
||||
|
||||
for i in `seq 12`
|
||||
for i in $(seq $EXTRA_TEAMS)
|
||||
do
|
||||
TNUM=$(($TNUM + 1))
|
||||
|
||||
|
|
@ -28,31 +76,52 @@ do
|
|||
|
||||
TID=`new_team $TNUM`
|
||||
|
||||
if ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null
|
||||
if [ "${GEN_CERTS}" -eq 1 ] && ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null
|
||||
then
|
||||
curl -s -f "${BASEURL}/api/teams/${TID}/certificate/generate"
|
||||
elif [ "${GEN_PASSWD}" -eq 1 ]
|
||||
then
|
||||
TEAMID=$(curl -s -f "${BASEURL}/api/teams/${TID}/" | jq -r .name)
|
||||
PASSWD=$(curl -X POST -s -f "${BASEURL}/api/teams/${TID}/password" | jq -r .password)
|
||||
NP=$(echo "${TEAMID}" | cut -d : -f 1 | sed 's/[[:upper:]]/\l&/g;s/[âáàä]/a/g;s/[êéèë]/e/g')
|
||||
cat >> teams.pass <<EOF
|
||||
${TEAMID}:${PASSWD}
|
||||
EOF
|
||||
SALT="$(openssl rand -base64 3)"
|
||||
HASHED="{SSHA}$(echo -n $PASSWD$SALT | openssl dgst -binary -sha1 | sed 's#$#'"$SALT"'#' | base64)"
|
||||
cat >> htpasswd.ssha <<EOF
|
||||
${NP}:${HASHED}
|
||||
EOF
|
||||
HASHED="$(echo -n $PASSWD | openssl passwd -apr1 -in -)"
|
||||
cat >> htpasswd.apr1 <<EOF
|
||||
${NP}:${HASHED}
|
||||
EOF
|
||||
fi
|
||||
echo
|
||||
done
|
||||
|
||||
TMAX=`sed "1d" "$PART_FILE" | cut -d \; -f 15 | sort | uniq | wc -l`
|
||||
[ "$#" -lt 1 ] && exit 0
|
||||
PART_FILE="$1"
|
||||
|
||||
TMAX=`cat "$PART_FILE" | cut -d "${CSV_SPLITER}" -f $CSV_COL_TEAM | sort | uniq | wc -l`
|
||||
TMAX=$(($TMAX + $TNUM))
|
||||
sed "1d" "$PART_FILE" | cut -d \; -f 15 | sort | uniq | while read TEAMID
|
||||
cat "$PART_FILE" | cut -d "${CSV_SPLITER}" -f $CSV_COL_TEAM | sort | uniq | while read TEAMID
|
||||
do
|
||||
TNUM=$(($TNUM + 1))
|
||||
|
||||
echo "Doing team $TNUM/$TMAX ("$(($TNUM*100/$TMAX))"%)..."
|
||||
|
||||
TID=`new_team $TNUM`
|
||||
TID=`new_team "${TNUM}" "${TEAMID}"`
|
||||
|
||||
(
|
||||
if ! (
|
||||
echo -n "["
|
||||
HAS_MEMBER=1
|
||||
grep ";$TEAMID\$" "$PART_FILE" | while read MEMBER
|
||||
grep "${TEAMID}${CSV_SPLITER}" "$PART_FILE" | while read MEMBER
|
||||
do
|
||||
LASTNAME=`echo $MEMBER | cut -d ";" -f 2`
|
||||
FIRSTNAME=`echo $MEMBER | cut -d ";" -f 3`
|
||||
COMPANY=`echo $MEMBER | cut -d ";" -f 4`
|
||||
LASTNAME=`echo $MEMBER | cut -d "${CSV_SPLITER}" -f $CSV_COL_LASTNAME | tr -d "\r\n"`
|
||||
FIRSTNAME=`echo $MEMBER | cut -d "${CSV_SPLITER}" -f $CSV_COL_FIRSTNAME | tr -d "\r\n"`
|
||||
NICKNAME=`echo $MEMBER | cut -d "${CSV_SPLITER}" -f $CSV_COL_NICKNAME | tr -d "\r\n"`
|
||||
COMPANY=`echo $MEMBER | cut -d "${CSV_SPLITER}" -f $CSV_COL_COMPANY | tr -d "\r\n"`
|
||||
|
||||
if [ $HAS_MEMBER = 0 ]
|
||||
then
|
||||
|
|
@ -65,17 +134,34 @@ do
|
|||
{
|
||||
"firstname": "$FIRSTNAME",
|
||||
"lastname": "$LASTNAME",
|
||||
"nickname": "",
|
||||
"nickname": "$NICKNAME",
|
||||
"company": "$COMPANY"
|
||||
}
|
||||
EOF
|
||||
done
|
||||
echo "]"
|
||||
) | curl -s -d @- "${BASEURL}/api/teams/${TID}" > /dev/null
|
||||
|
||||
if ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null
|
||||
) | curl -f -s -d @- "${BASEURL}/api/teams/${TID}/members"
|
||||
then
|
||||
echo "An error occured"
|
||||
elif [ "${GEN_CERTS}" -eq 1 ] && ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null
|
||||
then
|
||||
curl -s -f "${BASEURL}/api/teams/${TID}/certificate/generate"
|
||||
elif [ "${GEN_PASSWD}" -eq 1 ]
|
||||
then
|
||||
PASSWD=$(curl -X POST -s -f "${BASEURL}/api/teams/${TID}/password" | jq -r .password)
|
||||
NP=$(echo "${TEAMID}" | cut -d : -f 1 | sed 's/[[:upper:]]/\l&/g;s/[âáàä]/a/g;s/[êéèë]/e/g')
|
||||
cat >> teams.pass <<EOF
|
||||
${TEAMID}:${PASSWD}
|
||||
EOF
|
||||
SALT="$(openssl rand -base64 3)"
|
||||
HASHED="{SSHA}$(echo -n $PASSWD$SALT | openssl dgst -binary -sha1 | sed 's#$#'"$SALT"'#' | base64)"
|
||||
cat >> htpasswd.ssha <<EOF
|
||||
${NP}:${HASHED}
|
||||
EOF
|
||||
HASHED="$(echo -n $PASSWD | openssl passwd -apr1 -in -)"
|
||||
cat >> htpasswd.apr1 <<EOF
|
||||
${NP}:${HASHED}
|
||||
EOF
|
||||
fi
|
||||
echo
|
||||
done
|
||||
|
|
|
|||
15
admin/fill_teams_zqds.sh
Executable file
15
admin/fill_teams_zqds.sh
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
BASEURL="http://127.0.0.1:8081/admin"
|
||||
|
||||
EVENTID=6109ae5acbb7b36b789c9330
|
||||
BASEURL_ZQDS="https://api.well-played.gg"
|
||||
|
||||
curl -s -H 'accept: */*' "${BASEURL_ZQDS}/teams?event_id=${EVENTID}&size=100" | jq --compact-output .content[] | while read TEAMOBJ; do
|
||||
curl -s -d @- "${BASEURL}/api/teams/" <<EOF
|
||||
{
|
||||
"name": $(echo "${TEAMOBJ}" | jq .name),
|
||||
"external_id": $(echo "${TEAMOBJ}" | jq .id)
|
||||
}
|
||||
EOF
|
||||
done
|
||||
60
admin/generation/generation.go
Normal file
60
admin/generation/generation.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package generation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var GeneratorSocket string
|
||||
|
||||
func doGeneration(uri string, contenttype string, buf io.Reader) (*http.Response, error) {
|
||||
sockType := "unix"
|
||||
if strings.Contains(GeneratorSocket, ":") {
|
||||
sockType = "tcp"
|
||||
}
|
||||
|
||||
socket, err := net.Dial(sockType, GeneratorSocket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer socket.Close()
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: func(network, addr string) (net.Conn, error) {
|
||||
return socket, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return httpClient.Post("http://localhost"+uri, contenttype, buf)
|
||||
}
|
||||
|
||||
func EnqueueGeneration(gs fic.GenStruct) (*http.Response, error) {
|
||||
buf, err := json.Marshal(gs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Something is wrong with JSON encoder: %w", err)
|
||||
}
|
||||
|
||||
return doGeneration("/enqueue", "application/json", bytes.NewReader(buf))
|
||||
}
|
||||
|
||||
func PerformGeneration(gs fic.GenStruct) (*http.Response, error) {
|
||||
buf, err := json.Marshal(gs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Something is wrong with JSON encoder: %w", err)
|
||||
}
|
||||
|
||||
return doGeneration("/perform", "application/json", bytes.NewReader(buf))
|
||||
}
|
||||
|
||||
func FullGeneration() (*http.Response, error) {
|
||||
return doGeneration("/full", "application/json", nil)
|
||||
}
|
||||
133
admin/get-remote-files/main.go
Normal file
133
admin/get-remote-files/main.go
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cloudDAVBase := ""
|
||||
cloudUsername := "fic"
|
||||
cloudPassword := ""
|
||||
localImporterDirectory := ""
|
||||
|
||||
// Read paremeters from environment
|
||||
if v, exists := os.LookupEnv("FICCLOUD_URL"); exists {
|
||||
cloudDAVBase = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FICCLOUD_USER"); exists {
|
||||
cloudUsername = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FICCLOUD_PASS"); exists {
|
||||
cloudPassword = v
|
||||
}
|
||||
|
||||
// Read parameters from command line
|
||||
flag.StringVar(&localImporterDirectory, "localimport", localImporterDirectory,
|
||||
"Base directory where to find challenges files to import, local part")
|
||||
flag.StringVar(&cloudDAVBase, "clouddav", cloudDAVBase,
|
||||
"Base directory where to find challenges files to import, cloud part")
|
||||
flag.StringVar(&cloudUsername, "clouduser", cloudUsername, "Username used to sync")
|
||||
flag.StringVar(&cloudPassword, "cloudpass", cloudPassword, "Password used to sync")
|
||||
flag.Var(&sync.RemoteFileDomainWhitelist, "remote-file-domain-whitelist", "List of domains which are allowed to store remote files")
|
||||
flag.Parse()
|
||||
|
||||
// Do not display timestamp
|
||||
log.SetFlags(0)
|
||||
|
||||
// Instantiate importer
|
||||
regenImporter := false
|
||||
if localImporterDirectory != "" {
|
||||
sync.GlobalImporter = sync.LocalImporter{Base: localImporterDirectory, Symlink: true}
|
||||
} else if cloudDAVBase != "" {
|
||||
sync.GlobalImporter, _ = sync.NewCloudImporter(cloudDAVBase, cloudUsername, cloudPassword)
|
||||
} else {
|
||||
// In this case, we want to treat the entier path given
|
||||
regenImporter = true
|
||||
}
|
||||
|
||||
for _, p := range flag.Args() {
|
||||
if regenImporter {
|
||||
var err error
|
||||
p, err = filepath.Abs(p)
|
||||
if err != nil {
|
||||
p = path.Clean(p)
|
||||
}
|
||||
sync.GlobalImporter = sync.LocalImporter{
|
||||
Base: p,
|
||||
Symlink: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Find all challenge.toml or challenge.txt
|
||||
treatDir("")
|
||||
}
|
||||
}
|
||||
|
||||
func treatDir(p string) {
|
||||
var expath string
|
||||
|
||||
for _, f := range []string{"challenge.toml", "challenge.txt"} {
|
||||
if sync.GlobalImporter.Exists(path.Join(p, f)) {
|
||||
expath = p
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if expath != "" {
|
||||
treatExercice(expath)
|
||||
} else {
|
||||
files, err := sync.GlobalImporter.ListDir(p)
|
||||
if err != nil {
|
||||
log.Printf("Unable to readdir at %s: %s", p, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
st, err := sync.GlobalImporter.Stat(path.Join(p, f))
|
||||
if err == nil && st.IsDir() {
|
||||
treatDir(path.Join(p, f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func treatExercice(expath string) {
|
||||
// Load exercice
|
||||
exercice, _, _, _, _, err := sync.BuildExercice(sync.GlobalImporter, &fic.Theme{}, expath, nil, nil)
|
||||
if exercice == nil {
|
||||
log.Printf("Unable to treat exercice %q: %s", expath, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paramsFiles, err := sync.GetExerciceFilesParams(sync.GlobalImporter, exercice)
|
||||
if err != nil {
|
||||
log.Printf("Unable to read challenge.toml %q: %s", expath, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for fname, pf := range paramsFiles {
|
||||
if pf.URL == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
dest := path.Join(exercice.Path, "files", fname)
|
||||
|
||||
log.Printf("Downloading %s...", fname)
|
||||
if li, ok := sync.GlobalImporter.(sync.LocalImporter); ok {
|
||||
err = sync.DownloadExerciceFile(paramsFiles[fname], li.GetLocalPath(dest), exercice, false)
|
||||
} else {
|
||||
err = sync.DownloadExerciceFile(paramsFiles[fname], dest, exercice, false)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println("DownloadExerciceFile error:", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
BASEURL="http://localhost:8081"
|
||||
BASEURI="https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016"
|
||||
CLOUDUSER='fic'
|
||||
CLOUDPASS='f>t\nV33R|(+?$i*'
|
||||
|
||||
if [ $# -gt 0 ]
|
||||
then
|
||||
WHERE=$1
|
||||
else
|
||||
WHERE="files"
|
||||
fi
|
||||
|
||||
curl -q -f ${BASEURL}/api/themes/files-bindings | while read l
|
||||
do
|
||||
FROM=$(echo "$l" | cut -d ";" -f 1)
|
||||
DEST=$(echo "$l" | cut -d ";" -f 2)
|
||||
|
||||
mkdir -p $(dirname "${WHERE}${DEST}")
|
||||
|
||||
wget -O "${WHERE}${DEST}" --user "${CLOUDUSER}" --password "${CLOUDPASS}" "${BASEURI}${FROM}"
|
||||
done
|
||||
174
admin/index.go
Normal file
174
admin/index.go
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
package main
|
||||
|
||||
const indextpl = `<!DOCTYPE html>
|
||||
<html ng-app="FICApp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ .title }} - Administration</title>
|
||||
<link href="{{.urlbase}}css/bootstrap.min.css" type="text/css" rel="stylesheet">
|
||||
<link href="{{.urlbase}}css/glyphicon.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<style>
|
||||
.cksum {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.bg-mfound {
|
||||
background-color: #7bcfd0 !important;
|
||||
}
|
||||
.bg-ffound {
|
||||
background-color: #7bdfc0 !important;
|
||||
}
|
||||
.bg-wchoices {
|
||||
background-color: #c07bdf !important;
|
||||
}
|
||||
.table th.frotated {
|
||||
border: 0;
|
||||
}
|
||||
.table th.rotated {
|
||||
height: 100px;
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
max-width: 40px;
|
||||
position: relative;
|
||||
vertical-align: bottom;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
line-height: 0.9;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
th.rotated > div {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
left: -50px;
|
||||
height: 100%;
|
||||
transform: skew(45deg,0deg);
|
||||
overflow: hidden;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
th.rotated div a {
|
||||
transform: skew(-45deg,0deg) rotate(45deg);
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
left: -35px;
|
||||
display: inline-block;
|
||||
width: 110px;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.col img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.circle-anim {
|
||||
z-index:1;
|
||||
border: black 1px solid;
|
||||
border-radius: .5em;
|
||||
margin-top: .4em;
|
||||
margin-left: .5em;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
transition: transform ease-in .7s;
|
||||
transform: scale(1);
|
||||
}
|
||||
.circle-anim.play {
|
||||
transform: scale(250);
|
||||
opacity:0;
|
||||
}
|
||||
</style>
|
||||
<base href="{{.urlbase}}">
|
||||
<script src="js/d3.v3.min.js"></script>
|
||||
</head>
|
||||
<body class="bg-light text-dark">
|
||||
<nav class="navbar sticky-top navbar-expand-lg navbar-dark text-light" ng-class="{'bg-dark': settings.wip, 'bg-danger': !settings.wip}">
|
||||
<a class="navbar-brand" href=".">
|
||||
<img alt="{{ .title }}" src="{{ .logo }}" style="height: 30px">
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#adminMenu" aria-controls="adminMenu" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="adminMenu">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/teams')}"><a class="nav-link" href="teams">Équipes</a></li>
|
||||
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/auth')}"><a class="nav-link" href="auth">Authentification</a></li>
|
||||
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/themes')}"><a class="nav-link" href="themes">Thèmes</a></li>
|
||||
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/exercices')}"><a class="nav-link" href="exercices">Exercices</a></li>
|
||||
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/public')}"><a class="nav-link" href="public/0">Public</a></li>
|
||||
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/events')}"><a class="nav-link" href="events">Événements</a></li>
|
||||
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/claims')}"><a class="nav-link" href="claims" ng-controller="ClaimsTinyListController">
|
||||
Tâches
|
||||
<span class="badge badge-{{ "{{ priorities[myClaimsMaxLevel] }}" }}" ng-show="myClaims" title="Tâches qui me sont assignées">{{ "{{ myClaims }}" }}</span>
|
||||
<span class="badge badge-{{ "{{ priorities[newClaimsMaxLevel] }}" }}" ng-show="newClaims" title="Nouvelles tâches en attente d'attribution">{{ "{{ newClaims }}" }}</span>
|
||||
</a></li>
|
||||
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/sync') || $location.path().startsWith('/repositories')}">
|
||||
<a class="nav-link" href="sync" ng-show="settings.wip">
|
||||
Synchronisation
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/settings')}">
|
||||
<a class="nav-link" href="settings">
|
||||
Paramètres
|
||||
</a>
|
||||
</li>
|
||||
<span class="d-flex flex-column justify-content-center" ng-show="!settings.wip" ng-cloak>
|
||||
<span class="badge badge-light p-1">
|
||||
prod
|
||||
</span>
|
||||
</span>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<span id="clock" class="navbar-text" ng-controller="CountdownController" ng-cloak>
|
||||
<div style="pointer-events: none; position: absolute;">
|
||||
<div style="position: absolute;" id="circle1" class="circle-anim border-danger"></div>
|
||||
<div style="position: absolute;" id="circle2" class="circle-anim border-info"></div>
|
||||
</div>
|
||||
<button type="button" class="mr-2 btn btn-sm" ng-class="{'btn-info':staticFilesNeedUpdate,'btn-secondary':!staticFilesNeedUpdate}" ng-click="regenerateStaticFiles()" ng-disabled="staticRegenerationInProgress">
|
||||
<span class="glyphicon glyphicon-refresh" aria-hidden="true" title="Regénérer les fichiers statiques" ng-show="!staticRegenerationInProgress"></span>
|
||||
<div class="spinner-border spinner-border-sm" role="status" ng-show="staticRegenerationInProgress">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
<span ng-if="staticFilesNeedUpdate"> {{ "{{ staticFilesNeedUpdate }}" }}</span>
|
||||
</button>
|
||||
<span ng-show="startIn > 0">
|
||||
Démarrage dans :
|
||||
<span>{{"{{ startIn }}"}}</span>"
|
||||
<span class="point">|</span>
|
||||
</span>
|
||||
<span ng-show="settings && settings.end > 0">
|
||||
<span id="hours">{{"{{ time.hours | time }}"}}</span>
|
||||
<span class="point">:</span>
|
||||
<span id="min">{{"{{ time.minutes | time }}"}}</span>
|
||||
<span class="point">:</span>
|
||||
<span id="sec">{{"{{ time.seconds | time }}"}}</span>
|
||||
</span>
|
||||
</span>
|
||||
</nav>
|
||||
|
||||
<div class="progress" style="background-color: #4eaee6; height: 3px; border-radius: 0;">
|
||||
<div class="progress-bar bg-secondary" role="progressbar" style="width: {{ "{{timeProgression * 100}}" }}%"></div>
|
||||
</div>
|
||||
|
||||
<div class="container mt-1" ng-view></div>
|
||||
|
||||
<div style="position: fixed; top: 60px; right: 0; z-index: 10; min-width: 30vw;">
|
||||
<toast ng-repeat="toast in toasts" yes-no="toast.yesFunc || toast.noFunc" onyes="toast.yesFunc" onno="toast.noFunc" date="toast.date" msg="toast.msg" timeout="toast.timeout" title="toast.title" variant="toast.variant"></toast>
|
||||
</div>
|
||||
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/popper.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script src="js/angular.min.js"></script>
|
||||
<script src="js/angular-resource.min.js"></script>
|
||||
<script src="js/angular-route.min.js"></script>
|
||||
<script src="js/angular-sanitize.min.js"></script>
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/common.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
332
admin/main.go
332
admin/main.go
|
|
@ -2,78 +2,332 @@ package main
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/api"
|
||||
"srs.epita.fr/fic-server/admin/generation"
|
||||
"srs.epita.fr/fic-server/admin/pki"
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
"srs.epita.fr/fic-server/settings"
|
||||
)
|
||||
|
||||
var PKIDir string
|
||||
var SubmissionDir string
|
||||
var BaseURL string
|
||||
var CloudDAVBase string
|
||||
var CloudUsername string
|
||||
var CloudPassword string
|
||||
|
||||
func main() {
|
||||
var bind = flag.String("bind", "0.0.0.0:8081", "Bind port/socket")
|
||||
var dsn = flag.String("dsn", "fic:fic@/fic", "DSN to connect to the MySQL server")
|
||||
flag.StringVar(&BaseURL, "baseurl", "http://fic.srs.epita.fr/", "URL prepended to each URL")
|
||||
flag.StringVar(&SubmissionDir, "submission", "./submissions/", "Base directory where save submissions")
|
||||
flag.StringVar(&PKIDir, "pki", "./pki/", "Base directory where found PKI scripts")
|
||||
flag.StringVar(&fic.FilesDir, "files", "./FILES/", "Base directory where found challenges files, local part")
|
||||
flag.StringVar(&CloudDAVBase, "clouddav", "https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016",
|
||||
"Base directory where found challenges files, cloud part")
|
||||
flag.StringVar(&CloudUsername, "clouduser", "fic", "Username used to sync")
|
||||
flag.StringVar(&CloudPassword, "cloudpass", "", "Password used to sync")
|
||||
var err error
|
||||
bind := "127.0.0.1:8081"
|
||||
cloudDAVBase := ""
|
||||
cloudUsername := "fic"
|
||||
cloudPassword := ""
|
||||
localImporterDirectory := ""
|
||||
gitImporterRemote := ""
|
||||
gitImporterBranch := ""
|
||||
localImporterSymlink := false
|
||||
baseURL := "/"
|
||||
checkplugins := sync.CheckPluginList{}
|
||||
|
||||
// Read paremeters from environment
|
||||
if v, exists := os.LookupEnv("FICOIDC_ISSUER"); exists {
|
||||
api.OidcIssuer = v
|
||||
} else if v, exists := os.LookupEnv("FICOIDC_ISSUER_FILE"); exists {
|
||||
fd, err := os.Open(v)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to open FICOIDC_ISSUER_FILE:", err)
|
||||
}
|
||||
|
||||
b, _ := ioutil.ReadAll(fd)
|
||||
api.OidcIssuer = strings.TrimSpace(string(b))
|
||||
|
||||
fd.Close()
|
||||
}
|
||||
if v, exists := os.LookupEnv("FICOIDC_SECRET"); exists {
|
||||
api.OidcSecret = v
|
||||
} else if v, exists := os.LookupEnv("FICOIDC_SECRET_FILE"); exists {
|
||||
fd, err := os.Open(v)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to open FICOIDC_SECRET_FILE:", err)
|
||||
}
|
||||
|
||||
b, _ := ioutil.ReadAll(fd)
|
||||
api.OidcSecret = strings.TrimSpace(string(b))
|
||||
|
||||
fd.Close()
|
||||
}
|
||||
if v, exists := os.LookupEnv("FICCA_PASS"); exists {
|
||||
pki.SetCAPassword(v)
|
||||
} else if v, exists := os.LookupEnv("FICCA_PASS_FILE"); exists {
|
||||
fd, err := os.Open(v)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to open FICCA_PASS_FILE:", err)
|
||||
}
|
||||
|
||||
b, _ := ioutil.ReadAll(fd)
|
||||
pki.SetCAPassword(strings.TrimSpace(string(b)))
|
||||
|
||||
fd.Close()
|
||||
} else {
|
||||
log.Println("WARNING: no password defined for the CA, will use empty password to secure CA private key")
|
||||
log.Println("WARNING: PLEASE DEFINE ENVIRONMENT VARIABLE: FICCA_PASS")
|
||||
}
|
||||
if v, exists := os.LookupEnv("FICCLOUD_URL"); exists {
|
||||
cloudDAVBase = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FICCLOUD_USER"); exists {
|
||||
cloudUsername = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FICCLOUD_PASS"); exists {
|
||||
cloudPassword = v
|
||||
} else if v, exists := os.LookupEnv("FICCLOUD_PASS_FILE"); exists {
|
||||
fd, err := os.Open(v)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to open FICCLOUD_PASS_FILE:", err)
|
||||
}
|
||||
|
||||
b, _ := ioutil.ReadAll(fd)
|
||||
cloudPassword = strings.TrimSpace(string(b))
|
||||
|
||||
fd.Close()
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_BASEURL"); exists {
|
||||
baseURL = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_4REAL"); exists {
|
||||
api.IsProductionEnv, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to parse FIC_4REAL variable:", err)
|
||||
}
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_ADMIN_BIND"); exists {
|
||||
bind = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_TIMESTAMPCHECK"); exists {
|
||||
api.TimestampCheck = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_SETTINGS"); exists {
|
||||
settings.SettingsDir = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_FILES"); exists {
|
||||
fic.FilesDir = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_SYNC_LOCALIMPORT"); exists {
|
||||
localImporterDirectory = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_SYNC_LOCALIMPORTSYMLINK"); exists {
|
||||
localImporterSymlink, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to parse FIC_SYNC_LOCALIMPORTSYMLINK variable:", err)
|
||||
}
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_SYNC_GIT_IMPORT_REMOTE"); exists {
|
||||
gitImporterRemote = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_SYNC_GIT_BRANCH"); exists {
|
||||
gitImporterBranch = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_OPTIONALDIGEST"); exists {
|
||||
fic.OptionalDigest, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to parse FIC_OPTIONALDIGEST variable:", err)
|
||||
}
|
||||
}
|
||||
if v, exists := os.LookupEnv("FIC_STRONGDIGEST"); exists {
|
||||
fic.StrongDigest, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to parse FIC_STRONGDIGEST variable:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Read parameters from command line
|
||||
flag.StringVar(&bind, "bind", bind, "Bind port/socket")
|
||||
var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server")
|
||||
flag.StringVar(&baseURL, "baseurl", baseURL, "URL prepended to each URL")
|
||||
flag.StringVar(&api.TimestampCheck, "timestampCheck", api.TimestampCheck, "Path regularly touched by frontend to check time synchronisation")
|
||||
flag.StringVar(&pki.PKIDir, "pki", "./PKI", "Base directory where found PKI scripts")
|
||||
var staticDir = flag.String("static", "", "Directory containing static files (default if not provided: use embedded files)")
|
||||
flag.StringVar(&api.TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files")
|
||||
flag.StringVar(&api.DashboardDir, "dashbord", "./DASHBOARD", "Base directory where save public JSON files")
|
||||
flag.StringVar(&settings.SettingsDir, "settings", settings.SettingsDir, "Base directory where load and save settings")
|
||||
flag.StringVar(&fic.FilesDir, "files", fic.FilesDir, "Base directory where found challenges files, local part")
|
||||
flag.StringVar(&generation.GeneratorSocket, "generator", "./GENERATOR/generator.socket", "Path to the generator socket (used to trigger issues.json generations, use an empty string to generate locally)")
|
||||
flag.StringVar(&localImporterDirectory, "localimport", localImporterDirectory,
|
||||
"Base directory where found challenges files to import, local part")
|
||||
flag.BoolVar(&localImporterSymlink, "localimportsymlink", localImporterSymlink,
|
||||
"Copy files or just create symlink?")
|
||||
flag.StringVar(&gitImporterRemote, "git-import-remote", gitImporterRemote,
|
||||
"Remote URL of the git repository to use as synchronization source")
|
||||
flag.StringVar(&gitImporterBranch, "git-branch", gitImporterBranch,
|
||||
"Branch to use in the git repository")
|
||||
flag.StringVar(&cloudDAVBase, "clouddav", cloudDAVBase,
|
||||
"Base directory where found challenges files to import, cloud part")
|
||||
flag.StringVar(&cloudUsername, "clouduser", cloudUsername, "Username used to sync")
|
||||
flag.StringVar(&cloudPassword, "cloudpass", cloudPassword, "Password used to sync")
|
||||
flag.BoolVar(&fic.OptionalDigest, "optionaldigest", fic.OptionalDigest, "Is the digest required when importing files?")
|
||||
flag.BoolVar(&fic.StrongDigest, "strongdigest", fic.StrongDigest, "Are BLAKE2b digests required or is SHA-1 good enough?")
|
||||
flag.BoolVar(&api.IsProductionEnv, "4real", api.IsProductionEnv, "Set this flag when running for a real challenge (it disallows or avoid most of mass user progression deletion)")
|
||||
flag.Var(&checkplugins, "rules-plugins", "List of libraries containing others rules to checks")
|
||||
flag.Var(&sync.RemoteFileDomainWhitelist, "remote-file-domain-whitelist", "List of domains which are allowed to store remote files")
|
||||
flag.Parse()
|
||||
|
||||
log.Prefix("[admin] ")
|
||||
log.SetPrefix("[admin] ")
|
||||
|
||||
var staticDir string
|
||||
var err error
|
||||
// Instantiate importer
|
||||
if localImporterDirectory != "" && cloudDAVBase != "" {
|
||||
log.Fatal("Cannot have both --clouddav and --localimport defined.")
|
||||
return
|
||||
} else if gitImporterRemote != "" && cloudDAVBase != "" {
|
||||
log.Fatal("Cannot have both --clouddav and --git-import-remote defined.")
|
||||
return
|
||||
} else if gitImporterRemote != "" {
|
||||
sync.GlobalImporter = sync.NewGitImporter(sync.LocalImporter{Base: localImporterDirectory, Symlink: localImporterSymlink}, gitImporterRemote, gitImporterBranch)
|
||||
} else if localImporterDirectory != "" {
|
||||
sync.GlobalImporter = sync.LocalImporter{Base: localImporterDirectory, Symlink: localImporterSymlink}
|
||||
} else if cloudDAVBase != "" {
|
||||
sync.GlobalImporter, _ = sync.NewCloudImporter(cloudDAVBase, cloudUsername, cloudPassword)
|
||||
}
|
||||
if sync.GlobalImporter != nil {
|
||||
if err := sync.GlobalImporter.Init(); err != nil {
|
||||
log.Fatal("Unable to initialize the importer: ", err.Error())
|
||||
}
|
||||
log.Println("Using", sync.GlobalImporter.Kind())
|
||||
|
||||
challengeinfo, err := sync.GetFileContent(sync.GlobalImporter, settings.ChallengeFile)
|
||||
if err == nil {
|
||||
// Initial distribution of challenge.json
|
||||
if _, err := os.Stat(path.Join(settings.SettingsDir, settings.ChallengeFile)); os.IsNotExist(err) {
|
||||
if fd, err := os.Create(path.Join(settings.SettingsDir, settings.ChallengeFile)); err != nil {
|
||||
log.Fatal("Unable to open SETTINGS/challenge.json:", err)
|
||||
} else {
|
||||
fd.Write([]byte(challengeinfo))
|
||||
err = fd.Close()
|
||||
if err != nil {
|
||||
log.Fatal("Something went wrong during SETTINGS/challenge.json writing:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ci, err := settings.ReadChallengeInfo(challengeinfo); err == nil {
|
||||
fic.StandaloneExercicesTheme.Authors = ci.Authors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize options
|
||||
log.Println("Checking paths...")
|
||||
if staticDir, err = filepath.Abs("./static/"); err != nil {
|
||||
log.Fatal(err)
|
||||
if staticDir != nil && *staticDir != "" {
|
||||
if sDir, err := filepath.Abs(*staticDir); err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
log.Println("Serving pages from", sDir)
|
||||
staticFS = http.Dir(sDir)
|
||||
sync.DeepReportPath = path.Join(sDir, sync.DeepReportPath)
|
||||
}
|
||||
} else {
|
||||
sub, err := fs.Sub(assets, "static")
|
||||
if err != nil {
|
||||
log.Fatal("Unable to cd to static/ directory:", err)
|
||||
}
|
||||
log.Println("Serving pages from memory.")
|
||||
staticFS = http.FS(sub)
|
||||
|
||||
sync.DeepReportPath = path.Join("SYNC", sync.DeepReportPath)
|
||||
if _, err := os.Stat("SYNC"); os.IsNotExist(err) {
|
||||
os.MkdirAll("SYNC", 0751)
|
||||
}
|
||||
}
|
||||
if fic.FilesDir, err = filepath.Abs(fic.FilesDir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if PKIDir, err = filepath.Abs(PKIDir); err != nil {
|
||||
if pki.PKIDir, err = filepath.Abs(pki.PKIDir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if SubmissionDir, err = filepath.Abs(SubmissionDir); err != nil {
|
||||
if api.DashboardDir, err = filepath.Abs(api.DashboardDir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if fic.FilesDir, err = filepath.Abs(fic.FilesDir); err != nil {
|
||||
if api.TeamsDir, err = filepath.Abs(api.TeamsDir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if api.TimestampCheck, err = filepath.Abs(api.TimestampCheck); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if settings.SettingsDir, err = filepath.Abs(settings.SettingsDir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if baseURL != "/" {
|
||||
baseURL = path.Clean(baseURL)
|
||||
} else {
|
||||
baseURL = ""
|
||||
}
|
||||
|
||||
// Creating minimal directories structure
|
||||
os.MkdirAll(fic.FilesDir, 0751)
|
||||
os.MkdirAll(pki.PKIDir, 0711)
|
||||
os.MkdirAll(api.TeamsDir, 0751)
|
||||
os.MkdirAll(api.DashboardDir, 0751)
|
||||
os.MkdirAll(settings.SettingsDir, 0751)
|
||||
|
||||
// Load rules plugins
|
||||
for _, p := range checkplugins {
|
||||
if err := sync.LoadChecksPlugin(p); err != nil {
|
||||
log.Fatalf("Unable to load rule plugin %q: %s", p, err.Error())
|
||||
} else {
|
||||
log.Printf("Rules plugin %q successfully loaded", p)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize settings and load them
|
||||
if !settings.ExistsSettings(path.Join(settings.SettingsDir, settings.SettingsFile)) {
|
||||
if err = api.ResetSettings(); err != nil {
|
||||
log.Fatal("Unable to initialize settings.json:", err)
|
||||
}
|
||||
}
|
||||
var config *settings.Settings
|
||||
if config, err = settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)); err != nil {
|
||||
log.Fatal("Unable to read settings.json:", err)
|
||||
} else {
|
||||
api.ApplySettings(config)
|
||||
}
|
||||
|
||||
// Initialize dashboard presets
|
||||
if err = api.InitDashboardPresets(api.DashboardDir); err != nil {
|
||||
log.Println("Unable to initialize dashboards presets:", err)
|
||||
}
|
||||
|
||||
// Database connection
|
||||
log.Println("Opening database...")
|
||||
if err := fic.DBInit(fmt.Sprintf("%s?parseTime=true", *dsn)); err != nil {
|
||||
if err = fic.DBInit(*dsn); err != nil {
|
||||
log.Fatal("Cannot open the database: ", err)
|
||||
}
|
||||
defer fic.DBClose()
|
||||
|
||||
log.Println("Creating database...")
|
||||
if err := fic.DBCreate(); err != nil {
|
||||
if err = fic.DBCreate(); err != nil {
|
||||
log.Fatal("Cannot create database: ", err)
|
||||
}
|
||||
|
||||
os.Chdir(PKIDir)
|
||||
// Update base URL on main page
|
||||
log.Println("Changing base URL to", baseURL+"/", "...")
|
||||
genIndex(baseURL)
|
||||
|
||||
log.Println("Registering handlers...")
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/api/", http.StripPrefix("/api", ApiHandler()))
|
||||
mux.Handle("/teams/", StaticHandler(staticDir))
|
||||
mux.Handle("/themes/", StaticHandler(staticDir))
|
||||
mux.Handle("/", http.FileServer(http.Dir(staticDir)))
|
||||
// Prepare graceful shutdown
|
||||
interrupt := make(chan os.Signal, 1)
|
||||
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
log.Println(fmt.Sprintf("Ready, listening on %s", *bind))
|
||||
if err := http.ListenAndServe(*bind, mux); err != nil {
|
||||
log.Fatal("Unable to listen and serve: ", err)
|
||||
}
|
||||
app := NewApp(config, baseURL, bind)
|
||||
go app.Start()
|
||||
|
||||
// Wait shutdown signal
|
||||
<-interrupt
|
||||
|
||||
log.Print("The service is shutting down...")
|
||||
app.Stop()
|
||||
log.Println("done")
|
||||
}
|
||||
|
|
|
|||
284
admin/pki/CA.sh
284
admin/pki/CA.sh
|
|
@ -1,284 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd $(dirname "$0")
|
||||
|
||||
if [ -z "${PKI_BASEDIR}" ]; then
|
||||
PKI_BASEDIR=$(dirname `pwd`) # equivalent to $(realpath `pwd`/..
|
||||
fi
|
||||
|
||||
PKI_DIR=${PKI_BASEDIR}/PKI
|
||||
SHARED_DIR=${PKI_DIR}/shared
|
||||
OPENSSL_CONF=`pwd`/openssl.cnf
|
||||
|
||||
CAKEY=${PKI_DIR}/private/cakey.key
|
||||
CAREQ=${PKI_DIR}/careq.csr
|
||||
CACRT=${SHARED_DIR}/cacert.crt
|
||||
CADER=${SHARED_DIR}/cacert.der
|
||||
|
||||
SRVKEY=${SHARED_DIR}/server.key
|
||||
SRVREQ=${SHARED_DIR}/server.csr
|
||||
SRVCRT=${SHARED_DIR}/server.crt
|
||||
|
||||
# Generate certificates valid for:
|
||||
DAYS=2
|
||||
STARTDATE=160125000000Z
|
||||
ENDDATE=160126235959Z
|
||||
VALIDITY="-startdate ${STARTDATE} -enddate ${ENDDATE}"
|
||||
#VALIDITY="-days ${DAYS}"
|
||||
|
||||
if [ -z "$PS1" ]
|
||||
then
|
||||
GREEN="\033[1;32m"
|
||||
RED="\033[1;31m"
|
||||
COLOR_RST="\033[0m"
|
||||
BOLD=""
|
||||
END_BOLD=""
|
||||
ECHO_OPTS="-e"
|
||||
else
|
||||
GREEN="<font color=green>"
|
||||
RED="<font color=red>"
|
||||
COLOR_RST="</font>"
|
||||
BOLD="<strong>"
|
||||
END_BOLD="</strong>"
|
||||
ECHO_OPTS=""
|
||||
fi
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "Usage: $0 (-newca|-newserver IP/URL|-revokeserver|-newclient NAME|-revoke NAME|-gencrl)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
clean()
|
||||
{
|
||||
if [ "$1" = "ca" ]; then
|
||||
rm -rf ${PKI_DIR}/* ${SHARED_DIR}/*
|
||||
mkdir -p ${PKI_DIR}/certs ${PKI_DIR}/crl ${PKI_DIR}/newcerts \
|
||||
${PKI_DIR}/private ${PKI_DIR}/pkcs ${SHARED_DIR}
|
||||
echo "01" > ${PKI_DIR}/crlnumber
|
||||
elif [ "$1" = "client" ]; then
|
||||
rm -rf ${PKI_DIR}/${2}.key ${PKI_DIR}/${2}.csr
|
||||
fi
|
||||
rm -rf $OUTPUT
|
||||
}
|
||||
|
||||
gen_crl()
|
||||
{
|
||||
echo $ECHO_OPTS "${GREEN}Generate shared/crl.pem${COLOR_RST}"
|
||||
if ! openssl ca -config ${OPENSSL_CONF} -gencrl -out ${SHARED_DIR}/crl.pem > $OUTPUT 2>&1
|
||||
then
|
||||
echo $ECHO_OPTS "${RED}Generate shared/crl.pem failed"
|
||||
cat $OUTPUT
|
||||
exit 5
|
||||
fi
|
||||
}
|
||||
|
||||
[ $# -lt 1 ] && usage
|
||||
|
||||
OUTPUT=$(mktemp)
|
||||
|
||||
case $1 in
|
||||
"-newca" )
|
||||
echo $ECHO_OPTS "${GREEN}Create the directories, take care this will delete the old directories ${COLOR_RST}"
|
||||
|
||||
clean "ca"
|
||||
touch ${PKI_DIR}/index.txt
|
||||
|
||||
ESCAPED=$(echo "${PKI_DIR}" | sed 's/[\/\.]/\\&/g')
|
||||
|
||||
echo $ECHO_OPTS "${GREEN}Making CA key and csr${COLOR_RST}"
|
||||
sed -i 's/=.*#COMMONNAME/= FIC CA #COMMONNAME/' $OPENSSL_CONF
|
||||
sed -i "s/=.*#DIR/= ${ESCAPED} #DIR/" $OPENSSL_CONF
|
||||
sed -i "s/=.*#DAYS/= ${DAYS} #DAYS/" $OPENSSL_CONF
|
||||
|
||||
type pwgen > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "command not found: pwgen"
|
||||
exit 5
|
||||
fi
|
||||
|
||||
pass=`pwgen -n -B -y 12 1`
|
||||
if ! openssl req -batch -new -keyout ${CAKEY} \
|
||||
-out ${CAREQ} -passout pass:$pass \
|
||||
-config ${OPENSSL_CONF} -extensions CORE_CA > $OUTPUT 2>&1
|
||||
then
|
||||
cat $OUTPUT
|
||||
clean "ca"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
# This line deleted the passphase for the FIC 2014 automatisation
|
||||
if ! openssl rsa -passin pass:$pass -in ${CAKEY} \
|
||||
-out ${CAKEY} > $OUTPUT 2>&1
|
||||
then
|
||||
cat $OUTPUT
|
||||
clean "ca"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
echo $ECHO_OPTS "${GREEN}Self signes the CA certificate${COLOR_RST}"
|
||||
if ! openssl ca -batch -create_serial -out ${CACRT} \
|
||||
${VALIDITY} -keyfile ${CAKEY} \
|
||||
-selfsign -extensions CORE_CA -config ${OPENSSL_CONF} \
|
||||
-infiles ${CAREQ} > $OUTPUT 2>&1
|
||||
then
|
||||
cat $OUTPUT
|
||||
clean "ca"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
echo $ECHO_OPTS "${GREEN}Generate DER format${COLOR_RST}"
|
||||
openssl x509 -in ${CACRT} -inform PEM -out ${CADER} -outform DER
|
||||
;;
|
||||
|
||||
"-newserver" )
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "Give as first argument the production IP or the domain that this certificat will cover."
|
||||
echo "eg.: $0 -newserver 10.42.23.69"
|
||||
echo " $0 -newserver fic.srs.epita.fr"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo $ECHO_OPTS "${GREEN}Making the Server key and cert${COLOR_RST}"
|
||||
if ! [ -f ${CAKEY} ]; then
|
||||
echo $ECHO_OPTS "${RED}Can not found the CA's key${COLOR_RST}"
|
||||
exit 2
|
||||
fi
|
||||
sed -i "s/=.*#COMMONNAME/=$2#COMMONNAME/" $OPENSSL_CONF
|
||||
sed -i "s/=.*#DAYS/= ${DAYS} #DAYS/" $OPENSSL_CONF
|
||||
if ! openssl req -batch -new -keyout ${SRVKEY} -out ${SRVREQ} \
|
||||
-days ${DAYS} -config ${OPENSSL_CONF} -extensions SERVER_SSL > $OUTPUT 2>&1
|
||||
then
|
||||
cat $OUTPUT
|
||||
exit 4
|
||||
fi
|
||||
echo $ECHO_OPTS "${GREEN}Signing the Server crt${COLOR_RST}"
|
||||
if ! openssl ca -policy policy_match -config ${OPENSSL_CONF} \
|
||||
-out ${SRVCRT} -extensions SERVER_SSL -infiles ${SRVREQ}
|
||||
then
|
||||
echo $ECHO_OPTS "${RED}Signing failed for new server${COLOR_RST}"
|
||||
rm -f ${SRVKEY} ${SRVREQ} ${SRVCRT}
|
||||
cat $OUTPUT
|
||||
exit 3
|
||||
else
|
||||
rm ${SRVREQ}
|
||||
echo $ECHO_OPTS "${GREEN}Signed certificate is in ${SRVCRT}${COLOR_RST}"
|
||||
fi
|
||||
;;
|
||||
|
||||
"-revokeserver" )
|
||||
echo $ECHO_OPTS "${GREEN}Revocate server certificate${COLOR_RST}"
|
||||
if ! [ -f ${CAKEY} ]; then
|
||||
echo $ECHO_OPTS "${RED}Can not found the CA's key${COLOR_RST}"
|
||||
exit 2
|
||||
fi
|
||||
if ! openssl ca -revoke ${SRVCRT} -config ${OPENSSL_CONF} \
|
||||
-keyfile ${CAKEY} -cert ${CACRT} > $OUTPUT 2>&1
|
||||
then
|
||||
echo $ECHO_OPTS "${RED}Server certificate revocation failed${COLOR_RST}"
|
||||
cat $OUTPUT
|
||||
exit 4
|
||||
fi
|
||||
rm ${SRVKEY} ${SRVCRT}
|
||||
|
||||
gen_crl
|
||||
;;
|
||||
|
||||
"-newclient" )
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 -newclient NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CLTNAM=$2
|
||||
CLTREQ=${PKI_DIR}/${CLTNAM}.csr
|
||||
CLTCRT=${PKI_DIR}/certs/${CLTNAM}.crt
|
||||
CLTKEY=${PKI_DIR}/${CLTNAM}.key
|
||||
CLTP12=${PKI_DIR}/pkcs/${CLTNAM}.p12
|
||||
|
||||
echo "=============================================================="
|
||||
echo $ECHO_OPTS "${GREEN}Making the client key and csr of ${BOLD}${2}${END_BOLD}${COLOR_RST}"
|
||||
|
||||
ESCAPED=$(echo "${PKI_DIR}" | sed 's/[\/\.]/\\&/g')
|
||||
sed -i "s/=.*#DIR/= ${ESCAPED} #DIR/" $OPENSSL_CONF
|
||||
sed -i "s/=.*#DAYS/= ${DAYS} #DAYS/" $OPENSSL_CONF
|
||||
|
||||
if ! [ -f ${CAKEY} ]; then
|
||||
echo $ECHO_OPTS "${RED}Can not found the CA's key${COLOR_RST}"
|
||||
exit 2
|
||||
fi
|
||||
sed -i "s/=.*#COMMONNAME/= $2#COMMONNAME/" $OPENSSL_CONF
|
||||
|
||||
type pwgen > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "command not found: pwgen"
|
||||
exit 5
|
||||
fi
|
||||
|
||||
pass=`pwgen -n -B -y 12 1`
|
||||
|
||||
if ! openssl req -batch -new -keyout "${CLTKEY}" -out "${CLTREQ}" \
|
||||
-config ${OPENSSL_CONF} -passout pass:$pass -days ${DAYS} -extensions CLIENT_SSL > $OUTPUT 2>&1
|
||||
then
|
||||
cat $OUTPUT
|
||||
clean "client" ${CLTNAM}
|
||||
exit 4
|
||||
fi
|
||||
|
||||
echo $ECHO_OPTS "${GREEN}Signing the Client crt${COLOR_RST}"
|
||||
if ! openssl ca -batch -policy policy_match -out "${CLTCRT}" \
|
||||
-config ${OPENSSL_CONF} -extensions CLIENT_SSL -infiles "${CLTREQ}" > $OUTPUT 2>&1
|
||||
then
|
||||
echo $ECHO_OPTS "${RED}Signing failed for $2 ${COLOR_RST}"
|
||||
cat $OUTPUT
|
||||
clean "client" ${CLTNAM}
|
||||
exit 3
|
||||
fi
|
||||
echo $ECHO_OPTS "${GREEN}Export the Client files to pkcs12${COLOR_RST}"
|
||||
if ! openssl pkcs12 -export -inkey "${CLTKEY}" -in "${CLTCRT}" -name ${2} \
|
||||
-passin pass:$pass -out "${CLTP12}" \
|
||||
-passout pass:$pass > $OUTPUT 2>&1
|
||||
then
|
||||
echo $ECHO_OPTS "${RED}pkcs12 export failed for ${BOLD}$2${END_BOLD}${COLOR_RST}"
|
||||
cat $OUTPUT
|
||||
clean "client" ${CLTNAM}
|
||||
exit 4
|
||||
else
|
||||
echo $ECHO_OPTS "Exported pkcs12 file is ${CLTP12}"
|
||||
fi
|
||||
echo "$CLTNAM:$pass" >> ${PKI_DIR}/teams.pass
|
||||
echo "$CLTNAM:$pass"
|
||||
clean "client" ${CLTNAM}
|
||||
;;
|
||||
|
||||
"-revoke" )
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 -revoke NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CLTNAM=$2
|
||||
CLTCRT=${PKI_DIR}/certs/${CLTNAM}.crt
|
||||
CLTP12=${PKI_DIR}/pkcs/${CLTNAM}.p12
|
||||
|
||||
echo $ECHO_OPTS "${GREEN}Revocate ${BOLD}${CLTNAM}${END_BOLD}${COLOR_RST}"
|
||||
if ! openssl ca -revoke "${CLTCRT}" -config "${OPENSSL_CONF}" \
|
||||
-keyfile "${CAKEY}" -cert "${CACRT}" > $OUTPUT 2>&1
|
||||
then
|
||||
echo $ECHO_OPTS "${RED}Revocation failed for ${BOLD}${CLTNAM}${END_BOLD}${COLOR_RST}"
|
||||
cat $OUTPUT
|
||||
exit 4
|
||||
fi
|
||||
rm "${CLTCRT}" "${CLTP12}"
|
||||
|
||||
gen_crl
|
||||
;;
|
||||
|
||||
"-gencrl" )
|
||||
gen_crl
|
||||
;;
|
||||
|
||||
* )
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
133
admin/pki/ca.go
Normal file
133
admin/pki/ca.go
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
package pki
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
var passwordCA string
|
||||
|
||||
func SetCAPassword(pass string) {
|
||||
passwordCA = pass
|
||||
}
|
||||
|
||||
func CACertPath() string {
|
||||
return path.Join(PKIDir, "shared", "ca.pem")
|
||||
}
|
||||
|
||||
func CAPrivkeyPath() string {
|
||||
return path.Join(PKIDir, "ca.key")
|
||||
}
|
||||
|
||||
func GenerateCA(notBefore time.Time, notAfter time.Time) error {
|
||||
ca := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(0),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"EPITA"},
|
||||
OrganizationalUnit: []string{"SRS laboratory"},
|
||||
Country: []string{"FR"},
|
||||
Locality: []string{"Paris"},
|
||||
CommonName: "FIC CA",
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
IsCA: true,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
// Ensure directories exists
|
||||
os.Mkdir(PKIDir, 0751)
|
||||
os.Mkdir(path.Join(PKIDir, "shared"), 0751)
|
||||
|
||||
pub, priv, err := GeneratePrivKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ca_b, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save certificate to file
|
||||
if err := saveCertificate(CACertPath(), ca_b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save private key to file
|
||||
if err := savePrivateKeyEncrypted(CAPrivkeyPath(), priv, passwordCA); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadCA() (priv ecdsa.PrivateKey, ca x509.Certificate, err error) {
|
||||
// Load certificate
|
||||
if fd, errr := os.Open(CACertPath()); errr != nil {
|
||||
return priv, ca, errr
|
||||
} else {
|
||||
defer fd.Close()
|
||||
if cert, errr := ioutil.ReadAll(fd); errr != nil {
|
||||
return priv, ca, errr
|
||||
} else {
|
||||
block, _ := pem.Decode(cert)
|
||||
if block == nil || block.Type != "CERTIFICATE" {
|
||||
return priv, ca, errors.New("failed to decode PEM block containing certificate")
|
||||
}
|
||||
if catmp, errr := x509.ParseCertificate(block.Bytes); errr != nil {
|
||||
return priv, ca, errr
|
||||
} else if catmp == nil {
|
||||
return priv, ca, errors.New("failed to parse certificate")
|
||||
} else {
|
||||
ca = *catmp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load private key
|
||||
if fd, errr := os.Open(CAPrivkeyPath()); errr != nil {
|
||||
return priv, ca, errr
|
||||
} else {
|
||||
defer fd.Close()
|
||||
if privkey, errr := ioutil.ReadAll(fd); errr != nil {
|
||||
return priv, ca, errr
|
||||
} else {
|
||||
block, _ := pem.Decode(privkey)
|
||||
if block == nil || block.Type != "EC PRIVATE KEY" {
|
||||
return priv, ca, errors.New("failed to decode PEM block containing EC private key")
|
||||
}
|
||||
|
||||
var decrypted_der []byte
|
||||
if x509.IsEncryptedPEMBlock(block) {
|
||||
decrypted_der, err = x509.DecryptPEMBlock(block, []byte(passwordCA))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
decrypted_der = block.Bytes
|
||||
}
|
||||
|
||||
if tmppriv, errr := x509.ParseECPrivateKey(decrypted_der); errr != nil {
|
||||
return priv, ca, errr
|
||||
} else if tmppriv == nil {
|
||||
return priv, ca, errors.New("failed to parse private key")
|
||||
} else {
|
||||
priv = *tmppriv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
84
admin/pki/client.go
Normal file
84
admin/pki/client.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package pki
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ClientCertificatePath(serial uint64) string {
|
||||
return path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), "cert.pem")
|
||||
}
|
||||
|
||||
func ClientPrivkeyPath(serial uint64) string {
|
||||
return path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), "privkey.pem")
|
||||
}
|
||||
|
||||
func ClientP12Path(serial uint64) string {
|
||||
return path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), "team.p12")
|
||||
}
|
||||
|
||||
func GenerateClient(serial uint64, notBefore time.Time, notAfter time.Time, parent_cert *x509.Certificate, parent_priv *ecdsa.PrivateKey) error {
|
||||
var certid big.Int
|
||||
certid.SetUint64(serial)
|
||||
client := &x509.Certificate{
|
||||
SerialNumber: &certid,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"EPITA"},
|
||||
OrganizationalUnit: []string{"SRS laboratory"},
|
||||
Country: []string{"FR"},
|
||||
Locality: []string{"Paris"},
|
||||
CommonName: fmt.Sprintf("TEAM-%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)),
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
IsCA: false,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
pub, priv, err := GeneratePrivKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client_b, err := x509.CreateCertificate(rand.Reader, client, parent_cert, pub, parent_priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create intermediate directory
|
||||
os.MkdirAll(path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2))), 0777)
|
||||
|
||||
// Save certificate to file
|
||||
if err := saveCertificate(ClientCertificatePath(serial), client_b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save private key to file
|
||||
if err := savePrivateKey(ClientPrivkeyPath(serial), priv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func WriteP12(serial uint64, password string) error {
|
||||
cmd := exec.Command("/usr/bin/openssl", "pkcs12", "-export",
|
||||
"-inkey", ClientPrivkeyPath(serial),
|
||||
"-in", ClientCertificatePath(serial),
|
||||
"-name", fmt.Sprintf("TEAM-%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)),
|
||||
"-passout", "pass:" + password,
|
||||
"-out", ClientP12Path(serial))
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
62
admin/pki/common.go
Normal file
62
admin/pki/common.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package pki
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
)
|
||||
|
||||
var PKIDir string
|
||||
|
||||
func GeneratePrivKey() (pub *ecdsa.PublicKey, priv *ecdsa.PrivateKey, err error) {
|
||||
if priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader); err == nil {
|
||||
pub = &priv.PublicKey
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func saveCertificate(path string, cert []byte) error {
|
||||
if certOut, err := os.Create(path); err != nil {
|
||||
return err
|
||||
} else {
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
|
||||
certOut.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func savePrivateKey(path string, private *ecdsa.PrivateKey) error {
|
||||
if keyOut, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
|
||||
return err
|
||||
} else if key_b, err := x509.MarshalECPrivateKey(private); err != nil {
|
||||
return err
|
||||
} else {
|
||||
pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: key_b})
|
||||
keyOut.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func savePrivateKeyEncrypted(path string, private *ecdsa.PrivateKey, password string) error {
|
||||
if password == "" {
|
||||
return savePrivateKey(path, private)
|
||||
}
|
||||
|
||||
if keyOut, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer keyOut.Close()
|
||||
|
||||
if key_b, err := x509.MarshalECPrivateKey(private); err != nil {
|
||||
return err
|
||||
} else if key_c, err := x509.EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", key_b, []byte(password), x509.PEMCipherAES256); err != nil {
|
||||
return err
|
||||
} else {
|
||||
pem.Encode(keyOut, key_c)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
#
|
||||
# OpenSSL example configuration file.
|
||||
# This is mostly being used for generation of certificate requests.
|
||||
#
|
||||
|
||||
# This definition stops the following lines choking if HOME isn't
|
||||
# defined.
|
||||
HOME = .
|
||||
RANDFILE = $ENV::HOME/.rnd
|
||||
|
||||
# Extra OBJECT IDENTIFIER info:
|
||||
#oid_file = $ENV::HOME/.oid
|
||||
oid_section = new_oids
|
||||
|
||||
# To use this configuration file with the "-extfile" option of the
|
||||
# "openssl x509" utility, name here the section containing the
|
||||
# X.509v3 extensions to use:
|
||||
# extensions =
|
||||
# (Alternatively, use a configuration file that has only
|
||||
# X.509v3 extensions in its main [= default] section.)
|
||||
|
||||
[ new_oids ]
|
||||
|
||||
# We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
|
||||
# Add a simple OID like this:
|
||||
# testoid1=1.2.3.4
|
||||
# Or use config file substitution like this:
|
||||
# testoid2=${testoid1}.5.6
|
||||
|
||||
# Policies used by the TSA examples.
|
||||
tsa_policy1 = 1.2.3.4.1
|
||||
tsa_policy2 = 1.2.3.4.5.6
|
||||
tsa_policy3 = 1.2.3.4.5.7
|
||||
|
||||
####################################################################
|
||||
[ ca ]
|
||||
default_ca = CA_default # The default ca section
|
||||
|
||||
####################################################################
|
||||
[ CA_default ]
|
||||
|
||||
dir = /home/nemunaire/workspace/gowks/src/srs.epita.fr/fic-server/admin/PKI #DIR # Where everything is kept
|
||||
certs = $dir/certs # Where the issued certs are kept
|
||||
crl_dir = $dir/crl # Where the issued crl are kept
|
||||
database = $dir/index.txt # database index file.
|
||||
#unique_subject = no # Set to 'no' to allow creation of
|
||||
# several ctificates with same subject.
|
||||
new_certs_dir = $dir/newcerts # default place for new certs.
|
||||
|
||||
certificate = $dir/shared/cacert.crt # The CA certificate
|
||||
serial = $dir/serial # The current serial number
|
||||
crlnumber = $dir/crlnumber # the current crl number
|
||||
# must be commented out to leave a V1 CRL
|
||||
crl = $dir/shared/crl.pem # The current CRL
|
||||
private_key = $dir/private/cakey.key # The private key
|
||||
RANDFILE = $dir/private/.rand # private random number file
|
||||
|
||||
# Comment out the following two lines for the "traditional"
|
||||
# (and highly broken) format.
|
||||
name_opt = ca_default # Subject Name options
|
||||
cert_opt = ca_default # Certificate field options
|
||||
|
||||
# Extension copying option: use with caution.
|
||||
# copy_extensions = copy
|
||||
|
||||
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
|
||||
# so this is commented out by default to leave a V1 CRL.
|
||||
# crlnumber must also be commented out to leave a V1 CRL.
|
||||
# crl_extensions = crl_ext
|
||||
|
||||
default_days = 2 #DAYS
|
||||
default_crl_days= 1 # how long before next CRL
|
||||
default_md = default # use public key default MD
|
||||
preserve = no # keep passed DN ordering
|
||||
|
||||
# A few difference way of specifying how similar the request should look
|
||||
# For type CA, the listed attributes must be the same, and the optional
|
||||
# and supplied fields are just that :-)
|
||||
policy = policy_match
|
||||
|
||||
# For the CA policy
|
||||
[ policy_match ]
|
||||
countryName = match
|
||||
stateOrProvinceName = match
|
||||
organizationName = match
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
# For the 'anything' policy
|
||||
# At this point in time, you must list all acceptable 'object'
|
||||
# types.
|
||||
[ policy_anything ]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
localityName = optional
|
||||
organizationName = optional
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
####################################################################
|
||||
[ req ]
|
||||
default_bits = 2048
|
||||
default_keyfile = privkey.pem
|
||||
distinguished_name = req_distinguished_name
|
||||
attributes = req_attributes
|
||||
x509_extensions = v3_ca # The extentions to add to the self signed cert
|
||||
|
||||
# Passwords for private keys if not present they will be prompted for
|
||||
# input_password = secret
|
||||
# output_password = secret
|
||||
|
||||
# This sets a mask for permitted string types. There are several options.
|
||||
# default: PrintableString, T61String, BMPString.
|
||||
# pkix : PrintableString, BMPString (PKIX recommendation before 2004)
|
||||
# utf8only: only UTF8Strings (PKIX recommendation after 2004).
|
||||
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
|
||||
# MASK:XXXX a literal mask value.
|
||||
# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
|
||||
string_mask = utf8only
|
||||
|
||||
# req_extensions = v3_req # The extensions to add to a certificate request
|
||||
|
||||
[ req_distinguished_name ]
|
||||
countryName = Country Name (2 letter code)
|
||||
countryName_default = FR
|
||||
countryName_min = 2
|
||||
countryName_max = 2
|
||||
|
||||
stateOrProvinceName = State or Province Name (full name)
|
||||
stateOrProvinceName_default = France
|
||||
|
||||
localityName = Locality Name (eg, city)
|
||||
localityName_default = Paris
|
||||
|
||||
0.organizationName = Organization Name (eg, company)
|
||||
0.organizationName_default = Epita
|
||||
|
||||
# we can do this but it is not needed normally :-)
|
||||
#1.organizationName = Second Organization Name (eg, company)
|
||||
#1.organizationName_default = World Wide Web Pty Ltd
|
||||
|
||||
organizationalUnitName = Organizational Unit Name (eg, section)
|
||||
organizationalUnitName_default = SRS
|
||||
|
||||
commonName = Common Name (e.g. server FQDN or YOUR name)
|
||||
commonName_default = Acier#COMMONNAME
|
||||
commonName_max = 64
|
||||
|
||||
emailAddress = Email Address
|
||||
emailAddress_max = 64
|
||||
emailAddress_default = root@srs.epita.fr
|
||||
|
||||
# SET-ex3 = SET extension number 3
|
||||
|
||||
[ req_attributes ]
|
||||
challengePassword = A challenge password
|
||||
challengePassword_min = 4
|
||||
challengePassword_max = 20
|
||||
|
||||
unstructuredName = An optional company name
|
||||
|
||||
[CORE_CA]
|
||||
nsComment = "FIC CA"
|
||||
basicConstraints = critical,CA:TRUE,pathlen:1
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid,issuer:always
|
||||
issuerAltName = issuer:copy
|
||||
keyUsage = keyCertSign, cRLSign
|
||||
nsCertType = sslCA
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer
|
||||
|
||||
[SERVER_SSL]
|
||||
nsComment = "FIC Server"
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid,issuer:always
|
||||
issuerAltName = issuer:copy
|
||||
basicConstraints = critical,CA:FALSE
|
||||
keyUsage = digitalSignature, keyEncipherment
|
||||
nsCertType = server
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer
|
||||
|
||||
[CLIENT_SSL]
|
||||
nsComment = "FIC Client"
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid,issuer:always
|
||||
issuerAltName = issuer:copy
|
||||
basicConstraints = critical,CA:FALSE
|
||||
keyUsage = digitalSignature, nonRepudiation
|
||||
nsCertType = client
|
||||
extendedKeyUsage = clientAuth
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer
|
||||
79
admin/pki/team.go
Normal file
79
admin/pki/team.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package pki
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const SymlinkPrefix = "_AUTH_ID_"
|
||||
|
||||
func GetCertificateAssociation(serial uint64) string {
|
||||
return fmt.Sprintf(SymlinkPrefix+"%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2))
|
||||
}
|
||||
|
||||
func GetAssociation(dirname string) (assocs string, err error) {
|
||||
return os.Readlink(dirname)
|
||||
}
|
||||
|
||||
func GetAssociations(dirname string) (assocs []string, err error) {
|
||||
if ds, errr := ioutil.ReadDir(dirname); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
for _, d := range ds {
|
||||
if d.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
assocs = append(assocs, d.Name())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func GetTeamSerials(dirname string, id_team int64) (serials []uint64, err error) {
|
||||
// As futher comparaisons will be made with strings, convert it only one time
|
||||
str_tid := fmt.Sprintf("%d", id_team)
|
||||
|
||||
var assocs []string
|
||||
if assocs, err = GetAssociations(dirname); err != nil {
|
||||
return
|
||||
} else {
|
||||
for _, assoc := range assocs {
|
||||
var tid string
|
||||
if tid, err = os.Readlink(path.Join(dirname, assoc)); err == nil && tid == str_tid && strings.HasPrefix(assoc, SymlinkPrefix) {
|
||||
if serial, err := strconv.ParseUint(assoc[9:], 16, 64); err == nil {
|
||||
serials = append(serials, serial)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetTeamAssociations(dirname string, id_team int64) (teamAssocs []string, err error) {
|
||||
// As futher comparaisons will be made with strings, convert it only one time
|
||||
str_tid := fmt.Sprintf("%d", id_team)
|
||||
|
||||
var assocs []string
|
||||
if assocs, err = GetAssociations(dirname); err != nil {
|
||||
return
|
||||
} else {
|
||||
for _, assoc := range assocs {
|
||||
var tid string
|
||||
if tid, err = os.Readlink(path.Join(dirname, assoc)); err == nil && tid == str_tid && !strings.HasPrefix(assoc, SymlinkPrefix) {
|
||||
teamAssocs = append(teamAssocs, assoc)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteTeamAssociation(dirname string, assoc string) error {
|
||||
if err := os.Remove(path.Join(dirname, assoc)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
154
admin/static.go
154
admin/static.go
|
|
@ -1,18 +1,160 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/api"
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
"srs.epita.fr/fic-server/settings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type staticRouting struct {
|
||||
StaticDir string
|
||||
//go:embed static
|
||||
|
||||
var assets embed.FS
|
||||
|
||||
var indexPage []byte
|
||||
|
||||
func genIndex(baseURL string) {
|
||||
tplcfg := map[string]string{
|
||||
"logo": "img/logo.png",
|
||||
"title": "Challenge",
|
||||
"urlbase": path.Clean(path.Join(baseURL+"/", "nuke"))[:len(path.Clean(path.Join(baseURL+"/", "nuke")))-4],
|
||||
}
|
||||
|
||||
ci, err := api.GetChallengeInfo()
|
||||
if err == nil && ci != nil {
|
||||
tplcfg["title"] = ci.Title
|
||||
if len(ci.MainLogo) > 0 {
|
||||
tplcfg["logo"] = "/files/logo/" + path.Base(ci.MainLogo[0])
|
||||
}
|
||||
}
|
||||
|
||||
b := bytes.NewBufferString("")
|
||||
if indexTmpl, err := template.New("index").Parse(indextpl); err != nil {
|
||||
log.Fatal("Cannot create template:", err)
|
||||
} else if err = indexTmpl.Execute(b, tplcfg); err != nil {
|
||||
log.Fatal("An error occurs during template execution:", err)
|
||||
} else {
|
||||
indexPage = b.Bytes()
|
||||
}
|
||||
}
|
||||
|
||||
func StaticHandler(staticDir string) http.Handler {
|
||||
return staticRouting{staticDir}
|
||||
func serveIndex(c *gin.Context) {
|
||||
c.Writer.Write(indexPage)
|
||||
}
|
||||
|
||||
func (a staticRouting) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, path.Join(a.StaticDir, "index.html"))
|
||||
var staticFS http.FileSystem
|
||||
|
||||
func serveFile(c *gin.Context, url string) {
|
||||
c.Request.URL.Path = url
|
||||
http.FileServer(staticFS).ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
|
||||
func declareStaticRoutes(router *gin.RouterGroup, cfg *settings.Settings, baseURL string) {
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/auth/*_", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/claims/*_", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/exercices/*_", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/events/*_", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/files", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/forge-links", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/public/*_", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/pki/*_", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/repositories", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/settings", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/sync", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/tags/*_", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/teams/*_", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
router.GET("/themes/*_", func(c *gin.Context) {
|
||||
serveIndex(c)
|
||||
})
|
||||
|
||||
router.GET("/css/*_", func(c *gin.Context) {
|
||||
serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL))
|
||||
})
|
||||
router.GET("/fonts/*_", func(c *gin.Context) {
|
||||
serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL))
|
||||
})
|
||||
router.GET("/img/*_", func(c *gin.Context) {
|
||||
serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL))
|
||||
})
|
||||
router.GET("/js/*_", func(c *gin.Context) {
|
||||
serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL))
|
||||
})
|
||||
router.GET("/views/*_", func(c *gin.Context) {
|
||||
serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL))
|
||||
})
|
||||
|
||||
router.GET("/files/*_", func(c *gin.Context) {
|
||||
filepath := path.Join(fic.FilesDir, strings.TrimPrefix(strings.TrimPrefix(c.Request.URL.Path, baseURL), "/files"))
|
||||
|
||||
if st, err := os.Stat(filepath); os.IsNotExist(err) || st.Size() == 0 {
|
||||
if st, err := os.Stat(filepath + ".gz"); err == nil {
|
||||
if fd, err := os.Open(filepath + ".gz"); err == nil {
|
||||
c.DataFromReader(http.StatusOK, st.Size(), "application/octet-stream", fd, map[string]string{
|
||||
"Content-Encoding": "gzip",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.File(filepath)
|
||||
})
|
||||
router.GET("/submissions/*_", func(c *gin.Context) {
|
||||
http.ServeFile(c.Writer, c.Request, path.Join(api.TimestampCheck, strings.TrimPrefix(c.Request.URL.Path, path.Join(baseURL, "submissions"))))
|
||||
})
|
||||
router.GET("/vids/*_", func(c *gin.Context) {
|
||||
if importer, ok := sync.GlobalImporter.(sync.DirectAccessImporter); ok {
|
||||
http.ServeFile(c.Writer, c.Request, importer.GetLocalPath(strings.TrimPrefix(c.Request.URL.Path, path.Join(baseURL, "vids"))))
|
||||
} else {
|
||||
c.AbortWithError(http.StatusBadRequest, errors.New("Only available with local importer."))
|
||||
}
|
||||
})
|
||||
|
||||
router.GET("/check_import.html", func(c *gin.Context) {
|
||||
serveFile(c, "check_import.html")
|
||||
})
|
||||
router.GET("/full_import_report.json", func(c *gin.Context) {
|
||||
http.ServeFile(c.Writer, c.Request, sync.DeepReportPath)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
52
admin/static/check_import.html
Normal file
52
admin/static/check_import.html
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Rapport d'import FIC</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<style>li:hover { background: lightgrey; }
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
function disp(data) {
|
||||
if (data["_updated"]) {
|
||||
document.getElementById("date_imp").innerHTML = new Intl.DateTimeFormat(undefined, {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric'}).format(new Date(data["_updated"][data["_updated"].length - 1]));
|
||||
}
|
||||
data.themes["_ALL"] = data._themes;
|
||||
Object.keys(data.themes).map(function(theme) {
|
||||
if (data.themes[theme] != null && theme != "_date") {
|
||||
var title = document.createElement("h3");
|
||||
title.id = theme;
|
||||
title.innerHTML = theme;
|
||||
document.getElementById("content").appendChild(title);
|
||||
|
||||
var row = document.createElement("ul");
|
||||
row.type = "square";
|
||||
for (var i = 0; i < data.themes[theme].length; i++) {
|
||||
var col = document.createElement("li");
|
||||
col.innerHTML = data.themes[theme][i];
|
||||
row.appendChild(col);
|
||||
}
|
||||
document.getElementById("content").appendChild(row);
|
||||
document.getElementById("content").appendChild(document.createElement("hr"));
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="container">
|
||||
<h1>Rapport d'import FIC</h1>
|
||||
<p>
|
||||
<strong>Date du dernier import :</strong> <span id="date_imp"></span>
|
||||
</p>
|
||||
<div id="content"></div>
|
||||
<script type="text/javascript">
|
||||
fetch('full_import_report.json')
|
||||
.then(function(response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function(report) {
|
||||
disp(report);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
9
admin/static/css/bootstrap.min.css
vendored
9
admin/static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
805
admin/static/css/glyphicon.css
Normal file
805
admin/static/css/glyphicon.css
Normal file
|
|
@ -0,0 +1,805 @@
|
|||
@font-face {
|
||||
font-family: 'Glyphicons Halflings';
|
||||
|
||||
src: url('../fonts/glyphicons-halflings-regular.eot');
|
||||
src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
|
||||
}
|
||||
.glyphicon {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
display: inline-block;
|
||||
font-family: 'Glyphicons Halflings';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
.glyphicon-asterisk:before {
|
||||
content: "\002a";
|
||||
}
|
||||
.glyphicon-plus:before {
|
||||
content: "\002b";
|
||||
}
|
||||
.glyphicon-euro:before,
|
||||
.glyphicon-eur:before {
|
||||
content: "\20ac";
|
||||
}
|
||||
.glyphicon-minus:before {
|
||||
content: "\2212";
|
||||
}
|
||||
.glyphicon-cloud:before {
|
||||
content: "\2601";
|
||||
}
|
||||
.glyphicon-envelope:before {
|
||||
content: "\2709";
|
||||
}
|
||||
.glyphicon-pencil:before {
|
||||
content: "\270f";
|
||||
}
|
||||
.glyphicon-glass:before {
|
||||
content: "\e001";
|
||||
}
|
||||
.glyphicon-music:before {
|
||||
content: "\e002";
|
||||
}
|
||||
.glyphicon-search:before {
|
||||
content: "\e003";
|
||||
}
|
||||
.glyphicon-heart:before {
|
||||
content: "\e005";
|
||||
}
|
||||
.glyphicon-star:before {
|
||||
content: "\e006";
|
||||
}
|
||||
.glyphicon-star-empty:before {
|
||||
content: "\e007";
|
||||
}
|
||||
.glyphicon-user:before {
|
||||
content: "\e008";
|
||||
}
|
||||
.glyphicon-film:before {
|
||||
content: "\e009";
|
||||
}
|
||||
.glyphicon-th-large:before {
|
||||
content: "\e010";
|
||||
}
|
||||
.glyphicon-th:before {
|
||||
content: "\e011";
|
||||
}
|
||||
.glyphicon-th-list:before {
|
||||
content: "\e012";
|
||||
}
|
||||
.glyphicon-ok:before {
|
||||
content: "\e013";
|
||||
}
|
||||
.glyphicon-remove:before {
|
||||
content: "\e014";
|
||||
}
|
||||
.glyphicon-zoom-in:before {
|
||||
content: "\e015";
|
||||
}
|
||||
.glyphicon-zoom-out:before {
|
||||
content: "\e016";
|
||||
}
|
||||
.glyphicon-off:before {
|
||||
content: "\e017";
|
||||
}
|
||||
.glyphicon-signal:before {
|
||||
content: "\e018";
|
||||
}
|
||||
.glyphicon-cog:before {
|
||||
content: "\e019";
|
||||
}
|
||||
.glyphicon-trash:before {
|
||||
content: "\e020";
|
||||
}
|
||||
.glyphicon-home:before {
|
||||
content: "\e021";
|
||||
}
|
||||
.glyphicon-file:before {
|
||||
content: "\e022";
|
||||
}
|
||||
.glyphicon-time:before {
|
||||
content: "\e023";
|
||||
}
|
||||
.glyphicon-road:before {
|
||||
content: "\e024";
|
||||
}
|
||||
.glyphicon-download-alt:before {
|
||||
content: "\e025";
|
||||
}
|
||||
.glyphicon-download:before {
|
||||
content: "\e026";
|
||||
}
|
||||
.glyphicon-upload:before {
|
||||
content: "\e027";
|
||||
}
|
||||
.glyphicon-inbox:before {
|
||||
content: "\e028";
|
||||
}
|
||||
.glyphicon-play-circle:before {
|
||||
content: "\e029";
|
||||
}
|
||||
.glyphicon-repeat:before {
|
||||
content: "\e030";
|
||||
}
|
||||
.glyphicon-refresh:before {
|
||||
content: "\e031";
|
||||
}
|
||||
.glyphicon-list-alt:before {
|
||||
content: "\e032";
|
||||
}
|
||||
.glyphicon-lock:before {
|
||||
content: "\e033";
|
||||
}
|
||||
.glyphicon-flag:before {
|
||||
content: "\e034";
|
||||
}
|
||||
.glyphicon-headphones:before {
|
||||
content: "\e035";
|
||||
}
|
||||
.glyphicon-volume-off:before {
|
||||
content: "\e036";
|
||||
}
|
||||
.glyphicon-volume-down:before {
|
||||
content: "\e037";
|
||||
}
|
||||
.glyphicon-volume-up:before {
|
||||
content: "\e038";
|
||||
}
|
||||
.glyphicon-qrcode:before {
|
||||
content: "\e039";
|
||||
}
|
||||
.glyphicon-barcode:before {
|
||||
content: "\e040";
|
||||
}
|
||||
.glyphicon-tag:before {
|
||||
content: "\e041";
|
||||
}
|
||||
.glyphicon-tags:before {
|
||||
content: "\e042";
|
||||
}
|
||||
.glyphicon-book:before {
|
||||
content: "\e043";
|
||||
}
|
||||
.glyphicon-bookmark:before {
|
||||
content: "\e044";
|
||||
}
|
||||
.glyphicon-print:before {
|
||||
content: "\e045";
|
||||
}
|
||||
.glyphicon-camera:before {
|
||||
content: "\e046";
|
||||
}
|
||||
.glyphicon-font:before {
|
||||
content: "\e047";
|
||||
}
|
||||
.glyphicon-bold:before {
|
||||
content: "\e048";
|
||||
}
|
||||
.glyphicon-italic:before {
|
||||
content: "\e049";
|
||||
}
|
||||
.glyphicon-text-height:before {
|
||||
content: "\e050";
|
||||
}
|
||||
.glyphicon-text-width:before {
|
||||
content: "\e051";
|
||||
}
|
||||
.glyphicon-align-left:before {
|
||||
content: "\e052";
|
||||
}
|
||||
.glyphicon-align-center:before {
|
||||
content: "\e053";
|
||||
}
|
||||
.glyphicon-align-right:before {
|
||||
content: "\e054";
|
||||
}
|
||||
.glyphicon-align-justify:before {
|
||||
content: "\e055";
|
||||
}
|
||||
.glyphicon-list:before {
|
||||
content: "\e056";
|
||||
}
|
||||
.glyphicon-indent-left:before {
|
||||
content: "\e057";
|
||||
}
|
||||
.glyphicon-indent-right:before {
|
||||
content: "\e058";
|
||||
}
|
||||
.glyphicon-facetime-video:before {
|
||||
content: "\e059";
|
||||
}
|
||||
.glyphicon-picture:before {
|
||||
content: "\e060";
|
||||
}
|
||||
.glyphicon-map-marker:before {
|
||||
content: "\e062";
|
||||
}
|
||||
.glyphicon-adjust:before {
|
||||
content: "\e063";
|
||||
}
|
||||
.glyphicon-tint:before {
|
||||
content: "\e064";
|
||||
}
|
||||
.glyphicon-edit:before {
|
||||
content: "\e065";
|
||||
}
|
||||
.glyphicon-share:before {
|
||||
content: "\e066";
|
||||
}
|
||||
.glyphicon-check:before {
|
||||
content: "\e067";
|
||||
}
|
||||
.glyphicon-move:before {
|
||||
content: "\e068";
|
||||
}
|
||||
.glyphicon-step-backward:before {
|
||||
content: "\e069";
|
||||
}
|
||||
.glyphicon-fast-backward:before {
|
||||
content: "\e070";
|
||||
}
|
||||
.glyphicon-backward:before {
|
||||
content: "\e071";
|
||||
}
|
||||
.glyphicon-play:before {
|
||||
content: "\e072";
|
||||
}
|
||||
.glyphicon-pause:before {
|
||||
content: "\e073";
|
||||
}
|
||||
.glyphicon-stop:before {
|
||||
content: "\e074";
|
||||
}
|
||||
.glyphicon-forward:before {
|
||||
content: "\e075";
|
||||
}
|
||||
.glyphicon-fast-forward:before {
|
||||
content: "\e076";
|
||||
}
|
||||
.glyphicon-step-forward:before {
|
||||
content: "\e077";
|
||||
}
|
||||
.glyphicon-eject:before {
|
||||
content: "\e078";
|
||||
}
|
||||
.glyphicon-chevron-left:before {
|
||||
content: "\e079";
|
||||
}
|
||||
.glyphicon-chevron-right:before {
|
||||
content: "\e080";
|
||||
}
|
||||
.glyphicon-plus-sign:before {
|
||||
content: "\e081";
|
||||
}
|
||||
.glyphicon-minus-sign:before {
|
||||
content: "\e082";
|
||||
}
|
||||
.glyphicon-remove-sign:before {
|
||||
content: "\e083";
|
||||
}
|
||||
.glyphicon-ok-sign:before {
|
||||
content: "\e084";
|
||||
}
|
||||
.glyphicon-question-sign:before {
|
||||
content: "\e085";
|
||||
}
|
||||
.glyphicon-info-sign:before {
|
||||
content: "\e086";
|
||||
}
|
||||
.glyphicon-screenshot:before {
|
||||
content: "\e087";
|
||||
}
|
||||
.glyphicon-remove-circle:before {
|
||||
content: "\e088";
|
||||
}
|
||||
.glyphicon-ok-circle:before {
|
||||
content: "\e089";
|
||||
}
|
||||
.glyphicon-ban-circle:before {
|
||||
content: "\e090";
|
||||
}
|
||||
.glyphicon-arrow-left:before {
|
||||
content: "\e091";
|
||||
}
|
||||
.glyphicon-arrow-right:before {
|
||||
content: "\e092";
|
||||
}
|
||||
.glyphicon-arrow-up:before {
|
||||
content: "\e093";
|
||||
}
|
||||
.glyphicon-arrow-down:before {
|
||||
content: "\e094";
|
||||
}
|
||||
.glyphicon-share-alt:before {
|
||||
content: "\e095";
|
||||
}
|
||||
.glyphicon-resize-full:before {
|
||||
content: "\e096";
|
||||
}
|
||||
.glyphicon-resize-small:before {
|
||||
content: "\e097";
|
||||
}
|
||||
.glyphicon-exclamation-sign:before {
|
||||
content: "\e101";
|
||||
}
|
||||
.glyphicon-gift:before {
|
||||
content: "\e102";
|
||||
}
|
||||
.glyphicon-leaf:before {
|
||||
content: "\e103";
|
||||
}
|
||||
.glyphicon-fire:before {
|
||||
content: "\e104";
|
||||
}
|
||||
.glyphicon-eye-open:before {
|
||||
content: "\e105";
|
||||
}
|
||||
.glyphicon-eye-close:before {
|
||||
content: "\e106";
|
||||
}
|
||||
.glyphicon-warning-sign:before {
|
||||
content: "\e107";
|
||||
}
|
||||
.glyphicon-plane:before {
|
||||
content: "\e108";
|
||||
}
|
||||
.glyphicon-calendar:before {
|
||||
content: "\e109";
|
||||
}
|
||||
.glyphicon-random:before {
|
||||
content: "\e110";
|
||||
}
|
||||
.glyphicon-comment:before {
|
||||
content: "\e111";
|
||||
}
|
||||
.glyphicon-magnet:before {
|
||||
content: "\e112";
|
||||
}
|
||||
.glyphicon-chevron-up:before {
|
||||
content: "\e113";
|
||||
}
|
||||
.glyphicon-chevron-down:before {
|
||||
content: "\e114";
|
||||
}
|
||||
.glyphicon-retweet:before {
|
||||
content: "\e115";
|
||||
}
|
||||
.glyphicon-shopping-cart:before {
|
||||
content: "\e116";
|
||||
}
|
||||
.glyphicon-folder-close:before {
|
||||
content: "\e117";
|
||||
}
|
||||
.glyphicon-folder-open:before {
|
||||
content: "\e118";
|
||||
}
|
||||
.glyphicon-resize-vertical:before {
|
||||
content: "\e119";
|
||||
}
|
||||
.glyphicon-resize-horizontal:before {
|
||||
content: "\e120";
|
||||
}
|
||||
.glyphicon-hdd:before {
|
||||
content: "\e121";
|
||||
}
|
||||
.glyphicon-bullhorn:before {
|
||||
content: "\e122";
|
||||
}
|
||||
.glyphicon-bell:before {
|
||||
content: "\e123";
|
||||
}
|
||||
.glyphicon-certificate:before {
|
||||
content: "\e124";
|
||||
}
|
||||
.glyphicon-thumbs-up:before {
|
||||
content: "\e125";
|
||||
}
|
||||
.glyphicon-thumbs-down:before {
|
||||
content: "\e126";
|
||||
}
|
||||
.glyphicon-hand-right:before {
|
||||
content: "\e127";
|
||||
}
|
||||
.glyphicon-hand-left:before {
|
||||
content: "\e128";
|
||||
}
|
||||
.glyphicon-hand-up:before {
|
||||
content: "\e129";
|
||||
}
|
||||
.glyphicon-hand-down:before {
|
||||
content: "\e130";
|
||||
}
|
||||
.glyphicon-circle-arrow-right:before {
|
||||
content: "\e131";
|
||||
}
|
||||
.glyphicon-circle-arrow-left:before {
|
||||
content: "\e132";
|
||||
}
|
||||
.glyphicon-circle-arrow-up:before {
|
||||
content: "\e133";
|
||||
}
|
||||
.glyphicon-circle-arrow-down:before {
|
||||
content: "\e134";
|
||||
}
|
||||
.glyphicon-globe:before {
|
||||
content: "\e135";
|
||||
}
|
||||
.glyphicon-wrench:before {
|
||||
content: "\e136";
|
||||
}
|
||||
.glyphicon-tasks:before {
|
||||
content: "\e137";
|
||||
}
|
||||
.glyphicon-filter:before {
|
||||
content: "\e138";
|
||||
}
|
||||
.glyphicon-briefcase:before {
|
||||
content: "\e139";
|
||||
}
|
||||
.glyphicon-fullscreen:before {
|
||||
content: "\e140";
|
||||
}
|
||||
.glyphicon-dashboard:before {
|
||||
content: "\e141";
|
||||
}
|
||||
.glyphicon-paperclip:before {
|
||||
content: "\e142";
|
||||
}
|
||||
.glyphicon-heart-empty:before {
|
||||
content: "\e143";
|
||||
}
|
||||
.glyphicon-link:before {
|
||||
content: "\e144";
|
||||
}
|
||||
.glyphicon-phone:before {
|
||||
content: "\e145";
|
||||
}
|
||||
.glyphicon-pushpin:before {
|
||||
content: "\e146";
|
||||
}
|
||||
.glyphicon-usd:before {
|
||||
content: "\e148";
|
||||
}
|
||||
.glyphicon-gbp:before {
|
||||
content: "\e149";
|
||||
}
|
||||
.glyphicon-sort:before {
|
||||
content: "\e150";
|
||||
}
|
||||
.glyphicon-sort-by-alphabet:before {
|
||||
content: "\e151";
|
||||
}
|
||||
.glyphicon-sort-by-alphabet-alt:before {
|
||||
content: "\e152";
|
||||
}
|
||||
.glyphicon-sort-by-order:before {
|
||||
content: "\e153";
|
||||
}
|
||||
.glyphicon-sort-by-order-alt:before {
|
||||
content: "\e154";
|
||||
}
|
||||
.glyphicon-sort-by-attributes:before {
|
||||
content: "\e155";
|
||||
}
|
||||
.glyphicon-sort-by-attributes-alt:before {
|
||||
content: "\e156";
|
||||
}
|
||||
.glyphicon-unchecked:before {
|
||||
content: "\e157";
|
||||
}
|
||||
.glyphicon-expand:before {
|
||||
content: "\e158";
|
||||
}
|
||||
.glyphicon-collapse-down:before {
|
||||
content: "\e159";
|
||||
}
|
||||
.glyphicon-collapse-up:before {
|
||||
content: "\e160";
|
||||
}
|
||||
.glyphicon-log-in:before {
|
||||
content: "\e161";
|
||||
}
|
||||
.glyphicon-flash:before {
|
||||
content: "\e162";
|
||||
}
|
||||
.glyphicon-log-out:before {
|
||||
content: "\e163";
|
||||
}
|
||||
.glyphicon-new-window:before {
|
||||
content: "\e164";
|
||||
}
|
||||
.glyphicon-record:before {
|
||||
content: "\e165";
|
||||
}
|
||||
.glyphicon-save:before {
|
||||
content: "\e166";
|
||||
}
|
||||
.glyphicon-open:before {
|
||||
content: "\e167";
|
||||
}
|
||||
.glyphicon-saved:before {
|
||||
content: "\e168";
|
||||
}
|
||||
.glyphicon-import:before {
|
||||
content: "\e169";
|
||||
}
|
||||
.glyphicon-export:before {
|
||||
content: "\e170";
|
||||
}
|
||||
.glyphicon-send:before {
|
||||
content: "\e171";
|
||||
}
|
||||
.glyphicon-floppy-disk:before {
|
||||
content: "\e172";
|
||||
}
|
||||
.glyphicon-floppy-saved:before {
|
||||
content: "\e173";
|
||||
}
|
||||
.glyphicon-floppy-remove:before {
|
||||
content: "\e174";
|
||||
}
|
||||
.glyphicon-floppy-save:before {
|
||||
content: "\e175";
|
||||
}
|
||||
.glyphicon-floppy-open:before {
|
||||
content: "\e176";
|
||||
}
|
||||
.glyphicon-credit-card:before {
|
||||
content: "\e177";
|
||||
}
|
||||
.glyphicon-transfer:before {
|
||||
content: "\e178";
|
||||
}
|
||||
.glyphicon-cutlery:before {
|
||||
content: "\e179";
|
||||
}
|
||||
.glyphicon-header:before {
|
||||
content: "\e180";
|
||||
}
|
||||
.glyphicon-compressed:before {
|
||||
content: "\e181";
|
||||
}
|
||||
.glyphicon-earphone:before {
|
||||
content: "\e182";
|
||||
}
|
||||
.glyphicon-phone-alt:before {
|
||||
content: "\e183";
|
||||
}
|
||||
.glyphicon-tower:before {
|
||||
content: "\e184";
|
||||
}
|
||||
.glyphicon-stats:before {
|
||||
content: "\e185";
|
||||
}
|
||||
.glyphicon-sd-video:before {
|
||||
content: "\e186";
|
||||
}
|
||||
.glyphicon-hd-video:before {
|
||||
content: "\e187";
|
||||
}
|
||||
.glyphicon-subtitles:before {
|
||||
content: "\e188";
|
||||
}
|
||||
.glyphicon-sound-stereo:before {
|
||||
content: "\e189";
|
||||
}
|
||||
.glyphicon-sound-dolby:before {
|
||||
content: "\e190";
|
||||
}
|
||||
.glyphicon-sound-5-1:before {
|
||||
content: "\e191";
|
||||
}
|
||||
.glyphicon-sound-6-1:before {
|
||||
content: "\e192";
|
||||
}
|
||||
.glyphicon-sound-7-1:before {
|
||||
content: "\e193";
|
||||
}
|
||||
.glyphicon-copyright-mark:before {
|
||||
content: "\e194";
|
||||
}
|
||||
.glyphicon-registration-mark:before {
|
||||
content: "\e195";
|
||||
}
|
||||
.glyphicon-cloud-download:before {
|
||||
content: "\e197";
|
||||
}
|
||||
.glyphicon-cloud-upload:before {
|
||||
content: "\e198";
|
||||
}
|
||||
.glyphicon-tree-conifer:before {
|
||||
content: "\e199";
|
||||
}
|
||||
.glyphicon-tree-deciduous:before {
|
||||
content: "\e200";
|
||||
}
|
||||
.glyphicon-cd:before {
|
||||
content: "\e201";
|
||||
}
|
||||
.glyphicon-save-file:before {
|
||||
content: "\e202";
|
||||
}
|
||||
.glyphicon-open-file:before {
|
||||
content: "\e203";
|
||||
}
|
||||
.glyphicon-level-up:before {
|
||||
content: "\e204";
|
||||
}
|
||||
.glyphicon-copy:before {
|
||||
content: "\e205";
|
||||
}
|
||||
.glyphicon-paste:before {
|
||||
content: "\e206";
|
||||
}
|
||||
.glyphicon-alert:before {
|
||||
content: "\e209";
|
||||
}
|
||||
.glyphicon-equalizer:before {
|
||||
content: "\e210";
|
||||
}
|
||||
.glyphicon-king:before {
|
||||
content: "\e211";
|
||||
}
|
||||
.glyphicon-queen:before {
|
||||
content: "\e212";
|
||||
}
|
||||
.glyphicon-pawn:before {
|
||||
content: "\e213";
|
||||
}
|
||||
.glyphicon-bishop:before {
|
||||
content: "\e214";
|
||||
}
|
||||
.glyphicon-knight:before {
|
||||
content: "\e215";
|
||||
}
|
||||
.glyphicon-baby-formula:before {
|
||||
content: "\e216";
|
||||
}
|
||||
.glyphicon-tent:before {
|
||||
content: "\26fa";
|
||||
}
|
||||
.glyphicon-blackboard:before {
|
||||
content: "\e218";
|
||||
}
|
||||
.glyphicon-bed:before {
|
||||
content: "\e219";
|
||||
}
|
||||
.glyphicon-apple:before {
|
||||
content: "\f8ff";
|
||||
}
|
||||
.glyphicon-erase:before {
|
||||
content: "\e221";
|
||||
}
|
||||
.glyphicon-hourglass:before {
|
||||
content: "\231b";
|
||||
}
|
||||
.glyphicon-lamp:before {
|
||||
content: "\e223";
|
||||
}
|
||||
.glyphicon-duplicate:before {
|
||||
content: "\e224";
|
||||
}
|
||||
.glyphicon-piggy-bank:before {
|
||||
content: "\e225";
|
||||
}
|
||||
.glyphicon-scissors:before {
|
||||
content: "\e226";
|
||||
}
|
||||
.glyphicon-bitcoin:before {
|
||||
content: "\e227";
|
||||
}
|
||||
.glyphicon-btc:before {
|
||||
content: "\e227";
|
||||
}
|
||||
.glyphicon-xbt:before {
|
||||
content: "\e227";
|
||||
}
|
||||
.glyphicon-yen:before {
|
||||
content: "\00a5";
|
||||
}
|
||||
.glyphicon-jpy:before {
|
||||
content: "\00a5";
|
||||
}
|
||||
.glyphicon-ruble:before {
|
||||
content: "\20bd";
|
||||
}
|
||||
.glyphicon-rub:before {
|
||||
content: "\20bd";
|
||||
}
|
||||
.glyphicon-scale:before {
|
||||
content: "\e230";
|
||||
}
|
||||
.glyphicon-ice-lolly:before {
|
||||
content: "\e231";
|
||||
}
|
||||
.glyphicon-ice-lolly-tasted:before {
|
||||
content: "\e232";
|
||||
}
|
||||
.glyphicon-education:before {
|
||||
content: "\e233";
|
||||
}
|
||||
.glyphicon-option-horizontal:before {
|
||||
content: "\e234";
|
||||
}
|
||||
.glyphicon-option-vertical:before {
|
||||
content: "\e235";
|
||||
}
|
||||
.glyphicon-menu-hamburger:before {
|
||||
content: "\e236";
|
||||
}
|
||||
.glyphicon-modal-window:before {
|
||||
content: "\e237";
|
||||
}
|
||||
.glyphicon-oil:before {
|
||||
content: "\e238";
|
||||
}
|
||||
.glyphicon-grain:before {
|
||||
content: "\e239";
|
||||
}
|
||||
.glyphicon-sunglasses:before {
|
||||
content: "\e240";
|
||||
}
|
||||
.glyphicon-text-size:before {
|
||||
content: "\e241";
|
||||
}
|
||||
.glyphicon-text-color:before {
|
||||
content: "\e242";
|
||||
}
|
||||
.glyphicon-text-background:before {
|
||||
content: "\e243";
|
||||
}
|
||||
.glyphicon-object-align-top:before {
|
||||
content: "\e244";
|
||||
}
|
||||
.glyphicon-object-align-bottom:before {
|
||||
content: "\e245";
|
||||
}
|
||||
.glyphicon-object-align-horizontal:before {
|
||||
content: "\e246";
|
||||
}
|
||||
.glyphicon-object-align-left:before {
|
||||
content: "\e247";
|
||||
}
|
||||
.glyphicon-object-align-vertical:before {
|
||||
content: "\e248";
|
||||
}
|
||||
.glyphicon-object-align-right:before {
|
||||
content: "\e249";
|
||||
}
|
||||
.glyphicon-triangle-right:before {
|
||||
content: "\e250";
|
||||
}
|
||||
.glyphicon-triangle-left:before {
|
||||
content: "\e251";
|
||||
}
|
||||
.glyphicon-triangle-bottom:before {
|
||||
content: "\e252";
|
||||
}
|
||||
.glyphicon-triangle-top:before {
|
||||
content: "\e253";
|
||||
}
|
||||
.glyphicon-console:before {
|
||||
content: "\e254";
|
||||
}
|
||||
.glyphicon-superscript:before {
|
||||
content: "\e255";
|
||||
}
|
||||
.glyphicon-subscript:before {
|
||||
content: "\e256";
|
||||
}
|
||||
.glyphicon-menu-left:before {
|
||||
content: "\e257";
|
||||
}
|
||||
.glyphicon-menu-right:before {
|
||||
content: "\e258";
|
||||
}
|
||||
.glyphicon-menu-down:before {
|
||||
content: "\e259";
|
||||
}
|
||||
.glyphicon-menu-up:before {
|
||||
content: "\e260";
|
||||
}
|
||||
11
admin/static/css/slate.min.css
vendored
11
admin/static/css/slate.min.css
vendored
File diff suppressed because one or more lines are too long
BIN
admin/static/fonts/FantasqueSansMono-Regular.woff
Normal file
BIN
admin/static/fonts/FantasqueSansMono-Regular.woff
Normal file
Binary file not shown.
BIN
admin/static/fonts/LinBiolinum_R.woff
Normal file
BIN
admin/static/fonts/LinBiolinum_R.woff
Normal file
Binary file not shown.
BIN
admin/static/fonts/LinBiolinum_RB.woff
Normal file
BIN
admin/static/fonts/LinBiolinum_RB.woff
Normal file
Binary file not shown.
BIN
admin/static/fonts/LinBiolinum_RI.woff
Normal file
BIN
admin/static/fonts/LinBiolinum_RI.woff
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
|
|
@ -1,51 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html ng-app="FICApp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Challenge Forensic - Administration</title>
|
||||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||||
<base href="/">
|
||||
<script src="//d3js.org/d3.v3.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse navbar-static-top">
|
||||
<div class="container">
|
||||
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img alt="FIC" src="img/fic.png" style="height: 100%">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="/teams">Équipes</a></li>
|
||||
<li><a href="/themes">Thèmes</a></li>
|
||||
<li><a href="/exercices">Exercices</a></li>
|
||||
<li><a href="/events">Événements</a></li>
|
||||
</ul>
|
||||
|
||||
<p id="clock" class="navbar-text navbar-right" ng-controller="CountdownController">
|
||||
<span id="hours">{{ time.hours | time }}</span>
|
||||
<span class="point">:</span>
|
||||
<span id="min">{{ time.minutes | time }}</span>
|
||||
<span class="point">:</span>
|
||||
<span id="sec">{{ time.seconds | time }}</span>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12" ng-view></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
<script src="/js/angular.min.js"></script>
|
||||
<script src="/js/angular-resource.min.js"></script>
|
||||
<script src="/js/angular-route.min.js"></script>
|
||||
<script src="/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
21
admin/static/js/angular-resource.min.js
vendored
21
admin/static/js/angular-resource.min.js
vendored
|
|
@ -1,14 +1,15 @@
|
|||
/*
|
||||
AngularJS v1.4.8
|
||||
(c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
AngularJS v1.7.9
|
||||
(c) 2010-2018 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(I,f,C){'use strict';function D(t,e){e=e||{};f.forEach(e,function(f,k){delete e[k]});for(var k in t)!t.hasOwnProperty(k)||"$"===k.charAt(0)&&"$"===k.charAt(1)||(e[k]=t[k]);return e}var y=f.$$minErr("$resource"),B=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;f.module("ngResource",["ng"]).provider("$resource",function(){var t=/^https?:\/\/[^\/]*/,e=this;this.defaults={stripTrailingSlashes:!0,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}}};
|
||||
this.$get=["$http","$q",function(k,F){function w(f,g){this.template=f;this.defaults=r({},e.defaults,g);this.urlParams={}}function z(l,g,s,h){function c(a,q){var c={};q=r({},g,q);u(q,function(b,q){x(b)&&(b=b());var m;if(b&&b.charAt&&"@"==b.charAt(0)){m=a;var d=b.substr(1);if(null==d||""===d||"hasOwnProperty"===d||!B.test("."+d))throw y("badmember",d);for(var d=d.split("."),n=0,g=d.length;n<g&&f.isDefined(m);n++){var e=d[n];m=null!==m?m[e]:C}}else m=b;c[q]=m});return c}function G(a){return a.resource}
|
||||
function d(a){D(a||{},this)}var t=new w(l,h);s=r({},e.defaults.actions,s);d.prototype.toJSON=function(){var a=r({},this);delete a.$promise;delete a.$resolved;return a};u(s,function(a,q){var g=/^(POST|PUT|PATCH)$/i.test(a.method);d[q]=function(b,A,m,e){var n={},h,l,s;switch(arguments.length){case 4:s=e,l=m;case 3:case 2:if(x(A)){if(x(b)){l=b;s=A;break}l=A;s=m}else{n=b;h=A;l=m;break}case 1:x(b)?l=b:g?h=b:n=b;break;case 0:break;default:throw y("badargs",arguments.length);}var w=this instanceof d,p=w?
|
||||
h:a.isArray?[]:new d(h),v={},z=a.interceptor&&a.interceptor.response||G,B=a.interceptor&&a.interceptor.responseError||C;u(a,function(a,b){switch(b){default:v[b]=H(a);break;case "params":case "isArray":case "interceptor":break;case "timeout":v[b]=a}});g&&(v.data=h);t.setUrlParams(v,r({},c(h,a.params||{}),n),a.url);n=k(v).then(function(b){var c=b.data,m=p.$promise;if(c){if(f.isArray(c)!==!!a.isArray)throw y("badcfg",q,a.isArray?"array":"object",f.isArray(c)?"array":"object",v.method,v.url);a.isArray?
|
||||
(p.length=0,u(c,function(b){"object"===typeof b?p.push(new d(b)):p.push(b)})):(D(c,p),p.$promise=m)}p.$resolved=!0;b.resource=p;return b},function(b){p.$resolved=!0;(s||E)(b);return F.reject(b)});n=n.then(function(b){var a=z(b);(l||E)(a,b.headers);return a},B);return w?n:(p.$promise=n,p.$resolved=!1,p)};d.prototype["$"+q]=function(b,a,c){x(b)&&(c=a,a=b,b={});b=d[q].call(this,b,this,a,c);return b.$promise||b}});d.bind=function(a){return z(l,r({},g,a),s)};return d}var E=f.noop,u=f.forEach,r=f.extend,
|
||||
H=f.copy,x=f.isFunction;w.prototype={setUrlParams:function(l,g,e){var h=this,c=e||h.template,k,d,r="",a=h.urlParams={};u(c.split(/\W/),function(d){if("hasOwnProperty"===d)throw y("badname");!/^\d+$/.test(d)&&d&&(new RegExp("(^|[^\\\\]):"+d+"(\\W|$)")).test(c)&&(a[d]=!0)});c=c.replace(/\\:/g,":");c=c.replace(t,function(a){r=a;return""});g=g||{};u(h.urlParams,function(a,e){k=g.hasOwnProperty(e)?g[e]:h.defaults[e];f.isDefined(k)&&null!==k?(d=encodeURIComponent(k).replace(/%40/gi,"@").replace(/%3A/gi,
|
||||
":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),c=c.replace(new RegExp(":"+e+"(\\W|$)","g"),function(b,a){return d+a})):c=c.replace(new RegExp("(/?):"+e+"(\\W|$)","g"),function(b,a,c){return"/"==c.charAt(0)?c:a+c})});h.defaults.stripTrailingSlashes&&(c=c.replace(/\/+$/,"")||"/");c=c.replace(/\/\.(?=\w+($|\?))/,".");l.url=r+c.replace(/\/\\\./,"/.");u(g,function(a,c){h.urlParams[c]||(l.params=l.params||{},l.params[c]=
|
||||
a)})}};return z}]})})(window,window.angular);
|
||||
(function(T,a){'use strict';function M(m,f){f=f||{};a.forEach(f,function(a,d){delete f[d]});for(var d in m)!m.hasOwnProperty(d)||"$"===d.charAt(0)&&"$"===d.charAt(1)||(f[d]=m[d]);return f}var B=a.$$minErr("$resource"),H=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;a.module("ngResource",["ng"]).info({angularVersion:"1.7.9"}).provider("$resource",function(){var m=/^https?:\/\/\[[^\]]*][^/]*/,f=this;this.defaults={stripTrailingSlashes:!0,cancellable:!1,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",
|
||||
isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}}};this.$get=["$http","$log","$q","$timeout",function(d,F,G,N){function C(a,d){this.template=a;this.defaults=n({},f.defaults,d);this.urlParams={}}var O=a.noop,r=a.forEach,n=a.extend,R=a.copy,P=a.isArray,D=a.isDefined,x=a.isFunction,I=a.isNumber,y=a.$$encodeUriQuery,S=a.$$encodeUriSegment;C.prototype={setUrlParams:function(a,d,f){var g=this,c=f||g.template,s,h,n="",b=g.urlParams=Object.create(null);r(c.split(/\W/),function(a){if("hasOwnProperty"===
|
||||
a)throw B("badname");!/^\d+$/.test(a)&&a&&(new RegExp("(^|[^\\\\]):"+a+"(\\W|$)")).test(c)&&(b[a]={isQueryParamValue:(new RegExp("\\?.*=:"+a+"(?:\\W|$)")).test(c)})});c=c.replace(/\\:/g,":");c=c.replace(m,function(b){n=b;return""});d=d||{};r(g.urlParams,function(b,a){s=d.hasOwnProperty(a)?d[a]:g.defaults[a];D(s)&&null!==s?(h=b.isQueryParamValue?y(s,!0):S(s),c=c.replace(new RegExp(":"+a+"(\\W|$)","g"),function(b,a){return h+a})):c=c.replace(new RegExp("(/?):"+a+"(\\W|$)","g"),function(b,a,e){return"/"===
|
||||
e.charAt(0)?e:a+e})});g.defaults.stripTrailingSlashes&&(c=c.replace(/\/+$/,"")||"/");c=c.replace(/\/\.(?=\w+($|\?))/,".");a.url=n+c.replace(/\/(\\|%5C)\./,"/.");r(d,function(b,c){g.urlParams[c]||(a.params=a.params||{},a.params[c]=b)})}};return function(m,y,z,g){function c(b,c){var d={};c=n({},y,c);r(c,function(c,f){x(c)&&(c=c(b));var e;if(c&&c.charAt&&"@"===c.charAt(0)){e=b;var k=c.substr(1);if(null==k||""===k||"hasOwnProperty"===k||!H.test("."+k))throw B("badmember",k);for(var k=k.split("."),h=0,
|
||||
n=k.length;h<n&&a.isDefined(e);h++){var g=k[h];e=null!==e?e[g]:void 0}}else e=c;d[f]=e});return d}function s(b){return b.resource}function h(b){M(b||{},this)}var Q=new C(m,g);z=n({},f.defaults.actions,z);h.prototype.toJSON=function(){var b=n({},this);delete b.$promise;delete b.$resolved;delete b.$cancelRequest;return b};r(z,function(b,a){var f=!0===b.hasBody||!1!==b.hasBody&&/^(POST|PUT|PATCH)$/i.test(b.method),g=b.timeout,m=D(b.cancellable)?b.cancellable:Q.defaults.cancellable;g&&!I(g)&&(F.debug("ngResource:\n Only numeric values are allowed as `timeout`.\n Promises are not supported in $resource, because the same value would be used for multiple requests. If you are looking for a way to cancel requests, you should use the `cancellable` option."),
|
||||
delete b.timeout,g=null);h[a]=function(e,k,J,y){function z(a){p.catch(O);null!==u&&u.resolve(a)}var K={},v,t,w;switch(arguments.length){case 4:w=y,t=J;case 3:case 2:if(x(k)){if(x(e)){t=e;w=k;break}t=k;w=J}else{K=e;v=k;t=J;break}case 1:x(e)?t=e:f?v=e:K=e;break;case 0:break;default:throw B("badargs",arguments.length);}var E=this instanceof h,l=E?v:b.isArray?[]:new h(v),q={},C=b.interceptor&&b.interceptor.request||void 0,D=b.interceptor&&b.interceptor.requestError||void 0,F=b.interceptor&&b.interceptor.response||
|
||||
s,H=b.interceptor&&b.interceptor.responseError||G.reject,I=t?function(a){t(a,A.headers,A.status,A.statusText)}:void 0;w=w||void 0;var u,L,A;r(b,function(a,b){switch(b){default:q[b]=R(a);case "params":case "isArray":case "interceptor":case "cancellable":}});!E&&m&&(u=G.defer(),q.timeout=u.promise,g&&(L=N(u.resolve,g)));f&&(q.data=v);Q.setUrlParams(q,n({},c(v,b.params||{}),K),b.url);var p=G.resolve(q).then(C).catch(D).then(d),p=p.then(function(c){var e=c.data;if(e){if(P(e)!==!!b.isArray)throw B("badcfg",
|
||||
a,b.isArray?"array":"object",P(e)?"array":"object",q.method,q.url);if(b.isArray)l.length=0,r(e,function(a){"object"===typeof a?l.push(new h(a)):l.push(a)});else{var d=l.$promise;M(e,l);l.$promise=d}}c.resource=l;A=c;return F(c)},function(a){a.resource=l;A=a;return H(a)}),p=p["finally"](function(){l.$resolved=!0;!E&&m&&(l.$cancelRequest=O,N.cancel(L),u=L=q.timeout=null)});p.then(I,w);return E?p:(l.$promise=p,l.$resolved=!1,m&&(l.$cancelRequest=z),l)};h.prototype["$"+a]=function(b,c,d){x(b)&&(d=c,c=
|
||||
b,b={});b=h[a].call(this,b,this,c,d);return b.$promise||b}});return h}}]})})(window,window.angular);
|
||||
//# sourceMappingURL=angular-resource.min.js.map
|
||||
|
|
|
|||
24
admin/static/js/angular-route.min.js
vendored
24
admin/static/js/angular-route.min.js
vendored
|
|
@ -1,15 +1,17 @@
|
|||
/*
|
||||
AngularJS v1.4.8
|
||||
(c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
AngularJS v1.7.9
|
||||
(c) 2010-2018 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(p,c,C){'use strict';function v(r,h,g){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,f,b,d,y){function z(){k&&(g.cancel(k),k=null);l&&(l.$destroy(),l=null);m&&(k=g.leave(m),k.then(function(){k=null}),m=null)}function x(){var b=r.current&&r.current.locals;if(c.isDefined(b&&b.$template)){var b=a.$new(),d=r.current;m=y(b,function(b){g.enter(b,null,m||f).then(function(){!c.isDefined(t)||t&&!a.$eval(t)||h()});z()});l=d.scope=b;l.$emit("$viewContentLoaded");
|
||||
l.$eval(w)}else z()}var l,m,k,t=b.autoscroll,w=b.onload||"";a.$on("$routeChangeSuccess",x);x()}}}function A(c,h,g){return{restrict:"ECA",priority:-400,link:function(a,f){var b=g.current,d=b.locals;f.html(d.$template);var y=c(f.contents());b.controller&&(d.$scope=a,d=h(b.controller,d),b.controllerAs&&(a[b.controllerAs]=d),f.data("$ngControllerController",d),f.children().data("$ngControllerController",d));y(a)}}}p=c.module("ngRoute",["ng"]).provider("$route",function(){function r(a,f){return c.extend(Object.create(a),
|
||||
f)}function h(a,c){var b=c.caseInsensitiveMatch,d={originalPath:a,regexp:a},g=d.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,c,b,d){a="?"===d?d:null;d="*"===d?d:null;g.push({name:b,optional:!!a});c=c||"";return""+(a?"":c)+"(?:"+(a?c:"")+(d&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");d.regexp=new RegExp("^"+a+"$",b?"i":"");return d}var g={};this.when=function(a,f){var b=c.copy(f);c.isUndefined(b.reloadOnSearch)&&(b.reloadOnSearch=!0);
|
||||
c.isUndefined(b.caseInsensitiveMatch)&&(b.caseInsensitiveMatch=this.caseInsensitiveMatch);g[a]=c.extend(b,a&&h(a,b));if(a){var d="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";g[d]=c.extend({redirectTo:a},h(d,b))}return this};this.caseInsensitiveMatch=!1;this.otherwise=function(a){"string"===typeof a&&(a={redirectTo:a});this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest","$sce",function(a,f,b,d,h,p,x){function l(b){var e=s.current;
|
||||
(v=(n=k())&&e&&n.$$route===e.$$route&&c.equals(n.pathParams,e.pathParams)&&!n.reloadOnSearch&&!w)||!e&&!n||a.$broadcast("$routeChangeStart",n,e).defaultPrevented&&b&&b.preventDefault()}function m(){var u=s.current,e=n;if(v)u.params=e.params,c.copy(u.params,b),a.$broadcast("$routeUpdate",u);else if(e||u)w=!1,(s.current=e)&&e.redirectTo&&(c.isString(e.redirectTo)?f.path(t(e.redirectTo,e.params)).search(e.params).replace():f.url(e.redirectTo(e.pathParams,f.path(),f.search())).replace()),d.when(e).then(function(){if(e){var a=
|
||||
c.extend({},e.resolve),b,f;c.forEach(a,function(b,e){a[e]=c.isString(b)?h.get(b):h.invoke(b,null,null,e)});c.isDefined(b=e.template)?c.isFunction(b)&&(b=b(e.params)):c.isDefined(f=e.templateUrl)&&(c.isFunction(f)&&(f=f(e.params)),c.isDefined(f)&&(e.loadedTemplateUrl=x.valueOf(f),b=p(f)));c.isDefined(b)&&(a.$template=b);return d.all(a)}}).then(function(f){e==s.current&&(e&&(e.locals=f,c.copy(e.params,b)),a.$broadcast("$routeChangeSuccess",e,u))},function(b){e==s.current&&a.$broadcast("$routeChangeError",
|
||||
e,u,b)})}function k(){var a,b;c.forEach(g,function(d,g){var q;if(q=!b){var h=f.path();q=d.keys;var l={};if(d.regexp)if(h=d.regexp.exec(h)){for(var k=1,m=h.length;k<m;++k){var n=q[k-1],p=h[k];n&&p&&(l[n.name]=p)}q=l}else q=null;else q=null;q=a=q}q&&(b=r(d,{params:c.extend({},f.search(),a),pathParams:a}),b.$$route=d)});return b||g[null]&&r(g[null],{params:{},pathParams:{}})}function t(a,b){var d=[];c.forEach((a||"").split(":"),function(a,c){if(0===c)d.push(a);else{var f=a.match(/(\w+)(?:[?*])?(.*)/),
|
||||
g=f[1];d.push(b[g]);d.push(f[2]||"");delete b[g]}});return d.join("")}var w=!1,n,v,s={routes:g,reload:function(){w=!0;a.$evalAsync(function(){l();m()})},updateParams:function(a){if(this.current&&this.current.$$route)a=c.extend({},this.current.params,a),f.path(t(this.current.$$route.originalPath,a)),f.search(a);else throw B("norout");}};a.$on("$locationChangeStart",l);a.$on("$locationChangeSuccess",m);return s}]});var B=c.$$minErr("ngRoute");p.provider("$routeParams",function(){this.$get=function(){return{}}});
|
||||
p.directive("ngView",v);p.directive("ngView",A);v.$inject=["$route","$anchorScroll","$animate"];A.$inject=["$compile","$controller","$route"]})(window,window.angular);
|
||||
(function(I,b){'use strict';function z(b,h){var d=[],c=b.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)(\*\?|[?*])?/g,function(b,c,h,k){b="?"===k||"*?"===k;k="*"===k||"*?"===k;d.push({name:h,optional:b});c=c||"";return(b?"(?:"+c:c+"(?:")+(k?"(.+?)":"([^/]+)")+(b?"?)?":")")}).replace(/([/$*])/g,"\\$1");h.ignoreTrailingSlashes&&(c=c.replace(/\/+$/,"")+"/*");return{keys:d,regexp:new RegExp("^"+c+"(?:[?#]|$)",h.caseInsensitiveMatch?"i":"")}}function A(b){p&&b.get("$route")}function v(u,h,d){return{restrict:"ECA",
|
||||
terminal:!0,priority:400,transclude:"element",link:function(c,f,g,l,k){function q(){r&&(d.cancel(r),r=null);m&&(m.$destroy(),m=null);s&&(r=d.leave(s),r.done(function(b){!1!==b&&(r=null)}),s=null)}function C(){var g=u.current&&u.current.locals;if(b.isDefined(g&&g.$template)){var g=c.$new(),l=u.current;s=k(g,function(g){d.enter(g,null,s||f).done(function(d){!1===d||!b.isDefined(w)||w&&!c.$eval(w)||h()});q()});m=l.scope=g;m.$emit("$viewContentLoaded");m.$eval(p)}else q()}var m,s,r,w=g.autoscroll,p=g.onload||
|
||||
"";c.$on("$routeChangeSuccess",C);C()}}}function x(b,h,d){return{restrict:"ECA",priority:-400,link:function(c,f){var g=d.current,l=g.locals;f.html(l.$template);var k=b(f.contents());if(g.controller){l.$scope=c;var q=h(g.controller,l);g.controllerAs&&(c[g.controllerAs]=q);f.data("$ngControllerController",q);f.children().data("$ngControllerController",q)}c[g.resolveAs||"$resolve"]=l;k(c)}}}var D,E,F,G,y=b.module("ngRoute",[]).info({angularVersion:"1.7.9"}).provider("$route",function(){function u(d,
|
||||
c){return b.extend(Object.create(d),c)}D=b.isArray;E=b.isObject;F=b.isDefined;G=b.noop;var h={};this.when=function(d,c){var f;f=void 0;if(D(c)){f=f||[];for(var g=0,l=c.length;g<l;g++)f[g]=c[g]}else if(E(c))for(g in f=f||{},c)if("$"!==g.charAt(0)||"$"!==g.charAt(1))f[g]=c[g];f=f||c;b.isUndefined(f.reloadOnUrl)&&(f.reloadOnUrl=!0);b.isUndefined(f.reloadOnSearch)&&(f.reloadOnSearch=!0);b.isUndefined(f.caseInsensitiveMatch)&&(f.caseInsensitiveMatch=this.caseInsensitiveMatch);h[d]=b.extend(f,{originalPath:d},
|
||||
d&&z(d,f));d&&(g="/"===d[d.length-1]?d.substr(0,d.length-1):d+"/",h[g]=b.extend({originalPath:d,redirectTo:d},z(g,f)));return this};this.caseInsensitiveMatch=!1;this.otherwise=function(b){"string"===typeof b&&(b={redirectTo:b});this.when(null,b);return this};p=!0;this.eagerInstantiationEnabled=function(b){return F(b)?(p=b,this):p};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest","$sce","$browser",function(d,c,f,g,l,k,q,p){function m(a){var e=t.current;n=A();(x=
|
||||
!B&&n&&e&&n.$$route===e.$$route&&(!n.reloadOnUrl||!n.reloadOnSearch&&b.equals(n.pathParams,e.pathParams)))||!e&&!n||d.$broadcast("$routeChangeStart",n,e).defaultPrevented&&a&&a.preventDefault()}function s(){var a=t.current,e=n;if(x)a.params=e.params,b.copy(a.params,f),d.$broadcast("$routeUpdate",a);else if(e||a){B=!1;t.current=e;var c=g.resolve(e);p.$$incOutstandingRequestCount("$route");c.then(r).then(w).then(function(g){return g&&c.then(y).then(function(c){e===t.current&&(e&&(e.locals=c,b.copy(e.params,
|
||||
f)),d.$broadcast("$routeChangeSuccess",e,a))})}).catch(function(b){e===t.current&&d.$broadcast("$routeChangeError",e,a,b)}).finally(function(){p.$$completeOutstandingRequest(G,"$route")})}}function r(a){var e={route:a,hasRedirection:!1};if(a)if(a.redirectTo)if(b.isString(a.redirectTo))e.path=v(a.redirectTo,a.params),e.search=a.params,e.hasRedirection=!0;else{var d=c.path(),f=c.search();a=a.redirectTo(a.pathParams,d,f);b.isDefined(a)&&(e.url=a,e.hasRedirection=!0)}else if(a.resolveRedirectTo)return g.resolve(l.invoke(a.resolveRedirectTo)).then(function(a){b.isDefined(a)&&
|
||||
(e.url=a,e.hasRedirection=!0);return e});return e}function w(a){var b=!0;if(a.route!==t.current)b=!1;else if(a.hasRedirection){var g=c.url(),d=a.url;d?c.url(d).replace():d=c.path(a.path).search(a.search).replace().url();d!==g&&(b=!1)}return b}function y(a){if(a){var e=b.extend({},a.resolve);b.forEach(e,function(a,c){e[c]=b.isString(a)?l.get(a):l.invoke(a,null,null,c)});a=z(a);b.isDefined(a)&&(e.$template=a);return g.all(e)}}function z(a){var e,c;b.isDefined(e=a.template)?b.isFunction(e)&&(e=e(a.params)):
|
||||
b.isDefined(c=a.templateUrl)&&(b.isFunction(c)&&(c=c(a.params)),b.isDefined(c)&&(a.loadedTemplateUrl=q.valueOf(c),e=k(c)));return e}function A(){var a,e;b.forEach(h,function(d,g){var f;if(f=!e){var h=c.path();f=d.keys;var l={};if(d.regexp)if(h=d.regexp.exec(h)){for(var k=1,p=h.length;k<p;++k){var m=f[k-1],n=h[k];m&&n&&(l[m.name]=n)}f=l}else f=null;else f=null;f=a=f}f&&(e=u(d,{params:b.extend({},c.search(),a),pathParams:a}),e.$$route=d)});return e||h[null]&&u(h[null],{params:{},pathParams:{}})}function v(a,
|
||||
c){var d=[];b.forEach((a||"").split(":"),function(a,b){if(0===b)d.push(a);else{var f=a.match(/(\w+)(?:[?*])?(.*)/),g=f[1];d.push(c[g]);d.push(f[2]||"");delete c[g]}});return d.join("")}var B=!1,n,x,t={routes:h,reload:function(){B=!0;var a={defaultPrevented:!1,preventDefault:function(){this.defaultPrevented=!0;B=!1}};d.$evalAsync(function(){m(a);a.defaultPrevented||s()})},updateParams:function(a){if(this.current&&this.current.$$route)a=b.extend({},this.current.params,a),c.path(v(this.current.$$route.originalPath,
|
||||
a)),c.search(a);else throw H("norout");}};d.$on("$locationChangeStart",m);d.$on("$locationChangeSuccess",s);return t}]}).run(A),H=b.$$minErr("ngRoute"),p;A.$inject=["$injector"];y.provider("$routeParams",function(){this.$get=function(){return{}}});y.directive("ngView",v);y.directive("ngView",x);v.$inject=["$route","$anchorScroll","$animate"];x.$inject=["$compile","$controller","$route"]})(window,window.angular);
|
||||
//# sourceMappingURL=angular-route.min.js.map
|
||||
|
|
|
|||
18
admin/static/js/angular-sanitize.min.js
vendored
Normal file
18
admin/static/js/angular-sanitize.min.js
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
AngularJS v1.7.9
|
||||
(c) 2010-2018 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(s,c){'use strict';function P(c){var h=[];C(h,E).chars(c);return h.join("")}var D=c.$$minErr("$sanitize"),F,h,G,H,I,q,E,J,K,C;c.module("ngSanitize",[]).provider("$sanitize",function(){function f(a,e){return B(a.split(","),e)}function B(a,e){var d={},b;for(b=0;b<a.length;b++)d[e?q(a[b]):a[b]]=!0;return d}function t(a,e){e&&e.length&&h(a,B(e))}function Q(a){for(var e={},d=0,b=a.length;d<b;d++){var k=a[d];e[k.name]=k.value}return e}function L(a){return a.replace(/&/g,"&").replace(z,function(a){var d=
|
||||
a.charCodeAt(0);a=a.charCodeAt(1);return"&#"+(1024*(d-55296)+(a-56320)+65536)+";"}).replace(u,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"<").replace(/>/g,">")}function A(a){for(;a;){if(a.nodeType===s.Node.ELEMENT_NODE)for(var e=a.attributes,d=0,b=e.length;d<b;d++){var k=e[d],g=k.name.toLowerCase();if("xmlns:ns1"===g||0===g.lastIndexOf("ns1:",0))a.removeAttributeNode(k),d--,b--}(e=a.firstChild)&&A(e);a=v("nextSibling",a)}}function v(a,e){var d=e[a];if(d&&J.call(e,d))throw D("elclob",
|
||||
e.outerHTML||e.outerText);return d}var y=!1,g=!1;this.$get=["$$sanitizeUri",function(a){y=!0;g&&h(m,l);return function(e){var d=[];K(e,C(d,function(b,d){return!/^unsafe:/.test(a(b,d))}));return d.join("")}}];this.enableSvg=function(a){return I(a)?(g=a,this):g};this.addValidElements=function(a){y||(H(a)&&(a={htmlElements:a}),t(l,a.svgElements),t(r,a.htmlVoidElements),t(m,a.htmlVoidElements),t(m,a.htmlElements));return this};this.addValidAttrs=function(a){y||h(M,B(a,!0));return this};F=c.bind;h=c.extend;
|
||||
G=c.forEach;H=c.isArray;I=c.isDefined;q=c.$$lowercase;E=c.noop;K=function(a,e){null===a||void 0===a?a="":"string"!==typeof a&&(a=""+a);var d=N(a);if(!d)return"";var b=5;do{if(0===b)throw D("uinput");b--;a=d.innerHTML;d=N(a)}while(a!==d.innerHTML);for(b=d.firstChild;b;){switch(b.nodeType){case 1:e.start(b.nodeName.toLowerCase(),Q(b.attributes));break;case 3:e.chars(b.textContent)}var k;if(!(k=b.firstChild)&&(1===b.nodeType&&e.end(b.nodeName.toLowerCase()),k=v("nextSibling",b),!k))for(;null==k;){b=
|
||||
v("parentNode",b);if(b===d)break;k=v("nextSibling",b);1===b.nodeType&&e.end(b.nodeName.toLowerCase())}b=k}for(;b=d.firstChild;)d.removeChild(b)};C=function(a,e){var d=!1,b=F(a,a.push);return{start:function(a,g){a=q(a);!d&&w[a]&&(d=a);d||!0!==m[a]||(b("<"),b(a),G(g,function(d,g){var c=q(g),f="img"===a&&"src"===c||"background"===c;!0!==M[c]||!0===O[c]&&!e(d,f)||(b(" "),b(g),b('="'),b(L(d)),b('"'))}),b(">"))},end:function(a){a=q(a);d||!0!==m[a]||!0===r[a]||(b("</"),b(a),b(">"));a==d&&(d=!1)},chars:function(a){d||
|
||||
b(L(a))}}};J=s.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)};var z=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,u=/([^#-~ |!])/g,r=f("area,br,col,hr,img,wbr"),x=f("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),p=f("rp,rt"),n=h({},p,x),x=h({},x,f("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul")),p=h({},p,f("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),
|
||||
l=f("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan"),w=f("script,style"),m=h({},r,x,p,n),O=f("background,cite,href,longdesc,src,xlink:href,xml:base"),n=f("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,valign,value,vspace,width"),
|
||||
p=f("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan",
|
||||
!0),M=h({},O,p,n),N=function(a,e){function d(b){b="<remove></remove>"+b;try{var d=(new a.DOMParser).parseFromString(b,"text/html").body;d.firstChild.remove();return d}catch(e){}}function b(a){c.innerHTML=a;e.documentMode&&A(c);return c}var g;if(e&&e.implementation)g=e.implementation.createHTMLDocument("inert");else throw D("noinert");var c=(g.documentElement||g.getDocumentElement()).querySelector("body");c.innerHTML='<svg><g onload="this.parentNode.remove()"></g></svg>';return c.querySelector("svg")?
|
||||
(c.innerHTML='<svg><p><style><img src="</style><img src=x onerror=alert(1)//">',c.querySelector("svg img")?d:b):function(b){b="<remove></remove>"+b;try{b=encodeURI(b)}catch(d){return}var e=new a.XMLHttpRequest;e.responseType="document";e.open("GET","data:text/html;charset=utf-8,"+b,!1);e.send(null);b=e.response.body;b.firstChild.remove();return b}}(s,s.document)}).info({angularVersion:"1.7.9"});c.module("ngSanitize").filter("linky",["$sanitize",function(f){var h=/((s?ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
|
||||
t=/^mailto:/i,q=c.$$minErr("linky"),s=c.isDefined,A=c.isFunction,v=c.isObject,y=c.isString;return function(c,z,u){function r(c){c&&l.push(P(c))}function x(c,g){var f,a=p(c);l.push("<a ");for(f in a)l.push(f+'="'+a[f]+'" ');!s(z)||"target"in a||l.push('target="',z,'" ');l.push('href="',c.replace(/"/g,"""),'">');r(g);l.push("</a>")}if(null==c||""===c)return c;if(!y(c))throw q("notstring",c);for(var p=A(u)?u:v(u)?function(){return u}:function(){return{}},n=c,l=[],w,m;c=n.match(h);)w=c[0],c[2]||
|
||||
c[4]||(w=(c[3]?"http://":"mailto:")+w),m=c.index,r(n.substr(0,m)),x(w,c[0].replace(t,"")),n=n.substring(m+c[0].length);r(n);return f(l.join(""))}}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-sanitize.min.js.map
|
||||
637
admin/static/js/angular.min.js
vendored
637
admin/static/js/angular.min.js
vendored
|
|
@ -1,295 +1,350 @@
|
|||
/*
|
||||
AngularJS v1.4.8
|
||||
(c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
AngularJS v1.7.9
|
||||
(c) 2010-2018 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(S,X,u){'use strict';function G(a){return function(){var b=arguments[0],d;d="["+(a?a+":":"")+b+"] http://errors.angularjs.org/1.4.8/"+(a?a+"/":"")+b;for(b=1;b<arguments.length;b++){d=d+(1==b?"?":"&")+"p"+(b-1)+"=";var c=encodeURIComponent,e;e=arguments[b];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;d+=c(e)}return Error(d)}}function za(a){if(null==a||Xa(a))return!1;if(I(a)||E(a)||B&&a instanceof B)return!0;
|
||||
var b="length"in Object(a)&&a.length;return Q(b)&&(0<=b&&b-1 in a||"function"==typeof a.item)}function n(a,b,d){var c,e;if(a)if(z(a))for(c in a)"prototype"==c||"length"==c||"name"==c||a.hasOwnProperty&&!a.hasOwnProperty(c)||b.call(d,a[c],c,a);else if(I(a)||za(a)){var f="object"!==typeof a;c=0;for(e=a.length;c<e;c++)(f||c in a)&&b.call(d,a[c],c,a)}else if(a.forEach&&a.forEach!==n)a.forEach(b,d,a);else if(nc(a))for(c in a)b.call(d,a[c],c,a);else if("function"===typeof a.hasOwnProperty)for(c in a)a.hasOwnProperty(c)&&
|
||||
b.call(d,a[c],c,a);else for(c in a)qa.call(a,c)&&b.call(d,a[c],c,a);return a}function oc(a,b,d){for(var c=Object.keys(a).sort(),e=0;e<c.length;e++)b.call(d,a[c[e]],c[e]);return c}function pc(a){return function(b,d){a(d,b)}}function Td(){return++nb}function Mb(a,b,d){for(var c=a.$$hashKey,e=0,f=b.length;e<f;++e){var g=b[e];if(H(g)||z(g))for(var h=Object.keys(g),k=0,l=h.length;k<l;k++){var m=h[k],r=g[m];d&&H(r)?da(r)?a[m]=new Date(r.valueOf()):Ma(r)?a[m]=new RegExp(r):r.nodeName?a[m]=r.cloneNode(!0):
|
||||
Nb(r)?a[m]=r.clone():(H(a[m])||(a[m]=I(r)?[]:{}),Mb(a[m],[r],!0)):a[m]=r}}c?a.$$hashKey=c:delete a.$$hashKey;return a}function M(a){return Mb(a,ra.call(arguments,1),!1)}function Ud(a){return Mb(a,ra.call(arguments,1),!0)}function ea(a){return parseInt(a,10)}function Ob(a,b){return M(Object.create(a),b)}function x(){}function Ya(a){return a}function na(a){return function(){return a}}function qc(a){return z(a.toString)&&a.toString!==sa}function q(a){return"undefined"===typeof a}function y(a){return"undefined"!==
|
||||
typeof a}function H(a){return null!==a&&"object"===typeof a}function nc(a){return null!==a&&"object"===typeof a&&!rc(a)}function E(a){return"string"===typeof a}function Q(a){return"number"===typeof a}function da(a){return"[object Date]"===sa.call(a)}function z(a){return"function"===typeof a}function Ma(a){return"[object RegExp]"===sa.call(a)}function Xa(a){return a&&a.window===a}function Za(a){return a&&a.$evalAsync&&a.$watch}function $a(a){return"boolean"===typeof a}function sc(a){return a&&Q(a.length)&&
|
||||
Vd.test(sa.call(a))}function Nb(a){return!(!a||!(a.nodeName||a.prop&&a.attr&&a.find))}function Wd(a){var b={};a=a.split(",");var d;for(d=0;d<a.length;d++)b[a[d]]=!0;return b}function ta(a){return F(a.nodeName||a[0]&&a[0].nodeName)}function ab(a,b){var d=a.indexOf(b);0<=d&&a.splice(d,1);return d}function bb(a,b){function d(a,b){var d=b.$$hashKey,e;if(I(a)){e=0;for(var f=a.length;e<f;e++)b.push(c(a[e]))}else if(nc(a))for(e in a)b[e]=c(a[e]);else if(a&&"function"===typeof a.hasOwnProperty)for(e in a)a.hasOwnProperty(e)&&
|
||||
(b[e]=c(a[e]));else for(e in a)qa.call(a,e)&&(b[e]=c(a[e]));d?b.$$hashKey=d:delete b.$$hashKey;return b}function c(a){if(!H(a))return a;var b=e.indexOf(a);if(-1!==b)return f[b];if(Xa(a)||Za(a))throw Aa("cpws");var b=!1,c;I(a)?(c=[],b=!0):sc(a)?c=new a.constructor(a):da(a)?c=new Date(a.getTime()):Ma(a)?(c=new RegExp(a.source,a.toString().match(/[^\/]*$/)[0]),c.lastIndex=a.lastIndex):z(a.cloneNode)?c=a.cloneNode(!0):(c=Object.create(rc(a)),b=!0);e.push(a);f.push(c);return b?d(a,c):c}var e=[],f=[];if(b){if(sc(b))throw Aa("cpta");
|
||||
if(a===b)throw Aa("cpi");I(b)?b.length=0:n(b,function(a,c){"$$hashKey"!==c&&delete b[c]});e.push(a);f.push(b);return d(a,b)}return c(a)}function ia(a,b){if(I(a)){b=b||[];for(var d=0,c=a.length;d<c;d++)b[d]=a[d]}else if(H(a))for(d in b=b||{},a)if("$"!==d.charAt(0)||"$"!==d.charAt(1))b[d]=a[d];return b||a}function ma(a,b){if(a===b)return!0;if(null===a||null===b)return!1;if(a!==a&&b!==b)return!0;var d=typeof a,c;if(d==typeof b&&"object"==d)if(I(a)){if(!I(b))return!1;if((d=a.length)==b.length){for(c=
|
||||
0;c<d;c++)if(!ma(a[c],b[c]))return!1;return!0}}else{if(da(a))return da(b)?ma(a.getTime(),b.getTime()):!1;if(Ma(a))return Ma(b)?a.toString()==b.toString():!1;if(Za(a)||Za(b)||Xa(a)||Xa(b)||I(b)||da(b)||Ma(b))return!1;d=$();for(c in a)if("$"!==c.charAt(0)&&!z(a[c])){if(!ma(a[c],b[c]))return!1;d[c]=!0}for(c in b)if(!(c in d)&&"$"!==c.charAt(0)&&y(b[c])&&!z(b[c]))return!1;return!0}return!1}function cb(a,b,d){return a.concat(ra.call(b,d))}function tc(a,b){var d=2<arguments.length?ra.call(arguments,2):
|
||||
[];return!z(b)||b instanceof RegExp?b:d.length?function(){return arguments.length?b.apply(a,cb(d,arguments,0)):b.apply(a,d)}:function(){return arguments.length?b.apply(a,arguments):b.call(a)}}function Xd(a,b){var d=b;"string"===typeof a&&"$"===a.charAt(0)&&"$"===a.charAt(1)?d=u:Xa(b)?d="$WINDOW":b&&X===b?d="$DOCUMENT":Za(b)&&(d="$SCOPE");return d}function db(a,b){if("undefined"===typeof a)return u;Q(b)||(b=b?2:null);return JSON.stringify(a,Xd,b)}function uc(a){return E(a)?JSON.parse(a):a}function vc(a,
|
||||
b){var d=Date.parse("Jan 01, 1970 00:00:00 "+a)/6E4;return isNaN(d)?b:d}function Pb(a,b,d){d=d?-1:1;var c=vc(b,a.getTimezoneOffset());b=a;a=d*(c-a.getTimezoneOffset());b=new Date(b.getTime());b.setMinutes(b.getMinutes()+a);return b}function ua(a){a=B(a).clone();try{a.empty()}catch(b){}var d=B("<div>").append(a).html();try{return a[0].nodeType===Na?F(d):d.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+F(b)})}catch(c){return F(d)}}function wc(a){try{return decodeURIComponent(a)}catch(b){}}
|
||||
function xc(a){var b={};n((a||"").split("&"),function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=wc(e),y(e)&&(f=y(f)?wc(f):!0,qa.call(b,e)?I(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function Qb(a){var b=[];n(a,function(a,c){I(a)?n(a,function(a){b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))}):b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))});return b.length?b.join("&"):""}function ob(a){return ja(a,!0).replace(/%26/gi,"&").replace(/%3D/gi,
|
||||
"=").replace(/%2B/gi,"+")}function ja(a,b){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function Yd(a,b){var d,c,e=Oa.length;for(c=0;c<e;++c)if(d=Oa[c]+b,E(d=a.getAttribute(d)))return d;return null}function Zd(a,b){var d,c,e={};n(Oa,function(b){b+="app";!d&&a.hasAttribute&&a.hasAttribute(b)&&(d=a,c=a.getAttribute(b))});n(Oa,function(b){b+="app";var e;!d&&(e=a.querySelector("["+b.replace(":",
|
||||
"\\:")+"]"))&&(d=e,c=e.getAttribute(b))});d&&(e.strictDi=null!==Yd(d,"strict-di"),b(d,c?[c]:[],e))}function yc(a,b,d){H(d)||(d={});d=M({strictDi:!1},d);var c=function(){a=B(a);if(a.injector()){var c=a[0]===X?"document":ua(a);throw Aa("btstrpd",c.replace(/</,"<").replace(/>/,">"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);b.unshift("ng");c=eb(b,d.strictDi);c.invoke(["$rootScope",
|
||||
"$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;S&&e.test(S.name)&&(d.debugInfoEnabled=!0,S.name=S.name.replace(e,""));if(S&&!f.test(S.name))return c();S.name=S.name.replace(f,"");fa.resumeBootstrap=function(a){n(a,function(a){b.push(a)});return c()};z(fa.resumeDeferredBootstrap)&&fa.resumeDeferredBootstrap()}function $d(){S.name="NG_ENABLE_DEBUG_INFO!"+S.name;S.location.reload()}
|
||||
function ae(a){a=fa.element(a).injector();if(!a)throw Aa("test");return a.get("$$testability")}function zc(a,b){b=b||"_";return a.replace(be,function(a,c){return(c?b:"")+a.toLowerCase()})}function ce(){var a;if(!Ac){var b=pb();(oa=q(b)?S.jQuery:b?S[b]:u)&&oa.fn.on?(B=oa,M(oa.fn,{scope:Pa.scope,isolateScope:Pa.isolateScope,controller:Pa.controller,injector:Pa.injector,inheritedData:Pa.inheritedData}),a=oa.cleanData,oa.cleanData=function(b){var c;if(Rb)Rb=!1;else for(var e=0,f;null!=(f=b[e]);e++)(c=
|
||||
oa._data(f,"events"))&&c.$destroy&&oa(f).triggerHandler("$destroy");a(b)}):B=N;fa.element=B;Ac=!0}}function qb(a,b,d){if(!a)throw Aa("areq",b||"?",d||"required");return a}function Qa(a,b,d){d&&I(a)&&(a=a[a.length-1]);qb(z(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Ra(a,b){if("hasOwnProperty"===a)throw Aa("badname",b);}function Bc(a,b,d){if(!b)return a;b=b.split(".");for(var c,e=a,f=b.length,g=0;g<f;g++)c=b[g],a&&(a=(e=a)[c]);return!d&&
|
||||
z(a)?tc(e,a):a}function rb(a){for(var b=a[0],d=a[a.length-1],c,e=1;b!==d&&(b=b.nextSibling);e++)if(c||a[e]!==b)c||(c=B(ra.call(a,0,e))),c.push(b);return c||a}function $(){return Object.create(null)}function de(a){function b(a,b,c){return a[b]||(a[b]=c())}var d=G("$injector"),c=G("ng");a=b(a,"angular",Object);a.$$minErr=a.$$minErr||G;return b(a,"module",function(){var a={};return function(f,g,h){if("hasOwnProperty"===f)throw c("badname","module");g&&a.hasOwnProperty(f)&&(a[f]=null);return b(a,f,function(){function a(b,
|
||||
d,e,f){f||(f=c);return function(){f[e||"push"]([b,d,arguments]);return v}}function b(a,d){return function(b,e){e&&z(e)&&(e.$$moduleName=f);c.push([a,d,arguments]);return v}}if(!g)throw d("nomod",f);var c=[],e=[],t=[],A=a("$injector","invoke","push",e),v={_invokeQueue:c,_configBlocks:e,_runBlocks:t,requires:g,name:f,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),decorator:b("$provide",
|
||||
"decorator"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),config:A,run:function(a){t.push(a);return this}};h&&A(h);return v})}})}function ee(a){M(a,{bootstrap:yc,copy:bb,extend:M,merge:Ud,equals:ma,element:B,forEach:n,injector:eb,noop:x,bind:tc,toJson:db,fromJson:uc,identity:Ya,isUndefined:q,isDefined:y,isString:E,isFunction:z,isObject:H,isNumber:Q,isElement:Nb,isArray:I,
|
||||
version:fe,isDate:da,lowercase:F,uppercase:sb,callbacks:{counter:0},getTestability:ae,$$minErr:G,$$csp:Ba,reloadWithDebugInfo:$d});Sb=de(S);Sb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:ge});a.provider("$compile",Cc).directive({a:he,input:Dc,textarea:Dc,form:ie,script:je,select:ke,style:le,option:me,ngBind:ne,ngBindHtml:oe,ngBindTemplate:pe,ngClass:qe,ngClassEven:re,ngClassOdd:se,ngCloak:te,ngController:ue,ngForm:ve,ngHide:we,ngIf:xe,ngInclude:ye,ngInit:ze,ngNonBindable:Ae,
|
||||
ngPluralize:Be,ngRepeat:Ce,ngShow:De,ngStyle:Ee,ngSwitch:Fe,ngSwitchWhen:Ge,ngSwitchDefault:He,ngOptions:Ie,ngTransclude:Je,ngModel:Ke,ngList:Le,ngChange:Me,pattern:Ec,ngPattern:Ec,required:Fc,ngRequired:Fc,minlength:Gc,ngMinlength:Gc,maxlength:Hc,ngMaxlength:Hc,ngValue:Ne,ngModelOptions:Oe}).directive({ngInclude:Pe}).directive(tb).directive(Ic);a.provider({$anchorScroll:Qe,$animate:Re,$animateCss:Se,$$animateQueue:Te,$$AnimateRunner:Ue,$browser:Ve,$cacheFactory:We,$controller:Xe,$document:Ye,$exceptionHandler:Ze,
|
||||
$filter:Jc,$$forceReflow:$e,$interpolate:af,$interval:bf,$http:cf,$httpParamSerializer:df,$httpParamSerializerJQLike:ef,$httpBackend:ff,$xhrFactory:gf,$location:hf,$log:jf,$parse:kf,$rootScope:lf,$q:mf,$$q:nf,$sce:of,$sceDelegate:pf,$sniffer:qf,$templateCache:rf,$templateRequest:sf,$$testability:tf,$timeout:uf,$window:vf,$$rAF:wf,$$jqLite:xf,$$HashMap:yf,$$cookieReader:zf})}])}function fb(a){return a.replace(Af,function(a,d,c,e){return e?c.toUpperCase():c}).replace(Bf,"Moz$1")}function Kc(a){a=a.nodeType;
|
||||
return 1===a||!a||9===a}function Lc(a,b){var d,c,e=b.createDocumentFragment(),f=[];if(Tb.test(a)){d=d||e.appendChild(b.createElement("div"));c=(Cf.exec(a)||["",""])[1].toLowerCase();c=ka[c]||ka._default;d.innerHTML=c[1]+a.replace(Df,"<$1></$2>")+c[2];for(c=c[0];c--;)d=d.lastChild;f=cb(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";n(f,function(a){e.appendChild(a)});return e}function N(a){if(a instanceof N)return a;var b;E(a)&&(a=U(a),
|
||||
b=!0);if(!(this instanceof N)){if(b&&"<"!=a.charAt(0))throw Ub("nosel");return new N(a)}if(b){b=X;var d;a=(d=Ef.exec(a))?[b.createElement(d[1])]:(d=Lc(a,b))?d.childNodes:[]}Mc(this,a)}function Vb(a){return a.cloneNode(!0)}function ub(a,b){b||vb(a);if(a.querySelectorAll)for(var d=a.querySelectorAll("*"),c=0,e=d.length;c<e;c++)vb(d[c])}function Nc(a,b,d,c){if(y(c))throw Ub("offargs");var e=(c=wb(a))&&c.events,f=c&&c.handle;if(f)if(b){var g=function(b){var c=e[b];y(d)&&ab(c||[],d);y(d)&&c&&0<c.length||
|
||||
(a.removeEventListener(b,f,!1),delete e[b])};n(b.split(" "),function(a){g(a);xb[a]&&g(xb[a])})}else for(b in e)"$destroy"!==b&&a.removeEventListener(b,f,!1),delete e[b]}function vb(a,b){var d=a.ng339,c=d&&gb[d];c&&(b?delete c.data[b]:(c.handle&&(c.events.$destroy&&c.handle({},"$destroy"),Nc(a)),delete gb[d],a.ng339=u))}function wb(a,b){var d=a.ng339,d=d&&gb[d];b&&!d&&(a.ng339=d=++Ff,d=gb[d]={events:{},data:{},handle:u});return d}function Wb(a,b,d){if(Kc(a)){var c=y(d),e=!c&&b&&!H(b),f=!b;a=(a=wb(a,
|
||||
!e))&&a.data;if(c)a[b]=d;else{if(f)return a;if(e)return a&&a[b];M(a,b)}}}function yb(a,b){return a.getAttribute?-1<(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+b+" "):!1}function zb(a,b){b&&a.setAttribute&&n(b.split(" "),function(b){a.setAttribute("class",U((" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+U(b)+" "," ")))})}function Ab(a,b){if(b&&a.setAttribute){var d=(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");n(b.split(" "),
|
||||
function(a){a=U(a);-1===d.indexOf(" "+a+" ")&&(d+=a+" ")});a.setAttribute("class",U(d))}}function Mc(a,b){if(b)if(b.nodeType)a[a.length++]=b;else{var d=b.length;if("number"===typeof d&&b.window!==b){if(d)for(var c=0;c<d;c++)a[a.length++]=b[c]}else a[a.length++]=b}}function Oc(a,b){return Bb(a,"$"+(b||"ngController")+"Controller")}function Bb(a,b,d){9==a.nodeType&&(a=a.documentElement);for(b=I(b)?b:[b];a;){for(var c=0,e=b.length;c<e;c++)if(y(d=B.data(a,b[c])))return d;a=a.parentNode||11===a.nodeType&&
|
||||
a.host}}function Pc(a){for(ub(a,!0);a.firstChild;)a.removeChild(a.firstChild)}function Xb(a,b){b||ub(a);var d=a.parentNode;d&&d.removeChild(a)}function Gf(a,b){b=b||S;if("complete"===b.document.readyState)b.setTimeout(a);else B(b).on("load",a)}function Qc(a,b){var d=Cb[b.toLowerCase()];return d&&Rc[ta(a)]&&d}function Hf(a,b){var d=function(c,d){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=b[d||c.type],g=f?f.length:0;if(g){if(q(c.immediatePropagationStopped)){var h=c.stopImmediatePropagation;
|
||||
c.stopImmediatePropagation=function(){c.immediatePropagationStopped=!0;c.stopPropagation&&c.stopPropagation();h&&h.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};var k=f.specialHandlerWrapper||If;1<g&&(f=ia(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||k(a,c,f[l])}};d.elem=a;return d}function If(a,b,d){d.call(a,b)}function Jf(a,b,d){var c=b.relatedTarget;c&&(c===a||Kf.call(a,c))||d.call(a,b)}function xf(){this.$get=function(){return M(N,
|
||||
{hasClass:function(a,b){a.attr&&(a=a[0]);return yb(a,b)},addClass:function(a,b){a.attr&&(a=a[0]);return Ab(a,b)},removeClass:function(a,b){a.attr&&(a=a[0]);return zb(a,b)}})}}function Ca(a,b){var d=a&&a.$$hashKey;if(d)return"function"===typeof d&&(d=a.$$hashKey()),d;d=typeof a;return d="function"==d||"object"==d&&null!==a?a.$$hashKey=d+":"+(b||Td)():d+":"+a}function Sa(a,b){if(b){var d=0;this.nextUid=function(){return++d}}n(a,this.put,this)}function Lf(a){return(a=a.toString().replace(Sc,"").match(Tc))?
|
||||
"function("+(a[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function eb(a,b){function d(a){return function(b,c){if(H(b))n(b,pc(a));else return a(b,c)}}function c(a,b){Ra(a,"service");if(z(b)||I(b))b=t.instantiate(b);if(!b.$get)throw Da("pget",a);return r[a+"Provider"]=b}function e(a,b){return function(){var c=v.invoke(b,this);if(q(c))throw Da("undef",a);return c}}function f(a,b,d){return c(a,{$get:!1!==d?e(a,b):b})}function g(a){qb(q(a)||I(a),"modulesToLoad","not an array");var b=[],c;n(a,function(a){function d(a){var b,
|
||||
c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=t.get(e[0]);f[e[1]].apply(f,e[2])}}if(!m.get(a)){m.put(a,!0);try{E(a)?(c=Sb(a),b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):z(a)?b.push(t.invoke(a)):I(a)?b.push(t.invoke(a)):Qa(a,"module")}catch(e){throw I(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Da("modulerr",a,e.stack||e.message||e);}}});return b}function h(a,c){function d(b,e){if(a.hasOwnProperty(b)){if(a[b]===
|
||||
k)throw Da("cdep",b+" <- "+l.join(" <- "));return a[b]}try{return l.unshift(b),a[b]=k,a[b]=c(b,e)}catch(f){throw a[b]===k&&delete a[b],f;}finally{l.shift()}}function e(a,c,f,g){"string"===typeof f&&(g=f,f=null);var h=[],k=eb.$$annotate(a,b,g),l,m,t;m=0;for(l=k.length;m<l;m++){t=k[m];if("string"!==typeof t)throw Da("itkn",t);h.push(f&&f.hasOwnProperty(t)?f[t]:d(t,g))}I(a)&&(a=a[l]);return a.apply(c,h)}return{invoke:e,instantiate:function(a,b,c){var d=Object.create((I(a)?a[a.length-1]:a).prototype||
|
||||
null);a=e(a,d,b,c);return H(a)||z(a)?a:d},get:d,annotate:eb.$$annotate,has:function(b){return r.hasOwnProperty(b+"Provider")||a.hasOwnProperty(b)}}}b=!0===b;var k={},l=[],m=new Sa([],!0),r={$provide:{provider:d(c),factory:d(f),service:d(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:d(function(a,b){return f(a,na(b),!1)}),constant:d(function(a,b){Ra(a,"constant");r[a]=b;A[a]=b}),decorator:function(a,b){var c=t.get(a+"Provider"),d=c.$get;c.$get=function(){var a=
|
||||
v.invoke(d,c);return v.invoke(b,null,{$delegate:a})}}}},t=r.$injector=h(r,function(a,b){fa.isString(b)&&l.push(b);throw Da("unpr",l.join(" <- "));}),A={},v=A.$injector=h(A,function(a,b){var c=t.get(a+"Provider",b);return v.invoke(c.$get,c,u,a)});n(g(a),function(a){a&&v.invoke(a)});return v}function Qe(){var a=!0;this.disableAutoScrolling=function(){a=!1};this.$get=["$window","$location","$rootScope",function(b,d,c){function e(a){var b=null;Array.prototype.some.call(a,function(a){if("a"===ta(a))return b=
|
||||
a,!0});return b}function f(a){if(a){a.scrollIntoView();var c;c=g.yOffset;z(c)?c=c():Nb(c)?(c=c[0],c="fixed"!==b.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):Q(c)||(c=0);c&&(a=a.getBoundingClientRect().top,b.scrollBy(0,a-c))}else b.scrollTo(0,0)}function g(a){a=E(a)?a:d.hash();var b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var h=b.document;a&&c.$watch(function(){return d.hash()},function(a,b){a===b&&""===a||Gf(function(){c.$evalAsync(g)})});
|
||||
return g}]}function hb(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;I(a)&&(a=a.join(" "));I(b)&&(b=b.join(" "));return a+" "+b}function Mf(a){E(a)&&(a=a.split(" "));var b=$();n(a,function(a){a.length&&(b[a]=!0)});return b}function Ea(a){return H(a)?a:{}}function Nf(a,b,d,c){function e(a){try{a.apply(null,ra.call(arguments,1))}finally{if(v--,0===v)for(;T.length;)try{T.pop()()}catch(b){d.error(b)}}}function f(){L=null;g();h()}function g(){a:{try{p=m.state;break a}catch(a){}p=void 0}p=q(p)?
|
||||
null:p;ma(p,J)&&(p=J);J=p}function h(){if(w!==k.url()||C!==p)w=k.url(),C=p,n(aa,function(a){a(k.url(),p)})}var k=this,l=a.location,m=a.history,r=a.setTimeout,t=a.clearTimeout,A={};k.isMock=!1;var v=0,T=[];k.$$completeOutstandingRequest=e;k.$$incOutstandingRequestCount=function(){v++};k.notifyWhenNoOutstandingRequests=function(a){0===v?a():T.push(a)};var p,C,w=l.href,ga=b.find("base"),L=null;g();C=p;k.url=function(b,d,e){q(e)&&(e=null);l!==a.location&&(l=a.location);m!==a.history&&(m=a.history);if(b){var f=
|
||||
C===e;if(w===b&&(!c.history||f))return k;var h=w&&Fa(w)===Fa(b);w=b;C=e;if(!c.history||h&&f){if(!h||L)L=b;d?l.replace(b):h?(d=l,e=b.indexOf("#"),e=-1===e?"":b.substr(e),d.hash=e):l.href=b;l.href!==b&&(L=b)}else m[d?"replaceState":"pushState"](e,"",b),g(),C=p;return k}return L||l.href.replace(/%27/g,"'")};k.state=function(){return p};var aa=[],D=!1,J=null;k.onUrlChange=function(b){if(!D){if(c.history)B(a).on("popstate",f);B(a).on("hashchange",f);D=!0}aa.push(b);return b};k.$$applicationDestroyed=function(){B(a).off("hashchange popstate",
|
||||
f)};k.$$checkUrlChange=h;k.baseHref=function(){var a=ga.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};k.defer=function(a,b){var c;v++;c=r(function(){delete A[c];e(a)},b||0);A[c]=!0;return c};k.defer.cancel=function(a){return A[a]?(delete A[a],t(a),e(x),!0):!1}}function Ve(){this.$get=["$window","$log","$sniffer","$document",function(a,b,d,c){return new Nf(a,c,b,d)}]}function We(){this.$get=function(){function a(a,c){function e(a){a!=r&&(t?t==a&&(t=a.n):t=a,f(a.n,a.p),f(a,r),r=a,
|
||||
r.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(a in b)throw G("$cacheFactory")("iid",a);var g=0,h=M({},c,{id:a}),k=$(),l=c&&c.capacity||Number.MAX_VALUE,m=$(),r=null,t=null;return b[a]={put:function(a,b){if(!q(b)){if(l<Number.MAX_VALUE){var c=m[a]||(m[a]={key:a});e(c)}a in k||g++;k[a]=b;g>l&&this.remove(t.key);return b}},get:function(a){if(l<Number.MAX_VALUE){var b=m[a];if(!b)return;e(b)}return k[a]},remove:function(a){if(l<Number.MAX_VALUE){var b=m[a];if(!b)return;b==r&&(r=b.p);b==t&&
|
||||
(t=b.n);f(b.n,b.p);delete m[a]}a in k&&(delete k[a],g--)},removeAll:function(){k=$();g=0;m=$();r=t=null},destroy:function(){m=h=k=null;delete b[a]},info:function(){return M({},h,{size:g})}}}var b={};a.info=function(){var a={};n(b,function(b,e){a[e]=b.info()});return a};a.get=function(a){return b[a]};return a}}function rf(){this.$get=["$cacheFactory",function(a){return a("templates")}]}function Cc(a,b){function d(a,b,c){var d=/^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/,e={};n(a,function(a,f){var g=a.match(d);
|
||||
if(!g)throw ha("iscp",b,f,a,c?"controller bindings definition":"isolate scope definition");e[f]={mode:g[1][0],collection:"*"===g[2],optional:"?"===g[3],attrName:g[4]||f}});return e}function c(a){var b=a.charAt(0);if(!b||b!==F(b))throw ha("baddir",a);if(a!==a.trim())throw ha("baddir",a);}var e={},f=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,g=/(([\w\-]+)(?:\:([^;]+))?;?)/,h=Wd("ngSrc,ngSrcset,src,srcset"),k=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,l=/^(on[a-z]+|formaction)$/;this.directive=function t(b,f){Ra(b,"directive");
|
||||
E(b)?(c(b),qb(f,"directiveFactory"),e.hasOwnProperty(b)||(e[b]=[],a.factory(b+"Directive",["$injector","$exceptionHandler",function(a,c){var f=[];n(e[b],function(e,g){try{var h=a.invoke(e);z(h)?h={compile:na(h)}:!h.compile&&h.link&&(h.compile=na(h.link));h.priority=h.priority||0;h.index=g;h.name=h.name||b;h.require=h.require||h.controller&&h.name;h.restrict=h.restrict||"EA";var k=h,l=h,m=h.name,t={isolateScope:null,bindToController:null};H(l.scope)&&(!0===l.bindToController?(t.bindToController=d(l.scope,
|
||||
m,!0),t.isolateScope={}):t.isolateScope=d(l.scope,m,!1));H(l.bindToController)&&(t.bindToController=d(l.bindToController,m,!0));if(H(t.bindToController)){var v=l.controller,R=l.controllerAs;if(!v)throw ha("noctrl",m);var V;a:if(R&&E(R))V=R;else{if(E(v)){var n=Uc.exec(v);if(n){V=n[3];break a}}V=void 0}if(!V)throw ha("noident",m);}var s=k.$$bindings=t;H(s.isolateScope)&&(h.$$isolateBindings=s.isolateScope);h.$$moduleName=e.$$moduleName;f.push(h)}catch(u){c(u)}});return f}])),e[b].push(f)):n(b,pc(t));
|
||||
return this};this.aHrefSanitizationWhitelist=function(a){return y(a)?(b.aHrefSanitizationWhitelist(a),this):b.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(a){return y(a)?(b.imgSrcSanitizationWhitelist(a),this):b.imgSrcSanitizationWhitelist()};var m=!0;this.debugInfoEnabled=function(a){return y(a)?(m=a,this):m};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,
|
||||
b,c,d,p,C,w,ga,L,aa,D){function J(a,b){try{a.addClass(b)}catch(c){}}function K(a,b,c,d,e){a instanceof B||(a=B(a));n(a,function(b,c){b.nodeType==Na&&b.nodeValue.match(/\S+/)&&(a[c]=B(b).wrap("<span></span>").parent()[0])});var f=O(a,b,a,c,d,e);K.$$addScopeClass(a);var g=null;return function(b,c,d){qb(b,"scope");e&&e.needsNewScope&&(b=b.$parent.$new());d=d||{};var h=d.parentBoundTranscludeFn,k=d.transcludeControllers;d=d.futureParentElement;h&&h.$$boundTransclude&&(h=h.$$boundTransclude);g||(g=(d=
|
||||
d&&d[0])?"foreignobject"!==ta(d)&&d.toString().match(/SVG/)?"svg":"html":"html");d="html"!==g?B(Yb(g,B("<div>").append(a).html())):c?Pa.clone.call(a):a;if(k)for(var l in k)d.data("$"+l+"Controller",k[l].instance);K.$$addScopeInfo(d,b);c&&c(d,b);f&&f(b,d,d,h);return d}}function O(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,m,t,w,D;if(p)for(D=Array(c.length),m=0;m<h.length;m+=3)f=h[m],D[f]=c[f];else D=c;m=0;for(t=h.length;m<t;)k=D[h[m++]],c=h[m++],f=h[m++],c?(c.scope?(l=a.$new(),K.$$addScopeInfo(B(k),
|
||||
l)):l=a,w=c.transcludeOnThisElement?R(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?R(a,b):null,c(f,l,k,d,w)):f&&f(a,k.childNodes,u,e)}for(var h=[],k,l,m,t,p,w=0;w<a.length;w++){k=new fa;l=V(a[w],[],k,0===w?d:u,e);(f=l.length?Z(l,a[w],k,b,c,null,[],[],f):null)&&f.scope&&K.$$addScopeClass(k.$$element);k=f&&f.terminal||!(m=a[w].childNodes)||!m.length?null:O(m,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(w,f,k),t=!0,p=p||f;f=null}return t?g:null}function R(a,
|
||||
b,c){return function(d,e,f,g,h){d||(d=a.$new(!1,h),d.$$transcluded=!0);return b(d,e,{parentBoundTranscludeFn:c,transcludeControllers:f,futureParentElement:g})}}function V(a,b,c,d,e){var h=c.$attr,k;switch(a.nodeType){case 1:P(b,va(ta(a)),"E",d,e);for(var l,m,t,p=a.attributes,w=0,D=p&&p.length;w<D;w++){var K=!1,A=!1;l=p[w];k=l.name;m=U(l.value);l=va(k);if(t=ka.test(l))k=k.replace(Vc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});(l=l.match(la))&&G(l[1])&&(K=k,A=k.substr(0,k.length-
|
||||
5)+"end",k=k.substr(0,k.length-6));l=va(k.toLowerCase());h[l]=k;if(t||!c.hasOwnProperty(l))c[l]=m,Qc(a,l)&&(c[l]=!0);W(a,b,m,l,t);P(b,l,"A",d,e,K,A)}a=a.className;H(a)&&(a=a.animVal);if(E(a)&&""!==a)for(;k=g.exec(a);)l=va(k[2]),P(b,l,"C",d,e)&&(c[l]=U(k[3])),a=a.substr(k.index+k[0].length);break;case Na:if(11===Ha)for(;a.parentNode&&a.nextSibling&&a.nextSibling.nodeType===Na;)a.nodeValue+=a.nextSibling.nodeValue,a.parentNode.removeChild(a.nextSibling);N(b,a.nodeValue);break;case 8:try{if(k=f.exec(a.nodeValue))l=
|
||||
va(k[1]),P(b,l,"M",d,e)&&(c[l]=U(k[2]))}catch(R){}}b.sort(Ia);return b}function Ta(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ha("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return B(d)}function s(a,b,c){return function(d,e,f,g,h){e=Ta(e[0],b,c);return a(d,e,f,g,h)}}function Z(a,b,d,e,f,g,h,l,m){function t(a,b,c,d){if(a){c&&(a=s(a,c,d));a.require=q.require;a.directiveName=x;if(O===
|
||||
q||q.$$isolateScope)a=ca(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=s(b,c,d));b.require=q.require;b.directiveName=x;if(O===q||q.$$isolateScope)b=ca(b,{isolateScope:!0});l.push(b)}}function p(a,b,c,d){var e;if(E(b)){var f=b.match(k);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;e||(d="$"+b+"Controller",e=g?c.inheritedData(d):c.data(d));if(!e&&!f)throw ha("ctreq",b,a);}else if(I(b))for(e=[],g=0,f=b.length;g<f;g++)e[g]=p(a,b[g],c,d);return e||
|
||||
null}function w(a,b,c,d,e,f){var g=$(),h;for(h in d){var k=d[h],l={$scope:k===O||k.$$isolateScope?e:f,$element:a,$attrs:b,$transclude:c},m=k.controller;"@"==m&&(m=b[k.name]);l=C(m,l,!0,k.controllerAs);g[k.name]=l;aa||a.data("$"+k.name+"Controller",l.instance)}return g}function D(a,c,e,f,g){function k(a,b,c){var d;Za(a)||(c=b,b=a,a=u);aa&&(d=v);c||(c=aa?V.parent():V);return g(a,b,d,c,Ta)}var m,t,A,v,C,V,Ga;b===e?(f=d,V=d.$$element):(V=B(e),f=new fa(V,d));A=c;O?t=c.$new(!0):R&&(A=c.$parent);g&&(C=k,
|
||||
C.$$boundTransclude=g);T&&(v=w(V,f,C,T,t,c));O&&(K.$$addScopeInfo(V,t,!0,!(J&&(J===O||J===O.$$originalDirective))),K.$$addScopeClass(V,!0),t.$$isolateBindings=O.$$isolateBindings,(Ga=ba(c,f,t,t.$$isolateBindings,O))&&t.$on("$destroy",Ga));for(var n in v){Ga=T[n];var ga=v[n],L=Ga.$$bindings.bindToController;ga.identifier&&L&&(m=ba(A,f,ga.instance,L,Ga));var q=ga();q!==ga.instance&&(ga.instance=q,V.data("$"+Ga.name+"Controller",q),m&&m(),m=ba(A,f,ga.instance,L,Ga))}F=0;for(M=h.length;F<M;F++)m=h[F],
|
||||
ea(m,m.isolateScope?t:c,V,f,m.require&&p(m.directiveName,m.require,V,v),C);var Ta=c;O&&(O.template||null===O.templateUrl)&&(Ta=t);a&&a(Ta,e.childNodes,u,g);for(F=l.length-1;0<=F;F--)m=l[F],ea(m,m.isolateScope?t:c,V,f,m.require&&p(m.directiveName,m.require,V,v),C)}m=m||{};for(var A=-Number.MAX_VALUE,R=m.newScopeDirective,T=m.controllerDirectives,O=m.newIsolateScopeDirective,J=m.templateDirective,n=m.nonTlbTranscludeDirective,ga=!1,L=!1,aa=m.hasElementTranscludeDirective,Z=d.$$element=B(b),q,x,P,Ia=
|
||||
e,G,F=0,M=a.length;F<M;F++){q=a[F];var N=q.$$start,Q=q.$$end;N&&(Z=Ta(b,N,Q));P=u;if(A>q.priority)break;if(P=q.scope)q.templateUrl||(H(P)?(Ua("new/isolated scope",O||R,q,Z),O=q):Ua("new/isolated scope",O,q,Z)),R=R||q;x=q.name;!q.templateUrl&&q.controller&&(P=q.controller,T=T||$(),Ua("'"+x+"' controller",T[x],q,Z),T[x]=q);if(P=q.transclude)ga=!0,q.$$tlb||(Ua("transclusion",n,q,Z),n=q),"element"==P?(aa=!0,A=q.priority,P=Z,Z=d.$$element=B(X.createComment(" "+x+": "+d[x]+" ")),b=Z[0],Y(f,ra.call(P,0),
|
||||
b),Ia=K(P,e,A,g&&g.name,{nonTlbTranscludeDirective:n})):(P=B(Vb(b)).contents(),Z.empty(),Ia=K(P,e,u,u,{needsNewScope:q.$$isolateScope||q.$$newScope}));if(q.template)if(L=!0,Ua("template",J,q,Z),J=q,P=z(q.template)?q.template(Z,d):q.template,P=ja(P),q.replace){g=q;P=Tb.test(P)?Xc(Yb(q.templateNamespace,U(P))):[];b=P[0];if(1!=P.length||1!==b.nodeType)throw ha("tplrt",x,"");Y(f,Z,b);P={$attr:{}};var Wc=V(b,[],P),W=a.splice(F+1,a.length-(F+1));(O||R)&&y(Wc,O,R);a=a.concat(Wc).concat(W);S(d,P);M=a.length}else Z.html(P);
|
||||
if(q.templateUrl)L=!0,Ua("template",J,q,Z),J=q,q.replace&&(g=q),D=Of(a.splice(F,a.length-F),Z,d,f,ga&&Ia,h,l,{controllerDirectives:T,newScopeDirective:R!==q&&R,newIsolateScopeDirective:O,templateDirective:J,nonTlbTranscludeDirective:n}),M=a.length;else if(q.compile)try{G=q.compile(Z,d,Ia),z(G)?t(null,G,N,Q):G&&t(G.pre,G.post,N,Q)}catch(da){c(da,ua(Z))}q.terminal&&(D.terminal=!0,A=Math.max(A,q.priority))}D.scope=R&&!0===R.scope;D.transcludeOnThisElement=ga;D.templateOnThisElement=L;D.transclude=Ia;
|
||||
m.hasElementTranscludeDirective=aa;return D}function y(a,b,c){for(var d=0,e=a.length;d<e;d++)a[d]=Ob(a[d],{$$isolateScope:b,$$newScope:c})}function P(b,d,f,g,h,k,l){if(d===h)return null;h=null;if(e.hasOwnProperty(d)){var m;d=a.get(d+"Directive");for(var p=0,w=d.length;p<w;p++)try{m=d[p],(q(g)||g>m.priority)&&-1!=m.restrict.indexOf(f)&&(k&&(m=Ob(m,{$$start:k,$$end:l})),b.push(m),h=m)}catch(D){c(D)}}return h}function G(b){if(e.hasOwnProperty(b))for(var c=a.get(b+"Directive"),d=0,f=c.length;d<f;d++)if(b=
|
||||
c[d],b.multiElement)return!0;return!1}function S(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;n(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});n(b,function(b,f){"class"==f?(J(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function Of(a,b,c,e,f,g,h,k){var l=[],m,t,p=b[0],w=a.shift(),D=Ob(w,{templateUrl:null,
|
||||
transclude:null,replace:null,$$originalDirective:w}),A=z(w.templateUrl)?w.templateUrl(b,c):w.templateUrl,K=w.templateNamespace;b.empty();d(A).then(function(d){var T,v;d=ja(d);if(w.replace){d=Tb.test(d)?Xc(Yb(K,U(d))):[];T=d[0];if(1!=d.length||1!==T.nodeType)throw ha("tplrt",w.name,A);d={$attr:{}};Y(e,b,T);var C=V(T,[],d);H(w.scope)&&y(C,!0);a=C.concat(a);S(c,d)}else T=p,b.html(d);a.unshift(D);m=Z(a,T,c,f,b,w,g,h,k);n(e,function(a,c){a==T&&(e[c]=b[0])});for(t=O(b[0].childNodes,f);l.length;){d=l.shift();
|
||||
v=l.shift();var ga=l.shift(),L=l.shift(),C=b[0];if(!d.$$destroyed){if(v!==p){var q=v.className;k.hasElementTranscludeDirective&&w.replace||(C=Vb(T));Y(ga,B(v),C);J(B(C),q)}v=m.transcludeOnThisElement?R(d,m.transclude,L):L;m(t,d,C,e,v)}}l=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(l?l.push(b,c,d,a):(m.transcludeOnThisElement&&(a=R(b,m.transclude,e)),m(t,b,c,d,a)))}}function Ia(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function Ua(a,
|
||||
b,c,d){function e(a){return a?" (module: "+a+")":""}if(b)throw ha("multidir",b.name,e(b.$$moduleName),c.name,e(c.$$moduleName),a,ua(d));}function N(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&K.$$addBindingClass(a);return function(a,c){var e=c.parent();b||K.$$addBindingClass(e);K.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function Yb(a,b){a=F(a||"html");switch(a){case "svg":case "math":var c=X.createElement("div");
|
||||
c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}function Q(a,b){if("srcdoc"==b)return L.HTML;var c=ta(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return L.RESOURCE_URL}function W(a,c,d,e,f){var g=Q(a,e);f=h[e]||f;var k=b(d,!0,g,f);if(k){if("multiple"===e&&"select"===ta(a))throw ha("selmulti",ua(a));c.push({priority:100,compile:function(){return{pre:function(a,c,h){c=h.$$observers||(h.$$observers=$());if(l.test(e))throw ha("nodomevents");
|
||||
var m=h[e];m!==d&&(k=m&&b(m,!0,g,f),d=m);k&&(h[e]=k(a),(c[e]||(c[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e,a)}))}}}})}}function Y(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context=c);break}f&&f.replaceChild(c,d);a=X.createDocumentFragment();a.appendChild(d);
|
||||
B.hasData(d)&&(B.data(c,B.data(d)),oa?(Rb=!0,oa.cleanData([d])):delete B.cache[d[B.expando]]);d=1;for(e=b.length;d<e;d++)f=b[d],B(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function ca(a,b){return M(function(){return a.apply(null,arguments)},a,b)}function ea(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,ua(d))}}function ba(a,c,d,e,f){var g=[];n(e,function(e,h){var k=e.attrName,l=e.optional,m,t,w,D;switch(e.mode){case "@":l||qa.call(c,k)||(d[h]=c[k]=void 0);c.$observe(k,function(a){E(a)&&
|
||||
(d[h]=a)});c.$$observers[k].$$scope=a;E(c[k])&&(d[h]=b(c[k])(a));break;case "=":if(!qa.call(c,k)){if(l)break;c[k]=void 0}if(l&&!c[k])break;t=p(c[k]);D=t.literal?ma:function(a,b){return a===b||a!==a&&b!==b};w=t.assign||function(){m=d[h]=t(a);throw ha("nonassign",c[k],f.name);};m=d[h]=t(a);l=function(b){D(b,d[h])||(D(b,m)?w(a,b=d[h]):d[h]=b);return m=b};l.$stateful=!0;l=e.collection?a.$watchCollection(c[k],l):a.$watch(p(c[k],l),null,t.literal);g.push(l);break;case "&":t=c.hasOwnProperty(k)?p(c[k]):
|
||||
x;if(t===x&&l)break;d[h]=function(b){return t(a,b)}}});return g.length&&function(){for(var a=0,b=g.length;a<b;++a)g[a]()}}var fa=function(a,b){if(b){var c=Object.keys(b),d,e,f;d=0;for(e=c.length;d<e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a};fa.prototype={$normalize:va,$addClass:function(a){a&&0<a.length&&aa.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&aa.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=Yc(a,b);c&&c.length&&aa.addClass(this.$$element,
|
||||
c);(c=Yc(b,a))&&c.length&&aa.removeClass(this.$$element,c)},$set:function(a,b,d,e){var f=Qc(this.$$element[0],a),g=Zc[a],h=a;f?(this.$$element.prop(a,b),e=f):g&&(this[g]=b,h=g);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=zc(a,"-"));f=ta(this.$$element);if("a"===f&&"href"===a||"img"===f&&"src"===a)this[a]=b=D(b,"src"===a);else if("img"===f&&"srcset"===a){for(var f="",g=U(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(g)?k:/(,)/,g=g.split(k),k=Math.floor(g.length/2),l=0;l<
|
||||
k;l++)var m=2*l,f=f+D(U(g[m]),!0),f=f+(" "+U(g[m+1]));g=U(g[2*l]).split(/\s/);f+=D(U(g[0]),!0);2===g.length&&(f+=" "+U(g[1]));this[a]=b=f}!1!==d&&(null===b||q(b)?this.$$element.removeAttr(e):this.$$element.attr(e,b));(a=this.$$observers)&&n(a[h],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=$()),e=d[a]||(d[a]=[]);e.push(b);w.$evalAsync(function(){e.$$inter||!c.hasOwnProperty(a)||q(c[a])||b(c[a])});return function(){ab(e,b)}}};var da=b.startSymbol(),
|
||||
ia=b.endSymbol(),ja="{{"==da||"}}"==ia?Ya:function(a){return a.replace(/\{\{/g,da).replace(/}}/g,ia)},ka=/^ngAttr[A-Z]/,la=/^(.+)Start$/;K.$$addBindingInfo=m?function(a,b){var c=a.data("$binding")||[];I(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:x;K.$$addBindingClass=m?function(a){J(a,"ng-binding")}:x;K.$$addScopeInfo=m?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:x;K.$$addScopeClass=m?function(a,b){J(a,b?"ng-isolate-scope":"ng-scope")}:x;return K}]}function va(a){return fb(a.replace(Vc,
|
||||
""))}function Yc(a,b){var d="",c=a.split(/\s+/),e=b.split(/\s+/),f=0;a:for(;f<c.length;f++){for(var g=c[f],h=0;h<e.length;h++)if(g==e[h])continue a;d+=(0<d.length?" ":"")+g}return d}function Xc(a){a=B(a);var b=a.length;if(1>=b)return a;for(;b--;)8===a[b].nodeType&&Pf.call(a,b,1);return a}function Xe(){var a={},b=!1;this.register=function(b,c){Ra(b,"controller");H(b)?M(a,b):a[b]=c};this.allowGlobals=function(){b=!0};this.$get=["$injector","$window",function(d,c){function e(a,b,c,d){if(!a||!H(a.$scope))throw G("$controller")("noscp",
|
||||
d,b);a.$scope[b]=c}return function(f,g,h,k){var l,m,r;h=!0===h;k&&E(k)&&(r=k);if(E(f)){k=f.match(Uc);if(!k)throw Qf("ctrlfmt",f);m=k[1];r=r||k[3];f=a.hasOwnProperty(m)?a[m]:Bc(g.$scope,m,!0)||(b?Bc(c,m,!0):u);Qa(f,m,!0)}if(h)return h=(I(f)?f[f.length-1]:f).prototype,l=Object.create(h||null),r&&e(g,r,l,m||f.name),M(function(){var a=d.invoke(f,l,g,m);a!==l&&(H(a)||z(a))&&(l=a,r&&e(g,r,l,m||f.name));return l},{instance:l,identifier:r});l=d.instantiate(f,g,m);r&&e(g,r,l,m||f.name);return l}}]}function Ye(){this.$get=
|
||||
["$window",function(a){return B(a.document)}]}function Ze(){this.$get=["$log",function(a){return function(b,d){a.error.apply(a,arguments)}}]}function Zb(a){return H(a)?da(a)?a.toISOString():db(a):a}function df(){this.$get=function(){return function(a){if(!a)return"";var b=[];oc(a,function(a,c){null===a||q(a)||(I(a)?n(a,function(a,d){b.push(ja(c)+"="+ja(Zb(a)))}):b.push(ja(c)+"="+ja(Zb(a))))});return b.join("&")}}}function ef(){this.$get=function(){return function(a){function b(a,e,f){null===a||q(a)||
|
||||
(I(a)?n(a,function(a,c){b(a,e+"["+(H(a)?c:"")+"]")}):H(a)&&!da(a)?oc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}):d.push(ja(e)+"="+ja(Zb(a))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function $b(a,b){if(E(a)){var d=a.replace(Rf,"").trim();if(d){var c=b("Content-Type");(c=c&&0===c.indexOf($c))||(c=(c=d.match(Sf))&&Tf[c[0]].test(d));c&&(a=uc(d))}}return a}function ad(a){var b=$(),d;E(a)?n(a.split("\n"),function(a){d=a.indexOf(":");var e=F(U(a.substr(0,d)));a=U(a.substr(d+1));e&&
|
||||
(b[e]=b[e]?b[e]+", "+a:a)}):H(a)&&n(a,function(a,d){var f=F(d),g=U(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function bd(a){var b;return function(d){b||(b=ad(a));return d?(d=b[F(d)],void 0===d&&(d=null),d):b}}function cd(a,b,d,c){if(z(c))return c(a,b,d);n(c,function(c){a=c(a,b,d)});return a}function cf(){var a=this.defaults={transformResponse:[$b],transformRequest:[function(a){return H(a)&&"[object File]"!==sa.call(a)&&"[object Blob]"!==sa.call(a)&&"[object FormData]"!==sa.call(a)?db(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},
|
||||
post:ia(ac),put:ia(ac),patch:ia(ac)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer"},b=!1;this.useApplyAsync=function(a){return y(a)?(b=!!a,this):b};var d=!0;this.useLegacyPromiseExtensions=function(a){return y(a)?(d=!!a,this):d};var c=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,g,h,k,l){function m(b){function c(a){var b=M({},a);b.data=cd(a.data,a.headers,a.status,f.transformResponse);
|
||||
a=a.status;return 200<=a&&300>a?b:k.reject(b)}function e(a,b){var c,d={};n(a,function(a,e){z(a)?(c=a(b),null!=c&&(d[e]=c)):d[e]=a});return d}if(!fa.isObject(b))throw G("$http")("badreq",b);var f=M({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer},b);f.headers=function(b){var c=a.headers,d=M({},b.headers),f,g,h,c=M({},c.common,c[F(b.method)]);a:for(f in c){g=F(f);for(h in d)if(F(h)===g)continue a;d[f]=c[f]}return e(d,ia(b))}(b);
|
||||
f.method=sb(f.method);f.paramSerializer=E(f.paramSerializer)?l.get(f.paramSerializer):f.paramSerializer;var g=[function(b){var d=b.headers,e=cd(b.data,bd(d),u,b.transformRequest);q(e)&&n(d,function(a,b){"content-type"===F(b)&&delete d[b]});q(b.withCredentials)&&!q(a.withCredentials)&&(b.withCredentials=a.withCredentials);return r(b,e).then(c,c)},u],h=k.when(f);for(n(v,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){b=
|
||||
g.shift();var m=g.shift(),h=h.then(b,m)}d?(h.success=function(a){Qa(a,"fn");h.then(function(b){a(b.data,b.status,b.headers,f)});return h},h.error=function(a){Qa(a,"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=dd("success"),h.error=dd("error"));return h}function r(c,d){function g(a,c,d,e){function f(){l(c,a,d,e)}J&&(200<=a&&300>a?J.put(R,[a,c,ad(d),e]):J.remove(R));b?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function l(a,b,d,e){b=-1<=b?b:0;(200<=b&&300>b?n.resolve:
|
||||
n.reject)({data:a,status:b,headers:bd(d),config:c,statusText:e})}function r(a){l(a.data,a.status,ia(a.headers()),a.statusText)}function v(){var a=m.pendingRequests.indexOf(c);-1!==a&&m.pendingRequests.splice(a,1)}var n=k.defer(),D=n.promise,J,K,O=c.headers,R=t(c.url,c.paramSerializer(c.params));m.pendingRequests.push(c);D.then(v,v);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(J=H(c.cache)?c.cache:H(a.cache)?a.cache:A);J&&(K=J.get(R),y(K)?K&&z(K.then)?K.then(r,r):I(K)?l(K[1],
|
||||
K[0],ia(K[2]),K[3]):l(K,200,{},"OK"):J.put(R,D));q(K)&&((K=ed(c.url)?f()[c.xsrfCookieName||a.xsrfCookieName]:u)&&(O[c.xsrfHeaderName||a.xsrfHeaderName]=K),e(c.method,R,d,g,O,c.timeout,c.withCredentials,c.responseType));return D}function t(a,b){0<b.length&&(a+=(-1==a.indexOf("?")?"?":"&")+b);return a}var A=g("$http");a.paramSerializer=E(a.paramSerializer)?l.get(a.paramSerializer):a.paramSerializer;var v=[];n(c,function(a){v.unshift(E(a)?l.get(a):l.invoke(a))});m.pendingRequests=[];(function(a){n(arguments,
|
||||
function(a){m[a]=function(b,c){return m(M({},c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){n(arguments,function(a){m[a]=function(b,c,d){return m(M({},d||{},{method:a,url:b,data:c}))}})})("post","put","patch");m.defaults=a;return m}]}function gf(){this.$get=function(){return function(){return new S.XMLHttpRequest}}}function ff(){this.$get=["$browser","$window","$document","$xhrFactory",function(a,b,d,c){return Uf(a,c,a.defer,b.angular.callbacks,d[0])}]}function Uf(a,b,d,
|
||||
c,e){function f(a,b,d){var f=e.createElement("script"),m=null;f.type="text/javascript";f.src=a;f.async=!0;m=function(a){f.removeEventListener("load",m,!1);f.removeEventListener("error",m,!1);e.body.removeChild(f);f=null;var g=-1,A="unknown";a&&("load"!==a.type||c[b].called||(a={type:"error"}),A=a.type,g="error"===a.type?404:200);d&&d(g,A)};f.addEventListener("load",m,!1);f.addEventListener("error",m,!1);e.body.appendChild(f);return m}return function(e,h,k,l,m,r,t,A){function v(){C&&C();w&&w.abort()}
|
||||
function T(b,c,e,f,g){y(L)&&d.cancel(L);C=w=null;b(c,e,f,g);a.$$completeOutstandingRequest(x)}a.$$incOutstandingRequestCount();h=h||a.url();if("jsonp"==F(e)){var p="_"+(c.counter++).toString(36);c[p]=function(a){c[p].data=a;c[p].called=!0};var C=f(h.replace("JSON_CALLBACK","angular.callbacks."+p),p,function(a,b){T(l,a,c[p].data,"",b);c[p]=x})}else{var w=b(e,h);w.open(e,h,!0);n(m,function(a,b){y(a)&&w.setRequestHeader(b,a)});w.onload=function(){var a=w.statusText||"",b="response"in w?w.response:w.responseText,
|
||||
c=1223===w.status?204:w.status;0===c&&(c=b?200:"file"==wa(h).protocol?404:0);T(l,c,b,w.getAllResponseHeaders(),a)};e=function(){T(l,-1,null,null,"")};w.onerror=e;w.onabort=e;t&&(w.withCredentials=!0);if(A)try{w.responseType=A}catch(ga){if("json"!==A)throw ga;}w.send(q(k)?null:k)}if(0<r)var L=d(v,r);else r&&z(r.then)&&r.then(v)}}function af(){var a="{{",b="}}";this.startSymbol=function(b){return b?(a=b,this):a};this.endSymbol=function(a){return a?(b=a,this):b};this.$get=["$parse","$exceptionHandler",
|
||||
"$sce",function(d,c,e){function f(a){return"\\\\\\"+a}function g(c){return c.replace(m,a).replace(r,b)}function h(f,h,m,r){function p(a){try{var b=a;a=m?e.getTrusted(m,b):e.valueOf(b);var d;if(r&&!y(a))d=a;else if(null==a)d="";else{switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=db(a)}d=a}return d}catch(g){c(Ja.interr(f,g))}}r=!!r;for(var C,w,n=0,L=[],s=[],D=f.length,J=[],K=[];n<D;)if(-1!=(C=f.indexOf(a,n))&&-1!=(w=f.indexOf(b,C+k)))n!==C&&J.push(g(f.substring(n,C))),n=f.substring(C+
|
||||
k,w),L.push(n),s.push(d(n,p)),n=w+l,K.push(J.length),J.push("");else{n!==D&&J.push(g(f.substring(n)));break}m&&1<J.length&&Ja.throwNoconcat(f);if(!h||L.length){var O=function(a){for(var b=0,c=L.length;b<c;b++){if(r&&q(a[b]))return;J[K[b]]=a[b]}return J.join("")};return M(function(a){var b=0,d=L.length,e=Array(d);try{for(;b<d;b++)e[b]=s[b](a);return O(e)}catch(g){c(Ja.interr(f,g))}},{exp:f,expressions:L,$$watchDelegate:function(a,b){var c;return a.$watchGroup(s,function(d,e){var f=O(d);z(b)&&b.call(this,
|
||||
f,d!==e?c:f,a);c=f})}})}}var k=a.length,l=b.length,m=new RegExp(a.replace(/./g,f),"g"),r=new RegExp(b.replace(/./g,f),"g");h.startSymbol=function(){return a};h.endSymbol=function(){return b};return h}]}function bf(){this.$get=["$rootScope","$window","$q","$$q",function(a,b,d,c){function e(e,h,k,l){var m=4<arguments.length,r=m?ra.call(arguments,4):[],t=b.setInterval,A=b.clearInterval,v=0,n=y(l)&&!l,p=(n?c:d).defer(),C=p.promise;k=y(k)?k:0;C.then(null,null,m?function(){e.apply(null,r)}:e);C.$$intervalId=
|
||||
t(function(){p.notify(v++);0<k&&v>=k&&(p.resolve(v),A(C.$$intervalId),delete f[C.$$intervalId]);n||a.$apply()},h);f[C.$$intervalId]=p;return C}var f={};e.cancel=function(a){return a&&a.$$intervalId in f?(f[a.$$intervalId].reject("canceled"),b.clearInterval(a.$$intervalId),delete f[a.$$intervalId],!0):!1};return e}]}function bc(a){a=a.split("/");for(var b=a.length;b--;)a[b]=ob(a[b]);return a.join("/")}function fd(a,b){var d=wa(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=ea(d.port)||Vf[d.protocol]||
|
||||
null}function gd(a,b){var d="/"!==a.charAt(0);d&&(a="/"+a);var c=wa(a);b.$$path=decodeURIComponent(d&&"/"===c.pathname.charAt(0)?c.pathname.substring(1):c.pathname);b.$$search=xc(c.search);b.$$hash=decodeURIComponent(c.hash);b.$$path&&"/"!=b.$$path.charAt(0)&&(b.$$path="/"+b.$$path)}function pa(a,b){if(0===b.indexOf(a))return b.substr(a.length)}function Fa(a){var b=a.indexOf("#");return-1==b?a:a.substr(0,b)}function ib(a){return a.replace(/(#.+)|#$/,"$1")}function cc(a,b,d){this.$$html5=!0;d=d||"";
|
||||
fd(a,this);this.$$parse=function(a){var d=pa(b,a);if(!E(d))throw Db("ipthprfx",a,b);gd(d,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Qb(this.$$search),d=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=bc(this.$$path)+(a?"?"+a:"")+d;this.$$absUrl=b+this.$$url.substr(1)};this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;y(f=pa(a,c))?(g=f,g=y(f=pa(d,f))?b+(pa("/",f)||f):a+g):y(f=pa(b,c))?g=b+f:b==c+"/"&&(g=b);g&&this.$$parse(g);
|
||||
return!!g}}function dc(a,b,d){fd(a,this);this.$$parse=function(c){var e=pa(a,c)||pa(b,c),f;q(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",q(e)&&(a=c,this.replace())):(f=pa(d,e),q(f)&&(f=e));gd(f,this);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))?f[1]:c);this.$$path=c;this.$$compose()};this.$$compose=function(){var b=Qb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=bc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+(this.$$url?
|
||||
d+this.$$url:"")};this.$$parseLinkUrl=function(b,d){return Fa(a)==Fa(b)?(this.$$parse(b),!0):!1}}function hd(a,b,d){this.$$html5=!0;dc.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a==Fa(c)?f=c:(g=pa(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$compose=function(){var b=Qb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=bc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+d+this.$$url}}function Eb(a){return function(){return this[a]}}
|
||||
function id(a,b){return function(d){if(q(d))return this[a];this[a]=b(d);this.$$compose();return this}}function hf(){var a="",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return y(b)?(a=b,this):a};this.html5Mode=function(a){return $a(a)?(b.enabled=a,this):H(a)?($a(a.enabled)&&(b.enabled=a.enabled),$a(a.requireBase)&&(b.requireBase=a.requireBase),$a(a.rewriteLinks)&&(b.rewriteLinks=a.rewriteLinks),this):b};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",
|
||||
function(d,c,e,f,g){function h(a,b,d){var e=l.url(),f=l.$$state;try{c.url(a,b,d),l.$$state=c.state()}catch(g){throw l.url(e),l.$$state=f,g;}}function k(a,b){d.$broadcast("$locationChangeSuccess",l.absUrl(),a,l.$$state,b)}var l,m;m=c.baseHref();var r=c.url(),t;if(b.enabled){if(!m&&b.requireBase)throw Db("nobase");t=r.substring(0,r.indexOf("/",r.indexOf("//")+2))+(m||"/");m=e.history?cc:hd}else t=Fa(r),m=dc;var A=t.substr(0,Fa(t).lastIndexOf("/")+1);l=new m(t,A,"#"+a);l.$$parseLinkUrl(r,r);l.$$state=
|
||||
c.state();var v=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(b.rewriteLinks&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!=a.which&&2!=a.button){for(var e=B(a.target);"a"!==ta(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),k=e.attr("href")||e.attr("xlink:href");H(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=wa(h.animVal).href);v.test(h)||!h||e.attr("target")||a.isDefaultPrevented()||!l.$$parseLinkUrl(h,k)||(a.preventDefault(),l.absUrl()!=c.url()&&(d.$apply(),g.angular["ff-684208-preventDefault"]=
|
||||
!0))}});ib(l.absUrl())!=ib(r)&&c.url(l.absUrl(),!0);var n=!0;c.onUrlChange(function(a,b){q(pa(A,a))?g.location.href=a:(d.$evalAsync(function(){var c=l.absUrl(),e=l.$$state,f;a=ib(a);l.$$parse(a);l.$$state=b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;l.absUrl()===a&&(f?(l.$$parse(c),l.$$state=e,h(c,!1,e)):(n=!1,k(c,e)))}),d.$$phase||d.$digest())});d.$watch(function(){var a=ib(c.url()),b=ib(l.absUrl()),f=c.state(),g=l.$$replace,m=a!==b||l.$$html5&&e.history&&f!==l.$$state;if(n||
|
||||
m)n=!1,d.$evalAsync(function(){var b=l.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,l.$$state,f).defaultPrevented;l.absUrl()===b&&(c?(l.$$parse(a),l.$$state=f):(m&&h(b,g,f===l.$$state?null:l.$$state),k(a,f)))});l.$$replace=!1});return l}]}function jf(){var a=!0,b=this;this.debugEnabled=function(b){return y(b)?(a=b,this):a};this.$get=["$window",function(d){function c(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&
|
||||
(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=d.console||{},e=b[a]||b.log||x;a=!1;try{a=!!e.apply}catch(k){}return a?function(){var a=[];n(arguments,function(b){a.push(c(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){a&&c.apply(b,arguments)}}()}}]}function Va(a,b){if("__defineGetter__"===a||"__defineSetter__"===a||"__lookupGetter__"===a||"__lookupSetter__"===
|
||||
a||"__proto__"===a)throw ba("isecfld",b);return a}function jd(a,b){a+="";if(!E(a))throw ba("iseccst",b);return a}function xa(a,b){if(a){if(a.constructor===a)throw ba("isecfn",b);if(a.window===a)throw ba("isecwindow",b);if(a.children&&(a.nodeName||a.prop&&a.attr&&a.find))throw ba("isecdom",b);if(a===Object)throw ba("isecobj",b);}return a}function kd(a,b){if(a){if(a.constructor===a)throw ba("isecfn",b);if(a===Wf||a===Xf||a===Yf)throw ba("isecff",b);}}function ld(a,b){if(a&&(a===(0).constructor||a===
|
||||
(!1).constructor||a==="".constructor||a==={}.constructor||a===[].constructor||a===Function.constructor))throw ba("isecaf",b);}function Zf(a,b){return"undefined"!==typeof a?a:b}function md(a,b){return"undefined"===typeof a?b:"undefined"===typeof b?a:a+b}function W(a,b){var d,c;switch(a.type){case s.Program:d=!0;n(a.body,function(a){W(a.expression,b);d=d&&a.expression.constant});a.constant=d;break;case s.Literal:a.constant=!0;a.toWatch=[];break;case s.UnaryExpression:W(a.argument,b);a.constant=a.argument.constant;
|
||||
a.toWatch=a.argument.toWatch;break;case s.BinaryExpression:W(a.left,b);W(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case s.LogicalExpression:W(a.left,b);W(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case s.ConditionalExpression:W(a.test,b);W(a.alternate,b);W(a.consequent,b);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case s.Identifier:a.constant=
|
||||
!1;a.toWatch=[a];break;case s.MemberExpression:W(a.object,b);a.computed&&W(a.property,b);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=[a];break;case s.CallExpression:d=a.filter?!b(a.callee.name).$stateful:!1;c=[];n(a.arguments,function(a){W(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=a.filter&&!b(a.callee.name).$stateful?c:[a];break;case s.AssignmentExpression:W(a.left,b);W(a.right,b);a.constant=a.left.constant&&a.right.constant;
|
||||
a.toWatch=[a];break;case s.ArrayExpression:d=!0;c=[];n(a.elements,function(a){W(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=c;break;case s.ObjectExpression:d=!0;c=[];n(a.properties,function(a){W(a.value,b);d=d&&a.value.constant;a.value.constant||c.push.apply(c,a.value.toWatch)});a.constant=d;a.toWatch=c;break;case s.ThisExpression:a.constant=!1,a.toWatch=[]}}function nd(a){if(1==a.length){a=a[0].expression;var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:u}}
|
||||
function od(a){return a.type===s.Identifier||a.type===s.MemberExpression}function pd(a){if(1===a.body.length&&od(a.body[0].expression))return{type:s.AssignmentExpression,left:a.body[0].expression,right:{type:s.NGValueParameter},operator:"="}}function qd(a){return 0===a.body.length||1===a.body.length&&(a.body[0].expression.type===s.Literal||a.body[0].expression.type===s.ArrayExpression||a.body[0].expression.type===s.ObjectExpression)}function rd(a,b){this.astBuilder=a;this.$filter=b}function sd(a,
|
||||
b){this.astBuilder=a;this.$filter=b}function Fb(a){return"constructor"==a}function ec(a){return z(a.valueOf)?a.valueOf():$f.call(a)}function kf(){var a=$(),b=$();this.$get=["$filter",function(d){function c(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=ec(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function e(a,b,d,e,f){var g=e.inputs,h;if(1===g.length){var k=c,g=g[0];return a.$watch(function(a){var b=g(a);c(b,k)||(h=e(a,u,u,[b]),k=b&&ec(b));return h},b,d,f)}for(var l=[],m=[],r=0,n=
|
||||
g.length;r<n;r++)l[r]=c,m[r]=null;return a.$watch(function(a){for(var b=!1,d=0,f=g.length;d<f;d++){var k=g[d](a);if(b||(b=!c(k,l[d])))m[d]=k,l[d]=k&&ec(k)}b&&(h=e(a,u,u,m));return h},b,d,f)}function f(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;z(b)&&b.apply(this,arguments);y(a)&&d.$$postDigest(function(){y(f)&&e()})},c)}function g(a,b,c,d){function e(a){var b=!0;n(a,function(a){y(a)||(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},function(a,
|
||||
c,d){g=a;z(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function h(a,b,c,d){var e;return e=a.$watch(function(a){return d(a)},function(a,c,d){z(b)&&b.apply(this,arguments);e()},c)}function k(a,b){if(!b)return a;var c=a.$$watchDelegate,d=!1,c=c!==g&&c!==f?function(c,e,f,g){f=d&&g?g[0]:a(c,e,f,g);return b(f,c,e)}:function(c,d,e,f){e=a(c,d,e,f);c=b(e,c,d);return y(e)?c:e};a.$$watchDelegate&&a.$$watchDelegate!==e?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=
|
||||
e,d=!a.inputs,c.inputs=a.inputs?a.inputs:[a]);return c}var l=Ba().noUnsafeEval,m={csp:l,expensiveChecks:!1},r={csp:l,expensiveChecks:!0};return function(c,l,v){var n,p,q;switch(typeof c){case "string":q=c=c.trim();var w=v?b:a;n=w[q];n||(":"===c.charAt(0)&&":"===c.charAt(1)&&(p=!0,c=c.substring(2)),v=v?r:m,n=new fc(v),n=(new gc(n,d,v)).parse(c),n.constant?n.$$watchDelegate=h:p?n.$$watchDelegate=n.literal?g:f:n.inputs&&(n.$$watchDelegate=e),w[q]=n);return k(n,l);case "function":return k(c,l);default:return x}}}]}
|
||||
function mf(){this.$get=["$rootScope","$exceptionHandler",function(a,b){return td(function(b){a.$evalAsync(b)},b)}]}function nf(){this.$get=["$browser","$exceptionHandler",function(a,b){return td(function(b){a.defer(b)},b)}]}function td(a,b){function d(a,b,c){function d(b){return function(c){e||(e=!0,b.call(a,c))}}var e=!1;return[d(b),d(c)]}function c(){this.$$state={status:0}}function e(a,b){return function(c){b.call(a,c)}}function f(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,a(function(){var a,
|
||||
d,e;e=c.pending;c.processScheduled=!1;c.pending=u;for(var f=0,g=e.length;f<g;++f){d=e[f][0];a=e[f][c.status];try{z(a)?d.resolve(a(c.value)):1===c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),b(h)}}}))}function g(){this.promise=new c;this.resolve=e(this,this.resolve);this.reject=e(this,this.reject);this.notify=e(this,this.notify)}var h=G("$q",TypeError);M(c.prototype,{then:function(a,b,c){if(q(a)&&q(b)&&q(c))return this;var d=new g;this.$$state.pending=this.$$state.pending||[];
|
||||
this.$$state.pending.push([d,a,b,c]);0<this.$$state.status&&f(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return l(b,!0,a)},function(b){return l(b,!1,a)},b)}});M(g.prototype,{resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(h("qcycle",a)):this.$$resolve(a))},$$resolve:function(a){var c,e;e=d(this,this.$$resolve,this.$$reject);try{if(H(a)||z(a))c=a&&a.then;z(c)?(this.promise.$$state.status=
|
||||
-1,c.call(a,e[0],e[1],this.notify)):(this.promise.$$state.value=a,this.promise.$$state.status=1,f(this.promise.$$state))}catch(g){e[1](g),b(g)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;f(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&a(function(){for(var a,e,f=0,g=d.length;f<g;f++){e=d[f][0];a=d[f][3];try{e.notify(z(a)?
|
||||
a(c):c)}catch(h){b(h)}}})}});var k=function(a,b){var c=new g;b?c.resolve(a):c.reject(a);return c.promise},l=function(a,b,c){var d=null;try{z(c)&&(d=c())}catch(e){return k(e,!1)}return d&&z(d.then)?d.then(function(){return k(a,b)},function(a){return k(a,!1)}):k(a,b)},m=function(a,b,c,d){var e=new g;e.resolve(a);return e.promise.then(b,c,d)},r=function A(a){if(!z(a))throw h("norslvr",a);if(!(this instanceof A))return new A(a);var b=new g;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};
|
||||
r.defer=function(){return new g};r.reject=function(a){var b=new g;b.reject(a);return b.promise};r.when=m;r.resolve=m;r.all=function(a){var b=new g,c=0,d=I(a)?[]:{};n(a,function(a,e){c++;m(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return r}function wf(){this.$get=["$window","$timeout",function(a,b){var d=a.requestAnimationFrame||a.webkitRequestAnimationFrame,c=a.cancelAnimationFrame||a.webkitCancelAnimationFrame||
|
||||
a.webkitCancelRequestAnimationFrame,e=!!d,f=e?function(a){var b=d(a);return function(){c(b)}}:function(a){var c=b(a,16.66,!1);return function(){b.cancel(c)}};f.supported=e;return f}]}function lf(){function a(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++nb;this.$$ChildScope=null}b.prototype=a;return b}var b=10,d=G("$rootScope"),c=null,e=null;this.digestTtl=function(a){arguments.length&&
|
||||
(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(f,g,h,k){function l(a){a.currentScope.$$destroyed=!0}function m(a){9===Ha&&(a.$$childHead&&m(a.$$childHead),a.$$nextSibling&&m(a.$$nextSibling));a.$parent=a.$$nextSibling=a.$$prevSibling=a.$$childHead=a.$$childTail=a.$root=a.$$watchers=null}function r(){this.$id=++nb;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=this;this.$$destroyed=
|
||||
!1;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$$isolateBindings=null}function t(a){if(w.$$phase)throw d("inprog",w.$$phase);w.$$phase=a}function A(a,b){do a.$$watchersCount+=b;while(a=a.$parent)}function v(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function s(){}function p(){for(;aa.length;)try{aa.shift()()}catch(a){g(a)}e=null}function C(){null===e&&(e=k.defer(function(){w.$apply(p)}))}r.prototype={constructor:r,
|
||||
$new:function(b,c){var d;c=c||this;b?(d=new r,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=a(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(b||c!=this)&&d.$on("$destroy",l);return d},$watch:function(a,b,d,e){var f=h(a);if(f.$$watchDelegate)return f.$$watchDelegate(this,b,d,f,a);var g=this,k=g.$$watchers,l={fn:b,last:s,get:f,exp:e||a,eq:!!d};c=null;z(b)||(l.fn=x);k||
|
||||
(k=g.$$watchers=[]);k.unshift(l);A(this,1);return function(){0<=ab(k,l)&&A(g,-1);c=null}},$watchGroup:function(a,b){function c(){h=!1;k?(k=!1,b(e,e,g)):b(e,d,g)}var d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0;if(!a.length){var l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return function(){l=!1}}if(1===a.length)return this.$watch(a[0],function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});n(a,function(a,b){var k=g.$watch(a,function(a,f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},
|
||||
$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!q(e)){if(H(e))if(za(e))for(f!==r&&(f=r,n=f.length=0,l++),a=e.length,n!==a&&(l++,f.length=n=a),b=0;b<a;b++)h=f[b],g=e[b],d=h!==h&&g!==g,d||h===g||(l++,f[b]=g);else{f!==t&&(f=t={},n=0,l++);a=0;for(b in e)qa.call(e,b)&&(a++,g=e[b],h=f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(n++,f[b]=g,l++));if(n>a)for(b in l++,f)qa.call(e,b)||(n--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,g,k=1<b.length,l=0,m=
|
||||
h(a,c),r=[],t={},p=!0,n=0;return this.$watch(m,function(){p?(p=!1,b(e,e,d)):b(e,g,d);if(k)if(H(e))if(za(e)){g=Array(e.length);for(var a=0;a<e.length;a++)g[a]=e[a]}else for(a in g={},e)qa.call(e,a)&&(g[a]=e[a]);else g=e})},$digest:function(){var a,f,h,l,m,r,n=b,A,q=[],v,C;t("$digest");k.$$checkUrlChange();this===w&&null!==e&&(k.defer.cancel(e),p());c=null;do{r=!1;for(A=this;u.length;){try{C=u.shift(),C.scope.$eval(C.expression,C.locals)}catch(aa){g(aa)}c=null}a:do{if(l=A.$$watchers)for(m=l.length;m--;)try{if(a=
|
||||
l[m])if((f=a.get(A))!==(h=a.last)&&!(a.eq?ma(f,h):"number"===typeof f&&"number"===typeof h&&isNaN(f)&&isNaN(h)))r=!0,c=a,a.last=a.eq?bb(f,null):f,a.fn(f,h===s?f:h,A),5>n&&(v=4-n,q[v]||(q[v]=[]),q[v].push({msg:z(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:f,oldVal:h}));else if(a===c){r=!1;break a}}catch(y){g(y)}if(!(l=A.$$watchersCount&&A.$$childHead||A!==this&&A.$$nextSibling))for(;A!==this&&!(l=A.$$nextSibling);)A=A.$parent}while(A=l);if((r||u.length)&&!n--)throw w.$$phase=null,d("infdig",
|
||||
b,q);}while(r||u.length);for(w.$$phase=null;L.length;)try{L.shift()()}catch(x){g(x)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===w&&k.$$applicationDestroyed();A(this,-this.$$watchersCount);for(var b in this.$$listenerCount)v(this,this.$$listenerCount[b],b);a&&a.$$childHead==this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=
|
||||
this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=x;this.$on=this.$watch=this.$watchGroup=function(){return x};this.$$listeners={};this.$$nextSibling=null;m(this)}},$eval:function(a,b){return h(a)(this,b)},$evalAsync:function(a,b){w.$$phase||u.length||k.defer(function(){u.length&&w.$digest()});u.push({scope:this,expression:a,locals:b})},$$postDigest:function(a){L.push(a)},$apply:function(a){try{t("$apply");
|
||||
try{return this.$eval(a)}finally{w.$$phase=null}}catch(b){g(b)}finally{try{w.$digest()}catch(c){throw g(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&aa.push(b);C()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,v(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,f=!1,h=
|
||||
{name:a,targetScope:e,stopPropagation:function(){f=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=cb([h],arguments,1),l,m;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(m=d.length;l<m;l++)if(d[l])try{d[l].apply(null,k)}catch(r){g(r)}else d.splice(l,1),l--,m--;if(f)return h.currentScope=null,h;e=e.$parent}while(e);h.currentScope=null;return h},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};
|
||||
if(!this.$$listenerCount[a])return e;for(var f=cb([e],arguments,1),h,k;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,f)}catch(l){g(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}e.currentScope=null;return e}};var w=new r,u=w.$$asyncQueue=[],L=w.$$postDigestQueue=[],aa=w.$$applyAsyncQueue=[];return w}]}function ge(){var a=/^\s*(https?|ftp|mailto|tel|file):/,
|
||||
b=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(b){return y(b)?(a=b,this):a};this.imgSrcSanitizationWhitelist=function(a){return y(a)?(b=a,this):b};this.$get=function(){return function(d,c){var e=c?b:a,f;f=wa(d).href;return""===f||f.match(e)?d:"unsafe:"+f}}}function ag(a){if("self"===a)return a;if(E(a)){if(-1<a.indexOf("***"))throw ya("iwcard",a);a=ud(a).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+a+"$")}if(Ma(a))return new RegExp("^"+
|
||||
a.source+"$");throw ya("imatcher");}function vd(a){var b=[];y(a)&&n(a,function(a){b.push(ag(a))});return b}function pf(){this.SCE_CONTEXTS=la;var a=["self"],b=[];this.resourceUrlWhitelist=function(b){arguments.length&&(a=vd(b));return a};this.resourceUrlBlacklist=function(a){arguments.length&&(b=vd(a));return b};this.$get=["$injector",function(d){function c(a,b){return"self"===a?ed(b):!!a.exec(b.href)}function e(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=
|
||||
new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var f=function(a){throw ya("unsafe");};d.has("$sanitize")&&(f=d.get("$sanitize"));var g=e(),h={};h[la.HTML]=e(g);h[la.CSS]=e(g);h[la.URL]=e(g);h[la.JS]=e(g);h[la.RESOURCE_URL]=e(h[la.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw ya("icontext",a,b);if(null===b||q(b)||""===b)return b;if("string"!==typeof b)throw ya("itype",
|
||||
a);return new c(b)},getTrusted:function(d,e){if(null===e||q(e)||""===e)return e;var g=h.hasOwnProperty(d)?h[d]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(d===la.RESOURCE_URL){var g=wa(e.toString()),r,t,n=!1;r=0;for(t=a.length;r<t;r++)if(c(a[r],g)){n=!0;break}if(n)for(r=0,t=b.length;r<t;r++)if(c(b[r],g)){n=!1;break}if(n)return e;throw ya("insecurl",e.toString());}if(d===la.HTML)return f(e);throw ya("unsafe");},valueOf:function(a){return a instanceof g?a.$$unwrapTrustedValue():a}}}]}
|
||||
function of(){var a=!0;this.enabled=function(b){arguments.length&&(a=!!b);return a};this.$get=["$parse","$sceDelegate",function(b,d){if(a&&8>Ha)throw ya("iequirks");var c=ia(la);c.isEnabled=function(){return a};c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b},c.valueOf=Ya);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,f=c.getTrusted,g=c.trustAs;n(la,function(a,
|
||||
b){var d=F(b);c[fb("parse_as_"+d)]=function(b){return e(a,b)};c[fb("get_trusted_"+d)]=function(b){return f(a,b)};c[fb("trust_as_"+d)]=function(b){return g(a,b)}});return c}]}function qf(){this.$get=["$window","$document",function(a,b){var d={},c=ea((/android (\d+)/.exec(F((a.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((a.navigator||{}).userAgent),f=b[0]||{},g,h=/^(Moz|webkit|ms)(?=[A-Z])/,k=f.body&&f.body.style,l=!1,m=!1;if(k){for(var r in k)if(l=h.exec(r)){g=l[0];g=g.substr(0,1).toUpperCase()+
|
||||
g.substr(1);break}g||(g="WebkitOpacity"in k&&"webkit");l=!!("transition"in k||g+"Transition"in k);m=!!("animation"in k||g+"Animation"in k);!c||l&&m||(l=E(k.webkitTransition),m=E(k.webkitAnimation))}return{history:!(!a.history||!a.history.pushState||4>c||e),hasEvent:function(a){if("input"===a&&11>=Ha)return!1;if(q(d[a])){var b=f.createElement("div");d[a]="on"+a in b}return d[a]},csp:Ba(),vendorPrefix:g,transitions:l,animations:m,android:c}}]}function sf(){this.$get=["$templateCache","$http","$q","$sce",
|
||||
function(a,b,d,c){function e(f,g){e.totalPendingRequests++;E(f)&&a.get(f)||(f=c.getTrustedResourceUrl(f));var h=b.defaults&&b.defaults.transformResponse;I(h)?h=h.filter(function(a){return a!==$b}):h===$b&&(h=null);return b.get(f,{cache:a,transformResponse:h})["finally"](function(){e.totalPendingRequests--}).then(function(b){a.put(f,b.data);return b.data},function(a){if(!g)throw ha("tpload",f,a.status,a.statusText);return d.reject(a)})}e.totalPendingRequests=0;return e}]}function tf(){this.$get=["$rootScope",
|
||||
"$browser","$location",function(a,b,d){return{findBindings:function(a,b,d){a=a.getElementsByClassName("ng-binding");var g=[];n(a,function(a){var c=fa.element(a).data("$binding");c&&n(c,function(c){d?(new RegExp("(^|\\s)"+ud(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!=c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],h=0;h<g.length;++h){var k=a.querySelectorAll("["+g[h]+"model"+(d?"=":"*=")+'"'+b+'"]');if(k.length)return k}},getLocation:function(){return d.url()},
|
||||
setLocation:function(b){b!==d.url()&&(d.url(b),a.$digest())},whenStable:function(a){b.notifyWhenNoOutstandingRequests(a)}}}]}function uf(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",function(a,b,d,c,e){function f(f,k,l){z(f)||(l=k,k=f,f=x);var m=ra.call(arguments,3),r=y(l)&&!l,t=(r?c:d).defer(),n=t.promise,q;q=b.defer(function(){try{t.resolve(f.apply(null,m))}catch(b){t.reject(b),e(b)}finally{delete g[n.$$timeoutId]}r||a.$apply()},k);n.$$timeoutId=q;g[q]=t;return n}var g={};
|
||||
f.cancel=function(a){return a&&a.$$timeoutId in g?(g[a.$$timeoutId].reject("canceled"),delete g[a.$$timeoutId],b.defer.cancel(a.$$timeoutId)):!1};return f}]}function wa(a){Ha&&(Y.setAttribute("href",a),a=Y.href);Y.setAttribute("href",a);return{href:Y.href,protocol:Y.protocol?Y.protocol.replace(/:$/,""):"",host:Y.host,search:Y.search?Y.search.replace(/^\?/,""):"",hash:Y.hash?Y.hash.replace(/^#/,""):"",hostname:Y.hostname,port:Y.port,pathname:"/"===Y.pathname.charAt(0)?Y.pathname:"/"+Y.pathname}}function ed(a){a=
|
||||
E(a)?wa(a):a;return a.protocol===wd.protocol&&a.host===wd.host}function vf(){this.$get=na(S)}function xd(a){function b(a){try{return decodeURIComponent(a)}catch(b){return a}}var d=a[0]||{},c={},e="";return function(){var a,g,h,k,l;a=d.cookie||"";if(a!==e)for(e=a,a=e.split("; "),c={},h=0;h<a.length;h++)g=a[h],k=g.indexOf("="),0<k&&(l=b(g.substring(0,k)),q(c[l])&&(c[l]=b(g.substring(k+1))));return c}}function zf(){this.$get=xd}function Jc(a){function b(d,c){if(H(d)){var e={};n(d,function(a,c){e[c]=
|
||||
b(c,a)});return e}return a.factory(d+"Filter",c)}this.register=b;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];b("currency",yd);b("date",zd);b("filter",bg);b("json",cg);b("limitTo",dg);b("lowercase",eg);b("number",Ad);b("orderBy",Bd);b("uppercase",fg)}function bg(){return function(a,b,d){if(!za(a)){if(null==a)return a;throw G("filter")("notarray",a);}var c;switch(hc(b)){case "function":break;case "boolean":case "null":case "number":case "string":c=!0;case "object":b=
|
||||
gg(b,d,c);break;default:return a}return Array.prototype.filter.call(a,b)}}function gg(a,b,d){var c=H(a)&&"$"in a;!0===b?b=ma:z(b)||(b=function(a,b){if(q(a))return!1;if(null===a||null===b)return a===b;if(H(b)||H(a)&&!qc(a))return!1;a=F(""+a);b=F(""+b);return-1!==a.indexOf(b)});return function(e){return c&&!H(e)?Ka(e,a.$,b,!1):Ka(e,a,b,d)}}function Ka(a,b,d,c,e){var f=hc(a),g=hc(b);if("string"===g&&"!"===b.charAt(0))return!Ka(a,b.substring(1),d,c);if(I(a))return a.some(function(a){return Ka(a,b,d,c)});
|
||||
switch(f){case "object":var h;if(c){for(h in a)if("$"!==h.charAt(0)&&Ka(a[h],b,d,!0))return!0;return e?!1:Ka(a,b,d,!1)}if("object"===g){for(h in b)if(e=b[h],!z(e)&&!q(e)&&(f="$"===h,!Ka(f?a:a[h],e,d,f,f)))return!1;return!0}return d(a,b);case "function":return!1;default:return d(a,b)}}function hc(a){return null===a?"null":typeof a}function yd(a){var b=a.NUMBER_FORMATS;return function(a,c,e){q(c)&&(c=b.CURRENCY_SYM);q(e)&&(e=b.PATTERNS[1].maxFrac);return null==a?a:Cd(a,b.PATTERNS[1],b.GROUP_SEP,b.DECIMAL_SEP,
|
||||
e).replace(/\u00A4/g,c)}}function Ad(a){var b=a.NUMBER_FORMATS;return function(a,c){return null==a?a:Cd(a,b.PATTERNS[0],b.GROUP_SEP,b.DECIMAL_SEP,c)}}function Cd(a,b,d,c,e){if(H(a))return"";var f=0>a;a=Math.abs(a);var g=Infinity===a;if(!g&&!isFinite(a))return"";var h=a+"",k="",l=!1,m=[];g&&(k="\u221e");if(!g&&-1!==h.indexOf("e")){var r=h.match(/([\d\.]+)e(-?)(\d+)/);r&&"-"==r[2]&&r[3]>e+1?a=0:(k=h,l=!0)}if(g||l)0<e&&1>a&&(k=a.toFixed(e),a=parseFloat(k),k=k.replace(ic,c));else{g=(h.split(ic)[1]||"").length;
|
||||
q(e)&&(e=Math.min(Math.max(b.minFrac,g),b.maxFrac));a=+(Math.round(+(a.toString()+"e"+e)).toString()+"e"+-e);var g=(""+a).split(ic),h=g[0],g=g[1]||"",r=0,t=b.lgSize,n=b.gSize;if(h.length>=t+n)for(r=h.length-t,l=0;l<r;l++)0===(r-l)%n&&0!==l&&(k+=d),k+=h.charAt(l);for(l=r;l<h.length;l++)0===(h.length-l)%t&&0!==l&&(k+=d),k+=h.charAt(l);for(;g.length<e;)g+="0";e&&"0"!==e&&(k+=c+g.substr(0,e))}0===a&&(f=!1);m.push(f?b.negPre:b.posPre,k,f?b.negSuf:b.posSuf);return m.join("")}function Gb(a,b,d){var c="";
|
||||
0>a&&(c="-",a=-a);for(a=""+a;a.length<b;)a="0"+a;d&&(a=a.substr(a.length-b));return c+a}function ca(a,b,d,c){d=d||0;return function(e){e=e["get"+a]();if(0<d||e>-d)e+=d;0===e&&-12==d&&(e=12);return Gb(e,b,c)}}function Hb(a,b){return function(d,c){var e=d["get"+a](),f=sb(b?"SHORT"+a:a);return c[f][e]}}function Dd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Ed(a){return function(b){var d=Dd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-
|
||||
+d;b=1+Math.round(b/6048E5);return Gb(b,a)}}function jc(a,b){return 0>=a.getFullYear()?b.ERAS[0]:b.ERAS[1]}function zd(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,k=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=ea(b[9]+b[10]),g=ea(b[9]+b[11]));h.call(a,ea(b[1]),ea(b[2])-1,ea(b[3]));f=ea(b[4]||0)-f;g=ea(b[5]||0)-g;h=ea(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));k.call(a,f,g,h,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
|
||||
return function(c,d,f){var g="",h=[],k,l;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;E(c)&&(c=hg.test(c)?ea(c):b(c));Q(c)&&(c=new Date(c));if(!da(c)||!isFinite(c.getTime()))return c;for(;d;)(l=ig.exec(d))?(h=cb(h,l,1),d=h.pop()):(h.push(d),d=null);var m=c.getTimezoneOffset();f&&(m=vc(f,c.getTimezoneOffset()),c=Pb(c,f,!0));n(h,function(b){k=jg[b];g+=k?k(c,a.DATETIME_FORMATS,m):b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function cg(){return function(a,b){q(b)&&(b=2);return db(a,b)}}function dg(){return function(a,
|
||||
b,d){b=Infinity===Math.abs(Number(b))?Number(b):ea(b);if(isNaN(b))return a;Q(a)&&(a=a.toString());if(!I(a)&&!E(a))return a;d=!d||isNaN(d)?0:ea(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?a.slice(d,d+b):0===d?a.slice(b,a.length):a.slice(Math.max(0,d+b),d)}}function Bd(a){function b(b,d){d=d?-1:1;return b.map(function(b){var c=1,h=Ya;if(z(b))h=b;else if(E(b)){if("+"==b.charAt(0)||"-"==b.charAt(0))c="-"==b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(h=a(b),h.constant))var k=h(),h=function(a){return a[k]}}return{get:h,
|
||||
descending:c*d}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}return function(a,e,f){if(!za(a))return a;I(e)||(e=[e]);0===e.length&&(e=["+"]);var g=b(e,f);g.push({get:function(){return{}},descending:f?-1:1});a=Array.prototype.map.call(a,function(a,b){return{value:a,predicateValues:g.map(function(c){var e=c.get(a);c=typeof e;if(null===e)c="string",e="null";else if("string"===c)e=e.toLowerCase();else if("object"===c)a:{if("function"===typeof e.valueOf&&
|
||||
(e=e.valueOf(),d(e)))break a;if(qc(e)&&(e=e.toString(),d(e)))break a;e=b}return{value:e,type:c}})}});a.sort(function(a,b){for(var c=0,d=0,e=g.length;d<e;++d){var c=a.predicateValues[d],f=b.predicateValues[d],n=0;c.type===f.type?c.value!==f.value&&(n=c.value<f.value?-1:1):n=c.type<f.type?-1:1;if(c=n*g[d].descending)break}return c});return a=a.map(function(a){return a.value})}}function La(a){z(a)&&(a={link:a});a.restrict=a.restrict||"AC";return na(a)}function Fd(a,b,d,c,e){var f=this,g=[];f.$error=
|
||||
{};f.$$success={};f.$pending=u;f.$name=e(b.name||b.ngForm||"")(d);f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;f.$submitted=!1;f.$$parentForm=Ib;f.$rollbackViewValue=function(){n(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){n(g,function(a){a.$commitViewValue()})};f.$addControl=function(a){Ra(a.$name,"input");g.push(a);a.$name&&(f[a.$name]=a);a.$$parentForm=f};f.$$renameControl=function(a,b){var c=a.$name;f[c]===a&&delete f[c];f[b]=a;a.$name=b};f.$removeControl=function(a){a.$name&&
|
||||
f[a.$name]===a&&delete f[a.$name];n(f.$pending,function(b,c){f.$setValidity(c,null,a)});n(f.$error,function(b,c){f.$setValidity(c,null,a)});n(f.$$success,function(b,c){f.$setValidity(c,null,a)});ab(g,a);a.$$parentForm=Ib};Gd({ctrl:this,$element:a,set:function(a,b,c){var d=a[b];d?-1===d.indexOf(c)&&d.push(c):a[b]=[c]},unset:function(a,b,c){var d=a[b];d&&(ab(d,c),0===d.length&&delete a[b])},$animate:c});f.$setDirty=function(){c.removeClass(a,Wa);c.addClass(a,Jb);f.$dirty=!0;f.$pristine=!1;f.$$parentForm.$setDirty()};
|
||||
f.$setPristine=function(){c.setClass(a,Wa,Jb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;n(g,function(a){a.$setPristine()})};f.$setUntouched=function(){n(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){c.addClass(a,"ng-submitted");f.$submitted=!0;f.$$parentForm.$setSubmitted()}}function kc(a){a.$formatters.push(function(b){return a.$isEmpty(b)?b:b.toString()})}function jb(a,b,d,c,e,f){var g=F(b[0].type);if(!e.android){var h=!1;b.on("compositionstart",function(a){h=!0});
|
||||
b.on("compositionend",function(){h=!1;k()})}var k=function(a){l&&(f.defer.cancel(l),l=null);if(!h){var e=b.val();a=a&&a.type;"password"===g||d.ngTrim&&"false"===d.ngTrim||(e=U(e));(c.$viewValue!==e||""===e&&c.$$hasNativeValidators)&&c.$setViewValue(e,a)}};if(e.hasEvent("input"))b.on("input",k);else{var l,m=function(a,b,c){l||(l=f.defer(function(){l=null;b&&b.value===c||k(a)}))};b.on("keydown",function(a){var b=a.keyCode;91===b||15<b&&19>b||37<=b&&40>=b||m(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut",
|
||||
m)}b.on("change",k);c.$render=function(){var a=c.$isEmpty(c.$viewValue)?"":c.$viewValue;b.val()!==a&&b.val(a)}}function Kb(a,b){return function(d,c){var e,f;if(da(d))return d;if(E(d)){'"'==d.charAt(0)&&'"'==d.charAt(d.length-1)&&(d=d.substring(1,d.length-1));if(kg.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(),ss:c.getSeconds(),sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,
|
||||
mm:0,ss:0,sss:0},n(e,function(a,c){c<b.length&&(f[b[c]]=+a)}),new Date(f.yyyy,f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0)}return NaN}}function kb(a,b,d,c){return function(e,f,g,h,k,l,m){function r(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function n(a){return y(a)&&!da(a)?d(a)||u:a}Hd(e,f,g,h);jb(e,f,g,h,k,l);var A=h&&h.$options&&h.$options.timezone,v;h.$$parserName=a;h.$parsers.push(function(a){return h.$isEmpty(a)?null:b.test(a)?(a=d(a,v),A&&(a=Pb(a,A)),a):u});h.$formatters.push(function(a){if(a&&
|
||||
!da(a))throw lb("datefmt",a);if(r(a))return(v=a)&&A&&(v=Pb(v,A,!0)),m("date")(a,c,A);v=null;return""});if(y(g.min)||g.ngMin){var s;h.$validators.min=function(a){return!r(a)||q(s)||d(a)>=s};g.$observe("min",function(a){s=n(a);h.$validate()})}if(y(g.max)||g.ngMax){var p;h.$validators.max=function(a){return!r(a)||q(p)||d(a)<=p};g.$observe("max",function(a){p=n(a);h.$validate()})}}}function Hd(a,b,d,c){(c.$$hasNativeValidators=H(b[0].validity))&&c.$parsers.push(function(a){var c=b.prop("validity")||{};
|
||||
return c.badInput&&!c.typeMismatch?u:a})}function Id(a,b,d,c,e){if(y(c)){a=a(c);if(!a.constant)throw lb("constexpr",d,c);return a(b)}return e}function lc(a,b){a="ngClass"+a;return["$animate",function(d){function c(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],m=0;m<b.length;m++)if(e==b[m])continue a;c.push(e)}return c}function e(a){var b=[];return I(a)?(n(a,function(a){b=b.concat(e(a))}),b):E(a)?a.split(" "):H(a)?(n(a,function(a,c){a&&(b=b.concat(c.split(" ")))}),b):a}return{restrict:"AC",
|
||||
link:function(f,g,h){function k(a,b){var c=g.data("$classCounts")||$(),d=[];n(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts",c);return d.join(" ")}function l(a){if(!0===b||f.$index%2===b){var l=e(a||[]);if(!m){var n=k(l,1);h.$addClass(n)}else if(!ma(a,m)){var q=e(m),n=c(l,q),l=c(q,l),n=k(n,1),l=k(l,-1);n&&n.length&&d.addClass(g,n);l&&l.length&&d.removeClass(g,l)}}m=ia(a)}var m;f.$watch(h[a],l,!0);h.$observe("class",function(b){l(f.$eval(h[a]))});"ngClass"!==
|
||||
a&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var l=e(f.$eval(h[a]));g===b?(g=k(l,1),h.$addClass(g)):(g=k(l,-1),h.$removeClass(g))}})}}}]}function Gd(a){function b(a,b){b&&!f[a]?(k.addClass(e,a),f[a]=!0):!b&&f[a]&&(k.removeClass(e,a),f[a]=!1)}function d(a,c){a=a?"-"+zc(a,"-"):"";b(mb+a,!0===c);b(Jd+a,!1===c)}var c=a.ctrl,e=a.$element,f={},g=a.set,h=a.unset,k=a.$animate;f[Jd]=!(f[mb]=e.hasClass(mb));c.$setValidity=function(a,e,f){q(e)?(c.$pending||(c.$pending={}),g(c.$pending,a,f)):(c.$pending&&
|
||||
h(c.$pending,a,f),Kd(c.$pending)&&(c.$pending=u));$a(e)?e?(h(c.$error,a,f),g(c.$$success,a,f)):(g(c.$error,a,f),h(c.$$success,a,f)):(h(c.$error,a,f),h(c.$$success,a,f));c.$pending?(b(Ld,!0),c.$valid=c.$invalid=u,d("",null)):(b(Ld,!1),c.$valid=Kd(c.$error),c.$invalid=!c.$valid,d("",c.$valid));e=c.$pending&&c.$pending[a]?u:c.$error[a]?!1:c.$$success[a]?!0:null;d(a,e);c.$$parentForm.$setValidity(a,e,c)}}function Kd(a){if(a)for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}var lg=/^\/(.+)\/([a-z]*)$/,
|
||||
F=function(a){return E(a)?a.toLowerCase():a},qa=Object.prototype.hasOwnProperty,sb=function(a){return E(a)?a.toUpperCase():a},Ha,B,oa,ra=[].slice,Pf=[].splice,mg=[].push,sa=Object.prototype.toString,rc=Object.getPrototypeOf,Aa=G("ng"),fa=S.angular||(S.angular={}),Sb,nb=0;Ha=X.documentMode;x.$inject=[];Ya.$inject=[];var I=Array.isArray,Vd=/^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/,U=function(a){return E(a)?a.trim():a},ud=function(a){return a.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,
|
||||
"\\$1").replace(/\x08/g,"\\x08")},Ba=function(){if(!y(Ba.rules)){var a=X.querySelector("[ng-csp]")||X.querySelector("[data-ng-csp]");if(a){var b=a.getAttribute("ng-csp")||a.getAttribute("data-ng-csp");Ba.rules={noUnsafeEval:!b||-1!==b.indexOf("no-unsafe-eval"),noInlineStyle:!b||-1!==b.indexOf("no-inline-style")}}else{a=Ba;try{new Function(""),b=!1}catch(d){b=!0}a.rules={noUnsafeEval:b,noInlineStyle:!1}}}return Ba.rules},pb=function(){if(y(pb.name_))return pb.name_;var a,b,d=Oa.length,c,e;for(b=0;b<
|
||||
d;++b)if(c=Oa[b],a=X.querySelector("["+c.replace(":","\\:")+"jq]")){e=a.getAttribute(c+"jq");break}return pb.name_=e},Oa=["ng-","data-ng-","ng:","x-ng-"],be=/[A-Z]/g,Ac=!1,Rb,Na=3,fe={full:"1.4.8",major:1,minor:4,dot:8,codeName:"ice-manipulation"};N.expando="ng339";var gb=N.cache={},Ff=1;N._data=function(a){return this.cache[a[this.expando]]||{}};var Af=/([\:\-\_]+(.))/g,Bf=/^moz([A-Z])/,xb={mouseleave:"mouseout",mouseenter:"mouseover"},Ub=G("jqLite"),Ef=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Tb=/<|&#?\w+;/,
|
||||
Cf=/<([\w:-]+)/,Df=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,ka={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ka.optgroup=ka.option;ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead;ka.th=ka.td;var Kf=Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&
|
||||
16)},Pa=N.prototype={ready:function(a){function b(){d||(d=!0,a())}var d=!1;"complete"===X.readyState?setTimeout(b):(this.on("DOMContentLoaded",b),N(S).on("load",b))},toString:function(){var a=[];n(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?B(this[a]):B(this[this.length+a])},length:0,push:mg,sort:[].sort,splice:[].splice},Cb={};n("multiple selected checked disabled readOnly required open".split(" "),function(a){Cb[F(a)]=a});var Rc={};n("input select option textarea button form details".split(" "),
|
||||
function(a){Rc[a]=!0});var Zc={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};n({data:Wb,removeData:vb,hasData:function(a){for(var b in gb[a.ng339])return!0;return!1}},function(a,b){N[b]=a});n({data:Wb,inheritedData:Bb,scope:function(a){return B.data(a,"$scope")||Bb(a.parentNode||a,["$isolateScope","$scope"])},isolateScope:function(a){return B.data(a,"$isolateScope")||B.data(a,"$isolateScopeNoTemplate")},controller:Oc,injector:function(a){return Bb(a,
|
||||
"$injector")},removeAttr:function(a,b){a.removeAttribute(b)},hasClass:yb,css:function(a,b,d){b=fb(b);if(y(d))a.style[b]=d;else return a.style[b]},attr:function(a,b,d){var c=a.nodeType;if(c!==Na&&2!==c&&8!==c)if(c=F(b),Cb[c])if(y(d))d?(a[b]=!0,a.setAttribute(b,c)):(a[b]=!1,a.removeAttribute(c));else return a[b]||(a.attributes.getNamedItem(b)||x).specified?c:u;else if(y(d))a.setAttribute(b,d);else if(a.getAttribute)return a=a.getAttribute(b,2),null===a?u:a},prop:function(a,b,d){if(y(d))a[b]=d;else return a[b]},
|
||||
text:function(){function a(a,d){if(q(d)){var c=a.nodeType;return 1===c||c===Na?a.textContent:""}a.textContent=d}a.$dv="";return a}(),val:function(a,b){if(q(b)){if(a.multiple&&"select"===ta(a)){var d=[];n(a.options,function(a){a.selected&&d.push(a.value||a.text)});return 0===d.length?null:d}return a.value}a.value=b},html:function(a,b){if(q(b))return a.innerHTML;ub(a,!0);a.innerHTML=b},empty:Pc},function(a,b){N.prototype[b]=function(b,c){var e,f,g=this.length;if(a!==Pc&&q(2==a.length&&a!==yb&&a!==Oc?
|
||||
b:c)){if(H(b)){for(e=0;e<g;e++)if(a===Wb)a(this[e],b);else for(f in b)a(this[e],f,b[f]);return this}e=a.$dv;g=q(e)?Math.min(g,1):g;for(f=0;f<g;f++){var h=a(this[f],b,c);e=e?e+h:h}return e}for(e=0;e<g;e++)a(this[e],b,c);return this}});n({removeData:vb,on:function(a,b,d,c){if(y(c))throw Ub("onargs");if(Kc(a)){c=wb(a,!0);var e=c.events,f=c.handle;f||(f=c.handle=Hf(a,e));c=0<=b.indexOf(" ")?b.split(" "):[b];for(var g=c.length,h=function(b,c,g){var h=e[b];h||(h=e[b]=[],h.specialHandlerWrapper=c,"$destroy"===
|
||||
b||g||a.addEventListener(b,f,!1));h.push(d)};g--;)b=c[g],xb[b]?(h(xb[b],Jf),h(b,u,!0)):h(b)}},off:Nc,one:function(a,b,d){a=B(a);a.on(b,function e(){a.off(b,d);a.off(b,e)});a.on(b,d)},replaceWith:function(a,b){var d,c=a.parentNode;ub(a);n(new N(b),function(b){d?c.insertBefore(b,d.nextSibling):c.replaceChild(b,a);d=b})},children:function(a){var b=[];n(a.childNodes,function(a){1===a.nodeType&&b.push(a)});return b},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,b){var d=
|
||||
a.nodeType;if(1===d||11===d){b=new N(b);for(var d=0,c=b.length;d<c;d++)a.appendChild(b[d])}},prepend:function(a,b){if(1===a.nodeType){var d=a.firstChild;n(new N(b),function(b){a.insertBefore(b,d)})}},wrap:function(a,b){b=B(b).eq(0).clone()[0];var d=a.parentNode;d&&d.replaceChild(b,a);b.appendChild(a)},remove:Xb,detach:function(a){Xb(a,!0)},after:function(a,b){var d=a,c=a.parentNode;b=new N(b);for(var e=0,f=b.length;e<f;e++){var g=b[e];c.insertBefore(g,d.nextSibling);d=g}},addClass:Ab,removeClass:zb,
|
||||
toggleClass:function(a,b,d){b&&n(b.split(" "),function(b){var e=d;q(e)&&(e=!yb(a,b));(e?Ab:zb)(a,b)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,b){return a.getElementsByTagName?a.getElementsByTagName(b):[]},clone:Vb,triggerHandler:function(a,b,d){var c,e,f=b.type||b,g=wb(a);if(g=(g=g&&g.events)&&g[f])c={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},
|
||||
stopImmediatePropagation:function(){this.immediatePropagationStopped=!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:x,type:f,target:a},b.type&&(c=M(c,b)),b=ia(g),e=d?[c].concat(d):[c],n(b,function(b){c.isImmediatePropagationStopped()||b.apply(a,e)})}},function(a,b){N.prototype[b]=function(b,c,e){for(var f,g=0,h=this.length;g<h;g++)q(f)?(f=a(this[g],b,c,e),y(f)&&(f=B(f))):Mc(f,a(this[g],b,c,e));return y(f)?f:this};N.prototype.bind=N.prototype.on;
|
||||
N.prototype.unbind=N.prototype.off});Sa.prototype={put:function(a,b){this[Ca(a,this.nextUid)]=b},get:function(a){return this[Ca(a,this.nextUid)]},remove:function(a){var b=this[a=Ca(a,this.nextUid)];delete this[a];return b}};var yf=[function(){this.$get=[function(){return Sa}]}],Tc=/^[^\(]*\(\s*([^\)]*)\)/m,ng=/,/,og=/^\s*(_?)(\S+?)\1\s*$/,Sc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Da=G("$injector");eb.$$annotate=function(a,b,d){var c;if("function"===typeof a){if(!(c=a.$inject)){c=[];if(a.length){if(b)throw E(d)&&
|
||||
d||(d=a.name||Lf(a)),Da("strictdi",d);b=a.toString().replace(Sc,"");b=b.match(Tc);n(b[1].split(ng),function(a){a.replace(og,function(a,b,d){c.push(d)})})}a.$inject=c}}else I(a)?(b=a.length-1,Qa(a[b],"fn"),c=a.slice(0,b)):Qa(a,"fn",!0);return c};var Md=G("$animate"),Ue=function(){this.$get=["$q","$$rAF",function(a,b){function d(){}d.all=x;d.chain=x;d.prototype={end:x,cancel:x,resume:x,pause:x,complete:x,then:function(c,d){return a(function(a){b(function(){a()})}).then(c,d)}};return d}]},Te=function(){var a=
|
||||
new Sa,b=[];this.$get=["$$AnimateRunner","$rootScope",function(d,c){function e(a,b,c){var d=!1;b&&(b=E(b)?b.split(" "):I(b)?b:[],n(b,function(b){b&&(d=!0,a[b]=c)}));return d}function f(){n(b,function(b){var c=a.get(b);if(c){var d=Mf(b.attr("class")),e="",f="";n(c,function(a,b){a!==!!d[b]&&(a?e+=(e.length?" ":"")+b:f+=(f.length?" ":"")+b)});n(b,function(a){e&&Ab(a,e);f&&zb(a,f)});a.remove(b)}});b.length=0}return{enabled:x,on:x,off:x,pin:x,push:function(g,h,k,l){l&&l();k=k||{};k.from&&g.css(k.from);
|
||||
k.to&&g.css(k.to);if(k.addClass||k.removeClass)if(h=k.addClass,l=k.removeClass,k=a.get(g)||{},h=e(k,h,!0),l=e(k,l,!1),h||l)a.put(g,k),b.push(g),1===b.length&&c.$$postDigest(f);return new d}}}]},Re=["$provide",function(a){var b=this;this.$$registeredAnimations=Object.create(null);this.register=function(d,c){if(d&&"."!==d.charAt(0))throw Md("notcsel",d);var e=d+"-animation";b.$$registeredAnimations[d.substr(1)]=e;a.factory(e,c)};this.classNameFilter=function(a){if(1===arguments.length&&(this.$$classNameFilter=
|
||||
a instanceof RegExp?a:null)&&/(\s+|\/)ng-animate(\s+|\/)/.test(this.$$classNameFilter.toString()))throw Md("nongcls","ng-animate");return this.$$classNameFilter};this.$get=["$$animateQueue",function(a){function b(a,c,d){if(d){var h;a:{for(h=0;h<d.length;h++){var k=d[h];if(1===k.nodeType){h=k;break a}}h=void 0}!h||h.parentNode||h.previousElementSibling||(d=null)}d?d.after(a):c.prepend(a)}return{on:a.on,off:a.off,pin:a.pin,enabled:a.enabled,cancel:function(a){a.end&&a.end()},enter:function(e,f,g,h){f=
|
||||
f&&B(f);g=g&&B(g);f=f||g.parent();b(e,f,g);return a.push(e,"enter",Ea(h))},move:function(e,f,g,h){f=f&&B(f);g=g&&B(g);f=f||g.parent();b(e,f,g);return a.push(e,"move",Ea(h))},leave:function(b,c){return a.push(b,"leave",Ea(c),function(){b.remove()})},addClass:function(b,c,g){g=Ea(g);g.addClass=hb(g.addclass,c);return a.push(b,"addClass",g)},removeClass:function(b,c,g){g=Ea(g);g.removeClass=hb(g.removeClass,c);return a.push(b,"removeClass",g)},setClass:function(b,c,g,h){h=Ea(h);h.addClass=hb(h.addClass,
|
||||
c);h.removeClass=hb(h.removeClass,g);return a.push(b,"setClass",h)},animate:function(b,c,g,h,k){k=Ea(k);k.from=k.from?M(k.from,c):c;k.to=k.to?M(k.to,g):g;k.tempClasses=hb(k.tempClasses,h||"ng-inline-animate");return a.push(b,"animate",k)}}}]}],Se=function(){this.$get=["$$rAF","$q",function(a,b){var d=function(){};d.prototype={done:function(a){this.defer&&this.defer[!0===a?"reject":"resolve"]()},end:function(){this.done()},cancel:function(){this.done(!0)},getPromise:function(){this.defer||(this.defer=
|
||||
b.defer());return this.defer.promise},then:function(a,b){return this.getPromise().then(a,b)},"catch":function(a){return this.getPromise()["catch"](a)},"finally":function(a){return this.getPromise()["finally"](a)}};return function(b,e){function f(){a(function(){e.addClass&&(b.addClass(e.addClass),e.addClass=null);e.removeClass&&(b.removeClass(e.removeClass),e.removeClass=null);e.to&&(b.css(e.to),e.to=null);g||h.done();g=!0});return h}e.cleanupStyles&&(e.from=e.to=null);e.from&&(b.css(e.from),e.from=
|
||||
null);var g,h=new d;return{start:f,end:f}}}]},ha=G("$compile");Cc.$inject=["$provide","$$sanitizeUriProvider"];var Vc=/^((?:x|data)[\:\-_])/i,Qf=G("$controller"),Uc=/^(\S+)(\s+as\s+(\w+))?$/,$e=function(){this.$get=["$document",function(a){return function(b){b?!b.nodeType&&b instanceof B&&(b=b[0]):b=a[0].body;return b.offsetWidth+1}}]},$c="application/json",ac={"Content-Type":$c+";charset=utf-8"},Sf=/^\[|^\{(?!\{)/,Tf={"[":/]$/,"{":/}$/},Rf=/^\)\]\}',?\n/,pg=G("$http"),dd=function(a){return function(){throw pg("legacy",
|
||||
a);}},Ja=fa.$interpolateMinErr=G("$interpolate");Ja.throwNoconcat=function(a){throw Ja("noconcat",a);};Ja.interr=function(a,b){return Ja("interr",a,b.toString())};var qg=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,Vf={http:80,https:443,ftp:21},Db=G("$location"),rg={$$html5:!1,$$replace:!1,absUrl:Eb("$$absUrl"),url:function(a){if(q(a))return this.$$url;var b=qg.exec(a);(b[1]||""===a)&&this.path(decodeURIComponent(b[1]));(b[2]||b[1]||""===a)&&this.search(b[3]||"");this.hash(b[5]||"");return this},protocol:Eb("$$protocol"),
|
||||
host:Eb("$$host"),port:Eb("$$port"),path:id("$$path",function(a){a=null!==a?a.toString():"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,b){switch(arguments.length){case 0:return this.$$search;case 1:if(E(a)||Q(a))a=a.toString(),this.$$search=xc(a);else if(H(a))a=bb(a,{}),n(a,function(b,c){null==b&&delete a[c]}),this.$$search=a;else throw Db("isrcharg");break;default:q(b)||null===b?delete this.$$search[a]:this.$$search[a]=b}this.$$compose();return this},hash:id("$$hash",function(a){return null!==
|
||||
a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};n([hd,dc,cc],function(a){a.prototype=Object.create(rg);a.prototype.state=function(b){if(!arguments.length)return this.$$state;if(a!==cc||!this.$$html5)throw Db("nostate");this.$$state=q(b)?null:b;return this}});var ba=G("$parse"),Wf=Function.prototype.call,Xf=Function.prototype.apply,Yf=Function.prototype.bind,Lb=$();n("+ - * / % === !== == != < > <= >= && || ! = |".split(" "),function(a){Lb[a]=!0});var sg={n:"\n",f:"\f",r:"\r",
|
||||
t:"\t",v:"\v","'":"'",'"':'"'},fc=function(a){this.options=a};fc.prototype={constructor:fc,lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index<this.text.length;)if(a=this.text.charAt(this.index),'"'===a||"'"===a)this.readString(a);else if(this.isNumber(a)||"."===a&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(a))this.readIdent();else if(this.is(a,"(){}[].,;:?"))this.tokens.push({index:this.index,text:a}),this.index++;else if(this.isWhitespace(a))this.index++;
|
||||
else{var b=a+this.peek(),d=b+this.peek(2),c=Lb[b],e=Lb[d];Lb[a]||c||e?(a=e?d:c?b:a,this.tokens.push({index:this.index,text:a,operator:!0}),this.index+=a.length):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a,b){return-1!==b.indexOf(a)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||
|
||||
"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,b,d){d=d||this.index;b=y(b)?"s "+b+"-"+this.index+" ["+this.text.substring(b,d)+"]":" "+d;throw ba("lexerr",a,b,this.text);},readNumber:function(){for(var a="",b=this.index;this.index<this.text.length;){var d=F(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var c=this.peek();
|
||||
if("e"==d&&this.isExpOperator(c))a+=d;else if(this.isExpOperator(d)&&c&&this.isNumber(c)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||c&&this.isNumber(c)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:b,text:a,constant:!0,value:Number(a)})},readIdent:function(){for(var a=this.index;this.index<this.text.length;){var b=this.text.charAt(this.index);if(!this.isIdent(b)&&!this.isNumber(b))break;this.index++}this.tokens.push({index:a,
|
||||
text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var b=this.index;this.index++;for(var d="",c=a,e=!1;this.index<this.text.length;){var f=this.text.charAt(this.index),c=c+f;if(e)"u"===f?(e=this.text.substring(this.index+1,this.index+5),e.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+e+"]"),this.index+=4,d+=String.fromCharCode(parseInt(e,16))):d+=sg[f]||f,e=!1;else if("\\"===f)e=!0;else{if(f===a){this.index++;this.tokens.push({index:b,text:c,constant:!0,
|
||||
value:d});return}d+=f}this.index++}this.throwError("Unterminated quote",b)}};var s=function(a,b){this.lexer=a;this.options=b};s.Program="Program";s.ExpressionStatement="ExpressionStatement";s.AssignmentExpression="AssignmentExpression";s.ConditionalExpression="ConditionalExpression";s.LogicalExpression="LogicalExpression";s.BinaryExpression="BinaryExpression";s.UnaryExpression="UnaryExpression";s.CallExpression="CallExpression";s.MemberExpression="MemberExpression";s.Identifier="Identifier";s.Literal=
|
||||
"Literal";s.ArrayExpression="ArrayExpression";s.Property="Property";s.ObjectExpression="ObjectExpression";s.ThisExpression="ThisExpression";s.NGValueParameter="NGValueParameter";s.prototype={ast:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.program();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);return a},program:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.expressionStatement()),!this.expect(";"))return{type:s.Program,
|
||||
body:a}},expressionStatement:function(){return{type:s.ExpressionStatement,expression:this.filterChain()}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary();this.expect("=")&&(a={type:s.AssignmentExpression,left:a,right:this.assignment(),operator:"="});return a},ternary:function(){var a=this.logicalOR(),b,d;return this.expect("?")&&(b=this.expression(),this.consume(":"))?
|
||||
(d=this.expression(),{type:s.ConditionalExpression,test:a,alternate:b,consequent:d}):a},logicalOR:function(){for(var a=this.logicalAND();this.expect("||");)a={type:s.LogicalExpression,operator:"||",left:a,right:this.logicalAND()};return a},logicalAND:function(){for(var a=this.equality();this.expect("&&");)a={type:s.LogicalExpression,operator:"&&",left:a,right:this.equality()};return a},equality:function(){for(var a=this.relational(),b;b=this.expect("==","!=","===","!==");)a={type:s.BinaryExpression,
|
||||
operator:b.text,left:a,right:this.relational()};return a},relational:function(){for(var a=this.additive(),b;b=this.expect("<",">","<=",">=");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),b;b=this.expect("+","-");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:s.BinaryExpression,operator:b.text,
|
||||
left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:s.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.constants.hasOwnProperty(this.peek().text)?a=bb(this.constants[this.consume().text]):this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():
|
||||
this.throwError("not a primary expression",this.peek());for(var b;b=this.expect("(","[",".");)"("===b.text?(a={type:s.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")):"["===b.text?(a={type:s.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:s.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var b={type:s.CallExpression,callee:this.identifier(),
|
||||
arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return b},parseArguments:function(){var a=[];if(")"!==this.peekToken().text){do a.push(this.expression());while(this.expect(","))}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:s.Identifier,name:a.text}},constant:function(){return{type:s.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;
|
||||
a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:s.ArrayExpression,elements:a}},object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;b={type:s.Property,kind:"init"};this.peek().constant?b.key=this.constant():this.peek().identifier?b.key=this.identifier():this.throwError("invalid key",this.peek());this.consume(":");b.value=this.expression();a.push(b)}while(this.expect(","))}this.consume("}");return{type:s.ObjectExpression,properties:a}},
|
||||
throwError:function(a,b){throw ba("syntax",b.text,a,b.index+1,this.text,this.text.substring(b.index));},consume:function(a){if(0===this.tokens.length)throw ba("ueoe",this.text);var b=this.expect(a);b||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return b},peekToken:function(){if(0===this.tokens.length)throw ba("ueoe",this.text);return this.tokens[0]},peek:function(a,b,d,c){return this.peekAhead(0,a,b,d,c)},peekAhead:function(a,b,d,c,e){if(this.tokens.length>a){a=this.tokens[a];
|
||||
var f=a.text;if(f===b||f===d||f===c||f===e||!(b||d||c||e))return a}return!1},expect:function(a,b,d,c){return(a=this.peek(a,b,d,c))?(this.tokens.shift(),a):!1},constants:{"true":{type:s.Literal,value:!0},"false":{type:s.Literal,value:!1},"null":{type:s.Literal,value:null},undefined:{type:s.Literal,value:u},"this":{type:s.ThisExpression}}};rd.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:b,fn:{vars:[],body:[],own:{}},assign:{vars:[],
|
||||
body:[],own:{}},inputs:[]};W(c,d.$filter);var e="",f;this.stage="assign";if(f=pd(c))this.state.computing="assign",e=this.nextId(),this.recurse(f,e),this.return_(e),e="fn.assign="+this.generateFunction("assign","s,v,l");f=nd(c.body);d.stage="inputs";n(f,function(a,b){var c="fn"+b;d.state[c]={vars:[],body:[],own:{}};d.state.computing=c;var e=d.nextId();d.recurse(a,e);d.return_(e);d.state.inputs.push(c);a.watchId=b});this.state.computing="fn";this.stage="main";this.recurse(c);e='"'+this.USE+" "+this.STRICT+
|
||||
'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+e+this.watchFns()+"return fn;";e=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","getStringValue","ensureSafeAssignContext","ifDefined","plus","text",e))(this.$filter,Va,xa,kd,jd,ld,Zf,md,a);this.state=this.stage=u;e.literal=qd(c);e.constant=c.constant;return e},USE:"use",STRICT:"strict",watchFns:function(){var a=[],b=this.state.inputs,d=this;n(b,function(b){a.push("var "+b+"="+d.generateFunction(b,
|
||||
"s"))});b.length&&a.push("fn.inputs=["+b.join(",")+"];");return a.join("")},generateFunction:function(a,b){return"function("+b+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],b=this;n(this.state.filters,function(d,c){a.push(d+"=$filter("+b.escape(c)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,b,
|
||||
d,c,e,f){var g,h,k=this,l,m;c=c||x;if(!f&&y(a.watchId))b=b||this.nextId(),this.if_("i",this.lazyAssign(b,this.computedMember("i",a.watchId)),this.lazyRecurse(a,b,d,c,e,!0));else switch(a.type){case s.Program:n(a.body,function(b,c){k.recurse(b.expression,u,u,function(a){h=a});c!==a.body.length-1?k.current().body.push(h,";"):k.return_(h)});break;case s.Literal:m=this.escape(a.value);this.assign(b,m);c(m);break;case s.UnaryExpression:this.recurse(a.argument,u,u,function(a){h=a});m=a.operator+"("+this.ifDefined(h,
|
||||
0)+")";this.assign(b,m);c(m);break;case s.BinaryExpression:this.recurse(a.left,u,u,function(a){g=a});this.recurse(a.right,u,u,function(a){h=a});m="+"===a.operator?this.plus(g,h):"-"===a.operator?this.ifDefined(g,0)+a.operator+this.ifDefined(h,0):"("+g+")"+a.operator+"("+h+")";this.assign(b,m);c(m);break;case s.LogicalExpression:b=b||this.nextId();k.recurse(a.left,b);k.if_("&&"===a.operator?b:k.not(b),k.lazyRecurse(a.right,b));c(b);break;case s.ConditionalExpression:b=b||this.nextId();k.recurse(a.test,
|
||||
b);k.if_(b,k.lazyRecurse(a.alternate,b),k.lazyRecurse(a.consequent,b));c(b);break;case s.Identifier:b=b||this.nextId();d&&(d.context="inputs"===k.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l",a.name)+"?l:s"),d.computed=!1,d.name=a.name);Va(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)),function(){k.if_("inputs"===k.stage||"s",function(){e&&1!==e&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(b,k.nonComputedMember("s",
|
||||
a.name))})},b&&k.lazyAssign(b,k.nonComputedMember("l",a.name)));(k.state.expensiveChecks||Fb(a.name))&&k.addEnsureSafeObject(b);c(b);break;case s.MemberExpression:g=d&&(d.context=this.nextId())||this.nextId();b=b||this.nextId();k.recurse(a.object,g,u,function(){k.if_(k.notNull(g),function(){if(a.computed)h=k.nextId(),k.recurse(a.property,h),k.getStringValue(h),k.addEnsureSafeMemberName(h),e&&1!==e&&k.if_(k.not(k.computedMember(g,h)),k.lazyAssign(k.computedMember(g,h),"{}")),m=k.ensureSafeObject(k.computedMember(g,
|
||||
h)),k.assign(b,m),d&&(d.computed=!0,d.name=h);else{Va(a.property.name);e&&1!==e&&k.if_(k.not(k.nonComputedMember(g,a.property.name)),k.lazyAssign(k.nonComputedMember(g,a.property.name),"{}"));m=k.nonComputedMember(g,a.property.name);if(k.state.expensiveChecks||Fb(a.property.name))m=k.ensureSafeObject(m);k.assign(b,m);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(b,"undefined")});c(b)},!!e);break;case s.CallExpression:b=b||this.nextId();a.filter?(h=k.filter(a.callee.name),l=[],n(a.arguments,
|
||||
function(a){var b=k.nextId();k.recurse(a,b);l.push(b)}),m=h+"("+l.join(",")+")",k.assign(b,m),c(b)):(h=k.nextId(),g={},l=[],k.recurse(a.callee,h,g,function(){k.if_(k.notNull(h),function(){k.addEnsureSafeFunction(h);n(a.arguments,function(a){k.recurse(a,k.nextId(),u,function(a){l.push(k.ensureSafeObject(a))})});g.name?(k.state.expensiveChecks||k.addEnsureSafeObject(g.context),m=k.member(g.context,g.name,g.computed)+"("+l.join(",")+")"):m=h+"("+l.join(",")+")";m=k.ensureSafeObject(m);k.assign(b,m)},
|
||||
function(){k.assign(b,"undefined")});c(b)}));break;case s.AssignmentExpression:h=this.nextId();g={};if(!od(a.left))throw ba("lval");this.recurse(a.left,u,g,function(){k.if_(k.notNull(g.context),function(){k.recurse(a.right,h);k.addEnsureSafeObject(k.member(g.context,g.name,g.computed));k.addEnsureSafeAssignContext(g.context);m=k.member(g.context,g.name,g.computed)+a.operator+h;k.assign(b,m);c(b||m)})},1);break;case s.ArrayExpression:l=[];n(a.elements,function(a){k.recurse(a,k.nextId(),u,function(a){l.push(a)})});
|
||||
m="["+l.join(",")+"]";this.assign(b,m);c(m);break;case s.ObjectExpression:l=[];n(a.properties,function(a){k.recurse(a.value,k.nextId(),u,function(b){l.push(k.escape(a.key.type===s.Identifier?a.key.name:""+a.key.value)+":"+b)})});m="{"+l.join(",")+"}";this.assign(b,m);c(m);break;case s.ThisExpression:this.assign(b,"s");c("s");break;case s.NGValueParameter:this.assign(b,"v"),c("v")}},getHasOwnProperty:function(a,b){var d=a+"."+b,c=this.current().own;c.hasOwnProperty(d)||(c[d]=this.nextId(!1,a+"&&("+
|
||||
this.escape(b)+" in "+a+")"));return c[d]},assign:function(a,b){if(a)return this.current().body.push(a,"=",b,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,b){return"ifDefined("+a+","+this.escape(b)+")"},plus:function(a,b){return"plus("+a+","+b+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,b,d){if(!0===a)b();else{var c=this.current().body;c.push("if(",a,
|
||||
"){");b();c.push("}");d&&(c.push("else{"),d(),c.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+"!=null"},nonComputedMember:function(a,b){return a+"."+b},computedMember:function(a,b){return a+"["+b+"]"},member:function(a,b,d){return d?this.computedMember(a,b):this.nonComputedMember(a,b)},addEnsureSafeObject:function(a){this.current().body.push(this.ensureSafeObject(a),";")},addEnsureSafeMemberName:function(a){this.current().body.push(this.ensureSafeMemberName(a),";")},
|
||||
addEnsureSafeFunction:function(a){this.current().body.push(this.ensureSafeFunction(a),";")},addEnsureSafeAssignContext:function(a){this.current().body.push(this.ensureSafeAssignContext(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},getStringValue:function(a){this.assign(a,"getStringValue("+a+",text)")},ensureSafeAssignContext:function(a){return"ensureSafeAssignContext("+
|
||||
a+",text)"},lazyRecurse:function(a,b,d,c,e,f){var g=this;return function(){g.recurse(a,b,d,c,e,f)}},lazyAssign:function(a,b){var d=this;return function(){d.assign(a,b)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(E(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(Q(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"===
|
||||
typeof a)return"undefined";throw ba("esc");},nextId:function(a,b){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(b?"="+b:""));return d},current:function(){return this.state[this.state.computing]}};sd.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=b;W(c,d.$filter);var e,f;if(e=pd(c))f=this.recurse(e);e=nd(c.body);var g;e&&(g=[],n(e,function(a,b){var c=d.recurse(a);a.input=c;g.push(c);a.watchId=b}));var h=[];n(c.body,function(a){h.push(d.recurse(a.expression))});
|
||||
e=0===c.body.length?function(){}:1===c.body.length?h[0]:function(a,b){var c;n(h,function(d){c=d(a,b)});return c};f&&(e.assign=function(a,b,c){return f(a,c,b)});g&&(e.inputs=g);e.literal=qd(c);e.constant=c.constant;return e},recurse:function(a,b,d){var c,e,f=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case s.Literal:return this.value(a.value,b);case s.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case s.BinaryExpression:return c=this.recurse(a.left),
|
||||
e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case s.Identifier:return Va(a.name,f.expression),f.identifier(a.name,f.expensiveChecks||Fb(a.name),b,d,f.expression);case s.MemberExpression:return c=this.recurse(a.object,!1,!!d),a.computed||(Va(a.property.name,
|
||||
f.expression),e=a.property.name),a.computed&&(e=this.recurse(a.property)),a.computed?this.computedMember(c,e,b,d,f.expression):this.nonComputedMember(c,e,f.expensiveChecks,b,d,f.expression);case s.CallExpression:return g=[],n(a.arguments,function(a){g.push(f.recurse(a))}),a.filter&&(e=this.$filter(a.callee.name)),a.filter||(e=this.recurse(a.callee,!0)),a.filter?function(a,c,d,f){for(var r=[],n=0;n<g.length;++n)r.push(g[n](a,c,d,f));a=e.apply(u,r,f);return b?{context:u,name:u,value:a}:a}:function(a,
|
||||
c,d,m){var r=e(a,c,d,m),n;if(null!=r.value){xa(r.context,f.expression);kd(r.value,f.expression);n=[];for(var q=0;q<g.length;++q)n.push(xa(g[q](a,c,d,m),f.expression));n=xa(r.value.apply(r.context,n),f.expression)}return b?{value:n}:n};case s.AssignmentExpression:return c=this.recurse(a.left,!0,1),e=this.recurse(a.right),function(a,d,g,m){var n=c(a,d,g,m);a=e(a,d,g,m);xa(n.value,f.expression);ld(n.context);n.context[n.name]=a;return b?{value:a}:a};case s.ArrayExpression:return g=[],n(a.elements,function(a){g.push(f.recurse(a))}),
|
||||
function(a,c,d,e){for(var f=[],n=0;n<g.length;++n)f.push(g[n](a,c,d,e));return b?{value:f}:f};case s.ObjectExpression:return g=[],n(a.properties,function(a){g.push({key:a.key.type===s.Identifier?a.key.name:""+a.key.value,value:f.recurse(a.value)})}),function(a,c,d,e){for(var f={},n=0;n<g.length;++n)f[g[n].key]=g[n].value(a,c,d,e);return b?{value:f}:f};case s.ThisExpression:return function(a){return b?{value:a}:a};case s.NGValueParameter:return function(a,c,d,e){return b?{value:d}:d}}},"unary+":function(a,
|
||||
b){return function(d,c,e,f){d=a(d,c,e,f);d=y(d)?+d:0;return b?{value:d}:d}},"unary-":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=y(d)?-d:0;return b?{value:d}:d}},"unary!":function(a,b){return function(d,c,e,f){d=!a(d,c,e,f);return b?{value:d}:d}},"binary+":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);h=md(h,c);return d?{value:h}:h}},"binary-":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);h=(y(h)?h:0)-(y(c)?c:0);return d?{value:h}:h}},"binary*":function(a,
|
||||
b,d){return function(c,e,f,g){c=a(c,e,f,g)*b(c,e,f,g);return d?{value:c}:c}},"binary/":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)/b(c,e,f,g);return d?{value:c}:c}},"binary%":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)%b(c,e,f,g);return d?{value:c}:c}},"binary===":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)===b(c,e,f,g);return d?{value:c}:c}},"binary!==":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)!==b(c,e,f,g);return d?{value:c}:c}},"binary==":function(a,b,
|
||||
d){return function(c,e,f,g){c=a(c,e,f,g)==b(c,e,f,g);return d?{value:c}:c}},"binary!=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)!=b(c,e,f,g);return d?{value:c}:c}},"binary<":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<b(c,e,f,g);return d?{value:c}:c}},"binary>":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>b(c,e,f,g);return d?{value:c}:c}},"binary<=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<=b(c,e,f,g);return d?{value:c}:c}},"binary>=":function(a,b,d){return function(c,
|
||||
e,f,g){c=a(c,e,f,g)>=b(c,e,f,g);return d?{value:c}:c}},"binary&&":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)&&b(c,e,f,g);return d?{value:c}:c}},"binary||":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)||b(c,e,f,g);return d?{value:c}:c}},"ternary?:":function(a,b,d,c){return function(e,f,g,h){e=a(e,f,g,h)?b(e,f,g,h):d(e,f,g,h);return c?{value:e}:e}},value:function(a,b){return function(){return b?{context:u,name:u,value:a}:a}},identifier:function(a,b,d,c,e){return function(f,g,h,k){f=
|
||||
g&&a in g?g:f;c&&1!==c&&f&&!f[a]&&(f[a]={});g=f?f[a]:u;b&&xa(g,e);return d?{context:f,name:a,value:g}:g}},computedMember:function(a,b,d,c,e){return function(f,g,h,k){var l=a(f,g,h,k),m,n;null!=l&&(m=b(f,g,h,k),m=jd(m),Va(m,e),c&&1!==c&&l&&!l[m]&&(l[m]={}),n=l[m],xa(n,e));return d?{context:l,name:m,value:n}:n}},nonComputedMember:function(a,b,d,c,e,f){return function(g,h,k,l){g=a(g,h,k,l);e&&1!==e&&g&&!g[b]&&(g[b]={});h=null!=g?g[b]:u;(d||Fb(b))&&xa(h,f);return c?{context:g,name:b,value:h}:h}},inputs:function(a,
|
||||
b){return function(d,c,e,f){return f?f[b]:a(d,c,e)}}};var gc=function(a,b,d){this.lexer=a;this.$filter=b;this.options=d;this.ast=new s(this.lexer);this.astCompiler=d.csp?new sd(this.ast,b):new rd(this.ast,b)};gc.prototype={constructor:gc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};$();$();var $f=Object.prototype.valueOf,ya=G("$sce"),la={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},ha=G("$compile"),Y=X.createElement("a"),wd=wa(S.location.href);
|
||||
xd.$inject=["$document"];Jc.$inject=["$provide"];yd.$inject=["$locale"];Ad.$inject=["$locale"];var ic=".",jg={yyyy:ca("FullYear",4),yy:ca("FullYear",2,0,!0),y:ca("FullYear",1),MMMM:Hb("Month"),MMM:Hb("Month",!0),MM:ca("Month",2,1),M:ca("Month",1,1),dd:ca("Date",2),d:ca("Date",1),HH:ca("Hours",2),H:ca("Hours",1),hh:ca("Hours",2,-12),h:ca("Hours",1,-12),mm:ca("Minutes",2),m:ca("Minutes",1),ss:ca("Seconds",2),s:ca("Seconds",1),sss:ca("Milliseconds",3),EEEE:Hb("Day"),EEE:Hb("Day",!0),a:function(a,b){return 12>
|
||||
a.getHours()?b.AMPMS[0]:b.AMPMS[1]},Z:function(a,b,d){a=-1*d;return a=(0<=a?"+":"")+(Gb(Math[0<a?"floor":"ceil"](a/60),2)+Gb(Math.abs(a%60),2))},ww:Ed(2),w:Ed(1),G:jc,GG:jc,GGG:jc,GGGG:function(a,b){return 0>=a.getFullYear()?b.ERANAMES[0]:b.ERANAMES[1]}},ig=/((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,hg=/^\-?\d+$/;zd.$inject=["$locale"];var eg=na(F),fg=na(sb);Bd.$inject=["$parse"];var he=na({restrict:"E",compile:function(a,b){if(!b.href&&!b.xlinkHref)return function(a,
|
||||
b){if("a"===b[0].nodeName.toLowerCase()){var e="[object SVGAnimatedString]"===sa.call(b.prop("href"))?"xlink:href":"href";b.on("click",function(a){b.attr(e)||a.preventDefault()})}}}}),tb={};n(Cb,function(a,b){function d(a,d,e){a.$watch(e[c],function(a){e.$set(b,!!a)})}if("multiple"!=a){var c=va("ng-"+b),e=d;"checked"===a&&(e=function(a,b,e){e.ngModel!==e[c]&&d(a,b,e)});tb[c]=function(){return{restrict:"A",priority:100,link:e}}}});n(Zc,function(a,b){tb[b]=function(){return{priority:100,link:function(a,
|
||||
c,e){if("ngPattern"===b&&"/"==e.ngPattern.charAt(0)&&(c=e.ngPattern.match(lg))){e.$set("ngPattern",new RegExp(c[1],c[2]));return}a.$watch(e[b],function(a){e.$set(b,a)})}}}});n(["src","srcset","href"],function(a){var b=va("ng-"+a);tb[b]=function(){return{priority:99,link:function(d,c,e){var f=a,g=a;"href"===a&&"[object SVGAnimatedString]"===sa.call(c.prop("href"))&&(g="xlinkHref",e.$attr[g]="xlink:href",f=null);e.$observe(b,function(b){b?(e.$set(g,b),Ha&&f&&c.prop(f,e[g])):"href"===a&&e.$set(g,null)})}}}});
|
||||
var Ib={$addControl:x,$$renameControl:function(a,b){a.$name=b},$removeControl:x,$setValidity:x,$setDirty:x,$setPristine:x,$setSubmitted:x};Fd.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Nd=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||x}return{name:"form",restrict:a?"EAC":"E",require:["form","^^?form"],controller:Fd,compile:function(d,f){d.addClass(Wa).addClass(mb);var g=f.name?"name":a&&f.ngForm?"ngForm":
|
||||
!1;return{pre:function(a,d,e,f){var n=f[0];if(!("action"in e)){var q=function(b){a.$apply(function(){n.$commitViewValue();n.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",q,!1);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",q,!1)},0,!1)})}(f[1]||n.$$parentForm).$addControl(n);var s=g?c(n.$name):x;g&&(s(a,n),e.$observe(g,function(b){n.$name!==b&&(s(a,u),n.$$parentForm.$$renameControl(n,b),s=c(n.$name),s(a,n))}));d.on("$destroy",function(){n.$$parentForm.$removeControl(n);
|
||||
s(a,u);M(n,Ib)})}}}}}]},ie=Nd(),ve=Nd(!0),kg=/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/,tg=/^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/,ug=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,vg=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,Od=/^(\d{4})-(\d{2})-(\d{2})$/,Pd=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,mc=/^(\d{4})-W(\d\d)$/,Qd=/^(\d{4})-(\d\d)$/,
|
||||
Rd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Sd={text:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);kc(c)},date:kb("date",Od,Kb(Od,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":kb("datetimelocal",Pd,Kb(Pd,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:kb("time",Rd,Kb(Rd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:kb("week",mc,function(a,b){if(da(a))return a;if(E(a)){mc.lastIndex=0;var d=mc.exec(a);if(d){var c=+d[1],e=+d[2],f=d=0,g=0,h=0,k=Dd(c),e=7*(e-1);b&&(d=b.getHours(),f=
|
||||
b.getMinutes(),g=b.getSeconds(),h=b.getMilliseconds());return new Date(c,0,k.getDate()+e,d,f,g,h)}}return NaN},"yyyy-Www"),month:kb("month",Qd,Kb(Qd,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f){Hd(a,b,d,c);jb(a,b,d,c,e,f);c.$$parserName="number";c.$parsers.push(function(a){return c.$isEmpty(a)?null:vg.test(a)?parseFloat(a):u});c.$formatters.push(function(a){if(!c.$isEmpty(a)){if(!Q(a))throw lb("numfmt",a);a=a.toString()}return a});if(y(d.min)||d.ngMin){var g;c.$validators.min=function(a){return c.$isEmpty(a)||
|
||||
q(g)||a>=g};d.$observe("min",function(a){y(a)&&!Q(a)&&(a=parseFloat(a,10));g=Q(a)&&!isNaN(a)?a:u;c.$validate()})}if(y(d.max)||d.ngMax){var h;c.$validators.max=function(a){return c.$isEmpty(a)||q(h)||a<=h};d.$observe("max",function(a){y(a)&&!Q(a)&&(a=parseFloat(a,10));h=Q(a)&&!isNaN(a)?a:u;c.$validate()})}},url:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);kc(c);c.$$parserName="url";c.$validators.url=function(a,b){var d=a||b;return c.$isEmpty(d)||tg.test(d)}},email:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);kc(c);
|
||||
c.$$parserName="email";c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)||ug.test(d)}},radio:function(a,b,d,c){q(d.name)&&b.attr("name",++nb);b.on("click",function(a){b[0].checked&&c.$setViewValue(d.value,a&&a.type)});c.$render=function(){b[0].checked=d.value==c.$viewValue};d.$observe("value",c.$render)},checkbox:function(a,b,d,c,e,f,g,h){var k=Id(h,a,"ngTrueValue",d.ngTrueValue,!0),l=Id(h,a,"ngFalseValue",d.ngFalseValue,!1);b.on("click",function(a){c.$setViewValue(b[0].checked,a&&
|
||||
a.type)});c.$render=function(){b[0].checked=c.$viewValue};c.$isEmpty=function(a){return!1===a};c.$formatters.push(function(a){return ma(a,k)});c.$parsers.push(function(a){return a?k:l})},hidden:x,button:x,submit:x,reset:x,file:x},Dc=["$browser","$sniffer","$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,h){h[0]&&(Sd[F(g.type)]||Sd.text)(e,f,g,h[0],b,a,d,c)}}}}],wg=/^(true|false|\d+)$/,Ne=function(){return{restrict:"A",priority:100,compile:function(a,
|
||||
b){return wg.test(b.ngValue)?function(a,b,e){e.$set("value",a.$eval(e.ngValue))}:function(a,b,e){a.$watch(e.ngValue,function(a){e.$set("value",a)})}}}},ne=["$compile",function(a){return{restrict:"AC",compile:function(b){a.$$addBindingClass(b);return function(b,c,e){a.$$addBindingInfo(c,e.ngBind);c=c[0];b.$watch(e.ngBind,function(a){c.textContent=q(a)?"":a})}}}}],pe=["$interpolate","$compile",function(a,b){return{compile:function(d){b.$$addBindingClass(d);return function(c,d,f){c=a(d.attr(f.$attr.ngBindTemplate));
|
||||
b.$$addBindingInfo(d,c.expressions);d=d[0];f.$observe("ngBindTemplate",function(a){d.textContent=q(a)?"":a})}}}}],oe=["$sce","$parse","$compile",function(a,b,d){return{restrict:"A",compile:function(c,e){var f=b(e.ngBindHtml),g=b(e.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(c);return function(b,c,e){d.$$addBindingInfo(c,e.ngBindHtml);b.$watch(g,function(){c.html(a.getTrustedHtml(f(b))||"")})}}}}],Me=na({restrict:"A",require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),
|
||||
qe=lc("",!0),se=lc("Odd",0),re=lc("Even",1),te=La({compile:function(a,b){b.$set("ngCloak",u);a.removeClass("ng-cloak")}}),ue=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],Ic={},xg={blur:!0,focus:!0};n("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var b=va("ng-"+a);Ic[b]=["$parse","$rootScope",function(d,c){return{restrict:"A",compile:function(e,f){var g=
|
||||
d(f[b],null,!0);return function(b,d){d.on(a,function(d){var e=function(){g(b,{$event:d})};xg[a]&&c.$$phase?b.$evalAsync(e):b.$apply(e)})}}}}]});var xe=["$animate",function(a){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(b,d,c,e,f){var g,h,k;b.$watch(c.ngIf,function(b){b?h||f(function(b,e){h=e;b[b.length++]=X.createComment(" end ngIf: "+c.ngIf+" ");g={clone:b};a.enter(b,d.parent(),d)}):(k&&(k.remove(),k=null),h&&(h.$destroy(),h=null),g&&(k=
|
||||
rb(g.clone),a.leave(k).then(function(){k=null}),g=null))})}}}],ye=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:fa.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",h=e.autoscroll;return function(c,e,m,n,q){var s=0,v,u,p,C=function(){u&&(u.remove(),u=null);v&&(v.$destroy(),v=null);p&&(d.leave(p).then(function(){u=null}),u=p,p=null)};c.$watch(f,function(f){var m=function(){!y(h)||h&&!c.$eval(h)||
|
||||
b()},u=++s;f?(a(f,!0).then(function(a){if(u===s){var b=c.$new();n.template=a;a=q(b,function(a){C();d.enter(a,null,e).then(m)});v=b;p=a;v.$emit("$includeContentLoaded",f);c.$eval(g)}},function(){u===s&&(C(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(C(),n.template=null)})}}}}],Pe=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(b,d,c,e){/SVG/.test(d[0].toString())?(d.empty(),a(Lc(e.template,X).childNodes)(b,function(a){d.append(a)},
|
||||
{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],ze=La({priority:450,compile:function(){return{pre:function(a,b,d){a.$eval(d.ngInit)}}}}),Le=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=b.attr(d.$attr.ngList)||", ",f="false"!==d.ngTrim,g=f?U(e):e;c.$parsers.push(function(a){if(!q(a)){var b=[];a&&n(a.split(g),function(a){a&&b.push(f?U(a):a)});return b}});c.$formatters.push(function(a){return I(a)?a.join(e):u});c.$isEmpty=function(a){return!a||
|
||||
!a.length}}}},mb="ng-valid",Jd="ng-invalid",Wa="ng-pristine",Jb="ng-dirty",Ld="ng-pending",lb=G("ngModel"),yg=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,b,d,c,e,f,g,h,k,l){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=u;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=!1;
|
||||
this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=u;this.$name=l(d.name||"",!1)(a);this.$$parentForm=Ib;var m=e(d.ngModel),r=m.assign,t=m,s=r,v=null,B,p=this;this.$$setOptions=function(a){if((p.$options=a)&&a.getterSetter){var b=e(d.ngModel+"()"),f=e(d.ngModel+"($$$p)");t=function(a){var c=m(a);z(c)&&(c=b(a));return c};s=function(a,b){z(m(a))?f(a,{$$$p:p.$modelValue}):r(a,p.$modelValue)}}else if(!m.assign)throw lb("nonassign",d.ngModel,ua(c));};this.$render=x;this.$isEmpty=
|
||||
function(a){return q(a)||""===a||null===a||a!==a};var C=0;Gd({ctrl:this,$element:c,set:function(a,b){a[b]=!0},unset:function(a,b){delete a[b]},$animate:f});this.$setPristine=function(){p.$dirty=!1;p.$pristine=!0;f.removeClass(c,Jb);f.addClass(c,Wa)};this.$setDirty=function(){p.$dirty=!0;p.$pristine=!1;f.removeClass(c,Wa);f.addClass(c,Jb);p.$$parentForm.$setDirty()};this.$setUntouched=function(){p.$touched=!1;p.$untouched=!0;f.setClass(c,"ng-untouched","ng-touched")};this.$setTouched=function(){p.$touched=
|
||||
!0;p.$untouched=!1;f.setClass(c,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){g.cancel(v);p.$viewValue=p.$$lastCommittedViewValue;p.$render()};this.$validate=function(){if(!Q(p.$modelValue)||!isNaN(p.$modelValue)){var a=p.$$rawModelValue,b=p.$valid,c=p.$modelValue,d=p.$options&&p.$options.allowInvalid;p.$$runValidators(a,p.$$lastCommittedViewValue,function(e){d||b===e||(p.$modelValue=e?a:u,p.$modelValue!==c&&p.$$writeModelToScope())})}};this.$$runValidators=function(a,b,c){function d(){var c=
|
||||
!0;n(p.$validators,function(d,e){var g=d(a,b);c=c&&g;f(e,g)});return c?!0:(n(p.$asyncValidators,function(a,b){f(b,null)}),!1)}function e(){var c=[],d=!0;n(p.$asyncValidators,function(e,g){var h=e(a,b);if(!h||!z(h.then))throw lb("$asyncValidators",h);f(g,u);c.push(h.then(function(){f(g,!0)},function(a){d=!1;f(g,!1)}))});c.length?k.all(c).then(function(){g(d)},x):g(!0)}function f(a,b){h===C&&p.$setValidity(a,b)}function g(a){h===C&&c(a)}C++;var h=C;(function(){var a=p.$$parserName||"parse";if(q(B))f(a,
|
||||
null);else return B||(n(p.$validators,function(a,b){f(b,null)}),n(p.$asyncValidators,function(a,b){f(b,null)})),f(a,B),B;return!0})()?d()?e():g(!1):g(!1)};this.$commitViewValue=function(){var a=p.$viewValue;g.cancel(v);if(p.$$lastCommittedViewValue!==a||""===a&&p.$$hasNativeValidators)p.$$lastCommittedViewValue=a,p.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var b=p.$$lastCommittedViewValue;if(B=q(b)?u:!0)for(var c=0;c<p.$parsers.length;c++)if(b=p.$parsers[c](b),
|
||||
q(b)){B=!1;break}Q(p.$modelValue)&&isNaN(p.$modelValue)&&(p.$modelValue=t(a));var d=p.$modelValue,e=p.$options&&p.$options.allowInvalid;p.$$rawModelValue=b;e&&(p.$modelValue=b,p.$modelValue!==d&&p.$$writeModelToScope());p.$$runValidators(b,p.$$lastCommittedViewValue,function(a){e||(p.$modelValue=a?b:u,p.$modelValue!==d&&p.$$writeModelToScope())})};this.$$writeModelToScope=function(){s(a,p.$modelValue);n(p.$viewChangeListeners,function(a){try{a()}catch(c){b(c)}})};this.$setViewValue=function(a,b){p.$viewValue=
|
||||
a;p.$options&&!p.$options.updateOnDefault||p.$$debounceViewValueCommit(b)};this.$$debounceViewValueCommit=function(b){var c=0,d=p.$options;d&&y(d.debounce)&&(d=d.debounce,Q(d)?c=d:Q(d[b])?c=d[b]:Q(d["default"])&&(c=d["default"]));g.cancel(v);c?v=g(function(){p.$commitViewValue()},c):h.$$phase?p.$commitViewValue():a.$apply(function(){p.$commitViewValue()})};a.$watch(function(){var b=t(a);if(b!==p.$modelValue&&(p.$modelValue===p.$modelValue||b===b)){p.$modelValue=p.$$rawModelValue=b;B=u;for(var c=p.$formatters,
|
||||
d=c.length,e=b;d--;)e=c[d](e);p.$viewValue!==e&&(p.$viewValue=p.$$lastCommittedViewValue=e,p.$render(),p.$$runValidators(b,e,x))}return b})}],Ke=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:yg,priority:1,compile:function(b){b.addClass(Wa).addClass("ng-untouched").addClass(mb);return{pre:function(a,b,e,f){var g=f[0];b=f[1]||g.$$parentForm;g.$$setOptions(f[2]&&f[2].$options);b.$addControl(g);e.$observe("name",function(a){g.$name!==a&&g.$$parentForm.$$renameControl(g,
|
||||
a)});a.$on("$destroy",function(){g.$$parentForm.$removeControl(g)})},post:function(b,c,e,f){var g=f[0];if(g.$options&&g.$options.updateOn)c.on(g.$options.updateOn,function(a){g.$$debounceViewValueCommit(a&&a.type)});c.on("blur",function(c){g.$touched||(a.$$phase?b.$evalAsync(g.$setTouched):b.$apply(g.$setTouched))})}}}}}],zg=/(\s+|^)default(\s+|$)/,Oe=function(){return{restrict:"A",controller:["$scope","$attrs",function(a,b){var d=this;this.$options=bb(a.$eval(b.ngModelOptions));y(this.$options.updateOn)?
|
||||
(this.$options.updateOnDefault=!1,this.$options.updateOn=U(this.$options.updateOn.replace(zg,function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},Ae=La({terminal:!0,priority:1E3}),Ag=G("ngOptions"),Bg=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,Ie=["$compile","$parse",function(a,
|
||||
b){function d(a,c,d){function e(a,b,c,d,f){this.selectValue=a;this.viewValue=b;this.label=c;this.group=d;this.disabled=f}function l(a){var b;if(!q&&za(a))b=a;else{b=[];for(var c in a)a.hasOwnProperty(c)&&"$"!==c.charAt(0)&&b.push(c)}return b}var m=a.match(Bg);if(!m)throw Ag("iexp",a,ua(c));var n=m[5]||m[7],q=m[6];a=/ as /.test(m[0])&&m[1];var s=m[9];c=b(m[2]?m[1]:n);var v=a&&b(a)||c,u=s&&b(s),p=s?function(a,b){return u(d,b)}:function(a){return Ca(a)},C=function(a,b){return p(a,z(a,b))},w=b(m[2]||
|
||||
m[1]),y=b(m[3]||""),B=b(m[4]||""),x=b(m[8]),D={},z=q?function(a,b){D[q]=b;D[n]=a;return D}:function(a){D[n]=a;return D};return{trackBy:s,getTrackByValue:C,getWatchables:b(x,function(a){var b=[];a=a||[];for(var c=l(a),e=c.length,f=0;f<e;f++){var g=a===c?f:c[f],k=z(a[g],g),g=p(a[g],k);b.push(g);if(m[2]||m[1])g=w(d,k),b.push(g);m[4]&&(k=B(d,k),b.push(k))}return b}),getOptions:function(){for(var a=[],b={},c=x(d)||[],f=l(c),g=f.length,m=0;m<g;m++){var n=c===f?m:f[m],r=z(c[n],n),q=v(d,r),n=p(q,r),t=w(d,
|
||||
r),u=y(d,r),r=B(d,r),q=new e(n,q,t,u,r);a.push(q);b[n]=q}return{items:a,selectValueMap:b,getOptionFromViewValue:function(a){return b[C(a)]},getViewValueFromOption:function(a){return s?fa.copy(a.viewValue):a.viewValue}}}}}var c=X.createElement("option"),e=X.createElement("optgroup");return{restrict:"A",terminal:!0,require:["select","?ngModel"],link:{pre:function(a,b,c,d){d[0].registerOption=x},post:function(b,g,h,k){function l(a,b){a.element=b;b.disabled=a.disabled;a.label!==b.label&&(b.label=a.label,
|
||||
b.textContent=a.label);a.value!==b.value&&(b.value=a.selectValue)}function m(a,b,c,d){b&&F(b.nodeName)===c?c=b:(c=d.cloneNode(!1),b?a.insertBefore(c,b):a.appendChild(c));return c}function r(a){for(var b;a;)b=a.nextSibling,Xb(a),a=b}function q(a){var b=p&&p[0],c=z&&z[0];if(b||c)for(;a&&(a===b||a===c||8===a.nodeType||""===a.value);)a=a.nextSibling;return a}function s(){var a=D&&u.readValue();D=E.getOptions();var b={},d=g[0].firstChild;x&&g.prepend(p);d=q(d);D.items.forEach(function(a){var f,h;a.group?
|
||||
(f=b[a.group],f||(f=m(g[0],d,"optgroup",e),d=f.nextSibling,f.label=a.group,f=b[a.group]={groupElement:f,currentOptionElement:f.firstChild}),h=m(f.groupElement,f.currentOptionElement,"option",c),l(a,h),f.currentOptionElement=h.nextSibling):(h=m(g[0],d,"option",c),l(a,h),d=h.nextSibling)});Object.keys(b).forEach(function(a){r(b[a].currentOptionElement)});r(d);v.$render();if(!v.$isEmpty(a)){var f=u.readValue();(E.trackBy?ma(a,f):a===f)||(v.$setViewValue(f),v.$render())}}var v=k[1];if(v){var u=k[0];k=
|
||||
h.multiple;for(var p,C=0,w=g.children(),y=w.length;C<y;C++)if(""===w[C].value){p=w.eq(C);break}var x=!!p,z=B(c.cloneNode(!1));z.val("?");var D,E=d(h.ngOptions,g,b);k?(v.$isEmpty=function(a){return!a||0===a.length},u.writeValue=function(a){D.items.forEach(function(a){a.element.selected=!1});a&&a.forEach(function(a){(a=D.getOptionFromViewValue(a))&&!a.disabled&&(a.element.selected=!0)})},u.readValue=function(){var a=g.val()||[],b=[];n(a,function(a){(a=D.selectValueMap[a])&&!a.disabled&&b.push(D.getViewValueFromOption(a))});
|
||||
return b},E.trackBy&&b.$watchCollection(function(){if(I(v.$viewValue))return v.$viewValue.map(function(a){return E.getTrackByValue(a)})},function(){v.$render()})):(u.writeValue=function(a){var b=D.getOptionFromViewValue(a);b&&!b.disabled?g[0].value!==b.selectValue&&(z.remove(),x||p.remove(),g[0].value=b.selectValue,b.element.selected=!0,b.element.setAttribute("selected","selected")):null===a||x?(z.remove(),x||g.prepend(p),g.val(""),p.prop("selected",!0),p.attr("selected",!0)):(x||p.remove(),g.prepend(z),
|
||||
g.val("?"),z.prop("selected",!0),z.attr("selected",!0))},u.readValue=function(){var a=D.selectValueMap[g.val()];return a&&!a.disabled?(x||p.remove(),z.remove(),D.getViewValueFromOption(a)):null},E.trackBy&&b.$watch(function(){return E.getTrackByValue(v.$viewValue)},function(){v.$render()}));x?(p.remove(),a(p)(b),p.removeClass("ng-scope")):p=B(c.cloneNode(!1));s();b.$watchCollection(E.getWatchables,s)}}}}}],Be=["$locale","$interpolate","$log",function(a,b,d){var c=/{}/g,e=/^when(Minus)?(.+)$/;return{link:function(f,
|
||||
g,h){function k(a){g.text(a||"")}var l=h.count,m=h.$attr.when&&g.attr(h.$attr.when),r=h.offset||0,s=f.$eval(m)||{},u={},v=b.startSymbol(),y=b.endSymbol(),p=v+l+"-"+r+y,C=fa.noop,w;n(h,function(a,b){var c=e.exec(b);c&&(c=(c[1]?"-":"")+F(c[2]),s[c]=g.attr(h.$attr[b]))});n(s,function(a,d){u[d]=b(a.replace(c,p))});f.$watch(l,function(b){var c=parseFloat(b),e=isNaN(c);e||c in s||(c=a.pluralCat(c-r));c===w||e&&Q(w)&&isNaN(w)||(C(),e=u[c],q(e)?(null!=b&&d.debug("ngPluralize: no rule defined for '"+c+"' in "+
|
||||
m),C=x,k()):C=f.$watch(e,k),w=c)})}}}],Ce=["$parse","$animate",function(a,b){var d=G("ngRepeat"),c=function(a,b,c,d,k,l,m){a[c]=d;k&&(a[k]=l);a.$index=b;a.$first=0===b;a.$last=b===m-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(b&1))};return{restrict:"A",multiElement:!0,transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,compile:function(e,f){var g=f.ngRepeat,h=X.createComment(" end ngRepeat: "+g+" "),k=g.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
|
||||
if(!k)throw d("iexp",g);var l=k[1],m=k[2],r=k[3],q=k[4],k=l.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);if(!k)throw d("iidexp",l);var s=k[3]||k[1],v=k[2];if(r&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(r)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(r)))throw d("badident",r);var x,p,y,w,z={$id:Ca};q?x=a(q):(y=function(a,b){return Ca(b)},w=function(a){return a});return function(a,e,f,k,l){x&&(p=function(b,c,d){v&&(z[v]=b);z[s]=c;z.$index=
|
||||
d;return x(a,z)});var q=$();a.$watchCollection(m,function(f){var k,m,t=e[0],x,z=$(),D,E,H,F,I,G,J;r&&(a[r]=f);if(za(f))I=f,m=p||y;else for(J in m=p||w,I=[],f)qa.call(f,J)&&"$"!==J.charAt(0)&&I.push(J);D=I.length;J=Array(D);for(k=0;k<D;k++)if(E=f===I?k:I[k],H=f[E],F=m(E,H,k),q[F])G=q[F],delete q[F],z[F]=G,J[k]=G;else{if(z[F])throw n(J,function(a){a&&a.scope&&(q[a.id]=a)}),d("dupes",g,F,H);J[k]={id:F,scope:u,clone:u};z[F]=!0}for(x in q){G=q[x];F=rb(G.clone);b.leave(F);if(F[0].parentNode)for(k=0,m=F.length;k<
|
||||
m;k++)F[k].$$NG_REMOVED=!0;G.scope.$destroy()}for(k=0;k<D;k++)if(E=f===I?k:I[k],H=f[E],G=J[k],G.scope){x=t;do x=x.nextSibling;while(x&&x.$$NG_REMOVED);G.clone[0]!=x&&b.move(rb(G.clone),null,B(t));t=G.clone[G.clone.length-1];c(G.scope,k,s,H,v,E,D)}else l(function(a,d){G.scope=d;var e=h.cloneNode(!1);a[a.length++]=e;b.enter(a,null,B(t));t=e;G.clone=a;z[G.id]=G;c(G.scope,k,s,H,v,E,D)});q=z})}}}}],De=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngShow,function(b){a[b?
|
||||
"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],we=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngHide,function(b){a[b?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Ee=La(function(a,b,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&n(d,function(a,c){b.css(c,"")});a&&b.css(a)},!0)}),Fe=["$animate",function(a){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(b,
|
||||
d,c,e){var f=[],g=[],h=[],k=[],l=function(a,b){return function(){a.splice(b,1)}};b.$watch(c.ngSwitch||c.on,function(b){var c,d;c=0;for(d=h.length;c<d;++c)a.cancel(h[c]);c=h.length=0;for(d=k.length;c<d;++c){var q=rb(g[c].clone);k[c].$destroy();(h[c]=a.leave(q)).then(l(h,c))}g.length=0;k.length=0;(f=e.cases["!"+b]||e.cases["?"])&&n(f,function(b){b.transclude(function(c,d){k.push(d);var e=b.element;c[c.length++]=X.createComment(" end ngSwitchWhen: ");g.push({clone:c});a.enter(c,e.parent(),e)})})})}}}],
|
||||
Ge=La({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["!"+d.ngSwitchWhen]=c.cases["!"+d.ngSwitchWhen]||[];c.cases["!"+d.ngSwitchWhen].push({transclude:e,element:b})}}),He=La({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["?"]=c.cases["?"]||[];c.cases["?"].push({transclude:e,element:b})}}),Je=La({restrict:"EAC",link:function(a,b,d,c,e){if(!e)throw G("ngTransclude")("orphan",ua(b));e(function(a){b.empty();
|
||||
b.append(a)})}}),je=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(b,d){"text/ng-template"==d.type&&a.put(d.id,b[0].text)}}}],Cg={$setViewValue:x,$render:x},Dg=["$element","$scope","$attrs",function(a,b,d){var c=this,e=new Sa;c.ngModelCtrl=Cg;c.unknownOption=B(X.createElement("option"));c.renderUnknownOption=function(b){b="? "+Ca(b)+" ?";c.unknownOption.val(b);a.prepend(c.unknownOption);a.val(b)};b.$on("$destroy",function(){c.renderUnknownOption=x});c.removeUnknownOption=
|
||||
function(){c.unknownOption.parent()&&c.unknownOption.remove()};c.readValue=function(){c.removeUnknownOption();return a.val()};c.writeValue=function(b){c.hasOption(b)?(c.removeUnknownOption(),a.val(b),""===b&&c.emptyOption.prop("selected",!0)):null==b&&c.emptyOption?(c.removeUnknownOption(),a.val("")):c.renderUnknownOption(b)};c.addOption=function(a,b){Ra(a,'"option value"');""===a&&(c.emptyOption=b);var d=e.get(a)||0;e.put(a,d+1);c.ngModelCtrl.$render();b[0].hasAttribute("selected")&&(b[0].selected=
|
||||
!0)};c.removeOption=function(a){var b=e.get(a);b&&(1===b?(e.remove(a),""===a&&(c.emptyOption=u)):e.put(a,b-1))};c.hasOption=function(a){return!!e.get(a)};c.registerOption=function(a,b,d,e,l){if(e){var m;d.$observe("value",function(a){y(m)&&c.removeOption(m);m=a;c.addOption(a,b)})}else l?a.$watch(l,function(a,e){d.$set("value",a);e!==a&&c.removeOption(e);c.addOption(a,b)}):c.addOption(d.value,b);b.on("$destroy",function(){c.removeOption(d.value);c.ngModelCtrl.$render()})}}],ke=function(){return{restrict:"E",
|
||||
require:["select","?ngModel"],controller:Dg,priority:1,link:{pre:function(a,b,d,c){var e=c[1];if(e){var f=c[0];f.ngModelCtrl=e;e.$render=function(){f.writeValue(e.$viewValue)};b.on("change",function(){a.$apply(function(){e.$setViewValue(f.readValue())})});if(d.multiple){f.readValue=function(){var a=[];n(b.find("option"),function(b){b.selected&&a.push(b.value)});return a};f.writeValue=function(a){var c=new Sa(a);n(b.find("option"),function(a){a.selected=y(c.get(a.value))})};var g,h=NaN;a.$watch(function(){h!==
|
||||
e.$viewValue||ma(g,e.$viewValue)||(g=ia(e.$viewValue),e.$render());h=e.$viewValue});e.$isEmpty=function(a){return!a||0===a.length}}}}}}},me=["$interpolate",function(a){return{restrict:"E",priority:100,compile:function(b,d){if(y(d.value))var c=a(d.value,!0);else{var e=a(b.text(),!0);e||d.$set("value",b.text())}return function(a,b,d){var k=b.parent();(k=k.data("$selectController")||k.parent().data("$selectController"))&&k.registerOption(a,b,d,c,e)}}}}],le=na({restrict:"E",terminal:!1}),Fc=function(){return{restrict:"A",
|
||||
require:"?ngModel",link:function(a,b,d,c){c&&(d.required=!0,c.$validators.required=function(a,b){return!d.required||!c.$isEmpty(b)},d.$observe("required",function(){c.$validate()}))}}},Ec=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e,f=d.ngPattern||d.pattern;d.$observe("pattern",function(a){E(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw G("ngPattern")("noregexp",f,a,ua(b));e=a||u;c.$validate()});c.$validators.pattern=function(a,b){return c.$isEmpty(b)||
|
||||
q(e)||e.test(b)}}}}},Hc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=-1;d.$observe("maxlength",function(a){a=ea(a);e=isNaN(a)?-1:a;c.$validate()});c.$validators.maxlength=function(a,b){return 0>e||c.$isEmpty(b)||b.length<=e}}}}},Gc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=0;d.$observe("minlength",function(a){e=ea(a)||0;c.$validate()});c.$validators.minlength=function(a,b){return c.$isEmpty(b)||b.length>=e}}}}};S.angular.bootstrap?
|
||||
console.log("WARNING: Tried to load angular more than once."):(ce(),ee(fa),fa.module("ngLocale",[],["$provide",function(a){function b(a){a+="";var b=a.indexOf(".");return-1==b?0:a.length-b-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),
|
||||
SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",
|
||||
negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",pluralCat:function(a,c){var e=a|0,f=c;u===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),B(X).ready(function(){Zd(X,yc)}))})(window,document);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
|
||||
(function(C){'use strict';function re(a){if(D(a))w(a.objectMaxDepth)&&(Wb.objectMaxDepth=Xb(a.objectMaxDepth)?a.objectMaxDepth:NaN),w(a.urlErrorParamsEnabled)&&Ga(a.urlErrorParamsEnabled)&&(Wb.urlErrorParamsEnabled=a.urlErrorParamsEnabled);else return Wb}function Xb(a){return W(a)&&0<a}function F(a,b){b=b||Error;return function(){var d=arguments[0],c;c="["+(a?a+":":"")+d+"] http://errors.angularjs.org/1.7.9/"+(a?a+"/":"")+d;for(d=1;d<arguments.length;d++){c=c+(1==d?"?":"&")+"p"+(d-1)+"=";var e=encodeURIComponent,
|
||||
f;f=arguments[d];f="function"==typeof f?f.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof f?"undefined":"string"!=typeof f?JSON.stringify(f):f;c+=e(f)}return new b(c)}}function ya(a){if(null==a||$a(a))return!1;if(H(a)||A(a)||x&&a instanceof x)return!0;var b="length"in Object(a)&&a.length;return W(b)&&(0<=b&&b-1 in a||"function"===typeof a.item)}function r(a,b,d){var c,e;if(a)if(B(a))for(c in a)"prototype"!==c&&"length"!==c&&"name"!==c&&a.hasOwnProperty(c)&&b.call(d,a[c],c,a);else if(H(a)||
|
||||
ya(a)){var f="object"!==typeof a;c=0;for(e=a.length;c<e;c++)(f||c in a)&&b.call(d,a[c],c,a)}else if(a.forEach&&a.forEach!==r)a.forEach(b,d,a);else if(Nc(a))for(c in a)b.call(d,a[c],c,a);else if("function"===typeof a.hasOwnProperty)for(c in a)a.hasOwnProperty(c)&&b.call(d,a[c],c,a);else for(c in a)ta.call(a,c)&&b.call(d,a[c],c,a);return a}function Oc(a,b,d){for(var c=Object.keys(a).sort(),e=0;e<c.length;e++)b.call(d,a[c[e]],c[e]);return c}function Yb(a){return function(b,d){a(d,b)}}function se(){return++pb}
|
||||
function Zb(a,b,d){for(var c=a.$$hashKey,e=0,f=b.length;e<f;++e){var g=b[e];if(D(g)||B(g))for(var k=Object.keys(g),h=0,l=k.length;h<l;h++){var m=k[h],p=g[m];d&&D(p)?ha(p)?a[m]=new Date(p.valueOf()):ab(p)?a[m]=new RegExp(p):p.nodeName?a[m]=p.cloneNode(!0):$b(p)?a[m]=p.clone():"__proto__"!==m&&(D(a[m])||(a[m]=H(p)?[]:{}),Zb(a[m],[p],!0)):a[m]=p}}c?a.$$hashKey=c:delete a.$$hashKey;return a}function S(a){return Zb(a,Ha.call(arguments,1),!1)}function te(a){return Zb(a,Ha.call(arguments,1),!0)}function fa(a){return parseInt(a,
|
||||
10)}function ac(a,b){return S(Object.create(a),b)}function E(){}function Ta(a){return a}function ia(a){return function(){return a}}function bc(a){return B(a.toString)&&a.toString!==la}function z(a){return"undefined"===typeof a}function w(a){return"undefined"!==typeof a}function D(a){return null!==a&&"object"===typeof a}function Nc(a){return null!==a&&"object"===typeof a&&!Pc(a)}function A(a){return"string"===typeof a}function W(a){return"number"===typeof a}function ha(a){return"[object Date]"===la.call(a)}
|
||||
function H(a){return Array.isArray(a)||a instanceof Array}function cc(a){switch(la.call(a)){case "[object Error]":return!0;case "[object Exception]":return!0;case "[object DOMException]":return!0;default:return a instanceof Error}}function B(a){return"function"===typeof a}function ab(a){return"[object RegExp]"===la.call(a)}function $a(a){return a&&a.window===a}function bb(a){return a&&a.$evalAsync&&a.$watch}function Ga(a){return"boolean"===typeof a}function ue(a){return a&&W(a.length)&&ve.test(la.call(a))}
|
||||
function $b(a){return!(!a||!(a.nodeName||a.prop&&a.attr&&a.find))}function we(a){var b={};a=a.split(",");var d;for(d=0;d<a.length;d++)b[a[d]]=!0;return b}function ua(a){return K(a.nodeName||a[0]&&a[0].nodeName)}function cb(a,b){var d=a.indexOf(b);0<=d&&a.splice(d,1);return d}function Ia(a,b,d){function c(a,b,c){c--;if(0>c)return"...";var d=b.$$hashKey,f;if(H(a)){f=0;for(var g=a.length;f<g;f++)b.push(e(a[f],c))}else if(Nc(a))for(f in a)b[f]=e(a[f],c);else if(a&&"function"===typeof a.hasOwnProperty)for(f in a)a.hasOwnProperty(f)&&
|
||||
(b[f]=e(a[f],c));else for(f in a)ta.call(a,f)&&(b[f]=e(a[f],c));d?b.$$hashKey=d:delete b.$$hashKey;return b}function e(a,b){if(!D(a))return a;var d=g.indexOf(a);if(-1!==d)return k[d];if($a(a)||bb(a))throw pa("cpws");var d=!1,e=f(a);void 0===e&&(e=H(a)?[]:Object.create(Pc(a)),d=!0);g.push(a);k.push(e);return d?c(a,e,b):e}function f(a){switch(la.call(a)){case "[object Int8Array]":case "[object Int16Array]":case "[object Int32Array]":case "[object Float32Array]":case "[object Float64Array]":case "[object Uint8Array]":case "[object Uint8ClampedArray]":case "[object Uint16Array]":case "[object Uint32Array]":return new a.constructor(e(a.buffer),
|
||||
a.byteOffset,a.length);case "[object ArrayBuffer]":if(!a.slice){var b=new ArrayBuffer(a.byteLength);(new Uint8Array(b)).set(new Uint8Array(a));return b}return a.slice(0);case "[object Boolean]":case "[object Number]":case "[object String]":case "[object Date]":return new a.constructor(a.valueOf());case "[object RegExp]":return b=new RegExp(a.source,a.toString().match(/[^/]*$/)[0]),b.lastIndex=a.lastIndex,b;case "[object Blob]":return new a.constructor([a],{type:a.type})}if(B(a.cloneNode))return a.cloneNode(!0)}
|
||||
var g=[],k=[];d=Xb(d)?d:NaN;if(b){if(ue(b)||"[object ArrayBuffer]"===la.call(b))throw pa("cpta");if(a===b)throw pa("cpi");H(b)?b.length=0:r(b,function(a,c){"$$hashKey"!==c&&delete b[c]});g.push(a);k.push(b);return c(a,b,d)}return e(a,d)}function dc(a,b){return a===b||a!==a&&b!==b}function va(a,b){if(a===b)return!0;if(null===a||null===b)return!1;if(a!==a&&b!==b)return!0;var d=typeof a,c;if(d===typeof b&&"object"===d)if(H(a)){if(!H(b))return!1;if((d=a.length)===b.length){for(c=0;c<d;c++)if(!va(a[c],
|
||||
b[c]))return!1;return!0}}else{if(ha(a))return ha(b)?dc(a.getTime(),b.getTime()):!1;if(ab(a))return ab(b)?a.toString()===b.toString():!1;if(bb(a)||bb(b)||$a(a)||$a(b)||H(b)||ha(b)||ab(b))return!1;d=T();for(c in a)if("$"!==c.charAt(0)&&!B(a[c])){if(!va(a[c],b[c]))return!1;d[c]=!0}for(c in b)if(!(c in d)&&"$"!==c.charAt(0)&&w(b[c])&&!B(b[c]))return!1;return!0}return!1}function db(a,b,d){return a.concat(Ha.call(b,d))}function Va(a,b){var d=2<arguments.length?Ha.call(arguments,2):[];return!B(b)||b instanceof
|
||||
RegExp?b:d.length?function(){return arguments.length?b.apply(a,db(d,arguments,0)):b.apply(a,d)}:function(){return arguments.length?b.apply(a,arguments):b.call(a)}}function Qc(a,b){var d=b;"string"===typeof a&&"$"===a.charAt(0)&&"$"===a.charAt(1)?d=void 0:$a(b)?d="$WINDOW":b&&C.document===b?d="$DOCUMENT":bb(b)&&(d="$SCOPE");return d}function eb(a,b){if(!z(a))return W(b)||(b=b?2:null),JSON.stringify(a,Qc,b)}function Rc(a){return A(a)?JSON.parse(a):a}function ec(a,b){a=a.replace(xe,"");var d=Date.parse("Jan 01, 1970 00:00:00 "+
|
||||
a)/6E4;return X(d)?b:d}function Sc(a,b){a=new Date(a.getTime());a.setMinutes(a.getMinutes()+b);return a}function fc(a,b,d){d=d?-1:1;var c=a.getTimezoneOffset();b=ec(b,c);return Sc(a,d*(b-c))}function za(a){a=x(a).clone().empty();var b=x("<div></div>").append(a).html();try{return a[0].nodeType===Pa?K(b):b.match(/^(<[^>]+>)/)[1].replace(/^<([\w-]+)/,function(a,b){return"<"+K(b)})}catch(d){return K(b)}}function Tc(a){try{return decodeURIComponent(a)}catch(b){}}function gc(a){var b={};r((a||"").split("&"),
|
||||
function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=Tc(e),w(e)&&(f=w(f)?Tc(f):!0,ta.call(b,e)?H(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function ye(a){var b=[];r(a,function(a,c){H(a)?r(a,function(a){b.push(ba(c,!0)+(!0===a?"":"="+ba(a,!0)))}):b.push(ba(c,!0)+(!0===a?"":"="+ba(a,!0)))});return b.length?b.join("&"):""}function hc(a){return ba(a,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ba(a,
|
||||
b){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function ze(a,b){var d,c,e=Qa.length;for(c=0;c<e;++c)if(d=Qa[c]+b,A(d=a.getAttribute(d)))return d;return null}function Ae(a,b){var d,c,e={};r(Qa,function(b){b+="app";!d&&a.hasAttribute&&a.hasAttribute(b)&&(d=a,c=a.getAttribute(b))});r(Qa,function(b){b+="app";var e;!d&&(e=a.querySelector("["+b.replace(":","\\:")+"]"))&&(d=e,c=e.getAttribute(b))});
|
||||
d&&(Be?(e.strictDi=null!==ze(d,"strict-di"),b(d,c?[c]:[],e)):C.console.error("AngularJS: disabling automatic bootstrap. <script> protocol indicates an extension, document.location.href does not match."))}function Uc(a,b,d){D(d)||(d={});d=S({strictDi:!1},d);var c=function(){a=x(a);if(a.injector()){var c=a[0]===C.document?"document":za(a);throw pa("btstrpd",c.replace(/</,"<").replace(/>/,">"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider",
|
||||
function(a){a.debugInfoEnabled(!0)}]);b.unshift("ng");c=fb(b,d.strictDi);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;C&&e.test(C.name)&&(d.debugInfoEnabled=!0,C.name=C.name.replace(e,""));if(C&&!f.test(C.name))return c();C.name=C.name.replace(f,"");ca.resumeBootstrap=function(a){r(a,function(a){b.push(a)});return c()};B(ca.resumeDeferredBootstrap)&&
|
||||
ca.resumeDeferredBootstrap()}function Ce(){C.name="NG_ENABLE_DEBUG_INFO!"+C.name;C.location.reload()}function De(a){a=ca.element(a).injector();if(!a)throw pa("test");return a.get("$$testability")}function Vc(a,b){b=b||"_";return a.replace(Ee,function(a,c){return(c?b:"")+a.toLowerCase()})}function Fe(){var a;if(!Wc){var b=qb();(rb=z(b)?C.jQuery:b?C[b]:void 0)&&rb.fn.on?(x=rb,S(rb.fn,{scope:Wa.scope,isolateScope:Wa.isolateScope,controller:Wa.controller,injector:Wa.injector,inheritedData:Wa.inheritedData})):
|
||||
x=Y;a=x.cleanData;x.cleanData=function(b){for(var c,e=0,f;null!=(f=b[e]);e++)(c=(x._data(f)||{}).events)&&c.$destroy&&x(f).triggerHandler("$destroy");a(b)};ca.element=x;Wc=!0}}function gb(a,b,d){if(!a)throw pa("areq",b||"?",d||"required");return a}function sb(a,b,d){d&&H(a)&&(a=a[a.length-1]);gb(B(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Ja(a,b){if("hasOwnProperty"===a)throw pa("badname",b);}function Ge(a,b,d){if(!b)return a;b=b.split(".");
|
||||
for(var c,e=a,f=b.length,g=0;g<f;g++)c=b[g],a&&(a=(e=a)[c]);return!d&&B(a)?Va(e,a):a}function tb(a){for(var b=a[0],d=a[a.length-1],c,e=1;b!==d&&(b=b.nextSibling);e++)if(c||a[e]!==b)c||(c=x(Ha.call(a,0,e))),c.push(b);return c||a}function T(){return Object.create(null)}function ic(a){if(null==a)return"";switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=!bc(a)||H(a)||ha(a)?eb(a):a.toString()}return a}function He(a){function b(a,b,c){return a[b]||(a[b]=c())}var d=F("$injector"),
|
||||
c=F("ng");a=b(a,"angular",Object);a.$$minErr=a.$$minErr||F;return b(a,"module",function(){var a={};return function(f,g,k){var h={};if("hasOwnProperty"===f)throw c("badname","module");g&&a.hasOwnProperty(f)&&(a[f]=null);return b(a,f,function(){function a(b,c,d,f){f||(f=e);return function(){f[d||"push"]([b,c,arguments]);return t}}function b(a,c,d){d||(d=e);return function(b,e){e&&B(e)&&(e.$$moduleName=f);d.push([a,c,arguments]);return t}}if(!g)throw d("nomod",f);var e=[],n=[],s=[],G=a("$injector","invoke",
|
||||
"push",n),t={_invokeQueue:e,_configBlocks:n,_runBlocks:s,info:function(a){if(w(a)){if(!D(a))throw c("aobj","value");h=a;return this}return h},requires:g,name:f,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),decorator:b("$provide","decorator",n),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider",
|
||||
"directive"),component:b("$compileProvider","component"),config:G,run:function(a){s.push(a);return this}};k&&G(k);return t})}})}function ja(a,b){if(H(a)){b=b||[];for(var d=0,c=a.length;d<c;d++)b[d]=a[d]}else if(D(a))for(d in b=b||{},a)if("$"!==d.charAt(0)||"$"!==d.charAt(1))b[d]=a[d];return b||a}function Ie(a,b){var d=[];Xb(b)&&(a=ca.copy(a,null,b));return JSON.stringify(a,function(a,b){b=Qc(a,b);if(D(b)){if(0<=d.indexOf(b))return"...";d.push(b)}return b})}function Je(a){S(a,{errorHandlingConfig:re,
|
||||
bootstrap:Uc,copy:Ia,extend:S,merge:te,equals:va,element:x,forEach:r,injector:fb,noop:E,bind:Va,toJson:eb,fromJson:Rc,identity:Ta,isUndefined:z,isDefined:w,isString:A,isFunction:B,isObject:D,isNumber:W,isElement:$b,isArray:H,version:Ke,isDate:ha,callbacks:{$$counter:0},getTestability:De,reloadWithDebugInfo:Ce,$$minErr:F,$$csp:Aa,$$encodeUriSegment:hc,$$encodeUriQuery:ba,$$lowercase:K,$$stringify:ic,$$uppercase:ub});kc=He(C);kc("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:Le});
|
||||
a.provider("$compile",Xc).directive({a:Me,input:Yc,textarea:Yc,form:Ne,script:Oe,select:Pe,option:Qe,ngBind:Re,ngBindHtml:Se,ngBindTemplate:Te,ngClass:Ue,ngClassEven:Ve,ngClassOdd:We,ngCloak:Xe,ngController:Ye,ngForm:Ze,ngHide:$e,ngIf:af,ngInclude:bf,ngInit:cf,ngNonBindable:df,ngPluralize:ef,ngRef:ff,ngRepeat:gf,ngShow:hf,ngStyle:jf,ngSwitch:kf,ngSwitchWhen:lf,ngSwitchDefault:mf,ngOptions:nf,ngTransclude:of,ngModel:pf,ngList:qf,ngChange:rf,pattern:Zc,ngPattern:Zc,required:$c,ngRequired:$c,minlength:ad,
|
||||
ngMinlength:ad,maxlength:bd,ngMaxlength:bd,ngValue:sf,ngModelOptions:tf}).directive({ngInclude:uf,input:vf}).directive(vb).directive(cd);a.provider({$anchorScroll:wf,$animate:xf,$animateCss:yf,$$animateJs:zf,$$animateQueue:Af,$$AnimateRunner:Bf,$$animateAsyncRun:Cf,$browser:Df,$cacheFactory:Ef,$controller:Ff,$document:Gf,$$isDocumentHidden:Hf,$exceptionHandler:If,$filter:dd,$$forceReflow:Jf,$interpolate:Kf,$interval:Lf,$$intervalFactory:Mf,$http:Nf,$httpParamSerializer:Of,$httpParamSerializerJQLike:Pf,
|
||||
$httpBackend:Qf,$xhrFactory:Rf,$jsonpCallbacks:Sf,$location:Tf,$log:Uf,$parse:Vf,$rootScope:Wf,$q:Xf,$$q:Yf,$sce:Zf,$sceDelegate:$f,$sniffer:ag,$$taskTrackerFactory:bg,$templateCache:cg,$templateRequest:dg,$$testability:eg,$timeout:fg,$window:gg,$$rAF:hg,$$jqLite:ig,$$Map:jg,$$cookieReader:kg})}]).info({angularVersion:"1.7.9"})}function wb(a,b){return b.toUpperCase()}function xb(a){return a.replace(lg,wb)}function lc(a){a=a.nodeType;return 1===a||!a||9===a}function ed(a,b){var d,c,e=b.createDocumentFragment(),
|
||||
f=[];if(mc.test(a)){d=e.appendChild(b.createElement("div"));c=(mg.exec(a)||["",""])[1].toLowerCase();c=oa[c]||oa._default;d.innerHTML=c[1]+a.replace(ng,"<$1></$2>")+c[2];for(c=c[0];c--;)d=d.lastChild;f=db(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";r(f,function(a){e.appendChild(a)});return e}function Y(a){if(a instanceof Y)return a;var b;A(a)&&(a=U(a),b=!0);if(!(this instanceof Y)){if(b&&"<"!==a.charAt(0))throw nc("nosel");return new Y(a)}if(b){b=
|
||||
C.document;var d;a=(d=og.exec(a))?[b.createElement(d[1])]:(d=ed(a,b))?d.childNodes:[];oc(this,a)}else B(a)?fd(a):oc(this,a)}function pc(a){return a.cloneNode(!0)}function yb(a,b){!b&&lc(a)&&x.cleanData([a]);a.querySelectorAll&&x.cleanData(a.querySelectorAll("*"))}function gd(a){for(var b in a)return!1;return!0}function hd(a){var b=a.ng339,d=b&&Ka[b],c=d&&d.events,d=d&&d.data;d&&!gd(d)||c&&!gd(c)||(delete Ka[b],a.ng339=void 0)}function id(a,b,d,c){if(w(c))throw nc("offargs");var e=(c=zb(a))&&c.events,
|
||||
f=c&&c.handle;if(f){if(b){var g=function(b){var c=e[b];w(d)&&cb(c||[],d);w(d)&&c&&0<c.length||(a.removeEventListener(b,f),delete e[b])};r(b.split(" "),function(a){g(a);Ab[a]&&g(Ab[a])})}else for(b in e)"$destroy"!==b&&a.removeEventListener(b,f),delete e[b];hd(a)}}function qc(a,b){var d=a.ng339;if(d=d&&Ka[d])b?delete d.data[b]:d.data={},hd(a)}function zb(a,b){var d=a.ng339,d=d&&Ka[d];b&&!d&&(a.ng339=d=++pg,d=Ka[d]={events:{},data:{},handle:void 0});return d}function rc(a,b,d){if(lc(a)){var c,e=w(d),
|
||||
f=!e&&b&&!D(b),g=!b;a=(a=zb(a,!f))&&a.data;if(e)a[xb(b)]=d;else{if(g)return a;if(f)return a&&a[xb(b)];for(c in b)a[xb(c)]=b[c]}}}function Bb(a,b){return a.getAttribute?-1<(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+b+" "):!1}function Cb(a,b){if(b&&a.setAttribute){var d=(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," "),c=d;r(b.split(" "),function(a){a=U(a);c=c.replace(" "+a+" "," ")});c!==d&&a.setAttribute("class",U(c))}}function Db(a,b){if(b&&a.setAttribute){var d=
|
||||
(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," "),c=d;r(b.split(" "),function(a){a=U(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});c!==d&&a.setAttribute("class",U(c))}}function oc(a,b){if(b)if(b.nodeType)a[a.length++]=b;else{var d=b.length;if("number"===typeof d&&b.window!==b){if(d)for(var c=0;c<d;c++)a[a.length++]=b[c]}else a[a.length++]=b}}function jd(a,b){return Eb(a,"$"+(b||"ngController")+"Controller")}function Eb(a,b,d){9===a.nodeType&&(a=a.documentElement);for(b=H(b)?b:[b];a;){for(var c=
|
||||
0,e=b.length;c<e;c++)if(w(d=x.data(a,b[c])))return d;a=a.parentNode||11===a.nodeType&&a.host}}function kd(a){for(yb(a,!0);a.firstChild;)a.removeChild(a.firstChild)}function Fb(a,b){b||yb(a);var d=a.parentNode;d&&d.removeChild(a)}function qg(a,b){b=b||C;if("complete"===b.document.readyState)b.setTimeout(a);else x(b).on("load",a)}function fd(a){function b(){C.document.removeEventListener("DOMContentLoaded",b);C.removeEventListener("load",b);a()}"complete"===C.document.readyState?C.setTimeout(a):(C.document.addEventListener("DOMContentLoaded",
|
||||
b),C.addEventListener("load",b))}function ld(a,b){var d=Gb[b.toLowerCase()];return d&&md[ua(a)]&&d}function rg(a,b){var d=function(c,d){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=b[d||c.type],g=f?f.length:0;if(g){if(z(c.immediatePropagationStopped)){var k=c.stopImmediatePropagation;c.stopImmediatePropagation=function(){c.immediatePropagationStopped=!0;c.stopPropagation&&c.stopPropagation();k&&k.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};
|
||||
var h=f.specialHandlerWrapper||sg;1<g&&(f=ja(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||h(a,c,f[l])}};d.elem=a;return d}function sg(a,b,d){d.call(a,b)}function tg(a,b,d){var c=b.relatedTarget;c&&(c===a||ug.call(a,c))||d.call(a,b)}function ig(){this.$get=function(){return S(Y,{hasClass:function(a,b){a.attr&&(a=a[0]);return Bb(a,b)},addClass:function(a,b){a.attr&&(a=a[0]);return Db(a,b)},removeClass:function(a,b){a.attr&&(a=a[0]);return Cb(a,b)}})}}function La(a,b){var d=a&&a.$$hashKey;
|
||||
if(d)return"function"===typeof d&&(d=a.$$hashKey()),d;d=typeof a;return d="function"===d||"object"===d&&null!==a?a.$$hashKey=d+":"+(b||se)():d+":"+a}function nd(){this._keys=[];this._values=[];this._lastKey=NaN;this._lastIndex=-1}function od(a){a=Function.prototype.toString.call(a).replace(vg,"");return a.match(wg)||a.match(xg)}function yg(a){return(a=od(a))?"function("+(a[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function fb(a,b){function d(a){return function(b,c){if(D(b))r(b,Yb(a));else return a(b,
|
||||
c)}}function c(a,b){Ja(a,"service");if(B(b)||H(b))b=n.instantiate(b);if(!b.$get)throw Ba("pget",a);return p[a+"Provider"]=b}function e(a,b){return function(){var c=t.invoke(b,this);if(z(c))throw Ba("undef",a);return c}}function f(a,b,d){return c(a,{$get:!1!==d?e(a,b):b})}function g(a){gb(z(a)||H(a),"modulesToLoad","not an array");var b=[],c;r(a,function(a){function d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=n.get(e[0]);f[e[1]].apply(f,e[2])}}if(!m.get(a)){m.set(a,!0);try{A(a)?(c=kc(a),
|
||||
t.modules[a]=c,b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):B(a)?b.push(n.invoke(a)):H(a)?b.push(n.invoke(a)):sb(a,"module")}catch(e){throw H(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1===e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Ba("modulerr",a,e.stack||e.message||e);}}});return b}function k(a,c){function d(b,e){if(a.hasOwnProperty(b)){if(a[b]===h)throw Ba("cdep",b+" <- "+l.join(" <- "));return a[b]}try{return l.unshift(b),a[b]=h,a[b]=c(b,e),
|
||||
a[b]}catch(f){throw a[b]===h&&delete a[b],f;}finally{l.shift()}}function e(a,c,f){var g=[];a=fb.$$annotate(a,b,f);for(var h=0,k=a.length;h<k;h++){var l=a[h];if("string"!==typeof l)throw Ba("itkn",l);g.push(c&&c.hasOwnProperty(l)?c[l]:d(l,f))}return g}return{invoke:function(a,b,c,d){"string"===typeof c&&(d=c,c=null);c=e(a,c,d);H(a)&&(a=a[a.length-1]);d=a;if(Ca||"function"!==typeof d)d=!1;else{var f=d.$$ngIsClass;Ga(f)||(f=d.$$ngIsClass=/^class\b/.test(Function.prototype.toString.call(d)));d=f}return d?
|
||||
(c.unshift(null),new (Function.prototype.bind.apply(a,c))):a.apply(b,c)},instantiate:function(a,b,c){var d=H(a)?a[a.length-1]:a;a=e(a,b,c);a.unshift(null);return new (Function.prototype.bind.apply(d,a))},get:d,annotate:fb.$$annotate,has:function(b){return p.hasOwnProperty(b+"Provider")||a.hasOwnProperty(b)}}}b=!0===b;var h={},l=[],m=new Hb,p={$provide:{provider:d(c),factory:d(f),service:d(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:d(function(a,b){return f(a,
|
||||
ia(b),!1)}),constant:d(function(a,b){Ja(a,"constant");p[a]=b;s[a]=b}),decorator:function(a,b){var c=n.get(a+"Provider"),d=c.$get;c.$get=function(){var a=t.invoke(d,c);return t.invoke(b,null,{$delegate:a})}}}},n=p.$injector=k(p,function(a,b){ca.isString(b)&&l.push(b);throw Ba("unpr",l.join(" <- "));}),s={},G=k(s,function(a,b){var c=n.get(a+"Provider",b);return t.invoke(c.$get,c,void 0,a)}),t=G;p.$injectorProvider={$get:ia(G)};t.modules=n.modules=T();var N=g(a),t=G.get("$injector");t.strictDi=b;r(N,
|
||||
function(a){a&&t.invoke(a)});t.loadNewModules=function(a){r(g(a),function(a){a&&t.invoke(a)})};return t}function wf(){var a=!0;this.disableAutoScrolling=function(){a=!1};this.$get=["$window","$location","$rootScope",function(b,d,c){function e(a){var b=null;Array.prototype.some.call(a,function(a){if("a"===ua(a))return b=a,!0});return b}function f(a){if(a){a.scrollIntoView();var c;c=g.yOffset;B(c)?c=c():$b(c)?(c=c[0],c="fixed"!==b.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):W(c)||
|
||||
(c=0);c&&(a=a.getBoundingClientRect().top,b.scrollBy(0,a-c))}else b.scrollTo(0,0)}function g(a){a=A(a)?a:W(a)?a.toString():d.hash();var b;a?(b=k.getElementById(a))?f(b):(b=e(k.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var k=b.document;a&&c.$watch(function(){return d.hash()},function(a,b){a===b&&""===a||qg(function(){c.$evalAsync(g)})});return g}]}function hb(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;H(a)&&(a=a.join(" "));H(b)&&(b=b.join(" "));return a+" "+b}function zg(a){A(a)&&
|
||||
(a=a.split(" "));var b=T();r(a,function(a){a.length&&(b[a]=!0)});return b}function ra(a){return D(a)?a:{}}function Ag(a,b,d,c,e){function f(){qa=null;k()}function g(){t=y();t=z(t)?null:t;va(t,P)&&(t=P);N=P=t}function k(){var a=N;g();if(v!==h.url()||a!==t)v=h.url(),N=t,r(J,function(a){a(h.url(),t)})}var h=this,l=a.location,m=a.history,p=a.setTimeout,n=a.clearTimeout,s={},G=e(d);h.isMock=!1;h.$$completeOutstandingRequest=G.completeTask;h.$$incOutstandingRequestCount=G.incTaskCount;h.notifyWhenNoOutstandingRequests=
|
||||
G.notifyWhenNoPendingTasks;var t,N,v=l.href,jc=b.find("base"),qa=null,y=c.history?function(){try{return m.state}catch(a){}}:E;g();h.url=function(b,d,e){z(e)&&(e=null);l!==a.location&&(l=a.location);m!==a.history&&(m=a.history);if(b){var f=N===e;b=ga(b).href;if(v===b&&(!c.history||f))return h;var k=v&&Da(v)===Da(b);v=b;N=e;!c.history||k&&f?(k||(qa=b),d?l.replace(b):k?(d=l,e=b,f=e.indexOf("#"),e=-1===f?"":e.substr(f),d.hash=e):l.href=b,l.href!==b&&(qa=b)):(m[d?"replaceState":"pushState"](e,"",b),g());
|
||||
qa&&(qa=b);return h}return(qa||l.href).replace(/#$/,"")};h.state=function(){return t};var J=[],I=!1,P=null;h.onUrlChange=function(b){if(!I){if(c.history)x(a).on("popstate",f);x(a).on("hashchange",f);I=!0}J.push(b);return b};h.$$applicationDestroyed=function(){x(a).off("hashchange popstate",f)};h.$$checkUrlChange=k;h.baseHref=function(){var a=jc.attr("href");return a?a.replace(/^(https?:)?\/\/[^/]*/,""):""};h.defer=function(a,b,c){var d;b=b||0;c=c||G.DEFAULT_TASK_TYPE;G.incTaskCount(c);d=p(function(){delete s[d];
|
||||
G.completeTask(a,c)},b);s[d]=c;return d};h.defer.cancel=function(a){if(s.hasOwnProperty(a)){var b=s[a];delete s[a];n(a);G.completeTask(E,b);return!0}return!1}}function Df(){this.$get=["$window","$log","$sniffer","$document","$$taskTrackerFactory",function(a,b,d,c,e){return new Ag(a,c,b,d,e)}]}function Ef(){this.$get=function(){function a(a,c){function e(a){a!==p&&(n?n===a&&(n=a.n):n=a,f(a.n,a.p),f(a,p),p=a,p.n=null)}function f(a,b){a!==b&&(a&&(a.p=b),b&&(b.n=a))}if(a in b)throw F("$cacheFactory")("iid",
|
||||
a);var g=0,k=S({},c,{id:a}),h=T(),l=c&&c.capacity||Number.MAX_VALUE,m=T(),p=null,n=null;return b[a]={put:function(a,b){if(!z(b)){if(l<Number.MAX_VALUE){var c=m[a]||(m[a]={key:a});e(c)}a in h||g++;h[a]=b;g>l&&this.remove(n.key);return b}},get:function(a){if(l<Number.MAX_VALUE){var b=m[a];if(!b)return;e(b)}return h[a]},remove:function(a){if(l<Number.MAX_VALUE){var b=m[a];if(!b)return;b===p&&(p=b.p);b===n&&(n=b.n);f(b.n,b.p);delete m[a]}a in h&&(delete h[a],g--)},removeAll:function(){h=T();g=0;m=T();
|
||||
p=n=null},destroy:function(){m=k=h=null;delete b[a]},info:function(){return S({},k,{size:g})}}}var b={};a.info=function(){var a={};r(b,function(b,e){a[e]=b.info()});return a};a.get=function(a){return b[a]};return a}}function cg(){this.$get=["$cacheFactory",function(a){return a("templates")}]}function Xc(a,b){function d(a,b,c){var d=/^([@&]|[=<](\*?))(\??)\s*([\w$]*)$/,e=T();r(a,function(a,f){a=a.trim();if(a in p)e[f]=p[a];else{var g=a.match(d);if(!g)throw $("iscp",b,f,a,c?"controller bindings definition":
|
||||
"isolate scope definition");e[f]={mode:g[1][0],collection:"*"===g[2],optional:"?"===g[3],attrName:g[4]||f};g[4]&&(p[a]=e[f])}});return e}function c(a){var b=a.charAt(0);if(!b||b!==K(b))throw $("baddir",a);if(a!==a.trim())throw $("baddir",a);}function e(a){var b=a.require||a.controller&&a.name;!H(b)&&D(b)&&r(b,function(a,c){var d=a.match(l);a.substring(d[0].length)||(b[c]=d[0]+c)});return b}var f={},g=/^\s*directive:\s*([\w-]+)\s+(.*)$/,k=/(([\w-]+)(?::([^;]+))?;?)/,h=we("ngSrc,ngSrcset,src,srcset"),
|
||||
l=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,m=/^(on[a-z]+|formaction)$/,p=T();this.directive=function qa(b,d){gb(b,"name");Ja(b,"directive");A(b)?(c(b),gb(d,"directiveFactory"),f.hasOwnProperty(b)||(f[b]=[],a.factory(b+"Directive",["$injector","$exceptionHandler",function(a,c){var d=[];r(f[b],function(f,g){try{var h=a.invoke(f);B(h)?h={compile:ia(h)}:!h.compile&&h.link&&(h.compile=ia(h.link));h.priority=h.priority||0;h.index=g;h.name=h.name||b;h.require=e(h);var k=h,l=h.restrict;if(l&&(!A(l)||!/[EACM]/.test(l)))throw $("badrestrict",
|
||||
l,b);k.restrict=l||"EA";h.$$moduleName=f.$$moduleName;d.push(h)}catch(m){c(m)}});return d}])),f[b].push(d)):r(b,Yb(qa));return this};this.component=function y(a,b){function c(a){function e(b){return B(b)||H(b)?function(c,d){return a.invoke(b,this,{$element:c,$attrs:d})}:b}var f=b.template||b.templateUrl?b.template:"",g={controller:d,controllerAs:Bg(b.controller)||b.controllerAs||"$ctrl",template:e(f),templateUrl:e(b.templateUrl),transclude:b.transclude,scope:{},bindToController:b.bindings||{},restrict:"E",
|
||||
require:b.require};r(b,function(a,b){"$"===b.charAt(0)&&(g[b]=a)});return g}if(!A(a))return r(a,Yb(Va(this,y))),this;var d=b.controller||function(){};r(b,function(a,b){"$"===b.charAt(0)&&(c[b]=a,B(d)&&(d[b]=a))});c.$inject=["$injector"];return this.directive(a,c)};this.aHrefSanitizationWhitelist=function(a){return w(a)?(b.aHrefSanitizationWhitelist(a),this):b.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(a){return w(a)?(b.imgSrcSanitizationWhitelist(a),this):b.imgSrcSanitizationWhitelist()};
|
||||
var n=!0;this.debugInfoEnabled=function(a){return w(a)?(n=a,this):n};var s=!1;this.strictComponentBindingsEnabled=function(a){return w(a)?(s=a,this):s};var G=10;this.onChangesTtl=function(a){return arguments.length?(G=a,this):G};var t=!0;this.commentDirectivesEnabled=function(a){return arguments.length?(t=a,this):t};var N=!0;this.cssClassDirectivesEnabled=function(a){return arguments.length?(N=a,this):N};var v=T();this.addPropertySecurityContext=function(a,b,c){var d=a.toLowerCase()+"|"+b.toLowerCase();
|
||||
if(d in v&&v[d]!==c)throw $("ctxoverride",a,b,v[d],c);v[d]=c;return this};(function(){function a(b,c){r(c,function(a){v[a.toLowerCase()]=b})}a(V.HTML,["iframe|srcdoc","*|innerHTML","*|outerHTML"]);a(V.CSS,["*|style"]);a(V.URL,"area|href area|ping a|href a|ping blockquote|cite body|background del|cite input|src ins|cite q|cite".split(" "));a(V.MEDIA_URL,"audio|src img|src img|srcset source|src source|srcset track|src video|src video|poster".split(" "));a(V.RESOURCE_URL,"*|formAction applet|code applet|codebase base|href embed|src frame|src form|action head|profile html|manifest iframe|src link|href media|src object|codebase object|data script|src".split(" "))})();
|
||||
this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$sce","$animate",function(a,b,c,e,p,M,L,u,R){function q(){try{if(!--Ja)throw Ua=void 0,$("infchng",G);L.$apply(function(){for(var a=0,b=Ua.length;a<b;++a)try{Ua[a]()}catch(d){c(d)}Ua=void 0})}finally{Ja++}}function ma(a,b){if(!a)return a;if(!A(a))throw $("srcset",b,a.toString());for(var c="",d=U(a),e=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,e=/\s/.test(d)?e:/(,)/,d=d.split(e),e=Math.floor(d.length/
|
||||
2),f=0;f<e;f++)var g=2*f,c=c+u.getTrustedMediaUrl(U(d[g])),c=c+(" "+U(d[g+1]));d=U(d[2*f]).split(/\s/);c+=u.getTrustedMediaUrl(U(d[0]));2===d.length&&(c+=" "+U(d[1]));return c}function w(a,b){if(b){var c=Object.keys(b),d,e,f;d=0;for(e=c.length;d<e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a}function O(a,b,c){Fa.innerHTML="<span "+b+">";b=Fa.firstChild.attributes;var d=b[0];b.removeNamedItem(d.name);d.value=c;a.attributes.setNamedItem(d)}function sa(a,b){try{a.addClass(b)}catch(c){}}
|
||||
function da(a,b,c,d,e){a instanceof x||(a=x(a));var f=Xa(a,b,a,c,d,e);da.$$addScopeClass(a);var g=null;return function(b,c,d){if(!a)throw $("multilink");gb(b,"scope");e&&e.needsNewScope&&(b=b.$parent.$new());d=d||{};var h=d.parentBoundTranscludeFn,k=d.transcludeControllers;d=d.futureParentElement;h&&h.$$boundTransclude&&(h=h.$$boundTransclude);g||(g=(d=d&&d[0])?"foreignobject"!==ua(d)&&la.call(d).match(/SVG/)?"svg":"html":"html");d="html"!==g?x(ja(g,x("<div></div>").append(a).html())):c?Wa.clone.call(a):
|
||||
a;if(k)for(var l in k)d.data("$"+l+"Controller",k[l].instance);da.$$addScopeInfo(d,b);c&&c(d,b);f&&f(b,d,d,h);c||(a=f=null);return d}}function Xa(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,m,p,I,t;if(n)for(t=Array(c.length),m=0;m<h.length;m+=3)f=h[m],t[f]=c[f];else t=c;m=0;for(p=h.length;m<p;)k=t[h[m++]],c=h[m++],f=h[m++],c?(c.scope?(l=a.$new(),da.$$addScopeInfo(x(k),l)):l=a,I=c.transcludeOnThisElement?ka(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?ka(a,b):null,c(f,l,k,d,I)):f&&f(a,k.childNodes,
|
||||
void 0,e)}for(var h=[],k=H(a)||a instanceof x,l,m,p,I,n,t=0;t<a.length;t++){l=new w;11===Ca&&ib(a,t,k);m=sc(a[t],[],l,0===t?d:void 0,e);(f=m.length?aa(m,a[t],l,b,c,null,[],[],f):null)&&f.scope&&da.$$addScopeClass(l.$$element);l=f&&f.terminal||!(p=a[t].childNodes)||!p.length?null:Xa(p,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||l)h.push(t,f,l),I=!0,n=n||f;f=null}return I?g:null}function ib(a,b,c){var d=a[b],e=d.parentNode,f;if(d.nodeType===Pa)for(;;){f=e?d.nextSibling:
|
||||
a[b+1];if(!f||f.nodeType!==Pa)break;d.nodeValue+=f.nodeValue;f.parentNode&&f.parentNode.removeChild(f);c&&f===a[b+1]&&a.splice(b+1,1)}}function ka(a,b,c){function d(e,f,g,h,k){e||(e=a.$new(!1,k),e.$$transcluded=!0);return b(e,f,{parentBoundTranscludeFn:c,transcludeControllers:g,futureParentElement:h})}var e=d.$$slots=T(),f;for(f in b.$$slots)e[f]=b.$$slots[f]?ka(a,b.$$slots[f],c):null;return d}function sc(a,b,d,e,f){var g=d.$attr,h;switch(a.nodeType){case 1:h=ua(a);X(b,wa(h),"E",e,f);for(var l,m,
|
||||
n,t,J,s=a.attributes,v=0,G=s&&s.length;v<G;v++){var P=!1,N=!1,r=!1,y=!1,u=!1,M;l=s[v];m=l.name;t=l.value;n=wa(m.toLowerCase());(J=n.match(Ra))?(r="Attr"===J[1],y="Prop"===J[1],u="On"===J[1],m=m.replace(pd,"").toLowerCase().substr(4+J[1].length).replace(/_(.)/g,function(a,b){return b.toUpperCase()})):(M=n.match(Sa))&&ca(M[1])&&(P=m,N=m.substr(0,m.length-5)+"end",m=m.substr(0,m.length-6));if(y||u)d[n]=t,g[n]=l.name,y?Ea(a,b,n,m):b.push(qd(p,L,c,n,m,!1));else{n=wa(m.toLowerCase());g[n]=m;if(r||!d.hasOwnProperty(n))d[n]=
|
||||
t,ld(a,n)&&(d[n]=!0);Ia(a,b,t,n,r);X(b,n,"A",e,f,P,N)}}"input"===h&&"hidden"===a.getAttribute("type")&&a.setAttribute("autocomplete","off");if(!Qa)break;g=a.className;D(g)&&(g=g.animVal);if(A(g)&&""!==g)for(;a=k.exec(g);)n=wa(a[2]),X(b,n,"C",e,f)&&(d[n]=U(a[3])),g=g.substr(a.index+a[0].length);break;case Pa:na(b,a.nodeValue);break;case 8:if(!Oa)break;F(a,b,d,e,f)}b.sort(ia);return b}function F(a,b,c,d,e){try{var f=g.exec(a.nodeValue);if(f){var h=wa(f[1]);X(b,h,"M",d,e)&&(c[h]=U(f[2]))}}catch(k){}}
|
||||
function V(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw $("uterdir",b,c);1===a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return x(d)}function Y(a,b,c){return function(d,e,f,g,h){e=V(e[0],b,c);return a(d,e,f,g,h)}}function Z(a,b,c,d,e,f){var g;return a?da(b,c,d,e,f):function(){g||(g=da(b,c,d,e,f),b=c=f=null);return g.apply(this,arguments)}}function aa(a,b,d,e,f,g,h,k,l){function m(a,b,c,d){if(a){c&&(a=
|
||||
Y(a,c,d));a.require=u.require;a.directiveName=Q;if(s===u||u.$$isolateScope)a=Aa(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=Y(b,c,d));b.require=u.require;b.directiveName=Q;if(s===u||u.$$isolateScope)b=Aa(b,{isolateScope:!0});k.push(b)}}function p(a,e,f,g,l){function m(a,b,c,d){var e;bb(a)||(d=c,c=b,b=a,a=void 0);N&&(e=P);c||(c=N?Q.parent():Q);if(d){var f=l.$$slots[d];if(f)return f(a,b,e,c,R);if(z(f))throw $("noslot",d,za(Q));}else return l(a,b,e,c,R)}var n,u,L,y,G,P,M,Q;b===f?(g=d,Q=d.$$element):(Q=
|
||||
x(f),g=new w(Q,d));G=e;s?y=e.$new(!0):t&&(G=e.$parent);l&&(M=m,M.$$boundTransclude=l,M.isSlotFilled=function(a){return!!l.$$slots[a]});J&&(P=ea(Q,g,M,J,y,e,s));s&&(da.$$addScopeInfo(Q,y,!0,!(v&&(v===s||v===s.$$originalDirective))),da.$$addScopeClass(Q,!0),y.$$isolateBindings=s.$$isolateBindings,u=Da(e,g,y,y.$$isolateBindings,s),u.removeWatches&&y.$on("$destroy",u.removeWatches));for(n in P){u=J[n];L=P[n];var Cg=u.$$bindings.bindToController;L.instance=L();Q.data("$"+u.name+"Controller",L.instance);
|
||||
L.bindingInfo=Da(G,g,L.instance,Cg,u)}r(J,function(a,b){var c=a.require;a.bindToController&&!H(c)&&D(c)&&S(P[b].instance,W(b,c,Q,P))});r(P,function(a){var b=a.instance;if(B(b.$onChanges))try{b.$onChanges(a.bindingInfo.initialChanges)}catch(d){c(d)}if(B(b.$onInit))try{b.$onInit()}catch(e){c(e)}B(b.$doCheck)&&(G.$watch(function(){b.$doCheck()}),b.$doCheck());B(b.$onDestroy)&&G.$on("$destroy",function(){b.$onDestroy()})});n=0;for(u=h.length;n<u;n++)L=h[n],Ba(L,L.isolateScope?y:e,Q,g,L.require&&W(L.directiveName,
|
||||
L.require,Q,P),M);var R=e;s&&(s.template||null===s.templateUrl)&&(R=y);a&&a(R,f.childNodes,void 0,l);for(n=k.length-1;0<=n;n--)L=k[n],Ba(L,L.isolateScope?y:e,Q,g,L.require&&W(L.directiveName,L.require,Q,P),M);r(P,function(a){a=a.instance;B(a.$postLink)&&a.$postLink()})}l=l||{};for(var n=-Number.MAX_VALUE,t=l.newScopeDirective,J=l.controllerDirectives,s=l.newIsolateScopeDirective,v=l.templateDirective,L=l.nonTlbTranscludeDirective,G=!1,P=!1,N=l.hasElementTranscludeDirective,y=d.$$element=x(b),u,Q,
|
||||
M,R=e,q,ma=!1,Ib=!1,O,sa=0,A=a.length;sa<A;sa++){u=a[sa];var E=u.$$start,ib=u.$$end;E&&(y=V(b,E,ib));M=void 0;if(n>u.priority)break;if(O=u.scope)u.templateUrl||(D(O)?(ba("new/isolated scope",s||t,u,y),s=u):ba("new/isolated scope",s,u,y)),t=t||u;Q=u.name;if(!ma&&(u.replace&&(u.templateUrl||u.template)||u.transclude&&!u.$$tlb)){for(O=sa+1;ma=a[O++];)if(ma.transclude&&!ma.$$tlb||ma.replace&&(ma.templateUrl||ma.template)){Ib=!0;break}ma=!0}!u.templateUrl&&u.controller&&(J=J||T(),ba("'"+Q+"' controller",
|
||||
J[Q],u,y),J[Q]=u);if(O=u.transclude)if(G=!0,u.$$tlb||(ba("transclusion",L,u,y),L=u),"element"===O)N=!0,n=u.priority,M=y,y=d.$$element=x(da.$$createComment(Q,d[Q])),b=y[0],pa(f,Ha.call(M,0),b),R=Z(Ib,M,e,n,g&&g.name,{nonTlbTranscludeDirective:L});else{var ka=T();if(D(O)){M=C.document.createDocumentFragment();var Xa=T(),F=T();r(O,function(a,b){var c="?"===a.charAt(0);a=c?a.substring(1):a;Xa[a]=b;ka[b]=null;F[b]=c});r(y.contents(),function(a){var b=Xa[wa(ua(a))];b?(F[b]=!0,ka[b]=ka[b]||C.document.createDocumentFragment(),
|
||||
ka[b].appendChild(a)):M.appendChild(a)});r(F,function(a,b){if(!a)throw $("reqslot",b);});for(var K in ka)ka[K]&&(R=x(ka[K].childNodes),ka[K]=Z(Ib,R,e));M=x(M.childNodes)}else M=x(pc(b)).contents();y.empty();R=Z(Ib,M,e,void 0,void 0,{needsNewScope:u.$$isolateScope||u.$$newScope});R.$$slots=ka}if(u.template)if(P=!0,ba("template",v,u,y),v=u,O=B(u.template)?u.template(y,d):u.template,O=Na(O),u.replace){g=u;M=mc.test(O)?rd(ja(u.templateNamespace,U(O))):[];b=M[0];if(1!==M.length||1!==b.nodeType)throw $("tplrt",
|
||||
Q,"");pa(f,y,b);A={$attr:{}};O=sc(b,[],A);var Dg=a.splice(sa+1,a.length-(sa+1));(s||t)&&fa(O,s,t);a=a.concat(O).concat(Dg);ga(d,A);A=a.length}else y.html(O);if(u.templateUrl)P=!0,ba("template",v,u,y),v=u,u.replace&&(g=u),p=ha(a.splice(sa,a.length-sa),y,d,f,G&&R,h,k,{controllerDirectives:J,newScopeDirective:t!==u&&t,newIsolateScopeDirective:s,templateDirective:v,nonTlbTranscludeDirective:L}),A=a.length;else if(u.compile)try{q=u.compile(y,d,R);var X=u.$$originalDirective||u;B(q)?m(null,Va(X,q),E,ib):
|
||||
q&&m(Va(X,q.pre),Va(X,q.post),E,ib)}catch(ca){c(ca,za(y))}u.terminal&&(p.terminal=!0,n=Math.max(n,u.priority))}p.scope=t&&!0===t.scope;p.transcludeOnThisElement=G;p.templateOnThisElement=P;p.transclude=R;l.hasElementTranscludeDirective=N;return p}function W(a,b,c,d){var e;if(A(b)){var f=b.match(l);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;if(!e){var h="$"+b+"Controller";e="^^"===g&&c[0]&&9===c[0].nodeType?null:g?c.inheritedData(h):c.data(h)}if(!e&&
|
||||
!f)throw $("ctreq",b,a);}else if(H(b))for(e=[],g=0,f=b.length;g<f;g++)e[g]=W(a,b[g],c,d);else D(b)&&(e={},r(b,function(b,f){e[f]=W(a,b,c,d)}));return e||null}function ea(a,b,c,d,e,f,g){var h=T(),k;for(k in d){var l=d[k],m={$scope:l===g||l.$$isolateScope?e:f,$element:a,$attrs:b,$transclude:c},p=l.controller;"@"===p&&(p=b[l.name]);m=M(p,m,!0,l.controllerAs);h[l.name]=m;a.data("$"+l.name+"Controller",m.instance)}return h}function fa(a,b,c){for(var d=0,e=a.length;d<e;d++)a[d]=ac(a[d],{$$isolateScope:b,
|
||||
$$newScope:c})}function X(b,c,e,g,h,k,l){if(c===h)return null;var m=null;if(f.hasOwnProperty(c)){h=a.get(c+"Directive");for(var p=0,n=h.length;p<n;p++)if(c=h[p],(z(g)||g>c.priority)&&-1!==c.restrict.indexOf(e)){k&&(c=ac(c,{$$start:k,$$end:l}));if(!c.$$bindings){var I=m=c,t=c.name,u={isolateScope:null,bindToController:null};D(I.scope)&&(!0===I.bindToController?(u.bindToController=d(I.scope,t,!0),u.isolateScope={}):u.isolateScope=d(I.scope,t,!1));D(I.bindToController)&&(u.bindToController=d(I.bindToController,
|
||||
t,!0));if(u.bindToController&&!I.controller)throw $("noctrl",t);m=m.$$bindings=u;D(m.isolateScope)&&(c.$$isolateBindings=m.isolateScope)}b.push(c);m=c}}return m}function ca(b){if(f.hasOwnProperty(b))for(var c=a.get(b+"Directive"),d=0,e=c.length;d<e;d++)if(b=c[d],b.multiElement)return!0;return!1}function ga(a,b){var c=b.$attr,d=a.$attr;r(a,function(d,e){"$"!==e.charAt(0)&&(b[e]&&b[e]!==d&&(d=d.length?d+(("style"===e?";":" ")+b[e]):b[e]),a.$set(e,d,!0,c[e]))});r(b,function(b,e){a.hasOwnProperty(e)||
|
||||
"$"===e.charAt(0)||(a[e]=b,"class"!==e&&"style"!==e&&(d[e]=c[e]))})}function ha(a,b,d,f,g,h,k,l){var m=[],p,n,t=b[0],u=a.shift(),J=ac(u,{templateUrl:null,transclude:null,replace:null,$$originalDirective:u}),s=B(u.templateUrl)?u.templateUrl(b,d):u.templateUrl,L=u.templateNamespace;b.empty();e(s).then(function(c){var e,I;c=Na(c);if(u.replace){c=mc.test(c)?rd(ja(L,U(c))):[];e=c[0];if(1!==c.length||1!==e.nodeType)throw $("tplrt",u.name,s);c={$attr:{}};pa(f,b,e);var v=sc(e,[],c);D(u.scope)&&fa(v,!0);a=
|
||||
v.concat(a);ga(d,c)}else e=t,b.html(c);a.unshift(J);p=aa(a,e,d,g,b,u,h,k,l);r(f,function(a,c){a===e&&(f[c]=b[0])});for(n=Xa(b[0].childNodes,g);m.length;){c=m.shift();I=m.shift();var y=m.shift(),P=m.shift(),v=b[0];if(!c.$$destroyed){if(I!==t){var G=I.className;l.hasElementTranscludeDirective&&u.replace||(v=pc(e));pa(y,x(I),v);sa(x(v),G)}I=p.transcludeOnThisElement?ka(c,p.transclude,P):P;p(n,c,v,f,I)}}m=null}).catch(function(a){cc(a)&&c(a)});return function(a,b,c,d,e){a=e;b.$$destroyed||(m?m.push(b,
|
||||
c,d,a):(p.transcludeOnThisElement&&(a=ka(b,p.transclude,e)),p(n,b,c,d,a)))}}function ia(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function ba(a,b,c,d){function e(a){return a?" (module: "+a+")":""}if(b)throw $("multidir",b.name,e(b.$$moduleName),c.name,e(c.$$moduleName),a,za(d));}function na(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&da.$$addBindingClass(a);return function(a,c){var e=c.parent();
|
||||
b||da.$$addBindingClass(e);da.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function ja(a,b){a=K(a||"html");switch(a){case "svg":case "math":var c=C.document.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}function oa(a,b){if("srcdoc"===b)return u.HTML;if("src"===b||"ngSrc"===b)return-1===["img","video","audio","source","track"].indexOf(a)?u.RESOURCE_URL:u.MEDIA_URL;if("xlinkHref"===b)return"image"===a?u.MEDIA_URL:
|
||||
"a"===a?u.URL:u.RESOURCE_URL;if("form"===a&&"action"===b||"base"===a&&"href"===b||"link"===a&&"href"===b)return u.RESOURCE_URL;if("a"===a&&("href"===b||"ngHref"===b))return u.URL}function xa(a,b){var c=b.toLowerCase();return v[a+"|"+c]||v["*|"+c]}function ya(a){return ma(u.valueOf(a),"ng-prop-srcset")}function Ea(a,b,c,d){if(m.test(d))throw $("nodomevents");a=ua(a);var e=xa(a,d),f=Ta;"srcset"!==d||"img"!==a&&"source"!==a?e&&(f=u.getTrusted.bind(u,e)):f=ya;b.push({priority:100,compile:function(a,b){var e=
|
||||
p(b[c]),g=p(b[c],function(a){return u.valueOf(a)});return{pre:function(a,b){function c(){var g=e(a);b[0][d]=f(g)}c();a.$watch(g,c)}}}})}function Ia(a,c,d,e,f){var g=ua(a),k=oa(g,e),l=h[e]||f,p=b(d,!f,k,l);if(p){if("multiple"===e&&"select"===g)throw $("selmulti",za(a));if(m.test(e))throw $("nodomevents");c.push({priority:100,compile:function(){return{pre:function(a,c,f){c=f.$$observers||(f.$$observers=T());var g=f[e];g!==d&&(p=g&&b(g,!0,k,l),d=g);p&&(f[e]=p(a),(c[e]||(c[e]=[])).$$inter=!0,(f.$$observers&&
|
||||
f.$$observers[e].$$scope||a).$watch(p,function(a,b){"class"===e&&a!==b?f.$updateClass(a,b):f.$set(e,a)}))}}}})}}function pa(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]===d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context=c);break}f&&f.replaceChild(c,d);a=C.document.createDocumentFragment();for(g=0;g<e;g++)a.appendChild(b[g]);x.hasData(d)&&(x.data(c,x.data(d)),x(d).off("$destroy"));x.cleanData(a.querySelectorAll("*"));
|
||||
for(g=1;g<e;g++)delete b[g];b[0]=c;b.length=1}function Aa(a,b){return S(function(){return a.apply(null,arguments)},a,b)}function Ba(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,za(d))}}function ra(a,b){if(s)throw $("missingattr",a,b);}function Da(a,c,d,e,f){function g(b,c,e){B(d.$onChanges)&&!dc(c,e)&&(Ua||(a.$$postDigest(q),Ua=[]),m||(m={},Ua.push(h)),m[b]&&(e=m[b].previousValue),m[b]=new Jb(e,c))}function h(){d.$onChanges(m);m=void 0}var k=[],l={},m;r(e,function(e,h){var m=e.attrName,n=e.optional,
|
||||
I,t,u,s;switch(e.mode){case "@":n||ta.call(c,m)||(ra(m,f.name),d[h]=c[m]=void 0);n=c.$observe(m,function(a){if(A(a)||Ga(a))g(h,a,d[h]),d[h]=a});c.$$observers[m].$$scope=a;I=c[m];A(I)?d[h]=b(I)(a):Ga(I)&&(d[h]=I);l[h]=new Jb(tc,d[h]);k.push(n);break;case "=":if(!ta.call(c,m)){if(n)break;ra(m,f.name);c[m]=void 0}if(n&&!c[m])break;t=p(c[m]);s=t.literal?va:dc;u=t.assign||function(){I=d[h]=t(a);throw $("nonassign",c[m],m,f.name);};I=d[h]=t(a);n=function(b){s(b,d[h])||(s(b,I)?u(a,b=d[h]):d[h]=b);return I=
|
||||
b};n.$stateful=!0;n=e.collection?a.$watchCollection(c[m],n):a.$watch(p(c[m],n),null,t.literal);k.push(n);break;case "<":if(!ta.call(c,m)){if(n)break;ra(m,f.name);c[m]=void 0}if(n&&!c[m])break;t=p(c[m]);var v=t.literal,L=d[h]=t(a);l[h]=new Jb(tc,d[h]);n=a[e.collection?"$watchCollection":"$watch"](t,function(a,b){if(b===a){if(b===L||v&&va(b,L))return;b=L}g(h,a,b);d[h]=a});k.push(n);break;case "&":n||ta.call(c,m)||ra(m,f.name);t=c.hasOwnProperty(m)?p(c[m]):E;if(t===E&&n)break;d[h]=function(b){return t(a,
|
||||
b)}}});return{initialChanges:l,removeWatches:k.length&&function(){for(var a=0,b=k.length;a<b;++a)k[a]()}}}var Ma=/^\w/,Fa=C.document.createElement("div"),Oa=t,Qa=N,Ja=G,Ua;w.prototype={$normalize:wa,$addClass:function(a){a&&0<a.length&&R.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&R.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=sd(a,b);c&&c.length&&R.addClass(this.$$element,c);(c=sd(b,a))&&c.length&&R.removeClass(this.$$element,c)},$set:function(a,b,d,e){var f=
|
||||
ld(this.$$element[0],a),g=td[a],h=a;f?(this.$$element.prop(a,b),e=f):g&&(this[g]=b,h=g);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=Vc(a,"-"));"img"===ua(this.$$element)&&"srcset"===a&&(this[a]=b=ma(b,"$set('srcset', value)"));!1!==d&&(null===b||z(b)?this.$$element.removeAttr(e):Ma.test(e)?f&&!1===b?this.$$element.removeAttr(e):this.$$element.attr(e,b):O(this.$$element[0],e,b));(a=this.$$observers)&&r(a[h],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,
|
||||
d=c.$$observers||(c.$$observers=T()),e=d[a]||(d[a]=[]);e.push(b);L.$evalAsync(function(){e.$$inter||!c.hasOwnProperty(a)||z(c[a])||b(c[a])});return function(){cb(e,b)}}};var Ka=b.startSymbol(),La=b.endSymbol(),Na="{{"===Ka&&"}}"===La?Ta:function(a){return a.replace(/\{\{/g,Ka).replace(/}}/g,La)},Ra=/^ng(Attr|Prop|On)([A-Z].*)$/,Sa=/^(.+)Start$/;da.$$addBindingInfo=n?function(a,b){var c=a.data("$binding")||[];H(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:E;da.$$addBindingClass=n?function(a){sa(a,
|
||||
"ng-binding")}:E;da.$$addScopeInfo=n?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:E;da.$$addScopeClass=n?function(a,b){sa(a,b?"ng-isolate-scope":"ng-scope")}:E;da.$$createComment=function(a,b){var c="";n&&(c=" "+(a||"")+": ",b&&(c+=b+" "));return C.document.createComment(c)};return da}]}function Jb(a,b){this.previousValue=a;this.currentValue=b}function wa(a){return a.replace(pd,"").replace(Eg,function(a,d,c){return c?d.toUpperCase():d})}function sd(a,b){var d=
|
||||
"",c=a.split(/\s+/),e=b.split(/\s+/),f=0;a:for(;f<c.length;f++){for(var g=c[f],k=0;k<e.length;k++)if(g===e[k])continue a;d+=(0<d.length?" ":"")+g}return d}function rd(a){a=x(a);var b=a.length;if(1>=b)return a;for(;b--;){var d=a[b];(8===d.nodeType||d.nodeType===Pa&&""===d.nodeValue.trim())&&Fg.call(a,b,1)}return a}function Bg(a,b){if(b&&A(b))return b;if(A(a)){var d=ud.exec(a);if(d)return d[3]}}function Ff(){var a={};this.has=function(b){return a.hasOwnProperty(b)};this.register=function(b,d){Ja(b,
|
||||
"controller");D(b)?S(a,b):a[b]=d};this.$get=["$injector",function(b){function d(a,b,d,g){if(!a||!D(a.$scope))throw F("$controller")("noscp",g,b);a.$scope[b]=d}return function(c,e,f,g){var k,h,l;f=!0===f;g&&A(g)&&(l=g);if(A(c)){g=c.match(ud);if(!g)throw vd("ctrlfmt",c);h=g[1];l=l||g[3];c=a.hasOwnProperty(h)?a[h]:Ge(e.$scope,h,!0);if(!c)throw vd("ctrlreg",h);sb(c,h,!0)}if(f)return f=(H(c)?c[c.length-1]:c).prototype,k=Object.create(f||null),l&&d(e,l,k,h||c.name),S(function(){var a=b.invoke(c,k,e,h);
|
||||
a!==k&&(D(a)||B(a))&&(k=a,l&&d(e,l,k,h||c.name));return k},{instance:k,identifier:l});k=b.instantiate(c,e,h);l&&d(e,l,k,h||c.name);return k}}]}function Gf(){this.$get=["$window",function(a){return x(a.document)}]}function Hf(){this.$get=["$document","$rootScope",function(a,b){function d(){e=c.hidden}var c=a[0],e=c&&c.hidden;a.on("visibilitychange",d);b.$on("$destroy",function(){a.off("visibilitychange",d)});return function(){return e}}]}function If(){this.$get=["$log",function(a){return function(b,
|
||||
d){a.error.apply(a,arguments)}}]}function uc(a){return D(a)?ha(a)?a.toISOString():eb(a):a}function Of(){this.$get=function(){return function(a){if(!a)return"";var b=[];Oc(a,function(a,c){null===a||z(a)||B(a)||(H(a)?r(a,function(a){b.push(ba(c)+"="+ba(uc(a)))}):b.push(ba(c)+"="+ba(uc(a))))});return b.join("&")}}}function Pf(){this.$get=function(){return function(a){function b(a,e,f){H(a)?r(a,function(a,c){b(a,e+"["+(D(a)?c:"")+"]")}):D(a)&&!ha(a)?Oc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}):
|
||||
(B(a)&&(a=a()),d.push(ba(e)+"="+(null==a?"":ba(uc(a)))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function vc(a,b){if(A(a)){var d=a.replace(Gg,"").trim();if(d){var c=b("Content-Type"),c=c&&0===c.indexOf(wd),e;(e=c)||(e=(e=d.match(Hg))&&Ig[e[0]].test(d));if(e)try{a=Rc(d)}catch(f){if(!c)return a;throw Kb("baddata",a,f);}}}return a}function xd(a){var b=T(),d;A(a)?r(a.split("\n"),function(a){d=a.indexOf(":");var e=K(U(a.substr(0,d)));a=U(a.substr(d+1));e&&(b[e]=b[e]?b[e]+", "+a:a)}):D(a)&&
|
||||
r(a,function(a,d){var f=K(d),g=U(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function yd(a){var b;return function(d){b||(b=xd(a));return d?(d=b[K(d)],void 0===d&&(d=null),d):b}}function zd(a,b,d,c){if(B(c))return c(a,b,d);r(c,function(c){a=c(a,b,d)});return a}function Nf(){var a=this.defaults={transformResponse:[vc],transformRequest:[function(a){return D(a)&&"[object File]"!==la.call(a)&&"[object Blob]"!==la.call(a)&&"[object FormData]"!==la.call(a)?eb(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},
|
||||
post:ja(wc),put:ja(wc),patch:ja(wc)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer",jsonpCallbackParam:"callback"},b=!1;this.useApplyAsync=function(a){return w(a)?(b=!!a,this):b};var d=this.interceptors=[],c=this.xsrfWhitelistedOrigins=[];this.$get=["$browser","$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector","$sce",function(e,f,g,k,h,l,m,p){function n(b){function c(a,b){for(var d=0,e=b.length;d<e;){var f=b[d++],g=b[d++];
|
||||
a=a.then(f,g)}b.length=0;return a}function d(a,b){var c,e={};r(a,function(a,d){B(a)?(c=a(b),null!=c&&(e[d]=c)):e[d]=a});return e}function f(a){var b=S({},a);b.data=zd(a.data,a.headers,a.status,g.transformResponse);a=a.status;return 200<=a&&300>a?b:l.reject(b)}if(!D(b))throw F("$http")("badreq",b);if(!A(p.valueOf(b.url)))throw F("$http")("badreq",b.url);var g=S({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer,jsonpCallbackParam:a.jsonpCallbackParam},
|
||||
b);g.headers=function(b){var c=a.headers,e=S({},b.headers),f,g,h,c=S({},c.common,c[K(b.method)]);a:for(f in c){g=K(f);for(h in e)if(K(h)===g)continue a;e[f]=c[f]}return d(e,ja(b))}(b);g.method=ub(g.method);g.paramSerializer=A(g.paramSerializer)?m.get(g.paramSerializer):g.paramSerializer;e.$$incOutstandingRequestCount("$http");var h=[],k=[];b=l.resolve(g);r(v,function(a){(a.request||a.requestError)&&h.unshift(a.request,a.requestError);(a.response||a.responseError)&&k.push(a.response,a.responseError)});
|
||||
b=c(b,h);b=b.then(function(b){var c=b.headers,d=zd(b.data,yd(c),void 0,b.transformRequest);z(d)&&r(c,function(a,b){"content-type"===K(b)&&delete c[b]});z(b.withCredentials)&&!z(a.withCredentials)&&(b.withCredentials=a.withCredentials);return s(b,d).then(f,f)});b=c(b,k);return b=b.finally(function(){e.$$completeOutstandingRequest(E,"$http")})}function s(c,d){function e(a){if(a){var c={};r(a,function(a,d){c[d]=function(c){function d(){a(c)}b?h.$applyAsync(d):h.$$phase?d():h.$apply(d)}});return c}}function k(a,
|
||||
c,d,e,f){function g(){m(c,a,d,e,f)}R&&(200<=a&&300>a?R.put(O,[a,c,xd(d),e,f]):R.remove(O));b?h.$applyAsync(g):(g(),h.$$phase||h.$apply())}function m(a,b,d,e,f){b=-1<=b?b:0;(200<=b&&300>b?L.resolve:L.reject)({data:a,status:b,headers:yd(d),config:c,statusText:e,xhrStatus:f})}function s(a){m(a.data,a.status,ja(a.headers()),a.statusText,a.xhrStatus)}function v(){var a=n.pendingRequests.indexOf(c);-1!==a&&n.pendingRequests.splice(a,1)}var L=l.defer(),u=L.promise,R,q,ma=c.headers,x="jsonp"===K(c.method),
|
||||
O=c.url;x?O=p.getTrustedResourceUrl(O):A(O)||(O=p.valueOf(O));O=G(O,c.paramSerializer(c.params));x&&(O=t(O,c.jsonpCallbackParam));n.pendingRequests.push(c);u.then(v,v);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(R=D(c.cache)?c.cache:D(a.cache)?a.cache:N);R&&(q=R.get(O),w(q)?q&&B(q.then)?q.then(s,s):H(q)?m(q[1],q[0],ja(q[2]),q[3],q[4]):m(q,200,{},"OK","complete"):R.put(O,u));z(q)&&((q=jc(c.url)?g()[c.xsrfCookieName||a.xsrfCookieName]:void 0)&&(ma[c.xsrfHeaderName||a.xsrfHeaderName]=
|
||||
q),f(c.method,O,d,k,ma,c.timeout,c.withCredentials,c.responseType,e(c.eventHandlers),e(c.uploadEventHandlers)));return u}function G(a,b){0<b.length&&(a+=(-1===a.indexOf("?")?"?":"&")+b);return a}function t(a,b){var c=a.split("?");if(2<c.length)throw Kb("badjsonp",a);c=gc(c[1]);r(c,function(c,d){if("JSON_CALLBACK"===c)throw Kb("badjsonp",a);if(d===b)throw Kb("badjsonp",b,a);});return a+=(-1===a.indexOf("?")?"?":"&")+b+"=JSON_CALLBACK"}var N=k("$http");a.paramSerializer=A(a.paramSerializer)?m.get(a.paramSerializer):
|
||||
a.paramSerializer;var v=[];r(d,function(a){v.unshift(A(a)?m.get(a):m.invoke(a))});var jc=Jg(c);n.pendingRequests=[];(function(a){r(arguments,function(a){n[a]=function(b,c){return n(S({},c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){r(arguments,function(a){n[a]=function(b,c,d){return n(S({},d||{},{method:a,url:b,data:c}))}})})("post","put","patch");n.defaults=a;return n}]}function Rf(){this.$get=function(){return function(){return new C.XMLHttpRequest}}}function Qf(){this.$get=
|
||||
["$browser","$jsonpCallbacks","$document","$xhrFactory",function(a,b,d,c){return Kg(a,c,a.defer,b,d[0])}]}function Kg(a,b,d,c,e){function f(a,b,d){a=a.replace("JSON_CALLBACK",b);var f=e.createElement("script"),m=null;f.type="text/javascript";f.src=a;f.async=!0;m=function(a){f.removeEventListener("load",m);f.removeEventListener("error",m);e.body.removeChild(f);f=null;var g=-1,s="unknown";a&&("load"!==a.type||c.wasCalled(b)||(a={type:"error"}),s=a.type,g="error"===a.type?404:200);d&&d(g,s)};f.addEventListener("load",
|
||||
m);f.addEventListener("error",m);e.body.appendChild(f);return m}return function(e,k,h,l,m,p,n,s,G,t){function N(a){J="timeout"===a;qa&&qa();y&&y.abort()}function v(a,b,c,e,f,g){w(P)&&d.cancel(P);qa=y=null;a(b,c,e,f,g)}k=k||a.url();if("jsonp"===K(e))var q=c.createCallback(k),qa=f(k,q,function(a,b){var d=200===a&&c.getResponse(q);v(l,a,d,"",b,"complete");c.removeCallback(q)});else{var y=b(e,k),J=!1;y.open(e,k,!0);r(m,function(a,b){w(a)&&y.setRequestHeader(b,a)});y.onload=function(){var a=y.statusText||
|
||||
"",b="response"in y?y.response:y.responseText,c=1223===y.status?204:y.status;0===c&&(c=b?200:"file"===ga(k).protocol?404:0);v(l,c,b,y.getAllResponseHeaders(),a,"complete")};y.onerror=function(){v(l,-1,null,null,"","error")};y.ontimeout=function(){v(l,-1,null,null,"","timeout")};y.onabort=function(){v(l,-1,null,null,"",J?"timeout":"abort")};r(G,function(a,b){y.addEventListener(b,a)});r(t,function(a,b){y.upload.addEventListener(b,a)});n&&(y.withCredentials=!0);if(s)try{y.responseType=s}catch(I){if("json"!==
|
||||
s)throw I;}y.send(z(h)?null:h)}if(0<p)var P=d(function(){N("timeout")},p);else p&&B(p.then)&&p.then(function(){N(w(p.$$timeoutId)?"timeout":"abort")})}}function Kf(){var a="{{",b="}}";this.startSymbol=function(b){return b?(a=b,this):a};this.endSymbol=function(a){return a?(b=a,this):b};this.$get=["$parse","$exceptionHandler","$sce",function(d,c,e){function f(a){return"\\\\\\"+a}function g(c){return c.replace(p,a).replace(n,b)}function k(a,b,c,d){var e=a.$watch(function(a){e();return d(a)},b,c);return e}
|
||||
function h(f,h,n,p){function v(a){try{return a=n&&!r?e.getTrusted(n,a):e.valueOf(a),p&&!w(a)?a:ic(a)}catch(b){c(Ma.interr(f,b))}}var r=n===e.URL||n===e.MEDIA_URL;if(!f.length||-1===f.indexOf(a)){if(h)return;h=g(f);r&&(h=e.getTrusted(n,h));h=ia(h);h.exp=f;h.expressions=[];h.$$watchDelegate=k;return h}p=!!p;for(var q,y,J=0,I=[],P,Q=f.length,M=[],L=[],u;J<Q;)if(-1!==(q=f.indexOf(a,J))&&-1!==(y=f.indexOf(b,q+l)))J!==q&&M.push(g(f.substring(J,q))),J=f.substring(q+l,y),I.push(J),J=y+m,L.push(M.length),
|
||||
M.push("");else{J!==Q&&M.push(g(f.substring(J)));break}u=1===M.length&&1===L.length;var R=r&&u?void 0:v;P=I.map(function(a){return d(a,R)});if(!h||I.length){var x=function(a){for(var b=0,c=I.length;b<c;b++){if(p&&z(a[b]))return;M[L[b]]=a[b]}if(r)return e.getTrusted(n,u?M[0]:M.join(""));n&&1<M.length&&Ma.throwNoconcat(f);return M.join("")};return S(function(a){var b=0,d=I.length,e=Array(d);try{for(;b<d;b++)e[b]=P[b](a);return x(e)}catch(g){c(Ma.interr(f,g))}},{exp:f,expressions:I,$$watchDelegate:function(a,
|
||||
b){var c;return a.$watchGroup(P,function(d,e){var f=x(d);b.call(this,f,d!==e?c:f,a);c=f})}})}}var l=a.length,m=b.length,p=new RegExp(a.replace(/./g,f),"g"),n=new RegExp(b.replace(/./g,f),"g");h.startSymbol=function(){return a};h.endSymbol=function(){return b};return h}]}function Lf(){this.$get=["$$intervalFactory","$window",function(a,b){var d={},c=function(a){b.clearInterval(a);delete d[a]},e=a(function(a,c,e){a=b.setInterval(a,c);d[a]=e;return a},c);e.cancel=function(a){if(!a)return!1;if(!a.hasOwnProperty("$$intervalId"))throw Lg("badprom");
|
||||
if(!d.hasOwnProperty(a.$$intervalId))return!1;a=a.$$intervalId;var b=d[a],e=b.promise;e.$$state&&(e.$$state.pur=!0);b.reject("canceled");c(a);return!0};return e}]}function Mf(){this.$get=["$browser","$q","$$q","$rootScope",function(a,b,d,c){return function(e,f){return function(g,k,h,l){function m(){p?g.apply(null,n):g(s)}var p=4<arguments.length,n=p?Ha.call(arguments,4):[],s=0,G=w(l)&&!l,t=(G?d:b).defer(),r=t.promise;h=w(h)?h:0;r.$$intervalId=e(function(){G?a.defer(m):c.$evalAsync(m);t.notify(s++);
|
||||
0<h&&s>=h&&(t.resolve(s),f(r.$$intervalId));G||c.$apply()},k,t,G);return r}}}]}function Ad(a,b){var d=ga(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=fa(d.port)||Mg[d.protocol]||null}function Bd(a,b,d){if(Ng.test(a))throw jb("badpath",a);var c="/"!==a.charAt(0);c&&(a="/"+a);a=ga(a);for(var c=(c&&"/"===a.pathname.charAt(0)?a.pathname.substring(1):a.pathname).split("/"),e=c.length;e--;)c[e]=decodeURIComponent(c[e]),d&&(c[e]=c[e].replace(/\//g,"%2F"));d=c.join("/");b.$$path=d;b.$$search=gc(a.search);
|
||||
b.$$hash=decodeURIComponent(a.hash);b.$$path&&"/"!==b.$$path.charAt(0)&&(b.$$path="/"+b.$$path)}function xc(a,b){return a.slice(0,b.length)===b}function xa(a,b){if(xc(b,a))return b.substr(a.length)}function Da(a){var b=a.indexOf("#");return-1===b?a:a.substr(0,b)}function yc(a,b,d){this.$$html5=!0;d=d||"";Ad(a,this);this.$$parse=function(a){var d=xa(b,a);if(!A(d))throw jb("ipthprfx",a,b);Bd(d,this,!0);this.$$path||(this.$$path="/");this.$$compose()};this.$$normalizeUrl=function(a){return b+a.substr(1)};
|
||||
this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;w(f=xa(a,c))?(g=f,g=d&&w(f=xa(d,f))?b+(xa("/",f)||f):a+g):w(f=xa(b,c))?g=b+f:b===c+"/"&&(g=b);g&&this.$$parse(g);return!!g}}function zc(a,b,d){Ad(a,this);this.$$parse=function(c){var e=xa(a,c)||xa(b,c),f;z(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",z(e)&&(a=c,this.replace())):(f=xa(d,e),z(f)&&(f=e));Bd(f,this,!1);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;xc(f,e)&&(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))?
|
||||
f[1]:c);this.$$path=c;this.$$compose()};this.$$normalizeUrl=function(b){return a+(b?d+b:"")};this.$$parseLinkUrl=function(b,d){return Da(a)===Da(b)?(this.$$parse(b),!0):!1}}function Cd(a,b,d){this.$$html5=!0;zc.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a===Da(c)?f=c:(g=xa(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$normalizeUrl=function(b){return a+d+b}}function Lb(a){return function(){return this[a]}}function Dd(a,
|
||||
b){return function(d){if(z(d))return this[a];this[a]=b(d);this.$$compose();return this}}function Tf(){var a="!",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return w(b)?(a=b,this):a};this.html5Mode=function(a){if(Ga(a))return b.enabled=a,this;if(D(a)){Ga(a.enabled)&&(b.enabled=a.enabled);Ga(a.requireBase)&&(b.requireBase=a.requireBase);if(Ga(a.rewriteLinks)||A(a.rewriteLinks))b.rewriteLinks=a.rewriteLinks;return this}return b};this.$get=["$rootScope","$browser","$sniffer",
|
||||
"$rootElement","$window",function(d,c,e,f,g){function k(a,b){return a===b||ga(a).href===ga(b).href}function h(a,b,d){var e=m.url(),f=m.$$state;try{c.url(a,b,d),m.$$state=c.state()}catch(g){throw m.url(e),m.$$state=f,g;}}function l(a,b){d.$broadcast("$locationChangeSuccess",m.absUrl(),a,m.$$state,b)}var m,p;p=c.baseHref();var n=c.url(),s;if(b.enabled){if(!p&&b.requireBase)throw jb("nobase");s=n.substring(0,n.indexOf("/",n.indexOf("//")+2))+(p||"/");p=e.history?yc:Cd}else s=Da(n),p=zc;var r=s.substr(0,
|
||||
Da(s).lastIndexOf("/")+1);m=new p(s,r,"#"+a);m.$$parseLinkUrl(n,n);m.$$state=c.state();var t=/^\s*(javascript|mailto):/i;f.on("click",function(a){var e=b.rewriteLinks;if(e&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!==a.which&&2!==a.button){for(var g=x(a.target);"a"!==ua(g[0]);)if(g[0]===f[0]||!(g=g.parent())[0])return;if(!A(e)||!z(g.attr(e))){var e=g.prop("href"),h=g.attr("href")||g.attr("xlink:href");D(e)&&"[object SVGAnimatedString]"===e.toString()&&(e=ga(e.animVal).href);t.test(e)||!e||g.attr("target")||
|
||||
a.isDefaultPrevented()||!m.$$parseLinkUrl(e,h)||(a.preventDefault(),m.absUrl()!==c.url()&&d.$apply())}}});m.absUrl()!==n&&c.url(m.absUrl(),!0);var N=!0;c.onUrlChange(function(a,b){xc(a,r)?(d.$evalAsync(function(){var c=m.absUrl(),e=m.$$state,f;m.$$parse(a);m.$$state=b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;m.absUrl()===a&&(f?(m.$$parse(c),m.$$state=e,h(c,!1,e)):(N=!1,l(c,e)))}),d.$$phase||d.$digest()):g.location.href=a});d.$watch(function(){if(N||m.$$urlUpdatedByLocation){m.$$urlUpdatedByLocation=
|
||||
!1;var a=c.url(),b=m.absUrl(),f=c.state(),g=m.$$replace,n=!k(a,b)||m.$$html5&&e.history&&f!==m.$$state;if(N||n)N=!1,d.$evalAsync(function(){var b=m.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,m.$$state,f).defaultPrevented;m.absUrl()===b&&(c?(m.$$parse(a),m.$$state=f):(n&&h(b,g,f===m.$$state?null:m.$$state),l(a,f)))})}m.$$replace=!1});return m}]}function Uf(){var a=!0,b=this;this.debugEnabled=function(b){return w(b)?(a=b,this):a};this.$get=["$window",function(d){function c(a){cc(a)&&(a.stack&&
|
||||
f?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=d.console||{},e=b[a]||b.log||E;return function(){var a=[];r(arguments,function(b){a.push(c(b))});return Function.prototype.apply.call(e,b,a)}}var f=Ca||/\bEdge\//.test(d.navigator&&d.navigator.userAgent);return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){a&&c.apply(b,
|
||||
arguments)}}()}}]}function Og(a){return a+""}function Pg(a,b){return"undefined"!==typeof a?a:b}function Ed(a,b){return"undefined"===typeof a?b:"undefined"===typeof b?a:a+b}function Qg(a,b){switch(a.type){case q.MemberExpression:if(a.computed)return!1;break;case q.UnaryExpression:return 1;case q.BinaryExpression:return"+"!==a.operator?1:!1;case q.CallExpression:return!1}return void 0===b?Fd:b}function Z(a,b,d){var c,e,f=a.isPure=Qg(a,d);switch(a.type){case q.Program:c=!0;r(a.body,function(a){Z(a.expression,
|
||||
b,f);c=c&&a.expression.constant});a.constant=c;break;case q.Literal:a.constant=!0;a.toWatch=[];break;case q.UnaryExpression:Z(a.argument,b,f);a.constant=a.argument.constant;a.toWatch=a.argument.toWatch;break;case q.BinaryExpression:Z(a.left,b,f);Z(a.right,b,f);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case q.LogicalExpression:Z(a.left,b,f);Z(a.right,b,f);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case q.ConditionalExpression:Z(a.test,
|
||||
b,f);Z(a.alternate,b,f);Z(a.consequent,b,f);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case q.Identifier:a.constant=!1;a.toWatch=[a];break;case q.MemberExpression:Z(a.object,b,f);a.computed&&Z(a.property,b,f);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=a.constant?[]:[a];break;case q.CallExpression:c=d=a.filter?!b(a.callee.name).$stateful:!1;e=[];r(a.arguments,function(a){Z(a,b,f);c=c&&a.constant;e.push.apply(e,
|
||||
a.toWatch)});a.constant=c;a.toWatch=d?e:[a];break;case q.AssignmentExpression:Z(a.left,b,f);Z(a.right,b,f);a.constant=a.left.constant&&a.right.constant;a.toWatch=[a];break;case q.ArrayExpression:c=!0;e=[];r(a.elements,function(a){Z(a,b,f);c=c&&a.constant;e.push.apply(e,a.toWatch)});a.constant=c;a.toWatch=e;break;case q.ObjectExpression:c=!0;e=[];r(a.properties,function(a){Z(a.value,b,f);c=c&&a.value.constant;e.push.apply(e,a.value.toWatch);a.computed&&(Z(a.key,b,!1),c=c&&a.key.constant,e.push.apply(e,
|
||||
a.key.toWatch))});a.constant=c;a.toWatch=e;break;case q.ThisExpression:a.constant=!1;a.toWatch=[];break;case q.LocalsExpression:a.constant=!1,a.toWatch=[]}}function Gd(a){if(1===a.length){a=a[0].expression;var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:void 0}}function Hd(a){return a.type===q.Identifier||a.type===q.MemberExpression}function Id(a){if(1===a.body.length&&Hd(a.body[0].expression))return{type:q.AssignmentExpression,left:a.body[0].expression,right:{type:q.NGValueParameter},operator:"="}}
|
||||
function Jd(a){this.$filter=a}function Kd(a){this.$filter=a}function Mb(a,b,d){this.ast=new q(a,d);this.astCompiler=d.csp?new Kd(b):new Jd(b)}function Ac(a){return B(a.valueOf)?a.valueOf():Rg.call(a)}function Vf(){var a=T(),b={"true":!0,"false":!1,"null":null,undefined:void 0},d,c;this.addLiteral=function(a,c){b[a]=c};this.setIdentifierFns=function(a,b){d=a;c=b;return this};this.$get=["$filter",function(e){function f(b,c){var d,f;switch(typeof b){case "string":return f=b=b.trim(),d=a[f],d||(d=new Nb(G),
|
||||
d=(new Mb(d,e,G)).parse(b),a[f]=p(d)),s(d,c);case "function":return s(b,c);default:return s(E,c)}}function g(a,b,c){return null==a||null==b?a===b:"object"!==typeof a||(a=Ac(a),"object"!==typeof a||c)?a===b||a!==a&&b!==b:!1}function k(a,b,c,d,e){var f=d.inputs,h;if(1===f.length){var k=g,f=f[0];return a.$watch(function(a){var b=f(a);g(b,k,f.isPure)||(h=d(a,void 0,void 0,[b]),k=b&&Ac(b));return h},b,c,e)}for(var l=[],m=[],n=0,p=f.length;n<p;n++)l[n]=g,m[n]=null;return a.$watch(function(a){for(var b=
|
||||
!1,c=0,e=f.length;c<e;c++){var k=f[c](a);if(b||(b=!g(k,l[c],f[c].isPure)))m[c]=k,l[c]=k&&Ac(k)}b&&(h=d(a,void 0,void 0,m));return h},b,c,e)}function h(a,b,c,d,e){function f(){h(m)&&k()}function g(a,b,c,d){m=u&&d?d[0]:n(a,b,c,d);h(m)&&a.$$postDigest(f);return s(m)}var h=d.literal?l:w,k,m,n=d.$$intercepted||d,s=d.$$interceptor||Ta,u=d.inputs&&!n.inputs;g.literal=d.literal;g.constant=d.constant;g.inputs=d.inputs;p(g);return k=a.$watch(g,b,c,e)}function l(a){var b=!0;r(a,function(a){w(a)||(b=!1)});return b}
|
||||
function m(a,b,c,d){var e=a.$watch(function(a){e();return d(a)},b,c);return e}function p(a){a.constant?a.$$watchDelegate=m:a.oneTime?a.$$watchDelegate=h:a.inputs&&(a.$$watchDelegate=k);return a}function n(a,b){function c(d){return b(a(d))}c.$stateful=a.$stateful||b.$stateful;c.$$pure=a.$$pure&&b.$$pure;return c}function s(a,b){if(!b)return a;a.$$interceptor&&(b=n(a.$$interceptor,b),a=a.$$intercepted);var c=!1,d=function(d,e,f,g){d=c&&g?g[0]:a(d,e,f,g);return b(d)};d.$$intercepted=a;d.$$interceptor=
|
||||
b;d.literal=a.literal;d.oneTime=a.oneTime;d.constant=a.constant;b.$stateful||(c=!a.inputs,d.inputs=a.inputs?a.inputs:[a],b.$$pure||(d.inputs=d.inputs.map(function(a){return a.isPure===Fd?function(b){return a(b)}:a})));return p(d)}var G={csp:Aa().noUnsafeEval,literals:Ia(b),isIdentifierStart:B(d)&&d,isIdentifierContinue:B(c)&&c};f.$$getAst=function(a){var b=new Nb(G);return(new Mb(b,e,G)).getAst(a).ast};return f}]}function Xf(){var a=!0;this.$get=["$rootScope","$exceptionHandler",function(b,d){return Ld(function(a){b.$evalAsync(a)},
|
||||
d,a)}];this.errorOnUnhandledRejections=function(b){return w(b)?(a=b,this):a}}function Yf(){var a=!0;this.$get=["$browser","$exceptionHandler",function(b,d){return Ld(function(a){b.defer(a)},d,a)}];this.errorOnUnhandledRejections=function(b){return w(b)?(a=b,this):a}}function Ld(a,b,d){function c(){return new e}function e(){var a=this.promise=new f;this.resolve=function(b){h(a,b)};this.reject=function(b){m(a,b)};this.notify=function(b){n(a,b)}}function f(){this.$$state={status:0}}function g(){for(;!w&&
|
||||
x.length;){var a=x.shift();if(!a.pur){a.pur=!0;var c=a.value,c="Possibly unhandled rejection: "+("function"===typeof c?c.toString().replace(/ \{[\s\S]*$/,""):z(c)?"undefined":"string"!==typeof c?Ie(c,void 0):c);cc(a.value)?b(a.value,c):b(c)}}}function k(c){!d||c.pending||2!==c.status||c.pur||(0===w&&0===x.length&&a(g),x.push(c));!c.processScheduled&&c.pending&&(c.processScheduled=!0,++w,a(function(){var e,f,k;k=c.pending;c.processScheduled=!1;c.pending=void 0;try{for(var l=0,n=k.length;l<n;++l){c.pur=
|
||||
!0;f=k[l][0];e=k[l][c.status];try{B(e)?h(f,e(c.value)):1===c.status?h(f,c.value):m(f,c.value)}catch(p){m(f,p),p&&!0===p.$$passToExceptionHandler&&b(p)}}}finally{--w,d&&0===w&&a(g)}}))}function h(a,b){a.$$state.status||(b===a?p(a,v("qcycle",b)):l(a,b))}function l(a,b){function c(b){g||(g=!0,l(a,b))}function d(b){g||(g=!0,p(a,b))}function e(b){n(a,b)}var f,g=!1;try{if(D(b)||B(b))f=b.then;B(f)?(a.$$state.status=-1,f.call(b,c,d,e)):(a.$$state.value=b,a.$$state.status=1,k(a.$$state))}catch(h){d(h)}}function m(a,
|
||||
b){a.$$state.status||p(a,b)}function p(a,b){a.$$state.value=b;a.$$state.status=2;k(a.$$state)}function n(c,d){var e=c.$$state.pending;0>=c.$$state.status&&e&&e.length&&a(function(){for(var a,c,f=0,g=e.length;f<g;f++){c=e[f][0];a=e[f][3];try{n(c,B(a)?a(d):d)}catch(h){b(h)}}})}function s(a){var b=new f;m(b,a);return b}function G(a,b,c){var d=null;try{B(c)&&(d=c())}catch(e){return s(e)}return d&&B(d.then)?d.then(function(){return b(a)},s):b(a)}function t(a,b,c,d){var e=new f;h(e,a);return e.then(b,c,
|
||||
d)}function q(a){if(!B(a))throw v("norslvr",a);var b=new f;a(function(a){h(b,a)},function(a){m(b,a)});return b}var v=F("$q",TypeError),w=0,x=[];S(f.prototype,{then:function(a,b,c){if(z(a)&&z(b)&&z(c))return this;var d=new f;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d,a,b,c]);0<this.$$state.status&&k(this.$$state);return d},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return G(b,y,a)},function(b){return G(b,s,a)},
|
||||
b)}});var y=t;q.prototype=f.prototype;q.defer=c;q.reject=s;q.when=t;q.resolve=y;q.all=function(a){var b=new f,c=0,d=H(a)?[]:{};r(a,function(a,e){c++;t(a).then(function(a){d[e]=a;--c||h(b,d)},function(a){m(b,a)})});0===c&&h(b,d);return b};q.race=function(a){var b=c();r(a,function(a){t(a).then(b.resolve,b.reject)});return b.promise};return q}function hg(){this.$get=["$window","$timeout",function(a,b){var d=a.requestAnimationFrame||a.webkitRequestAnimationFrame,c=a.cancelAnimationFrame||a.webkitCancelAnimationFrame||
|
||||
a.webkitCancelRequestAnimationFrame,e=!!d,f=e?function(a){var b=d(a);return function(){c(b)}}:function(a){var c=b(a,16.66,!1);return function(){b.cancel(c)}};f.supported=e;return f}]}function Wf(){function a(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++pb;this.$$ChildScope=null;this.$$suspended=!1}b.prototype=a;return b}var b=10,d=F("$rootScope"),c=null,e=null;this.digestTtl=
|
||||
function(a){arguments.length&&(b=a);return b};this.$get=["$exceptionHandler","$parse","$browser",function(f,g,k){function h(a){a.currentScope.$$destroyed=!0}function l(a){9===Ca&&(a.$$childHead&&l(a.$$childHead),a.$$nextSibling&&l(a.$$nextSibling));a.$parent=a.$$nextSibling=a.$$prevSibling=a.$$childHead=a.$$childTail=a.$root=a.$$watchers=null}function m(){this.$id=++pb;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=
|
||||
this;this.$$suspended=this.$$destroyed=!1;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$$isolateBindings=null}function p(a){if(v.$$phase)throw d("inprog",v.$$phase);v.$$phase=a}function n(a,b){do a.$$watchersCount+=b;while(a=a.$parent)}function s(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function G(){}function t(){for(;y.length;)try{y.shift()()}catch(a){f(a)}e=null}function q(){null===e&&(e=k.defer(function(){v.$apply(t)},
|
||||
null,"$applyAsync"))}m.prototype={constructor:m,$new:function(b,c){var d;c=c||this;b?(d=new m,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=a(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(b||c!==this)&&d.$on("$destroy",h);return d},$watch:function(a,b,d,e){var f=g(a);b=B(b)?b:E;if(f.$$watchDelegate)return f.$$watchDelegate(this,b,d,f,a);var h=this,k=h.$$watchers,l=
|
||||
{fn:b,last:G,get:f,exp:e||a,eq:!!d};c=null;k||(k=h.$$watchers=[],k.$$digestWatchIndex=-1);k.unshift(l);k.$$digestWatchIndex++;n(this,1);return function(){var a=cb(k,l);0<=a&&(n(h,-1),a<k.$$digestWatchIndex&&k.$$digestWatchIndex--);c=null}},$watchGroup:function(a,b){function c(){h=!1;try{k?(k=!1,b(e,e,g)):b(e,d,g)}finally{for(var f=0;f<a.length;f++)d[f]=e[f]}}var d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0;if(!a.length){var l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return function(){l=
|
||||
!1}}if(1===a.length)return this.$watch(a[0],function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});r(a,function(a,b){var d=g.$watch(a,function(a){e[b]=a;h||(h=!0,g.$evalAsync(c))});f.push(d)});return function(){for(;f.length;)f.shift()()}},$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!z(e)){if(D(e))if(ya(e))for(f!==n&&(f=n,t=f.length=0,l++),a=e.length,t!==a&&(l++,f.length=t=a),b=0;b<a;b++)h=f[b],g=e[b],d=h!==h&&g!==g,d||h===g||(l++,f[b]=g);else{f!==p&&(f=p={},t=0,l++);a=0;for(b in e)ta.call(e,
|
||||
b)&&(a++,g=e[b],h=f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(t++,f[b]=g,l++));if(t>a)for(b in l++,f)ta.call(e,b)||(t--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$$pure=g(a).literal;c.$stateful=!c.$$pure;var d=this,e,f,h,k=1<b.length,l=0,m=g(a,c),n=[],p={},s=!0,t=0;return this.$watch(m,function(){s?(s=!1,b(e,e,d)):b(e,h,d);if(k)if(D(e))if(ya(e)){h=Array(e.length);for(var a=0;a<e.length;a++)h[a]=e[a]}else for(a in h={},e)ta.call(e,a)&&(h[a]=e[a]);else h=e})},$digest:function(){var a,
|
||||
g,h,l,m,n,s,r=b,q,y=w.length?v:this,N=[],z,A;p("$digest");k.$$checkUrlChange();this===v&&null!==e&&(k.defer.cancel(e),t());c=null;do{s=!1;q=y;for(n=0;n<w.length;n++){try{A=w[n],l=A.fn,l(A.scope,A.locals)}catch(C){f(C)}c=null}w.length=0;a:do{if(n=!q.$$suspended&&q.$$watchers)for(n.$$digestWatchIndex=n.length;n.$$digestWatchIndex--;)try{if(a=n[n.$$digestWatchIndex])if(m=a.get,(g=m(q))!==(h=a.last)&&!(a.eq?va(g,h):X(g)&&X(h)))s=!0,c=a,a.last=a.eq?Ia(g,null):g,l=a.fn,l(g,h===G?g:h,q),5>r&&(z=4-r,N[z]||
|
||||
(N[z]=[]),N[z].push({msg:B(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:g,oldVal:h}));else if(a===c){s=!1;break a}}catch(E){f(E)}if(!(n=!q.$$suspended&&q.$$watchersCount&&q.$$childHead||q!==y&&q.$$nextSibling))for(;q!==y&&!(n=q.$$nextSibling);)q=q.$parent}while(q=n);if((s||w.length)&&!r--)throw v.$$phase=null,d("infdig",b,N);}while(s||w.length);for(v.$$phase=null;J<x.length;)try{x[J++]()}catch(D){f(D)}x.length=J=0;k.$$checkUrlChange()},$suspend:function(){this.$$suspended=!0},$isSuspended:function(){return this.$$suspended},
|
||||
$resume:function(){this.$$suspended=!1},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===v&&k.$$applicationDestroyed();n(this,-this.$$watchersCount);for(var b in this.$$listenerCount)s(this,this.$$listenerCount[b],b);a&&a.$$childHead===this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail===this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=
|
||||
this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=E;this.$on=this.$watch=this.$watchGroup=function(){return E};this.$$listeners={};this.$$nextSibling=null;l(this)}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a,b){v.$$phase||w.length||k.defer(function(){w.length&&v.$digest()},null,"$evalAsync");w.push({scope:this,fn:g(a),locals:b})},$$postDigest:function(a){x.push(a)},$apply:function(a){try{p("$apply");try{return this.$eval(a)}finally{v.$$phase=
|
||||
null}}catch(b){f(b)}finally{try{v.$digest()}catch(c){throw f(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&y.push(b);a=g(a);q()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(delete c[d],s(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,g=!1,h={name:a,targetScope:e,stopPropagation:function(){g=
|
||||
!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=db([h],arguments,1),l,m;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(m=d.length;l<m;l++)if(d[l])try{d[l].apply(null,k)}catch(n){f(n)}else d.splice(l,1),l--,m--;if(g)break;e=e.$parent}while(e);h.currentScope=null;return h},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};if(!this.$$listenerCount[a])return e;for(var g=db([e],arguments,
|
||||
1),h,k;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,g)}catch(l){f(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}e.currentScope=null;return e}};var v=new m,w=v.$$asyncQueue=[],x=v.$$postDigestQueue=[],y=v.$$applyAsyncQueue=[],J=0;return v}]}function Le(){var a=/^\s*(https?|s?ftp|mailto|tel|file):/,b=/^\s*((https?|ftp|file|blob):|data:image\/)/;
|
||||
this.aHrefSanitizationWhitelist=function(b){return w(b)?(a=b,this):a};this.imgSrcSanitizationWhitelist=function(a){return w(a)?(b=a,this):b};this.$get=function(){return function(d,c){var e=c?b:a,f=ga(d&&d.trim()).href;return""===f||f.match(e)?d:"unsafe:"+f}}}function Sg(a){if("self"===a)return a;if(A(a)){if(-1<a.indexOf("***"))throw Ea("iwcard",a);a=Md(a).replace(/\\\*\\\*/g,".*").replace(/\\\*/g,"[^:/.?&;]*");return new RegExp("^"+a+"$")}if(ab(a))return new RegExp("^"+a.source+"$");throw Ea("imatcher");
|
||||
}function Nd(a){var b=[];w(a)&&r(a,function(a){b.push(Sg(a))});return b}function $f(){this.SCE_CONTEXTS=V;var a=["self"],b=[];this.resourceUrlWhitelist=function(b){arguments.length&&(a=Nd(b));return a};this.resourceUrlBlacklist=function(a){arguments.length&&(b=Nd(a));return b};this.$get=["$injector","$$sanitizeUri",function(d,c){function e(a,b){var c;"self"===a?(c=Bc(b,Od))||(C.document.baseURI?c=C.document.baseURI:(Na||(Na=C.document.createElement("a"),Na.href=".",Na=Na.cloneNode(!1)),c=Na.href),
|
||||
c=Bc(b,c)):c=!!a.exec(b.href);return c}function f(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var g=function(a){throw Ea("unsafe");};d.has("$sanitize")&&(g=d.get("$sanitize"));var k=f(),h={};h[V.HTML]=f(k);h[V.CSS]=f(k);h[V.MEDIA_URL]=f(k);h[V.URL]=f(h[V.MEDIA_URL]);h[V.JS]=f(k);h[V.RESOURCE_URL]=
|
||||
f(h[V.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw Ea("icontext",a,b);if(null===b||z(b)||""===b)return b;if("string"!==typeof b)throw Ea("itype",a);return new c(b)},getTrusted:function(d,f){if(null===f||z(f)||""===f)return f;var k=h.hasOwnProperty(d)?h[d]:null;if(k&&f instanceof k)return f.$$unwrapTrustedValue();B(f.$$unwrapTrustedValue)&&(f=f.$$unwrapTrustedValue());if(d===V.MEDIA_URL||d===V.URL)return c(f.toString(),d===V.MEDIA_URL);if(d===V.RESOURCE_URL){var k=
|
||||
ga(f.toString()),n,s,r=!1;n=0;for(s=a.length;n<s;n++)if(e(a[n],k)){r=!0;break}if(r)for(n=0,s=b.length;n<s;n++)if(e(b[n],k)){r=!1;break}if(r)return f;throw Ea("insecurl",f.toString());}if(d===V.HTML)return g(f);throw Ea("unsafe");},valueOf:function(a){return a instanceof k?a.$$unwrapTrustedValue():a}}}]}function Zf(){var a=!0;this.enabled=function(b){arguments.length&&(a=!!b);return a};this.$get=["$parse","$sceDelegate",function(b,d){if(a&&8>Ca)throw Ea("iequirks");var c=ja(V);c.isEnabled=function(){return a};
|
||||
c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b},c.valueOf=Ta);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,f=c.getTrusted,g=c.trustAs;r(V,function(a,b){var d=K(b);c[("parse_as_"+d).replace(Cc,wb)]=function(b){return e(a,b)};c[("get_trusted_"+d).replace(Cc,wb)]=function(b){return f(a,b)};c[("trust_as_"+d).replace(Cc,wb)]=function(b){return g(a,b)}});
|
||||
return c}]}function ag(){this.$get=["$window","$document",function(a,b){var d={},c=!((!a.nw||!a.nw.process)&&a.chrome&&(a.chrome.app&&a.chrome.app.runtime||!a.chrome.app&&a.chrome.runtime&&a.chrome.runtime.id))&&a.history&&a.history.pushState,e=fa((/android (\d+)/.exec(K((a.navigator||{}).userAgent))||[])[1]),f=/Boxee/i.test((a.navigator||{}).userAgent),g=b[0]||{},k=g.body&&g.body.style,h=!1,l=!1;k&&(h=!!("transition"in k||"webkitTransition"in k),l=!!("animation"in k||"webkitAnimation"in k));return{history:!(!c||
|
||||
4>e||f),hasEvent:function(a){if("input"===a&&Ca)return!1;if(z(d[a])){var b=g.createElement("div");d[a]="on"+a in b}return d[a]},csp:Aa(),transitions:h,animations:l,android:e}}]}function bg(){this.$get=ia(function(a){return new Tg(a)})}function Tg(a){function b(){var a=e.pop();return a&&a.cb}function d(a){for(var b=e.length-1;0<=b;--b){var c=e[b];if(c.type===a)return e.splice(b,1),c.cb}}var c={},e=[],f=this.ALL_TASKS_TYPE="$$all$$",g=this.DEFAULT_TASK_TYPE="$$default$$";this.completeTask=function(e,
|
||||
h){h=h||g;try{e()}finally{var l;l=h||g;c[l]&&(c[l]--,c[f]--);l=c[h];var m=c[f];if(!m||!l)for(l=m?d:b;m=l(h);)try{m()}catch(p){a.error(p)}}};this.incTaskCount=function(a){a=a||g;c[a]=(c[a]||0)+1;c[f]=(c[f]||0)+1};this.notifyWhenNoPendingTasks=function(a,b){b=b||f;c[b]?e.push({type:b,cb:a}):a()}}function dg(){var a;this.httpOptions=function(b){return b?(a=b,this):a};this.$get=["$exceptionHandler","$templateCache","$http","$q","$sce",function(b,d,c,e,f){function g(k,h){g.totalPendingRequests++;if(!A(k)||
|
||||
z(d.get(k)))k=f.getTrustedResourceUrl(k);var l=c.defaults&&c.defaults.transformResponse;H(l)?l=l.filter(function(a){return a!==vc}):l===vc&&(l=null);return c.get(k,S({cache:d,transformResponse:l},a)).finally(function(){g.totalPendingRequests--}).then(function(a){return d.put(k,a.data)},function(a){h||(a=Ug("tpload",k,a.status,a.statusText),b(a));return e.reject(a)})}g.totalPendingRequests=0;return g}]}function eg(){this.$get=["$rootScope","$browser","$location",function(a,b,d){return{findBindings:function(a,
|
||||
b,d){a=a.getElementsByClassName("ng-binding");var g=[];r(a,function(a){var c=ca.element(a).data("$binding");c&&r(c,function(c){d?(new RegExp("(^|\\s)"+Md(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!==c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],k=0;k<g.length;++k){var h=a.querySelectorAll("["+g[k]+"model"+(d?"=":"*=")+'"'+b+'"]');if(h.length)return h}},getLocation:function(){return d.url()},setLocation:function(b){b!==d.url()&&(d.url(b),a.$digest())},
|
||||
whenStable:function(a){b.notifyWhenNoOutstandingRequests(a)}}}]}function fg(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",function(a,b,d,c,e){function f(f,h,l){B(f)||(l=h,h=f,f=E);var m=Ha.call(arguments,3),p=w(l)&&!l,n=(p?c:d).defer(),s=n.promise,r;r=b.defer(function(){try{n.resolve(f.apply(null,m))}catch(b){n.reject(b),e(b)}finally{delete g[s.$$timeoutId]}p||a.$apply()},h,"$timeout");s.$$timeoutId=r;g[r]=n;return s}var g={};f.cancel=function(a){if(!a)return!1;if(!a.hasOwnProperty("$$timeoutId"))throw Vg("badprom");
|
||||
if(!g.hasOwnProperty(a.$$timeoutId))return!1;a=a.$$timeoutId;var c=g[a],d=c.promise;d.$$state&&(d.$$state.pur=!0);c.reject("canceled");delete g[a];return b.defer.cancel(a)};return f}]}function ga(a){if(!A(a))return a;Ca&&(aa.setAttribute("href",a),a=aa.href);aa.setAttribute("href",a);a=aa.hostname;!Wg&&-1<a.indexOf(":")&&(a="["+a+"]");return{href:aa.href,protocol:aa.protocol?aa.protocol.replace(/:$/,""):"",host:aa.host,search:aa.search?aa.search.replace(/^\?/,""):"",hash:aa.hash?aa.hash.replace(/^#/,
|
||||
""):"",hostname:a,port:aa.port,pathname:"/"===aa.pathname.charAt(0)?aa.pathname:"/"+aa.pathname}}function Jg(a){var b=[Od].concat(a.map(ga));return function(a){a=ga(a);return b.some(Bc.bind(null,a))}}function Bc(a,b){a=ga(a);b=ga(b);return a.protocol===b.protocol&&a.host===b.host}function gg(){this.$get=ia(C)}function Pd(a){function b(a){try{return decodeURIComponent(a)}catch(b){return a}}var d=a[0]||{},c={},e="";return function(){var a,g,k,h,l;try{a=d.cookie||""}catch(m){a=""}if(a!==e)for(e=a,a=
|
||||
e.split("; "),c={},k=0;k<a.length;k++)g=a[k],h=g.indexOf("="),0<h&&(l=b(g.substring(0,h)),z(c[l])&&(c[l]=b(g.substring(h+1))));return c}}function kg(){this.$get=Pd}function dd(a){function b(d,c){if(D(d)){var e={};r(d,function(a,c){e[c]=b(c,a)});return e}return a.factory(d+"Filter",c)}this.register=b;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];b("currency",Qd);b("date",Rd);b("filter",Xg);b("json",Yg);b("limitTo",Zg);b("lowercase",$g);b("number",Sd);b("orderBy",
|
||||
Td);b("uppercase",ah)}function Xg(){return function(a,b,d,c){if(!ya(a)){if(null==a)return a;throw F("filter")("notarray",a);}c=c||"$";var e;switch(Dc(b)){case "function":break;case "boolean":case "null":case "number":case "string":e=!0;case "object":b=bh(b,d,c,e);break;default:return a}return Array.prototype.filter.call(a,b)}}function bh(a,b,d,c){var e=D(a)&&d in a;!0===b?b=va:B(b)||(b=function(a,b){if(z(a))return!1;if(null===a||null===b)return a===b;if(D(b)||D(a)&&!bc(a))return!1;a=K(""+a);b=K(""+
|
||||
b);return-1!==a.indexOf(b)});return function(f){return e&&!D(f)?Fa(f,a[d],b,d,!1):Fa(f,a,b,d,c)}}function Fa(a,b,d,c,e,f){var g=Dc(a),k=Dc(b);if("string"===k&&"!"===b.charAt(0))return!Fa(a,b.substring(1),d,c,e);if(H(a))return a.some(function(a){return Fa(a,b,d,c,e)});switch(g){case "object":var h;if(e){for(h in a)if(h.charAt&&"$"!==h.charAt(0)&&Fa(a[h],b,d,c,!0))return!0;return f?!1:Fa(a,b,d,c,!1)}if("object"===k){for(h in b)if(f=b[h],!B(f)&&!z(f)&&(g=h===c,!Fa(g?a:a[h],f,d,c,g,g)))return!1;return!0}return d(a,
|
||||
b);case "function":return!1;default:return d(a,b)}}function Dc(a){return null===a?"null":typeof a}function Qd(a){var b=a.NUMBER_FORMATS;return function(a,c,e){z(c)&&(c=b.CURRENCY_SYM);z(e)&&(e=b.PATTERNS[1].maxFrac);var f=c?/\u00A4/g:/\s*\u00A4\s*/g;return null==a?a:Ud(a,b.PATTERNS[1],b.GROUP_SEP,b.DECIMAL_SEP,e).replace(f,c)}}function Sd(a){var b=a.NUMBER_FORMATS;return function(a,c){return null==a?a:Ud(a,b.PATTERNS[0],b.GROUP_SEP,b.DECIMAL_SEP,c)}}function ch(a){var b=0,d,c,e,f,g;-1<(c=a.indexOf(Vd))&&
|
||||
(a=a.replace(Vd,""));0<(e=a.search(/e/i))?(0>c&&(c=e),c+=+a.slice(e+1),a=a.substring(0,e)):0>c&&(c=a.length);for(e=0;a.charAt(e)===Ec;e++);if(e===(g=a.length))d=[0],c=1;else{for(g--;a.charAt(g)===Ec;)g--;c-=e;d=[];for(f=0;e<=g;e++,f++)d[f]=+a.charAt(e)}c>Wd&&(d=d.splice(0,Wd-1),b=c-1,c=1);return{d:d,e:b,i:c}}function dh(a,b,d,c){var e=a.d,f=e.length-a.i;b=z(b)?Math.min(Math.max(d,f),c):+b;d=b+a.i;c=e[d];if(0<d){e.splice(Math.max(a.i,d));for(var g=d;g<e.length;g++)e[g]=0}else for(f=Math.max(0,f),a.i=
|
||||
1,e.length=Math.max(1,d=b+1),e[0]=0,g=1;g<d;g++)e[g]=0;if(5<=c)if(0>d-1){for(c=0;c>d;c--)e.unshift(0),a.i++;e.unshift(1);a.i++}else e[d-1]++;for(;f<Math.max(0,b);f++)e.push(0);if(b=e.reduceRight(function(a,b,c,d){b+=a;d[c]=b%10;return Math.floor(b/10)},0))e.unshift(b),a.i++}function Ud(a,b,d,c,e){if(!A(a)&&!W(a)||isNaN(a))return"";var f=!isFinite(a),g=!1,k=Math.abs(a)+"",h="";if(f)h="\u221e";else{g=ch(k);dh(g,e,b.minFrac,b.maxFrac);h=g.d;k=g.i;e=g.e;f=[];for(g=h.reduce(function(a,b){return a&&!b},
|
||||
!0);0>k;)h.unshift(0),k++;0<k?f=h.splice(k,h.length):(f=h,h=[0]);k=[];for(h.length>=b.lgSize&&k.unshift(h.splice(-b.lgSize,h.length).join(""));h.length>b.gSize;)k.unshift(h.splice(-b.gSize,h.length).join(""));h.length&&k.unshift(h.join(""));h=k.join(d);f.length&&(h+=c+f.join(""));e&&(h+="e+"+e)}return 0>a&&!g?b.negPre+h+b.negSuf:b.posPre+h+b.posSuf}function Ob(a,b,d,c){var e="";if(0>a||c&&0>=a)c?a=-a+1:(a=-a,e="-");for(a=""+a;a.length<b;)a=Ec+a;d&&(a=a.substr(a.length-b));return e+a}function ea(a,
|
||||
b,d,c,e){d=d||0;return function(f){f=f["get"+a]();if(0<d||f>-d)f+=d;0===f&&-12===d&&(f=12);return Ob(f,b,c,e)}}function kb(a,b,d){return function(c,e){var f=c["get"+a](),g=ub((d?"STANDALONE":"")+(b?"SHORT":"")+a);return e[g][f]}}function Xd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Yd(a){return function(b){var d=Xd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-+d;b=1+Math.round(b/6048E5);return Ob(b,a)}}function Fc(a,b){return 0>=
|
||||
a.getFullYear()?b.ERAS[0]:b.ERAS[1]}function Rd(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,k=b[8]?a.setUTCFullYear:a.setFullYear,h=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=fa(b[9]+b[10]),g=fa(b[9]+b[11]));k.call(a,fa(b[1]),fa(b[2])-1,fa(b[3]));f=fa(b[4]||0)-f;g=fa(b[5]||0)-g;k=fa(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));h.call(a,f,g,k,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,
|
||||
d,f){var g="",k=[],h,l;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;A(c)&&(c=eh.test(c)?fa(c):b(c));W(c)&&(c=new Date(c));if(!ha(c)||!isFinite(c.getTime()))return c;for(;d;)(l=fh.exec(d))?(k=db(k,l,1),d=k.pop()):(k.push(d),d=null);var m=c.getTimezoneOffset();f&&(m=ec(f,m),c=fc(c,f,!0));r(k,function(b){h=gh[b];g+=h?h(c,a.DATETIME_FORMATS,m):"''"===b?"'":b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function Yg(){return function(a,b){z(b)&&(b=2);return eb(a,b)}}function Zg(){return function(a,
|
||||
b,d){b=Infinity===Math.abs(Number(b))?Number(b):fa(b);if(X(b))return a;W(a)&&(a=a.toString());if(!ya(a))return a;d=!d||isNaN(d)?0:fa(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?Gc(a,d,d+b):0===d?Gc(a,b,a.length):Gc(a,Math.max(0,d+b),d)}}function Gc(a,b,d){return A(a)?a.slice(b,d):Ha.call(a,b,d)}function Td(a){function b(b){return b.map(function(b){var c=1,d=Ta;if(B(b))d=b;else if(A(b)){if("+"===b.charAt(0)||"-"===b.charAt(0))c="-"===b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(d=a(b),d.constant))var e=
|
||||
d(),d=function(a){return a[e]}}return{get:d,descending:c}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}function c(a,b){var c=0,d=a.type,h=b.type;if(d===h){var h=a.value,l=b.value;"string"===d?(h=h.toLowerCase(),l=l.toLowerCase()):"object"===d&&(D(h)&&(h=a.index),D(l)&&(l=b.index));h!==l&&(c=h<l?-1:1)}else c="undefined"===d?1:"undefined"===h?-1:"null"===d?1:"null"===h?-1:d<h?-1:1;return c}return function(a,f,g,k){if(null==a)return a;if(!ya(a))throw F("orderBy")("notarray",
|
||||
a);H(f)||(f=[f]);0===f.length&&(f=["+"]);var h=b(f),l=g?-1:1,m=B(k)?k:c;a=Array.prototype.map.call(a,function(a,b){return{value:a,tieBreaker:{value:b,type:"number",index:b},predicateValues:h.map(function(c){var e=c.get(a);c=typeof e;if(null===e)c="null";else if("object"===c)a:{if(B(e.valueOf)&&(e=e.valueOf(),d(e)))break a;bc(e)&&(e=e.toString(),d(e))}return{value:e,type:c,index:b}})}});a.sort(function(a,b){for(var d=0,e=h.length;d<e;d++){var f=m(a.predicateValues[d],b.predicateValues[d]);if(f)return f*
|
||||
h[d].descending*l}return(m(a.tieBreaker,b.tieBreaker)||c(a.tieBreaker,b.tieBreaker))*l});return a=a.map(function(a){return a.value})}}function Ra(a){B(a)&&(a={link:a});a.restrict=a.restrict||"AC";return ia(a)}function Pb(a,b,d,c,e){this.$$controls=[];this.$error={};this.$$success={};this.$pending=void 0;this.$name=e(b.name||b.ngForm||"")(d);this.$dirty=!1;this.$valid=this.$pristine=!0;this.$submitted=this.$invalid=!1;this.$$parentForm=lb;this.$$element=a;this.$$animate=c;Zd(this)}function Zd(a){a.$$classCache=
|
||||
{};a.$$classCache[$d]=!(a.$$classCache[mb]=a.$$element.hasClass(mb))}function ae(a){function b(a,b,c){c&&!a.$$classCache[b]?(a.$$animate.addClass(a.$$element,b),a.$$classCache[b]=!0):!c&&a.$$classCache[b]&&(a.$$animate.removeClass(a.$$element,b),a.$$classCache[b]=!1)}function d(a,c,d){c=c?"-"+Vc(c,"-"):"";b(a,mb+c,!0===d);b(a,$d+c,!1===d)}var c=a.set,e=a.unset;a.clazz.prototype.$setValidity=function(a,g,k){z(g)?(this.$pending||(this.$pending={}),c(this.$pending,a,k)):(this.$pending&&e(this.$pending,
|
||||
a,k),be(this.$pending)&&(this.$pending=void 0));Ga(g)?g?(e(this.$error,a,k),c(this.$$success,a,k)):(c(this.$error,a,k),e(this.$$success,a,k)):(e(this.$error,a,k),e(this.$$success,a,k));this.$pending?(b(this,"ng-pending",!0),this.$valid=this.$invalid=void 0,d(this,"",null)):(b(this,"ng-pending",!1),this.$valid=be(this.$error),this.$invalid=!this.$valid,d(this,"",this.$valid));g=this.$pending&&this.$pending[a]?void 0:this.$error[a]?!1:this.$$success[a]?!0:null;d(this,a,g);this.$$parentForm.$setValidity(a,
|
||||
g,this)}}function be(a){if(a)for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}function Hc(a){a.$formatters.push(function(b){return a.$isEmpty(b)?b:b.toString()})}function Sa(a,b,d,c,e,f){var g=K(b[0].type);if(!e.android){var k=!1;b.on("compositionstart",function(){k=!0});b.on("compositionupdate",function(a){if(z(a.data)||""===a.data)k=!1});b.on("compositionend",function(){k=!1;l()})}var h,l=function(a){h&&(f.defer.cancel(h),h=null);if(!k){var e=b.val();a=a&&a.type;"password"===g||d.ngTrim&&
|
||||
"false"===d.ngTrim||(e=U(e));(c.$viewValue!==e||""===e&&c.$$hasNativeValidators)&&c.$setViewValue(e,a)}};if(e.hasEvent("input"))b.on("input",l);else{var m=function(a,b,c){h||(h=f.defer(function(){h=null;b&&b.value===c||l(a)}))};b.on("keydown",function(a){var b=a.keyCode;91===b||15<b&&19>b||37<=b&&40>=b||m(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut drop",m)}b.on("change",l);if(ce[g]&&c.$$hasNativeValidators&&g===d.type)b.on("keydown wheel mousedown",function(a){if(!h){var b=this.validity,
|
||||
c=b.badInput,d=b.typeMismatch;h=f.defer(function(){h=null;b.badInput===c&&b.typeMismatch===d||l(a)})}});c.$render=function(){var a=c.$isEmpty(c.$viewValue)?"":c.$viewValue;b.val()!==a&&b.val(a)}}function Qb(a,b){return function(d,c){var e,f;if(ha(d))return d;if(A(d)){'"'===d.charAt(0)&&'"'===d.charAt(d.length-1)&&(d=d.substring(1,d.length-1));if(hh.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(),
|
||||
ss:c.getSeconds(),sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},r(e,function(a,c){c<b.length&&(f[b[c]]=+a)}),e=new Date(f.yyyy,f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0),100>f.yyyy&&e.setFullYear(f.yyyy),e}return NaN}}function nb(a,b,d,c){return function(e,f,g,k,h,l,m,p){function n(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function s(a){return w(a)&&!ha(a)?r(a)||void 0:a}function r(a,b){var c=k.$options.getOption("timezone");v&&v!==c&&(b=Sc(b,ec(v)));var e=d(a,
|
||||
b);!isNaN(e)&&c&&(e=fc(e,c));return e}Ic(e,f,g,k,a);Sa(e,f,g,k,h,l);var t="time"===a||"datetimelocal"===a,q,v;k.$parsers.push(function(c){if(k.$isEmpty(c))return null;if(b.test(c))return r(c,q);k.$$parserName=a});k.$formatters.push(function(a){if(a&&!ha(a))throw ob("datefmt",a);if(n(a)){q=a;var b=k.$options.getOption("timezone");b&&(v=b,q=fc(q,b,!0));var d=c;t&&A(k.$options.getOption("timeSecondsFormat"))&&(d=c.replace("ss.sss",k.$options.getOption("timeSecondsFormat")).replace(/:$/,""));a=m("date")(a,
|
||||
d,b);t&&k.$options.getOption("timeStripZeroSeconds")&&(a=a.replace(/(?::00)?(?:\.000)?$/,""));return a}v=q=null;return""});if(w(g.min)||g.ngMin){var x=g.min||p(g.ngMin)(e),B=s(x);k.$validators.min=function(a){return!n(a)||z(B)||d(a)>=B};g.$observe("min",function(a){a!==x&&(B=s(a),x=a,k.$validate())})}if(w(g.max)||g.ngMax){var y=g.max||p(g.ngMax)(e),J=s(y);k.$validators.max=function(a){return!n(a)||z(J)||d(a)<=J};g.$observe("max",function(a){a!==y&&(J=s(a),y=a,k.$validate())})}}}function Ic(a,b,d,
|
||||
c,e){(c.$$hasNativeValidators=D(b[0].validity))&&c.$parsers.push(function(a){var d=b.prop("validity")||{};if(d.badInput||d.typeMismatch)c.$$parserName=e;else return a})}function de(a){a.$parsers.push(function(b){if(a.$isEmpty(b))return null;if(ih.test(b))return parseFloat(b);a.$$parserName="number"});a.$formatters.push(function(b){if(!a.$isEmpty(b)){if(!W(b))throw ob("numfmt",b);b=b.toString()}return b})}function na(a){w(a)&&!W(a)&&(a=parseFloat(a));return X(a)?void 0:a}function Jc(a){var b=a.toString(),
|
||||
d=b.indexOf(".");return-1===d?-1<a&&1>a&&(a=/e-(\d+)$/.exec(b))?Number(a[1]):0:b.length-d-1}function ee(a,b,d){a=Number(a);var c=(a|0)!==a,e=(b|0)!==b,f=(d|0)!==d;if(c||e||f){var g=c?Jc(a):0,k=e?Jc(b):0,h=f?Jc(d):0,g=Math.max(g,k,h),g=Math.pow(10,g);a*=g;b*=g;d*=g;c&&(a=Math.round(a));e&&(b=Math.round(b));f&&(d=Math.round(d))}return 0===(a-b)%d}function fe(a,b,d,c,e){if(w(c)){a=a(c);if(!a.constant)throw ob("constexpr",d,c);return a(b)}return e}function Kc(a,b){function d(a,b){if(!a||!a.length)return[];
|
||||
if(!b||!b.length)return a;var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],m=0;m<b.length;m++)if(e===b[m])continue a;c.push(e)}return c}function c(a){if(!a)return a;var b=a;H(a)?b=a.map(c).join(" "):D(a)?b=Object.keys(a).filter(function(b){return a[b]}).join(" "):A(a)||(b=a+"");return b}a="ngClass"+a;var e;return["$parse",function(f){return{restrict:"AC",link:function(g,k,h){function l(a,b){var c=[];r(a,function(a){if(0<b||p[a])p[a]=(p[a]||0)+b,p[a]===+(0<b)&&c.push(a)});return c.join(" ")}function m(a){if(a===
|
||||
b){var c=s,c=l(c&&c.split(" "),1);h.$addClass(c)}else c=s,c=l(c&&c.split(" "),-1),h.$removeClass(c);n=a}var p=k.data("$classCounts"),n=!0,s;p||(p=T(),k.data("$classCounts",p));"ngClass"!==a&&(e||(e=f("$index",function(a){return a&1})),g.$watch(e,m));g.$watch(f(h[a],c),function(a){if(n===b){var c=s&&s.split(" "),e=a&&a.split(" "),f=d(c,e),c=d(e,c),f=l(f,-1),c=l(c,1);h.$addClass(c);h.$removeClass(f)}s=a})}}}]}function qd(a,b,d,c,e,f){return{restrict:"A",compile:function(g,k){var h=a(k[c]);return function(a,
|
||||
c){c.on(e,function(c){var e=function(){h(a,{$event:c})};if(b.$$phase)if(f)a.$evalAsync(e);else try{e()}catch(g){d(g)}else a.$apply(e)})}}}}function Rb(a,b,d,c,e,f,g,k,h){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=void 0;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=
|
||||
void 0;this.$name=h(d.name||"",!1)(a);this.$$parentForm=lb;this.$options=Sb;this.$$updateEvents="";this.$$updateEventHandler=this.$$updateEventHandler.bind(this);this.$$parsedNgModel=e(d.ngModel);this.$$parsedNgModelAssign=this.$$parsedNgModel.assign;this.$$ngModelGet=this.$$parsedNgModel;this.$$ngModelSet=this.$$parsedNgModelAssign;this.$$pendingDebounce=null;this.$$parserValid=void 0;this.$$parserName="parse";this.$$currentValidationRunId=0;this.$$scope=a;this.$$rootScope=a.$root;this.$$attr=d;
|
||||
this.$$element=c;this.$$animate=f;this.$$timeout=g;this.$$parse=e;this.$$q=k;this.$$exceptionHandler=b;Zd(this);jh(this)}function jh(a){a.$$scope.$watch(function(b){b=a.$$ngModelGet(b);b===a.$modelValue||a.$modelValue!==a.$modelValue&&b!==b||a.$$setModelValue(b);return b})}function Lc(a){this.$$options=a}function ge(a,b){r(b,function(b,c){w(a[c])||(a[c]=b)})}function Oa(a,b){a.prop("selected",b);a.attr("selected",b)}function he(a,b,d){if(a){A(a)&&(a=new RegExp("^"+a+"$"));if(!a.test)throw F("ngPattern")("noregexp",
|
||||
b,a,za(d));return a}}function Tb(a){a=fa(a);return X(a)?-1:a}var Wb={objectMaxDepth:5,urlErrorParamsEnabled:!0},ie=/^\/(.+)\/([a-z]*)$/,ta=Object.prototype.hasOwnProperty,K=function(a){return A(a)?a.toLowerCase():a},ub=function(a){return A(a)?a.toUpperCase():a},Ca,x,rb,Ha=[].slice,Fg=[].splice,kh=[].push,la=Object.prototype.toString,Pc=Object.getPrototypeOf,pa=F("ng"),ca=C.angular||(C.angular={}),kc,pb=0;Ca=C.document.documentMode;var X=Number.isNaN||function(a){return a!==a};E.$inject=[];Ta.$inject=
|
||||
[];var ve=/^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array]$/,U=function(a){return A(a)?a.trim():a},Md=function(a){return a.replace(/([-()[\]{}+?*.$^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},Aa=function(){if(!w(Aa.rules)){var a=C.document.querySelector("[ng-csp]")||C.document.querySelector("[data-ng-csp]");if(a){var b=a.getAttribute("ng-csp")||a.getAttribute("data-ng-csp");Aa.rules={noUnsafeEval:!b||-1!==b.indexOf("no-unsafe-eval"),noInlineStyle:!b||-1!==
|
||||
b.indexOf("no-inline-style")}}else{a=Aa;try{new Function(""),b=!1}catch(d){b=!0}a.rules={noUnsafeEval:b,noInlineStyle:!1}}}return Aa.rules},qb=function(){if(w(qb.name_))return qb.name_;var a,b,d=Qa.length,c,e;for(b=0;b<d;++b)if(c=Qa[b],a=C.document.querySelector("["+c.replace(":","\\:")+"jq]")){e=a.getAttribute(c+"jq");break}return qb.name_=e},xe=/:/g,Qa=["ng-","data-ng-","ng:","x-ng-"],Be=function(a){var b=a.currentScript;if(!b)return!0;if(!(b instanceof C.HTMLScriptElement||b instanceof C.SVGScriptElement))return!1;
|
||||
b=b.attributes;return[b.getNamedItem("src"),b.getNamedItem("href"),b.getNamedItem("xlink:href")].every(function(b){if(!b)return!0;if(!b.value)return!1;var c=a.createElement("a");c.href=b.value;if(a.location.origin===c.origin)return!0;switch(c.protocol){case "http:":case "https:":case "ftp:":case "blob:":case "file:":case "data:":return!0;default:return!1}})}(C.document),Ee=/[A-Z]/g,Wc=!1,Pa=3,Ke={full:"1.7.9",major:1,minor:7,dot:9,codeName:"pollution-eradication"};Y.expando="ng339";var Ka=Y.cache=
|
||||
{},pg=1;Y._data=function(a){return this.cache[a[this.expando]]||{}};var lg=/-([a-z])/g,lh=/^-ms-/,Ab={mouseleave:"mouseout",mouseenter:"mouseover"},nc=F("jqLite"),og=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,mc=/<|&#?\w+;/,mg=/<([\w:-]+)/,ng=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,oa={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>",
|
||||
"</tr></tbody></table>"],_default:[0,"",""]};oa.optgroup=oa.option;oa.tbody=oa.tfoot=oa.colgroup=oa.caption=oa.thead;oa.th=oa.td;var ug=C.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)},Wa=Y.prototype={ready:fd,toString:function(){var a=[];r(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?x(this[a]):x(this[this.length+a])},length:0,push:kh,sort:[].sort,splice:[].splice},Gb={};r("multiple selected checked disabled readOnly required open".split(" "),
|
||||
function(a){Gb[K(a)]=a});var md={};r("input select option textarea button form details".split(" "),function(a){md[a]=!0});var td={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern",ngStep:"step"};r({data:rc,removeData:qc,hasData:function(a){for(var b in Ka[a.ng339])return!0;return!1},cleanData:function(a){for(var b=0,d=a.length;b<d;b++)qc(a[b]),id(a[b])}},function(a,b){Y[b]=a});r({data:rc,inheritedData:Eb,scope:function(a){return x.data(a,"$scope")||Eb(a.parentNode||
|
||||
a,["$isolateScope","$scope"])},isolateScope:function(a){return x.data(a,"$isolateScope")||x.data(a,"$isolateScopeNoTemplate")},controller:jd,injector:function(a){return Eb(a,"$injector")},removeAttr:function(a,b){a.removeAttribute(b)},hasClass:Bb,css:function(a,b,d){b=xb(b.replace(lh,"ms-"));if(w(d))a.style[b]=d;else return a.style[b]},attr:function(a,b,d){var c=a.nodeType;if(c!==Pa&&2!==c&&8!==c&&a.getAttribute){var c=K(b),e=Gb[c];if(w(d))null===d||!1===d&&e?a.removeAttribute(b):a.setAttribute(b,
|
||||
e?c:d);else return a=a.getAttribute(b),e&&null!==a&&(a=c),null===a?void 0:a}},prop:function(a,b,d){if(w(d))a[b]=d;else return a[b]},text:function(){function a(a,d){if(z(d)){var c=a.nodeType;return 1===c||c===Pa?a.textContent:""}a.textContent=d}a.$dv="";return a}(),val:function(a,b){if(z(b)){if(a.multiple&&"select"===ua(a)){var d=[];r(a.options,function(a){a.selected&&d.push(a.value||a.text)});return d}return a.value}a.value=b},html:function(a,b){if(z(b))return a.innerHTML;yb(a,!0);a.innerHTML=b},
|
||||
empty:kd},function(a,b){Y.prototype[b]=function(b,c){var e,f,g=this.length;if(a!==kd&&z(2===a.length&&a!==Bb&&a!==jd?b:c)){if(D(b)){for(e=0;e<g;e++)if(a===rc)a(this[e],b);else for(f in b)a(this[e],f,b[f]);return this}e=a.$dv;g=z(e)?Math.min(g,1):g;for(f=0;f<g;f++){var k=a(this[f],b,c);e=e?e+k:k}return e}for(e=0;e<g;e++)a(this[e],b,c);return this}});r({removeData:qc,on:function(a,b,d,c){if(w(c))throw nc("onargs");if(lc(a)){c=zb(a,!0);var e=c.events,f=c.handle;f||(f=c.handle=rg(a,e));c=0<=b.indexOf(" ")?
|
||||
b.split(" "):[b];for(var g=c.length,k=function(b,c,g){var k=e[b];k||(k=e[b]=[],k.specialHandlerWrapper=c,"$destroy"===b||g||a.addEventListener(b,f));k.push(d)};g--;)b=c[g],Ab[b]?(k(Ab[b],tg),k(b,void 0,!0)):k(b)}},off:id,one:function(a,b,d){a=x(a);a.on(b,function e(){a.off(b,d);a.off(b,e)});a.on(b,d)},replaceWith:function(a,b){var d,c=a.parentNode;yb(a);r(new Y(b),function(b){d?c.insertBefore(b,d.nextSibling):c.replaceChild(b,a);d=b})},children:function(a){var b=[];r(a.childNodes,function(a){1===
|
||||
a.nodeType&&b.push(a)});return b},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,b){var d=a.nodeType;if(1===d||11===d){b=new Y(b);for(var d=0,c=b.length;d<c;d++)a.appendChild(b[d])}},prepend:function(a,b){if(1===a.nodeType){var d=a.firstChild;r(new Y(b),function(b){a.insertBefore(b,d)})}},wrap:function(a,b){var d=x(b).eq(0).clone()[0],c=a.parentNode;c&&c.replaceChild(d,a);d.appendChild(a)},remove:Fb,detach:function(a){Fb(a,!0)},after:function(a,b){var d=a,c=a.parentNode;
|
||||
if(c){b=new Y(b);for(var e=0,f=b.length;e<f;e++){var g=b[e];c.insertBefore(g,d.nextSibling);d=g}}},addClass:Db,removeClass:Cb,toggleClass:function(a,b,d){b&&r(b.split(" "),function(b){var e=d;z(e)&&(e=!Bb(a,b));(e?Db:Cb)(a,b)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,b){return a.getElementsByTagName?a.getElementsByTagName(b):[]},clone:pc,triggerHandler:function(a,b,d){var c,e,f=b.type||b,g=zb(a);if(g=(g=g&&g.events)&&
|
||||
g[f])c={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},stopImmediatePropagation:function(){this.immediatePropagationStopped=!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:E,type:f,target:a},b.type&&(c=S(c,b)),b=ja(g),e=d?[c].concat(d):[c],r(b,function(b){c.isImmediatePropagationStopped()||b.apply(a,e)})}},function(a,b){Y.prototype[b]=function(b,c,e){for(var f,g=0,k=this.length;g<
|
||||
k;g++)z(f)?(f=a(this[g],b,c,e),w(f)&&(f=x(f))):oc(f,a(this[g],b,c,e));return w(f)?f:this}});Y.prototype.bind=Y.prototype.on;Y.prototype.unbind=Y.prototype.off;var mh=Object.create(null);nd.prototype={_idx:function(a){a!==this._lastKey&&(this._lastKey=a,this._lastIndex=this._keys.indexOf(a));return this._lastIndex},_transformKey:function(a){return X(a)?mh:a},get:function(a){a=this._transformKey(a);a=this._idx(a);if(-1!==a)return this._values[a]},has:function(a){a=this._transformKey(a);return-1!==this._idx(a)},
|
||||
set:function(a,b){a=this._transformKey(a);var d=this._idx(a);-1===d&&(d=this._lastIndex=this._keys.length);this._keys[d]=a;this._values[d]=b},delete:function(a){a=this._transformKey(a);a=this._idx(a);if(-1===a)return!1;this._keys.splice(a,1);this._values.splice(a,1);this._lastKey=NaN;this._lastIndex=-1;return!0}};var Hb=nd,jg=[function(){this.$get=[function(){return Hb}]}],wg=/^([^(]+?)=>/,xg=/^[^(]*\(\s*([^)]*)\)/m,nh=/,/,oh=/^\s*(_?)(\S+?)\1\s*$/,vg=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ba=F("$injector");
|
||||
fb.$$annotate=function(a,b,d){var c;if("function"===typeof a){if(!(c=a.$inject)){c=[];if(a.length){if(b)throw A(d)&&d||(d=a.name||yg(a)),Ba("strictdi",d);b=od(a);r(b[1].split(nh),function(a){a.replace(oh,function(a,b,d){c.push(d)})})}a.$inject=c}}else H(a)?(b=a.length-1,sb(a[b],"fn"),c=a.slice(0,b)):sb(a,"fn",!0);return c};var je=F("$animate"),zf=function(){this.$get=E},Af=function(){var a=new Hb,b=[];this.$get=["$$AnimateRunner","$rootScope",function(d,c){function e(a,b,c){var d=!1;b&&(b=A(b)?b.split(" "):
|
||||
H(b)?b:[],r(b,function(b){b&&(d=!0,a[b]=c)}));return d}function f(){r(b,function(b){var c=a.get(b);if(c){var d=zg(b.attr("class")),e="",f="";r(c,function(a,b){a!==!!d[b]&&(a?e+=(e.length?" ":"")+b:f+=(f.length?" ":"")+b)});r(b,function(a){e&&Db(a,e);f&&Cb(a,f)});a.delete(b)}});b.length=0}return{enabled:E,on:E,off:E,pin:E,push:function(g,k,h,l){l&&l();h=h||{};h.from&&g.css(h.from);h.to&&g.css(h.to);if(h.addClass||h.removeClass)if(k=h.addClass,l=h.removeClass,h=a.get(g)||{},k=e(h,k,!0),l=e(h,l,!1),
|
||||
k||l)a.set(g,h),b.push(g),1===b.length&&c.$$postDigest(f);g=new d;g.complete();return g}}}]},xf=["$provide",function(a){var b=this,d=null,c=null;this.$$registeredAnimations=Object.create(null);this.register=function(c,d){if(c&&"."!==c.charAt(0))throw je("notcsel",c);var g=c+"-animation";b.$$registeredAnimations[c.substr(1)]=g;a.factory(g,d)};this.customFilter=function(a){1===arguments.length&&(c=B(a)?a:null);return c};this.classNameFilter=function(a){if(1===arguments.length&&(d=a instanceof RegExp?
|
||||
a:null)&&/[(\s|\/)]ng-animate[(\s|\/)]/.test(d.toString()))throw d=null,je("nongcls","ng-animate");return d};this.$get=["$$animateQueue",function(a){function b(a,c,d){if(d){var e;a:{for(e=0;e<d.length;e++){var f=d[e];if(1===f.nodeType){e=f;break a}}e=void 0}!e||e.parentNode||e.previousElementSibling||(d=null)}d?d.after(a):c.prepend(a)}return{on:a.on,off:a.off,pin:a.pin,enabled:a.enabled,cancel:function(a){a.cancel&&a.cancel()},enter:function(c,d,h,l){d=d&&x(d);h=h&&x(h);d=d||h.parent();b(c,d,h);return a.push(c,
|
||||
"enter",ra(l))},move:function(c,d,h,l){d=d&&x(d);h=h&&x(h);d=d||h.parent();b(c,d,h);return a.push(c,"move",ra(l))},leave:function(b,c){return a.push(b,"leave",ra(c),function(){b.remove()})},addClass:function(b,c,d){d=ra(d);d.addClass=hb(d.addclass,c);return a.push(b,"addClass",d)},removeClass:function(b,c,d){d=ra(d);d.removeClass=hb(d.removeClass,c);return a.push(b,"removeClass",d)},setClass:function(b,c,d,f){f=ra(f);f.addClass=hb(f.addClass,c);f.removeClass=hb(f.removeClass,d);return a.push(b,"setClass",
|
||||
f)},animate:function(b,c,d,f,m){m=ra(m);m.from=m.from?S(m.from,c):c;m.to=m.to?S(m.to,d):d;m.tempClasses=hb(m.tempClasses,f||"ng-inline-animate");return a.push(b,"animate",m)}}}]}],Cf=function(){this.$get=["$$rAF",function(a){function b(b){d.push(b);1<d.length||a(function(){for(var a=0;a<d.length;a++)d[a]();d=[]})}var d=[];return function(){var a=!1;b(function(){a=!0});return function(d){a?d():b(d)}}}]},Bf=function(){this.$get=["$q","$sniffer","$$animateAsyncRun","$$isDocumentHidden","$timeout",function(a,
|
||||
b,d,c,e){function f(a){this.setHost(a);var b=d();this._doneCallbacks=[];this._tick=function(a){c()?e(a,0,!1):b(a)};this._state=0}f.chain=function(a,b){function c(){if(d===a.length)b(!0);else a[d](function(a){!1===a?b(!1):(d++,c())})}var d=0;c()};f.all=function(a,b){function c(f){e=e&&f;++d===a.length&&b(e)}var d=0,e=!0;r(a,function(a){a.done(c)})};f.prototype={setHost:function(a){this.host=a||{}},done:function(a){2===this._state?a():this._doneCallbacks.push(a)},progress:E,getPromise:function(){if(!this.promise){var b=
|
||||
this;this.promise=a(function(a,c){b.done(function(b){!1===b?c():a()})})}return this.promise},then:function(a,b){return this.getPromise().then(a,b)},"catch":function(a){return this.getPromise()["catch"](a)},"finally":function(a){return this.getPromise()["finally"](a)},pause:function(){this.host.pause&&this.host.pause()},resume:function(){this.host.resume&&this.host.resume()},end:function(){this.host.end&&this.host.end();this._resolve(!0)},cancel:function(){this.host.cancel&&this.host.cancel();this._resolve(!1)},
|
||||
complete:function(a){var b=this;0===b._state&&(b._state=1,b._tick(function(){b._resolve(a)}))},_resolve:function(a){2!==this._state&&(r(this._doneCallbacks,function(b){b(a)}),this._doneCallbacks.length=0,this._state=2)}};return f}]},yf=function(){this.$get=["$$rAF","$q","$$AnimateRunner",function(a,b,d){return function(b,e){function f(){a(function(){g.addClass&&(b.addClass(g.addClass),g.addClass=null);g.removeClass&&(b.removeClass(g.removeClass),g.removeClass=null);g.to&&(b.css(g.to),g.to=null);k||
|
||||
h.complete();k=!0});return h}var g=e||{};g.$$prepared||(g=Ia(g));g.cleanupStyles&&(g.from=g.to=null);g.from&&(b.css(g.from),g.from=null);var k,h=new d;return{start:f,end:f}}}]},$=F("$compile"),tc=new function(){};Xc.$inject=["$provide","$$sanitizeUriProvider"];Jb.prototype.isFirstChange=function(){return this.previousValue===tc};var pd=/^((?:x|data)[:\-_])/i,Eg=/[:\-_]+(.)/g,vd=F("$controller"),ud=/^(\S+)(\s+as\s+([\w$]+))?$/,Jf=function(){this.$get=["$document",function(a){return function(b){b?!b.nodeType&&
|
||||
b instanceof x&&(b=b[0]):b=a[0].body;return b.offsetWidth+1}}]},wd="application/json",wc={"Content-Type":wd+";charset=utf-8"},Hg=/^\[|^\{(?!\{)/,Ig={"[":/]$/,"{":/}$/},Gg=/^\)]\}',?\n/,Kb=F("$http"),Ma=ca.$interpolateMinErr=F("$interpolate");Ma.throwNoconcat=function(a){throw Ma("noconcat",a);};Ma.interr=function(a,b){return Ma("interr",a,b.toString())};var Lg=F("$interval"),Sf=function(){this.$get=function(){function a(a){var b=function(a){b.data=a;b.called=!0};b.id=a;return b}var b=ca.callbacks,
|
||||
d={};return{createCallback:function(c){c="_"+(b.$$counter++).toString(36);var e="angular.callbacks."+c,f=a(c);d[e]=b[c]=f;return e},wasCalled:function(a){return d[a].called},getResponse:function(a){return d[a].data},removeCallback:function(a){delete b[d[a].id];delete d[a]}}}},ph=/^([^?#]*)(\?([^#]*))?(#(.*))?$/,Mg={http:80,https:443,ftp:21},jb=F("$location"),Ng=/^\s*[\\/]{2,}/,qh={$$absUrl:"",$$html5:!1,$$replace:!1,$$compose:function(){for(var a=this.$$path,b=this.$$hash,d=ye(this.$$search),b=b?
|
||||
"#"+hc(b):"",a=a.split("/"),c=a.length;c--;)a[c]=hc(a[c].replace(/%2F/g,"/"));this.$$url=a.join("/")+(d?"?"+d:"")+b;this.$$absUrl=this.$$normalizeUrl(this.$$url);this.$$urlUpdatedByLocation=!0},absUrl:Lb("$$absUrl"),url:function(a){if(z(a))return this.$$url;var b=ph.exec(a);(b[1]||""===a)&&this.path(decodeURIComponent(b[1]));(b[2]||b[1]||""===a)&&this.search(b[3]||"");this.hash(b[5]||"");return this},protocol:Lb("$$protocol"),host:Lb("$$host"),port:Lb("$$port"),path:Dd("$$path",function(a){a=null!==
|
||||
a?a.toString():"";return"/"===a.charAt(0)?a:"/"+a}),search:function(a,b){switch(arguments.length){case 0:return this.$$search;case 1:if(A(a)||W(a))a=a.toString(),this.$$search=gc(a);else if(D(a))a=Ia(a,{}),r(a,function(b,c){null==b&&delete a[c]}),this.$$search=a;else throw jb("isrcharg");break;default:z(b)||null===b?delete this.$$search[a]:this.$$search[a]=b}this.$$compose();return this},hash:Dd("$$hash",function(a){return null!==a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};
|
||||
r([Cd,zc,yc],function(a){a.prototype=Object.create(qh);a.prototype.state=function(b){if(!arguments.length)return this.$$state;if(a!==yc||!this.$$html5)throw jb("nostate");this.$$state=z(b)?null:b;this.$$urlUpdatedByLocation=!0;return this}});var Ya=F("$parse"),Rg={}.constructor.prototype.valueOf,Ub=T();r("+ - * / % === !== == != < > <= >= && || ! = |".split(" "),function(a){Ub[a]=!0});var rh={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},Nb=function(a){this.options=a};Nb.prototype={constructor:Nb,
|
||||
lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index<this.text.length;)if(a=this.text.charAt(this.index),'"'===a||"'"===a)this.readString(a);else if(this.isNumber(a)||"."===a&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdentifierStart(this.peekMultichar()))this.readIdent();else if(this.is(a,"(){}[].,;:?"))this.tokens.push({index:this.index,text:a}),this.index++;else if(this.isWhitespace(a))this.index++;else{var b=a+this.peek(),d=b+this.peek(2),c=Ub[b],e=Ub[d];Ub[a]||
|
||||
c||e?(a=e?d:c?b:a,this.tokens.push({index:this.index,text:a,operator:!0}),this.index+=a.length):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a,b){return-1!==b.indexOf(a)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdentifierStart:function(a){return this.options.isIdentifierStart?
|
||||
this.options.isIdentifierStart(a,this.codePointAt(a)):this.isValidIdentifierStart(a)},isValidIdentifierStart:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isIdentifierContinue:function(a){return this.options.isIdentifierContinue?this.options.isIdentifierContinue(a,this.codePointAt(a)):this.isValidIdentifierContinue(a)},isValidIdentifierContinue:function(a,b){return this.isValidIdentifierStart(a,b)||this.isNumber(a)},codePointAt:function(a){return 1===a.length?a.charCodeAt(0):
|
||||
(a.charCodeAt(0)<<10)+a.charCodeAt(1)-56613888},peekMultichar:function(){var a=this.text.charAt(this.index),b=this.peek();if(!b)return a;var d=a.charCodeAt(0),c=b.charCodeAt(0);return 55296<=d&&56319>=d&&56320<=c&&57343>=c?a+b:a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,b,d){d=d||this.index;b=w(b)?"s "+b+"-"+this.index+" ["+this.text.substring(b,d)+"]":" "+d;throw Ya("lexerr",a,b,this.text);},readNumber:function(){for(var a="",b=this.index;this.index<
|
||||
this.text.length;){var d=K(this.text.charAt(this.index));if("."===d||this.isNumber(d))a+=d;else{var c=this.peek();if("e"===d&&this.isExpOperator(c))a+=d;else if(this.isExpOperator(d)&&c&&this.isNumber(c)&&"e"===a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||c&&this.isNumber(c)||"e"!==a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:b,text:a,constant:!0,value:Number(a)})},readIdent:function(){var a=this.index;for(this.index+=this.peekMultichar().length;this.index<
|
||||
this.text.length;){var b=this.peekMultichar();if(!this.isIdentifierContinue(b))break;this.index+=b.length}this.tokens.push({index:a,text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var b=this.index;this.index++;for(var d="",c=a,e=!1;this.index<this.text.length;){var f=this.text.charAt(this.index),c=c+f;if(e)"u"===f?(e=this.text.substring(this.index+1,this.index+5),e.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+e+"]"),this.index+=4,d+=String.fromCharCode(parseInt(e,
|
||||
16))):d+=rh[f]||f,e=!1;else if("\\"===f)e=!0;else{if(f===a){this.index++;this.tokens.push({index:b,text:c,constant:!0,value:d});return}d+=f}this.index++}this.throwError("Unterminated quote",b)}};var q=function(a,b){this.lexer=a;this.options=b};q.Program="Program";q.ExpressionStatement="ExpressionStatement";q.AssignmentExpression="AssignmentExpression";q.ConditionalExpression="ConditionalExpression";q.LogicalExpression="LogicalExpression";q.BinaryExpression="BinaryExpression";q.UnaryExpression="UnaryExpression";
|
||||
q.CallExpression="CallExpression";q.MemberExpression="MemberExpression";q.Identifier="Identifier";q.Literal="Literal";q.ArrayExpression="ArrayExpression";q.Property="Property";q.ObjectExpression="ObjectExpression";q.ThisExpression="ThisExpression";q.LocalsExpression="LocalsExpression";q.NGValueParameter="NGValueParameter";q.prototype={ast:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.program();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);return a},
|
||||
program:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.expressionStatement()),!this.expect(";"))return{type:q.Program,body:a}},expressionStatement:function(){return{type:q.ExpressionStatement,expression:this.filterChain()}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary();if(this.expect("=")){if(!Hd(a))throw Ya("lval");
|
||||
a={type:q.AssignmentExpression,left:a,right:this.assignment(),operator:"="}}return a},ternary:function(){var a=this.logicalOR(),b,d;return this.expect("?")&&(b=this.expression(),this.consume(":"))?(d=this.expression(),{type:q.ConditionalExpression,test:a,alternate:b,consequent:d}):a},logicalOR:function(){for(var a=this.logicalAND();this.expect("||");)a={type:q.LogicalExpression,operator:"||",left:a,right:this.logicalAND()};return a},logicalAND:function(){for(var a=this.equality();this.expect("&&");)a=
|
||||
{type:q.LogicalExpression,operator:"&&",left:a,right:this.equality()};return a},equality:function(){for(var a=this.relational(),b;b=this.expect("==","!=","===","!==");)a={type:q.BinaryExpression,operator:b.text,left:a,right:this.relational()};return a},relational:function(){for(var a=this.additive(),b;b=this.expect("<",">","<=",">=");)a={type:q.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),b;b=this.expect("+","-");)a={type:q.BinaryExpression,
|
||||
operator:b.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:q.BinaryExpression,operator:b.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:q.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")?
|
||||
a=this.object():this.selfReferential.hasOwnProperty(this.peek().text)?a=Ia(this.selfReferential[this.consume().text]):this.options.literals.hasOwnProperty(this.peek().text)?a={type:q.Literal,value:this.options.literals[this.consume().text]}:this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():this.throwError("not a primary expression",this.peek());for(var b;b=this.expect("(","[",".");)"("===b.text?(a={type:q.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")):
|
||||
"["===b.text?(a={type:q.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:q.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var b={type:q.CallExpression,callee:this.identifier(),arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return b},parseArguments:function(){var a=[];if(")"!==this.peekToken().text){do a.push(this.filterChain());while(this.expect(","))
|
||||
}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:q.Identifier,name:a.text}},constant:function(){return{type:q.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:q.ArrayExpression,elements:a}},object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;
|
||||
b={type:q.Property,kind:"init"};this.peek().constant?(b.key=this.constant(),b.computed=!1,this.consume(":"),b.value=this.expression()):this.peek().identifier?(b.key=this.identifier(),b.computed=!1,this.peek(":")?(this.consume(":"),b.value=this.expression()):b.value=b.key):this.peek("[")?(this.consume("["),b.key=this.expression(),this.consume("]"),b.computed=!0,this.consume(":"),b.value=this.expression()):this.throwError("invalid key",this.peek());a.push(b)}while(this.expect(","))}this.consume("}");
|
||||
return{type:q.ObjectExpression,properties:a}},throwError:function(a,b){throw Ya("syntax",b.text,a,b.index+1,this.text,this.text.substring(b.index));},consume:function(a){if(0===this.tokens.length)throw Ya("ueoe",this.text);var b=this.expect(a);b||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return b},peekToken:function(){if(0===this.tokens.length)throw Ya("ueoe",this.text);return this.tokens[0]},peek:function(a,b,d,c){return this.peekAhead(0,a,b,d,c)},peekAhead:function(a,b,d,c,
|
||||
e){if(this.tokens.length>a){a=this.tokens[a];var f=a.text;if(f===b||f===d||f===c||f===e||!(b||d||c||e))return a}return!1},expect:function(a,b,d,c){return(a=this.peek(a,b,d,c))?(this.tokens.shift(),a):!1},selfReferential:{"this":{type:q.ThisExpression},$locals:{type:q.LocalsExpression}}};var Fd=2;Jd.prototype={compile:function(a){var b=this;this.state={nextId:0,filters:{},fn:{vars:[],body:[],own:{}},assign:{vars:[],body:[],own:{}},inputs:[]};Z(a,b.$filter);var d="",c;this.stage="assign";if(c=Id(a))this.state.computing=
|
||||
"assign",d=this.nextId(),this.recurse(c,d),this.return_(d),d="fn.assign="+this.generateFunction("assign","s,v,l");c=Gd(a.body);b.stage="inputs";r(c,function(a,c){var d="fn"+c;b.state[d]={vars:[],body:[],own:{}};b.state.computing=d;var k=b.nextId();b.recurse(a,k);b.return_(k);b.state.inputs.push({name:d,isPure:a.isPure});a.watchId=c});this.state.computing="fn";this.stage="main";this.recurse(a);a='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+
|
||||
d+this.watchFns()+"return fn;";a=(new Function("$filter","getStringValue","ifDefined","plus",a))(this.$filter,Og,Pg,Ed);this.state=this.stage=void 0;return a},USE:"use",STRICT:"strict",watchFns:function(){var a=[],b=this.state.inputs,d=this;r(b,function(b){a.push("var "+b.name+"="+d.generateFunction(b.name,"s"));b.isPure&&a.push(b.name,".isPure="+JSON.stringify(b.isPure)+";")});b.length&&a.push("fn.inputs=["+b.map(function(a){return a.name}).join(",")+"];");return a.join("")},generateFunction:function(a,
|
||||
b){return"function("+b+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],b=this;r(this.state.filters,function(d,c){a.push(d+"=$filter("+b.escape(c)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,b,d,c,e,f){var g,k,h=this,l,m,p;c=c||E;if(!f&&w(a.watchId))b=b||this.nextId(),this.if_("i",this.lazyAssign(b,
|
||||
this.computedMember("i",a.watchId)),this.lazyRecurse(a,b,d,c,e,!0));else switch(a.type){case q.Program:r(a.body,function(b,c){h.recurse(b.expression,void 0,void 0,function(a){k=a});c!==a.body.length-1?h.current().body.push(k,";"):h.return_(k)});break;case q.Literal:m=this.escape(a.value);this.assign(b,m);c(b||m);break;case q.UnaryExpression:this.recurse(a.argument,void 0,void 0,function(a){k=a});m=a.operator+"("+this.ifDefined(k,0)+")";this.assign(b,m);c(m);break;case q.BinaryExpression:this.recurse(a.left,
|
||||
void 0,void 0,function(a){g=a});this.recurse(a.right,void 0,void 0,function(a){k=a});m="+"===a.operator?this.plus(g,k):"-"===a.operator?this.ifDefined(g,0)+a.operator+this.ifDefined(k,0):"("+g+")"+a.operator+"("+k+")";this.assign(b,m);c(m);break;case q.LogicalExpression:b=b||this.nextId();h.recurse(a.left,b);h.if_("&&"===a.operator?b:h.not(b),h.lazyRecurse(a.right,b));c(b);break;case q.ConditionalExpression:b=b||this.nextId();h.recurse(a.test,b);h.if_(b,h.lazyRecurse(a.alternate,b),h.lazyRecurse(a.consequent,
|
||||
b));c(b);break;case q.Identifier:b=b||this.nextId();d&&(d.context="inputs"===h.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l",a.name)+"?l:s"),d.computed=!1,d.name=a.name);h.if_("inputs"===h.stage||h.not(h.getHasOwnProperty("l",a.name)),function(){h.if_("inputs"===h.stage||"s",function(){e&&1!==e&&h.if_(h.isNull(h.nonComputedMember("s",a.name)),h.lazyAssign(h.nonComputedMember("s",a.name),"{}"));h.assign(b,h.nonComputedMember("s",a.name))})},b&&h.lazyAssign(b,h.nonComputedMember("l",
|
||||
a.name)));c(b);break;case q.MemberExpression:g=d&&(d.context=this.nextId())||this.nextId();b=b||this.nextId();h.recurse(a.object,g,void 0,function(){h.if_(h.notNull(g),function(){a.computed?(k=h.nextId(),h.recurse(a.property,k),h.getStringValue(k),e&&1!==e&&h.if_(h.not(h.computedMember(g,k)),h.lazyAssign(h.computedMember(g,k),"{}")),m=h.computedMember(g,k),h.assign(b,m),d&&(d.computed=!0,d.name=k)):(e&&1!==e&&h.if_(h.isNull(h.nonComputedMember(g,a.property.name)),h.lazyAssign(h.nonComputedMember(g,
|
||||
a.property.name),"{}")),m=h.nonComputedMember(g,a.property.name),h.assign(b,m),d&&(d.computed=!1,d.name=a.property.name))},function(){h.assign(b,"undefined")});c(b)},!!e);break;case q.CallExpression:b=b||this.nextId();a.filter?(k=h.filter(a.callee.name),l=[],r(a.arguments,function(a){var b=h.nextId();h.recurse(a,b);l.push(b)}),m=k+"("+l.join(",")+")",h.assign(b,m),c(b)):(k=h.nextId(),g={},l=[],h.recurse(a.callee,k,g,function(){h.if_(h.notNull(k),function(){r(a.arguments,function(b){h.recurse(b,a.constant?
|
||||
void 0:h.nextId(),void 0,function(a){l.push(a)})});m=g.name?h.member(g.context,g.name,g.computed)+"("+l.join(",")+")":k+"("+l.join(",")+")";h.assign(b,m)},function(){h.assign(b,"undefined")});c(b)}));break;case q.AssignmentExpression:k=this.nextId();g={};this.recurse(a.left,void 0,g,function(){h.if_(h.notNull(g.context),function(){h.recurse(a.right,k);m=h.member(g.context,g.name,g.computed)+a.operator+k;h.assign(b,m);c(b||m)})},1);break;case q.ArrayExpression:l=[];r(a.elements,function(b){h.recurse(b,
|
||||
a.constant?void 0:h.nextId(),void 0,function(a){l.push(a)})});m="["+l.join(",")+"]";this.assign(b,m);c(b||m);break;case q.ObjectExpression:l=[];p=!1;r(a.properties,function(a){a.computed&&(p=!0)});p?(b=b||this.nextId(),this.assign(b,"{}"),r(a.properties,function(a){a.computed?(g=h.nextId(),h.recurse(a.key,g)):g=a.key.type===q.Identifier?a.key.name:""+a.key.value;k=h.nextId();h.recurse(a.value,k);h.assign(h.member(b,g,a.computed),k)})):(r(a.properties,function(b){h.recurse(b.value,a.constant?void 0:
|
||||
h.nextId(),void 0,function(a){l.push(h.escape(b.key.type===q.Identifier?b.key.name:""+b.key.value)+":"+a)})}),m="{"+l.join(",")+"}",this.assign(b,m));c(b||m);break;case q.ThisExpression:this.assign(b,"s");c(b||"s");break;case q.LocalsExpression:this.assign(b,"l");c(b||"l");break;case q.NGValueParameter:this.assign(b,"v"),c(b||"v")}},getHasOwnProperty:function(a,b){var d=a+"."+b,c=this.current().own;c.hasOwnProperty(d)||(c[d]=this.nextId(!1,a+"&&("+this.escape(b)+" in "+a+")"));return c[d]},assign:function(a,
|
||||
b){if(a)return this.current().body.push(a,"=",b,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,b){return"ifDefined("+a+","+this.escape(b)+")"},plus:function(a,b){return"plus("+a+","+b+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,b,d){if(!0===a)b();else{var c=this.current().body;c.push("if(",a,"){");b();c.push("}");d&&(c.push("else{"),d(),c.push("}"))}},
|
||||
not:function(a){return"!("+a+")"},isNull:function(a){return a+"==null"},notNull:function(a){return a+"!=null"},nonComputedMember:function(a,b){var d=/[^$_a-zA-Z0-9]/g;return/^[$_a-zA-Z][$_a-zA-Z0-9]*$/.test(b)?a+"."+b:a+'["'+b.replace(d,this.stringEscapeFn)+'"]'},computedMember:function(a,b){return a+"["+b+"]"},member:function(a,b,d){return d?this.computedMember(a,b):this.nonComputedMember(a,b)},getStringValue:function(a){this.assign(a,"getStringValue("+a+")")},lazyRecurse:function(a,b,d,c,e,f){var g=
|
||||
this;return function(){g.recurse(a,b,d,c,e,f)}},lazyAssign:function(a,b){var d=this;return function(){d.assign(a,b)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(A(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(W(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"===typeof a)return"undefined";throw Ya("esc");},nextId:function(a,
|
||||
b){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(b?"="+b:""));return d},current:function(){return this.state[this.state.computing]}};Kd.prototype={compile:function(a){var b=this;Z(a,b.$filter);var d,c;if(d=Id(a))c=this.recurse(d);d=Gd(a.body);var e;d&&(e=[],r(d,function(a,c){var d=b.recurse(a);d.isPure=a.isPure;a.input=d;e.push(d);a.watchId=c}));var f=[];r(a.body,function(a){f.push(b.recurse(a.expression))});a=0===a.body.length?E:1===a.body.length?f[0]:function(a,b){var c;r(f,function(d){c=
|
||||
d(a,b)});return c};c&&(a.assign=function(a,b,d){return c(a,d,b)});e&&(a.inputs=e);return a},recurse:function(a,b,d){var c,e,f=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case q.Literal:return this.value(a.value,b);case q.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case q.BinaryExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case q.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right),
|
||||
this["binary"+a.operator](c,e,b);case q.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case q.Identifier:return f.identifier(a.name,b,d);case q.MemberExpression:return c=this.recurse(a.object,!1,!!d),a.computed||(e=a.property.name),a.computed&&(e=this.recurse(a.property)),a.computed?this.computedMember(c,e,b,d):this.nonComputedMember(c,e,b,d);case q.CallExpression:return g=[],r(a.arguments,function(a){g.push(f.recurse(a))}),
|
||||
a.filter&&(e=this.$filter(a.callee.name)),a.filter||(e=this.recurse(a.callee,!0)),a.filter?function(a,c,d,f){for(var p=[],n=0;n<g.length;++n)p.push(g[n](a,c,d,f));a=e.apply(void 0,p,f);return b?{context:void 0,name:void 0,value:a}:a}:function(a,c,d,f){var p=e(a,c,d,f),n;if(null!=p.value){n=[];for(var s=0;s<g.length;++s)n.push(g[s](a,c,d,f));n=p.value.apply(p.context,n)}return b?{value:n}:n};case q.AssignmentExpression:return c=this.recurse(a.left,!0,1),e=this.recurse(a.right),function(a,d,f,g){var p=
|
||||
c(a,d,f,g);a=e(a,d,f,g);p.context[p.name]=a;return b?{value:a}:a};case q.ArrayExpression:return g=[],r(a.elements,function(a){g.push(f.recurse(a))}),function(a,c,d,e){for(var f=[],n=0;n<g.length;++n)f.push(g[n](a,c,d,e));return b?{value:f}:f};case q.ObjectExpression:return g=[],r(a.properties,function(a){a.computed?g.push({key:f.recurse(a.key),computed:!0,value:f.recurse(a.value)}):g.push({key:a.key.type===q.Identifier?a.key.name:""+a.key.value,computed:!1,value:f.recurse(a.value)})}),function(a,
|
||||
c,d,e){for(var f={},n=0;n<g.length;++n)g[n].computed?f[g[n].key(a,c,d,e)]=g[n].value(a,c,d,e):f[g[n].key]=g[n].value(a,c,d,e);return b?{value:f}:f};case q.ThisExpression:return function(a){return b?{value:a}:a};case q.LocalsExpression:return function(a,c){return b?{value:c}:c};case q.NGValueParameter:return function(a,c,d){return b?{value:d}:d}}},"unary+":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=w(d)?+d:0;return b?{value:d}:d}},"unary-":function(a,b){return function(d,c,e,f){d=a(d,c,
|
||||
e,f);d=w(d)?-d:-0;return b?{value:d}:d}},"unary!":function(a,b){return function(d,c,e,f){d=!a(d,c,e,f);return b?{value:d}:d}},"binary+":function(a,b,d){return function(c,e,f,g){var k=a(c,e,f,g);c=b(c,e,f,g);k=Ed(k,c);return d?{value:k}:k}},"binary-":function(a,b,d){return function(c,e,f,g){var k=a(c,e,f,g);c=b(c,e,f,g);k=(w(k)?k:0)-(w(c)?c:0);return d?{value:k}:k}},"binary*":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)*b(c,e,f,g);return d?{value:c}:c}},"binary/":function(a,b,d){return function(c,
|
||||
e,f,g){c=a(c,e,f,g)/b(c,e,f,g);return d?{value:c}:c}},"binary%":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)%b(c,e,f,g);return d?{value:c}:c}},"binary===":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)===b(c,e,f,g);return d?{value:c}:c}},"binary!==":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)!==b(c,e,f,g);return d?{value:c}:c}},"binary==":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)==b(c,e,f,g);return d?{value:c}:c}},"binary!=":function(a,b,d){return function(c,
|
||||
e,f,g){c=a(c,e,f,g)!=b(c,e,f,g);return d?{value:c}:c}},"binary<":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<b(c,e,f,g);return d?{value:c}:c}},"binary>":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>b(c,e,f,g);return d?{value:c}:c}},"binary<=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<=b(c,e,f,g);return d?{value:c}:c}},"binary>=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>=b(c,e,f,g);return d?{value:c}:c}},"binary&&":function(a,b,d){return function(c,e,f,g){c=
|
||||
a(c,e,f,g)&&b(c,e,f,g);return d?{value:c}:c}},"binary||":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)||b(c,e,f,g);return d?{value:c}:c}},"ternary?:":function(a,b,d,c){return function(e,f,g,k){e=a(e,f,g,k)?b(e,f,g,k):d(e,f,g,k);return c?{value:e}:e}},value:function(a,b){return function(){return b?{context:void 0,name:void 0,value:a}:a}},identifier:function(a,b,d){return function(c,e,f,g){c=e&&a in e?e:c;d&&1!==d&&c&&null==c[a]&&(c[a]={});e=c?c[a]:void 0;return b?{context:c,name:a,value:e}:
|
||||
e}},computedMember:function(a,b,d,c){return function(e,f,g,k){var h=a(e,f,g,k),l,m;null!=h&&(l=b(e,f,g,k),l+="",c&&1!==c&&h&&!h[l]&&(h[l]={}),m=h[l]);return d?{context:h,name:l,value:m}:m}},nonComputedMember:function(a,b,d,c){return function(e,f,g,k){e=a(e,f,g,k);c&&1!==c&&e&&null==e[b]&&(e[b]={});f=null!=e?e[b]:void 0;return d?{context:e,name:b,value:f}:f}},inputs:function(a,b){return function(d,c,e,f){return f?f[b]:a(d,c,e)}}};Mb.prototype={constructor:Mb,parse:function(a){a=this.getAst(a);var b=
|
||||
this.astCompiler.compile(a.ast),d=a.ast;b.literal=0===d.body.length||1===d.body.length&&(d.body[0].expression.type===q.Literal||d.body[0].expression.type===q.ArrayExpression||d.body[0].expression.type===q.ObjectExpression);b.constant=a.ast.constant;b.oneTime=a.oneTime;return b},getAst:function(a){var b=!1;a=a.trim();":"===a.charAt(0)&&":"===a.charAt(1)&&(b=!0,a=a.substring(2));return{ast:this.ast.ast(a),oneTime:b}}};var Ea=F("$sce"),V={HTML:"html",CSS:"css",MEDIA_URL:"mediaUrl",URL:"url",RESOURCE_URL:"resourceUrl",
|
||||
JS:"js"},Cc=/_([a-z])/g,Ug=F("$templateRequest"),Vg=F("$timeout"),aa=C.document.createElement("a"),Od=ga(C.location.href),Na;aa.href="http://[::1]";var Wg="[::1]"===aa.hostname;Pd.$inject=["$document"];dd.$inject=["$provide"];var Wd=22,Vd=".",Ec="0";Qd.$inject=["$locale"];Sd.$inject=["$locale"];var gh={yyyy:ea("FullYear",4,0,!1,!0),yy:ea("FullYear",2,0,!0,!0),y:ea("FullYear",1,0,!1,!0),MMMM:kb("Month"),MMM:kb("Month",!0),MM:ea("Month",2,1),M:ea("Month",1,1),LLLL:kb("Month",!1,!0),dd:ea("Date",2),
|
||||
d:ea("Date",1),HH:ea("Hours",2),H:ea("Hours",1),hh:ea("Hours",2,-12),h:ea("Hours",1,-12),mm:ea("Minutes",2),m:ea("Minutes",1),ss:ea("Seconds",2),s:ea("Seconds",1),sss:ea("Milliseconds",3),EEEE:kb("Day"),EEE:kb("Day",!0),a:function(a,b){return 12>a.getHours()?b.AMPMS[0]:b.AMPMS[1]},Z:function(a,b,d){a=-1*d;return a=(0<=a?"+":"")+(Ob(Math[0<a?"floor":"ceil"](a/60),2)+Ob(Math.abs(a%60),2))},ww:Yd(2),w:Yd(1),G:Fc,GG:Fc,GGG:Fc,GGGG:function(a,b){return 0>=a.getFullYear()?b.ERANAMES[0]:b.ERANAMES[1]}},
|
||||
fh=/((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))([\s\S]*)/,eh=/^-?\d+$/;Rd.$inject=["$locale"];var $g=ia(K),ah=ia(ub);Td.$inject=["$parse"];var Me=ia({restrict:"E",compile:function(a,b){if(!b.href&&!b.xlinkHref)return function(a,b){if("a"===b[0].nodeName.toLowerCase()){var e="[object SVGAnimatedString]"===la.call(b.prop("href"))?"xlink:href":"href";b.on("click",function(a){b.attr(e)||a.preventDefault()})}}}}),vb={};r(Gb,function(a,b){function d(a,d,e){a.$watch(e[c],
|
||||
function(a){e.$set(b,!!a)})}if("multiple"!==a){var c=wa("ng-"+b),e=d;"checked"===a&&(e=function(a,b,e){e.ngModel!==e[c]&&d(a,b,e)});vb[c]=function(){return{restrict:"A",priority:100,link:e}}}});r(td,function(a,b){vb[b]=function(){return{priority:100,link:function(a,c,e){if("ngPattern"===b&&"/"===e.ngPattern.charAt(0)&&(c=e.ngPattern.match(ie))){e.$set("ngPattern",new RegExp(c[1],c[2]));return}a.$watch(e[b],function(a){e.$set(b,a)})}}}});r(["src","srcset","href"],function(a){var b=wa("ng-"+a);vb[b]=
|
||||
["$sce",function(d){return{priority:99,link:function(c,e,f){var g=a,k=a;"href"===a&&"[object SVGAnimatedString]"===la.call(e.prop("href"))&&(k="xlinkHref",f.$attr[k]="xlink:href",g=null);f.$set(b,d.getTrustedMediaUrl(f[b]));f.$observe(b,function(b){b?(f.$set(k,b),Ca&&g&&e.prop(g,f[k])):"href"===a&&f.$set(k,null)})}}}]});var lb={$addControl:E,$getControls:ia([]),$$renameControl:function(a,b){a.$name=b},$removeControl:E,$setValidity:E,$setDirty:E,$setPristine:E,$setSubmitted:E,$$setSubmitted:E};Pb.$inject=
|
||||
["$element","$attrs","$scope","$animate","$interpolate"];Pb.prototype={$rollbackViewValue:function(){r(this.$$controls,function(a){a.$rollbackViewValue()})},$commitViewValue:function(){r(this.$$controls,function(a){a.$commitViewValue()})},$addControl:function(a){Ja(a.$name,"input");this.$$controls.push(a);a.$name&&(this[a.$name]=a);a.$$parentForm=this},$getControls:function(){return ja(this.$$controls)},$$renameControl:function(a,b){var d=a.$name;this[d]===a&&delete this[d];this[b]=a;a.$name=b},$removeControl:function(a){a.$name&&
|
||||
this[a.$name]===a&&delete this[a.$name];r(this.$pending,function(b,d){this.$setValidity(d,null,a)},this);r(this.$error,function(b,d){this.$setValidity(d,null,a)},this);r(this.$$success,function(b,d){this.$setValidity(d,null,a)},this);cb(this.$$controls,a);a.$$parentForm=lb},$setDirty:function(){this.$$animate.removeClass(this.$$element,Za);this.$$animate.addClass(this.$$element,Vb);this.$dirty=!0;this.$pristine=!1;this.$$parentForm.$setDirty()},$setPristine:function(){this.$$animate.setClass(this.$$element,
|
||||
Za,Vb+" ng-submitted");this.$dirty=!1;this.$pristine=!0;this.$submitted=!1;r(this.$$controls,function(a){a.$setPristine()})},$setUntouched:function(){r(this.$$controls,function(a){a.$setUntouched()})},$setSubmitted:function(){for(var a=this;a.$$parentForm&&a.$$parentForm!==lb;)a=a.$$parentForm;a.$$setSubmitted()},$$setSubmitted:function(){this.$$animate.addClass(this.$$element,"ng-submitted");this.$submitted=!0;r(this.$$controls,function(a){a.$$setSubmitted&&a.$$setSubmitted()})}};ae({clazz:Pb,set:function(a,
|
||||
b,d){var c=a[b];c?-1===c.indexOf(d)&&c.push(d):a[b]=[d]},unset:function(a,b,d){var c=a[b];c&&(cb(c,d),0===c.length&&delete a[b])}});var ke=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||E}return{name:"form",restrict:a?"EAC":"E",require:["form","^^?form"],controller:Pb,compile:function(d,f){d.addClass(Za).addClass(mb);var g=f.name?"name":a&&f.ngForm?"ngForm":!1;return{pre:function(a,d,e,f){var p=f[0];if(!("action"in e)){var n=function(b){a.$apply(function(){p.$commitViewValue();
|
||||
p.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",n);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",n)},0,!1)})}(f[1]||p.$$parentForm).$addControl(p);var s=g?c(p.$name):E;g&&(s(a,p),e.$observe(g,function(b){p.$name!==b&&(s(a,void 0),p.$$parentForm.$$renameControl(p,b),s=c(p.$name),s(a,p))}));d.on("$destroy",function(){p.$$parentForm.$removeControl(p);s(a,void 0);S(p,lb)})}}}}}]},Ne=ke(),Ze=ke(!0),hh=/^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/,
|
||||
sh=/^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i,th=/^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/,ih=/^\s*(-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,le=/^(\d{4,})-(\d{2})-(\d{2})$/,me=/^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Mc=/^(\d{4,})-W(\d\d)$/,ne=/^(\d{4,})-(\d\d)$/,
|
||||
oe=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,ce=T();r(["date","datetime-local","month","time","week"],function(a){ce[a]=!0});var pe={text:function(a,b,d,c,e,f){Sa(a,b,d,c,e,f);Hc(c)},date:nb("date",le,Qb(le,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":nb("datetimelocal",me,Qb(me,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:nb("time",oe,Qb(oe,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:nb("week",Mc,function(a,b){if(ha(a))return a;if(A(a)){Mc.lastIndex=0;var d=Mc.exec(a);
|
||||
if(d){var c=+d[1],e=+d[2],f=d=0,g=0,k=0,h=Xd(c),e=7*(e-1);b&&(d=b.getHours(),f=b.getMinutes(),g=b.getSeconds(),k=b.getMilliseconds());return new Date(c,0,h.getDate()+e,d,f,g,k)}}return NaN},"yyyy-Www"),month:nb("month",ne,Qb(ne,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f,g,k){Ic(a,b,d,c,"number");de(c);Sa(a,b,d,c,e,f);var h;if(w(d.min)||d.ngMin){var l=d.min||k(d.ngMin)(a);h=na(l);c.$validators.min=function(a,b){return c.$isEmpty(b)||z(h)||b>=h};d.$observe("min",function(a){a!==l&&(h=na(a),
|
||||
l=a,c.$validate())})}if(w(d.max)||d.ngMax){var m=d.max||k(d.ngMax)(a),p=na(m);c.$validators.max=function(a,b){return c.$isEmpty(b)||z(p)||b<=p};d.$observe("max",function(a){a!==m&&(p=na(a),m=a,c.$validate())})}if(w(d.step)||d.ngStep){var n=d.step||k(d.ngStep)(a),s=na(n);c.$validators.step=function(a,b){return c.$isEmpty(b)||z(s)||ee(b,h||0,s)};d.$observe("step",function(a){a!==n&&(s=na(a),n=a,c.$validate())})}},url:function(a,b,d,c,e,f){Sa(a,b,d,c,e,f);Hc(c);c.$validators.url=function(a,b){var d=
|
||||
a||b;return c.$isEmpty(d)||sh.test(d)}},email:function(a,b,d,c,e,f){Sa(a,b,d,c,e,f);Hc(c);c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)||th.test(d)}},radio:function(a,b,d,c){var e=!d.ngTrim||"false"!==U(d.ngTrim);z(d.name)&&b.attr("name",++pb);b.on("change",function(a){var g;b[0].checked&&(g=d.value,e&&(g=U(g)),c.$setViewValue(g,a&&a.type))});c.$render=function(){var a=d.value;e&&(a=U(a));b[0].checked=a===c.$viewValue};d.$observe("value",c.$render)},range:function(a,b,d,c,e,f){function g(a,
|
||||
c){b.attr(a,d[a]);var e=d[a];d.$observe(a,function(a){a!==e&&(e=a,c(a))})}function k(a){p=na(a);X(c.$modelValue)||(m?(a=b.val(),p>a&&(a=p,b.val(a)),c.$setViewValue(a)):c.$validate())}function h(a){n=na(a);X(c.$modelValue)||(m?(a=b.val(),n<a&&(b.val(n),a=n<p?p:n),c.$setViewValue(a)):c.$validate())}function l(a){s=na(a);X(c.$modelValue)||(m?c.$viewValue!==b.val()&&c.$setViewValue(b.val()):c.$validate())}Ic(a,b,d,c,"range");de(c);Sa(a,b,d,c,e,f);var m=c.$$hasNativeValidators&&"range"===b[0].type,p=m?
|
||||
0:void 0,n=m?100:void 0,s=m?1:void 0,r=b[0].validity;a=w(d.min);e=w(d.max);f=w(d.step);var q=c.$render;c.$render=m&&w(r.rangeUnderflow)&&w(r.rangeOverflow)?function(){q();c.$setViewValue(b.val())}:q;a&&(p=na(d.min),c.$validators.min=m?function(){return!0}:function(a,b){return c.$isEmpty(b)||z(p)||b>=p},g("min",k));e&&(n=na(d.max),c.$validators.max=m?function(){return!0}:function(a,b){return c.$isEmpty(b)||z(n)||b<=n},g("max",h));f&&(s=na(d.step),c.$validators.step=m?function(){return!r.stepMismatch}:
|
||||
function(a,b){return c.$isEmpty(b)||z(s)||ee(b,p||0,s)},g("step",l))},checkbox:function(a,b,d,c,e,f,g,k){var h=fe(k,a,"ngTrueValue",d.ngTrueValue,!0),l=fe(k,a,"ngFalseValue",d.ngFalseValue,!1);b.on("change",function(a){c.$setViewValue(b[0].checked,a&&a.type)});c.$render=function(){b[0].checked=c.$viewValue};c.$isEmpty=function(a){return!1===a};c.$formatters.push(function(a){return va(a,h)});c.$parsers.push(function(a){return a?h:l})},hidden:E,button:E,submit:E,reset:E,file:E},Yc=["$browser","$sniffer",
|
||||
"$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,k){k[0]&&(pe[K(g.type)]||pe.text)(e,f,g,k[0],b,a,d,c)}}}}],vf=function(){var a={configurable:!0,enumerable:!1,get:function(){return this.getAttribute("value")||""},set:function(a){this.setAttribute("value",a)}};return{restrict:"E",priority:200,compile:function(b,d){if("hidden"===K(d.type))return{pre:function(b,d,f,g){b=d[0];b.parentNode&&b.parentNode.insertBefore(b,b.nextSibling);Object.defineProperty&&
|
||||
Object.defineProperty(b,"value",a)}}}}},uh=/^(true|false|\d+)$/,sf=function(){function a(a,d,c){var e=w(c)?c:9===Ca?"":null;a.prop("value",e);d.$set("value",c)}return{restrict:"A",priority:100,compile:function(b,d){return uh.test(d.ngValue)?function(b,d,f){b=b.$eval(f.ngValue);a(d,f,b)}:function(b,d,f){b.$watch(f.ngValue,function(b){a(d,f,b)})}}}},Re=["$compile",function(a){return{restrict:"AC",compile:function(b){a.$$addBindingClass(b);return function(b,c,e){a.$$addBindingInfo(c,e.ngBind);c=c[0];
|
||||
b.$watch(e.ngBind,function(a){c.textContent=ic(a)})}}}}],Te=["$interpolate","$compile",function(a,b){return{compile:function(d){b.$$addBindingClass(d);return function(c,d,f){c=a(d.attr(f.$attr.ngBindTemplate));b.$$addBindingInfo(d,c.expressions);d=d[0];f.$observe("ngBindTemplate",function(a){d.textContent=z(a)?"":a})}}}}],Se=["$sce","$parse","$compile",function(a,b,d){return{restrict:"A",compile:function(c,e){var f=b(e.ngBindHtml),g=b(e.ngBindHtml,function(b){return a.valueOf(b)});d.$$addBindingClass(c);
|
||||
return function(b,c,e){d.$$addBindingInfo(c,e.ngBindHtml);b.$watch(g,function(){var d=f(b);c.html(a.getTrustedHtml(d)||"")})}}}}],rf=ia({restrict:"A",require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),Ue=Kc("",!0),We=Kc("Odd",0),Ve=Kc("Even",1),Xe=Ra({compile:function(a,b){b.$set("ngCloak",void 0);a.removeClass("ng-cloak")}}),Ye=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],cd={},vh={blur:!0,focus:!0};r("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),
|
||||
function(a){var b=wa("ng-"+a);cd[b]=["$parse","$rootScope","$exceptionHandler",function(d,c,e){return qd(d,c,e,b,a,vh[a])}]});var af=["$animate","$compile",function(a,b){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(d,c,e,f,g){var k,h,l;d.$watch(e.ngIf,function(d){d?h||g(function(d,f){h=f;d[d.length++]=b.$$createComment("end ngIf",e.ngIf);k={clone:d};a.enter(d,c.parent(),c)}):(l&&(l.remove(),l=null),h&&(h.$destroy(),h=null),k&&(l=tb(k.clone),
|
||||
a.leave(l).done(function(a){!1!==a&&(l=null)}),k=null))})}}}],bf=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:ca.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",k=e.autoscroll;return function(c,e,m,p,n){var r=0,q,t,x,v=function(){t&&(t.remove(),t=null);q&&(q.$destroy(),q=null);x&&(d.leave(x).done(function(a){!1!==a&&(t=null)}),t=x,x=null)};c.$watch(f,function(f){var m=function(a){!1===
|
||||
a||!w(k)||k&&!c.$eval(k)||b()},t=++r;f?(a(f,!0).then(function(a){if(!c.$$destroyed&&t===r){var b=c.$new();p.template=a;a=n(b,function(a){v();d.enter(a,null,e).done(m)});q=b;x=a;q.$emit("$includeContentLoaded",f);c.$eval(g)}},function(){c.$$destroyed||t!==r||(v(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(v(),p.template=null)})}}}}],uf=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(b,d,c,e){la.call(d[0]).match(/SVG/)?
|
||||
(d.empty(),a(ed(e.template,C.document).childNodes)(b,function(a){d.append(a)},{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],cf=Ra({priority:450,compile:function(){return{pre:function(a,b,d){a.$eval(d.ngInit)}}}}),qf=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=d.ngList||", ",f="false"!==d.ngTrim,g=f?U(e):e;c.$parsers.push(function(a){if(!z(a)){var b=[];a&&r(a.split(g),function(a){a&&b.push(f?U(a):a)});return b}});c.$formatters.push(function(a){if(H(a))return a.join(e)});
|
||||
c.$isEmpty=function(a){return!a||!a.length}}}},mb="ng-valid",$d="ng-invalid",Za="ng-pristine",Vb="ng-dirty",ob=F("ngModel");Rb.$inject="$scope $exceptionHandler $attrs $element $parse $animate $timeout $q $interpolate".split(" ");Rb.prototype={$$initGetterSetters:function(){if(this.$options.getOption("getterSetter")){var a=this.$$parse(this.$$attr.ngModel+"()"),b=this.$$parse(this.$$attr.ngModel+"($$$p)");this.$$ngModelGet=function(b){var c=this.$$parsedNgModel(b);B(c)&&(c=a(b));return c};this.$$ngModelSet=
|
||||
function(a,c){B(this.$$parsedNgModel(a))?b(a,{$$$p:c}):this.$$parsedNgModelAssign(a,c)}}else if(!this.$$parsedNgModel.assign)throw ob("nonassign",this.$$attr.ngModel,za(this.$$element));},$render:E,$isEmpty:function(a){return z(a)||""===a||null===a||a!==a},$$updateEmptyClasses:function(a){this.$isEmpty(a)?(this.$$animate.removeClass(this.$$element,"ng-not-empty"),this.$$animate.addClass(this.$$element,"ng-empty")):(this.$$animate.removeClass(this.$$element,"ng-empty"),this.$$animate.addClass(this.$$element,
|
||||
"ng-not-empty"))},$setPristine:function(){this.$dirty=!1;this.$pristine=!0;this.$$animate.removeClass(this.$$element,Vb);this.$$animate.addClass(this.$$element,Za)},$setDirty:function(){this.$dirty=!0;this.$pristine=!1;this.$$animate.removeClass(this.$$element,Za);this.$$animate.addClass(this.$$element,Vb);this.$$parentForm.$setDirty()},$setUntouched:function(){this.$touched=!1;this.$untouched=!0;this.$$animate.setClass(this.$$element,"ng-untouched","ng-touched")},$setTouched:function(){this.$touched=
|
||||
!0;this.$untouched=!1;this.$$animate.setClass(this.$$element,"ng-touched","ng-untouched")},$rollbackViewValue:function(){this.$$timeout.cancel(this.$$pendingDebounce);this.$viewValue=this.$$lastCommittedViewValue;this.$render()},$validate:function(){if(!X(this.$modelValue)){var a=this.$$lastCommittedViewValue,b=this.$$rawModelValue,d=this.$valid,c=this.$modelValue,e=this.$options.getOption("allowInvalid"),f=this;this.$$runValidators(b,a,function(a){e||d===a||(f.$modelValue=a?b:void 0,f.$modelValue!==
|
||||
c&&f.$$writeModelToScope())})}},$$runValidators:function(a,b,d){function c(){var c=!0;r(h.$validators,function(d,e){var g=Boolean(d(a,b));c=c&&g;f(e,g)});return c?!0:(r(h.$asyncValidators,function(a,b){f(b,null)}),!1)}function e(){var c=[],d=!0;r(h.$asyncValidators,function(e,g){var h=e(a,b);if(!h||!B(h.then))throw ob("nopromise",h);f(g,void 0);c.push(h.then(function(){f(g,!0)},function(){d=!1;f(g,!1)}))});c.length?h.$$q.all(c).then(function(){g(d)},E):g(!0)}function f(a,b){k===h.$$currentValidationRunId&&
|
||||
h.$setValidity(a,b)}function g(a){k===h.$$currentValidationRunId&&d(a)}this.$$currentValidationRunId++;var k=this.$$currentValidationRunId,h=this;(function(){var a=h.$$parserName;if(z(h.$$parserValid))f(a,null);else return h.$$parserValid||(r(h.$validators,function(a,b){f(b,null)}),r(h.$asyncValidators,function(a,b){f(b,null)})),f(a,h.$$parserValid),h.$$parserValid;return!0})()?c()?e():g(!1):g(!1)},$commitViewValue:function(){var a=this.$viewValue;this.$$timeout.cancel(this.$$pendingDebounce);if(this.$$lastCommittedViewValue!==
|
||||
a||""===a&&this.$$hasNativeValidators)this.$$updateEmptyClasses(a),this.$$lastCommittedViewValue=a,this.$pristine&&this.$setDirty(),this.$$parseAndValidate()},$$parseAndValidate:function(){var a=this.$$lastCommittedViewValue,b=this;this.$$parserValid=z(a)?void 0:!0;this.$setValidity(this.$$parserName,null);this.$$parserName="parse";if(this.$$parserValid)for(var d=0;d<this.$parsers.length;d++)if(a=this.$parsers[d](a),z(a)){this.$$parserValid=!1;break}X(this.$modelValue)&&(this.$modelValue=this.$$ngModelGet(this.$$scope));
|
||||
var c=this.$modelValue,e=this.$options.getOption("allowInvalid");this.$$rawModelValue=a;e&&(this.$modelValue=a,b.$modelValue!==c&&b.$$writeModelToScope());this.$$runValidators(a,this.$$lastCommittedViewValue,function(d){e||(b.$modelValue=d?a:void 0,b.$modelValue!==c&&b.$$writeModelToScope())})},$$writeModelToScope:function(){this.$$ngModelSet(this.$$scope,this.$modelValue);r(this.$viewChangeListeners,function(a){try{a()}catch(b){this.$$exceptionHandler(b)}},this)},$setViewValue:function(a,b){this.$viewValue=
|
||||
a;this.$options.getOption("updateOnDefault")&&this.$$debounceViewValueCommit(b)},$$debounceViewValueCommit:function(a){var b=this.$options.getOption("debounce");W(b[a])?b=b[a]:W(b["default"])&&-1===this.$options.getOption("updateOn").indexOf(a)?b=b["default"]:W(b["*"])&&(b=b["*"]);this.$$timeout.cancel(this.$$pendingDebounce);var d=this;0<b?this.$$pendingDebounce=this.$$timeout(function(){d.$commitViewValue()},b):this.$$rootScope.$$phase?this.$commitViewValue():this.$$scope.$apply(function(){d.$commitViewValue()})},
|
||||
$overrideModelOptions:function(a){this.$options=this.$options.createChild(a);this.$$setUpdateOnEvents()},$processModelValue:function(){var a=this.$$format();this.$viewValue!==a&&(this.$$updateEmptyClasses(a),this.$viewValue=this.$$lastCommittedViewValue=a,this.$render(),this.$$runValidators(this.$modelValue,this.$viewValue,E))},$$format:function(){for(var a=this.$formatters,b=a.length,d=this.$modelValue;b--;)d=a[b](d);return d},$$setModelValue:function(a){this.$modelValue=this.$$rawModelValue=a;this.$$parserValid=
|
||||
void 0;this.$processModelValue()},$$setUpdateOnEvents:function(){this.$$updateEvents&&this.$$element.off(this.$$updateEvents,this.$$updateEventHandler);if(this.$$updateEvents=this.$options.getOption("updateOn"))this.$$element.on(this.$$updateEvents,this.$$updateEventHandler)},$$updateEventHandler:function(a){this.$$debounceViewValueCommit(a&&a.type)}};ae({clazz:Rb,set:function(a,b){a[b]=!0},unset:function(a,b){delete a[b]}});var pf=["$rootScope",function(a){return{restrict:"A",require:["ngModel",
|
||||
"^?form","^?ngModelOptions"],controller:Rb,priority:1,compile:function(b){b.addClass(Za).addClass("ng-untouched").addClass(mb);return{pre:function(a,b,e,f){var g=f[0];b=f[1]||g.$$parentForm;if(f=f[2])g.$options=f.$options;g.$$initGetterSetters();b.$addControl(g);e.$observe("name",function(a){g.$name!==a&&g.$$parentForm.$$renameControl(g,a)});a.$on("$destroy",function(){g.$$parentForm.$removeControl(g)})},post:function(b,c,e,f){function g(){k.$setTouched()}var k=f[0];k.$$setUpdateOnEvents();c.on("blur",
|
||||
function(){k.$touched||(a.$$phase?b.$evalAsync(g):b.$apply(g))})}}}}}],Sb,wh=/(\s+|^)default(\s+|$)/;Lc.prototype={getOption:function(a){return this.$$options[a]},createChild:function(a){var b=!1;a=S({},a);r(a,function(d,c){"$inherit"===d?"*"===c?b=!0:(a[c]=this.$$options[c],"updateOn"===c&&(a.updateOnDefault=this.$$options.updateOnDefault)):"updateOn"===c&&(a.updateOnDefault=!1,a[c]=U(d.replace(wh,function(){a.updateOnDefault=!0;return" "})))},this);b&&(delete a["*"],ge(a,this.$$options));ge(a,Sb.$$options);
|
||||
return new Lc(a)}};Sb=new Lc({updateOn:"",updateOnDefault:!0,debounce:0,getterSetter:!1,allowInvalid:!1,timezone:null});var tf=function(){function a(a,d){this.$$attrs=a;this.$$scope=d}a.$inject=["$attrs","$scope"];a.prototype={$onInit:function(){var a=this.parentCtrl?this.parentCtrl.$options:Sb,d=this.$$scope.$eval(this.$$attrs.ngModelOptions);this.$options=a.createChild(d)}};return{restrict:"A",priority:10,require:{parentCtrl:"?^^ngModelOptions"},bindToController:!0,controller:a}},df=Ra({terminal:!0,
|
||||
priority:1E3}),xh=F("ngOptions"),yh=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([$\w][$\w]*)|(?:\(\s*([$\w][$\w]*)\s*,\s*([$\w][$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,nf=["$compile","$document","$parse",function(a,b,d){function c(a,b,c){function e(a,b,c,d,f){this.selectValue=a;this.viewValue=b;this.label=c;this.group=d;this.disabled=f}function f(a){var b;if(!r&&ya(a))b=a;else{b=[];for(var c in a)a.hasOwnProperty(c)&&
|
||||
"$"!==c.charAt(0)&&b.push(c)}return b}var p=a.match(yh);if(!p)throw xh("iexp",a,za(b));var n=p[5]||p[7],r=p[6];a=/ as /.test(p[0])&&p[1];var q=p[9];b=d(p[2]?p[1]:n);var t=a&&d(a)||b,w=q&&d(q),v=q?function(a,b){return w(c,b)}:function(a){return La(a)},x=function(a,b){return v(a,A(a,b))},z=d(p[2]||p[1]),y=d(p[3]||""),J=d(p[4]||""),I=d(p[8]),B={},A=r?function(a,b){B[r]=b;B[n]=a;return B}:function(a){B[n]=a;return B};return{trackBy:q,getTrackByValue:x,getWatchables:d(I,function(a){var b=[];a=a||[];for(var d=
|
||||
f(a),e=d.length,g=0;g<e;g++){var k=a===d?g:d[g],l=a[k],k=A(l,k),l=v(l,k);b.push(l);if(p[2]||p[1])l=z(c,k),b.push(l);p[4]&&(k=J(c,k),b.push(k))}return b}),getOptions:function(){for(var a=[],b={},d=I(c)||[],g=f(d),k=g.length,n=0;n<k;n++){var p=d===g?n:g[n],r=A(d[p],p),s=t(c,r),p=v(s,r),w=z(c,r),B=y(c,r),r=J(c,r),s=new e(p,s,w,B,r);a.push(s);b[p]=s}return{items:a,selectValueMap:b,getOptionFromViewValue:function(a){return b[x(a)]},getViewValueFromOption:function(a){return q?Ia(a.viewValue):a.viewValue}}}}}
|
||||
var e=C.document.createElement("option"),f=C.document.createElement("optgroup");return{restrict:"A",terminal:!0,require:["select","ngModel"],link:{pre:function(a,b,c,d){d[0].registerOption=E},post:function(d,k,h,l){function m(a){var b=(a=v.getOptionFromViewValue(a))&&a.element;b&&!b.selected&&(b.selected=!0);return a}function p(a,b){a.element=b;b.disabled=a.disabled;a.label!==b.label&&(b.label=a.label,b.textContent=a.label);b.value=a.selectValue}var n=l[0],q=l[1],z=h.multiple;l=0;for(var t=k.children(),
|
||||
B=t.length;l<B;l++)if(""===t[l].value){n.hasEmptyOption=!0;n.emptyOption=t.eq(l);break}k.empty();l=!!n.emptyOption;x(e.cloneNode(!1)).val("?");var v,A=c(h.ngOptions,k,d),C=b[0].createDocumentFragment();n.generateUnknownOptionValue=function(a){return"?"};z?(n.writeValue=function(a){if(v){var b=a&&a.map(m)||[];v.items.forEach(function(a){a.element.selected&&-1===Array.prototype.indexOf.call(b,a)&&(a.element.selected=!1)})}},n.readValue=function(){var a=k.val()||[],b=[];r(a,function(a){(a=v.selectValueMap[a])&&
|
||||
!a.disabled&&b.push(v.getViewValueFromOption(a))});return b},A.trackBy&&d.$watchCollection(function(){if(H(q.$viewValue))return q.$viewValue.map(function(a){return A.getTrackByValue(a)})},function(){q.$render()})):(n.writeValue=function(a){if(v){var b=k[0].options[k[0].selectedIndex],c=v.getOptionFromViewValue(a);b&&b.removeAttribute("selected");c?(k[0].value!==c.selectValue&&(n.removeUnknownOption(),k[0].value=c.selectValue,c.element.selected=!0),c.element.setAttribute("selected","selected")):n.selectUnknownOrEmptyOption(a)}},
|
||||
n.readValue=function(){var a=v.selectValueMap[k.val()];return a&&!a.disabled?(n.unselectEmptyOption(),n.removeUnknownOption(),v.getViewValueFromOption(a)):null},A.trackBy&&d.$watch(function(){return A.getTrackByValue(q.$viewValue)},function(){q.$render()}));l&&(a(n.emptyOption)(d),k.prepend(n.emptyOption),8===n.emptyOption[0].nodeType?(n.hasEmptyOption=!1,n.registerOption=function(a,b){""===b.val()&&(n.hasEmptyOption=!0,n.emptyOption=b,n.emptyOption.removeClass("ng-scope"),q.$render(),b.on("$destroy",
|
||||
function(){var a=n.$isEmptyOptionSelected();n.hasEmptyOption=!1;n.emptyOption=void 0;a&&q.$render()}))}):n.emptyOption.removeClass("ng-scope"));d.$watchCollection(A.getWatchables,function(){var a=v&&n.readValue();if(v)for(var b=v.items.length-1;0<=b;b--){var c=v.items[b];w(c.group)?Fb(c.element.parentNode):Fb(c.element)}v=A.getOptions();var d={};v.items.forEach(function(a){var b;if(w(a.group)){b=d[a.group];b||(b=f.cloneNode(!1),C.appendChild(b),b.label=null===a.group?"null":a.group,d[a.group]=b);
|
||||
var c=e.cloneNode(!1);b.appendChild(c);p(a,c)}else b=e.cloneNode(!1),C.appendChild(b),p(a,b)});k[0].appendChild(C);q.$render();q.$isEmpty(a)||(b=n.readValue(),(A.trackBy||z?va(a,b):a===b)||(q.$setViewValue(b),q.$render()))})}}}}],ef=["$locale","$interpolate","$log",function(a,b,d){var c=/{}/g,e=/^when(Minus)?(.+)$/;return{link:function(f,g,k){function h(a){g.text(a||"")}var l=k.count,m=k.$attr.when&&g.attr(k.$attr.when),p=k.offset||0,n=f.$eval(m)||{},q={},w=b.startSymbol(),t=b.endSymbol(),x=w+l+"-"+
|
||||
p+t,v=ca.noop,A;r(k,function(a,b){var c=e.exec(b);c&&(c=(c[1]?"-":"")+K(c[2]),n[c]=g.attr(k.$attr[b]))});r(n,function(a,d){q[d]=b(a.replace(c,x))});f.$watch(l,function(b){var c=parseFloat(b),e=X(c);e||c in n||(c=a.pluralCat(c-p));c===A||e&&X(A)||(v(),e=q[c],z(e)?(null!=b&&d.debug("ngPluralize: no rule defined for '"+c+"' in "+m),v=E,h()):v=f.$watch(e,h),A=c)})}}}],qe=F("ngRef"),ff=["$parse",function(a){return{priority:-1,restrict:"A",compile:function(b,d){var c=wa(ua(b)),e=a(d.ngRef),f=e.assign||
|
||||
function(){throw qe("nonassign",d.ngRef);};return function(a,b,h){var l;if(h.hasOwnProperty("ngRefRead"))if("$element"===h.ngRefRead)l=b;else{if(l=b.data("$"+h.ngRefRead+"Controller"),!l)throw qe("noctrl",h.ngRefRead,d.ngRef);}else l=b.data("$"+c+"Controller");l=l||b;f(a,l);b.on("$destroy",function(){e(a)===l&&f(a,null)})}}}}],gf=["$parse","$animate","$compile",function(a,b,d){var c=F("ngRepeat"),e=function(a,b,c,d,e,f,g){a[c]=d;e&&(a[e]=f);a.$index=b;a.$first=0===b;a.$last=b===g-1;a.$middle=!(a.$first||
|
||||
a.$last);a.$odd=!(a.$even=0===(b&1))},f=function(a,b,c){return La(c)},g=function(a,b){return b};return{restrict:"A",multiElement:!0,transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,compile:function(k,h){var l=h.ngRepeat,m=d.$$createComment("end ngRepeat",l),p=l.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!p)throw c("iexp",l);var n=p[1],q=p[2],w=p[3],t=p[4],p=n.match(/^(?:(\s*[$\w]+)|\(\s*([$\w]+)\s*,\s*([$\w]+)\s*\))$/);if(!p)throw c("iidexp",
|
||||
n);var x=p[3]||p[1],v=p[2];if(w&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(w)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(w)))throw c("badident",w);var z;if(t){var A={$id:La},y=a(t);z=function(a,b,c,d){v&&(A[v]=b);A[x]=c;A.$index=d;return y(a,A)}}return function(a,d,h,k,n){var p=T();a.$watchCollection(q,function(h){var k,q,t=d[0],s,y=T(),B,C,E,D,H,F,K;w&&(a[w]=h);if(ya(h))H=h,q=z||f;else for(K in q=z||g,H=[],h)ta.call(h,K)&&"$"!==K.charAt(0)&&H.push(K);
|
||||
B=H.length;K=Array(B);for(k=0;k<B;k++)if(C=h===H?k:H[k],E=h[C],D=q(a,C,E,k),p[D])F=p[D],delete p[D],y[D]=F,K[k]=F;else{if(y[D])throw r(K,function(a){a&&a.scope&&(p[a.id]=a)}),c("dupes",l,D,E);K[k]={id:D,scope:void 0,clone:void 0};y[D]=!0}A&&(A[x]=void 0);for(s in p){F=p[s];D=tb(F.clone);b.leave(D);if(D[0].parentNode)for(k=0,q=D.length;k<q;k++)D[k].$$NG_REMOVED=!0;F.scope.$destroy()}for(k=0;k<B;k++)if(C=h===H?k:H[k],E=h[C],F=K[k],F.scope){s=t;do s=s.nextSibling;while(s&&s.$$NG_REMOVED);F.clone[0]!==
|
||||
s&&b.move(tb(F.clone),null,t);t=F.clone[F.clone.length-1];e(F.scope,k,x,E,v,C,B)}else n(function(a,c){F.scope=c;var d=m.cloneNode(!1);a[a.length++]=d;b.enter(a,null,t);t=d;F.clone=a;y[F.id]=F;e(F.scope,k,x,E,v,C,B)});p=y})}}}}],hf=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngShow,function(b){a[b?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],$e=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,
|
||||
d,c){b.$watch(c.ngHide,function(b){a[b?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],jf=Ra(function(a,b,d){a.$watchCollection(d.ngStyle,function(a,d){d&&a!==d&&r(d,function(a,c){b.css(c,"")});a&&b.css(a)})}),kf=["$animate","$compile",function(a,b){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(d,c,e,f){var g=[],k=[],h=[],l=[],m=function(a,b){return function(c){!1!==c&&a.splice(b,1)}};d.$watch(e.ngSwitch||e.on,function(c){for(var d,
|
||||
e;h.length;)a.cancel(h.pop());d=0;for(e=l.length;d<e;++d){var q=tb(k[d].clone);l[d].$destroy();(h[d]=a.leave(q)).done(m(h,d))}k.length=0;l.length=0;(g=f.cases["!"+c]||f.cases["?"])&&r(g,function(c){c.transclude(function(d,e){l.push(e);var f=c.element;d[d.length++]=b.$$createComment("end ngSwitchWhen");k.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],lf=Ra({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){a=d.ngSwitchWhen.split(d.ngSwitchWhenSeparator).sort().filter(function(a,
|
||||
b,c){return c[b-1]!==a});r(a,function(a){c.cases["!"+a]=c.cases["!"+a]||[];c.cases["!"+a].push({transclude:e,element:b})})}}),mf=Ra({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["?"]=c.cases["?"]||[];c.cases["?"].push({transclude:e,element:b})}}),zh=F("ngTransclude"),of=["$compile",function(a){return{restrict:"EAC",compile:function(b){var d=a(b.contents());b.empty();return function(a,b,f,g,k){function h(){d(a,function(a){b.append(a)})}if(!k)throw zh("orphan",
|
||||
za(b));f.ngTransclude===f.$attr.ngTransclude&&(f.ngTransclude="");f=f.ngTransclude||f.ngTranscludeSlot;k(function(a,c){var d;if(d=a.length)a:{d=0;for(var f=a.length;d<f;d++){var g=a[d];if(g.nodeType!==Pa||g.nodeValue.trim()){d=!0;break a}}d=void 0}d?b.append(a):(h(),c.$destroy())},null,f);f&&!k.isSlotFilled(f)&&h()}}}}],Oe=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(b,d){"text/ng-template"===d.type&&a.put(d.id,b[0].text)}}}],Ah={$setViewValue:E,$render:E},Bh=["$element",
|
||||
"$scope",function(a,b){function d(){g||(g=!0,b.$$postDigest(function(){g=!1;e.ngModelCtrl.$render()}))}function c(a){k||(k=!0,b.$$postDigest(function(){b.$$destroyed||(k=!1,e.ngModelCtrl.$setViewValue(e.readValue()),a&&e.ngModelCtrl.$render())}))}var e=this,f=new Hb;e.selectValueMap={};e.ngModelCtrl=Ah;e.multiple=!1;e.unknownOption=x(C.document.createElement("option"));e.hasEmptyOption=!1;e.emptyOption=void 0;e.renderUnknownOption=function(b){b=e.generateUnknownOptionValue(b);e.unknownOption.val(b);
|
||||
a.prepend(e.unknownOption);Oa(e.unknownOption,!0);a.val(b)};e.updateUnknownOption=function(b){b=e.generateUnknownOptionValue(b);e.unknownOption.val(b);Oa(e.unknownOption,!0);a.val(b)};e.generateUnknownOptionValue=function(a){return"? "+La(a)+" ?"};e.removeUnknownOption=function(){e.unknownOption.parent()&&e.unknownOption.remove()};e.selectEmptyOption=function(){e.emptyOption&&(a.val(""),Oa(e.emptyOption,!0))};e.unselectEmptyOption=function(){e.hasEmptyOption&&Oa(e.emptyOption,!1)};b.$on("$destroy",
|
||||
function(){e.renderUnknownOption=E});e.readValue=function(){var b=a.val(),b=b in e.selectValueMap?e.selectValueMap[b]:b;return e.hasOption(b)?b:null};e.writeValue=function(b){var c=a[0].options[a[0].selectedIndex];c&&Oa(x(c),!1);e.hasOption(b)?(e.removeUnknownOption(),c=La(b),a.val(c in e.selectValueMap?c:b),Oa(x(a[0].options[a[0].selectedIndex]),!0)):e.selectUnknownOrEmptyOption(b)};e.addOption=function(a,b){if(8!==b[0].nodeType){Ja(a,'"option value"');""===a&&(e.hasEmptyOption=!0,e.emptyOption=
|
||||
b);var c=f.get(a)||0;f.set(a,c+1);d()}};e.removeOption=function(a){var b=f.get(a);b&&(1===b?(f.delete(a),""===a&&(e.hasEmptyOption=!1,e.emptyOption=void 0)):f.set(a,b-1))};e.hasOption=function(a){return!!f.get(a)};e.$hasEmptyOption=function(){return e.hasEmptyOption};e.$isUnknownOptionSelected=function(){return a[0].options[0]===e.unknownOption[0]};e.$isEmptyOptionSelected=function(){return e.hasEmptyOption&&a[0].options[a[0].selectedIndex]===e.emptyOption[0]};e.selectUnknownOrEmptyOption=function(a){null==
|
||||
a&&e.emptyOption?(e.removeUnknownOption(),e.selectEmptyOption()):e.unknownOption.parent().length?e.updateUnknownOption(a):e.renderUnknownOption(a)};var g=!1,k=!1;e.registerOption=function(a,b,f,g,k){if(f.$attr.ngValue){var q,r;f.$observe("value",function(a){var d,f=b.prop("selected");w(r)&&(e.removeOption(q),delete e.selectValueMap[r],d=!0);r=La(a);q=a;e.selectValueMap[r]=a;e.addOption(a,b);b.attr("value",r);d&&f&&c()})}else g?f.$observe("value",function(a){e.readValue();var d,f=b.prop("selected");
|
||||
w(q)&&(e.removeOption(q),d=!0);q=a;e.addOption(a,b);d&&f&&c()}):k?a.$watch(k,function(a,d){f.$set("value",a);var g=b.prop("selected");d!==a&&e.removeOption(d);e.addOption(a,b);d&&g&&c()}):e.addOption(f.value,b);f.$observe("disabled",function(a){if("true"===a||a&&b.prop("selected"))e.multiple?c(!0):(e.ngModelCtrl.$setViewValue(null),e.ngModelCtrl.$render())});b.on("$destroy",function(){var a=e.readValue(),b=f.value;e.removeOption(b);d();(e.multiple&&a&&-1!==a.indexOf(b)||a===b)&&c(!0)})}}],Pe=function(){return{restrict:"E",
|
||||
require:["select","?ngModel"],controller:Bh,priority:1,link:{pre:function(a,b,d,c){var e=c[0],f=c[1];if(f){if(e.ngModelCtrl=f,b.on("change",function(){e.removeUnknownOption();a.$apply(function(){f.$setViewValue(e.readValue())})}),d.multiple){e.multiple=!0;e.readValue=function(){var a=[];r(b.find("option"),function(b){b.selected&&!b.disabled&&(b=b.value,a.push(b in e.selectValueMap?e.selectValueMap[b]:b))});return a};e.writeValue=function(a){r(b.find("option"),function(b){var c=!!a&&(-1!==Array.prototype.indexOf.call(a,
|
||||
b.value)||-1!==Array.prototype.indexOf.call(a,e.selectValueMap[b.value]));c!==b.selected&&Oa(x(b),c)})};var g,k=NaN;a.$watch(function(){k!==f.$viewValue||va(g,f.$viewValue)||(g=ja(f.$viewValue),f.$render());k=f.$viewValue});f.$isEmpty=function(a){return!a||0===a.length}}}else e.registerOption=E},post:function(a,b,d,c){var e=c[1];if(e){var f=c[0];e.$render=function(){f.writeValue(e.$viewValue)}}}}}},Qe=["$interpolate",function(a){return{restrict:"E",priority:100,compile:function(b,d){var c,e;w(d.ngValue)||
|
||||
(w(d.value)?c=a(d.value,!0):(e=a(b.text(),!0))||d.$set("value",b.text()));return function(a,b,d){var h=b.parent();(h=h.data("$selectController")||h.parent().data("$selectController"))&&h.registerOption(a,b,d,c,e)}}}}],$c=["$parse",function(a){return{restrict:"A",require:"?ngModel",link:function(b,d,c,e){if(e){var f=c.hasOwnProperty("required")||a(c.ngRequired)(b);c.ngRequired||(c.required=!0);e.$validators.required=function(a,b){return!f||!e.$isEmpty(b)};c.$observe("required",function(a){f!==a&&(f=
|
||||
a,e.$validate())})}}}}],Zc=["$parse",function(a){return{restrict:"A",require:"?ngModel",compile:function(b,d){var c,e;d.ngPattern&&(c=d.ngPattern,e="/"===d.ngPattern.charAt(0)&&ie.test(d.ngPattern)?function(){return d.ngPattern}:a(d.ngPattern));return function(a,b,d,h){if(h){var l=d.pattern;d.ngPattern?l=e(a):c=d.pattern;var m=he(l,c,b);d.$observe("pattern",function(a){var d=m;m=he(a,c,b);(d&&d.toString())!==(m&&m.toString())&&h.$validate()});h.$validators.pattern=function(a,b){return h.$isEmpty(b)||
|
||||
z(m)||m.test(b)}}}}}}],bd=["$parse",function(a){return{restrict:"A",require:"?ngModel",link:function(b,d,c,e){if(e){var f=c.maxlength||a(c.ngMaxlength)(b),g=Tb(f);c.$observe("maxlength",function(a){f!==a&&(g=Tb(a),f=a,e.$validate())});e.$validators.maxlength=function(a,b){return 0>g||e.$isEmpty(b)||b.length<=g}}}}}],ad=["$parse",function(a){return{restrict:"A",require:"?ngModel",link:function(b,d,c,e){if(e){var f=c.minlength||a(c.ngMinlength)(b),g=Tb(f)||-1;c.$observe("minlength",function(a){f!==
|
||||
a&&(g=Tb(a)||-1,f=a,e.$validate())});e.$validators.minlength=function(a,b){return e.$isEmpty(b)||b.length>=g}}}}}];C.angular.bootstrap?C.console&&console.log("WARNING: Tried to load AngularJS more than once."):(Fe(),Je(ca),ca.module("ngLocale",[],["$provide",function(a){function b(a){a+="";var b=a.indexOf(".");return-1==b?0:a.length-b-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],
|
||||
ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),STANDALONEMONTH:"January February March April May June July August September October November December".split(" "),WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a",
|
||||
"short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",localeID:"en_US",pluralCat:function(a,c){var e=a|0,f=c;void 0===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),x(function(){Ae(C.document,
|
||||
Uc)}))})(window);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
|
||||
//# sourceMappingURL=angular.min.js.map
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
12
admin/static/js/bootstrap.min.js
vendored
12
admin/static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
355
admin/static/js/common.js
Normal file
355
admin/static/js/common.js
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
var alertNbLines = true;
|
||||
|
||||
function treatFlagKey(flag) {
|
||||
if (flag.values !== undefined) {
|
||||
if (flag.separator) {
|
||||
for (var i = flag.values.length - 1; i >= 0; i--) {
|
||||
if (flag.nb_lines && (flag.values[i] == undefined || !flag.values[i].length)) {
|
||||
if (alertNbLines) {
|
||||
alertNbLines = false;
|
||||
if (!confirm("Lorsque plusieurs flags sont attendus pour une même question, ceux-ci ne sont pas validés un par un. Ils ne sont validés qu'une fois tous les champs remplis correctement. (Sauf mention contraire, l'ordre n'importe pas)"))
|
||||
console.log(flag.values[9999].length); // Launch exception here to avoid form validation
|
||||
}
|
||||
}
|
||||
else if (!flag.values[i].length) {
|
||||
flag.values.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (flag.ignore_order)
|
||||
flag.value = flag.values.slice().sort().join(flag.separator) + flag.separator;
|
||||
else
|
||||
flag.value = flag.values.join(flag.separator) + flag.separator;
|
||||
|
||||
if (flag.values.length == 0)
|
||||
flag.values = [""];
|
||||
}
|
||||
else
|
||||
flag.value = flag.values[0];
|
||||
}
|
||||
|
||||
if (flag.found == null && flag.soluce !== undefined) {
|
||||
if (flag.value && flag.soluce) {
|
||||
if (flag.ignore_case)
|
||||
flag.value = flag.value.toLowerCase();
|
||||
if (flag.capture_regexp) {
|
||||
var re = new RegExp(flag.capture_regexp, flag.ignore_case?'ui':'u');
|
||||
var match = re.exec(flag.value);
|
||||
match.shift();
|
||||
flag.value = match.join("+");
|
||||
}
|
||||
|
||||
if (flag.soluce == b2sum(flag.value))
|
||||
flag.found = new Date();
|
||||
}
|
||||
}
|
||||
return flag.found !== undefined && flag.found !== false;
|
||||
}
|
||||
|
||||
String.prototype.capitalize = function() {
|
||||
return this
|
||||
.toLowerCase()
|
||||
.replace(
|
||||
/(^|\s|-)([a-z])/g,
|
||||
function(m,p1,p2) { return p1+p2.toUpperCase(); }
|
||||
);
|
||||
}
|
||||
|
||||
Array.prototype.inArray = function(v) {
|
||||
return this.reduce(function(presence, current) {
|
||||
return presence || current == v;
|
||||
}, false);
|
||||
}
|
||||
|
||||
angular.module("FICApp")
|
||||
.directive('autofocus', ['$timeout', function($timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link : function($scope, $element) {
|
||||
$timeout(function() {
|
||||
$element[0].focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
}])
|
||||
.directive('autocarousel', ['$timeout', function($timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link : function($scope, $element) {
|
||||
$timeout(function() {
|
||||
$($element[0]).carousel();
|
||||
});
|
||||
}
|
||||
}
|
||||
}])
|
||||
.directive('fileModel', ['$parse', function ($parse) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function($scope, element, attrs) {
|
||||
var model = $parse(attrs.fileModel);
|
||||
var modelSetter = model.assign;
|
||||
|
||||
element.bind('change', function(){
|
||||
$scope.$apply(function(){
|
||||
modelSetter($scope, element[0].files[0]);
|
||||
$scope.uploadFile();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
angular.module("FICApp")
|
||||
.filter("escapeURL", function() {
|
||||
return function(input) {
|
||||
return encodeURIComponent(input);
|
||||
}
|
||||
})
|
||||
.filter("stripHTML", function() {
|
||||
return function(input) {
|
||||
if (!input)
|
||||
return input;
|
||||
return input.replace(
|
||||
/(<([^>]+)>)/ig,
|
||||
""
|
||||
);
|
||||
}
|
||||
})
|
||||
.filter("capitalize", function() {
|
||||
return function(input) {
|
||||
return input.capitalize();
|
||||
}
|
||||
})
|
||||
.filter("rankTitle", function() {
|
||||
var itms = {
|
||||
"rank": "Rang",
|
||||
"name": "Équipe",
|
||||
"score": "Score",
|
||||
};
|
||||
return function(input) {
|
||||
if (itms[input] != undefined) {
|
||||
return itms[input];
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
})
|
||||
.filter("time", function() {
|
||||
return function(input) {
|
||||
input = Math.floor(input);
|
||||
if (input == undefined) {
|
||||
return "--";
|
||||
} else if (input >= 10) {
|
||||
return input;
|
||||
} else {
|
||||
return "0" + input;
|
||||
}
|
||||
}
|
||||
})
|
||||
.filter("timer", function() {
|
||||
return function(input) {
|
||||
input = Math.floor(input / 1000);
|
||||
var res = ""
|
||||
|
||||
if (input >= 3600) {
|
||||
res += Math.floor(input / 3600) + ":";
|
||||
input = input % 3600;
|
||||
}
|
||||
if (res || input >= 60) {
|
||||
if (res && Math.floor(input / 60) <= 9)
|
||||
res += "0";
|
||||
res += Math.floor(input / 60) + "'";
|
||||
input = input % 60;
|
||||
}
|
||||
|
||||
return res + (input>9?input:"0"+input) + '"';
|
||||
}
|
||||
})
|
||||
.filter("since", function() {
|
||||
return function(passed) {
|
||||
if (passed < 120000) {
|
||||
return "Il y a " + Math.floor(passed/1000) + " secondes";
|
||||
} else {
|
||||
return "Il y a " + Math.floor(passed/60000) + " minutes";
|
||||
}
|
||||
}
|
||||
})
|
||||
.filter("size", function() {
|
||||
var units = [
|
||||
"o",
|
||||
"kio",
|
||||
"Mio",
|
||||
"Gio",
|
||||
"Tio",
|
||||
"Pio",
|
||||
"Eio",
|
||||
"Zio",
|
||||
"Yio",
|
||||
]
|
||||
return function(input) {
|
||||
var res = input;
|
||||
var unit = 0;
|
||||
while (res > 1024) {
|
||||
unit += 1;
|
||||
res = res / 1024;
|
||||
}
|
||||
return (Math.round(res * 100) / 100) + " " + units[unit];
|
||||
}
|
||||
})
|
||||
|
||||
.filter("coeff", function() {
|
||||
return function(input) {
|
||||
if (input > 1) {
|
||||
return "+" + Math.floor((input - 1) * 100) + " %"
|
||||
} else if (input < 1) {
|
||||
return "-" + Math.floor((1 - input) * 100) + " %"
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
.filter("objectLength", function() {
|
||||
return function(input) {
|
||||
if (input !== undefined)
|
||||
return Object.keys(input).length;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
})
|
||||
|
||||
.filter("bto16", function() {
|
||||
return function(input) {
|
||||
const raw = atob(input);
|
||||
let result = '';
|
||||
for (let i = 0; i < raw.length; i++) {
|
||||
const hex = raw.charCodeAt(i).toString(16);
|
||||
result += (hex.length === 2 ? hex : '0' + hex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
angular.module("FICApp")
|
||||
.component('flagKey', {
|
||||
bindings: {
|
||||
kid: '=',
|
||||
key: '=',
|
||||
settings: '=',
|
||||
wantchoices: '=',
|
||||
},
|
||||
controller: function() {
|
||||
this.additem = function(key) {
|
||||
this.key.values.push("");
|
||||
};
|
||||
},
|
||||
template: `
|
||||
<div class="form-group">
|
||||
<label for="sol_{{ $ctrl.kid }}_0" ng-class="{'text-light': !$ctrl.key.found}">{{ $ctrl.key.label }} :</label>
|
||||
<span ng-if="$ctrl.key.found && $ctrl.key.value" ng-bind="$ctrl.key.value"></span>
|
||||
<div class="input-group" ng-repeat="v in $ctrl.key.values track by $index" ng-class="{'mt-1': !$first}" ng-if="!$ctrl.key.found">
|
||||
<input type="text" class="form-control flag" id="sol_{{ $ctrl.kid }}_{{ $index }}" autocomplete="off" name="sol_{{ $ctrl.kid }}_{{ $index }}" ng-model="$ctrl.key.values[$index]" ng-if="!$ctrl.key.choices && !$ctrl.key.multiline" placeholder="{{ $ctrl.key.placeholder }}" title="{{ $ctrl.key.placeholder }}">
|
||||
<textarea class="form-control flag" id="sol_{{ $ctrl.kid }}_{{ $index }}" autocomplete="off" name="sol_{{ $ctrl.kid }}_{{ $index }}" ng-model="$ctrl.key.values[$index]" ng-if="!$ctrl.key.choices && $ctrl.key.multiline" placeholder="{{ $ctrl.key.placeholder }}" title="{{ $ctrl.key.placeholder }}"></textarea>
|
||||
<select class="custom-select" id="sol_{{ $ctrl.kid }}" name="sol_{{ $ctrl.kid }}" ng-model="$ctrl.key.values[$index]" ng-if="$ctrl.key.choices" ng-options="l as v for (l, v) in $ctrl.key.choices"></select>
|
||||
<div class="input-group-append" ng-if="$ctrl.key.choices_cost">
|
||||
<button class="btn btn-success" type="button" ng-click="$ctrl.wantchoices($ctrl.kid)" ng-class="{disabled: $ctrl.key.wcsubmitted}" title="Cliquez pour échanger ce champ de texte par une liste de choix. L'opération vous coûtera {{ $ctrl.key.choices_cost * $ctrl.settings.wchoiceCurrentCoefficient }} points.">
|
||||
<span class="glyphicon glyphicon-tasks" aria-hidden="true"></span>
|
||||
Liste de propositions (<ng-pluralize count="$ctrl.key.choices_cost * $ctrl.settings.wchoiceCurrentCoefficient" when="{'one': '{} point', 'other': '{} points'}"></ng-pluralize>)
|
||||
</button>
|
||||
</div>
|
||||
<div class="input-group-append" ng-if="$ctrl.key.separator && !$ctrl.key.nb_lines && $last">
|
||||
<button class="btn btn-success" type="button" ng-click="$ctrl.additem(key)" title="Ajouter un élément.">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<small class="form-text text-muted" ng-if="!$ctrl.key.found && $ctrl.key.help.length > 0" ng-bind-html="$ctrl.key.help"></small>
|
||||
<span class="glyphicon glyphicon-ok form-control-feedback text-success" aria-hidden="true" ng-if="$ctrl.key.found" title="Flag trouvé à {{ $ctrl.key.found | date:'mediumTime'}}"></span>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
|
||||
angular.module("FICApp")
|
||||
.run(function($rootScope) {
|
||||
$rootScope.recvTime = function(response) {
|
||||
time = {
|
||||
"cu": Math.floor(response.headers("x-fic-time") * 1000),
|
||||
"he": (new Date()).getTime(),
|
||||
};
|
||||
sessionStorage.time = angular.toJson(time);
|
||||
return time;
|
||||
}
|
||||
})
|
||||
|
||||
.controller("CountdownController", function($scope, $rootScope, $interval) {
|
||||
var time;
|
||||
if (sessionStorage.time)
|
||||
time = angular.fromJson(sessionStorage.time);
|
||||
|
||||
$scope.time = {};
|
||||
|
||||
$rootScope.getSrvTime = function() {
|
||||
if (time && time.cu && time.he)
|
||||
return new Date(Date.now() + (time.cu - time.he));
|
||||
else
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function updTime() {
|
||||
if (time && $rootScope.settings && $rootScope.settings.end) {
|
||||
var srv_cur = new Date(Date.now() + (time.cu - time.he));
|
||||
|
||||
// Refresh on start/activate time reached
|
||||
if (Math.floor($rootScope.settings.start / 1000) == Math.floor(srv_cur / 1000) ||Math.floor($rootScope.settings.activateTime / 1000) == Math.floor(srv_cur / 1000))
|
||||
$rootScope.refresh(true, true);
|
||||
|
||||
var remain = 0;
|
||||
if ($rootScope.settings.start === undefined || $rootScope.settings.start == 0) {
|
||||
$scope.time = {};
|
||||
return
|
||||
} else if ($rootScope.settings.start > srv_cur) {
|
||||
$scope.startIn = Math.floor(($rootScope.settings.start - srv_cur) / 1000);
|
||||
remain = $rootScope.settings.end - $rootScope.settings.start;
|
||||
} else if ($rootScope.settings.end > srv_cur) {
|
||||
$scope.startIn = 0;
|
||||
remain = $rootScope.settings.end - srv_cur;
|
||||
}
|
||||
|
||||
$rootScope.timeProgression = 1 - remain / ($rootScope.settings.end - $rootScope.settings.start);
|
||||
$rootScope.timeRemaining = remain;
|
||||
|
||||
if ($rootScope.settings.activateTime) {
|
||||
var now = new Date();
|
||||
var actTime = new Date($rootScope.settings.activateTime);
|
||||
|
||||
if (actTime > now)
|
||||
$rootScope.activateTimeCountDown = actTime - now;
|
||||
else
|
||||
$rootScope.activateTimeCountDown = null;
|
||||
} else {
|
||||
$rootScope.activateTimeCountDown = null;
|
||||
}
|
||||
|
||||
remain = remain / 1000;
|
||||
|
||||
if (remain < 0) {
|
||||
remain = 0;
|
||||
$scope.time.end = true;
|
||||
$scope.time.expired = true;
|
||||
} else if (remain < 60) {
|
||||
$scope.time.end = false;
|
||||
$scope.time.expired = true;
|
||||
} else {
|
||||
$scope.time.end = false;
|
||||
$scope.time.expired = false;
|
||||
}
|
||||
|
||||
$scope.time.remaining = remain;
|
||||
$scope.time.hours = Math.floor(remain / 3600);
|
||||
$scope.time.minutes = Math.floor((remain % 3600) / 60);
|
||||
$scope.time.seconds = Math.floor(remain % 60);
|
||||
}
|
||||
}
|
||||
updTime();
|
||||
$interval(updTime, 1000);
|
||||
})
|
||||
5
admin/static/js/d3.v3.min.js
vendored
Normal file
5
admin/static/js/d3.v3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -63,18 +63,18 @@ $provide.value("$locale", {
|
|||
"d\u00e9c."
|
||||
],
|
||||
"STANDALONEMONTH": [
|
||||
"Janvier",
|
||||
"F\u00e9vrier",
|
||||
"Mars",
|
||||
"Avril",
|
||||
"Mai",
|
||||
"Juin",
|
||||
"Juillet",
|
||||
"Ao\u00fbt",
|
||||
"Septembre",
|
||||
"Octobre",
|
||||
"Novembre",
|
||||
"D\u00e9cembre"
|
||||
"janvier",
|
||||
"f\u00e9vrier",
|
||||
"mars",
|
||||
"avril",
|
||||
"mai",
|
||||
"juin",
|
||||
"juillet",
|
||||
"ao\u00fbt",
|
||||
"septembre",
|
||||
"octobre",
|
||||
"novembre",
|
||||
"d\u00e9cembre"
|
||||
],
|
||||
"WEEKENDRANGE": [
|
||||
5,
|
||||
|
|
@ -119,6 +119,7 @@ $provide.value("$locale", {
|
|||
]
|
||||
},
|
||||
"id": "fr-fr",
|
||||
"localeID": "fr_FR",
|
||||
"pluralCat": function(n, opt_precision) { var i = n | 0; if (i == 0 || i == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
7
admin/static/js/jquery.min.js
vendored
7
admin/static/js/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue