Compare commits
797 Commits
Author | SHA1 | Date |
---|---|---|
Ravi Majithia | c79f97a16f | |
Oliver Tacke | d3d003e978 | |
Ravi Majithia | 22115c0aea | |
Erik Langhaug | aaf26d584b | |
Frode Petterson | eeefc1228b | |
Erik Langhaug | 13ba87ed58 | |
Erik Langhaug | f9a20e908f | |
Thomas Marstrander | 4599291d7c | |
Frode Petterson | 1c12b7bbf7 | |
Frode Petterson | 588f096afe | |
Frode Petterson | affaa83b51 | |
Frode Petterson | db3da7a144 | |
Frode Petterson | 17e97d48db | |
Frode Petterson | efd98a719d | |
Frode Petterson | 0eca5935c0 | |
Thomas Marstrander | f6fef74651 | |
Thomas Marstrander | cc5a0b2e57 | |
Thomas Marstrander | fe3f4a504d | |
Thomas Marstrander | d2522e3800 | |
Thomas Marstrander | 4c543f3214 | |
Thomas Marstrander | 1d8f0e8dac | |
Thomas Marstrander | a0f1e1a527 | |
Thomas Marstrander | 0308e3a888 | |
Thomas Marstrander | 8ba1611575 | |
Thomas Marstrander | 951912d7c1 | |
Thomas Marstrander | 0c40e081e7 | |
Hannaes | 982d07f137 | |
Hannaes | 96d5b63b2e | |
Thomas Marstrander | 16e51c06f5 | |
Thomas Marstrander | 09e7a7cb0d | |
Thomas Marstrander | 9aacfa1780 | |
Frode Petterson | f1ddd3b3f5 | |
Frode Petterson | 90835b7345 | |
Thomas Marstrander | 1831312d41 | |
Frode Petterson | 47d4499377 | |
Frode Petterson | fdd2a618bc | |
Frode Petterson | 8a29796cab | |
Frode Petterson | aa6c909572 | |
Frode Petterson | 0ba2496c11 | |
Frode Petterson | dab4e5120d | |
Frode Petterson | d5be0158a4 | |
Frode Petterson | 4d7dccad3d | |
Frode Petterson | 160b86e9ae | |
Frode Petterson | aa5abe786e | |
Frode Petterson | 48c961b661 | |
Frode Petterson | d1450fc1bb | |
Erik Langhaug | 96a2c1ff91 | |
Hannaes | 4b76c7ff8f | |
Erik Langhaug | 720e6b29a2 | |
Paal Joergensen | a5c3dd6c4e | |
Hannaes | 832707dcaf | |
Erik Langhaug | ba98139ccb | |
Thomas Marstrander | ae7b82a34a | |
Erik Langhaug | 25deeac560 | |
Thomas Marstrander | 7538202500 | |
Thomas Marstrander | 0d7abe0e51 | |
Thomas Marstrander | 14bad68d70 | |
Thomas Marstrander | 917a9b6ccb | |
Thomas Marstrander | 67bce48842 | |
Frode Petterson | d90accc55e | |
Frode Petterson | a7f2054101 | |
Frode Petterson | 03fe25df5a | |
Frode Petterson | 7cb4419eb0 | |
Thomas Marstrander | 645058f03e | |
Thomas Marstrander | b47ae6de6e | |
Frode Petterson | e65b22f0d1 | |
Frode Petterson | 8b317fa5fe | |
Thomas Marstrander | 1a644dda11 | |
Thomas Marstrander | 16c71b444b | |
Thomas Marstrander | eb52ec25a0 | |
Thomas Marstrander | 3a2232e690 | |
Thomas Marstrander | fb6278744c | |
Thomas Marstrander | 35eb39c469 | |
Thomas Marstrander | 555442d37e | |
Thomas Marstrander | c472d5172d | |
Oliver Tacke | a80773e8d6 | |
Oliver Tacke | ce373203b0 | |
Frode Petterson | 196888bf3e | |
Pål Jørgensen | 743147698f | |
Pål Jørgensen | 3b2990f9b8 | |
Andrew Nicols | 2cb43ad147 | |
Andrew Nicols | acec5b0b33 | |
Paal Joergensen | df9d25a5b3 | |
Paal Joergensen | bea974e597 | |
Paal Joergensen | 866f94ea47 | |
Pål Jørgensen | 8432a88386 | |
Andrew Nicols | d392737f45 | |
Andrew Nicols | 668233fb95 | |
Pål Jørgensen | 4c973c3088 | |
Pål Jørgensen | 87f6f3c970 | |
Thomas | 42568106fc | |
Frode Petterson | 541f6d8415 | |
Frode Petterson | 143a44cef5 | |
Frode Petterson | 55b04018d7 | |
Eloy Lafuente (stronk7) | f6f6d0fb56 | |
Frode Petterson | f37231eb3a | |
Frode Petterson | 128fbbc532 | |
Frode Petterson | 8bf43081dd | |
Frode Petterson | ec518a6f67 | |
Frode Petterson | e635d33169 | |
Frode Petterson | 43605b14d6 | |
Frode Petterson | f08655cfb0 | |
Frode Petterson | 0293d2cdfb | |
Andrew Nicols | 34bc10184d | |
Thomas Marstrander | 9a658ba387 | |
Frode Petterson | 159f83f1b8 | |
Frode Petterson | 117668d0e5 | |
Oliver Tacke | bf10430671 | |
Sjoerd Zonneveld | 7d7b420b45 | |
Paal Joergensen | e6fa3d7e54 | |
Thomas Marstrander | 4d061aef7a | |
Thomas Marstrander | 058a9f0a71 | |
Frode Petterson | 9577c57f95 | |
Thomas Marstrander | 8cfd2f0519 | |
Paal Joergensen | 0922db6fab | |
Thomas Marstrander | 7a8f894c85 | |
Paal Joergensen | 7bc8666f30 | |
Paal Joergensen | 8ceb77e881 | |
Thomas Marstrander | 064391236a | |
Frode Petterson | 4a9ea77d47 | |
Frode Petterson | 03794f36dd | |
Frode Petterson | 9e749eda92 | |
Frode Petterson | 2c15047e50 | |
Frode Petterson | fec8953ba8 | |
Frode Petterson | 3570441801 | |
Frode Petterson | b9a1e5f404 | |
Paal Joergensen | 5d7b480c3b | |
Thomas Marstrander | 665e5d424a | |
Thomas Marstrander | 3b685a5520 | |
Thomas Marstrander | 38fc962625 | |
Frode Petterson | 70278d15f4 | |
Thomas Marstrander | 0b1aadbbca | |
Thomas Marstrander | 687f886e3d | |
Thomas Marstrander | 39d27ab9bb | |
Thomas Marstrander | ada2f4009d | |
Thomas Marstrander | 75453e872c | |
Thomas Marstrander | 49c48e29b3 | |
Thomas Marstrander | a934d5b3ec | |
Thomas Marstrander | e0eb03026a | |
Frode Petterson | 052ad0ea81 | |
Frode Petterson | 7a85c115db | |
Frode Petterson | cdb53c5cfd | |
Frode Petterson | ac5dba1e19 | |
Thomas Horn Sivertsen | 4525d6383f | |
Thomas Marstrander | d4931ec20a | |
Thomas Marstrander | 8f7742ab0b | |
Thomas Marstrander | cd869f8400 | |
Frode Petterson | 60fb6c96ca | |
Frode Petterson | 366d8f2a0b | |
Frode Petterson | d38b3b1e8a | |
Frode Petterson | bf9250d80b | |
Frode Petterson | d05bc94d92 | |
Frode Petterson | 71038f89ea | |
Thomas Marstrander | 1a09b1a30e | |
Frode Petterson | 7b7b35ea39 | |
Frode Petterson | 68e56dd8fd | |
Frode Petterson | e9d08b973a | |
Frode Petterson | 53adda67c2 | |
Frode Petterson | fc044630bc | |
Frode Petterson | 840f5dcb12 | |
Frode Petterson | a5f1b49f6b | |
Frode Petterson | b64292af09 | |
Frode Petterson | 47d049afb2 | |
Frode Petterson | 01550e7f11 | |
Frode Petterson | d3b5b07669 | |
Thomas Marstrander | 801f3f33c3 | |
Thomas Marstrander | 32b0840ca6 | |
Frode Petterson | 7ee0c3372b | |
Frode Petterson | 2b2184fa30 | |
Frode Petterson | 415e101064 | |
Frode Petterson | d1dd47be6f | |
Frode Petterson | aa723bcb40 | |
Frode Petterson | 741cd04d34 | |
Frode Petterson | e7a256da05 | |
Frode Petterson | f96d04cc27 | |
Frode Petterson | c9e1ac9347 | |
Frode Petterson | 9cf3f4aa7f | |
Frode Petterson | bee7c550d9 | |
Frode Petterson | b6080a1a00 | |
Oliver Tacke | bfb7b5600c | |
Oliver Tacke | c2d7b987cc | |
Frode Petterson | 8d30949969 | |
Frode Petterson | 9a7a343844 | |
Frode Petterson | f19ca76461 | |
Frode Petterson | 4a9c3c7881 | |
Frode Petterson | 6f4c4d9cc3 | |
Frode Petterson | 2b474699b2 | |
thomasmars | b43051c785 | |
Frode Petterson | 96694e4c70 | |
Frode Petterson | 57570db8d6 | |
Oliver Tacke | 0678126f82 | |
Frode Petterson | 152dfc0fb2 | |
Oliver Tacke | 48f3805f94 | |
Oliver Tacke | 2be06b2eb9 | |
Oliver Tacke | 132f25e14a | |
Frode Petterson | f2a9801879 | |
Frode Petterson | c438f9136e | |
Frode Petterson | 1cd7b67010 | |
Frode Petterson | ec9127d245 | |
Frode Petterson | f7f2479b2a | |
Paal Joergensen | a0e7bcd2af | |
Frode Petterson | 85b278bd52 | |
Frode Petterson | c8ddb305ab | |
Frode Petterson | 39fc577fd5 | |
Paal Joergensen | 512a0de321 | |
Paal Joergensen | 898d975921 | |
Frode Petterson | 6b3b3db575 | |
Frode Petterson | db022830a6 | |
Paal Joergensen | d9940b81e2 | |
Paal Joergensen | 715fa6f803 | |
Frode Petterson | e15f6d6678 | |
Oliver Tacke | 37c0593ff4 | |
Oliver Tacke | 066ea94b11 | |
Paal Joergensen | fd2ac997ef | |
Paal Joergensen | 2e305ded71 | |
Paal Joergensen | f661248b5a | |
Paal Joergensen | 82b50fc2f1 | |
Paal Joergensen | e316eff18d | |
Paal Joergensen | 85d2e2eb75 | |
Paal Joergensen | c4e52f4f29 | |
Paal Joergensen | 8dafa5db91 | |
Frode Petterson | 06985cca7c | |
Paal Joergensen | 9081ca3128 | |
Paal Joergensen | e75745f0db | |
Frode Petterson | 7762f903c8 | |
Frode Petterson | cb2acb21c8 | |
Frode Petterson | 64c8090d13 | |
Paal Joergensen | 73f41e2dbd | |
Frode Petterson | d6e9c4ec09 | |
Frode Petterson | cfd15d8265 | |
Frode Petterson | 841d24cab8 | |
Paal Joergensen | e2c8d6459a | |
Paal Joergensen | 3566ac4141 | |
Paal Joergensen | d391d486c9 | |
Paal Joergensen | c9cb23e06c | |
Paal Joergensen | 2bf38c5b00 | |
Paal Joergensen | 3d1c1cbe38 | |
Paal Joergensen | eb766b0081 | |
Paal Joergensen | 4fe8eca0f2 | |
Paal Joergensen | 74f9a84034 | |
Paal Joergensen | 8067277e63 | |
Paal Joergensen | 425aac5d33 | |
Paal Joergensen | 9e4db6cdd9 | |
Paal Joergensen | e257e5ecff | |
Frode Petterson | 394a4aace3 | |
Frode Petterson | 97eab5c3ce | |
Oliver Tacke | 2d3fe61371 | |
Oliver Tacke | 775b45ab6c | |
Paal Joergensen | 43b9703fc9 | |
Paal Joergensen | e74fb6009a | |
Frode Petterson | e094da76fa | |
Frode Petterson | 768eb2a64b | |
Paal Joergensen | 8c374be79d | |
Paal Joergensen | 6959f65022 | |
Paal Joergensen | bbe99e4db8 | |
Paal Joergensen | ee0e97e17b | |
Paal Joergensen | 0c7df179a7 | |
Paal Joergensen | 6c4f904079 | |
Pål Jørgensen | b1db442554 | |
Bastian Heist | 3a8847424c | |
Oliver Tacke | 6d50bae108 | |
Oliver Tacke | 64e5ab4424 | |
Oliver Tacke | d068b82ff5 | |
thomasmars | 5be4ba1222 | |
Oliver Tacke | 9314c55994 | |
Oliver Tacke | 942fd922bc | |
Oliver Tacke | 62b6345c49 | |
Oliver Tacke | a55379adcf | |
Oliver Tacke | c45be49a4f | |
Oliver Tacke | d3a63dd756 | |
Oliver Tacke | 023613c131 | |
Oliver Tacke | e22766157b | |
Oliver Tacke | 9ee5fb9907 | |
Oliver Tacke | a295d7d434 | |
thomasmars | 579ba96b49 | |
Oliver Tacke | b1a01b728d | |
Frode Petterson | a015d1a134 | |
Frode Petterson | 10aaa38844 | |
Oliver Tacke | e241ec6963 | |
Oliver Tacke | 14bcb913b7 | |
Oliver Tacke | 972c7a13f6 | |
Oliver Tacke | ae85ea3f2a | |
Oliver Tacke | 9432d80b23 | |
Frode Petterson | 1605581608 | |
Frode Petterson | d24fd0e667 | |
thomasmars | d963a23a15 | |
Frode Petterson | b1446e8d60 | |
Oliver Tacke | 4d286b0bdb | |
Oliver Tacke | d113809e80 | |
Oliver Tacke | 8190fe1d42 | |
Oliver Tacke | 2a53b7bb7b | |
Oliver Tacke | 17162f32ef | |
Thomas | 30cb2aec45 | |
Thomas Horn Sivertsen | 930f85e0fb | |
Oliver Tacke | 04707a9f8a | |
Oliver Tacke | e179ec2934 | |
Thomas Horn Sivertsen | a59640672d | |
Oliver Tacke | 367763a215 | |
Oliver Tacke | be77130fef | |
Oliver Tacke | ea722126ff | |
Oliver Tacke | 0fbc6ef5a8 | |
Oliver Tacke | 8f45ea4d79 | |
thomasmars | 3ce0adf418 | |
Oliver Tacke | a30a93e62e | |
Oliver Tacke | 47be831f49 | |
Oliver Tacke | 00deb02aa7 | |
Oliver Tacke | 3deda27f9b | |
Frode Petterson | 942a083afa | |
Paal Joergensen | d99ab7eda2 | |
Frode Petterson | 022695ac74 | |
Oliver Tacke | 58597460f6 | |
Oliver Tacke | 3b38e273eb | |
Oliver Tacke | fbc21f6368 | |
Oliver Tacke | ef063ce5cb | |
Oliver Tacke | 83e3c58ba3 | |
Oliver Tacke | 95d99d0ad3 | |
Oliver Tacke | b28624ba8e | |
Oliver Tacke | c589285351 | |
Oliver Tacke | 6ef2f96e8b | |
Oliver Tacke | 011c7df675 | |
Frode Petterson | 0fb35a3f7c | |
Paal Joergensen | 78c19d6779 | |
Paal Joergensen | 4a0739f1c4 | |
Paal Joergensen | 3ed15f2249 | |
drsassafras | 24bbbf08ae | |
Thomas Horn Sivertsen | 615bac7c08 | |
Thomas Horn Sivertsen | 04edd73855 | |
Thomas Horn Sivertsen | 1b079d36f1 | |
Paal Joergensen | 95901159d0 | |
Paal Joergensen | 90d1e7579d | |
Frode Petterson | f3efd217c8 | |
Paal Joergensen | f91f2e82fb | |
Frode Petterson | 6798e0bbbf | |
Frode Petterson | 717edc2a6f | |
Frode Petterson | 5919b64c2a | |
Frode Petterson | b418d24c29 | |
Ian McNamara | 07ac995cf0 | |
Frode Petterson | 500c264b88 | |
Frode Petterson | dfdfb3bd99 | |
Paal Joergensen | cbbff0e296 | |
Paal Joergensen | 0252beb03d | |
Paal Joergensen | 64f170b8a3 | |
Tom Arild Jakobsen | e3d7cf2562 | |
Frode Petterson | f090e651e1 | |
Paal Joergensen | 7c3a322fda | |
Frode Petterson | f112f761c3 | |
Frode Petterson | 0dddbf654e | |
Frode Petterson | 7728243c18 | |
Frode Petterson | b6019ade21 | |
Cornel Les | 75803f8dfb | |
Frode Petterson | 3a2cd46f6a | |
Frode Petterson | cc5440cc4a | |
Frode Petterson | b1531bcbc0 | |
Timothy Lim | 91ff99145a | |
Timothy Lim | cc7caa6604 | |
Frode Petterson | 6444c1443b | |
thomasmars | f07ce1b3c3 | |
thomasmars | d2746b18b4 | |
Tom Arild Jakobsen | 9ea6f277e1 | |
Frode Petterson | 278586ef58 | |
Frode Petterson | 3192f0fe19 | |
Paal Joergensen | 46d772cb51 | |
Frode Petterson | aaa0861355 | |
Frode Petterson | 9adc92d3c4 | |
Frode Petterson | 59fd08594f | |
Frode Petterson | 42e0c99fd3 | |
thomasmars | fa31c56dce | |
thomasmars | 0ce7d66c3a | |
thomasmars | d1bbed49fc | |
Tom Arild Jakobsen | 87bd3c7e11 | |
Tom Arild Jakobsen | f277fed4d1 | |
Tom Arild Jakobsen | dfc2e52ba5 | |
Tom Arild Jakobsen | 481322e6ae | |
Paal Joergensen | 677adb0d8a | |
Paal Joergensen | 3fda439aa1 | |
Timothy Lim | fb76e09e8c | |
Timothy Lim | 56e453f6c1 | |
Timothy Lim | edb9e6244c | |
Timothy Lim | 8d01baca8e | |
Timothy Lim | 9ee5d2ebe7 | |
Frode Petterson | 60c822b47e | |
Frode Petterson | 41cee98970 | |
Frode Petterson | bd851f2929 | |
thomasmars | d4c90e716d | |
Paal Joergensen | da568e4b8a | |
Frode Petterson | dcbb5a2a18 | |
thomasmars | d50d98297b | |
Paal Joergensen | 727e88908a | |
thomasmars | 2ff14ecd37 | |
Frode Petterson | d81fa06499 | |
Frode Petterson | c475db843d | |
thomasmars | c4f2b26116 | |
Tom Arild Jakobsen | 86ee19905e | |
thomasmars | a0069235f1 | |
Frode Petterson | 29fd758bf5 | |
Frode Petterson | c5974e6212 | |
Frode Petterson | 280946979f | |
Svein-Tore Griff With | 98f1ac4abb | |
otacke | 8cb9e8b0d1 | |
otacke | bd83ddc5e7 | |
Frode Petterson | bfda796ab7 | |
Timothy Lim | 33406c69cc | |
thomasmars | d3d5dbe1a7 | |
thomasmars | 3611da5921 | |
thomasmars | 4098f7a0d9 | |
thomasmars | fd93b30a38 | |
thomasmars | 675c87df68 | |
thomasmars | 93e9d6e0ec | |
thomasmars | 75b8f9efd6 | |
thomasmars | 754346992b | |
thomasmars | 1b19f2701d | |
thomasmars | cf3022af43 | |
thomasmars | 0970f74b43 | |
Paal Joergensen | b3e0539261 | |
Frode Petterson | 907396e49b | |
Frode Petterson | 2b327323e1 | |
Svein-Tore Griff With | 58d5eef422 | |
Svein-Tore Griff With | 918084647f | |
Luke Muller | 4999862689 | |
thomasmars | 16982c0207 | |
thomasmars | a720d46548 | |
Andreas Nergaard | 358424f2be | |
Paal Joergensen | f247bbdfe8 | |
Paal Joergensen | 914ee55ac5 | |
Paal Joergensen | 37f4399ab4 | |
Paal Joergensen | c8b123c5c3 | |
thomasmars | ae56eef91d | |
thomasmars | a95323948e | |
thomasmars | 217c2006a4 | |
thomasmars | 318b9f4819 | |
thomasmars | 410c2a9ae1 | |
thomasmars | 0af411bac8 | |
thomasmars | c0e82edfb4 | |
thomasmars | 79e32f0a7f | |
thomasmars | 515365d8df | |
thomasmars | 34b8a2fb58 | |
thomasmars | 16228b3086 | |
thomasmars | 0e2edff5a8 | |
Paal Joergensen | 3dbb8fae70 | |
Frode Petterson | 90fd9a2a28 | |
Frode Petterson | 66409f1a14 | |
Frode Petterson | 179601657c | |
Frode Petterson | b6ca0cd2df | |
Frode Petterson | 0f08031abf | |
thomasmars | 362464ad56 | |
Frode Petterson | a83ea9999d | |
thomasmars | 3068d58b2b | |
thomasmars | 933b95f5ce | |
thomasmars | ec5a164971 | |
thomasmars | 0641b3b824 | |
thomasmars | 2b5a10bec6 | |
thomasmars | 1dec6453fd | |
thomasmars | fb1ed589e5 | |
thomasmars | 585f4d238a | |
thomasmars | e88a23d265 | |
thomasmars | a32f210188 | |
thomasmars | aa861fc8ce | |
thomasmars | 183ec0a930 | |
thomasmars | 4a8a791327 | |
thomasmars | 952a07456c | |
Frode Petterson | bdf60a943d | |
Frode Petterson | a6b01f510f | |
thomasmars | ff531a157c | |
thomasmars | 1f7d03dfd4 | |
thomasmars | f556cde385 | |
thomasmars | 58cbfc7f0c | |
Frode Petterson | 79fa5076f2 | |
thomasmars | 24fd6b1bc5 | |
thomasmars | 46d4d403f4 | |
thomasmars | c4affb23dd | |
thomasmars | c70f8521bb | |
thomasmars | ff496d9a38 | |
Frode Petterson | c4b88c3992 | |
Andreas Nergaard | 8f132cfffb | |
Andreas Nergaard | c41d001754 | |
Andreas Nergaard | 3e9277c0f8 | |
Frode Petterson | e66e6c5d76 | |
Frode Petterson | bb71ea20a3 | |
Frode Petterson | ea3b86dcfe | |
Frode Petterson | b555e4d7a4 | |
Frode Petterson | 26d0a3a341 | |
Frode Petterson | 39d7ddc6b8 | |
Frode Petterson | e548b3575f | |
Timothy Lim | 3b9dd95683 | |
Paal Joergensen | ef4165a11d | |
Paal Joergensen | 6ab68eb6e2 | |
Frode Petterson | 9abe7ef321 | |
Timothy Lim | 5dc9e3ddd1 | |
Frode Petterson | bdec1319ed | |
Frode Petterson | 03271418f0 | |
Frode Petterson | 95cfaf6b9e | |
Frode Petterson | e02df35bb1 | |
Paal Joergensen | aedd0112c2 | |
Frode Petterson | 79fb0e310c | |
Paal Joergensen | 6e135d0319 | |
Paal Joergensen | 738dced286 | |
Paal Joergensen | 2cc6c53da1 | |
Tom Arild Jakobsen | dcb02810f0 | |
Andreas Nergaard | fc668c5ab3 | |
Andreas Nergaard | 893f610861 | |
Paal Joergensen | a41df0ad9e | |
Paal Joergensen | e00bdbd125 | |
Paal Joergensen | 20042fba85 | |
Paal Joergensen | 671c079eee | |
thomasmars | 53f38d596b | |
thomasmars | e2634070d4 | |
Paal Joergensen | 8f681a2140 | |
Paal Joergensen | 8dd766ecb8 | |
Timothy Lim | b91be29cec | |
Paal Joergensen | 117e1457b7 | |
Paal Joergensen | 1341768c90 | |
Paal Joergensen | f6449a6043 | |
Paal Joergensen | 87ec43d687 | |
Paal Joergensen | 7b39a6900c | |
Paal Joergensen | ea0362c3da | |
Paal Joergensen | a1e68d212b | |
Paal Joergensen | 526d7ddd7e | |
Timothy Lim | 28e34c1c09 | |
Andreas Nergaard | 5a272ea2f1 | |
Timothy Lim | bbe16fb8da | |
Timothy Lim | f0d65d4f9d | |
Paal Joergensen | cfd6cb1e63 | |
jelenamilinovic | 1058dab02d | |
jelenamilinovic | 26cedf7d70 | |
jelenamilinovic | c442300cab | |
jelenamilinovic | afc2084c2e | |
jelenamilinovic | cddef83287 | |
thomasmars | a326066c97 | |
Paal Joergensen | e69a81f403 | |
Timothy Lim | 52dc1b2a81 | |
Paal Joergensen | 0a23dc043d | |
thomasmars | 7d741fa503 | |
Timothy Lim | a4d6f3aaab | |
thomasmars | d76ce3f3a5 | |
Andreas Nergaard | 180d179f66 | |
thomasmars | bc28592e48 | |
thomasmars | 47c35d5135 | |
Pål Jørgensen | c906cb7084 | |
thomasmars | d100c5102a | |
Paal Joergensen | 4d010ef38f | |
thomasmars | 5ec651e696 | |
Paal Joergensen | ed1fb00745 | |
Pål Jørgensen | ece72fcfbe | |
Tom Arild Jakobsen | 32d06356b1 | |
Tom Arild Jakobsen | 5494443533 | |
Tom Arild Jakobsen | 0882879552 | |
Tom Arild Jakobsen | b4615e01b1 | |
Tom Arild Jakobsen | b55c1838a9 | |
thomasmars | 9b0d77a721 | |
thomasmars | 3278535a04 | |
thomasmars | f82f3a354c | |
thomasmars | 0d3648d601 | |
thomasmars | d55134151c | |
thomasmars | 3e2266fe6c | |
thomasmars | ec2ff180c1 | |
thomasmars | bb29d0c161 | |
Frode Petterson | 44c0507887 | |
thomasmars | 49e0ff09f4 | |
Paal Joergensen | 6001b68b99 | |
Paal Joergensen | 7e90871cb7 | |
Paal Joergensen | 96f84a430a | |
Frode Petterson | 1173341b76 | |
thomasmars | 426f51caa0 | |
thomasmars | bae31451c0 | |
thomasmars | b4949c0d83 | |
thomasmars | 516c70add5 | |
Frode Petterson | 08a1cece26 | |
thomasmars | 0474ed89af | |
thomasmars | f28036ca0e | |
Frode Petterson | bf554b8ee2 | |
Mannes | 52723ad933 | |
Frode Petterson | c9e5680e85 | |
Frode Petterson | e4266efb22 | |
Frode Petterson | 0a9aa5a783 | |
Håkon Ellingsen | a2097d56cb | |
Mikael Lindqvist | ce796cf0cf | |
thomasmars | cee88a544f | |
Frode Petterson | 1c5b9707e5 | |
Frode Petterson | 263a987eeb | |
Frode Petterson | 6b6d4cd109 | |
Frode Petterson | f0e7ea1283 | |
thomasmars | 7d86a6235b | |
Thomas | 218e48ee91 | |
Paal Joergensen | 5f38e2b7f8 | |
Frode Petterson | fd34af6964 | |
Frode Petterson | c88c049438 | |
Frode Petterson | e8f90c9116 | |
Frode Petterson | 2896c9fdab | |
Frode Petterson | 7fca1d100d | |
Paal Joergensen | 5b8836b5be | |
Frode Petterson | d946f32b81 | |
Frode Petterson | 888a46844b | |
Frode Petterson | 832aebf5e0 | |
Frode Petterson | 1ef52dfafe | |
Frode Petterson | 44633fc1db | |
Frode Petterson | 8b31f88ca8 | |
Frode Petterson | 9f7b545f02 | |
Svein-Tore Griff With | 43748bc4b8 | |
Frode Petterson | 61219a7d84 | |
Frode Petterson | 3d14588160 | |
ndobromirov | 406a55be5c | |
Frode Petterson | 18e2b79cc6 | |
andyrandom | 8cb0aad09e | |
Frode Petterson | a6656c2e6f | |
Frode Petterson | 77b9959c87 | |
Dave Richer | bcdaf59e5e | |
Frode Petterson | d4d9f81518 | |
Frode Petterson | a9a99afb9b | |
Frode Petterson | 94d3334f37 | |
Frode Petterson | ba2f7c5e67 | |
Frode Petterson | 89541acfa8 | |
Frode Petterson | ec2e0fbb71 | |
Frode Petterson | 0ee4cc57fc | |
Frode Petterson | fca0537a4d | |
Frode Petterson | 5e87aee63c | |
Thomas Marstrander | b3c55928e3 | |
Thomas Marstrander | 51851f14c3 | |
Thomas Marstrander | e10594fb49 | |
Paal Joergensen | d42630c369 | |
Paal Joergensen | 6caf44e54b | |
Thomas Marstrander | 8615deb23b | |
Thomas Marstrander | 10e1a7ba65 | |
Thomas Marstrander | 8bd9e9c2db | |
Thomas Marstrander | 77fb832221 | |
Frode Petterson | b261754cf1 | |
Frode Petterson | 2ebc9225ee | |
Paal Joergensen | f4cee6e284 | |
Thomas Marstrander | 87658ed42b | |
Paal Joergensen | 7807b8af68 | |
Thomas Marstrander | e3b29a2199 | |
Frode Petterson | 2bcf3b783f | |
Thomas Marstrander | f6ce5dff74 | |
Thomas Marstrander | ed13203254 | |
Frode Petterson | 0d6fac4099 | |
Thomas Marstrander | 93a07d2e9b | |
Thomas Marstrander | b102a7de21 | |
Frode Petterson | ce89b516ef | |
Frode Petterson | 594d992f19 | |
Thomas Marstrander | eba774c863 | |
Frode Petterson | c2f5feaae5 | |
Thomas Marstrander | 0a63d123cf | |
Thomas Marstrander | 73a23d4679 | |
Frode Petterson | bba5190876 | |
Thomas Marstrander | c6b3b06a97 | |
Thomas Marstrander | 27bc158708 | |
Paal Joergensen | 2c4edb7b56 | |
Frode Petterson | cc121b2f53 | |
Frode Petterson | 417af36c81 | |
Frode Petterson | cbfa2913bc | |
Frode Petterson | 51698d325b | |
Frode Petterson | a36b2f1212 | |
Frode Petterson | e056b6776a | |
Thomas Marstrander | e412c3a228 | |
Thomas Marstrander | 7782b19e99 | |
Frode Petterson | acaccea977 | |
Thomas Marstrander | 6e7e144900 | |
Frode Petterson | c9583cb8fa | |
Thomas Marstrander | 4da518296e | |
Frode Petterson | c9349b8e26 | |
Frode Petterson | b6b2a6b1dd | |
Frode Petterson | 174d8c0941 | |
Frode Petterson | 325ea27562 | |
Frode Petterson | 7b38c3571e | |
Thomas Marstrander | 05dfc9c374 | |
Thomas Marstrander | 8a6733ceb4 | |
Frode Petterson | 1623f6701e | |
Dave Richer | 76e813a32e | |
Frode Petterson | db6f18c984 | |
Frode Petterson | e324e298b9 | |
Thomas Marstrander | 926ff4af3a | |
Paal Joergensen | 6869ecbb09 | |
Thomas Marstrander | 414751aad2 | |
Frode Petterson | 64de9d53f8 | |
Frode Petterson | b559935c09 | |
Svein-Tore Griff With | f510d0f0e9 | |
Dave Richer | 29a8601595 | |
Marc Laporte | b0f7c5cd35 | |
Frode Petterson | e26a662341 | |
Frode Petterson | b93b7082dc | |
Frode Petterson | cdfb810501 | |
Frode Petterson | 7474916f3a | |
Frode Petterson | 471a1ea6a8 | |
Frode Petterson | 38fc011c7f | |
Thomas Marstrander | 7f41e2b3c6 | |
Thomas Marstrander | 711b9326bc | |
Frode Petterson | 84d93af447 | |
Frode Petterson | 9741dd882d | |
Thomas Marstrander | 233b5e7653 | |
Thomas Marstrander | e8adccaa02 | |
Thomas Marstrander | ea620e12f0 | |
Thomas Marstrander | bf7f5c863c | |
Frode Petterson | 41c98cc13c | |
Frode Petterson | e0d3c2e1e0 | |
Frode Petterson | d4112a639a | |
Marc Laporte | 02e5f2045e | |
Thomas Marstrander | 35a7213a67 | |
Frode Petterson | 127dc4f0e1 | |
Frode Petterson | 0c5971d6d4 | |
Frode Petterson | cdfab4d4b6 | |
Frode Petterson | a11e8d4365 | |
Thomas Marstrander | 17ad3d4825 | |
Thomas Marstrander | 4d277b2304 | |
Paal Joergensen | d912232433 | |
Paal Joergensen | f05d3b3bf5 | |
Frode Petterson | 6b31d467a0 | |
Frode Petterson | 41464cb98c | |
Frode Petterson | 62e2f41884 | |
Frode Petterson | 007137c257 | |
Frode Petterson | 2ac631458f | |
Frode Petterson | 14605dc790 | |
Frode Petterson | b8a3965c6e | |
Paal Joergensen | 5229e82764 | |
Thomas Marstrander | 4775e3444f | |
Frode Petterson | de175737ea | |
Frode Petterson | 89227e4639 | |
Frode Petterson | 61ae8899cb | |
Frode Petterson | f05a370b42 | |
Frode Petterson | 5b965636e0 | |
Frode Petterson | 36c8bf3362 | |
Frode Petterson | 13a2988414 | |
Frode Petterson | 0218493eab | |
Frode Petterson | 437598aa8e | |
Thomas Marstrander | 4ed368f5e7 | |
Thomas Marstrander | ee245f9344 | |
Thomas Marstrander | fa7b42120d | |
Thomas Marstrander | 54ca645e2c | |
Frode Petterson | 93fd710509 | |
Frode Petterson | 43e9c7adcb | |
Frode Petterson | 35a0d09b9b | |
Frode Petterson | a41ecccb9a | |
Frode Petterson | a398589c80 | |
Frode Petterson | b42193729e | |
Frode Petterson | 0430e6ba28 | |
Thomas Marstrander | 92e4ab0f9e | |
Frode Petterson | 06e9508eda | |
Frode Petterson | 6bfffbcf30 | |
Frode Petterson | 4e06733ffb | |
Frode Petterson | 0aebdb345b | |
Frode Petterson | 256bf1b6d2 | |
Thomas Marstrander | bfcf4228db | |
Frode Petterson | 12ba0d2da5 | |
Frode Petterson | 3849f3a054 | |
Frode Petterson | b42b28ab03 | |
Frode Petterson | 7a87735f02 | |
Frode Petterson | 4cf7a32b4a | |
Frode Petterson | 58f3617fc1 | |
Frode Petterson | 6580508b1d | |
Frode Petterson | dc8b70748f | |
Frode Petterson | 2a20dc23ae | |
Frode Petterson | b16b0c1e0b | |
Frode Petterson | cf3373d4ee | |
Frode Petterson | c4011e3bbb | |
Frode Petterson | 70baeb55a1 | |
Frode Petterson | fa90b0a1c3 | |
Frode Petterson | 292e04de5d | |
Frode Petterson | 971e035626 | |
Frode Petterson | f5f89f2395 | |
Frode Petterson | 7328245e8d | |
Thomas Marstrander | 5287d455b2 | |
Thomas Marstrander | 2e0498cd3a | |
Frode Petterson | 530ad8ee33 | |
Frode Petterson | 62cd708f6b | |
Thomas Marstrander | 9b76fccfce | |
Frode Petterson | 3991875439 | |
Frode Petterson | 633186c0d5 | |
Frode Petterson | 36ca38705f | |
Frode Petterson | 3defc7abde | |
Frode Petterson | 73fd170a3a | |
Frode Petterson | 64e2001156 | |
Frode Petterson | 875a7ccef3 | |
Frode Petterson | 0f736ae1c5 | |
Frode Petterson | fb9a84e665 | |
Frode Petterson | 5eb0e0e14c | |
Frode Petterson | 8597baa253 | |
Frode Petterson | a65625d123 | |
Frode Petterson | 15381d64a4 | |
Frode Petterson | fa4b6ab524 | |
Frode Petterson | f8c2a47580 | |
Frode Petterson | 604218f2e3 | |
Frode Petterson | 7c146ce919 | |
Frode Petterson | dc69181025 | |
Frode Petterson | 12032461bc | |
Frode Petterson | 11481c3b4b | |
Frode Petterson | 00686b733d | |
Frode Petterson | 1bf393a9af | |
Frode Petterson | b4fd0e098c | |
Frode Petterson | f4ba0af1ef | |
Frode Petterson | 808e43bc66 | |
Frode Petterson | 0125afb440 | |
Frode Petterson | 2bd972168e | |
Frode Petterson | 510016b1cc | |
Frode Petterson | 59901fd1c9 | |
Paal Joergensen | 919ac77f35 | |
Paal Joergensen | e83b213183 | |
Frode Petterson | 5e45e7d7dd | |
Paal Joergensen | e6ec257c6d | |
Paal Joergensen | 2d0e409ac4 |
|
@ -1 +1,3 @@
|
||||||
|
vendor
|
||||||
*~
|
*~
|
||||||
|
.idea
|
|
@ -0,0 +1,40 @@
|
||||||
|
language: php
|
||||||
|
|
||||||
|
# At present the only jobs to run are a php lint.
|
||||||
|
# Run this against all supported versions of PHP.
|
||||||
|
jobs:
|
||||||
|
include:
|
||||||
|
# Bionic supports PHP 7.1, 7.2, 7.3, and 7.4.
|
||||||
|
# https://docs.travis-ci.com/user/reference/bionic/#php-support
|
||||||
|
- php: 7.4
|
||||||
|
dist: bionic
|
||||||
|
- php: 7.3
|
||||||
|
dist: bionic
|
||||||
|
- php: 7.2
|
||||||
|
dist: bionic
|
||||||
|
- php: 7.1
|
||||||
|
dist: bionic
|
||||||
|
|
||||||
|
# Xenial was the last Travis distribution to support PHP 5.6, and 7.0.
|
||||||
|
# https://docs.travis-ci.com/user/reference/xenial/#php-support
|
||||||
|
- php: 7.0
|
||||||
|
dist: xenial
|
||||||
|
- php: 5.6
|
||||||
|
dist: xenial
|
||||||
|
|
||||||
|
# Trusty was the last Travis distribution to support PHP 5.4, and 5.5.
|
||||||
|
# https://docs.travis-ci.com/user/languages/php/#php-54x---55x-support-is-available-on-precise-and-trusty-only
|
||||||
|
- php: 5.5
|
||||||
|
dist: trusty
|
||||||
|
- php: 5.4
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
|
|
||||||
|
# Precise was the last Travis distribution to support PHP 5.2, and 5.3.
|
||||||
|
# https://docs.travis-ci.com/user/languages/php/#php-52x---53x-support-is-available-on-precise-only
|
||||||
|
- php: 5.3
|
||||||
|
dist: precise
|
||||||
|
|
||||||
|
script:
|
||||||
|
# Run a php lint across all PHP files.
|
||||||
|
- find . -type f -name '*\.php' -print0 | xargs -0 -n1 php -l
|
12
README.txt
|
@ -1,14 +1,14 @@
|
||||||
This folder contains the h5p general library. The files within this folder are not specific to any framework.
|
This folder contains the general H5P library. The files within this folder are not specific to any framework.
|
||||||
|
|
||||||
Any interaction with LMS, CMS or other frameworks is done through interfaces. Plattforms needs to implement
|
Any interaction with an LMS, CMS or other frameworks is done through interfaces. Platforms need to implement
|
||||||
the H5PFrameworkInterface(in h5p.classes.php) and also do the following:
|
the H5PFrameworkInterface(in h5p.classes.php) and also do the following:
|
||||||
|
|
||||||
- Provide a form for uploading h5p packages.
|
- Provide a form for uploading H5P packages.
|
||||||
- Place the uploaded h5p packages in a temporary directory
|
- Place the uploaded H5P packages in a temporary directory
|
||||||
+++
|
+++
|
||||||
|
|
||||||
See existing implementations for details. For instance the Drupal h5p module located on drupal.org/project/h5p
|
See existing implementations for details. For instance the Drupal H5P module located at drupal.org/project/h5p
|
||||||
|
|
||||||
We will make available documentations and tutorials for creating platform integrations in the future
|
We will make available documentation and tutorials for creating platform integrations in the future.
|
||||||
|
|
||||||
The H5P PHP library is GPL licensed due to GPL code being used for purifying HTML provided by authors.
|
The H5P PHP library is GPL licensed due to GPL code being used for purifying HTML provided by authors.
|
|
@ -1,10 +1,35 @@
|
||||||
{
|
{
|
||||||
"name": "h5p/h5p-php-library",
|
"name": "h5p/h5p-core",
|
||||||
|
"type": "library",
|
||||||
|
"description": "H5P Core functionality in PHP",
|
||||||
|
"keywords": ["h5p","hvp","interactive","content","quiz"],
|
||||||
|
"homepage": "https://h5p.org",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Svein-Tore Griff With",
|
||||||
|
"email": "with@joubel.com",
|
||||||
|
"homepage": "http://joubel.com",
|
||||||
|
"role": "CEO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Frode Petterson",
|
||||||
|
"email": "frode.petterson@joubel.com",
|
||||||
|
"homepage": "http://joubel.com",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.0"
|
||||||
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"files": [
|
"files": [
|
||||||
|
"h5p.classes.php",
|
||||||
"h5p-development.class.php",
|
"h5p-development.class.php",
|
||||||
"h5p.classes.php"
|
"h5p-file-storage.interface.php",
|
||||||
|
"h5p-default-storage.class.php",
|
||||||
|
"h5p-event-base.class.php",
|
||||||
|
"h5p-metadata.class.php"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<h2>Overview</h2>
|
<h2>Overview</h2>
|
||||||
<p>H5P is a file format for content/applications made using modern, open web technlogies (HTML5). The format enables easy installation and transfer of applications/content on different CMSes, LMSes and other platforms. An H5P canbe uploaded and published on a platform in mostly the same way one would publish a Flash file today. H5P files may also be updated by simply uploading a new version of the file, the same way as one would using Flash.</p>
|
<p>H5P is a file format for content/applications made using modern, open web technologies (HTML5). The format enables easy installation and transfer of applications/content on different CMSes, LMSes and other platforms. An H5P can be uploaded and published on a platform in mostly the same way one would publish a Flash file today. H5P files may also be updated by simply uploading a new version of the file, the same way as one would using Flash.</p>
|
||||||
<p>H5P opens for extensive reuse of code and wide flexibility regarding what may be developed as an H5P.</p>
|
<p>H5P opens for extensive reuse of code and wide flexibility regarding what may be developed as an H5P.</p>
|
||||||
<p>The system uses package files containing all necessary files and libraries for the application to function. These files are based on open formats.</p>
|
<p>The system uses package files containing all necessary files and libraries for the application to function. These files are based on open formats.</p>
|
||||||
<h2>Overview of package files</h2>
|
<h2>Overview of package files</h2>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<li>A mandatory file in the root folder named <i>h5p.json</i></li>
|
<li>A mandatory file in the root folder named <i>h5p.json</i></li>
|
||||||
<li>An optional image file named <i>h5p.jpg</i>. This is an icon or an image of the application, 512 × 512 pixels. This image may be used by the platform as a preview of the application, and could be included in OG meta tags for use with social media.</li>
|
<li>An optional image file named <i>h5p.jpg</i>. This is an icon or an image of the application, 512 × 512 pixels. This image may be used by the platform as a preview of the application, and could be included in OG meta tags for use with social media.</li>
|
||||||
<li>One content folder, named <i>content</i>. This will contain the preset configuration for the application, as well as any required media files.</li>
|
<li>One content folder, named <i>content</i>. This will contain the preset configuration for the application, as well as any required media files.</li>
|
||||||
<li>One or more library diractories named the same as the library's internal name.</li>
|
<li>One or more library directories named the same as the library's internal name.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<h2>h5p.json</h2>
|
<h2>h5p.json</h2>
|
||||||
<p>The <i>h5p.json</i> file is a normal JSON text file containing a JSON object with the following predefined properties.</p>
|
<p>The <i>h5p.json</i> file is a normal JSON text file containing a JSON object with the following predefined properties.</p>
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
<ul><li>contentType - Textual description of the type of content.</li>
|
<ul><li>contentType - Textual description of the type of content.</li>
|
||||||
<li>description - Textual description of the package.</li>
|
<li>description - Textual description of the package.</li>
|
||||||
<li>author - Name of author.</li>
|
<li>author - Name of author.</li>
|
||||||
<li>license - Kode for the content license. Use the following creative commons codes: cc-by, cc-by-sa, cc-by-nd, cc-by-nc, cc-by-nc-sa, cc-by-nc-nd. In addition for public domain: pd, and closed license: cr. More may be added later.</li>
|
<li>license - Code for the content license. Use the following Creative Commons codes: cc-by, cc-by-sa, cc-by-nd, cc-by-nc, cc-by-nc-sa, cc-by-nc-nd. In addition for public domain: pd, and closed license: cr. More may be added later.</li>
|
||||||
<li>dynamicDependencies - Libraries that may be loaded dynamically during execution.</li>
|
<li>dynamicDependencies - Libraries that may be loaded dynamically during execution.</li>
|
||||||
<li>width - Width of the package content in cases where the package is not dynamically resizable.</li>
|
<li>width - Width of the package content in cases where the package is not dynamically resizable.</li>
|
||||||
<li>height - Height of the package content.</li>
|
<li>height - Height of the package content.</li>
|
||||||
|
@ -74,9 +74,9 @@
|
||||||
|
|
||||||
<p>The root of a library folder shall contain a file name <i>library.json</i> formatted similar to the package's <i>hp5.json</i>, but with a few differences. The library shall also have one or more images in the root folder, named <i>library.jpg</i>, <i>library1.jpg</i> etc. Image sizes 512px × 512px, and will be used in the H5P editor tool.</p>
|
<p>The root of a library folder shall contain a file name <i>library.json</i> formatted similar to the package's <i>hp5.json</i>, but with a few differences. The library shall also have one or more images in the root folder, named <i>library.jpg</i>, <i>library1.jpg</i> etc. Image sizes 512px × 512px, and will be used in the H5P editor tool.</p>
|
||||||
|
|
||||||
<p>Libraries are not allowed to modify the dokument tree in ways that will have consequences for the web site or will be noticable by the user without the library explicitly being initialized from the main package library or another invoked library.</p>
|
<p>Libraries are not allowed to modify the document tree in ways that will have consequences for the web site or will be noticeable by the user without the library explicitly being initialized from the main package library or another invoked library.</p>
|
||||||
|
|
||||||
<p>The library shall always include a Javascript object function named the same as the defined library <i>machineName</i> (defined in <i>library.json</i> and used as the library folder name). This object will be instantiated with the library options as parameter. The resulting object must contain a function <i>attach(target)</i> that will be called after instantiation to attach the library DOM to the main DOM inside <i>target</i></p>
|
<p>The library shall always include a JavaScript object function named the same as the defined library <i>machineName</i> (defined in <i>library.json</i> and used as the library folder name). This object will be instantiated with the library options as parameter. The resulting object must contain a function <i>attach(target)</i> that will be called after instantiation to attach the library DOM to the main DOM inside <i>target</i></p>
|
||||||
|
|
||||||
<h3>Example</h3>
|
<h3>Example</h3>
|
||||||
<p>A library called H5P.multichoice would typically be instantiated and attached to the page like this:</p>
|
<p>A library called H5P.multichoice would typically be instantiated and attached to the page like this:</p>
|
||||||
|
@ -138,7 +138,7 @@ multichoice.attach($multichoiceContainer);</code>
|
||||||
<h2>Allowed file types</h2>
|
<h2>Allowed file types</h2>
|
||||||
<p>Files that require server side execution or that cannot be regarded an open standard shall not be used. Allowed file types: js, json, png, jpg, gif, svg, css, mp3, wav (audio: PCM), m4a (audio: AAC), mp4 (video: H.264, audio: AAC/MP3), ogg (video: Theora, audio: Vorbis) and webm (video VP8, audio: Vorbis). Administrators of web sites implementing H5P may open for accepting further formats. HTML files shall not be used. HTML for each library shall be inserted from the library scripts to ease code reuse. (By avoiding content being defined in said HTML).</p>
|
<p>Files that require server side execution or that cannot be regarded an open standard shall not be used. Allowed file types: js, json, png, jpg, gif, svg, css, mp3, wav (audio: PCM), m4a (audio: AAC), mp4 (video: H.264, audio: AAC/MP3), ogg (video: Theora, audio: Vorbis) and webm (video VP8, audio: Vorbis). Administrators of web sites implementing H5P may open for accepting further formats. HTML files shall not be used. HTML for each library shall be inserted from the library scripts to ease code reuse. (By avoiding content being defined in said HTML).</p>
|
||||||
<h2>API functions</h2>
|
<h2>API functions</h2>
|
||||||
<p>The following javascript functions are available through h5p:</p>
|
<p>The following JavaScript functions are available through h5p:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>H5P.getUserData(namespace, variable)</li>
|
<li>H5P.getUserData(namespace, variable)</li>
|
||||||
<li>H5P.setUserData(namespace, variable, data)</li>
|
<li>H5P.setUserData(namespace, variable, data)</li>
|
||||||
|
@ -156,13 +156,13 @@ multichoice.attach($multichoiceContainer);</code>
|
||||||
<h2>Best practices</h2>
|
<h2>Best practices</h2>
|
||||||
<p>H5P is a very open standard. This is positive for flexibility. Most content may be produces as H5P. But this also allows for bad code, security weaknesses, code that may be difficult to reuse. Therefore the following best practices should be followed to get the most from H5P:</p>
|
<p>H5P is a very open standard. This is positive for flexibility. Most content may be produces as H5P. But this also allows for bad code, security weaknesses, code that may be difficult to reuse. Therefore the following best practices should be followed to get the most from H5P:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Think reusability when creating a library. H5P support depencies between libraries, so the same small quiz-library may be used in various larger packages or libraries.</li>
|
<li>Think reusability when creating a library. H5P support dependencies between libraries, so the same small quiz-library may be used in various larger packages or libraries.</li>
|
||||||
<li>H5P support library updates. This enables all content using a common library to be updated at once. This must be accounted for when writing new libraries. A library should be as general as possible. The content format should be thought out so there are no changes to the required content data when a library is updated. Note: Multiple versions of a library may exists at the same time, only patch level updates will be automatically installed.</li>
|
<li>H5P supports library updates. This enables all content using a common library to be updated at once. This must be accounted for when writing new libraries. A library should be as general as possible. The content format should be thought out so there are no changes to the required content data when a library is updated. Note: Multiple versions of a library may exists at the same time, only patch level updates will be automatically installed.</li>
|
||||||
<li>An H5P should not interact directly with the containing web site. It shall only affect elements within its own generated DOM tree. Elements shall also only be injected within the target defined on initialization. This is to avoid depencies to a specific platform or web page.</li>
|
<li>An H5P should not interact directly with the containing web site. It shall only affect elements within its own generated DOM tree. Elements shall also only be injected within the target defined on initialization. This is to avoid dependencies to a specific platform or web page.</li>
|
||||||
<li>Prefix objects, global functions, etc with h5p to minimize the chance of namespace conflicts with the rest of the web page. Remember that there may also be multiple H5P objects inserted on a page, so plan ahead to avoid conflicts.</li>
|
<li>Prefix objects, global functions, etc with h5p to minimize the chance of namespace conflicts with the rest of the web page. Remember that there may also be multiple H5P objects inserted on a page, so plan ahead to avoid conflicts.</li>
|
||||||
<li>Content should be responsive.</li>
|
<li>Content should be responsive.</li>
|
||||||
<li>Content should be WCAG 2 AA compliant</li>
|
<li>Content should be WCAG 2 AA compliant</li>
|
||||||
<li>All generated HTML should validate.</li>
|
<li>All generated HTML should validate.</li>
|
||||||
<li>All css should validate (some browser specific non-standard CSS may at times be required)</li>
|
<li>All CSS should validate (some browser specific non-standard CSS may at times be required)</li>
|
||||||
<li>Best practices for javascript, html, etc. should of course also be followed when writing an H5P.</li>
|
<li>Best practices for JavaScript, HTML, etc. should of course also be followed when writing an H5P.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<?php for ($i = 0, $s = count($styles); $i < $s; $i++): ?>
|
<?php for ($i = 0, $s = count($styles); $i < $s; $i++): ?>
|
||||||
<link rel="stylesheet" href="<?php print $styles[$i]; ?>">
|
<link rel="stylesheet" href="<?php print $styles[$i]; ?>">
|
||||||
<?php endfor; ?>
|
<?php endfor; ?>
|
||||||
|
<?php if (!empty($additional_embed_head_tags)): print implode("\n", $additional_embed_head_tags); endif; ?>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="h5p-content" data-content-id="<?php print $content['id']; ?>"></div>
|
<div class="h5p-content" data-content-id="<?php print $content['id']; ?>"></div>
|
||||||
|
|
After Width: | Height: | Size: 97 KiB |
|
@ -0,0 +1,38 @@
|
||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<metadata>
|
||||||
|
<json>
|
||||||
|
<![CDATA[
|
||||||
|
{
|
||||||
|
"fontFamily": "h5p-hub",
|
||||||
|
"description": "Font generated by IcoMoon.",
|
||||||
|
"majorVersion": 1,
|
||||||
|
"minorVersion": 3,
|
||||||
|
"version": "Version 1.3",
|
||||||
|
"fontId": "h5p-hub",
|
||||||
|
"psName": "h5p-hub",
|
||||||
|
"subFamily": "Regular",
|
||||||
|
"fullName": "h5p-hub"
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</json>
|
||||||
|
</metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="h5p-hub" horiz-adv-x="1024">
|
||||||
|
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||||
|
<missing-glyph horiz-adv-x="1024" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="512" d="" />
|
||||||
|
<glyph unicode="" glyph-name="dropdown" data-tags="dropdown" d="M1004.654 717.002c-11.526 11.526-27.853 19.209-45.142 19.209h-895.161c-35.538 0-63.391-27.853-64.352-63.391 0-17.289 6.724-33.616 19.209-46.103l447.582-447.582c24.013-24.972 63.391-25.933 88.363-1.92 0.961 0.961 0.961 0.961 1.92 1.92l447.582 447.582c24.972 24.013 25.933 63.391 1.92 88.363-0.961 0.961-0.961 0.961-1.92 1.92z" />
|
||||||
|
<glyph unicode="" glyph-name="info" data-tags="info" d="M745.14 76.307c-0.699 25.827-20.941 46.069-46.768 46.768h-46.069v418.813c-1.395 25.827-21.639 46.069-46.768 46.069h-279.209c-25.827 0-46.768-20.941-47.465-46.768v-92.837c0.699-25.827 20.941-46.069 46.768-46.768h46.768v-279.209h-46.069c-25.827-0.699-46.069-20.941-46.768-46.768v-92.837c0.699-25.827 20.941-46.069 46.768-46.768h372.045c25.827 0.699 46.069 20.941 46.768 46.768v93.534zM638.343 946.041c-8.376 9.075-20.242 13.96-32.806 13.96h-185.673c-25.827-0.699-46.069-20.941-46.768-46.768v-139.604c0.699-25.827 20.941-46.069 46.768-46.768h186.372c25.129 1.395 45.371 21.639 46.069 46.768v139.604c0 12.564-5.584 24.43-13.96 32.806z" />
|
||||||
|
<glyph unicode="" glyph-name="thick-arrow" data-tags="thick-arrow" d="M997.162 446.653c0.634 22.167-8.234 44.336-24.701 59.537l-428.789 428.789c-32.302 32.936-85.505 33.568-118.44 0.634 0 0-0.634-0.634-0.634-0.634l-49.403-49.403c-32.936-32.302-32.936-84.871-1.266-117.806 0.634-0.634 0.634-0.634 1.266-1.266l192.544-193.81h-463.625c-21.535 0.634-41.802-8.234-55.737-24.701-14.567-16.467-22.167-38.002-21.535-59.537v-84.238c-0.634-21.535 6.967-43.068 21.535-59.537 13.935-16.467 34.202-25.335 55.737-24.701h464.259l-193.81-190.644c-32.936-32.936-33.568-86.138-0.634-119.073 0 0 0.634-0.634 0.634-0.634l49.403-49.403c32.936-32.302 86.138-32.302 119.073 0l429.423 429.423c15.201 15.201 24.067 35.468 24.701 57.003v0z" />
|
||||||
|
<glyph unicode="" glyph-name="check" data-tags="check" d="M1021.469 686.541c0 17.084-6.328 32.903-18.349 44.924l-89.849 89.215c-24.677 24.677-64.539 24.677-89.215 0.633 0 0 0 0-0.633-0.633l-432.156-432.79-193.616 194.25c-24.677 24.677-64.539 24.677-89.215 0.633 0 0 0 0-0.633-0.633l-89.215-89.215c-24.677-24.677-24.677-64.539-0.633-89.215 0 0 0 0 0.633-0.633l327.755-327.755c24.677-24.677 64.539-24.677 89.215-0.633 0 0 0 0 0.633 0.633l569.46 569.46c12.022 12.022 18.349 27.84 18.349 44.924v0l-2.531-3.163z" />
|
||||||
|
<glyph unicode="" glyph-name="close" data-tags="close" d="M1024 856.869l-103.131 103.131-408.869-408.869-408.869 408.869-103.131-103.131 408.869-408.869-408.869-408.869 103.131-103.131 408.869 408.869 408.869-408.869 103.131 103.131-408.869 408.869z" />
|
||||||
|
<glyph unicode="" glyph-name="plus" data-tags="plus" d="M597.333 533.333v426.667h-170.667v-426.667h-426.667v-170.667h426.667v-426.667h170.667v426.667h426.667v170.667z" />
|
||||||
|
<glyph unicode="" glyph-name="filters" data-tags="filters" d="M217.543 834.589v98.249c0 14.999-12.163 27.162-27.162 27.162h-8.785c-14.999 0-27.162-12.163-27.162-27.162v-98.249c-63.792-14.19-110.78-70.3-110.78-137.38s46.988-123.189 109.844-137.208l0.936-592.841c0-14.999 12.163-27.162 27.162-27.162h7.99c14.999 0 27.162 12.163 27.162 27.162v588.669c69.551 9.191 122.666 68.109 122.666 139.435 0 71.041-52.699 129.772-121.13 139.246zM549.82 417.644v515.184c0 14.999-12.163 27.162-27.162 27.162h-4.796c-14.999 0-27.162-12.163-27.162-27.162v-513.593c-71.358-12.114-125.020-73.469-125.020-147.364s53.662-135.26 124.145-147.242l0.884-159.869c0-14.999 12.163-27.162 27.162-27.162h4.796c14.999 0 27.162 12.163 27.162 27.162v159.745c67.325 15.43 116.8 74.826 116.8 145.772s-49.464 130.342-115.794 145.579zM980.345 594.96c-0.075 69.212-51.022 126.499-117.453 136.489l-0.764 201.379c0 14.999-12.163 27.162-27.162 27.162h-6.387c-14.999 0-27.162-12.163-27.162-27.162v-205.273c-58.17-16.849-99.977-69.642-99.977-132.191s41.807-115.342 99-131.95l0.967-500.252c0-14.999 12.163-27.162 27.162-27.162h4.796c14.999 0 27.162 12.163 27.162 27.162v496.022c67.608 9.329 119.206 66.436 119.809 135.722z" />
|
||||||
|
<glyph unicode="" glyph-name="arrow-line" data-tags="arrow-line" d="M995.878 700.739l-43.703 43.703c-17.591 16.506-41.326 26.645-67.432 26.645s-49.837-10.139-67.483-26.693l-303.387-303.389-305.935 304.692c-17.468 16.781-41.244 27.116-67.432 27.116s-49.958-10.335-67.464-27.148l-44.92-44.92c-17.371-17.206-28.122-41.062-28.122-67.432s10.752-50.226 28.112-67.421l414.583-414.583c17.206-17.371 41.062-28.122 67.432-28.122s50.226 10.752 67.421 28.112l418.329 414.583c17.371 17.206 28.122 41.062 28.122 67.432s-10.752 50.226-28.112 67.421z" />
|
||||||
|
<glyph unicode="" glyph-name="check-empty" data-tags="check-empty" d="M910.2 846.2v-796.4h-796.4v796.4h796.4zM910.2 960h-796.4c-62.6 0-113.8-51.2-113.8-113.8v-796.4c0-62.6 51.2-113.8 113.8-113.8h796.4c62.6 0 113.8 51.2 113.8 113.8v796.4c0 62.6-51.2 113.8-113.8 113.8z" />
|
||||||
|
<glyph unicode="" glyph-name="check1" data-tags="check" d="M910.2 960h-796.4c-62.6 0-113.8-51.2-113.8-113.8v-796.4c0-62.6 51.2-113.8 113.8-113.8h796.4c62.6 0 113.8 51.2 113.8 113.8v796.4c0 62.6-51.2 113.8-113.8 113.8zM398.2 163.6l-284.4 284.4 79.6 79.6 204.8-204.8 432.4 432.4 79.6-79.6-512-512z" />
|
||||||
|
<glyph unicode="" glyph-name="details-arrow" data-tags="details-arrow" d="M512 960.001l-90.24-90.24 357.12-357.76h-778.879v-127.999h778.879l-357.12-357.76 90.24-90.24 511.999 511.999z" />
|
||||||
|
<glyph unicode="" glyph-name="Spinner" data-tags="Spinner" d="M1023.953 448.071c1.137 83.016-18.195 164.895-56.861 238.814-75.625 145.563-216.069 245.637-378.121 270.087v-135.328c85.291-16.489 162.621-63.115 217.207-130.779 89.839-108.035 112.015-257.578 56.861-387.219-38.097-91.545-110.309-163.758-201.855-201.855-93.82-40.37-200.149-40.37-293.969 0-91.545 38.097-163.758 110.309-201.855 201.855-55.155 129.642-32.979 279.184 56.861 387.219 54.586 67.663 131.917 114.289 217.207 130.779v135.328c-163.758-22.745-305.91-123.388-382.102-270.087-69.938-135.328-76.193-294.537-17.058-434.982 51.174-123.388 149.542-221.756 272.93-272.361 127.367-54.017 270.655-54.017 398.023 0 122.819 51.174 220.619 148.975 271.793 271.793 26.724 61.978 40.37 129.073 40.94 196.738v0z" />
|
||||||
|
</font></defs></svg>
|
After Width: | Height: | Size: 6.6 KiB |
BIN
fonts/h5p.eot
|
@ -1,23 +0,0 @@
|
||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<metadata>Generated by IcoMoon</metadata>
|
|
||||||
<defs>
|
|
||||||
<font id="h5p" horiz-adv-x="1024">
|
|
||||||
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
|
||||||
<missing-glyph horiz-adv-x="1024" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="512" d="" />
|
|
||||||
<glyph unicode="" glyph-name="uniE88D" d="M386.662 874.394h71.27v-71.27h-71.27v71.27zM566.067 874.394h71.27v-71.27h-71.27v71.27zM386.662 718.131h71.27v-71.27h-71.27v71.27zM566.067 718.131h71.27v-71.27h-71.27v71.27zM386.662 561.766h71.27v-71.27h-71.27v71.27zM566.067 561.766h71.27v-71.27h-71.27v71.27zM386.662 405.504h71.27v-71.27h-71.27v71.27zM566.067 405.504h71.27v-71.27h-71.27v71.27zM386.662 249.139h71.27v-71.27h-71.27v71.27zM566.067 249.139h71.27v-71.27h-71.27v71.27zM386.662 92.877h71.27v-71.27h-71.27v71.27zM566.067 92.877h71.27v-71.27h-71.27v71.27z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE601" d="M454.299 416.588l-116.917 116.917-84.781-84.707 201.696-201.697 317.097 317.097-84.781 84.706z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE888" horiz-adv-x="1321" d="M660.48 703.59c-140.288 0-253.952-113.664-253.952-253.952 0-122.47 86.63-224.666 202.138-248.627v206.234h-86.835c-11.264 0-14.541 7.168-7.373 16.179l133.12 164.659c7.168 9.011 18.842 9.011 26.010 0l133.12-164.659c7.373-8.602 3.686-16.179-7.373-16.179h-86.835v-206.234c115.507 23.962 202.138 126.157 202.138 248.627-0.205 140.288-113.869 253.952-254.157 253.952z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE889" horiz-adv-x="1321" d="M662.118 701.952c-140.288 0-253.952-113.664-253.952-253.952s113.664-253.952 253.952-253.952 253.952 113.664 253.952 253.952-113.664 253.952-253.952 253.952zM621.773 652.8h83.763v-65.946h-83.763v65.946zM748.749 273.92h-173.67v50.995h49.562v159.13h-49.562v50.995h133.53v-210.125h40.346v-50.995z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE88A" horiz-adv-x="1321" d="M925.491 236.646l-114.688 114.688c27.238 37.888 43.213 84.378 43.213 134.554 0 127.59-103.834 231.424-231.424 231.424s-231.424-103.834-231.424-231.424c0-127.59 103.834-231.424 231.424-231.424 50.176 0 96.666 15.974 134.554 43.213l114.688-114.688c5.325-5.325 13.926-5.325 19.251 0l34.406 34.406c5.325 5.12 5.325 13.926 0 19.251zM622.797 318.566c-92.365 0-167.117 74.752-167.117 167.117s74.752 167.117 167.117 167.117c92.365 0 167.117-74.752 167.117-167.117s-74.752-167.117-167.117-167.117z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE88C" horiz-adv-x="1321" d="M495.845 602.867c4.186 4.186 2.79 8.372-3.255 8.835l-111.615 11.86c-6.046 0.697-10.464-3.721-9.766-9.766l11.86-111.615c0.697-6.046 4.65-7.441 8.835-3.255l103.942 103.942zM421.202 534.968l64.876-64.876c4.186-4.186 11.161-4.186 15.581 0l23.254 23.254c4.186 4.186 4.186 11.161 0 15.581l-64.876 64.876zM932.774 498.924c4.186-4.186 8.372-2.79 8.835 3.255l11.86 111.615c0.697 6.046-3.721 10.464-9.766 9.766l-111.615-11.86c-6.046-0.697-7.441-4.65-3.255-8.835l103.942-103.942zM864.873 573.799l-64.876-64.876c-4.186-4.186-4.186-11.161 0-15.581l23.254-23.254c4.186-4.186 11.161-4.186 15.581 0l64.876 64.876zM828.83 284.064c-4.186-4.186-2.79-8.372 3.255-8.835l111.615-11.86c6.046-0.697 10.464 3.721 9.766 9.766l-11.86 111.615c-0.697 6.046-4.65 7.441-8.835 3.255l-103.942-103.942zM903.707 351.733l-64.876 64.876c-4.186 4.186-11.161 4.186-15.581 0l-23.254-23.254c-4.186-4.186-4.186-11.161 0-15.581l64.876-64.876zM391.903 388.008c-4.186 4.186-8.372 2.79-8.835-3.255l-11.86-111.615c-0.697-6.046 3.721-10.464 9.766-9.766l111.615 11.86c6.046 0.697 7.441 4.65 3.255 8.835l-103.942 103.942zM459.802 313.131l64.876 64.876c4.186 4.186 4.186 11.161 0 15.581l-23.254 23.254c-4.186 4.186-11.161 4.186-15.581 0l-64.876-64.876zM284.938 707.273v-518.547h751.079v518.547h-751.079zM990.906 233.837h-660.857v428.325h660.623v-428.325z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE88E" horiz-adv-x="1321" d="M1062.773 608.822c-21.364 20.404-53.527 31.205-96.254 31.205h-148.821v-76.81h-168.504l-14.162-60.968c11.762 5.521 28.565 9.602 40.087 12.483 11.521 2.88 23.044 1.681 34.325 1.681 38.405 0 69.369-12.001 93.131-35.285 23.763-23.044 35.765-52.567 35.765-87.611 0-24.722-6.241-48.488-18.484-71.529-12.243-22.804-29.764-41.766-52.327-53.768-8.161-4.321-17.043-2.4-26.644-12.001h141.62v144.021h70.811c47.767 0 83.292 9.843 106.335 31.684 23.285 21.844 34.806 51.848 34.806 90.494 0.241 37.205-10.321 66.009-31.684 86.411zM965.8 488.087c-9.12-7.921-25.204-11.284-48.006-11.284h-35.285v86.411h39.846c22.084 0 37.205-5.281 45.125-13.683 7.921-8.401 12.001-18.722 12.001-30.724 0-12.483-4.562-22.804-13.683-30.724zM671.518 446.559c-20.642 0-38.646-12.001-47.287-29.285l-103.694 15.122 46.807 207.629h-100.095v-163.222h-122.417v163.222h-120.017v-384.053h120.017v144.021h122.417v-144.021h148.579c-17.522 9.602-32.643 13.202-45.125 22.563-12.721 9.602-22.804 20.883-30.724 32.885s-13.921 25.685-19.203 43.686l103.694 15.122c8.642-17.283 26.403-29.044 47.047-29.044 29.044 0 52.567 23.522 52.567 52.567s-23.522 52.807-52.567 52.807z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE88F" horiz-adv-x="1321" d="M1030.554 429.363c1.638-3.277 0-3.277-1.638-6.349-20.89-22.323-46.49-35.226-76.8-41.574-12.902-1.638-25.6-3.277-36.864-3.277-12.493 0-18.637 0-27.238 1.638-1.638 0.205-3.277 1.638-4.71 3.277-67.174 60.826-135.987 121.651-203.162 182.477-1.638 1.638-4.71 1.638-6.349 1.638-23.962-6.349-47.923-12.902-73.523-19.251-25.6-4.71-51.2-1.638-75.162 12.902-12.902 7.987-22.323 19.251-28.877 33.587-4.71 9.626 1.638 22.323 12.902 25.6 43.213 12.902 86.426 28.877 128 44.851 12.902 4.71 27.238 7.987 41.574 6.349 4.71 0 9.626-3.277 14.336-4.71 41.574-15.974 83.149-30.31 124.723-46.49 1.638-1.638 4.71-1.638 7.987 0 30.31 7.987 62.464 17.613 92.774 25.6 3.277 1.638 4.71 0 6.349-1.638zM420.864 437.35c19.251 9.626 36.864 6.349 51.2-9.626 12.902-12.902 12.902-28.877 3.277-49.562 19.251 3.277 33.587-3.277 43.213-19.251 11.264-17.613 7.987-33.587-6.349-49.562 4.71 0 11.264 0 15.974-1.638 14.336-3.277 25.6-12.902 30.31-27.238s1.638-27.238-7.987-36.864c-4.71-6.349-11.264-11.264-15.974-17.613s-11.264-11.264-15.974-17.613c-14.336-14.336-38.502-15.974-52.838-1.638-30.31 30.31-55.91 64.102-83.149 97.69-17.613 22.323-33.587 43.213-49.562 65.536-7.987 9.626-12.902 19.251-14.336 31.949 0 7.987 1.638 15.974 7.987 22.323 9.626 9.626 17.613 19.251 27.238 28.877 17.613 17.613 47.923 12.698 62.464-7.987 1.434-1.434 3.072-4.506 4.506-7.782zM571.392 224.563l27.238-28.877c17.613-15.974 46.49-12.902 57.549 7.987l-3.277 3.277c-22.323 22.323-46.49 46.49-68.813 68.813-3.277 3.277-4.71 7.987-3.277 12.902 1.638 4.71 4.71 7.987 9.626 9.626 4.71 1.638 9.626 0 12.902-4.71 14.336-14.336 30.31-30.31 44.851-44.851 14.336-14.336 30.31-28.877 44.851-44.851 7.987-9.626 19.251-11.264 30.31-9.626 14.336 3.277 23.962 11.264 30.31 25.6 1.638 3.277 0 4.71-1.638 6.349-43.213 43.213-86.426 84.787-128 128-3.277 3.277-6.349 7.987-4.71 14.336 1.638 9.626 12.902 14.336 22.323 7.987 1.638-1.638 3.277-1.638 3.277-3.277 43.213-43.213 88.064-88.064 131.277-131.277 3.277-3.277 4.71-3.277 7.987-3.277 17.613 1.638 33.587 15.974 36.864 33.587 0 3.277 0 4.71-1.638 6.349-49.562 49.562-99.123 99.123-148.89 148.89-3.277 3.277-4.71 6.349-4.71 11.264 0 4.71 3.277 11.264 7.987 12.698 4.71 1.638 9.626 1.638 14.336-3.277 3.277-3.277 7.987-7.987 11.264-11.264 35.226-35.226 70.451-70.451 105.677-105.677 11.264-11.264 22.323-20.89 31.949-31.949 1.638-1.638 4.71-3.277 6.349-1.638 23.962 4.71 38.502 30.31 28.877 54.477l38.298-1.638c0-0.205 0-0.41 0.205-0.614 1.434-7.987 1.434-17.203-0.205-24.986-6.349-30.31-23.962-49.562-52.838-59.187-1.638 0-3.277-1.638-3.277-3.277-9.626-31.949-33.587-52.838-67.174-55.91-3.277 0-3.277-1.638-4.71-3.277-17.613-33.587-57.549-49.562-91.136-36.864-4.71 1.638-9.626 4.71-14.336 6.349-6.349-6.349-14.336-12.698-22.323-15.974-27.238-12.698-57.549-6.349-78.438 14.336-9.626 9.626-19.251 19.251-30.31 28.877 7.987 7.987 14.336 15.974 23.962 25.6l1.434-1.024zM404.89 744.55c31.949-9.626 62.464-20.89 94.413-30.31 33.587-11.264 65.536-20.89 99.123-31.949 1.638 0 1.638 0 3.277-1.638-17.613-6.349-33.587-11.264-49.562-17.613-1.638 0-3.277 0-4.71 0-44.851 14.336-91.136 28.877-135.987 43.213-3.277 1.638-4.71 0-7.987-1.638l-80.077-185.549c0-9.626 4.71-17.613 11.264-25.6 3.277-4.71 6.349-7.987 7.987-9.626-7.987-7.987-14.336-15.974-22.323-25.6-15.974 19.251-28.877 38.502-30.31 64.102l89.498 212.787c-0.41-0.205 12.902 13.312 25.395 9.421z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE890" horiz-adv-x="1321" d="M660.48 701.952c-140.288 0-253.952-113.664-253.952-253.952s113.664-253.952 253.952-253.952 253.952 113.664 253.952 253.952-113.664 253.952-253.952 253.952zM796.058 371.2c6.963-6.963 6.963-18.022 0-24.986l-33.997-33.997c-6.963-6.963-18.022-6.963-24.986 0l-76.8 76.8-76.595-76.595c-6.963-6.963-18.022-6.963-24.986 0l-33.997 33.997c-6.963 6.963-6.963 18.022 0 24.986l76.8 76.8-76.8 76.8c-6.963 6.963-6.963 18.022 0 24.986l33.997 33.997c6.963 6.963 18.022 6.963 24.986 0l76.8-76.8 76.8 76.8c6.963 6.963 18.022 6.963 24.986 0l33.997-33.997c6.963-6.963 6.963-18.022 0-24.986l-77.005-77.005 76.8-76.8z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE891" horiz-adv-x="1321" d="M324.468 566.591c-4.186-4.186-2.79-8.372 3.255-8.835l111.615-11.86c6.046-0.697 10.464 3.721 9.766 9.766l-11.86 111.615c-0.697 6.046-4.65 7.441-8.835 3.255l-103.942-103.942zM399.112 634.259l-64.644 64.876c-4.186 4.186-11.161 4.186-15.581 0l-23.254-23.254c-4.186-4.186-4.186-11.161 0-15.581l64.876-64.876zM896.497 670.533c-4.186 4.186-8.372 2.79-8.835-3.255l-11.86-111.615c-0.697-6.046 3.721-10.464 9.766-9.766l111.615 11.86c6.046 0.697 7.441 4.65 3.255 8.835l-103.942 103.942zM964.165 595.657l64.876 64.876c4.186 4.186 4.186 11.161 0 15.581l-23.254 23.254c-4.186 4.186-11.161 4.186-15.581 0l-64.876-64.876zM1000.44 320.34c4.186 4.186 2.79 8.372-3.255 8.835l-111.615 11.86c-6.046 0.697-10.464-3.721-9.766-9.766l11.86-111.615c0.697-6.046 4.65-7.441 8.835-3.255l103.942 103.942zM925.564 252.441l64.876-64.876c4.186-4.186 11.161-4.186 15.581 0l23.254 23.254c4.186 4.186 4.186 11.161 0 15.581l-64.876 64.876zM428.41 216.398c4.186-4.186 8.372-2.79 8.835 3.255l11.86 111.615c0.697 6.046-3.721 10.464-9.766 9.766l-111.615-11.86c-6.046-0.697-7.441-4.65-3.255-8.835l103.942-103.942zM360.51 291.273l-64.876-64.876c-4.186-4.186-4.186-11.161 0-15.581l23.254-23.254c4.186-4.186 11.161-4.186 15.581 0l64.876 64.876zM477.939 572.404v-248.809h365.076v248.809h-365.076zM797.905 368.707h-274.854v158.355h274.621v-158.355z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE892" horiz-adv-x="1321" d="M599.553 337.314c4.419-3.023 8.138-9.998 8.138-15.581v-58.599c0-5.348-3.721-7.441-8.138-4.419l-220.906 148.123c-4.419 3.023-8.138 9.998-8.138 15.348v50.228c0 5.348 3.721 12.323 8.138 15.581l220.906 149.517c4.419 3.023 8.138 1.162 8.138-4.419v-58.599c0-5.348-3.721-12.556-8.138-15.581l-152.773-106.731c-4.419-3.023-4.419-8.138 0-11.161l152.773-103.71zM874.175 440.559c4.419 3.023 4.65 8.138 0 11.161l-152.773 106.731c-4.419 3.023-8.138 10.232-8.138 15.581v58.599c0 5.348 3.721 7.441 8.138 4.419l220.906-149.517c4.419-3.023 8.138-9.998 8.138-15.581v-50.228c0-5.348-3.721-12.323-8.138-15.348l-220.906-148.123c-4.419-3.023-8.138-1.162-8.138 4.419v58.599c0 5.348 3.721 12.323 8.138 15.581l152.773 103.71z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE893" horiz-adv-x="1321" d="M502.17 522.957c-12.902 0-16.998-8.192-8.806-18.227l152.166-188.006c8.192-10.035 21.504-10.035 29.696 0l152.166 188.006c8.192 10.035 4.301 18.227-8.806 18.227h-316.416zM719.462 512.512v139.878c0 12.902-10.65 23.552-23.552 23.552h-70.656c-12.902 0-23.552-10.65-23.552-23.552v-139.878zM798.106 375.91c-8.602 0-20.070-5.53-25.6-12.288l-75.162-92.979c-5.325-6.758-15.36-16.589-22.118-21.914 0 0-4.506-3.686-14.746-3.686s-14.746 3.686-14.746 3.686c-6.758 5.325-16.589 15.36-22.118 21.914l-75.162 92.979c-5.325 6.758-16.998 12.288-25.6 12.288h-130.253c-8.602 0-15.77-6.963-15.77-15.77v-141.722c0-8.602 6.963-15.77 15.77-15.77h535.962c8.602 0 15.77 6.963 15.77 15.77v141.722c0 8.602-6.963 15.77-15.77 15.77h-130.458zM448.102 261.018c-15.565 0-28.262 12.698-28.262 28.262s12.698 28.262 28.262 28.262 28.262-12.698 28.262-28.262c-0.205-15.565-12.698-28.262-28.262-28.262z" />
|
|
||||||
<glyph unicode="" glyph-name="uniE894" horiz-adv-x="1321" d="M742.605 448l107.11 107.11c9.626 9.626 9.626 25.19 0 34.816l-47.309 47.309c-9.626 9.626-25.19 9.626-34.816 0l-107.11-107.11-107.11 107.11c-9.626 9.626-25.19 9.626-34.816 0l-47.309-47.309c-9.626-9.626-9.626-25.19 0-34.816l107.11-107.11-107.11-107.11c-9.626-9.626-9.626-25.19 0-34.816l47.309-47.309c9.626-9.626 25.19-9.626 34.816 0l107.11 107.11 107.11-107.11c9.626-9.626 25.19-9.626 34.816 0l47.309 47.309c9.626 9.626 9.626 25.19 0 34.816l-107.11 107.11z" />
|
|
||||||
</font></defs></svg>
|
|
Before Width: | Height: | Size: 12 KiB |
BIN
fonts/h5p.ttf
|
@ -0,0 +1,586 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File info?
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default file storage class for H5P. Will carry out the requested file
|
||||||
|
* operations using PHP's standard file operation functions.
|
||||||
|
*
|
||||||
|
* Some implementations of H5P that doesn't use the standard file system will
|
||||||
|
* want to create their own implementation of the \H5P\FileStorage interface.
|
||||||
|
*
|
||||||
|
* @package H5P
|
||||||
|
* @copyright 2016 Joubel AS
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
class H5PDefaultStorage implements \H5PFileStorage {
|
||||||
|
private $path, $alteditorpath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The great Constructor!
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* The base location of H5P files
|
||||||
|
* @param string $alteditorpath
|
||||||
|
* Optional. Use a different editor path
|
||||||
|
*/
|
||||||
|
function __construct($path, $alteditorpath = NULL) {
|
||||||
|
// Set H5P storage path
|
||||||
|
$this->path = $path;
|
||||||
|
$this->alteditorpath = $alteditorpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the library folder.
|
||||||
|
*
|
||||||
|
* @param array $library
|
||||||
|
* Library properties
|
||||||
|
*/
|
||||||
|
public function saveLibrary($library) {
|
||||||
|
$dest = $this->path . '/libraries/' . \H5PCore::libraryToString($library, TRUE);
|
||||||
|
|
||||||
|
// Make sure destination dir doesn't exist
|
||||||
|
\H5PCore::deleteFileTree($dest);
|
||||||
|
|
||||||
|
// Move library folder
|
||||||
|
self::copyFileTree($library['uploadDirectory'], $dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the content folder.
|
||||||
|
*
|
||||||
|
* @param string $source
|
||||||
|
* Path on file system to content directory.
|
||||||
|
* @param array $content
|
||||||
|
* Content properties
|
||||||
|
*/
|
||||||
|
public function saveContent($source, $content) {
|
||||||
|
$dest = "{$this->path}/content/{$content['id']}";
|
||||||
|
|
||||||
|
// Remove any old content
|
||||||
|
\H5PCore::deleteFileTree($dest);
|
||||||
|
|
||||||
|
self::copyFileTree($source, $dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove content folder.
|
||||||
|
*
|
||||||
|
* @param array $content
|
||||||
|
* Content properties
|
||||||
|
*/
|
||||||
|
public function deleteContent($content) {
|
||||||
|
\H5PCore::deleteFileTree("{$this->path}/content/{$content['id']}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a stored copy of the content folder.
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* Identifier of content to clone.
|
||||||
|
* @param int $newId
|
||||||
|
* The cloned content's identifier
|
||||||
|
*/
|
||||||
|
public function cloneContent($id, $newId) {
|
||||||
|
$path = $this->path . '/content/';
|
||||||
|
if (file_exists($path . $id)) {
|
||||||
|
self::copyFileTree($path . $id, $path . $newId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get path to a new unique tmp folder.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Path
|
||||||
|
*/
|
||||||
|
public function getTmpPath() {
|
||||||
|
$temp = "{$this->path}/temp";
|
||||||
|
self::dirReady($temp);
|
||||||
|
return "{$temp}/" . uniqid('h5p-');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch content folder and save in target directory.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* Content identifier
|
||||||
|
* @param string $target
|
||||||
|
* Where the content folder will be saved
|
||||||
|
*/
|
||||||
|
public function exportContent($id, $target) {
|
||||||
|
$source = "{$this->path}/content/{$id}";
|
||||||
|
if (file_exists($source)) {
|
||||||
|
// Copy content folder if it exists
|
||||||
|
self::copyFileTree($source, $target);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// No contnet folder, create emty dir for content.json
|
||||||
|
self::dirReady($target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch library folder and save in target directory.
|
||||||
|
*
|
||||||
|
* @param array $library
|
||||||
|
* Library properties
|
||||||
|
* @param string $target
|
||||||
|
* Where the library folder will be saved
|
||||||
|
* @param string $developmentPath
|
||||||
|
* Folder that library resides in
|
||||||
|
*/
|
||||||
|
public function exportLibrary($library, $target, $developmentPath=NULL) {
|
||||||
|
$folder = \H5PCore::libraryToString($library, TRUE);
|
||||||
|
$srcPath = ($developmentPath === NULL ? "/libraries/{$folder}" : $developmentPath);
|
||||||
|
self::copyFileTree("{$this->path}{$srcPath}", "{$target}/{$folder}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save export in file system
|
||||||
|
*
|
||||||
|
* @param string $source
|
||||||
|
* Path on file system to temporary export file.
|
||||||
|
* @param string $filename
|
||||||
|
* Name of export file.
|
||||||
|
* @throws Exception Unable to save the file
|
||||||
|
*/
|
||||||
|
public function saveExport($source, $filename) {
|
||||||
|
$this->deleteExport($filename);
|
||||||
|
|
||||||
|
if (!self::dirReady("{$this->path}/exports")) {
|
||||||
|
throw new Exception("Unable to create directory for H5P export file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!copy($source, "{$this->path}/exports/{$filename}")) {
|
||||||
|
throw new Exception("Unable to save H5P export file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes given export file
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
*/
|
||||||
|
public function deleteExport($filename) {
|
||||||
|
$target = "{$this->path}/exports/{$filename}";
|
||||||
|
if (file_exists($target)) {
|
||||||
|
unlink($target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given export file exists
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function hasExport($filename) {
|
||||||
|
$target = "{$this->path}/exports/{$filename}";
|
||||||
|
return file_exists($target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will concatenate all JavaScrips and Stylesheets into two files in order
|
||||||
|
* to improve page performance.
|
||||||
|
*
|
||||||
|
* @param array $files
|
||||||
|
* A set of all the assets required for content to display
|
||||||
|
* @param string $key
|
||||||
|
* Hashed key for cached asset
|
||||||
|
*/
|
||||||
|
public function cacheAssets(&$files, $key) {
|
||||||
|
foreach ($files as $type => $assets) {
|
||||||
|
if (empty($assets)) {
|
||||||
|
continue; // Skip no assets
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = '';
|
||||||
|
foreach ($assets as $asset) {
|
||||||
|
// Get content from asset file
|
||||||
|
$assetContent = file_get_contents($this->path . $asset->path);
|
||||||
|
$cssRelPath = preg_replace('/[^\/]+$/', '', $asset->path);
|
||||||
|
|
||||||
|
// Get file content and concatenate
|
||||||
|
if ($type === 'scripts') {
|
||||||
|
$content .= $assetContent . ";\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Rewrite relative URLs used inside stylesheets
|
||||||
|
$content .= preg_replace_callback(
|
||||||
|
'/url\([\'"]?([^"\')]+)[\'"]?\)/i',
|
||||||
|
function ($matches) use ($cssRelPath) {
|
||||||
|
if (preg_match("/^(data:|([a-z0-9]+:)?\/)/i", $matches[1]) === 1) {
|
||||||
|
return $matches[0]; // Not relative, skip
|
||||||
|
}
|
||||||
|
return 'url("../' . $cssRelPath . $matches[1] . '")';
|
||||||
|
},
|
||||||
|
$assetContent) . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::dirReady("{$this->path}/cachedassets");
|
||||||
|
$ext = ($type === 'scripts' ? 'js' : 'css');
|
||||||
|
$outputfile = "/cachedassets/{$key}.{$ext}";
|
||||||
|
file_put_contents($this->path . $outputfile, $content);
|
||||||
|
$files[$type] = array((object) array(
|
||||||
|
'path' => $outputfile,
|
||||||
|
'version' => ''
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will check if there are cache assets available for content.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* Hashed key for cached asset
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCachedAssets($key) {
|
||||||
|
$files = array();
|
||||||
|
|
||||||
|
$js = "/cachedassets/{$key}.js";
|
||||||
|
if (file_exists($this->path . $js)) {
|
||||||
|
$files['scripts'] = array((object) array(
|
||||||
|
'path' => $js,
|
||||||
|
'version' => ''
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$css = "/cachedassets/{$key}.css";
|
||||||
|
if (file_exists($this->path . $css)) {
|
||||||
|
$files['styles'] = array((object) array(
|
||||||
|
'path' => $css,
|
||||||
|
'version' => ''
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return empty($files) ? NULL : $files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the aggregated cache files.
|
||||||
|
*
|
||||||
|
* @param array $keys
|
||||||
|
* The hash keys of removed files
|
||||||
|
*/
|
||||||
|
public function deleteCachedAssets($keys) {
|
||||||
|
foreach ($keys as $hash) {
|
||||||
|
foreach (array('js', 'css') as $ext) {
|
||||||
|
$path = "{$this->path}/cachedassets/{$hash}.{$ext}";
|
||||||
|
if (file_exists($path)) {
|
||||||
|
unlink($path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read file content of given file and then return it.
|
||||||
|
*
|
||||||
|
* @param string $file_path
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContent($file_path) {
|
||||||
|
return file_get_contents($file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save files uploaded through the editor.
|
||||||
|
* The files must be marked as temporary until the content form is saved.
|
||||||
|
*
|
||||||
|
* @param \H5peditorFile $file
|
||||||
|
* @param int $contentid
|
||||||
|
*/
|
||||||
|
public function saveFile($file, $contentId) {
|
||||||
|
// Prepare directory
|
||||||
|
if (empty($contentId)) {
|
||||||
|
// Should be in editor tmp folder
|
||||||
|
$path = $this->getEditorPath();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Should be in content folder
|
||||||
|
$path = $this->path . '/content/' . $contentId;
|
||||||
|
}
|
||||||
|
$path .= '/' . $file->getType() . 's';
|
||||||
|
self::dirReady($path);
|
||||||
|
|
||||||
|
// Add filename to path
|
||||||
|
$path .= '/' . $file->getName();
|
||||||
|
|
||||||
|
copy($_FILES['file']['tmp_name'], $path);
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a file from another content or editor tmp dir.
|
||||||
|
* Used when copy pasting content in H5P Editor.
|
||||||
|
*
|
||||||
|
* @param string $file path + name
|
||||||
|
* @param string|int $fromid Content ID or 'editor' string
|
||||||
|
* @param int $toid Target Content ID
|
||||||
|
*/
|
||||||
|
public function cloneContentFile($file, $fromId, $toId) {
|
||||||
|
// Determine source path
|
||||||
|
if ($fromId === 'editor') {
|
||||||
|
$sourcepath = $this->getEditorPath();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$sourcepath = "{$this->path}/content/{$fromId}";
|
||||||
|
}
|
||||||
|
$sourcepath .= '/' . $file;
|
||||||
|
|
||||||
|
// Determine target path
|
||||||
|
$filename = basename($file);
|
||||||
|
$filedir = str_replace($filename, '', $file);
|
||||||
|
$targetpath = "{$this->path}/content/{$toId}/{$filedir}";
|
||||||
|
|
||||||
|
// Make sure it's ready
|
||||||
|
self::dirReady($targetpath);
|
||||||
|
|
||||||
|
$targetpath .= $filename;
|
||||||
|
|
||||||
|
// Check to see if source exist and if target doesn't
|
||||||
|
if (!file_exists($sourcepath) || file_exists($targetpath)) {
|
||||||
|
return; // Nothing to copy from or target already exists
|
||||||
|
}
|
||||||
|
|
||||||
|
copy($sourcepath, $targetpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a content from one directory to another. Defaults to cloning
|
||||||
|
* content from the current temporary upload folder to the editor path.
|
||||||
|
*
|
||||||
|
* @param string $source path to source directory
|
||||||
|
* @param string $contentId Id of contentarray
|
||||||
|
*/
|
||||||
|
public function moveContentDirectory($source, $contentId = NULL) {
|
||||||
|
if ($source === NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove $contentId and never copy temporary files into content folder. JI-366
|
||||||
|
if ($contentId === NULL || $contentId == 0) {
|
||||||
|
$target = $this->getEditorPath();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Use content folder
|
||||||
|
$target = "{$this->path}/content/{$contentId}";
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentSource = $source . '/' . 'content';
|
||||||
|
$contentFiles = array_diff(scandir($contentSource), array('.','..', 'content.json'));
|
||||||
|
foreach ($contentFiles as $file) {
|
||||||
|
if (is_dir("{$contentSource}/{$file}")) {
|
||||||
|
self::copyFileTree("{$contentSource}/{$file}", "{$target}/{$file}");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
copy("{$contentSource}/{$file}", "{$target}/{$file}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Return list of all files so that they can be marked as temporary. JI-366
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if content has the given file.
|
||||||
|
* Used when saving content.
|
||||||
|
*
|
||||||
|
* @param string $file path + name
|
||||||
|
* @param int $contentId
|
||||||
|
* @return string File ID or NULL if not found
|
||||||
|
*/
|
||||||
|
public function getContentFile($file, $contentId) {
|
||||||
|
$path = "{$this->path}/content/{$contentId}/{$file}";
|
||||||
|
return file_exists($path) ? $path : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if content has the given file.
|
||||||
|
* Used when saving content.
|
||||||
|
*
|
||||||
|
* @param string $file path + name
|
||||||
|
* @param int $contentid
|
||||||
|
* @return string|int File ID or NULL if not found
|
||||||
|
*/
|
||||||
|
public function removeContentFile($file, $contentId) {
|
||||||
|
$path = "{$this->path}/content/{$contentId}/{$file}";
|
||||||
|
if (file_exists($path)) {
|
||||||
|
unlink($path);
|
||||||
|
|
||||||
|
// Clean up any empty parent directories to avoid cluttering the file system
|
||||||
|
$parts = explode('/', $path);
|
||||||
|
while (array_pop($parts) !== NULL) {
|
||||||
|
$dir = implode('/', $parts);
|
||||||
|
if (is_dir($dir) && count(scandir($dir)) === 2) { // empty contains '.' and '..'
|
||||||
|
rmdir($dir); // Remove empty parent
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return; // Not empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if server setup has write permission to
|
||||||
|
* the required folders
|
||||||
|
*
|
||||||
|
* @return bool True if site can write to the H5P files folder
|
||||||
|
*/
|
||||||
|
public function hasWriteAccess() {
|
||||||
|
return self::dirReady($this->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the file presave.js exists in the root of the library
|
||||||
|
*
|
||||||
|
* @param string $libraryFolder
|
||||||
|
* @param string $developmentPath
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasPresave($libraryFolder, $developmentPath = null) {
|
||||||
|
$path = is_null($developmentPath) ? 'libraries' . '/' . $libraryFolder : $developmentPath;
|
||||||
|
$filePath = realpath($this->path . '/' . $path . '/' . 'presave.js');
|
||||||
|
return file_exists($filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if upgrades script exist for library.
|
||||||
|
*
|
||||||
|
* @param string $machineName
|
||||||
|
* @param int $majorVersion
|
||||||
|
* @param int $minorVersion
|
||||||
|
* @return string Relative path
|
||||||
|
*/
|
||||||
|
public function getUpgradeScript($machineName, $majorVersion, $minorVersion) {
|
||||||
|
$upgrades = "/libraries/{$machineName}-{$majorVersion}.{$minorVersion}/upgrades.js";
|
||||||
|
if (file_exists($this->path . $upgrades)) {
|
||||||
|
return $upgrades;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the given stream into the given file.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param string $file
|
||||||
|
* @param resource $stream
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveFileFromZip($path, $file, $stream) {
|
||||||
|
$filePath = $path . '/' . $file;
|
||||||
|
|
||||||
|
// Make sure the directory exists first
|
||||||
|
$matches = array();
|
||||||
|
preg_match('/(.+)\/[^\/]*$/', $filePath, $matches);
|
||||||
|
self::dirReady($matches[1]);
|
||||||
|
|
||||||
|
// Store in local storage folder
|
||||||
|
return file_put_contents($filePath, $stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursive function for copying directories.
|
||||||
|
*
|
||||||
|
* @param string $source
|
||||||
|
* From path
|
||||||
|
* @param string $destination
|
||||||
|
* To path
|
||||||
|
* @return boolean
|
||||||
|
* Indicates if the directory existed.
|
||||||
|
*
|
||||||
|
* @throws Exception Unable to copy the file
|
||||||
|
*/
|
||||||
|
private static function copyFileTree($source, $destination) {
|
||||||
|
if (!self::dirReady($destination)) {
|
||||||
|
throw new \Exception('unabletocopy');
|
||||||
|
}
|
||||||
|
|
||||||
|
$ignoredFiles = self::getIgnoredFiles("{$source}/.h5pignore");
|
||||||
|
|
||||||
|
$dir = opendir($source);
|
||||||
|
if ($dir === FALSE) {
|
||||||
|
trigger_error('Unable to open directory ' . $source, E_USER_WARNING);
|
||||||
|
throw new \Exception('unabletocopy');
|
||||||
|
}
|
||||||
|
|
||||||
|
while (false !== ($file = readdir($dir))) {
|
||||||
|
if (($file != '.') && ($file != '..') && $file != '.git' && $file != '.gitignore' && !in_array($file, $ignoredFiles)) {
|
||||||
|
if (is_dir("{$source}/{$file}")) {
|
||||||
|
self::copyFileTree("{$source}/{$file}", "{$destination}/{$file}");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
copy("{$source}/{$file}", "{$destination}/{$file}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve array of file names from file.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @return array Array with files that should be ignored
|
||||||
|
*/
|
||||||
|
private static function getIgnoredFiles($file) {
|
||||||
|
if (file_exists($file) === FALSE) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$contents = file_get_contents($file);
|
||||||
|
if ($contents === FALSE) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return preg_split('/\s+/', $contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursive function that makes sure the specified directory exists and
|
||||||
|
* is writable.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private static function dirReady($path) {
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
$parent = preg_replace("/\/[^\/]+\/?$/", '', $path);
|
||||||
|
if (!self::dirReady($parent)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir($path, 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_dir($path)) {
|
||||||
|
trigger_error('Path is not a directory ' . $path, E_USER_WARNING);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_writable($path)) {
|
||||||
|
trigger_error('Unable to write to ' . $path . ' – check directory permissions –', E_USER_WARNING);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Easy helper function for retrieving the editor path
|
||||||
|
*
|
||||||
|
* @return string Path to editor files
|
||||||
|
*/
|
||||||
|
private function getEditorPath() {
|
||||||
|
return ($this->alteditorpath !== NULL ? $this->alteditorpath : "{$this->path}/editor");
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,13 +14,14 @@ class H5PDevelopment {
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param object $H5PFramework
|
* @param H5PFrameworkInterface|object $H5PFramework
|
||||||
* The frameworks implementation of the H5PFrameworkInterface
|
* The frameworks implementation of the H5PFrameworkInterface
|
||||||
* @param string $filesPath
|
* @param string $filesPath
|
||||||
* Path to where H5P should store its files
|
* Path to where H5P should store its files
|
||||||
|
* @param $language
|
||||||
* @param array $libraries Optional cache input.
|
* @param array $libraries Optional cache input.
|
||||||
*/
|
*/
|
||||||
public function __construct($H5PFramework, $filesPath, $language, $libraries = NULL) {
|
public function __construct(H5PFrameworkInterface $H5PFramework, $filesPath, $language, $libraries = NULL) {
|
||||||
$this->h5pF = $H5PFramework;
|
$this->h5pF = $H5PFramework;
|
||||||
$this->language = $language;
|
$this->language = $language;
|
||||||
$this->filesPath = $filesPath;
|
$this->filesPath = $filesPath;
|
||||||
|
@ -35,7 +36,7 @@ class H5PDevelopment {
|
||||||
/**
|
/**
|
||||||
* Get contents of file.
|
* Get contents of file.
|
||||||
*
|
*
|
||||||
* @param string File path.
|
* @param string $file File path.
|
||||||
* @return mixed String on success or NULL on failure.
|
* @return mixed String on success or NULL on failure.
|
||||||
*/
|
*/
|
||||||
private function getFileContents($file) {
|
private function getFileContents($file) {
|
||||||
|
@ -66,7 +67,7 @@ class H5PDevelopment {
|
||||||
$contents = scandir($path);
|
$contents = scandir($path);
|
||||||
|
|
||||||
for ($i = 0, $s = count($contents); $i < $s; $i++) {
|
for ($i = 0, $s = count($contents); $i < $s; $i++) {
|
||||||
if ($contents[$i]{0} === '.') {
|
if ($contents[$i][0] === '.') {
|
||||||
continue; // Skip hidden stuff.
|
continue; // Skip hidden stuff.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,16 +78,25 @@ class H5PDevelopment {
|
||||||
}
|
}
|
||||||
|
|
||||||
$library = json_decode($libraryJSON, TRUE);
|
$library = json_decode($libraryJSON, TRUE);
|
||||||
if ($library === FALSE) {
|
if ($library === NULL) {
|
||||||
continue; // Invalid JSON.
|
continue; // Invalid JSON.
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Validate props? Not really needed, is it? this is a dev site.
|
// TODO: Validate props? Not really needed, is it? this is a dev site.
|
||||||
|
|
||||||
// Save/update library.
|
|
||||||
$library['libraryId'] = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']);
|
$library['libraryId'] = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']);
|
||||||
|
|
||||||
|
// Convert metadataSettings values to boolean & json_encode it before saving
|
||||||
|
$library['metadataSettings'] = isset($library['metadataSettings']) ?
|
||||||
|
H5PMetadata::boolifyAndEncodeSettings($library['metadataSettings']) :
|
||||||
|
NULL;
|
||||||
|
|
||||||
|
// Save/update library.
|
||||||
$this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE);
|
$this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE);
|
||||||
|
|
||||||
|
// Need to decode it again, since it is served from here.
|
||||||
|
$library['metadataSettings'] = json_decode($library['metadataSettings']);
|
||||||
|
|
||||||
$library['path'] = 'development/' . $contents[$i];
|
$library['path'] = 'development/' . $contents[$i];
|
||||||
$this->libraries[H5PDevelopment::libraryToString($library['machineName'], $library['majorVersion'], $library['minorVersion'])] = $library;
|
$this->libraries[H5PDevelopment::libraryToString($library['machineName'], $library['majorVersion'], $library['minorVersion'])] = $library;
|
||||||
}
|
}
|
||||||
|
@ -111,7 +121,7 @@ class H5PDevelopment {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array Libraris in development folder.
|
* @return array Libraries in development folder.
|
||||||
*/
|
*/
|
||||||
public function getLibraries() {
|
public function getLibraries() {
|
||||||
return $this->libraries;
|
return $this->libraries;
|
||||||
|
@ -152,6 +162,7 @@ class H5PDevelopment {
|
||||||
* @param string $name of the library.
|
* @param string $name of the library.
|
||||||
* @param int $majorVersion of the library.
|
* @param int $majorVersion of the library.
|
||||||
* @param int $minorVersion of the library.
|
* @param int $minorVersion of the library.
|
||||||
|
* @param $language
|
||||||
* @return string Translation
|
* @return string Translation
|
||||||
*/
|
*/
|
||||||
public function getLanguage($name, $majorVersion, $minorVersion, $language) {
|
public function getLanguage($name, $majorVersion, $minorVersion, $language) {
|
||||||
|
@ -169,7 +180,7 @@ class H5PDevelopment {
|
||||||
*
|
*
|
||||||
* @param string $name Machine readable library name
|
* @param string $name Machine readable library name
|
||||||
* @param integer $majorVersion
|
* @param integer $majorVersion
|
||||||
* @param integer $majorVersion
|
* @param $minorVersion
|
||||||
* @return string Library identifier.
|
* @return string Library identifier.
|
||||||
*/
|
*/
|
||||||
public static function libraryToString($name, $majorVersion, $minorVersion) {
|
public static function libraryToString($name, $majorVersion, $minorVersion) {
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base class for H5P events. Extend to track H5P events in your system.
|
||||||
|
*
|
||||||
|
* @package H5P
|
||||||
|
* @copyright 2016 Joubel AS
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
abstract class H5PEventBase {
|
||||||
|
// Constants
|
||||||
|
const LOG_NONE = 0;
|
||||||
|
const LOG_ALL = 1;
|
||||||
|
const LOG_ACTIONS = 2;
|
||||||
|
|
||||||
|
// Static options
|
||||||
|
public static $log_level = self::LOG_ACTIONS;
|
||||||
|
public static $log_time = 2592000; // 30 Days
|
||||||
|
|
||||||
|
// Protected variables
|
||||||
|
protected $id, $type, $sub_type, $content_id, $content_title, $library_name, $library_version, $time;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds event type, h5p library and timestamp to event before saving it.
|
||||||
|
*
|
||||||
|
* Common event types with sub type:
|
||||||
|
* content, <none> – content view
|
||||||
|
* embed – viewed through embed code
|
||||||
|
* shortcode – viewed through internal shortcode
|
||||||
|
* edit – opened in editor
|
||||||
|
* delete – deleted
|
||||||
|
* create – created through editor
|
||||||
|
* create upload – created through upload
|
||||||
|
* update – updated through editor
|
||||||
|
* update upload – updated through upload
|
||||||
|
* upgrade – upgraded
|
||||||
|
*
|
||||||
|
* results, <none> – view own results
|
||||||
|
* content – view results for content
|
||||||
|
* set – new results inserted or updated
|
||||||
|
*
|
||||||
|
* settings, <none> – settings page loaded
|
||||||
|
*
|
||||||
|
* library, <none> – loaded in editor
|
||||||
|
* create – new library installed
|
||||||
|
* update – old library updated
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* Name of event type
|
||||||
|
* @param string $sub_type
|
||||||
|
* Name of event sub type
|
||||||
|
* @param string $content_id
|
||||||
|
* Identifier for content affected by the event
|
||||||
|
* @param string $content_title
|
||||||
|
* Content title (makes it easier to know which content was deleted etc.)
|
||||||
|
* @param string $library_name
|
||||||
|
* Name of the library affected by the event
|
||||||
|
* @param string $library_version
|
||||||
|
* Library version
|
||||||
|
*/
|
||||||
|
function __construct($type, $sub_type = NULL, $content_id = NULL, $content_title = NULL, $library_name = NULL, $library_version = NULL) {
|
||||||
|
$this->type = $type;
|
||||||
|
$this->sub_type = $sub_type;
|
||||||
|
$this->content_id = $content_id;
|
||||||
|
$this->content_title = $content_title;
|
||||||
|
$this->library_name = $library_name;
|
||||||
|
$this->library_version = $library_version;
|
||||||
|
$this->time = time();
|
||||||
|
|
||||||
|
if (self::validLogLevel($type, $sub_type)) {
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
if (self::validStats($type, $sub_type)) {
|
||||||
|
$this->saveStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the event type should be saved/logged.
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* Name of event type
|
||||||
|
* @param string $sub_type
|
||||||
|
* Name of event sub type
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
private static function validLogLevel($type, $sub_type) {
|
||||||
|
switch (self::$log_level) {
|
||||||
|
default:
|
||||||
|
case self::LOG_NONE:
|
||||||
|
return FALSE;
|
||||||
|
case self::LOG_ALL:
|
||||||
|
return TRUE; // Log everything
|
||||||
|
case self::LOG_ACTIONS:
|
||||||
|
if (self::isAction($type, $sub_type)) {
|
||||||
|
return TRUE; // Log actions
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the event should be included in the statistics counter.
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* Name of event type
|
||||||
|
* @param string $sub_type
|
||||||
|
* Name of event sub type
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
private static function validStats($type, $sub_type) {
|
||||||
|
if ( ($type === 'content' && $sub_type === 'shortcode insert') || // Count number of shortcode inserts
|
||||||
|
($type === 'library' && $sub_type === NULL) || // Count number of times library is loaded in editor
|
||||||
|
($type === 'results' && $sub_type === 'content') ) { // Count number of times results page has been opened
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
elseif (self::isAction($type, $sub_type)) { // Count all actions
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if event type is an action.
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* Name of event type
|
||||||
|
* @param string $sub_type
|
||||||
|
* Name of event sub type
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
private static function isAction($type, $sub_type) {
|
||||||
|
if ( ($type === 'content' && in_array($sub_type, array('create', 'create upload', 'update', 'update upload', 'upgrade', 'delete'))) ||
|
||||||
|
($type === 'library' && in_array($sub_type, array('create', 'update'))) ) {
|
||||||
|
return TRUE; // Log actions
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper which makes it easier for systems to save the data.
|
||||||
|
* Add all relevant properties to a assoc. array.
|
||||||
|
* There are no NULL values. Empty string or 0 is used instead.
|
||||||
|
* Used by both Drupal and WordPress.
|
||||||
|
*
|
||||||
|
* @return array with keyed values
|
||||||
|
*/
|
||||||
|
protected function getDataArray() {
|
||||||
|
return array(
|
||||||
|
'created_at' => $this->time,
|
||||||
|
'type' => $this->type,
|
||||||
|
'sub_type' => empty($this->sub_type) ? '' : $this->sub_type,
|
||||||
|
'content_id' => empty($this->content_id) ? 0 : $this->content_id,
|
||||||
|
'content_title' => empty($this->content_title) ? '' : $this->content_title,
|
||||||
|
'library_name' => empty($this->library_name) ? '' : $this->library_name,
|
||||||
|
'library_version' => empty($this->library_version) ? '' : $this->library_version
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper which makes it easier for systems to save the data.
|
||||||
|
* Used in WordPress.
|
||||||
|
*
|
||||||
|
* @return array with strings
|
||||||
|
*/
|
||||||
|
protected function getFormatArray() {
|
||||||
|
return array(
|
||||||
|
'%d',
|
||||||
|
'%s',
|
||||||
|
'%s',
|
||||||
|
'%d',
|
||||||
|
'%s',
|
||||||
|
'%s',
|
||||||
|
'%s'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the event data in the database.
|
||||||
|
*
|
||||||
|
* Must be overridden by plugin.
|
||||||
|
*/
|
||||||
|
abstract protected function save();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add current event data to statistics counter.
|
||||||
|
*
|
||||||
|
* Must be overridden by plugin.
|
||||||
|
*/
|
||||||
|
abstract protected function saveStats();
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File info?
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface needed to handle storage and export of H5P Content.
|
||||||
|
*/
|
||||||
|
interface H5PFileStorage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the library folder.
|
||||||
|
*
|
||||||
|
* @param array $library
|
||||||
|
* Library properties
|
||||||
|
*/
|
||||||
|
public function saveLibrary($library);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the content folder.
|
||||||
|
*
|
||||||
|
* @param string $source
|
||||||
|
* Path on file system to content directory.
|
||||||
|
* @param array $content
|
||||||
|
* Content properties
|
||||||
|
*/
|
||||||
|
public function saveContent($source, $content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove content folder.
|
||||||
|
*
|
||||||
|
* @param array $content
|
||||||
|
* Content properties
|
||||||
|
*/
|
||||||
|
public function deleteContent($content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a stored copy of the content folder.
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* Identifier of content to clone.
|
||||||
|
* @param int $newId
|
||||||
|
* The cloned content's identifier
|
||||||
|
*/
|
||||||
|
public function cloneContent($id, $newId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get path to a new unique tmp folder.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Path
|
||||||
|
*/
|
||||||
|
public function getTmpPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch content folder and save in target directory.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* Content identifier
|
||||||
|
* @param string $target
|
||||||
|
* Where the content folder will be saved
|
||||||
|
*/
|
||||||
|
public function exportContent($id, $target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch library folder and save in target directory.
|
||||||
|
*
|
||||||
|
* @param array $library
|
||||||
|
* Library properties
|
||||||
|
* @param string $target
|
||||||
|
* Where the library folder will be saved
|
||||||
|
*/
|
||||||
|
public function exportLibrary($library, $target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save export in file system
|
||||||
|
*
|
||||||
|
* @param string $source
|
||||||
|
* Path on file system to temporary export file.
|
||||||
|
* @param string $filename
|
||||||
|
* Name of export file.
|
||||||
|
*/
|
||||||
|
public function saveExport($source, $filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes given export file
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
*/
|
||||||
|
public function deleteExport($filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given export file exists
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function hasExport($filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will concatenate all JavaScrips and Stylesheets into two files in order
|
||||||
|
* to improve page performance.
|
||||||
|
*
|
||||||
|
* @param array $files
|
||||||
|
* A set of all the assets required for content to display
|
||||||
|
* @param string $key
|
||||||
|
* Hashed key for cached asset
|
||||||
|
*/
|
||||||
|
public function cacheAssets(&$files, $key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will check if there are cache assets available for content.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* Hashed key for cached asset
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCachedAssets($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the aggregated cache files.
|
||||||
|
*
|
||||||
|
* @param array $keys
|
||||||
|
* The hash keys of removed files
|
||||||
|
*/
|
||||||
|
public function deleteCachedAssets($keys);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read file content of given file and then return it.
|
||||||
|
*
|
||||||
|
* @param string $file_path
|
||||||
|
* @return string contents
|
||||||
|
*/
|
||||||
|
public function getContent($file_path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save files uploaded through the editor.
|
||||||
|
* The files must be marked as temporary until the content form is saved.
|
||||||
|
*
|
||||||
|
* @param \H5peditorFile $file
|
||||||
|
* @param int $contentId
|
||||||
|
*/
|
||||||
|
public function saveFile($file, $contentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a file from another content or editor tmp dir.
|
||||||
|
* Used when copy pasting content in H5P.
|
||||||
|
*
|
||||||
|
* @param string $file path + name
|
||||||
|
* @param string|int $fromId Content ID or 'editor' string
|
||||||
|
* @param int $toId Target Content ID
|
||||||
|
*/
|
||||||
|
public function cloneContentFile($file, $fromId, $toId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a content from one directory to another. Defaults to cloning
|
||||||
|
* content from the current temporary upload folder to the editor path.
|
||||||
|
*
|
||||||
|
* @param string $source path to source directory
|
||||||
|
* @param string $contentId Id of content
|
||||||
|
*
|
||||||
|
* @return object Object containing h5p json and content json data
|
||||||
|
*/
|
||||||
|
public function moveContentDirectory($source, $contentId = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if content has the given file.
|
||||||
|
* Used when saving content.
|
||||||
|
*
|
||||||
|
* @param string $file path + name
|
||||||
|
* @param int $contentId
|
||||||
|
* @return string|int File ID or NULL if not found
|
||||||
|
*/
|
||||||
|
public function getContentFile($file, $contentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove content files that are no longer used.
|
||||||
|
* Used when saving content.
|
||||||
|
*
|
||||||
|
* @param string $file path + name
|
||||||
|
* @param int $contentId
|
||||||
|
*/
|
||||||
|
public function removeContentFile($file, $contentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if server setup has write permission to
|
||||||
|
* the required folders
|
||||||
|
*
|
||||||
|
* @return bool True if server has the proper write access
|
||||||
|
*/
|
||||||
|
public function hasWriteAccess();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the library has a presave.js in the root folder
|
||||||
|
*
|
||||||
|
* @param string $libraryName
|
||||||
|
* @param string $developmentPath
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasPresave($libraryName, $developmentPath = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if upgrades script exist for library.
|
||||||
|
*
|
||||||
|
* @param string $machineName
|
||||||
|
* @param int $majorVersion
|
||||||
|
* @param int $minorVersion
|
||||||
|
* @return string Relative path
|
||||||
|
*/
|
||||||
|
public function getUpgradeScript($machineName, $majorVersion, $minorVersion);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the given stream into the given file.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param string $file
|
||||||
|
* @param resource $stream
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveFileFromZip($path, $file, $stream);
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Utility class for handling metadata
|
||||||
|
*/
|
||||||
|
abstract class H5PMetadata {
|
||||||
|
|
||||||
|
private static $fields = array(
|
||||||
|
'title' => array(
|
||||||
|
'type' => 'text',
|
||||||
|
'maxLength' => 255
|
||||||
|
),
|
||||||
|
'a11yTitle' => array(
|
||||||
|
'type' => 'text',
|
||||||
|
'maxLength' => 255,
|
||||||
|
),
|
||||||
|
'authors' => array(
|
||||||
|
'type' => 'json'
|
||||||
|
),
|
||||||
|
'changes' => array(
|
||||||
|
'type' => 'json'
|
||||||
|
),
|
||||||
|
'source' => array(
|
||||||
|
'type' => 'text',
|
||||||
|
'maxLength' => 255
|
||||||
|
),
|
||||||
|
'license' => array(
|
||||||
|
'type' => 'text',
|
||||||
|
'maxLength' => 32
|
||||||
|
),
|
||||||
|
'licenseVersion' => array(
|
||||||
|
'type' => 'text',
|
||||||
|
'maxLength' => 10
|
||||||
|
),
|
||||||
|
'licenseExtras' => array(
|
||||||
|
'type' => 'text',
|
||||||
|
'maxLength' => 5000
|
||||||
|
),
|
||||||
|
'authorComments' => array(
|
||||||
|
'type' => 'text',
|
||||||
|
'maxLength' => 5000
|
||||||
|
),
|
||||||
|
'yearFrom' => array(
|
||||||
|
'type' => 'int'
|
||||||
|
),
|
||||||
|
'yearTo' => array(
|
||||||
|
'type' => 'int'
|
||||||
|
),
|
||||||
|
'defaultLanguage' => array(
|
||||||
|
'type' => 'text',
|
||||||
|
'maxLength' => 32,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON encode metadata
|
||||||
|
*
|
||||||
|
* @param object $content
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function toJSON($content) {
|
||||||
|
// Note: deliberatly creating JSON string "manually" to improve performance
|
||||||
|
return
|
||||||
|
'{"title":' . (isset($content->title) ? json_encode($content->title) : 'null') .
|
||||||
|
',"a11yTitle":' . (isset($content->a11y_title) ? $content->a11y_title : 'null') .
|
||||||
|
',"authors":' . (isset($content->authors) ? $content->authors : 'null') .
|
||||||
|
',"source":' . (isset($content->source) ? '"' . $content->source . '"' : 'null') .
|
||||||
|
',"license":' . (isset($content->license) ? '"' . $content->license . '"' : 'null') .
|
||||||
|
',"licenseVersion":' . (isset($content->license_version) ? '"' . $content->license_version . '"' : 'null') .
|
||||||
|
',"licenseExtras":' . (isset($content->license_extras) ? json_encode($content->license_extras) : 'null') .
|
||||||
|
',"yearFrom":' . (isset($content->year_from) ? $content->year_from : 'null') .
|
||||||
|
',"yearTo":' . (isset($content->year_to) ? $content->year_to : 'null') .
|
||||||
|
',"changes":' . (isset($content->changes) ? $content->changes : 'null') .
|
||||||
|
',"defaultLanguage":' . (isset($content->default_language) ? '"' . $content->default_language . '"' : 'null') .
|
||||||
|
',"authorComments":' . (isset($content->author_comments) ? json_encode($content->author_comments) : 'null') . '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the metadata into an associative array keyed by the property names
|
||||||
|
* @param mixed $metadata Array or object containing metadata
|
||||||
|
* @param bool $include_title
|
||||||
|
* @param bool $include_missing For metadata fields not being set, skip 'em.
|
||||||
|
* Relevant for content upgrade
|
||||||
|
* @param array $types
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function toDBArray($metadata, $include_title = true, $include_missing = true, &$types = array()) {
|
||||||
|
$fields = array();
|
||||||
|
|
||||||
|
if (!is_array($metadata)) {
|
||||||
|
$metadata = (array) $metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (self::$fields as $key => $config) {
|
||||||
|
|
||||||
|
// Ignore title?
|
||||||
|
if ($key === 'title' && !$include_title) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$exists = array_key_exists($key, $metadata);
|
||||||
|
|
||||||
|
// Don't include missing fields
|
||||||
|
if (!$include_missing && !$exists) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $exists ? $metadata[$key] : null;
|
||||||
|
|
||||||
|
// lowerCamelCase to snake_case
|
||||||
|
$db_field_name = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $key));
|
||||||
|
|
||||||
|
switch ($config['type']) {
|
||||||
|
case 'text':
|
||||||
|
if ($value !== null && strlen($value) > $config['maxLength']) {
|
||||||
|
$value = mb_substr($value, 0, $config['maxLength']);
|
||||||
|
}
|
||||||
|
$types[] = '%s';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'int':
|
||||||
|
$value = ($value !== null) ? intval($value) : null;
|
||||||
|
$types[] = '%d';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'json':
|
||||||
|
$value = ($value !== null) ? json_encode($value) : null;
|
||||||
|
$types[] = '%s';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields[$db_field_name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The metadataSettings field in libraryJson uses 1 for true and 0 for false.
|
||||||
|
* Here we are converting these to booleans, and also doing JSON encoding.
|
||||||
|
* This is invoked before the library data is beeing inserted/updated to DB.
|
||||||
|
*
|
||||||
|
* @param array $metadataSettings
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function boolifyAndEncodeSettings($metadataSettings) {
|
||||||
|
// Convert metadataSettings values to boolean
|
||||||
|
if (isset($metadataSettings['disable'])) {
|
||||||
|
$metadataSettings['disable'] = $metadataSettings['disable'] === 1;
|
||||||
|
}
|
||||||
|
if (isset($metadataSettings['disableExtraTitleField'])) {
|
||||||
|
$metadataSettings['disableExtraTitleField'] = $metadataSettings['disableExtraTitleField'] === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_encode($metadataSettings);
|
||||||
|
}
|
||||||
|
}
|
3351
h5p.classes.php
Before Width: | Height: | Size: 16 KiB |
|
@ -1,26 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
width="36px" height="36px" viewBox="0 0 36 36" enable-background="new 0 0 36 36" xml:space="preserve">
|
|
||||||
<g>
|
|
||||||
<path fill="#FFFFFF" d="M0.126,13.306h3.07l0.365,3.476h3.354L6.55,13.306h3.083l1.044,9.934H7.594l-0.422-4.018H3.818L4.24,23.24
|
|
||||||
H1.17L0.126,13.306z"/>
|
|
||||||
<path fill="#FFFFFF" d="M27.738,13.306h5.103c1.111,0,1.916,0.264,2.414,0.793c0.498,0.529,0.696,1.281,0.593,2.257
|
|
||||||
c-0.105,1.003-0.489,1.787-1.152,2.351c-0.662,0.565-1.613,0.847-2.85,0.847h-1.681l-0.387,3.686h-3.083L27.738,13.306z
|
|
||||||
M30.376,17.541h0.752c0.592,0,1.018-0.103,1.279-0.308c0.261-0.205,0.408-0.469,0.442-0.789c0.033-0.312-0.043-0.576-0.228-0.793
|
|
||||||
c-0.185-0.217-0.564-0.325-1.138-0.325h-0.874L30.376,17.541z"/>
|
|
||||||
<g>
|
|
||||||
<polygon fill="#E24E26" points="12.431,25.515 11.035,9.851 26.38,9.851 24.982,25.512 18.698,27.254 "/>
|
|
||||||
<polygon fill="#F06529" points="18.707,25.923 23.785,24.515 24.98,11.132 18.707,11.132 "/>
|
|
||||||
<polygon fill="#EAEAEA" points="18.707,16.941 16.165,16.941 15.99,14.974 18.707,14.974 18.707,13.053 18.701,13.053
|
|
||||||
13.89,13.053 13.936,13.568 14.408,18.862 18.707,18.862 "/>
|
|
||||||
<polygon fill="#EAEAEA" points="18.707,21.93 18.699,21.933 16.56,21.355 16.423,19.823 15.383,19.823 14.494,19.823
|
|
||||||
14.763,22.839 18.699,23.932 18.707,23.929 "/>
|
|
||||||
<polygon fill="#FFFFFF" points="18.701,16.941 18.701,18.862 21.066,18.862 20.843,21.354 18.701,21.932 18.701,23.931
|
|
||||||
22.639,22.839 22.668,22.514 23.119,17.457 23.166,16.941 22.649,16.941 "/>
|
|
||||||
<polygon fill="#FFFFFF" points="18.701,13.053 18.701,14.246 18.701,14.969 18.701,14.974 23.335,14.974 23.335,14.974
|
|
||||||
23.341,14.974 23.38,14.542 23.467,13.568 23.513,13.053 "/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB |
|
@ -1,19 +0,0 @@
|
||||||
(function ($) {
|
|
||||||
$(document).ready(function () {
|
|
||||||
var $inputs = $('.h5p-action-bar-settings input');
|
|
||||||
var $frame = $inputs.filter('input[name="frame"], input[name="h5p_frame"]');
|
|
||||||
var $others = $inputs.filter(':not(input[name="frame"], input[name="h5p_frame"])');
|
|
||||||
|
|
||||||
var toggle = function () {
|
|
||||||
if ($frame.is(':checked')) {
|
|
||||||
$others.attr('disabled', false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$others.attr('disabled', true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$frame.change(toggle);
|
|
||||||
toggle();
|
|
||||||
});
|
|
||||||
})(jQuery);
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/**
|
||||||
|
* @class
|
||||||
|
* @augments H5P.EventDispatcher
|
||||||
|
* @param {Object} displayOptions
|
||||||
|
* @param {boolean} displayOptions.export Triggers the display of the 'Download' button
|
||||||
|
* @param {boolean} displayOptions.copyright Triggers the display of the 'Copyright' button
|
||||||
|
* @param {boolean} displayOptions.embed Triggers the display of the 'Embed' button
|
||||||
|
* @param {boolean} displayOptions.icon Triggers the display of the 'H5P icon' link
|
||||||
|
*/
|
||||||
|
H5P.ActionBar = (function ($, EventDispatcher) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function ActionBar(displayOptions) {
|
||||||
|
EventDispatcher.call(this);
|
||||||
|
|
||||||
|
/** @alias H5P.ActionBar# */
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var hasActions = false;
|
||||||
|
|
||||||
|
// Create action bar
|
||||||
|
var $actions = H5P.jQuery('<ul class="h5p-actions"></ul>');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for creating action bar buttons.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} type
|
||||||
|
* @param {string} customClass Instead of type class
|
||||||
|
*/
|
||||||
|
var addActionButton = function (type, customClass) {
|
||||||
|
/**
|
||||||
|
* Handles selection of action
|
||||||
|
*/
|
||||||
|
var handler = function () {
|
||||||
|
self.trigger(type);
|
||||||
|
};
|
||||||
|
H5P.jQuery('<li/>', {
|
||||||
|
'class': 'h5p-button h5p-noselect h5p-' + (customClass ? customClass : type),
|
||||||
|
role: 'button',
|
||||||
|
tabindex: 0,
|
||||||
|
title: H5P.t(type + 'Description'),
|
||||||
|
html: H5P.t(type),
|
||||||
|
on: {
|
||||||
|
click: handler,
|
||||||
|
keypress: function (e) {
|
||||||
|
if (e.which === 32) {
|
||||||
|
handler();
|
||||||
|
e.preventDefault(); // (since return false will block other inputs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
appendTo: $actions
|
||||||
|
});
|
||||||
|
|
||||||
|
hasActions = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register action bar buttons
|
||||||
|
if (displayOptions.export || displayOptions.copy) {
|
||||||
|
// Add export button
|
||||||
|
addActionButton('reuse', 'export');
|
||||||
|
}
|
||||||
|
if (displayOptions.copyright) {
|
||||||
|
addActionButton('copyrights');
|
||||||
|
}
|
||||||
|
if (displayOptions.embed) {
|
||||||
|
addActionButton('embed');
|
||||||
|
}
|
||||||
|
if (displayOptions.icon) {
|
||||||
|
// Add about H5P button icon
|
||||||
|
H5P.jQuery('<li><a class="h5p-link" href="http://h5p.org" target="_blank" title="' + H5P.t('h5pDescription') + '"></a></li>').appendTo($actions);
|
||||||
|
hasActions = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a reference to the dom element
|
||||||
|
*
|
||||||
|
* @return {H5P.jQuery}
|
||||||
|
*/
|
||||||
|
self.getDOMElement = function () {
|
||||||
|
return $actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the actionbar contain actions?
|
||||||
|
*
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
self.hasActions = function () {
|
||||||
|
return hasActions;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionBar.prototype = Object.create(EventDispatcher.prototype);
|
||||||
|
ActionBar.prototype.constructor = ActionBar;
|
||||||
|
|
||||||
|
return ActionBar;
|
||||||
|
|
||||||
|
})(H5P.jQuery, H5P.EventDispatcher);
|
|
@ -0,0 +1,410 @@
|
||||||
|
/*global H5P*/
|
||||||
|
H5P.ConfirmationDialog = (function (EventDispatcher) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a confirmation dialog
|
||||||
|
*
|
||||||
|
* @param [options] Options for confirmation dialog
|
||||||
|
* @param [options.instance] Instance that uses confirmation dialog
|
||||||
|
* @param [options.headerText] Header text
|
||||||
|
* @param [options.dialogText] Dialog text
|
||||||
|
* @param [options.cancelText] Cancel dialog button text
|
||||||
|
* @param [options.confirmText] Confirm dialog button text
|
||||||
|
* @param [options.hideCancel] Hide cancel button
|
||||||
|
* @param [options.hideExit] Hide exit button
|
||||||
|
* @param [options.skipRestoreFocus] Skip restoring focus when hiding the dialog
|
||||||
|
* @param [options.classes] Extra classes for popup
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function ConfirmationDialog(options) {
|
||||||
|
EventDispatcher.call(this);
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Make sure confirmation dialogs have unique id
|
||||||
|
H5P.ConfirmationDialog.uniqueId += 1;
|
||||||
|
var uniqueId = H5P.ConfirmationDialog.uniqueId;
|
||||||
|
|
||||||
|
// Default options
|
||||||
|
options = options || {};
|
||||||
|
options.headerText = options.headerText || H5P.t('confirmDialogHeader');
|
||||||
|
options.dialogText = options.dialogText || H5P.t('confirmDialogBody');
|
||||||
|
options.cancelText = options.cancelText || H5P.t('cancelLabel');
|
||||||
|
options.confirmText = options.confirmText || H5P.t('confirmLabel');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle confirming event
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
function dialogConfirmed(e) {
|
||||||
|
self.hide();
|
||||||
|
self.trigger('confirmed');
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle dialog canceled
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
function dialogCanceled(e) {
|
||||||
|
self.hide();
|
||||||
|
self.trigger('canceled');
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flow focus to element
|
||||||
|
* @param {HTMLElement} element Next element to be focused
|
||||||
|
* @param {Event} e Original tab event
|
||||||
|
*/
|
||||||
|
function flowTo(element, e) {
|
||||||
|
element.focus();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset of exit button
|
||||||
|
var exitButtonOffset = 2 * 16;
|
||||||
|
var shadowOffset = 8;
|
||||||
|
|
||||||
|
// Determine if we are too large for our container and must resize
|
||||||
|
var resizeIFrame = false;
|
||||||
|
|
||||||
|
// Create background
|
||||||
|
var popupBackground = document.createElement('div');
|
||||||
|
popupBackground.classList
|
||||||
|
.add('h5p-confirmation-dialog-background', 'hidden', 'hiding');
|
||||||
|
|
||||||
|
// Create outer popup
|
||||||
|
var popup = document.createElement('div');
|
||||||
|
popup.classList.add('h5p-confirmation-dialog-popup', 'hidden');
|
||||||
|
if (options.classes) {
|
||||||
|
options.classes.forEach(function (popupClass) {
|
||||||
|
popup.classList.add(popupClass);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
popup.setAttribute('role', 'dialog');
|
||||||
|
popup.setAttribute('aria-labelledby', 'h5p-confirmation-dialog-dialog-text-' + uniqueId);
|
||||||
|
popupBackground.appendChild(popup);
|
||||||
|
popup.addEventListener('keydown', function (e) {
|
||||||
|
if (e.which === 27) {// Esc key
|
||||||
|
// Exit dialog
|
||||||
|
dialogCanceled(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Popup header
|
||||||
|
var header = document.createElement('div');
|
||||||
|
header.classList.add('h5p-confirmation-dialog-header');
|
||||||
|
popup.appendChild(header);
|
||||||
|
|
||||||
|
// Header text
|
||||||
|
var headerText = document.createElement('div');
|
||||||
|
headerText.classList.add('h5p-confirmation-dialog-header-text');
|
||||||
|
headerText.innerHTML = options.headerText;
|
||||||
|
header.appendChild(headerText);
|
||||||
|
|
||||||
|
// Popup body
|
||||||
|
var body = document.createElement('div');
|
||||||
|
body.classList.add('h5p-confirmation-dialog-body');
|
||||||
|
popup.appendChild(body);
|
||||||
|
|
||||||
|
// Popup text
|
||||||
|
var text = document.createElement('div');
|
||||||
|
text.classList.add('h5p-confirmation-dialog-text');
|
||||||
|
text.innerHTML = options.dialogText;
|
||||||
|
text.id = 'h5p-confirmation-dialog-dialog-text-' + uniqueId;
|
||||||
|
body.appendChild(text);
|
||||||
|
|
||||||
|
// Popup buttons
|
||||||
|
var buttons = document.createElement('div');
|
||||||
|
buttons.classList.add('h5p-confirmation-dialog-buttons');
|
||||||
|
body.appendChild(buttons);
|
||||||
|
|
||||||
|
// Cancel button
|
||||||
|
var cancelButton = document.createElement('button');
|
||||||
|
cancelButton.classList.add('h5p-core-cancel-button');
|
||||||
|
cancelButton.textContent = options.cancelText;
|
||||||
|
|
||||||
|
// Confirm button
|
||||||
|
var confirmButton = document.createElement('button');
|
||||||
|
confirmButton.classList.add('h5p-core-button');
|
||||||
|
confirmButton.classList.add('h5p-confirmation-dialog-confirm-button');
|
||||||
|
confirmButton.textContent = options.confirmText;
|
||||||
|
|
||||||
|
// Exit button
|
||||||
|
var exitButton = document.createElement('button');
|
||||||
|
exitButton.classList.add('h5p-confirmation-dialog-exit');
|
||||||
|
exitButton.setAttribute('aria-hidden', 'true');
|
||||||
|
exitButton.tabIndex = -1;
|
||||||
|
exitButton.title = options.cancelText;
|
||||||
|
|
||||||
|
// Cancel handler
|
||||||
|
cancelButton.addEventListener('click', dialogCanceled);
|
||||||
|
cancelButton.addEventListener('keydown', function (e) {
|
||||||
|
if (e.which === 32) { // Space
|
||||||
|
dialogCanceled(e);
|
||||||
|
}
|
||||||
|
else if (e.which === 9 && e.shiftKey) { // Shift-tab
|
||||||
|
flowTo(confirmButton, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!options.hideCancel) {
|
||||||
|
buttons.appendChild(cancelButton);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Center buttons
|
||||||
|
buttons.classList.add('center');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm handler
|
||||||
|
confirmButton.addEventListener('click', dialogConfirmed);
|
||||||
|
confirmButton.addEventListener('keydown', function (e) {
|
||||||
|
if (e.which === 32) { // Space
|
||||||
|
dialogConfirmed(e);
|
||||||
|
}
|
||||||
|
else if (e.which === 9 && !e.shiftKey) { // Tab
|
||||||
|
const nextButton = !options.hideCancel ? cancelButton : confirmButton;
|
||||||
|
flowTo(nextButton, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
buttons.appendChild(confirmButton);
|
||||||
|
|
||||||
|
// Exit handler
|
||||||
|
exitButton.addEventListener('click', dialogCanceled);
|
||||||
|
exitButton.addEventListener('keydown', function (e) {
|
||||||
|
if (e.which === 32) { // Space
|
||||||
|
dialogCanceled(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!options.hideExit) {
|
||||||
|
popup.appendChild(exitButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper element
|
||||||
|
var wrapperElement;
|
||||||
|
|
||||||
|
// Focus capturing
|
||||||
|
var focusPredator;
|
||||||
|
|
||||||
|
// Maintains hidden state of elements
|
||||||
|
var wrapperSiblingsHidden = [];
|
||||||
|
var popupSiblingsHidden = [];
|
||||||
|
|
||||||
|
// Element with focus before dialog
|
||||||
|
var previouslyFocused;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set parent of confirmation dialog
|
||||||
|
* @param {HTMLElement} wrapper
|
||||||
|
* @returns {H5P.ConfirmationDialog}
|
||||||
|
*/
|
||||||
|
this.appendTo = function (wrapper) {
|
||||||
|
wrapperElement = wrapper;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capture the focus element, send it to confirmation button
|
||||||
|
* @param {Event} e Original focus event
|
||||||
|
*/
|
||||||
|
var captureFocus = function (e) {
|
||||||
|
if (!popupBackground.contains(e.target)) {
|
||||||
|
e.preventDefault();
|
||||||
|
confirmButton.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide siblings of element from assistive technology
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element
|
||||||
|
* @returns {Array} The previous hidden state of all siblings
|
||||||
|
*/
|
||||||
|
var hideSiblings = function (element) {
|
||||||
|
var hiddenSiblings = [];
|
||||||
|
var siblings = element.parentNode.children;
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < siblings.length; i += 1) {
|
||||||
|
// Preserve hidden state
|
||||||
|
hiddenSiblings[i] = siblings[i].getAttribute('aria-hidden') ?
|
||||||
|
true : false;
|
||||||
|
|
||||||
|
if (siblings[i] !== element) {
|
||||||
|
siblings[i].setAttribute('aria-hidden', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hiddenSiblings;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores assistive technology state of element's siblings
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element
|
||||||
|
* @param {Array} hiddenSiblings Hidden state of all siblings
|
||||||
|
*/
|
||||||
|
var restoreSiblings = function (element, hiddenSiblings) {
|
||||||
|
var siblings = element.parentNode.children;
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < siblings.length; i += 1) {
|
||||||
|
if (siblings[i] !== element && !hiddenSiblings[i]) {
|
||||||
|
siblings[i].removeAttribute('aria-hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start capturing focus of parent and send it to dialog
|
||||||
|
*/
|
||||||
|
var startCapturingFocus = function () {
|
||||||
|
focusPredator = wrapperElement.parentNode || wrapperElement;
|
||||||
|
focusPredator.addEventListener('focus', captureFocus, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up event listener for capturing focus
|
||||||
|
*/
|
||||||
|
var stopCapturingFocus = function () {
|
||||||
|
focusPredator.removeAttribute('aria-hidden');
|
||||||
|
focusPredator.removeEventListener('focus', captureFocus, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide siblings in underlay from assistive technologies
|
||||||
|
*/
|
||||||
|
var disableUnderlay = function () {
|
||||||
|
wrapperSiblingsHidden = hideSiblings(wrapperElement);
|
||||||
|
popupSiblingsHidden = hideSiblings(popupBackground);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore state of underlay for assistive technologies
|
||||||
|
*/
|
||||||
|
var restoreUnderlay = function () {
|
||||||
|
restoreSiblings(wrapperElement, wrapperSiblingsHidden);
|
||||||
|
restoreSiblings(popupBackground, popupSiblingsHidden);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fit popup to container. Makes sure it doesn't overflow.
|
||||||
|
* @params {number} [offsetTop] Offset of popup
|
||||||
|
*/
|
||||||
|
var fitToContainer = function (offsetTop) {
|
||||||
|
var popupOffsetTop = parseInt(popup.style.top, 10);
|
||||||
|
if (offsetTop !== undefined) {
|
||||||
|
popupOffsetTop = offsetTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!popupOffsetTop) {
|
||||||
|
popupOffsetTop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overflows height
|
||||||
|
if (popupOffsetTop + popup.offsetHeight > wrapperElement.offsetHeight) {
|
||||||
|
popupOffsetTop = wrapperElement.offsetHeight - popup.offsetHeight - shadowOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (popupOffsetTop - exitButtonOffset <= 0) {
|
||||||
|
popupOffsetTop = exitButtonOffset + shadowOffset;
|
||||||
|
|
||||||
|
// We are too big and must resize
|
||||||
|
resizeIFrame = true;
|
||||||
|
}
|
||||||
|
popup.style.top = popupOffsetTop + 'px';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show confirmation dialog
|
||||||
|
* @params {number} offsetTop Offset top
|
||||||
|
* @returns {H5P.ConfirmationDialog}
|
||||||
|
*/
|
||||||
|
this.show = function (offsetTop) {
|
||||||
|
// Capture focused item
|
||||||
|
previouslyFocused = document.activeElement;
|
||||||
|
wrapperElement.appendChild(popupBackground);
|
||||||
|
startCapturingFocus();
|
||||||
|
disableUnderlay();
|
||||||
|
popupBackground.classList.remove('hidden');
|
||||||
|
fitToContainer(offsetTop);
|
||||||
|
setTimeout(function () {
|
||||||
|
popup.classList.remove('hidden');
|
||||||
|
popupBackground.classList.remove('hiding');
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
// Focus confirm button
|
||||||
|
confirmButton.focus();
|
||||||
|
|
||||||
|
// Resize iFrame if necessary
|
||||||
|
if (resizeIFrame && options.instance) {
|
||||||
|
var minHeight = parseInt(popup.offsetHeight, 10) +
|
||||||
|
exitButtonOffset + (2 * shadowOffset);
|
||||||
|
self.setViewPortMinimumHeight(minHeight);
|
||||||
|
options.instance.trigger('resize');
|
||||||
|
resizeIFrame = false;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide confirmation dialog
|
||||||
|
* @returns {H5P.ConfirmationDialog}
|
||||||
|
*/
|
||||||
|
this.hide = function () {
|
||||||
|
popupBackground.classList.add('hiding');
|
||||||
|
popup.classList.add('hidden');
|
||||||
|
|
||||||
|
// Restore focus
|
||||||
|
stopCapturingFocus();
|
||||||
|
if (!options.skipRestoreFocus) {
|
||||||
|
previouslyFocused.focus();
|
||||||
|
}
|
||||||
|
restoreUnderlay();
|
||||||
|
setTimeout(function () {
|
||||||
|
popupBackground.classList.add('hidden');
|
||||||
|
wrapperElement.removeChild(popupBackground);
|
||||||
|
self.setViewPortMinimumHeight(null);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve element
|
||||||
|
*
|
||||||
|
* @return {HTMLElement}
|
||||||
|
*/
|
||||||
|
this.getElement = function () {
|
||||||
|
return popup;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get previously focused element
|
||||||
|
* @return {HTMLElement}
|
||||||
|
*/
|
||||||
|
this.getPreviouslyFocused = function () {
|
||||||
|
return previouslyFocused;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the minimum height of the view port
|
||||||
|
*
|
||||||
|
* @param {number|null} minHeight
|
||||||
|
*/
|
||||||
|
this.setViewPortMinimumHeight = function (minHeight) {
|
||||||
|
var container = document.querySelector('.h5p-container') || document.body;
|
||||||
|
container.style.minHeight = (typeof minHeight === 'number') ? (minHeight + 'px') : minHeight;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmationDialog.prototype = Object.create(EventDispatcher.prototype);
|
||||||
|
ConfirmationDialog.prototype.constructor = ConfirmationDialog;
|
||||||
|
|
||||||
|
return ConfirmationDialog;
|
||||||
|
|
||||||
|
}(H5P.EventDispatcher));
|
||||||
|
|
||||||
|
H5P.ConfirmationDialog.uniqueId = -1;
|
|
@ -3,10 +3,17 @@
|
||||||
*
|
*
|
||||||
* Functions here may be overridable by the libraries. In special cases,
|
* Functions here may be overridable by the libraries. In special cases,
|
||||||
* it is also possible to override H5P.ContentType on a global level.
|
* it is also possible to override H5P.ContentType on a global level.
|
||||||
* */
|
*
|
||||||
H5P.ContentType = function (isRootLibrary, library) {
|
* NOTE that this doesn't actually 'extend' the event dispatcher but instead
|
||||||
|
* it creates a single instance which all content types shares as their base
|
||||||
|
* prototype. (in some cases this may be the root of strange event behavior)
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @augments H5P.EventDispatcher
|
||||||
|
*/
|
||||||
|
H5P.ContentType = function (isRootLibrary) {
|
||||||
|
|
||||||
function ContentType() {};
|
function ContentType() {}
|
||||||
|
|
||||||
// Inherit from EventDispatcher.
|
// Inherit from EventDispatcher.
|
||||||
ContentType.prototype = new H5P.EventDispatcher();
|
ContentType.prototype = new H5P.EventDispatcher();
|
||||||
|
@ -15,7 +22,6 @@ H5P.ContentType = function (isRootLibrary, library) {
|
||||||
* Is library standalone or not? Not beeing standalone, means it is
|
* Is library standalone or not? Not beeing standalone, means it is
|
||||||
* included in another library
|
* included in another library
|
||||||
*
|
*
|
||||||
* @method isStandalone
|
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
ContentType.prototype.isRoot = function () {
|
ContentType.prototype.isRoot = function () {
|
||||||
|
@ -24,7 +30,6 @@ H5P.ContentType = function (isRootLibrary, library) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the file path of a file in the current library
|
* Returns the file path of a file in the current library
|
||||||
* @method getLibraryFilePath
|
|
||||||
* @param {string} filePath The path to the file relative to the library folder
|
* @param {string} filePath The path to the file relative to the library folder
|
||||||
* @return {string} The full path to the file
|
* @return {string} The full path to the file
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -25,19 +25,28 @@ H5P.ContentUpgradeProcess = (function (Version) {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.loadLibrary = loadLibrary;
|
self.loadLibrary = loadLibrary;
|
||||||
self.upgrade(name, oldVersion, newVersion, params, function (err, result) {
|
self.upgrade(name, oldVersion, newVersion, params.params, params.metadata, function (err, upgradedParams, upgradedMetadata) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
err.id = id;
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
done(null, JSON.stringify(params));
|
done(null, JSON.stringify({params: upgradedParams, metadata: upgradedMetadata}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Run content upgrade.
|
||||||
*
|
*
|
||||||
|
* @public
|
||||||
|
* @param {string} name
|
||||||
|
* @param {Version} oldVersion
|
||||||
|
* @param {Version} newVersion
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {Object} metadata
|
||||||
|
* @param {Function} done
|
||||||
*/
|
*/
|
||||||
ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, done) {
|
ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, metadata, done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// Load library details and upgrade routines
|
// Load library details and upgrade routines
|
||||||
|
@ -45,9 +54,15 @@ H5P.ContentUpgradeProcess = (function (Version) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
|
if (library.semantics === null) {
|
||||||
|
return done({
|
||||||
|
type: 'libraryMissing',
|
||||||
|
library: library.name + ' ' + library.version.major + '.' + library.version.minor
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Run upgrade routines on params
|
// Run upgrade routines on params
|
||||||
self.processParams(library, oldVersion, newVersion, params, function (err, params) {
|
self.processParams(library, oldVersion, newVersion, params, metadata, function (err, params, metadata) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
|
@ -61,7 +76,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
|
||||||
next(err);
|
next(err);
|
||||||
});
|
});
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
done(err, params);
|
done(err, params, metadata);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -77,7 +92,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
|
||||||
* @param {Object} params
|
* @param {Object} params
|
||||||
* @param {Function} next
|
* @param {Function} next
|
||||||
*/
|
*/
|
||||||
ContentUpgradeProcess.prototype.processParams = function (library, oldVersion, newVersion, params, next) {
|
ContentUpgradeProcess.prototype.processParams = function (library, oldVersion, newVersion, params, metadata, next) {
|
||||||
if (H5PUpgrades[library.name] === undefined) {
|
if (H5PUpgrades[library.name] === undefined) {
|
||||||
if (library.upgradesScript) {
|
if (library.upgradesScript) {
|
||||||
// Upgrades script should be loaded so the upgrades should be here.
|
// Upgrades script should be loaded so the upgrades should be here.
|
||||||
|
@ -88,7 +103,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// No upgrades script. Move on
|
// No upgrades script. Move on
|
||||||
return next(null, params);
|
return next(null, params, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run upgrade hooks. Start by going through major versions
|
// Run upgrade hooks. Start by going through major versions
|
||||||
|
@ -100,6 +115,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
|
||||||
else {
|
else {
|
||||||
// Go through the minor versions for this major version
|
// Go through the minor versions for this major version
|
||||||
asyncSerial(minors, function (minor, upgrade, nextMinor) {
|
asyncSerial(minors, function (minor, upgrade, nextMinor) {
|
||||||
|
minor =+ minor;
|
||||||
if (minor <= oldVersion.minor || minor > newVersion.minor) {
|
if (minor <= oldVersion.minor || minor > newVersion.minor) {
|
||||||
// Older than or equal to the current version or newer than the selected
|
// Older than or equal to the current version or newer than the selected
|
||||||
nextMinor();
|
nextMinor();
|
||||||
|
@ -109,16 +125,19 @@ H5P.ContentUpgradeProcess = (function (Version) {
|
||||||
var unnecessaryWrapper = (upgrade.contentUpgrade !== undefined ? upgrade.contentUpgrade : upgrade);
|
var unnecessaryWrapper = (upgrade.contentUpgrade !== undefined ? upgrade.contentUpgrade : upgrade);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
unnecessaryWrapper(params, function (err, upgradedParams) {
|
unnecessaryWrapper(params, function (err, upgradedParams, upgradedExtras) {
|
||||||
params = upgradedParams;
|
params = upgradedParams;
|
||||||
|
if (upgradedExtras && upgradedExtras.metadata) { // Optional
|
||||||
|
metadata = upgradedExtras.metadata;
|
||||||
|
}
|
||||||
nextMinor(err);
|
nextMinor(err);
|
||||||
});
|
}, {metadata: metadata});
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
if (console && console.log) {
|
if (console && console.error) {
|
||||||
console.log("Error", err.stack);
|
console.error("Error", err.stack);
|
||||||
console.log("Error", err.name);
|
console.error("Error", err.name);
|
||||||
console.log("Error", err.message);
|
console.error("Error", err.message);
|
||||||
}
|
}
|
||||||
next(err);
|
next(err);
|
||||||
}
|
}
|
||||||
|
@ -126,7 +145,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
|
||||||
}, nextMajor);
|
}, nextMajor);
|
||||||
}
|
}
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
next(err, params);
|
next(err, params, metadata);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,7 +173,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
|
||||||
// Look for available upgrades
|
// Look for available upgrades
|
||||||
var usedLib = params.library.split(' ', 2);
|
var usedLib = params.library.split(' ', 2);
|
||||||
for (var i = 0; i < field.options.length; i++) {
|
for (var i = 0; i < field.options.length; i++) {
|
||||||
var availableLib = field.options[i].split(' ', 2);
|
var availableLib = (typeof field.options[i] === 'string') ? field.options[i].split(' ', 2) : field.options[i].name.split(' ', 2);
|
||||||
if (availableLib[0] === usedLib[0]) {
|
if (availableLib[0] === usedLib[0]) {
|
||||||
if (availableLib[1] === usedLib[1]) {
|
if (availableLib[1] === usedLib[1]) {
|
||||||
return done(); // Same version
|
return done(); // Same version
|
||||||
|
@ -164,24 +183,36 @@ H5P.ContentUpgradeProcess = (function (Version) {
|
||||||
var usedVer = new Version(usedLib[1]);
|
var usedVer = new Version(usedLib[1]);
|
||||||
var availableVer = new Version(availableLib[1]);
|
var availableVer = new Version(availableLib[1]);
|
||||||
if (usedVer.major > availableVer.major || (usedVer.major === availableVer.major && usedVer.minor >= availableVer.minor)) {
|
if (usedVer.major > availableVer.major || (usedVer.major === availableVer.major && usedVer.minor >= availableVer.minor)) {
|
||||||
return done(); // Larger or same version that's available
|
return done({
|
||||||
|
type: 'errorTooHighVersion',
|
||||||
|
used: usedLib[0] + ' ' + usedVer,
|
||||||
|
supported: availableLib[0] + ' ' + availableVer
|
||||||
|
}); // Larger or same version that's available
|
||||||
}
|
}
|
||||||
|
|
||||||
// A newer version is available, upgrade params
|
// A newer version is available, upgrade params
|
||||||
return self.upgrade(availableLib[0], usedVer, availableVer, params.params, function (err, upgraded) {
|
return self.upgrade(availableLib[0], usedVer, availableVer, params.params, params.metadata, function (err, upgradedParams, upgradedMetadata) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
params.library = availableLib[0] + ' ' + availableVer.major + '.' + availableVer.minor;
|
params.library = availableLib[0] + ' ' + availableVer.major + '.' + availableVer.minor;
|
||||||
params.params = upgraded;
|
params.params = upgradedParams;
|
||||||
|
if (upgradedMetadata) {
|
||||||
|
params.metadata = upgradedMetadata;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
done(err, params);
|
done(err, params);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done();
|
|
||||||
|
// Content type was not supporte by the higher version
|
||||||
|
done({
|
||||||
|
type: 'errorNotSupported',
|
||||||
|
used: usedLib[0] + ' ' + usedVer
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'group':
|
case 'group':
|
||||||
if (field.fields.length === 1) {
|
if (field.fields.length === 1 && field.isSubContent !== true) {
|
||||||
// Single field to process, wrapper will be skipped
|
// Single field to process, wrapper will be skipped
|
||||||
self.processField(field.fields[0], params, function (err, upgradedParams) {
|
self.processField(field.fields[0], params, function (err, upgradedParams) {
|
||||||
if (upgradedParams) {
|
if (upgradedParams) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* global importScripts */
|
||||||
var H5P = H5P || {};
|
var H5P = H5P || {};
|
||||||
importScripts('h5p-version.js', 'h5p-content-upgrade-process.js');
|
importScripts('h5p-version.js', 'h5p-content-upgrade-process.js');
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*jshint -W083 */
|
/* global H5PAdminIntegration H5PUtils */
|
||||||
|
|
||||||
(function ($, Version) {
|
(function ($, Version) {
|
||||||
var info, $container, librariesCache = {}, scriptsCache = {};
|
var info, $log, $container, librariesCache = {}, scriptsCache = {};
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
@ -9,7 +9,9 @@
|
||||||
info = H5PAdminIntegration.libraryInfo;
|
info = H5PAdminIntegration.libraryInfo;
|
||||||
|
|
||||||
// Get and reset container
|
// Get and reset container
|
||||||
$container = $('#h5p-admin-container').html('<p>' + info.message + '</p>');
|
const $wrapper = $('#h5p-admin-container').html('');
|
||||||
|
$log = $('<ul class="content-upgrade-log"></ul>').appendTo($wrapper);
|
||||||
|
$container = $('<div><p>' + info.message + '</p></div>').appendTo($wrapper);
|
||||||
|
|
||||||
// Make it possible to select version
|
// Make it possible to select version
|
||||||
var $version = $(getVersionSelect(info.versions)).appendTo($container);
|
var $version = $(getVersionSelect(info.versions)).appendTo($container);
|
||||||
|
@ -120,9 +122,7 @@
|
||||||
},
|
},
|
||||||
error: function (error) {
|
error: function (error) {
|
||||||
self.printError(error.err);
|
self.printError(error.err);
|
||||||
|
self.workDone(error.id, null, this);
|
||||||
// Stop everything
|
|
||||||
self.terminate();
|
|
||||||
},
|
},
|
||||||
loadLibrary: function (details) {
|
loadLibrary: function (details) {
|
||||||
var worker = this;
|
var worker = this;
|
||||||
|
@ -184,7 +184,7 @@
|
||||||
self.token = inData.token;
|
self.token = inData.token;
|
||||||
|
|
||||||
// Start processing
|
// Start processing
|
||||||
self.processBatch(inData.params);
|
self.processBatch(inData.params, inData.skipped);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -202,11 +202,12 @@
|
||||||
*
|
*
|
||||||
* @param {Object} parameters
|
* @param {Object} parameters
|
||||||
*/
|
*/
|
||||||
ContentUpgrade.prototype.processBatch = function (parameters) {
|
ContentUpgrade.prototype.processBatch = function (parameters, skipped) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// Track upgraded params
|
// Track upgraded params
|
||||||
self.upgraded = {};
|
self.upgraded = {};
|
||||||
|
self.skipped = skipped;
|
||||||
|
|
||||||
// Track current batch
|
// Track current batch
|
||||||
self.parameters = parameters;
|
self.parameters = parameters;
|
||||||
|
@ -276,7 +277,7 @@
|
||||||
}, function done(err, result) {
|
}, function done(err, result) {
|
||||||
if (err) {
|
if (err) {
|
||||||
self.printError(err);
|
self.printError(err);
|
||||||
return ;
|
result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.workDone(id, result);
|
self.workDone(id, result);
|
||||||
|
@ -291,7 +292,12 @@
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self.working--;
|
self.working--;
|
||||||
|
if (result === null) {
|
||||||
|
self.skipped.push(id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
self.upgraded[id] = result;
|
self.upgraded[id] = result;
|
||||||
|
}
|
||||||
|
|
||||||
// Update progress message
|
// Update progress message
|
||||||
self.throbber.setProgress(Math.round((info.total - self.left + self.current) / (info.total / 100)) + ' %');
|
self.throbber.setProgress(Math.round((info.total - self.left + self.current) / (info.total / 100)) + ' %');
|
||||||
|
@ -302,6 +308,7 @@
|
||||||
self.nextBatch({
|
self.nextBatch({
|
||||||
libraryId: self.version.libraryId,
|
libraryId: self.version.libraryId,
|
||||||
token: self.token,
|
token: self.token,
|
||||||
|
skipped: JSON.stringify(self.skipped),
|
||||||
params: JSON.stringify(self.upgraded)
|
params: JSON.stringify(self.upgraded)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -410,14 +417,29 @@
|
||||||
ContentUpgrade.prototype.printError = function (error) {
|
ContentUpgrade.prototype.printError = function (error) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (error.type === 'errorParamsBroken') {
|
switch (error.type) {
|
||||||
|
case 'errorParamsBroken':
|
||||||
error = info.errorContent.replace('%id', error.id) + ' ' + info.errorParamsBroken;
|
error = info.errorContent.replace('%id', error.id) + ' ' + info.errorParamsBroken;
|
||||||
}
|
break;
|
||||||
else if (error.type === 'scriptMissing') {
|
|
||||||
|
case 'libraryMissing':
|
||||||
|
error = info.errorLibrary.replace('%lib', error.library);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'scriptMissing':
|
||||||
error = info.errorScript.replace('%lib', error.library);
|
error = info.errorScript.replace('%lib', error.library);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'errorTooHighVersion':
|
||||||
|
error = info.errorContent.replace('%id', error.id) + ' ' + info.errorTooHighVersion.replace('%used', error.used).replace('%supported', error.supported);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'errorNotSupported':
|
||||||
|
error = info.errorContent.replace('%id', error.id) + ' ' + info.errorNotSupported.replace('%used', error.used);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.setStatus('<p>' + info.error + '<br/>' + error + '</p>');
|
$('<li>' + info.error + '<br/>' + error + '</li>').appendTo($log);
|
||||||
};
|
};
|
||||||
|
|
||||||
})(H5P.jQuery, H5P.Version);
|
})(H5P.jQuery, H5P.Version);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* global H5PUtils */
|
||||||
var H5PDataView = (function ($) {
|
var H5PDataView = (function ($) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,14 +51,23 @@ var H5PDataView = (function ($) {
|
||||||
self.limit = 20;
|
self.limit = 20;
|
||||||
self.offset = 0;
|
self.offset = 0;
|
||||||
self.filterOn = [];
|
self.filterOn = [];
|
||||||
|
self.facets = {};
|
||||||
|
|
||||||
|
// Index of column with author name; could be made more general by passing database column names and checking for position
|
||||||
|
self.columnIdAuthor = 2;
|
||||||
|
|
||||||
|
// Future option: Create more general solution for filter presets
|
||||||
|
if (H5PIntegration.user && parseInt(H5PIntegration.user.canToggleViewOthersH5PContents) === 1) {
|
||||||
|
self.updateTable([]);
|
||||||
|
self.filterByFacet(self.columnIdAuthor, H5PIntegration.user.id, H5PIntegration.user.name || '');
|
||||||
|
}
|
||||||
|
else {
|
||||||
self.loadData();
|
self.loadData();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load data from source URL.
|
* Load data from source URL.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
H5PDataView.prototype.loadData = function () {
|
H5PDataView.prototype.loadData = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -85,6 +95,15 @@ var H5PDataView = (function ($) {
|
||||||
url += '&filters[' + i + ']=' + encodeURIComponent(self.filterOn[i]);
|
url += '&filters[' + i + ']=' + encodeURIComponent(self.filterOn[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add facets
|
||||||
|
for (var col in self.facets) {
|
||||||
|
if (!self.facets.hasOwnProperty(col)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
url += '&facets[' + col + ']=' + self.facets[col].id;
|
||||||
|
}
|
||||||
|
|
||||||
// Fire ajax request
|
// Fire ajax request
|
||||||
$.ajax({
|
$.ajax({
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
|
@ -100,10 +119,10 @@ var H5PDataView = (function ($) {
|
||||||
else {
|
else {
|
||||||
// Update table data
|
// Update table data
|
||||||
self.updateTable(data.rows);
|
self.updateTable(data.rows);
|
||||||
|
}
|
||||||
|
|
||||||
// Update pagination widget
|
// Update pagination widget
|
||||||
self.updatePagination(data.num);
|
self.updatePagination(data.num);
|
||||||
}
|
|
||||||
|
|
||||||
if (self.loaded !== undefined) {
|
if (self.loaded !== undefined) {
|
||||||
self.loaded();
|
self.loaded();
|
||||||
|
@ -114,7 +133,6 @@ var H5PDataView = (function ($) {
|
||||||
/**
|
/**
|
||||||
* Display the given message to the user.
|
* Display the given message to the user.
|
||||||
*
|
*
|
||||||
* @public
|
|
||||||
* @param {jQuery} $message wrapper with message
|
* @param {jQuery} $message wrapper with message
|
||||||
*/
|
*/
|
||||||
H5PDataView.prototype.setMessage = function ($message) {
|
H5PDataView.prototype.setMessage = function ($message) {
|
||||||
|
@ -131,7 +149,6 @@ var H5PDataView = (function ($) {
|
||||||
/**
|
/**
|
||||||
* Update table data.
|
* Update table data.
|
||||||
*
|
*
|
||||||
* @public
|
|
||||||
* @param {Array} rows
|
* @param {Array} rows
|
||||||
*/
|
*/
|
||||||
H5PDataView.prototype.updateTable = function (rows) {
|
H5PDataView.prototype.updateTable = function (rows) {
|
||||||
|
@ -144,6 +161,18 @@ var H5PDataView = (function ($) {
|
||||||
// Add filters
|
// Add filters
|
||||||
self.addFilters();
|
self.addFilters();
|
||||||
|
|
||||||
|
// Add toggler for others' content
|
||||||
|
if (H5PIntegration.user && parseInt(H5PIntegration.user.canToggleViewOthersH5PContents) > 0) {
|
||||||
|
// canToggleViewOthersH5PContents = 1 is setting for only showing current user's contents
|
||||||
|
self.addOthersContentToggler(parseInt(H5PIntegration.user.canToggleViewOthersH5PContents) === 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add facets
|
||||||
|
self.$facets = $('<div/>', {
|
||||||
|
'class': 'h5p-facet-wrapper',
|
||||||
|
appendTo: self.$container
|
||||||
|
});
|
||||||
|
|
||||||
// Create new table
|
// Create new table
|
||||||
self.table = new H5PUtils.Table(self.classes, self.headers);
|
self.table = new H5PUtils.Table(self.classes, self.headers);
|
||||||
self.table.setHeaders(self.headers, function (order) {
|
self.table.setHeaders(self.headers, function (order) {
|
||||||
|
@ -154,20 +183,136 @@ var H5PDataView = (function ($) {
|
||||||
self.table.appendTo(self.$container);
|
self.table.appendTo(self.$container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process cell data before updating table
|
||||||
|
for (var i = 0; i < self.headers.length; i++) {
|
||||||
|
if (self.headers[i].facet === true) {
|
||||||
|
// Process rows for col, expect object or array
|
||||||
|
for (var j = 0; j < rows.length; j++) {
|
||||||
|
rows[j][i] = self.createFacets(rows[j][i], i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add/update rows
|
// Add/update rows
|
||||||
self.table.setRows(rows);
|
var $tbody = self.table.setRows(rows);
|
||||||
|
|
||||||
|
// Add event handlers for facets
|
||||||
|
$('.h5p-facet', $tbody).click(function () {
|
||||||
|
var $facet = $(this);
|
||||||
|
self.filterByFacet($facet.data('col'), $facet.data('id'), $facet.text());
|
||||||
|
}).keypress(function (event) {
|
||||||
|
if (event.which === 32) {
|
||||||
|
var $facet = $(this);
|
||||||
|
self.filterByFacet($facet.data('col'), $facet.data('id'), $facet.text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create button for adding facet to filter.
|
||||||
|
*
|
||||||
|
* @param (object|Array) input
|
||||||
|
* @param number col ID of column
|
||||||
|
*/
|
||||||
|
H5PDataView.prototype.createFacets = function (input, col) {
|
||||||
|
var facets = '';
|
||||||
|
|
||||||
|
if (input instanceof Array) {
|
||||||
|
// Facet can be filtered on multiple values at the same time
|
||||||
|
for (var i = 0; i < input.length; i++) {
|
||||||
|
if (facets !== '') {
|
||||||
|
facets += ', ';
|
||||||
|
}
|
||||||
|
facets += '<span class="h5p-facet" role="button" tabindex="0" data-id="' + input[i].id + '" data-col="' + col + '">' + input[i].title + '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Single value facet filtering
|
||||||
|
facets += '<span class="h5p-facet" role="button" tabindex="0" data-id="' + input.id + '" data-col="' + col + '">' + input.title + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return facets === '' ? '—' : facets;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a filter based on the given facet.
|
||||||
|
*
|
||||||
|
* @param number col ID of column we're filtering
|
||||||
|
* @param number id ID to filter on
|
||||||
|
* @param string text Human readable label for the filter
|
||||||
|
*/
|
||||||
|
H5PDataView.prototype.filterByFacet = function (col, id, text) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (self.facets[col] !== undefined) {
|
||||||
|
if (self.facets[col].id === id) {
|
||||||
|
return; // Don't use the same filter again
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove current filter for this col
|
||||||
|
self.facets[col].$tag.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to UI
|
||||||
|
self.facets[col] = {
|
||||||
|
id: id,
|
||||||
|
'$tag': $('<span/>', {
|
||||||
|
'class': 'h5p-facet-tag',
|
||||||
|
text: text,
|
||||||
|
appendTo: self.$facets,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Callback for removing filter.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
var remove = function () {
|
||||||
|
// Uncheck toggler for others' H5P contents
|
||||||
|
if ( self.$othersContentToggler && self.facets.hasOwnProperty( self.columnIdAuthor ) ) {
|
||||||
|
self.$othersContentToggler.prop('checked', false );
|
||||||
|
}
|
||||||
|
|
||||||
|
self.facets[col].$tag.remove();
|
||||||
|
delete self.facets[col];
|
||||||
|
self.loadData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove button
|
||||||
|
$('<span/>', {
|
||||||
|
role: 'button',
|
||||||
|
tabindex: 0,
|
||||||
|
appendTo: self.facets[col].$tag,
|
||||||
|
text: self.l10n.remove,
|
||||||
|
title: self.l10n.remove,
|
||||||
|
on: {
|
||||||
|
click: remove,
|
||||||
|
keypress: function (event) {
|
||||||
|
if (event.which === 32) {
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load data with new filter
|
||||||
|
self.loadData();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update pagination widget.
|
* Update pagination widget.
|
||||||
*
|
*
|
||||||
* @public
|
|
||||||
* @param {Number} num size of data collection
|
* @param {Number} num size of data collection
|
||||||
*/
|
*/
|
||||||
H5PDataView.prototype.updatePagination = function (num) {
|
H5PDataView.prototype.updatePagination = function (num) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (self.pagination === undefined) {
|
if (self.pagination === undefined) {
|
||||||
|
if (self.table === undefined) {
|
||||||
|
// No table, no pagination
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create new widget
|
// Create new widget
|
||||||
var $pagerContainer = $('<div/>', {'class': 'h5p-pagination'});
|
var $pagerContainer = $('<div/>', {'class': 'h5p-pagination'});
|
||||||
self.pagination = new H5PUtils.Pagination(num, self.limit, function (offset) {
|
self.pagination = new H5PUtils.Pagination(num, self.limit, function (offset) {
|
||||||
|
@ -187,8 +332,6 @@ var H5PDataView = (function ($) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add filters.
|
* Add filters.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
H5PDataView.prototype.addFilters = function () {
|
H5PDataView.prototype.addFilters = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -203,8 +346,7 @@ var H5PDataView = (function ($) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add text filter for given col num.
|
* Add text filter for given col num.
|
||||||
|
*
|
||||||
* @public
|
|
||||||
* @param {Number} col
|
* @param {Number} col
|
||||||
*/
|
*/
|
||||||
H5PDataView.prototype.addTextFilter = function (col) {
|
H5PDataView.prototype.addTextFilter = function (col) {
|
||||||
|
@ -252,5 +394,49 @@ var H5PDataView = (function ($) {
|
||||||
}).appendTo(self.$container);
|
}).appendTo(self.$container);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add toggle for others' H5P content.
|
||||||
|
* @param {boolean} [checked=false] Initial check setting.
|
||||||
|
*/
|
||||||
|
H5PDataView.prototype.addOthersContentToggler = function (checked) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
checked = (typeof checked === 'undefined') ? false : checked;
|
||||||
|
|
||||||
|
// Checkbox
|
||||||
|
this.$othersContentToggler = $('<input/>', {
|
||||||
|
type: 'checkbox',
|
||||||
|
'class': 'h5p-others-contents-toggler',
|
||||||
|
'id': 'h5p-others-contents-toggler',
|
||||||
|
'checked': checked,
|
||||||
|
'click': function () {
|
||||||
|
if ( this.checked ) {
|
||||||
|
// Add filter on current user
|
||||||
|
self.filterByFacet( self.columnIdAuthor, H5PIntegration.user.id, H5PIntegration.user.name );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Remove facet indicator and reload full data view
|
||||||
|
if ( self.facets.hasOwnProperty( self.columnIdAuthor ) && self.facets[self.columnIdAuthor].$tag ) {
|
||||||
|
self.facets[self.columnIdAuthor].$tag.remove();
|
||||||
|
}
|
||||||
|
delete self.facets[self.columnIdAuthor];
|
||||||
|
self.loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Label
|
||||||
|
var $label = $('<label>', {
|
||||||
|
'class': 'h5p-others-contents-toggler-label',
|
||||||
|
'text': this.l10n.showOwnContentOnly,
|
||||||
|
'for': 'h5p-others-contents-toggler'
|
||||||
|
}).prepend(this.$othersContentToggler);
|
||||||
|
|
||||||
|
$('<div>', {
|
||||||
|
'class': 'h5p-others-contents-toggler-wrapper'
|
||||||
|
}).append($label)
|
||||||
|
.appendTo(this.$container);
|
||||||
|
};
|
||||||
|
|
||||||
return H5PDataView;
|
return H5PDataView;
|
||||||
})(H5P.jQuery);
|
})(H5P.jQuery);
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* Utility that makes it possible to hide fields when a checkbox is unchecked
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
function setupHiding() {
|
||||||
|
var $toggler = $(this);
|
||||||
|
|
||||||
|
// Getting the field which should be hidden:
|
||||||
|
var $subject = $($toggler.data('h5p-visibility-subject-selector'));
|
||||||
|
|
||||||
|
var toggle = function () {
|
||||||
|
$subject.toggle($toggler.is(':checked'));
|
||||||
|
};
|
||||||
|
|
||||||
|
$toggler.change(toggle);
|
||||||
|
toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupRevealing() {
|
||||||
|
var $button = $(this);
|
||||||
|
|
||||||
|
// Getting the field which should have the value:
|
||||||
|
var $input = $('#' + $button.data('control'));
|
||||||
|
|
||||||
|
if (!$input.data('value')) {
|
||||||
|
$button.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup button action
|
||||||
|
var revealed = false;
|
||||||
|
var text = $button.html();
|
||||||
|
$button.click(function () {
|
||||||
|
if (revealed) {
|
||||||
|
$input.val('');
|
||||||
|
$button.html(text);
|
||||||
|
revealed = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$input.val($input.data('value'));
|
||||||
|
$button.html($button.data('hide'));
|
||||||
|
revealed = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
// Get the checkboxes making other fields being hidden:
|
||||||
|
$('.h5p-visibility-toggler').each(setupHiding);
|
||||||
|
|
||||||
|
// Get the buttons making other fields have hidden values:
|
||||||
|
$('.h5p-reveal-value').each(setupRevealing);
|
||||||
|
});
|
||||||
|
})(H5P.jQuery);
|
|
@ -1,4 +1,4 @@
|
||||||
var H5P = H5P || {};
|
var H5P = window.H5P = window.H5P || {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Event class for the EventDispatcher.
|
* The Event class for the EventDispatcher.
|
||||||
|
@ -10,7 +10,7 @@ var H5P = H5P || {};
|
||||||
* @param {boolean} [extras.bubbles]
|
* @param {boolean} [extras.bubbles]
|
||||||
* @param {boolean} [extras.external]
|
* @param {boolean} [extras.external]
|
||||||
*/
|
*/
|
||||||
H5P.Event = function(type, data, extras) {
|
H5P.Event = function (type, data, extras) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
var bubbles = false;
|
var bubbles = false;
|
||||||
|
@ -34,7 +34,7 @@ H5P.Event = function(type, data, extras) {
|
||||||
/**
|
/**
|
||||||
* Prevent this event from bubbling up to parent
|
* Prevent this event from bubbling up to parent
|
||||||
*/
|
*/
|
||||||
this.preventBubbling = function() {
|
this.preventBubbling = function () {
|
||||||
bubbles = false;
|
bubbles = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ H5P.Event = function(type, data, extras) {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
* true if bubbling false otherwise
|
* true if bubbling false otherwise
|
||||||
*/
|
*/
|
||||||
this.getBubbles = function() {
|
this.getBubbles = function () {
|
||||||
return bubbles;
|
return bubbles;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ H5P.Event = function(type, data, extras) {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
* true if external and not already scheduled, otherwise false
|
* true if external and not already scheduled, otherwise false
|
||||||
*/
|
*/
|
||||||
this.scheduleForExternal = function() {
|
this.scheduleForExternal = function () {
|
||||||
if (external && !scheduledForExternal) {
|
if (external && !scheduledForExternal) {
|
||||||
scheduledForExternal = true;
|
scheduledForExternal = true;
|
||||||
return true;
|
return true;
|
||||||
|
@ -99,7 +99,7 @@ H5P.EventDispatcher = (function () {
|
||||||
* Event type
|
* Event type
|
||||||
* @param {H5P.EventCallback} listener
|
* @param {H5P.EventCallback} listener
|
||||||
* Event listener
|
* Event listener
|
||||||
* @param {Object} thisArg
|
* @param {Object} [thisArg]
|
||||||
* Optionally specify the this value when calling listener.
|
* Optionally specify the this value when calling listener.
|
||||||
*/
|
*/
|
||||||
this.on = function (type, listener, thisArg) {
|
this.on = function (type, listener, thisArg) {
|
||||||
|
@ -139,8 +139,8 @@ H5P.EventDispatcher = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
var once = function (event) {
|
var once = function (event) {
|
||||||
self.off(event, once);
|
self.off(event.type, once);
|
||||||
listener.apply(this, event);
|
listener.call(this, event);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.on(type, once, thisArg);
|
self.on(type, once, thisArg);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
var H5PLibraryDetails= H5PLibraryDetails || {};
|
/* global H5PAdminIntegration H5PUtils */
|
||||||
|
var H5PLibraryDetails = H5PLibraryDetails || {};
|
||||||
|
|
||||||
(function ($) {
|
(function ($) {
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
*/
|
*/
|
||||||
H5PLibraryDetails.createContentTable = function () {
|
H5PLibraryDetails.createContentTable = function () {
|
||||||
// Remove it if it exists:
|
// Remove it if it exists:
|
||||||
if(H5PLibraryDetails.$contentTable) {
|
if (H5PLibraryDetails.$contentTable) {
|
||||||
H5PLibraryDetails.$contentTable.remove();
|
H5PLibraryDetails.$contentTable.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,10 +78,10 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
var i = (H5PLibraryDetails.currentPage*H5PLibraryDetails.PAGER_SIZE);
|
var i = (H5PLibraryDetails.currentPage*H5PLibraryDetails.PAGER_SIZE);
|
||||||
var lastIndex = (i+H5PLibraryDetails.PAGER_SIZE);
|
var lastIndex = (i+H5PLibraryDetails.PAGER_SIZE);
|
||||||
|
|
||||||
if(lastIndex > H5PLibraryDetails.currentContent.length) {
|
if (lastIndex > H5PLibraryDetails.currentContent.length) {
|
||||||
lastIndex = H5PLibraryDetails.currentContent.length;
|
lastIndex = H5PLibraryDetails.currentContent.length;
|
||||||
}
|
}
|
||||||
for(; i<lastIndex; i++) {
|
for (; i<lastIndex; i++) {
|
||||||
var content = H5PLibraryDetails.currentContent[i];
|
var content = H5PLibraryDetails.currentContent[i];
|
||||||
H5PLibraryDetails.$contentTable.append(H5PUtils.createTableRow(['<a href="' + content.url + '">' + content.title + '</a>']));
|
H5PLibraryDetails.$contentTable.append(H5PUtils.createTableRow(['<a href="' + content.url + '">' + content.title + '</a>']));
|
||||||
}
|
}
|
||||||
|
@ -93,15 +94,11 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
* Creates the pager element on the bottom of the list
|
* Creates the pager element on the bottom of the list
|
||||||
*/
|
*/
|
||||||
H5PLibraryDetails.createPagerElement = function () {
|
H5PLibraryDetails.createPagerElement = function () {
|
||||||
|
|
||||||
// Only create pager if needed:
|
|
||||||
if(H5PLibraryDetails.currentContent.length > H5PLibraryDetails.PAGER_SIZE) {
|
|
||||||
|
|
||||||
H5PLibraryDetails.$previous = $('<button type="button" class="previous h5p-admin"><</button>');
|
H5PLibraryDetails.$previous = $('<button type="button" class="previous h5p-admin"><</button>');
|
||||||
H5PLibraryDetails.$next = $('<button type="button" class="next h5p-admin">></button>');
|
H5PLibraryDetails.$next = $('<button type="button" class="next h5p-admin">></button>');
|
||||||
|
|
||||||
H5PLibraryDetails.$previous.on('click', function () {
|
H5PLibraryDetails.$previous.on('click', function () {
|
||||||
if(H5PLibraryDetails.$previous.hasClass('disabled')) {
|
if (H5PLibraryDetails.$previous.hasClass('disabled')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +108,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
});
|
});
|
||||||
|
|
||||||
H5PLibraryDetails.$next.on('click', function () {
|
H5PLibraryDetails.$next.on('click', function () {
|
||||||
if(H5PLibraryDetails.$next.hasClass('disabled')) {
|
if (H5PLibraryDetails.$next.hasClass('disabled')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +128,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
H5PLibraryDetails.$pagerInfo.hide();
|
H5PLibraryDetails.$pagerInfo.hide();
|
||||||
|
|
||||||
// User has updated the pageNumber
|
// User has updated the pageNumber
|
||||||
var pageNumerUpdated = function() {
|
var pageNumerUpdated = function () {
|
||||||
var newPageNum = $gotoInput.val()-1;
|
var newPageNum = $gotoInput.val()-1;
|
||||||
var intRegex = /^\d+$/;
|
var intRegex = /^\d+$/;
|
||||||
|
|
||||||
|
@ -139,7 +136,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
H5PLibraryDetails.$pagerInfo.css({display: 'inline-block'});
|
H5PLibraryDetails.$pagerInfo.css({display: 'inline-block'});
|
||||||
|
|
||||||
// Check if input value is valid, and that it has actually changed
|
// Check if input value is valid, and that it has actually changed
|
||||||
if(!(intRegex.test(newPageNum) && newPageNum >= 0 && newPageNum < H5PLibraryDetails.getNumPages() && newPageNum != H5PLibraryDetails.currentPage)) {
|
if (!(intRegex.test(newPageNum) && newPageNum >= 0 && newPageNum < H5PLibraryDetails.getNumPages() && newPageNum != H5PLibraryDetails.currentPage)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +171,6 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
});
|
});
|
||||||
|
|
||||||
H5PLibraryDetails.updatePager();
|
H5PLibraryDetails.updatePager();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -190,7 +186,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
H5PLibraryDetails.updatePager = function () {
|
H5PLibraryDetails.updatePager = function () {
|
||||||
H5PLibraryDetails.$pagerInfo.css({display: 'inline-block'});
|
H5PLibraryDetails.$pagerInfo.css({display: 'inline-block'});
|
||||||
|
|
||||||
if(H5PLibraryDetails.getNumPages() > 0) {
|
if (H5PLibraryDetails.getNumPages() > 0) {
|
||||||
var message = H5PUtils.translateReplace(H5PLibraryDetails.library.translations.pageXOfY, {
|
var message = H5PUtils.translateReplace(H5PLibraryDetails.library.translations.pageXOfY, {
|
||||||
'$x': (H5PLibraryDetails.currentPage+1),
|
'$x': (H5PLibraryDetails.currentPage+1),
|
||||||
'$y': H5PLibraryDetails.getNumPages()
|
'$y': H5PLibraryDetails.getNumPages()
|
||||||
|
@ -216,7 +212,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
var searchString = $('.h5p-content-search > input').val();
|
var searchString = $('.h5p-content-search > input').val();
|
||||||
|
|
||||||
// If search string same as previous, just do nothing
|
// If search string same as previous, just do nothing
|
||||||
if(H5PLibraryDetails.currentFilter === searchString) {
|
if (H5PLibraryDetails.currentFilter === searchString) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +220,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
// If empty search, use the complete list
|
// If empty search, use the complete list
|
||||||
H5PLibraryDetails.currentContent = H5PLibraryDetails.library.content;
|
H5PLibraryDetails.currentContent = H5PLibraryDetails.library.content;
|
||||||
}
|
}
|
||||||
else if(H5PLibraryDetails.filterCache[searchString]) {
|
else if (H5PLibraryDetails.filterCache[searchString]) {
|
||||||
// If search is cached, no need to filter
|
// If search is cached, no need to filter
|
||||||
H5PLibraryDetails.currentContent = H5PLibraryDetails.filterCache[searchString];
|
H5PLibraryDetails.currentContent = H5PLibraryDetails.filterCache[searchString];
|
||||||
}
|
}
|
||||||
|
@ -232,10 +228,10 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
var listToFilter = H5PLibraryDetails.library.content;
|
var listToFilter = H5PLibraryDetails.library.content;
|
||||||
|
|
||||||
// Check if we can filter the already filtered results (for performance)
|
// Check if we can filter the already filtered results (for performance)
|
||||||
if(searchString.length > 1 && H5PLibraryDetails.currentFilter === searchString.substr(0, H5PLibraryDetails.currentFilter.length)) {
|
if (searchString.length > 1 && H5PLibraryDetails.currentFilter === searchString.substr(0, H5PLibraryDetails.currentFilter.length)) {
|
||||||
listToFilter = H5PLibraryDetails.currentContent;
|
listToFilter = H5PLibraryDetails.currentContent;
|
||||||
}
|
}
|
||||||
H5PLibraryDetails.currentContent = $.grep(listToFilter, function(content) {
|
H5PLibraryDetails.currentContent = $.grep(listToFilter, function (content) {
|
||||||
return content.title && content.title.match(new RegExp(searchString, 'i'));
|
return content.title && content.title.match(new RegExp(searchString, 'i'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -261,7 +257,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
|
||||||
$('input', H5PLibraryDetails.$search).on('change keypress paste input', function () {
|
$('input', H5PLibraryDetails.$search).on('change keypress paste input', function () {
|
||||||
// Here we start the filtering
|
// Here we start the filtering
|
||||||
// We wait at least 500 ms after last input to perform search
|
// We wait at least 500 ms after last input to perform search
|
||||||
if(inputTimer) {
|
if (inputTimer) {
|
||||||
clearTimeout(inputTimer);
|
clearTimeout(inputTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*jshint multistr: true */
|
/* global H5PAdminIntegration H5PUtils */
|
||||||
var H5PLibraryList = H5PLibraryList || {};
|
var H5PLibraryList = H5PLibraryList || {};
|
||||||
|
|
||||||
(function ($) {
|
(function ($) {
|
||||||
|
@ -25,7 +25,7 @@ var H5PLibraryList = H5PLibraryList || {};
|
||||||
*/
|
*/
|
||||||
H5PLibraryList.createLibraryList = function (libraries) {
|
H5PLibraryList.createLibraryList = function (libraries) {
|
||||||
var t = H5PAdminIntegration.l10n;
|
var t = H5PAdminIntegration.l10n;
|
||||||
if(libraries.listData === undefined || libraries.listData.length === 0) {
|
if (libraries.listData === undefined || libraries.listData.length === 0) {
|
||||||
return $('<div>' + t.NA + '</div>');
|
return $('<div>' + t.NA + '</div>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,11 +50,11 @@ var H5PLibraryList = H5PLibraryList || {};
|
||||||
text: library.numLibraryDependencies,
|
text: library.numLibraryDependencies,
|
||||||
class: 'h5p-admin-center'
|
class: 'h5p-admin-center'
|
||||||
},
|
},
|
||||||
'<div class="h5p-admin-buttons-wrapper">\
|
'<div class="h5p-admin-buttons-wrapper">' +
|
||||||
<button class="h5p-admin-upgrade-library"></button>\
|
'<button class="h5p-admin-upgrade-library"></button>' +
|
||||||
<button class="h5p-admin-view-library" title="' + t.viewLibrary + '"></button>\
|
(library.detailsUrl ? '<button class="h5p-admin-view-library" title="' + t.viewLibrary + '"></button>' : '') +
|
||||||
<button class="h5p-admin-delete-library"></button>\
|
(library.deleteUrl ? '<button class="h5p-admin-delete-library"></button>' : '') +
|
||||||
</div>'
|
'</div>'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
H5PLibraryList.addRestricted($('.h5p-admin-restricted', $libraryRow), library.restrictedUrl, library.restricted);
|
H5PLibraryList.addRestricted($('.h5p-admin-restricted', $libraryRow), library.restrictedUrl, library.restricted);
|
||||||
|
@ -78,7 +78,12 @@ var H5PLibraryList = H5PLibraryList || {};
|
||||||
});
|
});
|
||||||
|
|
||||||
var $deleteButton = $('.h5p-admin-delete-library', $libraryRow);
|
var $deleteButton = $('.h5p-admin-delete-library', $libraryRow);
|
||||||
if (libraries.notCached !== undefined || hasContent || (library.numContentDependencies !== '' && library.numContentDependencies !== 0) || (library.numLibraryDependencies !== '' && library.numLibraryDependencies !== 0)) {
|
if (libraries.notCached !== undefined ||
|
||||||
|
hasContent ||
|
||||||
|
(library.numContentDependencies !== '' &&
|
||||||
|
library.numContentDependencies !== 0) ||
|
||||||
|
(library.numLibraryDependencies !== '' &&
|
||||||
|
library.numLibraryDependencies !== 0)) {
|
||||||
// Disabled delete if content.
|
// Disabled delete if content.
|
||||||
$deleteButton.attr('disabled', true);
|
$deleteButton.attr('disabled', true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,13 @@
|
||||||
actionHandlers.hello = function (iframe, data, respond) {
|
actionHandlers.hello = function (iframe, data, respond) {
|
||||||
// Make iframe responsive
|
// Make iframe responsive
|
||||||
iframe.style.width = '100%';
|
iframe.style.width = '100%';
|
||||||
iframe.contentDocument.body.style.height = 'auto';
|
|
||||||
|
// Bugfix for Chrome: Force update of iframe width. If this is not done the
|
||||||
|
// document size may not be updated before the content resizes.
|
||||||
|
iframe.getBoundingClientRect();
|
||||||
|
|
||||||
// Tell iframe that it needs to resize when our window resizes
|
// Tell iframe that it needs to resize when our window resizes
|
||||||
var resize = function (event) {
|
var resize = function () {
|
||||||
if (iframe.contentWindow) {
|
if (iframe.contentWindow) {
|
||||||
// Limit resize calls to avoid flickering
|
// Limit resize calls to avoid flickering
|
||||||
respond('resize');
|
respond('resize');
|
||||||
|
@ -47,7 +50,14 @@
|
||||||
* @param {Function} respond Send a response to the iframe
|
* @param {Function} respond Send a response to the iframe
|
||||||
*/
|
*/
|
||||||
actionHandlers.prepareResize = function (iframe, data, respond) {
|
actionHandlers.prepareResize = function (iframe, data, respond) {
|
||||||
|
// Do not resize unless page and scrolling differs
|
||||||
|
if (iframe.clientHeight !== data.scrollHeight ||
|
||||||
|
data.scrollHeight !== data.clientHeight) {
|
||||||
|
|
||||||
|
// Reset iframe height, in case content has shrinked.
|
||||||
|
iframe.style.height = data.clientHeight + 'px';
|
||||||
respond('resizePrepared');
|
respond('resizePrepared');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,17 +68,9 @@
|
||||||
* @param {Object} data Payload
|
* @param {Object} data Payload
|
||||||
* @param {Function} respond Send a response to the iframe
|
* @param {Function} respond Send a response to the iframe
|
||||||
*/
|
*/
|
||||||
actionHandlers.resize = function (iframe, data, respond) {
|
actionHandlers.resize = function (iframe, data) {
|
||||||
if (iframe.clientHeight === iframe.contentDocument.body.scrollHeight &&
|
|
||||||
iframe.contentDocument.body.scrollHeight === iframe.contentWindow.document.body.clientHeight) {
|
|
||||||
return; // Do not resize unless page and scrolling differs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset iframe height, in case content has shrinked.
|
|
||||||
iframe.style.height = iframe.contentWindow.document.body.clientHeight + 'px';
|
|
||||||
|
|
||||||
// Resize iframe so all content is visible. Use scrollHeight to make sure we get everything
|
// Resize iframe so all content is visible. Use scrollHeight to make sure we get everything
|
||||||
iframe.style.height = iframe.contentDocument.body.scrollHeight + 'px';
|
iframe.style.height = data.scrollHeight + 'px';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* global H5PAdminIntegration*/
|
||||||
var H5PUtils = H5PUtils || {};
|
var H5PUtils = H5PUtils || {};
|
||||||
|
|
||||||
(function ($) {
|
(function ($) {
|
||||||
|
@ -9,7 +10,7 @@ var H5PUtils = H5PUtils || {};
|
||||||
H5PUtils.createTable = function (headers) {
|
H5PUtils.createTable = function (headers) {
|
||||||
var $table = $('<table class="h5p-admin-table' + (H5PAdminIntegration.extraTableClasses !== undefined ? ' ' + H5PAdminIntegration.extraTableClasses : '') + '"></table>');
|
var $table = $('<table class="h5p-admin-table' + (H5PAdminIntegration.extraTableClasses !== undefined ? ' ' + H5PAdminIntegration.extraTableClasses : '') + '"></table>');
|
||||||
|
|
||||||
if(headers) {
|
if (headers) {
|
||||||
var $thead = $('<thead></thead>');
|
var $thead = $('<thead></thead>');
|
||||||
var $tr = $('<tr></tr>');
|
var $tr = $('<tr></tr>');
|
||||||
|
|
||||||
|
@ -303,6 +304,8 @@ var H5PUtils = H5PUtils || {};
|
||||||
|
|
||||||
$tbody.replaceWith($newTbody);
|
$tbody.replaceWith($newTbody);
|
||||||
$tbody = $newTbody;
|
$tbody = $newTbody;
|
||||||
|
|
||||||
|
return $tbody;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,11 +7,24 @@ H5P.Version = (function () {
|
||||||
* @param {String} version
|
* @param {String} version
|
||||||
*/
|
*/
|
||||||
function Version(version) {
|
function Version(version) {
|
||||||
var versionSplit = version.split('.', 3);
|
|
||||||
|
|
||||||
// Public
|
if (typeof version === 'string') {
|
||||||
this.major = versionSplit[0];
|
// Name version string (used by content upgrade)
|
||||||
this.minor = versionSplit[1];
|
var versionSplit = version.split('.', 3);
|
||||||
|
this.major =+ versionSplit[0];
|
||||||
|
this.minor =+ versionSplit[1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Library objects (used by editor)
|
||||||
|
if (version.localMajorVersion !== undefined) {
|
||||||
|
this.major =+ version.localMajorVersion;
|
||||||
|
this.minor =+ version.localMinorVersion;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.major =+ version.majorVersion;
|
||||||
|
this.minor =+ version.minorVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public. Custom string for this object.
|
* Public. Custom string for this object.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var H5P = H5P || {};
|
var H5P = window.H5P = window.H5P || {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for xAPI events.
|
* Used for xAPI events.
|
||||||
|
@ -133,13 +133,26 @@ H5P.XAPIEvent.prototype.setObject = function (instance) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (H5PIntegration && H5PIntegration.contents && H5PIntegration.contents['cid-' + instance.contentId].title) {
|
var content = H5P.getContentForInstance(instance.contentId);
|
||||||
|
if (content && content.metadata && content.metadata.title) {
|
||||||
this.data.statement.object.definition.name = {
|
this.data.statement.object.definition.name = {
|
||||||
"en-US": H5P.createTitle(H5PIntegration.contents['cid-' + instance.contentId].title)
|
"en-US": H5P.createTitle(content.metadata.title)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// Content types view always expect to have a contentId when they are displayed.
|
||||||
|
// This is not the case if they are displayed in the editor as part of a preview.
|
||||||
|
// The fix is to set an empty object with definition for the xAPI event, so all
|
||||||
|
// the content types that rely on this does not have to handle it. This means
|
||||||
|
// that content types that are being previewed will send xAPI completed events,
|
||||||
|
// but since there are no scripts that catch these events in the editor,
|
||||||
|
// this is not a problem.
|
||||||
|
this.data.statement.object = {
|
||||||
|
definition: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,7 +163,6 @@ H5P.XAPIEvent.prototype.setObject = function (instance) {
|
||||||
*/
|
*/
|
||||||
H5P.XAPIEvent.prototype.setContext = function (instance) {
|
H5P.XAPIEvent.prototype.setContext = function (instance) {
|
||||||
if (instance.parent && (instance.parent.contentId || instance.parent.subContentId)) {
|
if (instance.parent && (instance.parent.contentId || instance.parent.subContentId)) {
|
||||||
var parentId = instance.parent.subContentId === undefined ? instance.parent.contentId : instance.parent.subContentId;
|
|
||||||
this.data.statement.context = {
|
this.data.statement.context = {
|
||||||
"contextActivities": {
|
"contextActivities": {
|
||||||
"parent": [
|
"parent": [
|
||||||
|
@ -188,6 +200,7 @@ H5P.XAPIEvent.prototype.setActor = function () {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var uuid;
|
var uuid;
|
||||||
|
try {
|
||||||
if (localStorage.H5PUserUUID) {
|
if (localStorage.H5PUserUUID) {
|
||||||
uuid = localStorage.H5PUserUUID;
|
uuid = localStorage.H5PUserUUID;
|
||||||
}
|
}
|
||||||
|
@ -195,6 +208,11 @@ H5P.XAPIEvent.prototype.setActor = function () {
|
||||||
uuid = H5P.createUUID();
|
uuid = H5P.createUUID();
|
||||||
localStorage.H5PUserUUID = uuid;
|
localStorage.H5PUserUUID = uuid;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
// LocalStorage and Cookies are probably disabled. Do not track the user.
|
||||||
|
uuid = 'not-trackable-' + H5P.createUUID();
|
||||||
|
}
|
||||||
this.data.statement.actor = {
|
this.data.statement.actor = {
|
||||||
'account': {
|
'account': {
|
||||||
'name': uuid,
|
'name': uuid,
|
||||||
|
@ -211,7 +229,7 @@ H5P.XAPIEvent.prototype.setActor = function () {
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
* The max score, or null if not defined
|
* The max score, or null if not defined
|
||||||
*/
|
*/
|
||||||
H5P.XAPIEvent.prototype.getMaxScore = function() {
|
H5P.XAPIEvent.prototype.getMaxScore = function () {
|
||||||
return this.getVerifiedStatementValue(['result', 'score', 'max']);
|
return this.getVerifiedStatementValue(['result', 'score', 'max']);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -221,7 +239,7 @@ H5P.XAPIEvent.prototype.getMaxScore = function() {
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
* The score, or null if not defined
|
* The score, or null if not defined
|
||||||
*/
|
*/
|
||||||
H5P.XAPIEvent.prototype.getScore = function() {
|
H5P.XAPIEvent.prototype.getScore = function () {
|
||||||
return this.getVerifiedStatementValue(['result', 'score', 'raw']);
|
return this.getVerifiedStatementValue(['result', 'score', 'raw']);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -233,7 +251,7 @@ H5P.XAPIEvent.prototype.getScore = function() {
|
||||||
*/
|
*/
|
||||||
H5P.XAPIEvent.prototype.getContentXAPIId = function (instance) {
|
H5P.XAPIEvent.prototype.getContentXAPIId = function (instance) {
|
||||||
var xAPIId;
|
var xAPIId;
|
||||||
if (instance.contentId && H5PIntegration && H5PIntegration.contents) {
|
if (instance.contentId && H5PIntegration && H5PIntegration.contents && H5PIntegration.contents['cid-' + instance.contentId]) {
|
||||||
xAPIId = H5PIntegration.contents['cid-' + instance.contentId].url;
|
xAPIId = H5PIntegration.contents['cid-' + instance.contentId].url;
|
||||||
if (instance.subContentId) {
|
if (instance.subContentId) {
|
||||||
xAPIId += '?subContentId=' + instance.subContentId;
|
xAPIId += '?subContentId=' + instance.subContentId;
|
||||||
|
@ -242,6 +260,16 @@ H5P.XAPIEvent.prototype.getContentXAPIId = function (instance) {
|
||||||
return xAPIId;
|
return xAPIId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this event is sent from a child (i.e not from grandchild)
|
||||||
|
*
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
H5P.XAPIEvent.prototype.isFromChild = function () {
|
||||||
|
var parentId = this.getVerifiedStatementValue(['context', 'contextActivities', 'parent', 0, 'id']);
|
||||||
|
return !parentId || parentId.indexOf('subContentId') === -1;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Figure out if a property exists in the statement and return it
|
* Figure out if a property exists in the statement and return it
|
||||||
*
|
*
|
||||||
|
@ -251,7 +279,7 @@ H5P.XAPIEvent.prototype.getContentXAPIId = function (instance) {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
* The value of the property if it is set, null otherwise.
|
* The value of the property if it is set, null otherwise.
|
||||||
*/
|
*/
|
||||||
H5P.XAPIEvent.prototype.getVerifiedStatementValue = function(keys) {
|
H5P.XAPIEvent.prototype.getVerifiedStatementValue = function (keys) {
|
||||||
var val = this.data.statement;
|
var val = this.data.statement;
|
||||||
for (var i = 0; i < keys.length; i++) {
|
for (var i = 0; i < keys.length; i++) {
|
||||||
if (val[keys[i]] === undefined) {
|
if (val[keys[i]] === undefined) {
|
||||||
|
@ -292,5 +320,12 @@ H5P.XAPIEvent.allowedXAPIVerbs = [
|
||||||
'shared',
|
'shared',
|
||||||
'suspended',
|
'suspended',
|
||||||
'terminated',
|
'terminated',
|
||||||
'voided'
|
'voided',
|
||||||
|
|
||||||
|
// Custom verbs used for action toolbar below content
|
||||||
|
'downloaded',
|
||||||
|
'copied',
|
||||||
|
'accessed-reuse',
|
||||||
|
'accessed-embed',
|
||||||
|
'accessed-copyright'
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var H5P = H5P || {};
|
var H5P = window.H5P = window.H5P || {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The external event dispatcher. Others, outside of H5P may register and
|
* The external event dispatcher. Others, outside of H5P may register and
|
||||||
|
@ -92,10 +92,11 @@ H5P.EventDispatcher.prototype.triggerXAPIScored = function (score, maxScore, ver
|
||||||
this.trigger(event);
|
this.trigger(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
H5P.EventDispatcher.prototype.setActivityStarted = function() {
|
H5P.EventDispatcher.prototype.setActivityStarted = function () {
|
||||||
if (this.activityStartTime === undefined) {
|
if (this.activityStartTime === undefined) {
|
||||||
// Don't trigger xAPI events in the editor
|
// Don't trigger xAPI events in the editor
|
||||||
if (H5PIntegration.contents !== undefined &&
|
if (this.contentId !== undefined &&
|
||||||
|
H5PIntegration.contents !== undefined &&
|
||||||
H5PIntegration.contents['cid-' + this.contentId] !== undefined) {
|
H5PIntegration.contents['cid-' + this.contentId] !== undefined) {
|
||||||
this.triggerXAPI('attempted');
|
this.triggerXAPI('attempted');
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,436 @@
|
||||||
|
/**
|
||||||
|
* Queue requests and handle them at your convenience
|
||||||
|
*
|
||||||
|
* @type {RequestQueue}
|
||||||
|
*/
|
||||||
|
H5P.RequestQueue = (function ($, EventDispatcher) {
|
||||||
|
/**
|
||||||
|
* A queue for requests, will be automatically processed when regaining connection
|
||||||
|
*
|
||||||
|
* @param {boolean} [options.showToast] Show toast when losing or regaining connection
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
const RequestQueue = function (options) {
|
||||||
|
EventDispatcher.call(this);
|
||||||
|
this.processingQueue = false;
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
this.showToast = options.showToast;
|
||||||
|
this.itemName = 'requestQueue';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add request to queue. Only supports posts currently.
|
||||||
|
*
|
||||||
|
* @param {string} url
|
||||||
|
* @param {Object} data
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
RequestQueue.prototype.add = function (url, data) {
|
||||||
|
if (!window.localStorage) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let storedStatements = this.getStoredRequests();
|
||||||
|
if (!storedStatements) {
|
||||||
|
storedStatements = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
storedStatements.push({
|
||||||
|
url: url,
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
window.localStorage.setItem(this.itemName, JSON.stringify(storedStatements));
|
||||||
|
|
||||||
|
this.trigger('requestQueued', {
|
||||||
|
storedStatements: storedStatements,
|
||||||
|
processingQueue: this.processingQueue,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get stored requests
|
||||||
|
*
|
||||||
|
* @returns {boolean|Array} Stored requests
|
||||||
|
*/
|
||||||
|
RequestQueue.prototype.getStoredRequests = function () {
|
||||||
|
if (!window.localStorage) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = window.localStorage.getItem(this.itemName);
|
||||||
|
if (!item) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear stored requests
|
||||||
|
*
|
||||||
|
* @returns {boolean} True if the storage was successfully cleared
|
||||||
|
*/
|
||||||
|
RequestQueue.prototype.clearQueue = function () {
|
||||||
|
if (!window.localStorage) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.localStorage.removeItem(this.itemName);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start processing of requests queue
|
||||||
|
*
|
||||||
|
* @return {boolean} Returns false if it was not possible to resume processing queue
|
||||||
|
*/
|
||||||
|
RequestQueue.prototype.resumeQueue = function () {
|
||||||
|
// Not supported
|
||||||
|
if (!H5PIntegration || !window.navigator || !window.localStorage) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already processing
|
||||||
|
if (this.processingQueue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to send queued requests
|
||||||
|
const queue = this.getStoredRequests();
|
||||||
|
const queueLength = queue.length;
|
||||||
|
|
||||||
|
// Clear storage, failed requests will be re-added
|
||||||
|
this.clearQueue();
|
||||||
|
|
||||||
|
// No items left in queue
|
||||||
|
if (!queueLength) {
|
||||||
|
this.trigger('emptiedQueue', queue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure requests are not changed while they're being handled
|
||||||
|
this.processingQueue = true;
|
||||||
|
|
||||||
|
// Process queue in original order
|
||||||
|
this.processQueue(queue);
|
||||||
|
return true
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process first item in the request queue
|
||||||
|
*
|
||||||
|
* @param {Array} queue Request queue
|
||||||
|
*/
|
||||||
|
RequestQueue.prototype.processQueue = function (queue) {
|
||||||
|
if (!queue.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.trigger('processingQueue');
|
||||||
|
|
||||||
|
// Make sure the requests are processed in a FIFO order
|
||||||
|
const request = queue.shift();
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
$.post(request.url, request.data)
|
||||||
|
.fail(self.onQueuedRequestFail.bind(self, request))
|
||||||
|
.always(self.onQueuedRequestProcessed.bind(self, queue))
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request fail handler
|
||||||
|
*
|
||||||
|
* @param {Object} request
|
||||||
|
*/
|
||||||
|
RequestQueue.prototype.onQueuedRequestFail = function (request) {
|
||||||
|
// Queue the failed request again if we're offline
|
||||||
|
if (!window.navigator.onLine) {
|
||||||
|
this.add(request.url, request.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An item in the queue was processed
|
||||||
|
*
|
||||||
|
* @param {Array} queue Queue that was processed
|
||||||
|
*/
|
||||||
|
RequestQueue.prototype.onQueuedRequestProcessed = function (queue) {
|
||||||
|
if (queue.length) {
|
||||||
|
this.processQueue(queue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished processing this queue
|
||||||
|
this.processingQueue = false;
|
||||||
|
|
||||||
|
// Run empty queue callback with next request queue
|
||||||
|
const requestQueue = this.getStoredRequests();
|
||||||
|
this.trigger('queueEmptied', requestQueue);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display toast message on the first content of current page
|
||||||
|
*
|
||||||
|
* @param {string} msg Message to display
|
||||||
|
* @param {boolean} [forceShow] Force override showing the toast
|
||||||
|
* @param {Object} [configOverride] Override toast message config
|
||||||
|
*/
|
||||||
|
RequestQueue.prototype.displayToastMessage = function (msg, forceShow, configOverride) {
|
||||||
|
if (!this.showToast && !forceShow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = H5P.jQuery.extend(true, {}, {
|
||||||
|
position: {
|
||||||
|
horizontal : 'centered',
|
||||||
|
vertical: 'centered',
|
||||||
|
noOverflowX: true,
|
||||||
|
}
|
||||||
|
}, configOverride);
|
||||||
|
|
||||||
|
H5P.attachToastTo(H5P.jQuery('.h5p-content:first')[0], msg, config);
|
||||||
|
};
|
||||||
|
|
||||||
|
return RequestQueue;
|
||||||
|
})(H5P.jQuery, H5P.EventDispatcher);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request queue for retrying failing requests, will automatically retry them when you come online
|
||||||
|
*
|
||||||
|
* @type {offlineRequestQueue}
|
||||||
|
*/
|
||||||
|
H5P.OfflineRequestQueue = (function (RequestQueue, Dialog) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {Object} [options] Options for offline request queue
|
||||||
|
* @param {Object} [options.instance] The H5P instance which UI components are placed within
|
||||||
|
*/
|
||||||
|
const offlineRequestQueue = function (options) {
|
||||||
|
const requestQueue = new RequestQueue();
|
||||||
|
|
||||||
|
// We could handle requests from previous pages here, but instead we throw them away
|
||||||
|
requestQueue.clearQueue();
|
||||||
|
|
||||||
|
let startTime = null;
|
||||||
|
const retryIntervals = [10, 20, 40, 60, 120, 300, 600];
|
||||||
|
let intervalIndex = -1;
|
||||||
|
let currentInterval = null;
|
||||||
|
let isAttached = false;
|
||||||
|
let isShowing = false;
|
||||||
|
let isLoading = false;
|
||||||
|
const instance = options.instance;
|
||||||
|
|
||||||
|
const offlineDialog = new Dialog({
|
||||||
|
headerText: H5P.t('offlineDialogHeader'),
|
||||||
|
dialogText: H5P.t('offlineDialogBody'),
|
||||||
|
confirmText: H5P.t('offlineDialogRetryButtonLabel'),
|
||||||
|
hideCancel: true,
|
||||||
|
hideExit: true,
|
||||||
|
classes: ['offline'],
|
||||||
|
instance: instance,
|
||||||
|
skipRestoreFocus: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dialog = offlineDialog.getElement();
|
||||||
|
|
||||||
|
// Add retry text to body
|
||||||
|
const countDownText = document.createElement('div');
|
||||||
|
countDownText.classList.add('count-down');
|
||||||
|
countDownText.innerHTML = H5P.t('offlineDialogRetryMessage')
|
||||||
|
.replace(':num', '<span class="count-down-num">0</span>');
|
||||||
|
|
||||||
|
dialog.querySelector('.h5p-confirmation-dialog-text').appendChild(countDownText);
|
||||||
|
const countDownNum = countDownText.querySelector('.count-down-num');
|
||||||
|
|
||||||
|
// Create throbber
|
||||||
|
const throbberWrapper = document.createElement('div');
|
||||||
|
throbberWrapper.classList.add('throbber-wrapper');
|
||||||
|
const throbber = document.createElement('div');
|
||||||
|
throbber.classList.add('sending-requests-throbber');
|
||||||
|
throbberWrapper.appendChild(throbber);
|
||||||
|
|
||||||
|
requestQueue.on('requestQueued', function (e) {
|
||||||
|
// Already processing queue, wait until queue has finished processing before showing dialog
|
||||||
|
if (e.data && e.data.processingQueue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAttached) {
|
||||||
|
const rootContent = document.body.querySelector('.h5p-content');
|
||||||
|
if (!rootContent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
offlineDialog.appendTo(rootContent);
|
||||||
|
rootContent.appendChild(throbberWrapper);
|
||||||
|
isAttached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
startCountDown();
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
requestQueue.on('queueEmptied', function (e) {
|
||||||
|
if (e.data && e.data.length) {
|
||||||
|
// New requests were added while processing queue or requests failed again. Re-queue requests.
|
||||||
|
startCountDown(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Successfully emptied queue
|
||||||
|
clearInterval(currentInterval);
|
||||||
|
toggleThrobber(false);
|
||||||
|
intervalIndex = -1;
|
||||||
|
if (isShowing) {
|
||||||
|
offlineDialog.hide();
|
||||||
|
isShowing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestQueue.displayToastMessage(
|
||||||
|
H5P.t('offlineSuccessfulSubmit'),
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
position: {
|
||||||
|
vertical: 'top',
|
||||||
|
offsetVertical: '100',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
offlineDialog.on('confirmed', function () {
|
||||||
|
// Show dialog on next render in case it is being hidden by the 'confirm' button
|
||||||
|
isShowing = false;
|
||||||
|
setTimeout(function () {
|
||||||
|
retryRequests();
|
||||||
|
}, 100);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
// Initialize listener for when requests are added to queue
|
||||||
|
window.addEventListener('online', function () {
|
||||||
|
retryRequests();
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
// Listen for queued requests outside the iframe
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
const isValidQueueEvent = window.parent === event.source
|
||||||
|
&& event.data.context === 'h5p'
|
||||||
|
&& event.data.action === 'queueRequest';
|
||||||
|
|
||||||
|
if (!isValidQueueEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.add(event.data.url, event.data.data);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle throbber visibility
|
||||||
|
*
|
||||||
|
* @param {boolean} [forceShow] Will force throbber visibility if set
|
||||||
|
*/
|
||||||
|
const toggleThrobber = function (forceShow) {
|
||||||
|
isLoading = !isLoading;
|
||||||
|
if (forceShow !== undefined) {
|
||||||
|
isLoading = forceShow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading && isShowing) {
|
||||||
|
offlineDialog.hide();
|
||||||
|
isShowing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
throbberWrapper.classList.add('show');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throbberWrapper.classList.remove('show');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Retries the failed requests
|
||||||
|
*/
|
||||||
|
const retryRequests = function () {
|
||||||
|
clearInterval(currentInterval);
|
||||||
|
toggleThrobber(true);
|
||||||
|
requestQueue.resumeQueue();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments retry interval
|
||||||
|
*/
|
||||||
|
const incrementRetryInterval = function () {
|
||||||
|
intervalIndex += 1;
|
||||||
|
if (intervalIndex >= retryIntervals.length) {
|
||||||
|
intervalIndex = retryIntervals.length - 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts counting down to retrying queued requests.
|
||||||
|
*
|
||||||
|
* @param forceDelayedShow
|
||||||
|
*/
|
||||||
|
const startCountDown = function (forceDelayedShow) {
|
||||||
|
// Already showing, wait for retry
|
||||||
|
if (isShowing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleThrobber(false);
|
||||||
|
if (!isShowing) {
|
||||||
|
if (forceDelayedShow) {
|
||||||
|
// Must force delayed show since dialog may be hiding, and confirmation dialog does not
|
||||||
|
// support this.
|
||||||
|
setTimeout(function () {
|
||||||
|
offlineDialog.show(0);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
offlineDialog.show(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isShowing = true;
|
||||||
|
startTime = new Date().getTime();
|
||||||
|
incrementRetryInterval();
|
||||||
|
clearInterval(currentInterval);
|
||||||
|
currentInterval = setInterval(updateCountDown, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the count down timer. Retries requests when time expires.
|
||||||
|
*/
|
||||||
|
const updateCountDown = function () {
|
||||||
|
const time = new Date().getTime();
|
||||||
|
const timeElapsed = Math.floor((time - startTime) / 1000);
|
||||||
|
const timeLeft = retryIntervals[intervalIndex] - timeElapsed;
|
||||||
|
countDownNum.textContent = timeLeft.toString();
|
||||||
|
|
||||||
|
// Retry interval reached, retry requests
|
||||||
|
if (timeLeft <= 0) {
|
||||||
|
retryRequests();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add request to offline request queue. Only supports posts for now.
|
||||||
|
*
|
||||||
|
* @param {string} url The request url
|
||||||
|
* @param {Object} data The request data
|
||||||
|
*/
|
||||||
|
this.add = function (url, data) {
|
||||||
|
// Only queue request if it failed because we are offline
|
||||||
|
if (window.navigator.onLine) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestQueue.add(url, data);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return offlineRequestQueue;
|
||||||
|
})(H5P.RequestQueue, H5P.ConfirmationDialog);
|
|
@ -0,0 +1,68 @@
|
||||||
|
/* global H5PDisableHubData */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global data for disable hub functionality
|
||||||
|
*
|
||||||
|
* @typedef {object} H5PDisableHubData Data passed in from the backend
|
||||||
|
*
|
||||||
|
* @property {string} selector Selector for the disable hub check-button
|
||||||
|
* @property {string} overlaySelector Selector for the element that the confirmation dialog will mask
|
||||||
|
* @property {Array} errors Errors found with the current server setup
|
||||||
|
*
|
||||||
|
* @property {string} header Header of the confirmation dialog
|
||||||
|
* @property {string} confirmationDialogMsg Body of the confirmation dialog
|
||||||
|
* @property {string} cancelLabel Cancel label of the confirmation dialog
|
||||||
|
* @property {string} confirmLabel Confirm button label of the confirmation dialog
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Utility that makes it possible to force the user to confirm that he really
|
||||||
|
* wants to use the H5P hub without proper server settings.
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
|
||||||
|
$(document).on('ready', function () {
|
||||||
|
|
||||||
|
// No data found
|
||||||
|
if (!H5PDisableHubData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No errors found, no need for confirmation dialog
|
||||||
|
if (!H5PDisableHubData.errors || !H5PDisableHubData.errors.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
H5PDisableHubData.selector = H5PDisableHubData.selector ||
|
||||||
|
'.h5p-settings-disable-hub-checkbox';
|
||||||
|
H5PDisableHubData.overlaySelector = H5PDisableHubData.overlaySelector ||
|
||||||
|
'.h5p-settings-container';
|
||||||
|
|
||||||
|
var dialogHtml = '<div>' +
|
||||||
|
'<p>' + H5PDisableHubData.errors.join('</p><p>') + '</p>' +
|
||||||
|
'<p>' + H5PDisableHubData.confirmationDialogMsg + '</p>';
|
||||||
|
|
||||||
|
// Create confirmation dialog, make sure to include translations
|
||||||
|
var confirmationDialog = new H5P.ConfirmationDialog({
|
||||||
|
headerText: H5PDisableHubData.header,
|
||||||
|
dialogText: dialogHtml,
|
||||||
|
cancelText: H5PDisableHubData.cancelLabel,
|
||||||
|
confirmText: H5PDisableHubData.confirmLabel
|
||||||
|
}).appendTo($(H5PDisableHubData.overlaySelector).get(0));
|
||||||
|
|
||||||
|
confirmationDialog.on('confirmed', function () {
|
||||||
|
enableButton.get(0).checked = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmationDialog.on('canceled', function () {
|
||||||
|
enableButton.get(0).checked = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
var enableButton = $(H5PDisableHubData.selector);
|
||||||
|
enableButton.change(function () {
|
||||||
|
if ($(this).is(':checked')) {
|
||||||
|
confirmationDialog.show(enableButton.offset().top);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})(H5P.jQuery);
|
|
@ -1,143 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"machineName": "H5P.CoursePresentation",
|
|
||||||
"downloadUrl": "http://h5p.org/presentation",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 1,
|
|
||||||
"patch": 10
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"machineName": "H5P.Blanks",
|
|
||||||
"downloadUrl": "http://h5p.org/fill-in-the-blanks",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 65
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"machineName": "H5P.Dialogcards",
|
|
||||||
"downloadUrl": "http://h5p.org/dialog-cards",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 28
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"machineName": "H5P.DragQuestion",
|
|
||||||
"downloadUrl": "http://h5p.org/drag-and-drop",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 61
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"machineName": "H5P.InteractiveVideo",
|
|
||||||
"downloadUrl": "http://h5p.org/interactive-video",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 31
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 1,
|
|
||||||
"patch": 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 2,
|
|
||||||
"patch": 10
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"machineName": "H5P.Flashcards",
|
|
||||||
"downloadUrl": "http://h5p.org/flashcards",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 37
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"machineName": "H5P.ImageHotspots",
|
|
||||||
"downloadUrl": "http://h5p.org/image-hotspots",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"machineName": "H5P.JoubelUI",
|
|
||||||
"downloadUrl": "http://h5p.org/fill-in-the-blanks",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 6
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"machineName": "H5P.MultiChoice",
|
|
||||||
"downloadUrl": "http://h5p.org/multichoice",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 51
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"machineName": "H5P.QuestionSet",
|
|
||||||
"downloadUrl": "http://h5p.org/question-set",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 53
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"machineName": "H5P.Summary",
|
|
||||||
"downloadUrl": "http://h5p.org/summary",
|
|
||||||
"minimumVersions": [
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0,
|
|
||||||
"patch": 38
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"major": 1,
|
|
||||||
"minor": 1,
|
|
||||||
"patch": 4
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -9,6 +9,7 @@
|
||||||
.h5p-admin-table,
|
.h5p-admin-table,
|
||||||
.h5p-admin-table > tbody {
|
.h5p-admin-table > tbody {
|
||||||
border: none;
|
border: none;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h5p-admin-table tr:nth-child(odd),
|
.h5p-admin-table tr:nth-child(odd),
|
||||||
|
@ -203,7 +204,7 @@ button.h5p-admin.disabled:hover {
|
||||||
line-height: 130%;
|
line-height: 130%;
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
font-family: 'H5P'; /* TODO: Find content */
|
font-family: 'H5P';
|
||||||
font-size: 1.4em;
|
font-size: 1.4em;
|
||||||
}
|
}
|
||||||
.h5p-content-pager > button:focus {
|
.h5p-content-pager > button:focus {
|
||||||
|
@ -231,7 +232,8 @@ button.h5p-admin.disabled:hover {
|
||||||
.h5p-admin-header {
|
.h5p-admin-header {
|
||||||
margin-top: 1.5em;
|
margin-top: 1.5em;
|
||||||
}
|
}
|
||||||
#h5p-library-upload-form.h5p-admin-upload-libraries-form {
|
#h5p-library-upload-form.h5p-admin-upload-libraries-form,
|
||||||
|
#h5p-content-type-cache-update-form.h5p-admin-upload-libraries-form {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
@ -257,11 +259,27 @@ button.h5p-admin.disabled:hover {
|
||||||
}
|
}
|
||||||
.h5p-data-view input[type="text"] {
|
.h5p-data-view input[type="text"] {
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
float: left;
|
||||||
}
|
}
|
||||||
.h5p-data-view input[type="text"]::-ms-clear {
|
.h5p-data-view input[type="text"]::-ms-clear {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h5p-data-view .h5p-others-contents-toggler-wrapper {
|
||||||
|
float: right;
|
||||||
|
line-height: 2;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-data-view .h5p-others-contents-toggler-label {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-data-view .h5p-others-contents-toggler {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.h5p-data-view th[role="button"] {
|
.h5p-data-view th[role="button"] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
@ -283,3 +301,58 @@ button.h5p-admin.disabled:hover {
|
||||||
.h5p-data-view th[role="button"].h5p-sort:hover:after {
|
.h5p-data-view th[role="button"].h5p-sort:hover:after {
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
.h5p-data-view .h5p-facet {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #0073aa;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.h5p-data-view .h5p-facet:hover,
|
||||||
|
.h5p-data-view .h5p-facet:active {
|
||||||
|
color: #00a0d2;
|
||||||
|
}
|
||||||
|
.h5p-data-view .h5p-facet:focus {
|
||||||
|
color: #124964;
|
||||||
|
box-shadow: 0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);
|
||||||
|
}
|
||||||
|
.h5p-data-view .h5p-facet-wrapper {
|
||||||
|
line-height: 23px;
|
||||||
|
}
|
||||||
|
.h5p-data-view .h5p-facet-tag {
|
||||||
|
margin: 2px 0 0 0.5em;
|
||||||
|
font-size: 12px;
|
||||||
|
background: #e8e8e8;
|
||||||
|
border: 1px solid #cbcbcc;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #5d5d5d;
|
||||||
|
padding: 0 24px 0 10px;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.h5p-data-view .h5p-facet-tag > span {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: auto;
|
||||||
|
bottom: auto;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #a2a2a2;
|
||||||
|
outline: none;
|
||||||
|
width: 21px;
|
||||||
|
text-indent: 4px;
|
||||||
|
letter-spacing: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.h5p-data-view .h5p-facet-tag > span:before {
|
||||||
|
content: "×";
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.h5p-data-view .h5p-facet-tag > span:hover,
|
||||||
|
.h5p-data-view .h5p-facet-tag > span:focus {
|
||||||
|
color: #a20000;
|
||||||
|
}
|
||||||
|
.h5p-data-view .h5p-facet-tag > span:active {
|
||||||
|
color: #d20000;
|
||||||
|
}
|
||||||
|
.content-upgrade-log {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
.h5p-confirmation-dialog-background {
|
||||||
|
position: fixed;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
background: rgba(44, 44, 44, 0.9);
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
-webkit-transition: opacity 0.1s, linear 0s, visibility 0s linear 0s;
|
||||||
|
transition: opacity 0.1s linear 0s, visibility 0s linear 0s;
|
||||||
|
|
||||||
|
z-index: 201;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-background.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-background.hiding {
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
-webkit-transition: opacity 0.1s, linear 0s, visibility 0s linear 0.1s;
|
||||||
|
transition: opacity 0.1s linear 0s, visibility 0s linear 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-popup:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-popup {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 35em;
|
||||||
|
min-width: 25em;
|
||||||
|
|
||||||
|
top: 2em;
|
||||||
|
left: 50%;
|
||||||
|
-webkit-transform: translate(-50%, 0%);
|
||||||
|
-ms-transform: translate(-50%, 0%);
|
||||||
|
transform: translate(-50%, 0%);
|
||||||
|
|
||||||
|
color: #555;
|
||||||
|
box-shadow: 0 0 6px 6px rgba(10,10,10,0.3);
|
||||||
|
|
||||||
|
-webkit-transition: transform 0.1s ease-in;
|
||||||
|
transition: transform 0.1s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-popup.hidden {
|
||||||
|
-webkit-transform: translate(-50%, 50%);
|
||||||
|
-ms-transform: translate(-50%, 50%);
|
||||||
|
transform: translate(-50%, 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-header {
|
||||||
|
padding: 1.5em;
|
||||||
|
background: #fff;
|
||||||
|
color: #356593;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-header-text {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-body {
|
||||||
|
background: #fafbfc;
|
||||||
|
border-top: solid 1px #dde0e9;
|
||||||
|
padding: 1.25em 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-text {
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-buttons {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.h5p-confirmation-dialog-exit:visited,
|
||||||
|
button.h5p-confirmation-dialog-exit:link,
|
||||||
|
button.h5p-confirmation-dialog-exit {
|
||||||
|
position: absolute;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 2.5em;
|
||||||
|
top: -0.9em;
|
||||||
|
right: -1.15em;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.h5p-confirmation-dialog-exit:focus,
|
||||||
|
button.h5p-confirmation-dialog-exit:hover {
|
||||||
|
color: #E4ECF5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-exit:before {
|
||||||
|
font-family: "H5P";
|
||||||
|
content: "\e890";
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-core-button.h5p-confirmation-dialog-confirm-button {
|
||||||
|
padding-left: 0.75em;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-core-button.h5p-confirmation-dialog-confirm-button:before {
|
||||||
|
content: "\e601";
|
||||||
|
margin-top: -6px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-popup.offline .h5p-confirmation-dialog-buttons {
|
||||||
|
float: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-popup.offline .count-down {
|
||||||
|
font-family: Arial;
|
||||||
|
margin-top: 0.15em;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-confirmation-dialog-popup.offline .h5p-confirmation-dialog-confirm-button:before {
|
||||||
|
content: "\e90b";
|
||||||
|
font-weight: normal;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.throbber-wrapper {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
background: rgba(44, 44, 44, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.throbber-wrapper.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.throbber-wrapper .throbber-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.throbber-wrapper .sending-requests-throbber{
|
||||||
|
position: absolute;
|
||||||
|
top: 7em;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.throbber-wrapper .sending-requests-throbber:before {
|
||||||
|
display: block;
|
||||||
|
font-family: 'H5P';
|
||||||
|
content: "\e90b";
|
||||||
|
color: white;
|
||||||
|
font-size: 10em;
|
||||||
|
animation: request-throbber 1.5s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes request-throbber {
|
||||||
|
from {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: rotate(359deg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
button.h5p-core-button:visited,
|
||||||
|
button.h5p-core-button:link,
|
||||||
|
button.h5p-core-button {
|
||||||
|
font-family: "Open Sans", sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: 1.2;
|
||||||
|
padding: 0.5em 1.25em;
|
||||||
|
border-radius: 2em;
|
||||||
|
|
||||||
|
background: #2579c6;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
text-shadow: none;
|
||||||
|
vertical-align: baseline;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
-webkit-transition: initial;
|
||||||
|
transition: initial;
|
||||||
|
}
|
||||||
|
button.h5p-core-button:focus {
|
||||||
|
background: #1f67a8;
|
||||||
|
}
|
||||||
|
button.h5p-core-button:hover {
|
||||||
|
background: rgba(31, 103, 168, 0.83);
|
||||||
|
}
|
||||||
|
button.h5p-core-button:active {
|
||||||
|
background: #104888;
|
||||||
|
}
|
||||||
|
button.h5p-core-button:before {
|
||||||
|
font-family: 'H5P';
|
||||||
|
padding-right: 0.15em;
|
||||||
|
font-size: 1.5em;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 0.7;
|
||||||
|
}
|
||||||
|
button.h5p-core-cancel-button:visited,
|
||||||
|
button.h5p-core-cancel-button:link,
|
||||||
|
button.h5p-core-cancel-button {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
color: #a00;
|
||||||
|
margin-right: 1em;
|
||||||
|
font-size: 1em;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
button.h5p-core-cancel-button:hover,
|
||||||
|
button.h5p-core-cancel-button:focus {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #e40000;
|
||||||
|
}
|