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
|
fic.db
|
||||||
PKI/
|
PKI/
|
||||||
FILES/
|
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"
|
BASEURL="http://127.0.0.1:8081/admin"
|
||||||
PART_FILE="Challenge_Liste des participants.csv"
|
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() {
|
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/' |
|
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`
|
R=`echo $line | cut -d " " -f 1`
|
||||||
G=`echo $line | cut -d " " -f 2`
|
G=`echo $line | cut -d " " -f 2`
|
||||||
B=`echo $line | cut -d " " -f 3`
|
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))
|
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]+"
|
done | grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||||
}
|
}
|
||||||
|
|
||||||
TNUM=0
|
TNUM=0
|
||||||
|
|
||||||
for i in `seq 12`
|
for i in $(seq $EXTRA_TEAMS)
|
||||||
do
|
do
|
||||||
TNUM=$(($TNUM + 1))
|
TNUM=$(($TNUM + 1))
|
||||||
|
|
||||||
|
|
@ -28,31 +76,52 @@ do
|
||||||
|
|
||||||
TID=`new_team $TNUM`
|
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
|
then
|
||||||
curl -s -f "${BASEURL}/api/teams/${TID}/certificate/generate"
|
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
|
fi
|
||||||
echo
|
echo
|
||||||
done
|
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))
|
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
|
do
|
||||||
TNUM=$(($TNUM + 1))
|
TNUM=$(($TNUM + 1))
|
||||||
|
|
||||||
echo "Doing team $TNUM/$TMAX ("$(($TNUM*100/$TMAX))"%)..."
|
echo "Doing team $TNUM/$TMAX ("$(($TNUM*100/$TMAX))"%)..."
|
||||||
|
|
||||||
TID=`new_team $TNUM`
|
TID=`new_team "${TNUM}" "${TEAMID}"`
|
||||||
|
|
||||||
(
|
if ! (
|
||||||
echo -n "["
|
echo -n "["
|
||||||
HAS_MEMBER=1
|
HAS_MEMBER=1
|
||||||
grep ";$TEAMID\$" "$PART_FILE" | while read MEMBER
|
grep "${TEAMID}${CSV_SPLITER}" "$PART_FILE" | while read MEMBER
|
||||||
do
|
do
|
||||||
LASTNAME=`echo $MEMBER | cut -d ";" -f 2`
|
LASTNAME=`echo $MEMBER | cut -d "${CSV_SPLITER}" -f $CSV_COL_LASTNAME | tr -d "\r\n"`
|
||||||
FIRSTNAME=`echo $MEMBER | cut -d ";" -f 3`
|
FIRSTNAME=`echo $MEMBER | cut -d "${CSV_SPLITER}" -f $CSV_COL_FIRSTNAME | tr -d "\r\n"`
|
||||||
COMPANY=`echo $MEMBER | cut -d ";" -f 4`
|
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 ]
|
if [ $HAS_MEMBER = 0 ]
|
||||||
then
|
then
|
||||||
|
|
@ -65,17 +134,34 @@ do
|
||||||
{
|
{
|
||||||
"firstname": "$FIRSTNAME",
|
"firstname": "$FIRSTNAME",
|
||||||
"lastname": "$LASTNAME",
|
"lastname": "$LASTNAME",
|
||||||
"nickname": "",
|
"nickname": "$NICKNAME",
|
||||||
"company": "$COMPANY"
|
"company": "$COMPANY"
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
done
|
done
|
||||||
echo "]"
|
echo "]"
|
||||||
) | curl -s -d @- "${BASEURL}/api/teams/${TID}" > /dev/null
|
) | curl -f -s -d @- "${BASEURL}/api/teams/${TID}/members"
|
||||||
|
then
|
||||||
if ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null
|
echo "An error occured"
|
||||||
|
elif [ "${GEN_CERTS}" -eq 1 ] && ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null
|
||||||
then
|
then
|
||||||
curl -s -f "${BASEURL}/api/teams/${TID}/certificate/generate"
|
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
|
fi
|
||||||
echo
|
echo
|
||||||
done
|
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 (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"io/fs"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"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/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() {
|
func main() {
|
||||||
var bind = flag.String("bind", "0.0.0.0:8081", "Bind port/socket")
|
var err error
|
||||||
var dsn = flag.String("dsn", "fic:fic@/fic", "DSN to connect to the MySQL server")
|
bind := "127.0.0.1:8081"
|
||||||
flag.StringVar(&BaseURL, "baseurl", "http://fic.srs.epita.fr/", "URL prepended to each URL")
|
cloudDAVBase := ""
|
||||||
flag.StringVar(&SubmissionDir, "submission", "./submissions/", "Base directory where save submissions")
|
cloudUsername := "fic"
|
||||||
flag.StringVar(&PKIDir, "pki", "./pki/", "Base directory where found PKI scripts")
|
cloudPassword := ""
|
||||||
flag.StringVar(&fic.FilesDir, "files", "./FILES/", "Base directory where found challenges files, local part")
|
localImporterDirectory := ""
|
||||||
flag.StringVar(&CloudDAVBase, "clouddav", "https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016",
|
gitImporterRemote := ""
|
||||||
"Base directory where found challenges files, cloud part")
|
gitImporterBranch := ""
|
||||||
flag.StringVar(&CloudUsername, "clouduser", "fic", "Username used to sync")
|
localImporterSymlink := false
|
||||||
flag.StringVar(&CloudPassword, "cloudpass", "", "Password used to sync")
|
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()
|
flag.Parse()
|
||||||
|
|
||||||
log.Prefix("[admin] ")
|
log.SetPrefix("[admin] ")
|
||||||
|
|
||||||
var staticDir string
|
// Instantiate importer
|
||||||
var err error
|
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...")
|
log.Println("Checking paths...")
|
||||||
if staticDir, err = filepath.Abs("./static/"); err != nil {
|
if staticDir != nil && *staticDir != "" {
|
||||||
log.Fatal(err)
|
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 {
|
if fic.FilesDir, err = filepath.Abs(fic.FilesDir); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if PKIDir, err = filepath.Abs(PKIDir); err != nil {
|
if pki.PKIDir, err = filepath.Abs(pki.PKIDir); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if SubmissionDir, err = filepath.Abs(SubmissionDir); err != nil {
|
if api.DashboardDir, err = filepath.Abs(api.DashboardDir); err != nil {
|
||||||
log.Fatal(err)
|
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)
|
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...")
|
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)
|
log.Fatal("Cannot open the database: ", err)
|
||||||
}
|
}
|
||||||
defer fic.DBClose()
|
defer fic.DBClose()
|
||||||
|
|
||||||
log.Println("Creating database...")
|
log.Println("Creating database...")
|
||||||
if err := fic.DBCreate(); err != nil {
|
if err = fic.DBCreate(); err != nil {
|
||||||
log.Fatal("Cannot create database: ", err)
|
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...")
|
// Prepare graceful shutdown
|
||||||
mux := http.NewServeMux()
|
interrupt := make(chan os.Signal, 1)
|
||||||
mux.Handle("/api/", http.StripPrefix("/api", ApiHandler()))
|
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
|
||||||
mux.Handle("/teams/", StaticHandler(staticDir))
|
|
||||||
mux.Handle("/themes/", StaticHandler(staticDir))
|
|
||||||
mux.Handle("/", http.FileServer(http.Dir(staticDir)))
|
|
||||||
|
|
||||||
log.Println(fmt.Sprintf("Ready, listening on %s", *bind))
|
app := NewApp(config, baseURL, bind)
|
||||||
if err := http.ListenAndServe(*bind, mux); err != nil {
|
go app.Start()
|
||||||
log.Fatal("Unable to listen and serve: ", err)
|
|
||||||
}
|
// 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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"embed"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"path"
|
"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 {
|
//go:embed static
|
||||||
StaticDir string
|
|
||||||
|
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 {
|
func serveIndex(c *gin.Context) {
|
||||||
return staticRouting{staticDir}
|
c.Writer.Write(indexPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a staticRouting) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
var staticFS http.FileSystem
|
||||||
http.ServeFile(w, r, path.Join(a.StaticDir, "index.html"))
|
|
||||||
|
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
|
AngularJS v1.7.9
|
||||||
(c) 2010-2015 Google, Inc. http://angularjs.org
|
(c) 2010-2018 Google, Inc. http://angularjs.org
|
||||||
License: MIT
|
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"}}};
|
(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",
|
||||||
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}
|
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"===
|
||||||
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?
|
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"/"===
|
||||||
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?
|
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,
|
||||||
(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,
|
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."),
|
||||||
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,
|
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||
|
||||||
":").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]=
|
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)})}};return z}]})})(window,window.angular);
|
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
|
//# 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
|
AngularJS v1.7.9
|
||||||
(c) 2010-2015 Google, Inc. http://angularjs.org
|
(c) 2010-2018 Google, Inc. http://angularjs.org
|
||||||
License: MIT
|
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");
|
(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",
|
||||||
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),
|
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||
|
||||||
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.$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.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;
|
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},
|
||||||
(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=
|
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=
|
||||||
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",
|
!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,
|
||||||
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+)(?:[?*])?(.*)/),
|
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)&&
|
||||||
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{}}});
|
(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)):
|
||||||
p.directive("ngView",v);p.directive("ngView",A);v.$inject=["$route","$anchorScroll","$animate"];A.$inject=["$compile","$controller","$route"]})(window,window.angular);
|
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
|
//# 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
|
AngularJS v1.7.9
|
||||||
(c) 2010-2015 Google, Inc. http://angularjs.org
|
(c) 2010-2018 Google, Inc. http://angularjs.org
|
||||||
License: MIT
|
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;
|
(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,
|
||||||
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)&&
|
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)||
|
||||||
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):
|
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}
|
||||||
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"!==
|
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,
|
||||||
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)&&
|
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)}
|
||||||
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)&&
|
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))}
|
||||||
(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");
|
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)&&
|
||||||
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=
|
(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),
|
||||||
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):
|
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)}
|
||||||
[];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,
|
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){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){}}
|
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
|
||||||
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,
|
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 "+
|
||||||
"=").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(":",
|
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("&"),
|
||||||
"\\:")+"]"))&&(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",
|
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,
|
||||||
"$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()}
|
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))});
|
||||||
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=
|
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",
|
||||||
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&&
|
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)&&
|
||||||
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,
|
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})):
|
||||||
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",
|
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(".");
|
||||||
"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,
|
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"),
|
||||||
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,
|
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",
|
||||||
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,
|
"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",
|
||||||
$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;
|
"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,
|
||||||
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),
|
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});
|
||||||
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.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,
|
||||||
(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,
|
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,
|
||||||
!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(" "),
|
$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(),
|
||||||
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&&
|
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=
|
||||||
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.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,
|
||||||
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,
|
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),
|
||||||
{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))?
|
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=
|
||||||
"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,
|
(" "+(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=
|
||||||
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]===
|
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",
|
||||||
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||
|
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};
|
||||||
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=
|
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;
|
||||||
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=
|
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,
|
||||||
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)})});
|
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),
|
||||||
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)?
|
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),
|
||||||
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=
|
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===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",
|
(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,
|
||||||
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,
|
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,
|
||||||
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&&
|
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)||
|
||||||
(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);
|
(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)&&
|
||||||
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");
|
(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=
|
||||||
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,
|
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());
|
||||||
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));
|
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];
|
||||||
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,
|
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",
|
||||||
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=
|
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();
|
||||||
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),
|
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":
|
||||||
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,
|
"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"),
|
||||||
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-
|
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",
|
||||||
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=
|
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",
|
||||||
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===
|
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()};
|
||||||
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||
|
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();
|
||||||
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,
|
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(" "))})();
|
||||||
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],
|
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/
|
||||||
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=
|
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){}}
|
||||||
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),
|
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):
|
||||||
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);
|
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,
|
||||||
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;
|
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:
|
||||||
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=
|
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,
|
||||||
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,
|
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]=
|
||||||
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();
|
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){}}
|
||||||
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,
|
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=
|
||||||
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");
|
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=
|
||||||
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");
|
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);
|
||||||
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);
|
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,
|
||||||
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)&&
|
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,
|
||||||
(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]):
|
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",
|
||||||
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,
|
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(),
|
||||||
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<
|
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",
|
||||||
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(),
|
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):
|
||||||
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,
|
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&&
|
||||||
""))}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",
|
!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,
|
||||||
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=
|
$$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,
|
||||||
["$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)||
|
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)||
|
||||||
(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&&
|
"$"===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=
|
||||||
(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, */*"},
|
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,
|
||||||
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);
|
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();
|
||||||
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);
|
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:
|
||||||
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=
|
"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=
|
||||||
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:
|
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&&
|
||||||
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],
|
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("*"));
|
||||||
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,
|
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,
|
||||||
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,
|
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=
|
||||||
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()}
|
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,
|
||||||
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,
|
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=
|
||||||
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",
|
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,
|
||||||
"$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+
|
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,
|
||||||
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,
|
"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=
|
||||||
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=
|
"",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,
|
||||||
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]||
|
"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);
|
||||||
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||"";
|
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,
|
||||||
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);
|
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?"":"]"))}):
|
||||||
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?
|
(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)&&
|
||||||
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]}}
|
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, */*"},
|
||||||
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",
|
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++];
|
||||||
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=
|
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},
|
||||||
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"]=
|
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)});
|
||||||
!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||
|
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,
|
||||||
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&&
|
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),
|
||||||
(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__"===
|
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]=
|
||||||
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===
|
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):
|
||||||
(!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.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=
|
||||||
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=
|
["$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",
|
||||||
!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;
|
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||
|
||||||
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}}
|
"",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"!==
|
||||||
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,
|
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}
|
||||||
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=
|
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),
|
||||||
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,
|
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,
|
||||||
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=
|
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");
|
||||||
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}}}]}
|
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++);
|
||||||
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,
|
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);
|
||||||
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||[];
|
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.$$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=
|
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))?
|
||||||
-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)?
|
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,
|
||||||
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};
|
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",
|
||||||
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||
|
"$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,
|
||||||
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&&
|
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")||
|
||||||
(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=
|
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;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,
|
!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&&
|
||||||
$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||
|
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,
|
||||||
(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()()}},
|
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,
|
||||||
$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=
|
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,
|
||||||
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=
|
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,
|
||||||
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",
|
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,
|
||||||
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=
|
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:"="}}
|
||||||
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");
|
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),
|
||||||
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=
|
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=
|
||||||
{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};
|
!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}
|
||||||
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):/,
|
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=/^\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("^"+
|
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)},
|
||||||
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=
|
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&&
|
||||||
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",
|
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=
|
||||||
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}}}]}
|
!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,
|
||||||
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){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,
|
||||||
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()+
|
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)},
|
||||||
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",
|
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||
|
||||||
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",
|
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=
|
||||||
"$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()},
|
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=
|
||||||
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={};
|
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)},
|
||||||
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=
|
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=
|
||||||
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]=
|
{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=
|
||||||
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=
|
!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,
|
||||||
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)});
|
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,
|
||||||
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,
|
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]||
|
||||||
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;
|
(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},
|
||||||
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="";
|
$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=
|
||||||
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()))-
|
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=
|
||||||
+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))?)?$/;
|
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=
|
||||||
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,
|
!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,
|
||||||
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,
|
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\/)/;
|
||||||
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&&
|
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");
|
||||||
(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=
|
}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),
|
||||||
{};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&&
|
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[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(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=
|
||||||
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});
|
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};
|
||||||
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",
|
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)}});
|
||||||
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,
|
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||
|
||||||
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&&
|
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,
|
||||||
!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")||{};
|
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)||
|
||||||
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",
|
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,
|
||||||
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"!==
|
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())},
|
||||||
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&&
|
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");
|
||||||
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]*)$/,
|
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(/^#/,
|
||||||
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,
|
""):"",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=
|
||||||
"\\$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<
|
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",
|
||||||
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+;/,
|
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(""+
|
||||||
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)&
|
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,
|
||||||
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(" "),
|
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))&&
|
||||||
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,
|
(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=
|
||||||
"$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]},
|
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},
|
||||||
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?
|
!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: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,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>=
|
||||||
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.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,
|
||||||
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,
|
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,
|
||||||
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},
|
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=
|
||||||
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;
|
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",
|
||||||
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)&&
|
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*
|
||||||
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=
|
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=
|
||||||
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);
|
{};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,
|
||||||
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,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,
|
||||||
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=
|
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&&
|
||||||
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,
|
"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);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=
|
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(),
|
||||||
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=
|
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,
|
||||||
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",
|
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,
|
||||||
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"),
|
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,
|
||||||
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!==
|
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(),
|
||||||
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",
|
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[];
|
||||||
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++;
|
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===
|
||||||
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||
|
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,
|
||||||
"\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();
|
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=
|
||||||
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,
|
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;
|
||||||
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,
|
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",
|
||||||
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=
|
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=
|
||||||
"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,
|
[];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!==
|
||||||
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(":"))?
|
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;
|
||||||
(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,
|
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=
|
||||||
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,
|
{},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>",
|
||||||
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():
|
"</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(" "),
|
||||||
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(),
|
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||
|
||||||
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,["$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,
|
||||||
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}},
|
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},
|
||||||
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];
|
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(" ")?
|
||||||
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:[],
|
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===
|
||||||
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+
|
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;
|
||||||
'";\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,
|
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)&&
|
||||||
"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,
|
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<
|
||||||
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,
|
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)},
|
||||||
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,
|
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");
|
||||||
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",
|
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(" "):
|
||||||
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(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),
|
||||||
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,
|
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?
|
||||||
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)},
|
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,
|
||||||
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)})});
|
"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",
|
||||||
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+"&&("+
|
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,
|
||||||
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,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=
|
||||||
"){");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),";")},
|
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)},
|
||||||
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("+
|
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||
|
||||||
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"===
|
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&&
|
||||||
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))});
|
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,
|
||||||
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),
|
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?
|
||||||
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,
|
"#"+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!==
|
||||||
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,
|
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}};
|
||||||
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))}),
|
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,
|
||||||
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,
|
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]||
|
||||||
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,
|
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?
|
||||||
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,
|
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):
|
||||||
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,
|
(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<
|
||||||
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=
|
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<
|
||||||
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,
|
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,
|
||||||
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);
|
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";
|
||||||
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>
|
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},
|
||||||
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,
|
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");
|
||||||
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,
|
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=
|
||||||
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)})}}}});
|
{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,
|
||||||
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":
|
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("{")?
|
||||||
!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);
|
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(")")):
|
||||||
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)$/,
|
"["===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(","))
|
||||||
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=
|
}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.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)||
|
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("}");
|
||||||
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);
|
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,
|
||||||
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&&
|
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=
|
||||||
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,
|
"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")+
|
||||||
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));
|
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.$$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)})}}),
|
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,
|
||||||
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=
|
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,
|
||||||
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=
|
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,
|
||||||
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));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",
|
||||||
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)},
|
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,
|
||||||
{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.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?
|
||||||
!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;
|
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,
|
||||||
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=
|
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:
|
||||||
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=
|
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,
|
||||||
!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=
|
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("}"))}},
|
||||||
!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,
|
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=
|
||||||
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),
|
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,
|
||||||
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=
|
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=
|
||||||
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(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),
|
||||||
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,
|
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)});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)?
|
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=
|
||||||
(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,
|
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,
|
||||||
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]||
|
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,
|
||||||
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,
|
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,
|
||||||
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,
|
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,
|
||||||
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?
|
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=
|
||||||
(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=
|
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}:
|
||||||
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))});
|
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=
|
||||||
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),
|
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",
|
||||||
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,
|
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),
|
||||||
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 "+
|
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]}},
|
||||||
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*$/);
|
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],
|
||||||
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=
|
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]=
|
||||||
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<
|
["$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=
|
||||||
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?
|
["$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&&
|
||||||
"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,
|
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,
|
||||||
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)})})})}}}],
|
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,
|
||||||
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,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();
|
||||||
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=
|
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)$/,
|
||||||
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=
|
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)$/,
|
||||||
!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",
|
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);
|
||||||
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!==
|
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),
|
||||||
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",
|
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=
|
||||||
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)||
|
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,
|
||||||
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?
|
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?
|
||||||
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(" "),
|
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}:
|
||||||
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",
|
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",
|
||||||
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>');
|
"$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
|
//# 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."
|
"d\u00e9c."
|
||||||
],
|
],
|
||||||
"STANDALONEMONTH": [
|
"STANDALONEMONTH": [
|
||||||
"Janvier",
|
"janvier",
|
||||||
"F\u00e9vrier",
|
"f\u00e9vrier",
|
||||||
"Mars",
|
"mars",
|
||||||
"Avril",
|
"avril",
|
||||||
"Mai",
|
"mai",
|
||||||
"Juin",
|
"juin",
|
||||||
"Juillet",
|
"juillet",
|
||||||
"Ao\u00fbt",
|
"ao\u00fbt",
|
||||||
"Septembre",
|
"septembre",
|
||||||
"Octobre",
|
"octobre",
|
||||||
"Novembre",
|
"novembre",
|
||||||
"D\u00e9cembre"
|
"d\u00e9cembre"
|
||||||
],
|
],
|
||||||
"WEEKENDRANGE": [
|
"WEEKENDRANGE": [
|
||||||
5,
|
5,
|
||||||
|
|
@ -119,6 +119,7 @@ $provide.value("$locale", {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"id": "fr-fr",
|
"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;}
|
"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