diff --git a/locales/ar-arb/messages.po b/locales/ar-arb/messages.po index 83a5d96..6cd5c59 100644 --- a/locales/ar-arb/messages.po +++ b/locales/ar-arb/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: ar-arb\n" diff --git a/locales/ar-arb/taogroup.rdf.po b/locales/ar-arb/taogroup.rdf.po index 16578cf..c44f994 100644 --- a/locales/ar-arb/taogroup.rdf.po +++ b/locales/ar-arb/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: ar-arb\n" diff --git a/locales/da-DK/messages.po b/locales/da-DK/messages.po index 82e591e..51abd00 100644 --- a/locales/da-DK/messages.po +++ b/locales/da-DK/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: da-DK\n" diff --git a/locales/da-DK/taogroup.rdf.po b/locales/da-DK/taogroup.rdf.po index 5eb869a..674daaa 100644 --- a/locales/da-DK/taogroup.rdf.po +++ b/locales/da-DK/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: da-DK\n" diff --git a/locales/de-DE/messages.po b/locales/de-DE/messages.po index 2a3f0ff..88ef9a8 100644 --- a/locales/de-DE/messages.po +++ b/locales/de-DE/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: de-DE\n" diff --git a/locales/de-DE/taogroup.rdf.po b/locales/de-DE/taogroup.rdf.po index 31da49d..ed2e046 100644 --- a/locales/de-DE/taogroup.rdf.po +++ b/locales/de-DE/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: de-DE\n" diff --git a/locales/el-GR/messages.po b/locales/el-GR/messages.po index 3c90aa7..896ec0c 100644 --- a/locales/el-GR/messages.po +++ b/locales/el-GR/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: el-GR\n" diff --git a/locales/el-GR/taogroup.rdf.po b/locales/el-GR/taogroup.rdf.po index 09ad7a1..a68ddf5 100644 --- a/locales/el-GR/taogroup.rdf.po +++ b/locales/el-GR/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: el-GR\n" diff --git a/locales/en-US/messages.po b/locales/en-US/messages.po index dda61f4..88baceb 100644 --- a/locales/en-US/messages.po +++ b/locales/en-US/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: en-US\n" diff --git a/locales/en-US/taogroup.rdf.po b/locales/en-US/taogroup.rdf.po index a40e6e2..2244bea 100644 --- a/locales/en-US/taogroup.rdf.po +++ b/locales/en-US/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: en-US\n" diff --git a/locales/es-ES/messages.po b/locales/es-ES/messages.po index 56f3846..2298a75 100644 --- a/locales/es-ES/messages.po +++ b/locales/es-ES/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: es-ES\n" diff --git a/locales/es-ES/taogroup.rdf.po b/locales/es-ES/taogroup.rdf.po index b2cacf5..7e389fa 100644 --- a/locales/es-ES/taogroup.rdf.po +++ b/locales/es-ES/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: es-ES\n" diff --git a/locales/fi-FI/messages.po b/locales/fi-FI/messages.po index 79def34..f4f37fc 100644 --- a/locales/fi-FI/messages.po +++ b/locales/fi-FI/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: fi-FI\n" diff --git a/locales/fi-FI/taogroup.rdf.po b/locales/fi-FI/taogroup.rdf.po index 5902159..c2e1d94 100644 --- a/locales/fi-FI/taogroup.rdf.po +++ b/locales/fi-FI/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: fi-FI\n" diff --git a/locales/fr-CA/messages.po b/locales/fr-CA/messages.po index dd2fb02..482d7ff 100644 --- a/locales/fr-CA/messages.po +++ b/locales/fr-CA/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: fr-CA\n" diff --git a/locales/fr-CA/taogroup.rdf.po b/locales/fr-CA/taogroup.rdf.po index 411fa0f..6840969 100644 --- a/locales/fr-CA/taogroup.rdf.po +++ b/locales/fr-CA/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: fr-CA\n" diff --git a/locales/fr-FR/messages.po b/locales/fr-FR/messages.po index 8cc118a..f6c6140 100644 --- a/locales/fr-FR/messages.po +++ b/locales/fr-FR/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: fr-FR\n" diff --git a/locales/fr-FR/taogroup.rdf.po b/locales/fr-FR/taogroup.rdf.po index dcae88a..4b9b0e2 100644 --- a/locales/fr-FR/taogroup.rdf.po +++ b/locales/fr-FR/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: fr-FR\n" diff --git a/locales/hu-HU/messages.po b/locales/hu-HU/messages.po index 54e5a53..cc979b5 100644 --- a/locales/hu-HU/messages.po +++ b/locales/hu-HU/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: hu-HU\n" diff --git a/locales/hu-HU/taogroup.rdf.po b/locales/hu-HU/taogroup.rdf.po index 02aa595..00f9590 100644 --- a/locales/hu-HU/taogroup.rdf.po +++ b/locales/hu-HU/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: hu-HU\n" diff --git a/locales/id-ID/messages.po b/locales/id-ID/messages.po index 5323e4a..f7f2eaf 100644 --- a/locales/id-ID/messages.po +++ b/locales/id-ID/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: id-ID\n" diff --git a/locales/id-ID/taogroup.rdf.po b/locales/id-ID/taogroup.rdf.po index be5ed26..951dfd5 100644 --- a/locales/id-ID/taogroup.rdf.po +++ b/locales/id-ID/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: id-ID\n" diff --git a/locales/is-IS/messages.po b/locales/is-IS/messages.po index 12817ae..73e20cc 100644 --- a/locales/is-IS/messages.po +++ b/locales/is-IS/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: is-IS\n" diff --git a/locales/is-IS/taogroup.rdf.po b/locales/is-IS/taogroup.rdf.po index 53bb2da..f7b84c4 100644 --- a/locales/is-IS/taogroup.rdf.po +++ b/locales/is-IS/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: is-IS\n" diff --git a/locales/it-IT/messages.po b/locales/it-IT/messages.po index bff4d02..d9f3edc 100644 --- a/locales/it-IT/messages.po +++ b/locales/it-IT/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: it-IT\n" diff --git a/locales/it-IT/taogroup.rdf.po b/locales/it-IT/taogroup.rdf.po index bd2a4da..9a6471e 100644 --- a/locales/it-IT/taogroup.rdf.po +++ b/locales/it-IT/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: it-IT\n" diff --git a/locales/ja-JP/messages.po b/locales/ja-JP/messages.po index 56efe94..cd87d50 100644 --- a/locales/ja-JP/messages.po +++ b/locales/ja-JP/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: ja-JP\n" diff --git a/locales/ja-JP/taogroup.rdf.po b/locales/ja-JP/taogroup.rdf.po index 34d5711..3ec9c76 100644 --- a/locales/ja-JP/taogroup.rdf.po +++ b/locales/ja-JP/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: ja-JP\n" diff --git a/locales/lb-LU/messages.po b/locales/lb-LU/messages.po index 685acbc..3ad314b 100644 --- a/locales/lb-LU/messages.po +++ b/locales/lb-LU/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: lb-LU\n" diff --git a/locales/lb-LU/taogroup.rdf.po b/locales/lb-LU/taogroup.rdf.po index 4d4c486..228e49f 100644 --- a/locales/lb-LU/taogroup.rdf.po +++ b/locales/lb-LU/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: lb-LU\n" diff --git a/locales/nl-NL/messages.po b/locales/nl-NL/messages.po index 1f67a52..85a3bf4 100644 --- a/locales/nl-NL/messages.po +++ b/locales/nl-NL/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: nl-NL\n" diff --git a/locales/nl-NL/taogroup.rdf.po b/locales/nl-NL/taogroup.rdf.po index b4dda74..123123d 100644 --- a/locales/nl-NL/taogroup.rdf.po +++ b/locales/nl-NL/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: nl-NL\n" diff --git a/locales/pl-PL/messages.po b/locales/pl-PL/messages.po index 5df97a2..130033d 100644 --- a/locales/pl-PL/messages.po +++ b/locales/pl-PL/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: pl-PL\n" diff --git a/locales/pl-PL/taogroup.rdf.po b/locales/pl-PL/taogroup.rdf.po index e451627..b450d20 100644 --- a/locales/pl-PL/taogroup.rdf.po +++ b/locales/pl-PL/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: pl-PL\n" diff --git a/locales/pt-PT/messages.po b/locales/pt-PT/messages.po index 5236cf9..822354e 100644 --- a/locales/pt-PT/messages.po +++ b/locales/pt-PT/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: pt-PT\n" diff --git a/locales/pt-PT/taogroup.rdf.po b/locales/pt-PT/taogroup.rdf.po index f0aa0b0..465b8d6 100644 --- a/locales/pt-PT/taogroup.rdf.po +++ b/locales/pt-PT/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: pt-PT\n" diff --git a/locales/ru-RU/messages.po b/locales/ru-RU/messages.po index 874f655..e7cc56b 100644 --- a/locales/ru-RU/messages.po +++ b/locales/ru-RU/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: ru-RU\n" diff --git a/locales/ru-RU/taogroup.rdf.po b/locales/ru-RU/taogroup.rdf.po index 8efe065..e2f0c8f 100644 --- a/locales/ru-RU/taogroup.rdf.po +++ b/locales/ru-RU/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: ru-RU\n" diff --git a/locales/sk-SK/messages.po b/locales/sk-SK/messages.po index 452c3f6..8cac6b3 100644 --- a/locales/sk-SK/messages.po +++ b/locales/sk-SK/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: sk-SK\n" diff --git a/locales/sk-SK/taogroup.rdf.po b/locales/sk-SK/taogroup.rdf.po index 94bea95..a269683 100644 --- a/locales/sk-SK/taogroup.rdf.po +++ b/locales/sk-SK/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: sk-SK\n" diff --git a/locales/sv-SE/messages.po b/locales/sv-SE/messages.po index 7d8d3ca..46e0689 100644 --- a/locales/sv-SE/messages.po +++ b/locales/sv-SE/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: sv-SE\n" diff --git a/locales/sv-SE/taogroup.rdf.po b/locales/sv-SE/taogroup.rdf.po index 4429daf..d3cefa9 100644 --- a/locales/sv-SE/taogroup.rdf.po +++ b/locales/sv-SE/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: sv-SE\n" diff --git a/locales/uk-UA/messages.po b/locales/uk-UA/messages.po index 6b54029..eac8ab9 100644 --- a/locales/uk-UA/messages.po +++ b/locales/uk-UA/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: uk-UA\n" diff --git a/locales/uk-UA/taogroup.rdf.po b/locales/uk-UA/taogroup.rdf.po index e05fd80..8f1c801 100644 --- a/locales/uk-UA/taogroup.rdf.po +++ b/locales/uk-UA/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: uk-UA\n" diff --git a/locales/zh-CN/messages.po b/locales/zh-CN/messages.po index da0b7d3..77ec41b 100644 --- a/locales/zh-CN/messages.po +++ b/locales/zh-CN/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: zh-CN\n" diff --git a/locales/zh-CN/taogroup.rdf.po b/locales/zh-CN/taogroup.rdf.po index ca4f9ad..7d6bbf7 100644 --- a/locales/zh-CN/taogroup.rdf.po +++ b/locales/zh-CN/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: zh-CN\n" diff --git a/locales/zh-SG/messages.po b/locales/zh-SG/messages.po index e6ec309..4ea0bab 100644 --- a/locales/zh-SG/messages.po +++ b/locales/zh-SG/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: zh-SG\n" diff --git a/locales/zh-SG/taogroup.rdf.po b/locales/zh-SG/taogroup.rdf.po index 1d54dda..2770a14 100644 --- a/locales/zh-SG/taogroup.rdf.po +++ b/locales/zh-SG/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: zh-SG\n" diff --git a/locales/zh-TW/messages.po b/locales/zh-TW/messages.po index 9f23981..589ed54 100644 --- a/locales/zh-TW/messages.po +++ b/locales/zh-TW/messages.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: zh-TW\n" diff --git a/locales/zh-TW/taogroup.rdf.po b/locales/zh-TW/taogroup.rdf.po index 8692305..002e8fb 100644 --- a/locales/zh-TW/taogroup.rdf.po +++ b/locales/zh-TW/taogroup.rdf.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"Project-Id-Version: TAO 3.3.0-sprint101\n" -"PO-Revision-Date: 2019-05-10T14:31:21\n" +"Project-Id-Version: TAO 3.3.0-sprint106\n" +"PO-Revision-Date: 2019-07-19T08:12:25\n" "Last-Translator: TAO Translation Team \n" "MIME-Version: 1.0\n" "Language: zh-TW\n" diff --git a/manifest.php b/manifest.php index f476dc3..14aeeae 100755 --- a/manifest.php +++ b/manifest.php @@ -33,7 +33,7 @@ 'label' => 'Groups core extension', 'description' => 'TAO Groups extension', 'license' => 'GPL-2.0', - 'version' => '6.1.0', + 'version' => '6.1.1', 'author' => 'Open Assessment Technologies, CRP Henri Tudor', 'requires' => array( 'taoTestTaker' => '>=4.0.0', diff --git a/models/GroupsService.php b/models/GroupsService.php index 0f486c9..83ec6ad 100644 --- a/models/GroupsService.php +++ b/models/GroupsService.php @@ -25,9 +25,9 @@ use \core_kernel_classes_Class; use \core_kernel_classes_Property; use \core_kernel_classes_Resource; -use \tao_models_classes_ClassService; use oat\taoTestTaker\models\TestTakerService; use oat\oatbox\user\User; +use oat\tao\model\OntologyClassService; /** * Service methods to manage the Groups business models using the RDF API. @@ -37,8 +37,7 @@ * @package taoGroups */ -class GroupsService - extends tao_models_classes_ClassService +class GroupsService extends OntologyClassService { const CLASS_URI = 'http://www.tao.lu/Ontologies/TAOGroup.rdf#Group'; @@ -53,7 +52,7 @@ class GroupsService */ public function getRootClass() { - return new core_kernel_classes_Class(self::CLASS_URI); + return $this->getClass(self::CLASS_URI); } /** @@ -121,7 +120,8 @@ public function getUsers($groupUri) * @param core_kernel_classes_Resource $group * @return boolean */ - public function addUser($userUri, core_kernel_classes_Resource $group) { + public function addUser($userUri, core_kernel_classes_Resource $group) + { $user = new \core_kernel_classes_Resource($userUri); return $user->setPropertyValue(new core_kernel_classes_Property(self::PROPERTY_MEMBERS_URI), $group); } @@ -133,7 +133,8 @@ public function addUser($userUri, core_kernel_classes_Resource $group) { * @param core_kernel_classes_Resource $group * @return boolean */ - public function removeUser($userUri, core_kernel_classes_Resource $group) { + public function removeUser($userUri, core_kernel_classes_Resource $group) + { $user = new \core_kernel_classes_Resource($userUri); return $user->removePropertyValue(new core_kernel_classes_Property(self::PROPERTY_MEMBERS_URI), $group); } diff --git a/models/update/Updater.php b/models/update/Updater.php index a9bbe83..7b9c496 100644 --- a/models/update/Updater.php +++ b/models/update/Updater.php @@ -77,6 +77,6 @@ public function update($initialVersion) $this->setVersion('3.0.1'); } - $this->skip('3.0.1','6.1.0'); + $this->skip('3.0.1','6.1.1'); } } diff --git a/test/integration/GroupsTest.php b/test/integration/GroupsTest.php index fd63728..db8d5c5 100644 --- a/test/integration/GroupsTest.php +++ b/test/integration/GroupsTest.php @@ -41,40 +41,43 @@ */ class GroupsTest extends TaoPhpUnitTestRunner { - - /** - * @var GroupsService - */ - protected $groupsService = null; - - protected $subjectsService = null; - /** - * tests initialization - */ - public function setUp(){ + /** + * @var GroupsService + */ + protected $groupsService = null; + + protected $subjectsService = null; + + /** + * tests initialization + */ + public function setUp() + { TaoPhpUnitTestRunner::initTest(); - $this->subjectsService = TestTakerService::singleton(); - $this->groupsService = GroupsService::singleton(); - } - - /** - * Test the user service implementation - * @see tao_models_classes_ServiceFactory::get - * @see oat\taoGroups\models\GroupsService::__construct - */ - public function testService(){ - $this->assertIsA($this->subjectsService, '\tao_models_classes_Service'); - $this->assertIsA($this->groupsService, 'oat\taoGroups\models\GroupsService'); - } + $this->subjectsService = new TestTakerService(); + $this->groupsService = new GroupsService(); + } + + /** + * Test the user service implementation + * @see tao_models_classes_ServiceFactory::get + * @see oat\taoGroups\models\GroupsService::__construct + */ + public function testService() + { + $this->assertIsA($this->subjectsService, '\tao_models_classes_Service'); + $this->assertIsA($this->groupsService, 'oat\taoGroups\models\GroupsService'); + } /** * @return \core_kernel_classes_Class|null */ - public function testGroup() { - $groupClass = $this->groupsService->getRootClass(); - $this->assertIsA($groupClass, 'core_kernel_classes_Class'); - $this->assertEquals(GroupsService::CLASS_URI, $groupClass->getUri()); + public function testGroup() + { + $groupClass = $this->groupsService->getRootClass(); + $this->assertIsA($groupClass, 'core_kernel_classes_Class'); + $this->assertEquals(GroupsService::CLASS_URI, $groupClass->getUri()); return $groupClass; } @@ -84,11 +87,12 @@ public function testGroup() { * @param $group * @return \core_kernel_classes_Class */ - public function testSubGroup($groupClass) { - $subGroupLabel = 'subGroup class'; - $subGroup = $this->groupsService->createSubClass($groupClass, $subGroupLabel); - $this->assertIsA($subGroup, 'core_kernel_classes_Class'); - $this->assertEquals($subGroupLabel, $subGroup->getLabel()); + public function testSubGroup($groupClass) + { + $subGroupLabel = 'subGroup class'; + $subGroup = $this->groupsService->createSubClass($groupClass, $subGroupLabel); + $this->assertIsA($subGroup, 'core_kernel_classes_Class'); + $this->assertEquals($subGroupLabel, $subGroup->getLabel()); $this->assertTrue($this->groupsService->isGroupClass($subGroup)); @@ -101,11 +105,12 @@ public function testSubGroup($groupClass) { * @param $group * @return \core_kernel_classes_Resource */ - public function testGroupInstance($groupClass) { - $groupInstanceLabel = 'group instance'; - $groupInstance = $this->groupsService->createInstance($groupClass, $groupInstanceLabel); - $this->assertIsA($groupInstance, 'core_kernel_classes_Resource'); - $this->assertEquals($groupInstanceLabel, $groupInstance->getLabel()); + public function testGroupInstance($groupClass) + { + $groupInstanceLabel = 'group instance'; + $groupInstance = $this->groupsService->createInstance($groupClass, $groupInstanceLabel); + $this->assertIsA($groupInstance, 'core_kernel_classes_Resource'); + $this->assertEquals($groupInstanceLabel, $groupInstance->getLabel()); return $groupInstance; } @@ -115,18 +120,19 @@ public function testGroupInstance($groupClass) { * @param $subGroup * @return \core_kernel_classes_Class */ - public function testSubGroupInstance($subGroupClass) { - $subGroupInstanceLabel = 'subGroup instance'; - $subGroupInstance = $this->groupsService->createInstance($subGroupClass); - - $subGroupInstance->removePropertyValues(new core_kernel_classes_Property(OntologyRdfs::RDFS_LABEL)); - $subGroupInstance->setLabel($subGroupInstanceLabel); - $this->assertIsA($subGroupInstance, 'core_kernel_classes_Resource'); - $this->assertEquals($subGroupInstanceLabel, $subGroupInstance->getLabel()); - - $subGroupInstanceLabel2 = 'my sub group instance'; - $subGroupInstance->setLabel($subGroupInstanceLabel2); - $this->assertEquals($subGroupInstanceLabel2, $subGroupInstance->getLabel()); + public function testSubGroupInstance($subGroupClass) + { + $subGroupInstanceLabel = 'subGroup instance'; + $subGroupInstance = $this->groupsService->createInstance($subGroupClass); + + $subGroupInstance->removePropertyValues(new core_kernel_classes_Property(OntologyRdfs::RDFS_LABEL)); + $subGroupInstance->setLabel($subGroupInstanceLabel); + $this->assertIsA($subGroupInstance, 'core_kernel_classes_Resource'); + $this->assertEquals($subGroupInstanceLabel, $subGroupInstance->getLabel()); + + $subGroupInstanceLabel2 = 'my sub group instance'; + $subGroupInstance->setLabel($subGroupInstanceLabel2); + $this->assertEquals($subGroupInstanceLabel2, $subGroupInstance->getLabel()); return $subGroupInstance; } @@ -135,63 +141,67 @@ public function testSubGroupInstance($subGroupClass) { * @depends testGroupInstance * @param $groupInstance */ - public function testDeleteGroupInstance($groupInstance) { - $this->assertTrue($groupInstance->delete()); + public function testDeleteGroupInstance($groupInstance) + { + $this->assertTrue($groupInstance->delete()); } /** * @depends testSubGroupInstance * @param $subGroupInstance */ - public function testDeleteSubGroupInstance($subGroupInstance) { - $this->assertTrue($subGroupInstance->delete()); + public function testDeleteSubGroupInstance($subGroupInstance) + { + $this->assertTrue($subGroupInstance->delete()); } /** * @depends testSubGroup * @param $subGroup */ - public function testDeleteSubGroupClass($subGroup) { - $this->assertTrue($subGroup->delete()); + public function testDeleteSubGroupClass($subGroup) + { + $this->assertTrue($subGroup->delete()); } /** * * @author Lionel Lecaque, lionel@taotesting.com */ - public function testGetGroups(){ - $groupClass = GroupsService::singleton()->getRootClass(); - $this->assertTrue($this->groupsService->isGroupClass($groupClass)); - - $subject = $this->subjectsService->createInstance($this->subjectsService->getRootClass(),'testSubject'); - $oneGroup = $groupClass->createInstance('testGroupInstance'); - - $this->groupsService->addUser($subject->getUri(), $oneGroup); - $oneGroup2 = $groupClass->createInstance('testGroupInstance2'); - - $subclass = $groupClass->createSubClass('testGroupSubclass'); - $oneGroup3 = $subclass->createInstance('testSubGroupInstance'); - $this->groupsService->addUser($subject->getUri(), $oneGroup3); - - $generisUser = new \core_kernel_users_GenerisUser($subject); - $groups = $this->groupsService->getGroups($generisUser); - - $this->assertTrue(is_array($groups)); - $this->assertTrue(count($groups) == 2); - array_walk($groups, function (&$group) { - $group = $group->getUri(); - }); - $this->assertContains($oneGroup->getUri(), $groups); - $this->assertNotContains($oneGroup2->getUri(), $groups); - $this->assertContains($oneGroup3->getUri(), $groups); - - $this->assertTrue($this->groupsService->deleteGroup($oneGroup)); - $this->assertTrue($this->groupsService->deleteGroup($oneGroup2)); - $this->assertTrue($this->groupsService->deleteGroup($oneGroup3)); - - $this->assertTrue($this->groupsService->deleteClass($subclass)); - - $subject->delete(); - } + public function testGetGroups() + { + $groupClass = $this->groupsService->getRootClass(); + $this->assertTrue($this->groupsService->isGroupClass($groupClass)); + + $subject = $this->subjectsService->createInstance($this->subjectsService->getRootClass(),'testSubject'); + $oneGroup = $groupClass->createInstance('testGroupInstance'); + + $this->groupsService->addUser($subject->getUri(), $oneGroup); + $oneGroup2 = $groupClass->createInstance('testGroupInstance2'); + + $subclass = $groupClass->createSubClass('testGroupSubclass'); + $oneGroup3 = $subclass->createInstance('testSubGroupInstance'); + $this->groupsService->addUser($subject->getUri(), $oneGroup3); + + $generisUser = new \core_kernel_users_GenerisUser($subject); + $groups = $this->groupsService->getGroups($generisUser); + + $this->assertTrue(is_array($groups)); + $this->assertTrue(count($groups) == 2); + array_walk($groups, function (&$group) { + $group = $group->getUri(); + }); + $this->assertContains($oneGroup->getUri(), $groups); + $this->assertNotContains($oneGroup2->getUri(), $groups); + $this->assertContains($oneGroup3->getUri(), $groups); + + $this->assertTrue($this->groupsService->deleteGroup($oneGroup)); + $this->assertTrue($this->groupsService->deleteGroup($oneGroup2)); + $this->assertTrue($this->groupsService->deleteGroup($oneGroup3)); + + $this->assertTrue($this->groupsService->deleteClass($subclass)); + + $subject->delete(); + } } \ No newline at end of file diff --git a/views/js/loader/taoGroups.min.js b/views/js/loader/taoGroups.min.js index 6a59a52..5b302a4 100644 --- a/views/js/loader/taoGroups.min.js +++ b/views/js/loader/taoGroups.min.js @@ -1,2 +1,2 @@ -function _typeof(obj){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(obj){return typeof obj}:function(obj){return obj&&"function"==typeof Symbol&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj},_typeof(obj)}define("taoGroups/controller/routes",[],function(){'use strict';return{}}),function(global,factory){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=factory(require("lodash")):"function"==typeof define&&define.amd?define("core/format",["lodash"],factory):(global=global||self,global["core/format"]=factory(global._))}(this,function(_){'use strict';function format(message){var replacements=Array.prototype.slice.call(arguments,1);return _.reduce(message.match(pattern),function(acc,match,index){var replacement="";if(void 0!==replacements[index]){switch(match){case"%d":replacement=+replacements[index];break;case"%j":try{replacement=JSON.stringify(replacements[index]).replace(/"/g,"")}catch(e){}break;default:replacement=replacements[index];}message=message.replace(match,replacement)}return message},message)}_=_&&_.hasOwnProperty("default")?_["default"]:_;var pattern=/(%[sdj])/g;return format}),function(global,factory){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=factory(require("lodash"),require("context")):"function"==typeof define&&define.amd?define("util/url",["lodash","context"],factory):(global=global||self,global["util/url"]=factory(global._,global.context))}(this,function(_,context){'use strict';_=_&&_.hasOwnProperty("default")?_["default"]:_,context=context&&context.hasOwnProperty("default")?context["default"]:context;var parsers={absolute:/^(?:[a-z]+:)?\/\//i,base64:/^data:[^\/]+\/[^;]+(;charset=[\w]+)?;base64,/,query:/(?:^|&)([^&=]*)=?([^&]*)/g,url:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/};return{parse:function(url){var matches,keys=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","queryString","hash"],i=keys.length,parsed=Object.create({toString:function toString(){return this.source}});if(parsed.base64=parsers.base64.test(url),parsed.base64)parsed.source=url;else{for(matches=parsers.url.exec(url);i--;)parsed[keys[i]]=matches[i]||"";parsed.query={},parsed.queryString.replace(parsers.query,function($0,$1,$2){$1&&(parsed.query[$1]=$2)})}return parsed},isAbsolute:function(url){return"object"===_typeof(url)&&url.hasOwnProperty("source")?url.source!==url.relative:"string"==typeof url?parsers.absolute.test(url):void 0},isRelative:function(url){var absolute=this.isAbsolute(url);if("boolean"==typeof absolute)return!absolute},isBase64:function(url){return"object"===_typeof(url)&&url.hasOwnProperty("source")?url.base64:"string"==typeof url?parsers.base64.test(url):void 0},encodeAsXmlAttr:function(uri){return /[<>&']+/.test(uri)?encodeURIComponent(uri):uri},build:function(path,params){var url,hasQueryString,queryString="";return path&&(_.isString(path)&&(url=path),_.isArray(path)&&(url="",_.forEach(path,function(chunk){url+=/\/$/.test(url)&&/^\//.test(chunk)?chunk.substr(1):""===url||/\/$/.test(url)||/^\//.test(chunk)?chunk:"/"+chunk})),_.isPlainObject(params)&&(hasQueryString=-1=getLevelNum(minLevel)},loggerFactory=function loggerFactory(name,minLevel,fields){var baseRecord,logger;if(!_.isString(name)||_.isEmpty(name))throw new TypeError("A logger needs a name");return _.isPlainObject(minLevel)&&"undefined"==typeof field&&(fields=minLevel,minLevel=defaultLevel),baseRecord=_.defaults(fields||{},{name:name,pid:1,hostname:navigator.userAgent}),logger={log:function(level,recordFields,message){var record,err,rest=[],time=new Date().toISOString();if(!1!==loggerFactory.providers&&checkMinLevel(minLevel||defaultLevel,level))return _.isString(recordFields)||recordFields instanceof Error?(message=recordFields,recordFields={},rest=[].slice.call(arguments,2)):rest=[].slice.call(arguments,3),record={level:getLevel(level),v:0,time:time},checkMinLevel(levels.error,level)||message instanceof Error?(message instanceof Error?err=message:(message=_.isObject(message)?JSON.stringify(message):message,err=new Error(message)),record.msg=err.message,record.err=err):record.msg=format.apply(null,[message].concat(rest)),_.merge(record,baseRecord,recordFields),logQueue.push(record),loggerFactory.flush(),this},level:function level(value){return"undefined"==typeof value?getLevel(minLevel):(minLevel=getLevelNum(value),this)},child:function(childFields){return loggerFactory(name,minLevel,_.defaults(childFields,baseRecord))}},_.reduce(levels,function(target,level,levelName){return target[levelName]=_.partial(logger.log,level),target},logger)};return loggerFactory.levels=levels,loggerFactory.providers=!1,loggerFactory.load=function(providerConfigs){var self=this,modules=[];return this.providers=[],new Promise(function(resolve,reject){_.forEach(providerConfigs,function(providerConfig,providerName){modules.push(providerName)}),require(modules,function(){var loadedProviders=[].slice.call(arguments);_.forEach(loadedProviders,function(provider,moduleKey){try{self.register(provider,providerConfigs[modules[moduleKey]])}catch(err){reject(err)}}),self.flush(),resolve()},reject)})},loggerFactory.register=function(provider,providerConfig){if(!_.isPlainObject(provider)||!_.isFunction(provider.log))throw new TypeError("A log provider is an object with a log method");provider.checkMinLevel=checkMinLevel,_.isFunction(provider.setConfig)&&provider.setConfig(providerConfig),this.providers=this.providers||[],this.providers.push(provider)},loggerFactory.flush=function(){_.isArray(this.providers)&&0config.lowSpaceRatio?(logger.warn("The browser storage is getting low "+usedRatio.toFixed(2)+"% used",estimate),logger.warn("We will attempt to clean oldster databases in persistent backends"),store.cleanUpSpace(config.invalidation.oldster,[],localStorageBackend),isIndexDBSupported&&store.cleanUpSpace(config.invalidation.oldster,[],indexedDBBackend)):logger.debug("Browser storage estimate : "+usedRatio.toFixed(2)+"% used",estimate))}).catch(function(err){logger.warn("Unable to retrieve quotas : "+err.message)}),quotaChecked=!0},isBackendApiValid=function(backend){return _.all(backendApi,function(method){return _.isFunction(backend[method])})},isStorageApiValid=function(storage){return _.all(storeApi,function(method){return _.isFunction(storage[method])})},loadBackend=function(preselectedBackend){return isIndexDBSupported().then(function(){var backend=preselectedBackend||(supportsIndexedDB?indexedDBBackend:localStorageBackend);return _.isFunction(backend)?isBackendApiValid(backend)?(backend!==memoryBackend&&checkQuotas(),backend):Promise.reject(new TypeError("This backend doesn't comply with the store backend API")):Promise.reject(new TypeError("No backend, no storage!"))})};store=function(storeName,preselectedBackend){return loadBackend(preselectedBackend).then(function(backend){var storeInstance=backend(storeName);return isStorageApiValid(storeInstance)?storeInstance:Promise.reject(new TypeError("The store doesn't comply with the Storage interface"))})},store.backends={localStorage:localStorageBackend,indexedDB:indexedDBBackend,memory:memoryBackend},store.removeAll=function(validate,preselectedBackend){return loadBackend(preselectedBackend).then(function(backend){return backend.removeAll(validate)})},store.cleanUpSpace=function(since,storeNamePattern,preselectedBackend){var tsThreshold,invalidate=function(storeName,storeEntry){return!!(storeName&&storeEntry)&&(!(storeNamePattern instanceof RegExp)||storeNamePattern.test(storeName))&&_.isNumber(storeEntry.lastOpen)&&_.isNumber(tsThreshold)&&storeEntry.lastOpen<=tsThreshold};return _.isNumber(since)&&0config.tokenTimeLimit)||this.remove(token.value)},expireOldTokens:function(){var self=this;return self.getTokens().then(function(tokens){return _.reduce(tokens,function(previousPromise,nextToken){return previousPromise.then(function(){return self.checkExpiry(nextToken)})},Promise.resolve()).then(function(){return!0})})}}}_=_&&_.hasOwnProperty("default")?_["default"]:_,Promise=Promise&&Promise.hasOwnProperty("default")?Promise["default"]:Promise,store=store&&store.hasOwnProperty("default")?store["default"]:store;var defaultConfig={maxSize:6,tokenTimeLimit:1440000};return tokenStoreFactory}),function(global,factory){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=factory(require("lodash"),require("module"),require("core/tokenStore"),require("core/promise"),require("core/promiseQueue")):"function"==typeof define&&define.amd?define("core/tokenHandler",["lodash","module","core/tokenStore","core/promise","core/promiseQueue"],factory):(global=global||self,global["core/tokenHandler"]=factory(global._,global.module,global["core/tokenStore"],global["core/promise"],global["core/promiseQueue"]))}(this,function(_,module,tokenStoreFactory,Promise,promiseQueue){'use strict';function tokenHandlerFactory(options){var tokenStore;return _.isString(options)&&(options={initialToken:options}),options=_.defaults({},options,defaults),tokenStore=tokenStoreFactory(options),{getToken:function(){var self=this,initialToken=options.initialToken,getFirstTokenValue=function(){return tokenStore.dequeue().then(function(currentToken){return currentToken?currentToken.value:null})};return initialToken?(options.initialToken=null,Promise.resolve(initialToken)):tokenStore.expireOldTokens().then(function(){return tokenStore.getSize()}).then(function(queueSize){return 0 under the MIT License\n */\n\n /**\n * Parse the given URL and create an object with each URL chunks.\n *\n * BE CAREFUL! This util is different from UrlParser.\n * This one works only from the given string, when UrlParser work from window.location.\n * It means UrlParser will resolve the host of a relative URL using the host of the current window.\n *\n * @param {String} url - the URL to parse\n * @returns {Object} parsedUrl with the properties available in key below and query that contains query string key/values.\n */\n parse: function parse(url) {\n var matches;\n var keys = [\n 'source',\n 'protocol',\n 'authority',\n 'userInfo',\n 'user',\n 'password',\n 'host',\n 'port',\n 'relative',\n 'path',\n 'directory',\n 'file',\n 'queryString',\n 'hash'\n ];\n var i = keys.length;\n var parsed = Object.create({\n toString: function() {\n return this.source;\n }\n });\n\n parsed.base64 = parsers.base64.test(url);\n\n if (parsed.base64) {\n parsed.source = url;\n } else {\n matches = parsers.url.exec(url);\n while (i--) {\n parsed[keys[i]] = matches[i] || '';\n }\n parsed.query = {};\n parsed.queryString.replace(parsers.query, function($0, $1, $2) {\n if ($1) {\n parsed.query[$1] = $2;\n }\n });\n }\n return parsed;\n },\n\n /**\n * Check whether an URL is absolute\n * @param {String|Object} url - the url to check. It can be a parsed URL (result of {@link util/url#parse})\n * @returns {Boolean|undefined} true if the url is absolute, or undefined if the URL cannot be checked\n */\n isAbsolute: function isAbsolute(url) {\n //url from parse\n if (typeof url === 'object' && url.hasOwnProperty('source')) {\n return url.source !== url.relative;\n }\n if (typeof url === 'string') {\n return parsers.absolute.test(url);\n }\n },\n\n /**\n * Check whether an URL is relative\n * @param {String|Object} url - the url to check. It can be a parsed URL (result of {@link util/url#parse})\n * @returns {Boolean|undefined} true if the url is relative, or undefined if the URL cannot be checked\n */\n isRelative: function isRelative(url) {\n var absolute = this.isAbsolute(url);\n if (typeof absolute === 'boolean') {\n return !absolute;\n }\n },\n\n /**\n * Check whether an URL is encoded in base64\n * @param {String|Object} url - the url to check. It can be a parsed URL (result of {@link util/url#parse})\n * @returns {Boolean|undefined} true if the url is base64, or undefined if the URL cannot be checked\n */\n isBase64: function isBase64(url) {\n if (typeof url === 'object' && url.hasOwnProperty('source')) {\n return url.base64;\n }\n if (typeof url === 'string') {\n return parsers.base64.test(url);\n }\n },\n\n /**\n * Determine whether encoding is required to match XML standards for attributes\n * @param {String} url\n * @returns {String}\n */\n encodeAsXmlAttr: function encodeAsXmlAttr(uri) {\n return /[<>&']+/.test(uri) ? encodeURIComponent(uri) : uri;\n },\n\n /**\n * Build a URL.\n * It does not take case about baseURL.\n *\n * @param {String|Array} path - the URL path. Clean concat if an array (no dupe slashes)\n * @param {Object} [params] - params to add to the URL\n * @returns {String} the URL\n */\n build: function build(path, params) {\n var url,\n queryString = '',\n hasQueryString;\n\n if (path) {\n if (_.isString(path)) {\n url = path;\n }\n if (_.isArray(path)) {\n url = '';\n _.forEach(path, function(chunk) {\n if (/\\/$/.test(url) && /^\\//.test(chunk)) {\n url += chunk.substr(1);\n } else if (url !== '' && !/\\/$/.test(url) && !/^\\//.test(chunk)) {\n url += '/' + chunk;\n } else {\n url += chunk;\n }\n });\n }\n if (_.isPlainObject(params)) {\n hasQueryString = url.indexOf('?') > -1;\n queryString = _.reduce(\n params,\n function(acc, value, key) {\n if (!_.isEmpty(acc) || hasQueryString) {\n acc += '&';\n }\n if (typeof value === 'object' && !_.isArray(value)) {\n _.forOwn(value, function(parameterValue, parameterName) {\n acc +=\n encodeURIComponent(key) +\n '[' +\n encodeURIComponent(parameterName) +\n ']=' +\n encodeURIComponent(parameterValue) +\n '&';\n });\n } else {\n acc += encodeURIComponent(key) + '=' + encodeURIComponent(value);\n }\n return acc;\n },\n queryString\n );\n if (!_.isEmpty(queryString)) {\n if (!hasQueryString) {\n url += '?';\n }\n url += queryString;\n }\n }\n }\n\n return url;\n },\n\n /**\n * Get the URL from a TAO controller route\n * @param {String} action - The controller's action\n * @param {String} controller - The controller's name\n * @param {String} extension - The controller's extension\n * @param {String} [rootUrl] - to change the root url, otherwise taken from context\n * @param {Object} [params] - params to add to the URL\n * @returns {String} the url\n *\n * @throws {TypeError} if one of the required parameter is missing or empty\n */\n route: function route(action, controller, extension, params, rootUrl) {\n var routeParts = [extension, controller, action];\n\n if (\n _.some(routeParts, function(value) {\n return _.isEmpty(value) || !_.isString(value);\n })\n ) {\n throw new TypeError('All parts are required to build an URL');\n }\n\n rootUrl = rootUrl || (context && context['root_url']);\n\n return this.build([rootUrl].concat(routeParts), params);\n }\n };\n\n return urlUtil;\n\n}));\n\n","(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n\ttypeof define === 'function' && define.amd ? define('core/promise',factory) :\n\t(global = global || self, global['core/promise'] = factory());\n}(this, function () { 'use strict';\n\n\t/*\n\t * This program is free software; you can redistribute it and/or\n\t * modify it under the terms of the GNU General Public License\n\t * as published by the Free Software Foundation; under version 2\n\t * of the License (non-upgradable).\n\t *\n\t * This program is distributed in the hope that it will be useful,\n\t * but WITHOUT ANY WARRANTY; without even the implied warranty of\n\t * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\t * GNU General Public License for more details.\n\t *\n\t * You should have received a copy of the GNU General Public License\n\t * along with this program; if not, write to the Free Software\n\t * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n\t *\n\t * Copyright (c) 2015-2019 Open Assessment Technologies SA\n\t */\n\n\treturn Promise;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash'), require('core/format'), require('core/promise')) :\n typeof define === 'function' && define.amd ? define('core/logger/api',['lodash', 'core/format', 'core/promise'], factory) :\n (global = global || self, global['core/logger/api'] = factory(global._, global['core/format'], global['core/promise']));\n}(this, function (_, format, Promise) { 'use strict';\n\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n format = format && format.hasOwnProperty('default') ? format['default'] : format;\n Promise = Promise && Promise.hasOwnProperty('default') ? Promise['default'] : Promise;\n\n /*\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2017-2019 (original work) Open Assessment Technologies SA;\n *\n */\n\n /**\n * The default level\n */\n var defaultLevel = 'info';\n\n var levels = {\n fatal: 60, // The service/app is going to stop or become unusable now. An operator should definitely look into this soon.\n error: 50, // Fatal for a particular request, but the service/app continues servicing other requests. An operator should look at this soon(ish).\n warn: 40, // A note on something that should probably be looked at by an operator eventually.\n info: 30, // Detail on regular operation.\n debug: 20, // Anything else, i.e. too verbose to be included in \"info\" level.\n trace: 10 // Logging from external libraries used by your app or very detailed application logging.\n };\n\n /**\n * Major version of the node-bunyan package (for compat)\n */\n var bunyanVersion = 0;\n\n /**\n * Where messages dwells\n */\n var logQueue = [];\n\n /**\n * Get the actual level as a string,\n * fallback to the default level.\n * @param {String|Number} [level] - the level\n * @returns {String} the level\n */\n var getLevel = function getLevel(level) {\n if (typeof level === 'undefined' || (_.isString(level) && !_.has(levels, level))) {\n return defaultLevel;\n }\n if (_.isNumber(level)) {\n return (\n _.findKey(levels, function(l) {\n return l === level;\n }) || defaultLevel\n );\n }\n return level;\n };\n\n /**\n * Get the actual level as a number,\n * fallback to the default level.\n * @param {String|Number} [level] - the level\n * @returns {Number} the level\n */\n var getLevelNum = function getLevelNum(level) {\n if (_.isString(level) && _.has(levels, level)) {\n return levels[level];\n }\n if (_.isNumber(level) && _.contains(levels, level)) {\n return level;\n }\n return levels[defaultLevel];\n };\n\n /**\n * Check whether the given level is above the minimum level threshold\n * @param {String|Number} minlevel- the minimum level\n * @param {String|Number} [level] - the level to check\n * @returns {Boolean}\n */\n var checkMinLevel = function checkMinLevel(minLevel, level) {\n return getLevelNum(level) >= getLevelNum(minLevel);\n };\n\n /**\n * Creates a logger instance\n *\n * @param {String} name - each logger instance MUST have a name\n * @param {String|Number} [minLevel] - the minimum logging level\n * @param {Object} [fields] - fields to add to all records\n *\n * @returns {logger} a new logger instance\n */\n var loggerFactory = function loggerFactory(name, minLevel, fields) {\n var baseRecord;\n var logger;\n\n if (!_.isString(name) || _.isEmpty(name)) {\n throw new TypeError('A logger needs a name');\n }\n\n if (_.isPlainObject(minLevel) && typeof field === 'undefined') {\n fields = minLevel;\n minLevel = defaultLevel;\n }\n\n baseRecord = _.defaults(fields || {}, {\n name: name,\n pid: 1, // only for compat\n hostname: navigator.userAgent\n });\n\n /**\n * Exposes a log method and one by log level, like logger.trace()\n *\n * @typedef logger\n */\n logger = {\n /**\n * Log messages by delegating to the provider\n *\n * @param {String|Number} level - the log level\n * @param {Object} [recordFields] - fields to add to the log record\n * @param {String|Error} message - the message to log\n * @param {...String} [rest] - rest parameters if the message is formatted\n * @returns {logger} chains\n */\n log: function log(level, recordFields, message) {\n var record;\n var err;\n var rest = [];\n var time = new Date().toISOString();\n\n //without providers or not the level, we don't log.\n if (loggerFactory.providers === false || !checkMinLevel(minLevel || defaultLevel, level)) {\n return;\n }\n\n if (_.isString(recordFields) || recordFields instanceof Error) {\n message = recordFields;\n recordFields = {};\n rest = [].slice.call(arguments, 2);\n } else {\n rest = [].slice.call(arguments, 3);\n }\n\n record = {\n level: getLevel(level),\n v: bunyanVersion,\n time: time\n };\n\n if (checkMinLevel(levels.error, level) || message instanceof Error) {\n if (message instanceof Error) {\n err = message;\n } else {\n message = _.isObject(message) ? JSON.stringify(message) : message;\n err = new Error(message);\n }\n\n record.msg = err.message;\n record.err = err;\n } else {\n record.msg = format.apply(null, [message].concat(rest));\n }\n\n _.merge(record, baseRecord, recordFields);\n\n logQueue.push(record);\n\n loggerFactory.flush();\n\n return this;\n },\n\n /**\n * Get/set the default level of the logger\n * @param {String|Number} [level] - set the default level\n * @returns {String|logger} the default level as a getter or chains as a setter\n */\n level: function(value) {\n if (typeof value !== 'undefined') {\n //update the partial function\n minLevel = getLevelNum(value);\n return this;\n }\n return getLevel(minLevel);\n },\n\n /**\n * Fork the current logger to create a child logger :\n * same config + child fields\n *\n * @param {Object} [childFields] - specialized child fields\n * @return {logger} the child logger\n */\n child: function child(childFields) {\n return loggerFactory(name, minLevel, _.defaults(childFields, baseRecord));\n }\n };\n\n //augment the logger by each level\n return _.reduce(\n levels,\n function reduceLogLevel(target, level, levelName) {\n target[levelName] = _.partial(logger.log, level);\n return target;\n },\n logger\n );\n };\n\n /**\n * Exposes the levels\n * @type {Object}\n */\n loggerFactory.levels = levels;\n\n /**\n * The list of providers bound to the logger.\n * @type {Boolean|Array} false means we don't log, array even empty we keep the logs\n */\n loggerFactory.providers = false;\n\n /**\n * Load providers from AMD modules\n * @param {Object} providerConfigs - provider's modules to load and register\n * @returns {Promise} resolves once modules are registered\n */\n loggerFactory.load = function load(providerConfigs) {\n var self = this;\n var modules = [];\n this.providers = [];\n\n return new Promise(function(resolve, reject) {\n //we can load the loggers dynamically\n _.forEach(providerConfigs, function(providerConfig, providerName) {\n modules.push(providerName);\n });\n require(modules, function() {\n var loadedProviders = [].slice.call(arguments);\n _.forEach(loadedProviders, function(provider, moduleKey) {\n try {\n self.register(provider, providerConfigs[modules[moduleKey]]);\n } catch (err) {\n reject(err);\n }\n });\n\n //flush messages that arrived before the providers are there\n self.flush();\n\n resolve();\n }, reject);\n });\n };\n\n /**\n * A logger provider provides with a way to log\n * @typedef {Object} loggerProvider\n * @property {Function} log - called with the message in parameter\n * @param {Object} providerConfig - provider's config\n * @throws TypeError\n */\n loggerFactory.register = function register(provider, providerConfig) {\n if (!_.isPlainObject(provider) || !_.isFunction(provider.log)) {\n throw new TypeError('A log provider is an object with a log method');\n }\n //propogate checkMinLevel function\n provider.checkMinLevel = checkMinLevel;\n if (_.isFunction(provider.setConfig)) {\n provider.setConfig(providerConfig);\n }\n this.providers = this.providers || [];\n this.providers.push(provider);\n };\n\n /**\n * Flush the messages queue into the providers\n */\n loggerFactory.flush = function flush() {\n if (_.isArray(this.providers) && this.providers.length > 0) {\n _.forEach(logQueue, function(message) {\n //forward to the providers\n _.forEach(loggerFactory.providers, function(provider) {\n provider.log.call(provider, message);\n });\n });\n //clear the queue\n logQueue = [];\n }\n };\n\n /**\n * Change the default level for all loggers\n * @param {String|Number} [level] - set the default level\n * @returns {String} the defined level\n */\n loggerFactory.setDefaultLevel = function setDefaultLevel(level) {\n defaultLevel = getLevel(level);\n return defaultLevel;\n };\n\n return loggerFactory;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash'), require('module'), require('core/logger/api')) :\n typeof define === 'function' && define.amd ? define('core/logger',['lodash', 'module', 'core/logger/api'], factory) :\n (global = global || self, global['core/logger'] = factory(global._, global.module, global['core/logger/api']));\n}(this, function (_, module, loggerFactory) { 'use strict';\n\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n module = module && module.hasOwnProperty('default') ? module['default'] : module;\n loggerFactory = loggerFactory && loggerFactory.hasOwnProperty('default') ? loggerFactory['default'] : loggerFactory;\n\n /*\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2017-2019 (original work) Open Assessment Technologies SA;\n *\n */\n\n /**\n * The default configuration if nothing\n * is found on the module config\n */\n var defaultConfig = {\n level: loggerFactory.levels.warn,\n loggers: {\n 'core/logger/console': {\n level: 'warn'\n }\n }\n };\n\n //the logger providers are configured through the AMD module config\n var config = _.defaults(module.config() || {}, defaultConfig);\n var logger = loggerFactory('core/logger');\n\n loggerFactory.setDefaultLevel(config.level);\n loggerFactory.load(config.loggers);\n\n /**\n * Catch uncaught errors\n * @param msg - error message\n * @param url - current url\n * @param line - line number\n * @param col - column number\n * @param error - error object (not all browsers support).\n * @return {boolean}\n */\n window.onerror = function(msg, url, line, col, error) {\n logger.error(\"Caught[via window.onerror]: '\" + msg + \"' from \" + url + ':' + line + ':' + col);\n };\n\n /**\n * Expose explicitely an direct way to activate log levels\n * @param {String|Number} level - the new log level\n * @returns {String} the defined level\n */\n window.setTaoLogLevel = function setTaoLogLevel(level) {\n return loggerFactory.setDefaultLevel(level);\n };\n\n return loggerFactory;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash'), require('core/promise'), require('lib/uuid'), require('core/logger')) :\n typeof define === 'function' && define.amd ? define('core/eventifier',['lodash', 'core/promise', 'lib/uuid', 'core/logger'], factory) :\n (global = global || self, global['core/eventifier'] = factory(global._, global['core/promise'], global['lib/uuid'], global['core/logger']));\n}(this, function (_, Promise, uuid, loggerFactory) { 'use strict';\n\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n Promise = Promise && Promise.hasOwnProperty('default') ? Promise['default'] : Promise;\n uuid = uuid && uuid.hasOwnProperty('default') ? uuid['default'] : uuid;\n loggerFactory = loggerFactory && loggerFactory.hasOwnProperty('default') ? loggerFactory['default'] : loggerFactory;\n\n /*\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2015-2019 (original work) Open Assessment Technologies SA;\n *\n */\n\n /**\n * All events have a namespace, this one is the default\n */\n var defaultNs = '@';\n\n /**\n * Namespace that targets all event\n */\n var globalNs = '*';\n\n /**\n * Create a logger\n */\n var eventifierLogger = loggerFactory('core/eventifier');\n\n /**\n * Get the list of events from an eventName string (ie, separated by spaces)\n * @param {String} eventNames - the event strings\n * @returns {String[]} the event list (no empty, no duplicate)\n */\n function getEventNames(eventNames) {\n if (!_.isString(eventNames) || _.isEmpty(eventNames)) {\n return [];\n }\n return _(eventNames.split(/\\s/g))\n .compact()\n .uniq()\n .value();\n }\n\n /**\n * Get the name part of an event name: the 'foo' of 'foo.bar'\n * @param {String} eventName - the name of the event\n * @returns {String} the name part\n */\n function getName(eventName) {\n if (eventName.indexOf('.') > -1) {\n return eventName.substr(0, eventName.indexOf('.'));\n }\n return eventName;\n }\n\n /**\n * Get the namespace part of an event name: the 'bar' of 'foo.bar'\n * @param {String} eventName - the name of the event\n * @returns {String} the namespace, that defaults to defaultNs\n */\n function getNamespace(eventName) {\n if (eventName.indexOf('.') > -1) {\n return eventName.substr(eventName.indexOf('.') + 1);\n }\n return defaultNs;\n }\n\n /**\n * Creates a new EventHandler object structure\n * @returns {Object} the handler structure\n */\n function getHandlerObject() {\n return {\n before: [],\n between: [],\n after: []\n };\n }\n\n /**\n * Makes the target an event emitter by delegating calls to the event API.\n * @param {Object} [target = {}] - the target object, a new plain object is created when omited.\n * @returns {Object} the target for conveniance\n */\n function eventifier(target) {\n var targetName;\n var logger;\n var stoppedEvents;\n\n //it stores all the handlers under ns/name/[handlers]\n var eventHandlers = {};\n\n /**\n * Get the handlers for an event type\n * @param {String} eventName - the event name, namespace included\n * @param {String} [type = 'between'] - the type of event in before, between and after\n * @returns {Function[]} the handlers\n */\n var getHandlers = function getHandlers(eventName, type) {\n var name = getName(eventName);\n var ns = getNamespace(eventName);\n\n type = type || 'between';\n eventHandlers[ns] = eventHandlers[ns] || {};\n eventHandlers[ns][name] = eventHandlers[ns][name] || getHandlerObject();\n return eventHandlers[ns][name][type];\n };\n\n /**\n * The API itself is just a placeholder, all methods will be delegated to a target.\n */\n var eventApi = {\n /**\n * Attach an handler to an event.\n * Calling `on` with the same eventName multiple times add callbacks: they\n * will all be executed.\n *\n * @example target.on('foo', function(bar){ console.log('Cool ' + bar) } );\n *\n * @this the target\n * @param {String} eventNames - the name of the event, or multiple events separated by a space\n * @param {Function} handler - the callback to run once the event is triggered\n * @returns {Object} the target object\n */\n on: function on(eventNames, handler) {\n if (_.isFunction(handler)) {\n _.forEach(getEventNames(eventNames), function(eventName) {\n getHandlers(eventName).push(handler);\n });\n }\n return this;\n },\n\n /**\n * Remove ALL handlers for an event.\n *\n * @example remove ALL\n * target.off('foo');\n *\n * @example remove targeted namespace\n * target.off('foo.bar');\n *\n * @example remove all handlers by namespace\n * target.off('.bar');\n *\n * @example remove all namespaces, keep non namespace\n * target.off('.*');\n *\n * @this the target\n * @param {String} eventNames - the name of the event, or multiple events separated by a space\n * @returns {Object} the target object\n */\n off: function off(eventNames) {\n _.forEach(getEventNames(eventNames), function(eventName) {\n var name = getName(eventName);\n var ns = getNamespace(eventName);\n var offNamespaces;\n\n if (ns && !name) {\n if (ns === globalNs) {\n offNamespaces = {};\n offNamespaces[defaultNs] = eventHandlers[defaultNs];\n eventHandlers = offNamespaces;\n } else {\n //off the complete namespace\n eventHandlers[ns] = {};\n }\n } else {\n _.forEach(eventHandlers, function(nsHandlers, namespace) {\n if (nsHandlers[name] && (ns === defaultNs || ns === namespace)) {\n nsHandlers[name] = getHandlerObject();\n }\n });\n }\n });\n return this;\n },\n\n /**\n * Remove ALL registered handlers\n *\n * @example remove ALL\n * target.removeAllListeners();\n *\n * @this the target\n * @returns {Object} the target object\n */\n removeAllListeners: function removeAllListeners() {\n // full erase\n eventHandlers = {};\n return this;\n },\n\n /**\n * Trigger an event.\n *\n * @example target.trigger('foo', 'Awesome');\n *\n * @this the target\n * @param {String} eventNames - the name of the event to trigger, or multiple events separated by a space\n * @returns {Object} the target object\n */\n trigger: function trigger(eventNames) {\n var self = this;\n var args = [].slice.call(arguments, 1);\n\n stoppedEvents = {};\n\n _.forEach(getEventNames(eventNames), function(eventName) {\n var ns = getNamespace(eventName);\n var name = getName(eventName);\n\n //check which ns needs to be executed and then merge the handlers to be executed\n var mergedHandlers = _(eventHandlers)\n .filter(function(nsHandlers, namespace) {\n return nsHandlers[name] && (ns === defaultNs || ns === namespace || namespace === globalNs);\n })\n .reduce(function(acc, nsHandlers) {\n acc.before = acc.before.concat(nsHandlers[name].before);\n acc.between = acc.between.concat(nsHandlers[name].between);\n acc.after = acc.after.concat(nsHandlers[name].after);\n return acc;\n }, getHandlerObject());\n\n logger.trace({ event: eventName, args: args }, 'trigger %s', eventName);\n\n if (mergedHandlers) {\n triggerAllHandlers(mergedHandlers, name, ns);\n }\n });\n\n function triggerAllHandlers(allHandlers, name, ns) {\n var event = {\n name: name,\n namespace: ns\n };\n\n if (allHandlers.before.length) {\n triggerBefore(allHandlers.before, event)\n .then(function() {\n triggerBetween(allHandlers, event);\n })\n .catch(function(err) {\n logHandlerStop('before', event, err);\n });\n } else {\n triggerBetween(allHandlers, event);\n }\n }\n\n function triggerBefore(handlers, event) {\n var pHandlers,\n beforeArgs = args.slice();\n\n // .before() handlers will get a special 'event' object as their first parameter\n beforeArgs.unshift(event);\n\n pHandlers = handlers.map(function(handler) {\n // .before() handlers use to return false to cancel the call stack\n // to maintain backward compatibility, we treat this case as a rejected Promise\n var value = shouldStop(event.name) ? false : handler.apply(self, beforeArgs);\n return value === false ? Promise.reject() : value;\n });\n\n return Promise.all(pHandlers);\n }\n\n function triggerBetween(allHandlers, event) {\n if (shouldStop(event.name)) {\n logHandlerStop('before', event); // .stopEvent() has been called in an async .before() callback\n } else {\n // trigger the event handlers\n triggerHandlers(allHandlers.between, event)\n .then(function() {\n triggerAfter(allHandlers.after, event);\n })\n .catch(function(err) {\n logHandlerStop('on', event, err);\n });\n }\n }\n\n function triggerAfter(handlers, event) {\n if (shouldStop(event.name)) {\n logHandlerStop('on', event); // .stopEvent() has been called in an async .on() callback\n } else {\n triggerHandlers(handlers, event)\n .then(function() {\n if (shouldStop(event.name)) {\n logHandlerStop('after', event); // .stopEvent() has been called in an async .after() callback\n }\n })\n .catch(function(err) {\n logHandlerStop('after', event, err);\n });\n }\n }\n\n function triggerHandlers(handlers, event) {\n var pHandlers;\n pHandlers = handlers.map(function(handler) {\n return shouldStop(event.name) ? Promise.reject() : handler.apply(self, args);\n });\n return Promise.all(pHandlers);\n }\n\n function logHandlerStop(stoppedIn, event, err) {\n if (err instanceof Error) {\n logger.error(err);\n }\n logger.trace({ err: err, event: event.name, stoppedIn: stoppedIn }, event.name + ' handlers stopped');\n }\n\n function shouldStop(name) {\n return stoppedEvents[name];\n }\n\n return this;\n },\n\n /**\n * Register a callback that is executed before the given event name\n * Provides an opportunity to cancel the execution of the event if one of the returned value is false\n *\n * @this the target\n * @param {String} eventNames - the name of the event, or multiple events separated by a space\n * @param {Function} handler - the callback to run once the event is triggered\n * @returns {Object} the target object\n */\n before: function before(eventNames, handler) {\n if (_.isFunction(handler)) {\n _.forEach(getEventNames(eventNames), function(eventName) {\n getHandlers(eventName, 'before').push(handler);\n });\n }\n return this;\n },\n\n /**\n * Register a callback that is executed after the given event name\n * The handlers will all be executed, no matter what\n *\n * @this the target\n * @param {String} eventNames - the name of the event, or multiple events separated by a space\n * @param {Function} handler - the callback to run once the event is triggered\n * @returns {Object} the target object\n */\n after: function after(eventNames, handler) {\n if (_.isFunction(handler)) {\n _.forEach(getEventNames(eventNames), function(eventName) {\n getHandlers(eventName, 'after').push(handler);\n });\n }\n return this;\n },\n\n /**\n * If triggered into an sync handler, this immediately cancels the execution of all following handlers\n * regardless of their category\n * If triggered asynchronously, this will only cancel the next category of handlers:\n * - .on() and .after() if triggered during a .before() handler\n * - .after() if triggered during a .on() handler\n * - nothing if triggered during a .after() handler\n * In an async context, you can also reject a Promise with the same results\n *\n * @param {string} name - of the event to stop\n */\n stopEvent: function stopEvent(name) {\n if (_.isString(name) && !_.isEmpty(name.trim())) {\n stoppedEvents[name.trim()] = true;\n }\n },\n\n /**\n * Spread events to another eventifier object.\n * So when an event is triggered on the current target,\n * it get's triggered on the destination too.\n *\n * Be careful, the forward will be triggered only if the event reach the `on` steps\n * (it can be canceled by a before).\n *\n * @param {eventifier} destination - the destination emitter\n * @param {String|String[]} eventNames - the list of events to forward\n * @returns {Object} target - chains\n */\n spread: function spread(destination, eventNames) {\n var self = this;\n if (destination && _.isFunction(destination.trigger)) {\n if (_.isString(eventNames)) {\n eventNames = getEventNames(eventNames);\n }\n _.forEach(eventNames, function(eventName) {\n self.on(eventName, function forwardEventTo() {\n var args = [eventName].concat([].slice.call(arguments));\n\n destination.trigger.apply(destination, args);\n });\n });\n }\n return this;\n }\n };\n\n target = target || {};\n\n //try to get something that looks like a name, an id or generate one only for logging purposes\n targetName = target.name || target.id || target.serial || uuid(6);\n\n //create a child logger per eventifier\n logger = eventifierLogger.child({ target: targetName });\n\n _(eventApi)\n .functions()\n .forEach(function(method) {\n if (_.isFunction(target[method])) {\n eventifierLogger.warn('The target object has already a method named ' + method, target);\n }\n target[method] = function delegate() {\n var args = [].slice.call(arguments);\n return eventApi[method].apply(target, args);\n };\n });\n\n return target;\n }\n\n return eventifier;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash'), require('core/promise'), require('core/eventifier'), require('lib/uuid')) :\n typeof define === 'function' && define.amd ? define('core/promiseQueue',['lodash', 'core/promise', 'core/eventifier', 'lib/uuid'], factory) :\n (global = global || self, global['core/promiseQueue'] = factory(global._, global['core/promise'], global['core/eventifier'], global['lib/uuid']));\n}(this, function (_, Promise, eventifier, uuid) { 'use strict';\n\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n Promise = Promise && Promise.hasOwnProperty('default') ? Promise['default'] : Promise;\n eventifier = eventifier && eventifier.hasOwnProperty('default') ? eventifier['default'] : eventifier;\n uuid = uuid && uuid.hasOwnProperty('default') ? uuid['default'] : uuid;\n\n /*\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2017-2019 Open Assessment Technologies SA\n */\n\n /**\n * Creates a new promise queue\n * @returns {promiseQueue}\n */\n function promiseQueueFactory() {\n //where we keep the pending promises\n var queue = {};\n\n var getId = function getId() {\n var id = 'promise-' + uuid(6);\n if (typeof queue[id] === 'undefined') {\n return id;\n }\n return getId();\n };\n\n /**\n * @typedef {promiseQueue}\n */\n return {\n /**\n * Just add another promise to the queue\n * @param {Promise} promise\n * @return {promiseQueue} chains\n */\n add: function add(promise) {\n queue[getId()] = promise;\n return this;\n },\n\n /**\n * Get the queue values\n * @returns {Promise[]} the array of promises in the queue\n */\n getValues: function getValues() {\n return _.values(queue);\n },\n\n /**\n * Empty the queue\n * @return {promiseQueue} chains\n */\n clear: function clear() {\n queue = {};\n return this;\n },\n\n /**\n * Run the given promise at the end of the queue,\n * @param {Function} promiseFn - a function that returns a promise\n * @returns {Promise}\n */\n serie: function serie(promiseFn) {\n var id = getId();\n\n //the actual queue to execute before running the given promise\n var currentQueue = this.getValues();\n\n //use an emitter to notify the promise fulfillment, internally.\n var emitter = eventifier();\n\n //add a waiting promise into the queue (for others who are calling the queue)\n queue[id] = new Promise(function(resolve) {\n emitter.on('fulfilled', resolve);\n });\n\n //wait for the queue,\n //then run the given promise\n //and resolve the waiting promise (for others)\n return Promise.all(currentQueue)\n .then(function() {\n if (_.isFunction(promiseFn)) {\n return promiseFn();\n }\n })\n .then(function(data) {\n emitter.trigger('fulfilled');\n delete queue[id];\n return data;\n })\n .catch(function(err) {\n queue = {};\n throw err;\n });\n }\n };\n }\n\n return promiseQueueFactory;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash'), require('core/promise'), require('core/promiseQueue'), require('lib/uuid')) :\n typeof define === 'function' && define.amd ? define('core/store/localstorage',['lodash', 'core/promise', 'core/promiseQueue', 'lib/uuid'], factory) :\n (global = global || self, global['core/store/localstorage'] = factory(global._, global['core/promise'], global['core/promiseQueue'], global['lib/uuid']));\n}(this, function (_, Promise, promiseQueue, uuid) { 'use strict';\n\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n Promise = Promise && Promise.hasOwnProperty('default') ? Promise['default'] : Promise;\n promiseQueue = promiseQueue && promiseQueue.hasOwnProperty('default') ? promiseQueue['default'] : promiseQueue;\n uuid = uuid && uuid.hasOwnProperty('default') ? uuid['default'] : uuid;\n\n /**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2016-2019 (original work) Open Assessment Technologies SA ;\n */\n\n /**\n * Prefix all databases\n * @type {String}\n */\n var prefix = 'tao-store-';\n\n /**\n * Alias to the Storage API\n * @type {Storage}\n */\n var storage = window.localStorage;\n\n /**\n * The name of the store that contains the index of known stores.\n * @type {String}\n */\n var knownStoresName = 'index';\n\n /**\n * The name of the store that contains the store id\n * @type {String}\n */\n var idStoreName = 'id';\n\n var writingQueue = promiseQueue();\n\n /**\n * Set an entry into a store\n * @param {String} storeName - unprefixed store name\n * @param {String} key - entry key\n * @param {*} value - the value to set\n * @returns {Promise}\n */\n var setEntry = function setEntry(storeName, key, value) {\n return new Promise(function(resolve, reject){\n try{\n storage.setItem(prefix + storeName + '.' + key, JSON.stringify(value));\n resolve(true);\n } catch(ex){\n reject(ex);\n }\n });\n };\n\n /**\n * Get an entry from a store\n * @param {String} storeName - unprefixed store name\n * @param {String} key - entry key\n * @returns {Promise<*>} resolves with the value\n */\n var getEntry = function getEntry(storeName, key) {\n return new Promise(function(resolve, reject){\n var value;\n try{\n value = storage.getItem(prefix + storeName + '.' + key);\n if(value === null){\n resolve();\n } else {\n resolve(JSON.parse(value));\n }\n } catch(ex){\n reject(ex);\n }\n });\n };\n\n /**\n * Gets access to the store that contains the index of known stores.\n * @returns {Promise}\n */\n var getKnownStores = function getKnownStores() {\n return getEntry(knownStoresName, 'stores');\n };\n\n /**\n * Adds a store into the index of known stores.\n * @param {String} storeName\n * @returns {Promise}\n */\n var registerStore = function registerStore(storeName) {\n return getKnownStores()\n .then(function(stores){\n stores = stores || {};\n stores[storeName] = {\n name : storeName,\n lastOpen : Date.now()\n };\n return setEntry(knownStoresName, 'stores', stores);\n })\n ;\n };\n\n /**\n * Removes a store from the index of known stores.\n * @param {String} storeName\n * @returns {Promise}\n */\n var unregisterStore = function unregisterStore(storeName) {\n return getKnownStores()\n .then(function(stores){\n stores = stores || {};\n delete stores[storeName];\n return setEntry(knownStoresName, 'stores', stores);\n })\n ;\n };\n\n /**\n * Open and access a store\n * @param {String} storeName - the store name to open\n * @returns {Object} the store backend\n * @throws {TypeError} without a storeName\n */\n var localStorageBackend = function localStorageBackend(storeName){\n\n var name;\n var registered = false;\n\n var openStore = function openStore(){\n if(registered){\n return Promise.resolve();\n }\n return registerStore(storeName)\n .then(function(){\n registered = true;\n });\n };\n if(_.isEmpty(storeName) || !_.isString(storeName)){\n throw new TypeError('The store name is required');\n }\n\n //prefix all storage entries to avoid global keys confusion\n name = prefix + storeName + '.';\n\n\n /**\n * The store\n */\n return {\n\n /**\n * Get an item with the given key\n * @param {String} key\n * @returns {Promise} with the result in resolve, undefined if nothing\n */\n getItem : function getItem(key){\n return writingQueue.serie(function(){\n return openStore().then(function(){\n return getEntry(storeName, key);\n });\n });\n },\n\n /**\n * Set an item with the given key\n * @param {String} key - the item key\n * @param {*} value - the item value\n * @returns {Promise} with true in resolve if added/updated\n */\n setItem : function setItem(key, value){\n return writingQueue.serie(function(){\n return openStore().then(function(){\n return setEntry(storeName, key, value);\n });\n });\n },\n\n /**\n * Remove an item with the given key\n * @param {String} key - the item key\n * @returns {Promise} with true in resolve if removed\n */\n removeItem : function removeItem(key){\n return writingQueue.serie(function(){\n return openStore().then(function(){\n storage.removeItem(name + key);\n return true;\n });\n });\n },\n\n /**\n * Get all store items\n * @returns {Promise} with a collection of items\n */\n getItems: function getItems() {\n var keyPattern = new RegExp('^' + name);\n return writingQueue.serie(function(){\n return openStore().then(function(){\n return _(storage)\n .map(function(entry, index){\n return storage.key(index);\n })\n .filter(function(key){\n return keyPattern.test(key);\n })\n .reduce(function(acc, key){\n var value;\n var exposedKey = key.replace(name, '');\n try {\n value = storage.getItem(key);\n if(value !== null){\n acc[exposedKey] = JSON.parse(value);\n }\n }\n catch(ex){\n acc[exposedKey] = null;\n }\n return acc;\n }, {});\n });\n });\n },\n\n /**\n * Clear the current store\n * @returns {Promise} with true in resolve once cleared\n */\n clear : function clear(){\n var keyPattern = new RegExp('^' + name);\n return writingQueue.serie(function(){\n return openStore().then(function(){\n _(storage)\n .map(function(entry, index){\n return storage.key(index);\n })\n .filter(function(key){\n return keyPattern.test(key);\n })\n .forEach(function(key){\n storage.removeItem(key);\n });\n return true;\n });\n });\n },\n\n /**\n * Delete the database related to the current store\n * @returns {Promise} with true in resolve once cleared\n */\n removeStore : function removeStore() {\n return this.clear().then(function(){\n return unregisterStore(storeName);\n });\n }\n };\n };\n\n /**\n * Removes all storage\n * @param {Function} [validate] - An optional callback that validates the store to delete\n * @returns {Promise} with true in resolve once cleaned\n */\n localStorageBackend.removeAll = function removeAll(validate) {\n if (!_.isFunction(validate)) {\n validate = null;\n }\n return getKnownStores().then(function(stores){\n var removing = _(stores)\n .filter(function(store, storeName){\n return validate ? validate(storeName, store) : true;\n })\n .map(function(store){\n if(store && store.name){\n return localStorageBackend(store.name).removeStore();\n }\n return Promise.resolve();\n })\n .value();\n\n return Promise.all(removing);\n });\n };\n\n\n /**\n * Get all stores\n * @param {Function} [validate] - An optional callback that validates the stores to retrieve\n * @returns {Promise} resolves with the list of stores\n */\n localStorageBackend.getAll = function getAll(validate) {\n return getKnownStores().then(function(stores){\n return _(stores)\n .filter(function(store, storeName){\n return validate ? validate(storeName, store) : true;\n })\n .map(function(store){\n return store.name;\n })\n .value();\n });\n };\n\n /**\n * Get the identifier of the storage\n * @returns {Promise} that resolves with the store identifier\n */\n localStorageBackend.getStoreIdentifier = function getStoreIdentifier(){\n var idStore = localStorageBackend(idStoreName);\n\n //we use the storeName also as the id\n return idStore.getItem(idStoreName).then(function(id){\n if(!_.isEmpty(id)){\n return id;\n }\n id = uuid();\n\n return idStore.setItem(idStoreName, id).then(function(){\n return id;\n });\n });\n };\n\n return localStorageBackend;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash'), require('core/promise'), require('lib/store/idbstore'), require('lib/uuid')) :\n typeof define === 'function' && define.amd ? define('core/store/indexeddb',['lodash', 'core/promise', 'lib/store/idbstore', 'lib/uuid'], factory) :\n (global = global || self, global['core/store/indexeddb'] = factory(global._, global['core/promise'], global['lib/store/idbstore'], global['lib/uuid']));\n}(this, function (_, Promise, IDBStore, uuid) { 'use strict';\n\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n Promise = Promise && Promise.hasOwnProperty('default') ? Promise['default'] : Promise;\n IDBStore = IDBStore && IDBStore.hasOwnProperty('default') ? IDBStore['default'] : IDBStore;\n uuid = uuid && uuid.hasOwnProperty('default') ? uuid['default'] : uuid;\n\n /**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2016-2019 (original work) Open Assessment Technologies SA ;\n */\n\n /**\n * Prefix all databases\n * @type {String}\n */\n var prefix = 'tao-store-';\n\n /**\n * Access to the index of known stores.\n * This index is needed to maintain the list of stores created by TAO, in order to apply an auto clean up.\n * @type {Promise}\n */\n var knownStores;\n\n /**\n * The name of the store that contains the index of known stores.\n * @type {String}\n */\n var knownStoresName = 'index';\n\n /**\n * The name of the store that contains the store id\n * @type {String}\n */\n var idStoreName = 'id';\n\n /**\n * Check if we're using the v2 of IndexedDB\n * @type {Boolean}\n */\n var isIndexedDB2 = 'getAll' in IDBObjectStore.prototype;\n\n /**\n * Opens a store\n * @returns {Promise} with store instance in resolve\n */\n var openStore = function openStore(storeName) {\n return new Promise(function(resolve, reject) {\n var store = new IDBStore({\n dbVersion: 1,\n storeName: storeName,\n storePrefix: prefix,\n keyPath: 'key',\n autoIncrement: true,\n onStoreReady: function onStoreReady() {\n // auto closes when the changed version reflects a DB deletion\n store.db.onversionchange = function onversionchange(e) {\n if (!e || !e.newVersion) {\n store.db.close();\n }\n };\n resolve(store);\n },\n onError: reject\n });\n });\n };\n\n /**\n * Sets an entry into a particular store\n * @param store\n * @param key\n * @param value\n * @returns {Promise}\n */\n var setEntry = function setEntry(store, key, value) {\n return new Promise(function(resolve, reject) {\n var entry = {\n key: key,\n value: value\n };\n var success = function success(returnKey) {\n resolve(returnKey === key);\n };\n store.put(entry, success, reject);\n });\n };\n\n /**\n * Gets an entry from a particular store\n * @param store\n * @param key\n * @returns {Promise}\n */\n var getEntry = function getEntry(store, key) {\n return new Promise(function(resolve, reject) {\n var success = function success(entry) {\n if (!entry || typeof entry.value === 'undefined') {\n return resolve(entry);\n }\n\n resolve(entry.value);\n };\n store.get(key, success, reject);\n });\n };\n\n /**\n * Get entries from a store\n * @param store\n * @returns {Promise} entries\n */\n var getEntries = function getEntries(store) {\n return new Promise(function(resolve, reject) {\n var success = function success(entries) {\n if (!_.isArray(entries)) {\n return resolve({});\n }\n\n resolve(\n _.reduce(\n entries,\n function(acc, entry) {\n if (entry.key && entry.value) {\n acc[entry.key] = entry.value;\n }\n return acc;\n },\n {}\n )\n );\n };\n store.getAll(success, reject);\n });\n };\n\n /**\n * Remove an entry from a particular store\n * @param store\n * @param key\n * @param value\n * @returns {Promise}\n */\n var removeEntry = function removeEntry(store, key) {\n return new Promise(function(resolve, reject) {\n var success = function success(result) {\n resolve(result !== false);\n };\n store.remove(key, success, reject);\n });\n };\n\n /**\n * Gets access to the store that contains the index of known stores.\n * @returns {Promise}\n */\n var getKnownStores = function getKnownStores() {\n if (!knownStores) {\n knownStores = openStore(knownStoresName);\n }\n return knownStores;\n };\n\n /**\n * Adds a store into the index of known stores.\n * @param {String} storeName\n * @returns {Promise}\n */\n var registerStore = function registerStore(storeName) {\n return getKnownStores().then(function(store) {\n return setEntry(store, storeName, {\n name: storeName,\n lastOpen: Date.now()\n });\n });\n };\n\n /**\n * Removes a store from the index of known stores.\n * @param {String} storeName\n * @returns {Promise}\n */\n var unregisterStore = function unregisterStore(storeName) {\n return getKnownStores().then(function(store) {\n return removeEntry(store, storeName);\n });\n };\n\n /**\n * Deletes a store, then removes it from the index of known stores.\n * @param store\n * @param storeName\n * @returns {Promise}\n */\n var deleteStore = function deleteStore(store, storeName) {\n return new Promise(function(resolve, reject) {\n var success = function success() {\n unregisterStore(storeName)\n .then(function() {\n resolve(true);\n })\n .catch(reject);\n };\n //with old implementation, deleting a store is\n //either unsupported or buggy\n if (isIndexedDB2) {\n store.deleteDatabase(success, reject);\n } else {\n store.clear(success, reject);\n }\n });\n };\n\n /**\n * Open and access a store\n * @param {String} storeName - the store name to open\n * @returns {Object} the store backend\n * @throws {TypeError} without a storeName\n */\n var indexDbBackend = function indexDbBackend(storeName) {\n //keep a ref of the running store\n var innerStore;\n\n /**\n * Get the store\n * @returns {Promise} with store instance in resolve\n */\n var getStore = function getStore() {\n if (!innerStore) {\n innerStore = openStore(storeName).then(function(store) {\n return registerStore(storeName).then(function() {\n return Promise.resolve(store);\n });\n });\n }\n return innerStore;\n };\n\n //keep a ref to the promise actually writing\n var writePromise;\n\n /**\n * Ensure write promises are executed in series\n * @param {Function} getWritingPromise - the function that run the promise\n * @returns {Promise} the original one\n */\n var ensureSerie = function ensureSerie(getWritingPromise) {\n //first promise, keep the ref\n if (!writePromise) {\n writePromise = getWritingPromise();\n return writePromise;\n }\n\n //create a wrapping promise\n return new Promise(function(resolve, reject) {\n //run the current request\n var runWrite = function() {\n var p = getWritingPromise();\n writePromise = p; //and keep the ref\n p.then(resolve).catch(reject);\n };\n\n //wait the previous to resolve or fail and run the current one\n writePromise.then(runWrite).catch(runWrite);\n });\n };\n\n if (_.isEmpty(storeName) || !_.isString(storeName)) {\n throw new TypeError('The store name is required');\n }\n\n /**\n * The store\n */\n return {\n /**\n * Get an item with the given key\n * @param {String} key\n * @returns {Promise} with the result in resolve, undefined if nothing\n */\n getItem: function getItem(key) {\n return ensureSerie(function getWritingPromise() {\n return getStore().then(function(store) {\n return getEntry(store, key);\n });\n });\n },\n\n /**\n * Set an item with the given key\n * @param {String} key - the item key\n * @param {*} value - the item value\n * @returns {Promise} with true in resolve if added/updated\n */\n setItem: function setItem(key, value) {\n return ensureSerie(function getWritingPromise() {\n return getStore().then(function(store) {\n return setEntry(store, key, value);\n });\n });\n },\n\n /**\n * Remove an item with the given key\n * @param {String} key - the item key\n * @returns {Promise} with true in resolve if removed\n */\n removeItem: function removeItem(key) {\n return ensureSerie(function getWritingPromise() {\n return getStore().then(function(store) {\n return removeEntry(store, key);\n });\n });\n },\n\n /**\n * Get all store items\n * @returns {Promise} with a collection of items\n */\n getItems: function getItems() {\n return ensureSerie(function getWritingPromise() {\n return getStore().then(function(store) {\n return getEntries(store);\n });\n });\n },\n\n /**\n * Clear the current store\n * @returns {Promise} with true in resolve once cleared\n */\n clear: function clear() {\n return ensureSerie(function getWritingPromise() {\n return getStore().then(function(store) {\n return new Promise(function(resolve, reject) {\n var success = function success() {\n resolve(true);\n };\n store.clear(success, reject);\n });\n });\n });\n },\n\n /**\n * Delete the database related to the current store\n * @returns {Promise} with true in resolve once cleared\n */\n removeStore: function removeStore() {\n return ensureSerie(function getWritingPromise() {\n return getStore().then(function(store) {\n return deleteStore(store, storeName);\n });\n });\n }\n };\n };\n\n /**\n * Removes all storage\n * @param {Function} [validate] - An optional callback that validates the store to delete\n * @param {Function} [backend] - An optional storage handler to use\n * @returns {Promise} with true in resolve once cleaned\n */\n indexDbBackend.removeAll = function removeAll(validate) {\n if (!_.isFunction(validate)) {\n validate = null;\n }\n return getKnownStores().then(function(store) {\n return new Promise(function(resolve, reject) {\n function cleanUp(entries) {\n var all = [];\n _.forEach(entries, function(entry) {\n var storeName = entry && entry.key;\n if (storeName) {\n all.push(\n openStore(storeName).then(function(storeToRemove) {\n if (!validate || validate(storeName, entry.value)) {\n return deleteStore(storeToRemove, storeName);\n }\n })\n );\n }\n });\n\n Promise.all(all)\n .then(resolve)\n .catch(reject);\n }\n store.getAll(cleanUp, reject);\n });\n });\n };\n\n /**\n * Get all storage\n * @param {Function} [validate] - An optional callback that validates the store to delete\n * @param {Function} [backend] - An optional storage handler to use\n * @returns {Promise} with true in resolve once cleaned\n */\n indexDbBackend.getAll = function getAll(validate) {\n if (!_.isFunction(validate)) {\n validate = function valid() {\n return true;\n };\n }\n return getKnownStores().then(function(store) {\n return new Promise(function(resolve, reject) {\n store.getAll(function(entries) {\n var storeNames = _(entries)\n .filter(function(entry) {\n return entry && entry.key && validate(entry.key, entry.value);\n })\n .map(function(entry) {\n return entry.key;\n })\n .value();\n\n return resolve(storeNames);\n }, reject);\n });\n });\n };\n\n /**\n * Get the identifier of the storage\n * @returns {Promise} that resolves with the store identifier\n */\n indexDbBackend.getStoreIdentifier = function getStoreIdentifier() {\n return openStore(idStoreName).then(function(store) {\n return getEntry(store, idStoreName).then(function(id) {\n if (!_.isEmpty(id)) {\n return id;\n }\n id = uuid();\n\n return setEntry(store, idStoreName, id).then(function() {\n return id;\n });\n });\n });\n };\n\n return indexDbBackend;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash'), require('core/promise'), require('lib/uuid')) :\n typeof define === 'function' && define.amd ? define('core/store/memory',['lodash', 'core/promise', 'lib/uuid'], factory) :\n (global = global || self, global['core/store/memory'] = factory(global._, global['core/promise'], global['lib/uuid']));\n}(this, function (_, Promise, uuid) { 'use strict';\n\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n Promise = Promise && Promise.hasOwnProperty('default') ? Promise['default'] : Promise;\n uuid = uuid && uuid.hasOwnProperty('default') ? uuid['default'] : uuid;\n\n /**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2017-2019 (original work) Open Assessment Technologies SA ;\n */\n\n /**\n * where data dwelves\n */\n var memoryStore = {};\n\n /**\n * The storage identifier\n */\n var idStore;\n\n /**\n * Open and access a store\n * @param {String} storeName - the store name to open\n * @returns {Object} the store backend\n * @throws {TypeError} without a storeName\n */\n var memoryStorageBackend = function memoryStorageBackend(storeName) {\n if (_.isEmpty(storeName) || !_.isString(storeName)) {\n throw new TypeError('The store name is required');\n }\n\n memoryStore[storeName] = memoryStore[storeName] || {};\n\n /**\n * The store\n */\n return {\n /**\n * Get an item with the given key\n * @param {String} key\n * @returns {Promise} with the result in resolve, undfined if nothing\n */\n getItem: function getItem(key) {\n if (!_.isPlainObject(memoryStore[storeName])) {\n return Promise.resolve();\n }\n return Promise.resolve(memoryStore[storeName][key]);\n },\n\n /**\n * Set an item with the given key\n * @param {String} key - the item key\n * @param {*} value - the item value\n * @returns {Promise} with true in resolve if added/updated\n */\n setItem: function setItem(key, value) {\n if (!_.isPlainObject(memoryStore[storeName])) {\n memoryStore[storeName] = {};\n }\n memoryStore[storeName][key] = value;\n return Promise.resolve(true);\n },\n\n /**\n * Remove an item with the given key\n * @param {String} key - the item key\n * @returns {Promise} with true in resolve if removed\n */\n removeItem: function removeItem(key) {\n memoryStore[storeName] = _.omit(memoryStore[storeName], key);\n return Promise.resolve(typeof memoryStore[storeName][key] === 'undefined');\n },\n\n /**\n * Get all store items\n * @returns {Promise} with a collection of items\n */\n getItems: function getItems() {\n return Promise.resolve(memoryStore[storeName]);\n },\n\n /**\n * Clear the current store\n * @returns {Promise} with true in resolve once cleared\n */\n clear: function clear() {\n memoryStore[storeName] = {};\n return Promise.resolve(true);\n },\n\n /**\n * Delete the database related to the current store\n * @returns {Promise} with true in resolve once cleared\n */\n removeStore: function removeStore() {\n memoryStore = _.omit(memoryStore, storeName);\n return Promise.resolve(typeof memoryStore[storeName] === 'undefined');\n }\n };\n };\n\n /**\n * Removes all storage\n * @param {Function} [validate] - An optional callback that validates the store to delete\n * @returns {Promise} with true in resolve once cleaned\n */\n memoryStorageBackend.removeAll = function removeAll(validate) {\n if (!_.isFunction(validate)) {\n validate = null;\n }\n memoryStore = _.omit(memoryStore, function(store, storeName) {\n return validate ? validate(storeName) : true;\n });\n return Promise.resolve(true);\n };\n\n /**\n * Get all stores\n * @param {Function} [validate] - An optional callback that validates the stores to retrieve\n * @returns {Promise} resolves with the list of stores\n */\n memoryStorageBackend.getAll = function getAll(validate) {\n var storeNames = [];\n if (!_.isFunction(validate)) {\n validate = null;\n }\n storeNames = _(memoryStore)\n .map(function(store, storeName) {\n return storeName;\n })\n .filter(function(storeName) {\n return validate ? validate(storeName) : true;\n })\n .value();\n\n return Promise.resolve(storeNames);\n };\n\n /**\n * Get the identifier of the storage\n * @returns {Promise} that resolves with the store identifier\n */\n memoryStorageBackend.getStoreIdentifier = function getStoreIdentifier() {\n //we use the storeName also as the id\n if (_.isEmpty(idStore)) {\n idStore = uuid();\n }\n return Promise.resolve(idStore);\n };\n\n return memoryStorageBackend;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash'), require('moment'), require('module'), require('core/logger'), require('core/promise'), require('core/store/localstorage'), require('core/store/indexeddb'), require('core/store/memory')) :\n typeof define === 'function' && define.amd ? define('core/store',['lodash', 'moment', 'module', 'core/logger', 'core/promise', 'core/store/localstorage', 'core/store/indexeddb', 'core/store/memory'], factory) :\n (global = global || self, global['core/store'] = factory(global._, global.moment, global.module, global['core/logger'], global['core/promise'], global['core/store/localstorage'], global['core/store/indexeddb'], global['core/store/memory']));\n}(this, function (_, moment, module, loggerFactory, Promise, localStorageBackend, indexedDBBackend, memoryBackend) { 'use strict';\n\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n moment = moment && moment.hasOwnProperty('default') ? moment['default'] : moment;\n module = module && module.hasOwnProperty('default') ? module['default'] : module;\n loggerFactory = loggerFactory && loggerFactory.hasOwnProperty('default') ? loggerFactory['default'] : loggerFactory;\n Promise = Promise && Promise.hasOwnProperty('default') ? Promise['default'] : Promise;\n localStorageBackend = localStorageBackend && localStorageBackend.hasOwnProperty('default') ? localStorageBackend['default'] : localStorageBackend;\n indexedDBBackend = indexedDBBackend && indexedDBBackend.hasOwnProperty('default') ? indexedDBBackend['default'] : indexedDBBackend;\n memoryBackend = memoryBackend && memoryBackend.hasOwnProperty('default') ? memoryBackend['default'] : memoryBackend;\n\n /**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2016-2019 (original work) Open Assessment Technologies SA ;\n */\n\n var supportsIndexedDB = false;\n var dectectionDone = false;\n var quotaChecked = false;\n\n /**\n * The exported store module, can be used as a function to get one store\n * or as an object to run methods on multiple stores.\n *\n * @type {Function|Object}\n */\n var store;\n\n /**\n * The list of required methods exposed by a store backend\n * @type {String[]}\n */\n var backendApi = ['removeAll', 'getAll', 'getStoreIdentifier'];\n\n /**\n * The list of required methods exposed by a store implementation\n * @type {String[]}\n */\n var storeApi = ['getItem', 'setItem', 'removeItem', 'getItems', 'clear', 'removeStore'];\n\n /**\n * Dedicated logger\n */\n var logger = loggerFactory('core/store');\n\n /**\n * Main config\n */\n var config = _.defaults(module.config() || {}, {\n /**\n * Percent of used space (ie. 80% used)\n * to consider the browser as having low space\n * @type {Number}\n */\n lowSpaceRatio: 80,\n\n /**\n * Default duration thresholds to invalidate stores\n *\n * @type {Object} ISO 8601 duration\n */\n invalidation: {\n //candidate for invalidation if we're going over quota\n staled: 'P2W',\n\n //candidate for upfront invalidation if estimates are low\n oldster: 'P2M'\n }\n });\n\n /**\n * Detect IndexedDB support.\n * Due to a bug in Firefox private mode, we need to try to open a database to be sure it's available.\n * @returns {Promise} that resolve the result\n */\n var isIndexDBSupported = function isIndexDBSupported() {\n if (dectectionDone) {\n return Promise.resolve(supportsIndexedDB);\n }\n return new Promise(function(resolve) {\n var test, indexedDB;\n var done = function done(result) {\n supportsIndexedDB = !!result;\n dectectionDone = true;\n return resolve(supportsIndexedDB);\n };\n try {\n indexedDB =\n window.indexedDB ||\n window.webkitIndexedDB ||\n window.mozIndexedDB ||\n window.OIndexedDB ||\n window.msIndexedDB;\n if (!indexedDB) {\n return done(false);\n }\n\n //we need to try to open a db, for example FF in private browsing will fail.\n test = indexedDB.open('__feature_test', 1);\n test.onsuccess = function() {\n if (test.result) {\n test.result.close();\n return done(true);\n }\n };\n //if we can't open a DB, we assume, we fallback\n test.onerror = function(e) {\n e.preventDefault();\n done(false);\n return false;\n };\n } catch (err) {\n //a sync err, we fallback\n done(false);\n }\n });\n };\n\n /**\n * Check storage estimates and invalidate old\n * Estimates aren't widely supported,\n * but that worth to try it (progressive enhancement)\n */\n var checkQuotas = function checkQuotas() {\n if (!quotaChecked && 'storage' in window.navigator && window.navigator.storage.estimate) {\n window.navigator.storage\n .estimate()\n .then(function(estimate) {\n var usedRatio = 0;\n if (_.isNumber(estimate.usage) && _.isNumber(estimate.quota) && estimate.quota > 0) {\n usedRatio = estimate.usage / estimate.quota;\n if (usedRatio > config.lowSpaceRatio) {\n logger.warn('The browser storage is getting low ' + usedRatio.toFixed(2) + '% used', estimate);\n logger.warn('We will attempt to clean oldster databases in persistent backends');\n store.cleanUpSpace(config.invalidation.oldster, [], localStorageBackend);\n if (isIndexDBSupported) {\n store.cleanUpSpace(config.invalidation.oldster, [], indexedDBBackend);\n }\n } else {\n logger.debug('Browser storage estimate : ' + usedRatio.toFixed(2) + '% used', estimate);\n }\n }\n })\n .catch(function(err) {\n logger.warn('Unable to retrieve quotas : ' + err.message);\n });\n }\n quotaChecked = true;\n };\n\n /**\n * Check the backend object complies with the API\n * @param {Object} backend - the backend object to check\n * @returns {Boolean} true if valid\n */\n var isBackendApiValid = function isBackendApiValid(backend) {\n return _.all(backendApi, function methodExists(method) {\n return _.isFunction(backend[method]);\n });\n };\n\n /**\n * Check the storage object complies with the Storage API\n * @param {Storage} storage - the storage object to check\n * @returns {Boolean} true if valid\n */\n var isStorageApiValid = function isStorageApiValid(storage) {\n return _.all(storeApi, function methodExists(method) {\n return _.isFunction(storage[method]);\n });\n };\n\n /**\n * Load the backend based either on the pre-selected and the current support\n * @param {Object} [preselectedBackend] - the backend to force the selection\n * @returns {Promise} that resolves with the backend\n */\n var loadBackend = function loadBackend(preselectedBackend) {\n return isIndexDBSupported().then(function() {\n var backend = preselectedBackend || (supportsIndexedDB ? indexedDBBackend : localStorageBackend);\n if (!_.isFunction(backend)) {\n return Promise.reject(new TypeError('No backend, no storage!'));\n }\n if (!isBackendApiValid(backend)) {\n return Promise.reject(new TypeError(\"This backend doesn't comply with the store backend API\"));\n }\n\n //attempt to check the quotas\n if (backend !== memoryBackend) {\n checkQuotas();\n }\n\n return backend;\n });\n };\n\n /**\n * Loads a store\n *\n * @param {String} storeName - the name of the store\n * @param {Object} [preselectedBackend] - the backend to force the selection\n * @returns {Promise} that resolves with the Storage a Storage Like instance\n */\n store = function storeLoader(storeName, preselectedBackend) {\n return loadBackend(preselectedBackend).then(function(backend) {\n var storeInstance = backend(storeName);\n\n if (!isStorageApiValid(storeInstance)) {\n return Promise.reject(new TypeError(\"The store doesn't comply with the Storage interface\"));\n }\n\n return storeInstance;\n });\n };\n\n /**\n * The available backends,\n * exposed.\n */\n store.backends = {\n localStorage: localStorageBackend,\n indexedDB: indexedDBBackend,\n memory: memoryBackend\n };\n\n /**\n * Removes all storage\n * @param {validateStore} [validate] - An optional callback that validates the store to delete\n * @param {Object} [preselectedBackend] - the backend to force the selection\n * @returns {Promise} with true in resolve once cleaned\n */\n store.removeAll = function removeAll(validate, preselectedBackend) {\n return loadBackend(preselectedBackend).then(function(backend) {\n /**\n * @callback validateStore\n * @param {String} storeName - the name of the store\n * @param {Object} store - the store details\n */\n return backend.removeAll(validate);\n });\n };\n\n /**\n * Clean up storage meeting the invalidation conditions\n * @param {Number|String} [since] - unix timestamp in ms or ISO duration, to compare with lastOpen\n * @param {RegExp} [storeNamePattern] - applies only on store names that matches the pattern\n * @param {Object} [preselectedBackend] - the backend to force the selection\n * @returns {Promise}\n */\n store.cleanUpSpace = function cleanUpSpace(since, storeNamePattern, preselectedBackend) {\n var tsThreshold;\n\n /**\n * Create the invalidation callback\n * @type {validateStore}\n */\n var invalidate = function invalidate(storeName, storeEntry) {\n if (!storeName || !storeEntry) {\n return false;\n }\n\n //storeName matches ?\n if (storeNamePattern instanceof RegExp && !storeNamePattern.test(storeName)) {\n return false;\n }\n return _.isNumber(storeEntry.lastOpen) && _.isNumber(tsThreshold) && storeEntry.lastOpen <= tsThreshold;\n };\n\n if (_.isNumber(since) && since > 0) {\n tsThreshold = since;\n } else {\n if (!_.isString(since)) {\n since = config.invalidation.oldster;\n }\n tsThreshold = moment()\n .subtract(moment.duration(since))\n .valueOf();\n }\n\n logger.info('Trying to remove stores lastly opened before ' + tsThreshold + '(' + since + ')');\n\n return store.removeAll(invalidate, preselectedBackend);\n };\n\n /**\n * Get the name/key of all storages\n * @param {validateStore} [validate] - An optional callback that validates the store\n * @param {Object} [preselectedBackend] - the backend to force the selection\n * @returns {Promise} resolves with the names of the stores\n */\n store.getAll = function getAll(validate, preselectedBackend) {\n return loadBackend(preselectedBackend).then(function(backend) {\n return backend.getAll(validate);\n });\n };\n\n /**\n * Get the identifier of either the current (or the pre-selected store)\n * @param {Object} [preselectedBackend] - the backend to force the selection\n * @returns {Promise} that resolves with the identifier\n */\n store.getIdentifier = function getIdentifier(preselectedBackend) {\n return loadBackend(preselectedBackend).then(function(backend) {\n return backend.getStoreIdentifier();\n });\n };\n\n var store$1 = store;\n\n return store$1;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash'), require('core/promise'), require('core/store')) :\n typeof define === 'function' && define.amd ? define('core/tokenStore',['lodash', 'core/promise', 'core/store'], factory) :\n (global = global || self, global['core/tokenStore'] = factory(global._, global['core/promise'], global['core/store']));\n}(this, function (_, Promise, store) { 'use strict';\n\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n Promise = Promise && Promise.hasOwnProperty('default') ? Promise['default'] : Promise;\n store = store && store.hasOwnProperty('default') ? store['default'] : store;\n\n /*\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2019 Open Assessment Technologies SA\n */\n\n /**\n * The default number of tokens to store\n */\n var defaultConfig = {\n maxSize: 6,\n tokenTimeLimit: 1000 * 60 * 24\n };\n\n /**\n * Create a token store\n * @param {Object} [options]\n * @param {Number} [options.maxSize = 6] - the store limit\n * @param {Number} [options.tokenTimeLimit] - time in milliseconds each token remains valid for\n * @returns {tokenStore}\n */\n function tokenStoreFactory(options) {\n var config = _.defaults(options || {}, defaultConfig);\n\n // In memory storage\n // For security reasons, this is preferred over the indexeddb or localStorage implementations\n var getStore = function getStore() {\n return store('tokenStore.tokens', store.backends.memory);\n };\n\n /**\n * @typedef tokenStore\n */\n return {\n /**\n * Get the oldest token from the queue\n * Remove its store entry as well\n *\n * @returns {Promise} the token object\n */\n dequeue: function dequeue() {\n var self = this;\n return self.getIndex().then(function(latestIndex) {\n var key = _.first(latestIndex);\n if (!key) return Promise.resolve();\n\n return getStore()\n .then(function(storage) {\n return storage.getItem(key);\n })\n .then(function(token) {\n return self.remove(key).then(function() {\n return token;\n });\n });\n });\n },\n\n /**\n * Add a new token object to the queue\n * Add an entry to the store as well\n *\n * @param {Object} token - the token object\n * @param {String} token.value - long alphanumeric string\n * @param {Number} token.receivedAt - timestamp\n * @returns {Promise} - true if added\n */\n enqueue: function enqueue(token) {\n var self = this;\n // Handle legacy param type:\n if (_.isString(token)) {\n token = {\n value: token,\n receivedAt: Date.now()\n };\n }\n return getStore()\n .then(function(storage) {\n return storage.setItem(token.value, token);\n })\n .then(function(updated) {\n if (updated) {\n return self.enforceMaxSize().then(function() {\n return true;\n });\n }\n return false;\n });\n },\n\n /**\n * Generate a new (chronologically-sorted) index from the store contents\n * (because it would not be unique if stored in the module)\n *\n * @returns {Promise}\n */\n getIndex: function getIndex() {\n return this.getTokens().then(function(tokens) {\n return _.chain(tokens)\n .values()\n .sort(function(t1, t2) {\n return t1.receivedAt - t2.receivedAt;\n })\n .map('value')\n .value();\n });\n },\n\n /**\n * Check whether the given token is in the store\n *\n * @param {String} key - token string\n * @returns {Boolean}\n */\n has: function has(key) {\n return this.getIndex().then(function(latestIndex) {\n return _.contains(latestIndex, key);\n });\n },\n\n /**\n * Remove the token from the queue and the store\n *\n * @param {String} key - token string\n * @returns {Promise} resolves once removed\n */\n remove: function remove(key) {\n return this.has(key).then(function(result) {\n if (result) {\n return getStore().then(function(storage) {\n return storage.removeItem(key);\n });\n }\n return false;\n });\n },\n\n /**\n * Empty the queue and store\n * @returns {Promise}\n */\n clear: function clear() {\n return getStore().then(function(storage) {\n return storage.clear();\n });\n },\n\n /**\n * Gets all tokens in the store\n * @returns {Promise} - token objects\n */\n getTokens: function getTokens() {\n return getStore().then(function(storage) {\n return storage.getItems();\n });\n },\n\n /**\n * Gets the current size of the store\n * @returns {Promise}\n */\n getSize: function getSize() {\n return this.getIndex().then(function(latestIndex) {\n return latestIndex.length;\n });\n },\n\n /**\n * Setter for maximum pool size\n * @param {Integer} size\n */\n setMaxSize: function setMaxSize(size) {\n var self = this;\n if (_.isNumber(size) && size > 0 && size !== config.maxSize) {\n config.maxSize = size;\n self.enforceMaxSize();\n }\n },\n\n /**\n * Removes oldest tokens, if the pool is above its size limit\n * (Could happen if maxSize is reduced during the life of the tokenStore)\n * @returns {Promise} - resolves when done\n */\n enforceMaxSize: function enforceMaxSize() {\n var self = this;\n return this.getIndex().then(function(latestIndex) {\n var keysToRemove;\n var excess = latestIndex.length - config.maxSize;\n if (excess > 0) {\n keysToRemove = latestIndex.slice(0, excess);\n return Promise.all(\n _.map(keysToRemove, function(key) {\n return self.remove(key);\n })\n );\n }\n return true;\n });\n },\n\n /**\n * Checks one token and removes it from the store if expired\n * @param {Object} token - the token object\n * @returns {Promise}\n */\n checkExpiry: function checkExpiry(token) {\n if (Date.now() - token.receivedAt > config.tokenTimeLimit) {\n return this.remove(token.value);\n }\n return true;\n },\n\n /**\n * Checks all the tokens in the store to see if they expired\n * @returns {Promise} - resolves to true\n */\n expireOldTokens: function expireOldTokens() {\n var self = this;\n return self.getTokens().then(function(tokens) {\n // Check each token's expiry, synchronously:\n return _.reduce(\n tokens,\n function(previousPromise, nextToken) {\n return previousPromise.then(function() {\n return self.checkExpiry(nextToken);\n });\n },\n Promise.resolve()\n ).then(function() {\n // All done\n return true;\n });\n });\n }\n };\n }\n\n return tokenStoreFactory;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash'), require('module'), require('core/tokenStore'), require('core/promise'), require('core/promiseQueue')) :\n typeof define === 'function' && define.amd ? define('core/tokenHandler',['lodash', 'module', 'core/tokenStore', 'core/promise', 'core/promiseQueue'], factory) :\n (global = global || self, global['core/tokenHandler'] = factory(global._, global.module, global['core/tokenStore'], global['core/promise'], global['core/promiseQueue']));\n}(this, function (_, module, tokenStoreFactory, Promise, promiseQueue) { 'use strict';\n\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n module = module && module.hasOwnProperty('default') ? module['default'] : module;\n tokenStoreFactory = tokenStoreFactory && tokenStoreFactory.hasOwnProperty('default') ? tokenStoreFactory['default'] : tokenStoreFactory;\n Promise = Promise && Promise.hasOwnProperty('default') ? Promise['default'] : Promise;\n promiseQueue = promiseQueue && promiseQueue.hasOwnProperty('default') ? promiseQueue['default'] : promiseQueue;\n\n /**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2016-2019 (original work) Open Assessment Technologies SA ;\n */\n\n var clientConfigFetched = false;\n\n var defaults = {\n maxSize: 6,\n tokenTimeLimit: 1000 * 60 * 24\n };\n\n /**\n * Stores the security token queue\n * @param {Object} [options]\n * @param {String} [options.maxSize]\n * @param {String} [options.tokenTimeLimit]\n * @param {String} [options.initialToken]\n * @returns {tokenHandler}\n */\n function tokenHandlerFactory(options) {\n var tokenStore;\n\n // Convert legacy parameter:\n if (_.isString(options)) {\n options = {\n initialToken: options\n };\n }\n options = _.defaults({}, options, defaults);\n // Initialise storage for tokens:\n tokenStore = tokenStoreFactory(options);\n\n return {\n /**\n * Gets the next security token from the token queue\n * If none are available, it can check the ClientConfig (once only per page)\n * Once the token is got, it is erased from the store (because they are single-use by design)\n *\n * @returns {Promise} the token value\n */\n getToken: function getToken() {\n var self = this;\n var initialToken = options.initialToken;\n\n var getFirstTokenValue = function getFirstTokenValue() {\n return tokenStore.dequeue().then(function(currentToken) {\n if (currentToken) {\n return currentToken.value;\n }\n return null;\n });\n };\n\n // If set, initialToken will be provided directly, without using store:\n if (initialToken) {\n options.initialToken = null;\n return Promise.resolve(initialToken);\n }\n\n // Some async checks before we go for the token:\n return tokenStore\n .expireOldTokens()\n .then(function() {\n return tokenStore.getSize();\n })\n .then(function(queueSize) {\n if (queueSize > 0) {\n // Token available, use it\n return getFirstTokenValue();\n } else if (!clientConfigFetched) {\n // Client Config allowed! (first and only time)\n return self.getClientConfigTokens().then(getFirstTokenValue);\n } else {\n // No more token options, refresh needed\n return Promise.reject(new Error('No tokens available. Please refresh the page.'));\n }\n });\n },\n\n /**\n * Adds a new security token to the token queue\n * Internally, old tokens are deleted to keep queue within maximum pool size\n * @param {String} newToken\n * @returns {Promise} - resolves true if successful\n */\n setToken: function setToken(newToken) {\n return tokenStore.enqueue(newToken);\n },\n\n /**\n * Extracts tokens from the Client Config which should be received on every page load\n * @returns {Promise} - resolves true when completed\n */\n getClientConfigTokens: function getClientConfigTokens() {\n var self = this;\n var clientTokens = _.map(module.config().tokens, function(serverToken) {\n return {\n value: serverToken,\n receivedAt: Date.now()\n };\n });\n\n // Record that this function ran:\n clientConfigFetched = true;\n\n return Promise.resolve(clientTokens).then(function(newTokens) {\n // Add the fetched tokens to the store\n // Uses a promiseQueue to ensure synchronous adding\n var setTokenQueue = promiseQueue();\n\n _.forEach(newTokens, function(token) {\n setTokenQueue.serie(function() {\n return self.setToken(token);\n });\n });\n\n return setTokenQueue.serie(function() {\n return true;\n });\n });\n },\n\n /**\n * Clears the token store\n * @returns {Promise} - resolves to true when cleared\n */\n clearStore: function clearStore() {\n return tokenStore.clear();\n },\n\n /**\n * Getter for the current queue length\n * @returns {Promise}\n */\n getQueueLength: function getQueueLength() {\n return tokenStore.getSize();\n },\n\n /**\n * Setter for maximum pool size\n * @param {Integer} size\n */\n setMaxSize: function setMaxSize(size) {\n tokenStore.setMaxSize(size);\n }\n };\n }\n\n return tokenHandlerFactory;\n\n}));\n\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('lodash'), require('i18n'), require('module'), require('context'), require('core/promise'), require('core/promiseQueue'), require('core/tokenHandler'), require('core/logger')) :\n typeof define === 'function' && define.amd ? define('core/request',['jquery', 'lodash', 'i18n', 'module', 'context', 'core/promise', 'core/promiseQueue', 'core/tokenHandler', 'core/logger'], factory) :\n (global = global || self, global['core/request'] = factory(global.$, global._, global.__, global.module, global.context, global['core/promise'], global['core/promiseQueue'], global['core/tokenHandler'], global['core/logger']));\n}(this, function ($, _, __, module, context, Promise, promiseQueue, tokenHandlerFactory, loggerFactory) { 'use strict';\n\n $ = $ && $.hasOwnProperty('default') ? $['default'] : $;\n _ = _ && _.hasOwnProperty('default') ? _['default'] : _;\n __ = __ && __.hasOwnProperty('default') ? __['default'] : __;\n module = module && module.hasOwnProperty('default') ? module['default'] : module;\n context = context && context.hasOwnProperty('default') ? context['default'] : context;\n Promise = Promise && Promise.hasOwnProperty('default') ? Promise['default'] : Promise;\n promiseQueue = promiseQueue && promiseQueue.hasOwnProperty('default') ? promiseQueue['default'] : promiseQueue;\n tokenHandlerFactory = tokenHandlerFactory && tokenHandlerFactory.hasOwnProperty('default') ? tokenHandlerFactory['default'] : tokenHandlerFactory;\n loggerFactory = loggerFactory && loggerFactory.hasOwnProperty('default') ? loggerFactory['default'] : loggerFactory;\n\n /**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2019 (original work) Open Assessment Technologies SA;\n */\n\n var tokenHeaderName = 'X-CSRF-Token';\n\n var tokenHandler = tokenHandlerFactory();\n\n var queue = promiseQueue();\n\n var logger = loggerFactory('core/request');\n\n /**\n * Create a new error based on the given response\n * @param {Object} response - the server body response as plain object\n * @param {String} fallbackMessage - the error message in case the response isn't correct\n * @param {Number} httpCode - the response HTTP code\n * @param {Boolean} httpSent - the sent status\n * @returns {Error} the new error\n */\n var createError = function createError(response, fallbackMessage, httpCode, httpSent) {\n var err;\n if (response && response.errorCode) {\n err = new Error(response.errorCode + ' : ' + (response.errorMsg || response.errorMessage || response.error));\n } else {\n err = new Error(fallbackMessage);\n }\n err.response = response;\n err.sent = httpSent;\n\n if (httpCode) {\n err.code = httpCode;\n }\n return err;\n };\n\n /**\n * Request content from a TAO endpoint\n * @param {Object} options\n * @param {String} options.url - the endpoint full url\n * @param {String} [options.method='GET'] - the HTTP method\n * @param {Object} [options.data] - additional parameters (if method is 'POST')\n * @param {Object} [options.headers] - the HTTP headers\n * @param {String} [options.contentType] - what kind of data we're sending - usually 'json'\n * @param {String} [options.dataType] - what kind of data expected in response\n * @param {Boolean} [options.noToken=false] - by default, a token is always sent. If noToken=true, disables the token requirement\n * @param {Boolean} [options.background] - if true, the request should be done in the background, which in practice does not trigger the global handlers like ajaxStart or ajaxStop\n * @param {Boolean} [options.sequential] - if true, the request must join a queue to be run sequentially\n * @param {Number} [options.timeout] - timeout in seconds for the AJAX request\n * @returns {Promise} resolves with response, or reject if something went wrong\n */\n function request(options) {\n // Allow external config to override user option\n if (module.config().noToken) {\n options.noToken = true;\n }\n\n if (_.isEmpty(options.url)) {\n throw new TypeError('At least give a URL...');\n }\n\n /**\n * Function wrapper which allows the contents to be run now, or added to a queue\n * @returns {Promise} resolves with response, or rejects if something went wrong\n */\n function runRequest() {\n /**\n * Fetches a security token and appends it to headers, if required\n * @returns {Promise} - resolves with headers object\n */\n var computeHeaders = function computeHeaders() {\n var headers = _.extend({}, options.headers);\n if (!options.noToken) {\n return tokenHandler.getToken().then(function(token) {\n headers[tokenHeaderName] = token || 'none';\n return headers;\n });\n }\n return Promise.resolve(headers);\n };\n\n /**\n * Extracts returned security token from headers and adds it to store\n * @param {Object} xhr\n * @returns {Promise} - resolves when done\n */\n var setTokenFromXhr = function setTokenFromXhr(xhr) {\n var token;\n\n if (_.isFunction(xhr.getResponseHeader)) {\n token = xhr.getResponseHeader(tokenHeaderName);\n logger.debug('received %s header %s', tokenHeaderName, token);\n\n if (token) {\n return tokenHandler.setToken(token);\n }\n }\n return Promise.resolve();\n };\n\n return computeHeaders().then(function(customHeaders) {\n return new Promise(function(resolve, reject) {\n var noop;\n $.ajax({\n url: options.url,\n method: options.method || 'GET',\n headers: customHeaders,\n data: options.data,\n contentType: options.contentType || noop,\n dataType: options.dataType || 'json',\n async: true,\n timeout: options.timeout * 1000 || context.timeout * 1000 || 0,\n beforeSend: function() {\n if (!_.isEmpty(customHeaders)) {\n logger.debug(\n 'sending %s header %s',\n tokenHeaderName,\n customHeaders && customHeaders[tokenHeaderName]\n );\n }\n },\n global: !options.background //TODO fix this with TT-260\n })\n .done(function(response, status, xhr) {\n setTokenFromXhr(xhr)\n .then(function() {\n if (\n xhr.status === 204 ||\n (response && response.errorCode === 204) ||\n status === 'nocontent'\n ) {\n // no content, so resolve with empty data.\n return resolve();\n }\n\n // handle case where token expired or invalid\n if (xhr.status === 403 || (response && response.errorCode === 403)) {\n return reject(\n createError(\n response,\n xhr.status + ' : ' + xhr.statusText,\n xhr.status,\n xhr.readyState > 0\n )\n );\n }\n\n if (xhr.status === 200 || (response && response.success === true)) {\n // there's some data\n return resolve(response);\n }\n\n //the server has handled the error\n reject(\n createError(\n response,\n __('The server has sent an empty response'),\n xhr.status,\n xhr.readyState > 0\n )\n );\n })\n .catch(function(error) {\n logger.error(error);\n reject(createError(response, error, xhr.status, xhr.readyState > 0));\n });\n })\n .fail(function(xhr, textStatus, errorThrown) {\n var response;\n try {\n response = JSON.parse(xhr.responseText);\n } catch (parseErr) {\n response = xhr.responseText;\n }\n\n response = _.defaults(response, {\n success: false,\n source: 'network',\n cause: options.url,\n purpose: 'proxy',\n context: this,\n code: xhr.status,\n sent: xhr.readyState > 0,\n type: 'error',\n message: errorThrown || __('An error occurred!')\n });\n\n setTokenFromXhr(xhr)\n .then(function() {\n reject(\n createError(\n response,\n xhr.status + ' : ' + xhr.statusText,\n xhr.status,\n xhr.readyState > 0\n )\n );\n })\n .catch(function(error) {\n logger.error(error);\n reject(createError(response, error, xhr.status, xhr.readyState > 0));\n });\n });\n });\n });\n }\n\n // Decide how to launch the request based on certain params:\n return tokenHandler.getQueueLength().then(function(queueLength) {\n if (options.noToken === true) {\n // no token protection, run the request\n return runRequest();\n } else if (options.sequential || queueLength === 1) {\n // limited tokens, sequential queue must be used\n return queue.serie(runRequest);\n } else {\n // tokens ready\n return runRequest();\n }\n });\n }\n\n return request;\n\n}));\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2017-2019 (original work) Open Assessment Technologies SA;\n *\n * @author Alexander Zagovorichev \n */\n\ndefine('taoGroups/provider/group',['jquery', 'lodash', 'i18n', 'util/url', 'core/promise', 'core/request'], function ($, _, __, urlUtil, Promise, coreRequest) {\n 'use strict';\n\n /**\n * Creates a group provider\n *\n * @typedef {Object} groupProvider\n * @type {{addInstance: groupProvider.addInstance}}\n */\n return function groupProviderFactory () {\n\n /**\n * @returns {groupProvider}\n */\n return {\n\n /**\n * Create new group\n *\n * @param config\n * @param {String} [config.classUri] - rdf uri of the Group for current environment\n * @param {String} [config.id] - id of the Group for current environment\n * @param {String} [config.signature] - id signature received from BE, required\n * @param {String} [config.type] - Type of the instance\n * @return {*}\n */\n addInstance (config) {\n\n const _defaults = {\n classUri: 'http_2_www_0_tao_0_lu_1_Ontologies_1_TAOGroup_0_rdf_3_Group',\n id: 'http://www.tao.lu/Ontologies/TAOGroup.rdf#Group',\n type: 'instance'\n };\n\n config = _.defaults(config || {}, _defaults);\n\n return new Promise((resolve, reject) => {\n coreRequest({\n url: urlUtil.route('addInstance', 'Groups', 'taoGroups'),\n method: 'POST',\n data: config,\n dataType: 'json',\n })\n .then(function (group) {\n resolve(group);\n })\n .catch(function () {\n reject(new Error(__('Unable to create new group')));\n });\n });\n },\n\n /**\n * Group deleting\n *\n * @param uri\n * @return {*}\n */\n deleteGroup (uri) {\n\n return new Promise((resolve, reject) => {\n\n if (!_.isString(uri) || _.isEmpty(uri)) {\n return reject(new TypeError(__('Group uri is not valid')));\n }\n\n coreRequest({\n url: urlUtil.route('delete', 'Groups', 'taoGroups'),\n method: 'POST',\n data: {uri: uri},\n dataType: 'json',\n })\n .then(function (response) {\n resolve(response);\n })\n .catch(function () {\n reject(new Error(__('Unable to delete group')));\n });\n });\n }\n };\n };\n});\n\n","\ndefine(\"taoGroups/loader/taoGroups.bundle\", function(){});\n"]} \ No newline at end of file +{"version":3,"sources":["../controller/routes.js","../provider/group.js","module-create.js"],"names":[],"mappings":"AAqBA,MAAA,CAAA,6BAAA,CAAA,EAAA,CAAA,UAAA,CACA,aAEA,MAAA,EAEA,CALA,C,CCDA,MAAA,CAAA,0BAAA,CAAA,CAAA,QAAA,CAAA,QAAA,CAAA,MAAA,CAAA,UAAA,CAAA,cAAA,CAAA,cAAA,CAAA,CAAA,SAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,OAAA,CAAA,OAAA,CAAA,WAAA,CAAA,CACA,aAQA,MAAA,WAAA,CAKA,MAAA,CAYA,WAZA,sBAYA,MAZA,CAYA,CAUA,MAFA,CAAA,MAAA,CAAA,CAAA,CAAA,QAAA,CAAA,MAAA,EAAA,EAAA,CANA,CACA,QAAA,CAAA,6DADA,CAEA,EAAA,CAAA,iDAFA,CAGA,IAAA,CAAA,UAHA,CAMA,CAEA,CAAA,GAAA,CAAA,OAAA,CAAA,SAAA,OAAA,CAAA,MAAA,CAAA,CACA,WAAA,CAAA,CACA,GAAA,CAAA,OAAA,CAAA,KAAA,CAAA,aAAA,CAAA,QAAA,CAAA,WAAA,CADA,CAEA,MAAA,CAAA,MAFA,CAGA,IAAA,CAAA,MAHA,CAIA,QAAA,CAAA,MAJA,CAAA,CAAA,CAMA,IANA,CAMA,SAAA,KAAA,CAAA,CACA,OAAA,CAAA,KAAA,CACA,CARA,EASA,KATA,CASA,UAAA,CACA,MAAA,CAAA,GAAA,CAAA,KAAA,CAAA,EAAA,CAAA,4BAAA,CAAA,CAAA,CACA,CAXA,CAYA,CAbA,CAcA,CApCA,CA4CA,WA5CA,sBA4CA,GA5CA,CA4CA,CAEA,MAAA,IAAA,CAAA,OAAA,CAAA,SAAA,OAAA,CAAA,MAAA,CAAA,OAEA,CAAA,CAAA,CAAA,QAAA,CAAA,GAAA,CAAA,EAAA,CAAA,CAAA,OAAA,CAAA,GAAA,CAFA,CAGA,MAAA,CAAA,GAAA,CAAA,SAAA,CAAA,EAAA,CAAA,wBAAA,CAAA,CAAA,CAHA,KAMA,CAAA,WAAA,CAAA,CACA,GAAA,CAAA,OAAA,CAAA,KAAA,CAAA,QAAA,CAAA,QAAA,CAAA,WAAA,CADA,CAEA,MAAA,CAAA,MAFA,CAGA,IAAA,CAAA,CAAA,GAAA,CAAA,GAAA,CAHA,CAIA,QAAA,CAAA,MAJA,CAAA,CAAA,CAMA,IANA,CAMA,SAAA,QAAA,CAAA,CACA,OAAA,CAAA,QAAA,CACA,CARA,EASA,KATA,CASA,UAAA,CACA,MAAA,CAAA,GAAA,CAAA,KAAA,CAAA,EAAA,CAAA,wBAAA,CAAA,CAAA,CACA,CAXA,CAYA,CAlBA,CAmBA,CAjEA,CAmEA,CACA,CAlFA,C,CCnBA,MAAA,CAAA,mCAAA,CAAA,UAAA,CAAA,CAAA,C","sourcesContent":["/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2014 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);\n * \n * \n */\n\n//@see http://forge.taotesting.com/projects/tao/wiki/Front_js\ndefine('taoGroups/controller/routes',[],function(){\n 'use strict';\n\n return {\n };\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2017-2019 (original work) Open Assessment Technologies SA;\n *\n * @author Alexander Zagovorichev \n */\n\ndefine('taoGroups/provider/group',['jquery', 'lodash', 'i18n', 'util/url', 'core/promise', 'core/request'], function ($, _, __, urlUtil, Promise, coreRequest) {\n 'use strict';\n\n /**\n * Creates a group provider\n *\n * @typedef {Object} groupProvider\n * @type {{addInstance: groupProvider.addInstance}}\n */\n return function groupProviderFactory () {\n\n /**\n * @returns {groupProvider}\n */\n return {\n\n /**\n * Create new group\n *\n * @param config\n * @param {String} [config.classUri] - rdf uri of the Group for current environment\n * @param {String} [config.id] - id of the Group for current environment\n * @param {String} [config.signature] - id signature received from BE, required\n * @param {String} [config.type] - Type of the instance\n * @return {*}\n */\n addInstance (config) {\n\n const _defaults = {\n classUri: 'http_2_www_0_tao_0_lu_1_Ontologies_1_TAOGroup_0_rdf_3_Group',\n id: 'http://www.tao.lu/Ontologies/TAOGroup.rdf#Group',\n type: 'instance'\n };\n\n config = _.defaults(config || {}, _defaults);\n\n return new Promise((resolve, reject) => {\n coreRequest({\n url: urlUtil.route('addInstance', 'Groups', 'taoGroups'),\n method: 'POST',\n data: config,\n dataType: 'json',\n })\n .then(function (group) {\n resolve(group);\n })\n .catch(function () {\n reject(new Error(__('Unable to create new group')));\n });\n });\n },\n\n /**\n * Group deleting\n *\n * @param uri\n * @return {*}\n */\n deleteGroup (uri) {\n\n return new Promise((resolve, reject) => {\n\n if (!_.isString(uri) || _.isEmpty(uri)) {\n return reject(new TypeError(__('Group uri is not valid')));\n }\n\n coreRequest({\n url: urlUtil.route('delete', 'Groups', 'taoGroups'),\n method: 'POST',\n data: {uri: uri},\n dataType: 'json',\n })\n .then(function (response) {\n resolve(response);\n })\n .catch(function () {\n reject(new Error(__('Unable to delete group')));\n });\n });\n }\n };\n };\n});\n\n","\ndefine(\"taoGroups/loader/taoGroups.bundle\", function(){});\n"]} \ No newline at end of file