From 0e4a448f4eca6c6397075357a080c06f5534f556 Mon Sep 17 00:00:00 2001 From: artragis Date: Sun, 20 Sep 2020 18:35:30 +0200 Subject: [PATCH 01/78] =?UTF-8?q?quizz:=20Int=C3=A9gration=20IHM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/content-quizz.js | 83 +++++++++++++++++++++ package.json | 2 +- templates/tutorialv2/export/chapter.html | 11 ++- yarn.lock | 4 +- zds/tutorialv2/models/versioned.py | 5 +- zds/tutorialv2/urls/urls_contents.py | 20 ++++- zds/tutorialv2/utils.py | 7 +- zds/tutorialv2/views/containers_extracts.py | 4 +- 8 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 assets/js/content-quizz.js diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js new file mode 100644 index 0000000000..4da7fe7b31 --- /dev/null +++ b/assets/js/content-quizz.js @@ -0,0 +1,83 @@ +// to handle a new type of answer, you just need to create a method called +// initializeXXX(answers) that will add the reset the field content and mark good answer +// then add the two methods in the callback lists + +let index = 0 +function extractAnswer(radio, answers) { + radio.forEach((rb) => { + if (!rb.parentElement.parentElement.getAttribute('id')) { + rb.parentElement.parentElement.setAttribute('id', 'id-' + (index++)) + } + rb.setAttribute('name', rb.parentElement.parentElement.getAttribute('id')) + if (!answers[rb.parentNode.parentNode.getAttribute('id')]) { + answers[rb.parentNode.parentNode.getAttribute('id')] = [rb.checked] + } else { + answers[rb.parentNode.parentNode.getAttribute('id')].push(rb.checked) + } + rb.setAttribute('value', answers[rb.parentNode.parentNode.getAttribute('id')].length - 1) + rb.disabled = false + rb.checked = false + }) +} +function initializeCheckboxes(answers) { + const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') + extractAnswer(checkboxes, answers) +} + + + +function initializeRadio(answers) { + const radio = document.querySelectorAll('.quizz ul li input[type=radio]') + extractAnswer(radio, answers) +} + +const initializePipeline = [initializeCheckboxes, initializeRadio] +function computeForm(formdata, answers) { + const badAnswers = [] + formdata.entries().forEach(entry => { + const name = entry[0] + const values = entry[1] + let good = true + if (!answers[name]) { + console.log('not found ' + name) + return 0 + } else { + // for poc we assume we only deal with lists + for (let i = 0; i < answers[name].length && good; i++) { + if (answers[name][i]) { + good = good && values.indexOf(answers[name][i]) !== -1 + } else { + good = good && values.indexOf(answers[name][i]) === -1 + } + if (!good) { + badAnswers.push(name) + } + } + } + }) +} +function markBadAnswers(names, answers) { + names.forEach(name => { + document.querySelectorAll('input[name=' + name + ']').forEach(field => { + if (answers[name].indexOf(field.getAttribute('value')) !== -1 && !field.checked) { + field.addClass('quizz-forget') + } else if (answers[name].indexOf(field.getAttribute('value')) === -1 && field.checked) { + field.addClass('quizz-bad') + } + }) + }) +} +const answers = {} +initializePipeline.forEach(func => func(answers)) +document.querySelectorAll('form.quizz').forEach(form => { + form.addEventListener('submit', e => { + e.preventDefault() + e.stopPropagation() + const formData = new FormData(form) + // result = name of bad answers + const result = computeForm(formData, answers) + markBadAnswers(result, answers) + // here send result + console.log(result) + }) +}) diff --git a/package.json b/package.json index a0c59f7743..8f231386d3 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "gulp-concat": "2.6.1", "gulp-dart-sass": "1.0.2", "gulp-if": "3.0.0", - "gulp-imagemin": "7.1.0", + "gulp-imagemin": "^7.1.0", "gulp-options": "1.1.1", "gulp-postcss": "9.0.1", "gulp-terser-js": "5.2.2", diff --git a/templates/tutorialv2/export/chapter.html b/templates/tutorialv2/export/chapter.html index 7178530b82..1c71b18c50 100644 --- a/templates/tutorialv2/export/chapter.html +++ b/templates/tutorialv2/export/chapter.html @@ -24,9 +24,14 @@

{% if rendered_extract.text %} -
- {{ rendered_extract.text|safe }} -
+ {% if rendered_extract.is_quizz %} +
+ {% endif %} + {{ rendered_extract.get_text|emarkdown:is_js }} + {% if rendered_extract.is_quizz %} + +
+ {% endif %} {% endif %} {% endfor %} diff --git a/yarn.lock b/yarn.lock index 7c227c24c2..92b766147a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2555,7 +2555,7 @@ gif-encoder@~0.4.1: dependencies: readable-stream "~1.1.9" -gifsicle@^5.0.0: +gifsicle@^5.0.0, gifsicle@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/gifsicle/-/gifsicle-5.2.0.tgz#b06b25ed7530f033f6ed2c545d6f9b546cc182fb" integrity sha512-vOIS3j0XoTCxq9pkGj43gEix82RkI5FveNgaFZutjbaui/HH+4fR8Y56dwXDuxYo8hR4xOo6/j2h1WHoQW6XLw== @@ -2814,7 +2814,7 @@ gulp-if@3.0.0: ternary-stream "^3.0.0" through2 "^3.0.1" -gulp-imagemin@7.1.0: +gulp-imagemin@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/gulp-imagemin/-/gulp-imagemin-7.1.0.tgz#d1810a908fb64b4fbf15a750d303d988443e68cf" integrity sha512-6xBTNybmPY2YrvrhhlS8Mxi0zn0ypusLon63p9XXxDtIf7U7c6KcViz94K7Skosucr3378A6IY2kJSjJyuwylQ== diff --git a/zds/tutorialv2/models/versioned.py b/zds/tutorialv2/models/versioned.py index 77e2e57cc4..477a10ac72 100644 --- a/zds/tutorialv2/models/versioned.py +++ b/zds/tutorialv2/models/versioned.py @@ -46,6 +46,7 @@ class Container: children = [] children_dict = {} slug_pool = {} + is_quizz = False def __init__(self, title, slug="", parent=None, position_in_parent=1): """Initialize the data model that will handle the dialog with raw versionned data at level container. @@ -618,13 +619,14 @@ def repo_add_container( title, introduction, conclusion, commit_message=commit_message, do_commit=do_commit ) - def repo_add_extract(self, title, text, commit_message="", do_commit=True, slug=None): + def repo_add_extract(self, title, text, commit_message="", do_commit=True, slug=None, quizz=False): """ :param title: title of the new extract :param text: text of the new extract :param commit_message: commit message that will be used instead of the default one :param do_commit: perform the commit in repository if ``True`` :param generate_slug: indicates that is must generate slug + :param quizz: if is ``True`` marks the extract as quizz :return: commit sha :rtype: str """ @@ -632,6 +634,7 @@ def repo_add_extract(self, title, text, commit_message="", do_commit=True, slug= extract = Extract(title) else: extract = Extract(title, slug) + extract.is_quizz = quizz # can an extract be added ? try: self.add_extract(extract, generate_slug=slug is None) diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index 3addce26df..e75a3afaba 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -117,7 +117,25 @@ CreateExtract.as_view(), name="create-extract", ), - path("nouvelle-section///", CreateExtract.as_view(), name="create-extract"), + path( + "nouveau-quizz/////", + CreateExtract.as_view(quizz=True), + name="create-quizz" + ), + path( + "nouveau-quizz/(///", + CreateExtract.as_view(quizz=True), + name='create-quizz' + ), + path( + "nouvelle-section///", CreateExtract.as_view(), + name="create-extract" + ), + path( + "nouveau-quizz///", + CreateExtract.as_view(quizz=True), + name='create-quizz' + ), # edit: path( "editer-conteneur////" r"/", diff --git a/zds/tutorialv2/utils.py b/zds/tutorialv2/utils.py index 5914138369..cc30763ebb 100644 --- a/zds/tutorialv2/utils.py +++ b/zds/tutorialv2/utils.py @@ -397,6 +397,7 @@ def get_content_from_json(json, sha, slug_last_draft, public=False, max_title_le extract = Extract("text", "") if "text" in json: extract.text = json["text"] # probably 'text.md' ! + extract.is_quizz = json.get("is_quizz", False) versioned.add_extract(extract, generate_slug=True) else: # it's a tutorial @@ -408,6 +409,7 @@ def get_content_from_json(json, sha, slug_last_draft, public=False, max_title_le ) if "text" in extract: new_extract.text = extract["text"] + new_extract.is_quizz = extract.get("is_quizz", False) versioned.add_extract(new_extract, generate_slug=False) elif json["type"] == "BIG" and "parts" in json: @@ -444,6 +446,7 @@ def get_content_from_json(json, sha, slug_last_draft, public=False, max_title_le if "text" in extract: new_extract.text = extract["text"] + new_extract.is_quizz = extract.get("is_quizz", False) new_chapter.add_extract(new_extract, generate_slug=False) return versioned @@ -496,7 +499,7 @@ def fill_containers_from_json(json_sub, parent): except KeyError: pass new_extract = Extract(child["title"], slug) - + new_extract.is_quizz = child.get("is_quizz", False) if "text" in child: new_extract.text = child["text"] try: @@ -614,7 +617,7 @@ def export_extract(extract, with_text): dct["object"] = "extract" dct["slug"] = extract.slug dct["title"] = extract.title - + dct["is_quizz"] = extract.is_quizz if extract.text and not with_text: dct["text"] = extract.text elif extract.text: diff --git a/zds/tutorialv2/views/containers_extracts.py b/zds/tutorialv2/views/containers_extracts.py index ef56962a2d..60e61eecf1 100644 --- a/zds/tutorialv2/views/containers_extracts.py +++ b/zds/tutorialv2/views/containers_extracts.py @@ -196,6 +196,7 @@ class CreateExtract(LoggedWithReadWriteHability, SingleContentFormViewMixin, For form_class = ExtractForm content = None authorized_for_staff = True + quizz = False def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -216,7 +217,8 @@ def form_valid(self, form): parent = search_container_or_404(self.versioned_object, self.kwargs) sha = parent.repo_add_extract( - form.cleaned_data["title"], form.cleaned_data["text"], form.cleaned_data["msg_commit"] + form.cleaned_data["title"], form.cleaned_data["text"], form.cleaned_data["msg_commit"], + quizz=self.quizz ) # then save From fd130d507b02517f5e0fb57b0669f892d3d1ebb4 Mon Sep 17 00:00:00 2001 From: artragis Date: Sun, 20 Sep 2020 21:17:28 +0200 Subject: [PATCH 02/78] divers fix --- assets/js/content-quizz.js | 50 +++++++++++-------- assets/scss/base/_content.scss | 6 +++ templates/tutorialv2/create/extract.html | 16 +++++- templates/tutorialv2/export/chapter.html | 2 + templates/tutorialv2/includes/child.part.html | 19 +++++++ templates/tutorialv2/view/container.html | 16 ++++++ templates/tutorialv2/view/content.html | 11 ++++ zds/tutorialv2/utils.py | 2 +- zds/tutorialv2/views/containers_extracts.py | 2 +- 9 files changed, 99 insertions(+), 25 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 4da7fe7b31..9c1e4e242f 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -3,6 +3,7 @@ // then add the two methods in the callback lists let index = 0 + function extractAnswer(radio, answers) { radio.forEach((rb) => { if (!rb.parentElement.parentElement.getAttribute('id')) { @@ -19,54 +20,61 @@ function extractAnswer(radio, answers) { rb.checked = false }) } + function initializeCheckboxes(answers) { const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') extractAnswer(checkboxes, answers) } - function initializeRadio(answers) { const radio = document.querySelectorAll('.quizz ul li input[type=radio]') extractAnswer(radio, answers) } const initializePipeline = [initializeCheckboxes, initializeRadio] + function computeForm(formdata, answers) { const badAnswers = [] - formdata.entries().forEach(entry => { + for (const entry of formdata.entries()) { const name = entry[0] - const values = entry[1] - let good = true + const values = parseInt(entry[1], 10) if (!answers[name]) { console.log('not found ' + name) - return 0 + continue } else { + console.log(name + ' ' + values + ' : ' + answers[name][values]) // for poc we assume we only deal with lists - for (let i = 0; i < answers[name].length && good; i++) { - if (answers[name][i]) { - good = good && values.indexOf(answers[name][i]) !== -1 - } else { - good = good && values.indexOf(answers[name][i]) === -1 - } - if (!good) { - badAnswers.push(name) - } + if (!answers[name][values]) { + console.log('bad answer ' + name + ' ' + values) + badAnswers.push({ + name: name, + value: values + }) } } - }) + } + return badAnswers } + function markBadAnswers(names, answers) { - names.forEach(name => { - document.querySelectorAll('input[name=' + name + ']').forEach(field => { - if (answers[name].indexOf(field.getAttribute('value')) !== -1 && !field.checked) { - field.addClass('quizz-forget') - } else if (answers[name].indexOf(field.getAttribute('value')) === -1 && field.checked) { - field.addClass('quizz-bad') + console.log(names) + console.log(answers) + Object.keys(names).forEach(name => { + console.log(names) + document.querySelectorAll('input[name="' + name + '"]').forEach(field => { + if (answers[name][parseInt(field.getAttribute('value'), 10)] && !field.checked) { + field.parentElement.classList.add('quizz-forget') } }) }) + names.forEach(({ name, value }) => { + console.log('name ' + name) + console.log('value ' + value) + document.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`).classList.add('quizz-forget') + }) } + const answers = {} initializePipeline.forEach(func => func(answers)) document.querySelectorAll('form.quizz').forEach(form => { diff --git a/assets/scss/base/_content.scss b/assets/scss/base/_content.scss index 92a55603d3..b9c7062f46 100644 --- a/assets/scss/base/_content.scss +++ b/assets/scss/base/_content.scss @@ -615,6 +615,12 @@ li.task-list-item { background-color: $true-white !important; } + &.quizz-forget { + font-weight: bolder; + } + &.quizz-bad { + color: $color-danger; + } } .warn-typo { diff --git a/templates/tutorialv2/create/extract.html b/templates/tutorialv2/create/extract.html index d8ddb99b98..907ee48328 100644 --- a/templates/tutorialv2/create/extract.html +++ b/templates/tutorialv2/create/extract.html @@ -4,14 +4,22 @@ {% block title %} + {% if quizz %} + {% trans "Nouveau Quizz" %} + {% else %} {% trans "Nouvelle section" %} + {% endif %} {% endblock %} {% block headline %}

- {% trans "Nouvelle section" %} + {% if quizz %} + {% trans "Nouveau Quizz" %} + {% else %} + {% trans "Nouvelle section" %} + {% endif %}

{% endblock %} @@ -29,7 +37,11 @@

  • {{ container.title }}
  • -
  • {% trans "Nouvelle section" %}
  • +
  • {% if quizz %} + {% trans "Nouveau Quizz" %} + {% else %} + {% trans "Nouvelle section" %} + {% endif %}
  • {% endblock %} diff --git a/templates/tutorialv2/export/chapter.html b/templates/tutorialv2/export/chapter.html index 1c71b18c50..60c2fd6bfc 100644 --- a/templates/tutorialv2/export/chapter.html +++ b/templates/tutorialv2/export/chapter.html @@ -1,3 +1,5 @@ +{% load emarkdown %} +{% load i18n %} {# this template will be used to generate the HTML file for each container ! #} diff --git a/templates/tutorialv2/includes/child.part.html b/templates/tutorialv2/includes/child.part.html index 44727bed6e..3523f40651 100644 --- a/templates/tutorialv2/includes/child.part.html +++ b/templates/tutorialv2/includes/child.part.html @@ -111,6 +111,15 @@ {% trans "Ajouter une section" %} +
  • + + {% trans "Ajouter un quizz" %} + +
  • {% endif %} {% elif child.has_sub_containers %} @@ -148,6 +157,13 @@

    >{% trans "Ajouter une section" %}

    +
  • +

    + {% trans "Ajouter un quizz" %} +

    +
  • {% endif %} @@ -190,6 +206,9 @@

  • {% trans "Ajouter une section" %} + {% trans 'ou' %} + + {% trans "un quizz" %}
  • diff --git a/templates/tutorialv2/view/container.html b/templates/tutorialv2/view/container.html index 00aed5b4e9..d0ca1859b4 100644 --- a/templates/tutorialv2/view/container.html +++ b/templates/tutorialv2/view/container.html @@ -136,6 +136,15 @@

    {% trans "Ajouter une section" %} +
  • + + {% trans "Ajouter un quizz" %} + +
  • {% endif %} {% endif %} @@ -203,6 +212,13 @@

    {% url "content:create-extract" content.pk content.slug container.parent.slug container.slug %} {% endif %}"> {% trans "Ajouter une section" %} + {% trans 'ou' %} + + {% trans "un quizz" %}
    {% trans "Ajouter une section" %} +
  • + + {% trans "Ajouter un quizz" %} + +
  • {% endif %} {% endif %} @@ -230,6 +235,9 @@

    {% trans "Ajouter une section" %} + {% trans 'ou'%} + + {% trans "un quizz" %}
    @@ -283,6 +291,9 @@

    {% trans "Ajouter une section" %} + + {% trans "Ajouter un quizz" %} + {% endif %} diff --git a/zds/tutorialv2/utils.py b/zds/tutorialv2/utils.py index cc30763ebb..6f43f45052 100644 --- a/zds/tutorialv2/utils.py +++ b/zds/tutorialv2/utils.py @@ -466,7 +466,7 @@ def fill_containers_from_json(json_sub, parent): if "children" in json_sub: for child in json_sub["children"]: - if not all_is_string_appart_from_given_keys(child, ("children", "ready_to_publish")): + if not all_is_string_appart_from_given_keys(child, ("children", "ready_to_publish", "is_quizz")): raise BadManifestError( _("Le fichier manifest n'est pas bien formaté dans le conteneur " + str(json_sub["title"])) ) diff --git a/zds/tutorialv2/views/containers_extracts.py b/zds/tutorialv2/views/containers_extracts.py index 60e61eecf1..96589d7c62 100644 --- a/zds/tutorialv2/views/containers_extracts.py +++ b/zds/tutorialv2/views/containers_extracts.py @@ -202,7 +202,7 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["container"] = search_container_or_404(self.versioned_object, self.kwargs) context["gallery"] = self.object.gallery - + context["quizz"] = self.quizz return context def render_to_response(self, context, **response_kwargs): From 37c472fc36f7933e4019327eec3a76829476252c Mon Sep 17 00:00:00 2001 From: artragis Date: Sun, 20 Sep 2020 23:04:14 +0200 Subject: [PATCH 03/78] POC --- assets/js/content-quizz.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 9c1e4e242f..b1d30e7256 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -15,6 +15,8 @@ function extractAnswer(radio, answers) { } else { answers[rb.parentNode.parentNode.getAttribute('id')].push(rb.checked) } + console.log(index - 1) + console.log(rb.checked) rb.setAttribute('value', answers[rb.parentNode.parentNode.getAttribute('id')].length - 1) rb.disabled = false rb.checked = false @@ -58,21 +60,20 @@ function computeForm(formdata, answers) { } function markBadAnswers(names, answers) { - console.log(names) - console.log(answers) - Object.keys(names).forEach(name => { - console.log(names) + const toAdd = [] + names.forEach(({ name }) => { document.querySelectorAll('input[name="' + name + '"]').forEach(field => { if (answers[name][parseInt(field.getAttribute('value'), 10)] && !field.checked) { field.parentElement.classList.add('quizz-forget') + toAdd.push({ name: name, value: field.getAttribute('value') }) } }) }) names.forEach(({ name, value }) => { - console.log('name ' + name) - console.log('value ' + value) - document.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`).classList.add('quizz-forget') + document.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`) + .parentElement.classList.add('quizz-bad') }) + toAdd.forEach(name => names.push(name)) } const answers = {} @@ -85,6 +86,13 @@ document.querySelectorAll('form.quizz').forEach(form => { // result = name of bad answers const result = computeForm(formData, answers) markBadAnswers(result, answers) + const questions = [] + result.forEach(result => { + if (questions.indexOf(result.name) === -1) { + questions.push(result.name) + } + }) + alert('Vous mal avez répondu à ' + questions.length + ' questions.') // here send result console.log(result) }) From d68cce64d0a5dcb7c34f31850e03051367132217 Mon Sep 17 00:00:00 2001 From: artragis Date: Mon, 21 Sep 2020 19:55:19 +0200 Subject: [PATCH 04/78] =?UTF-8?q?Remont=C3=A9e=20des=20stats=20et=20blocs?= =?UTF-8?q?=20pour=20correction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/content-quizz.js | 28 ++++++++++++++++++++---- assets/scss/base/_content.scss | 18 +++++++++++++++ templates/misc/quizz.graph.part.html | 17 ++++++++++++++ templates/tutorialv2/export/chapter.html | 5 +++-- templates/tutorialv2/stats/index.html | 2 ++ zds/tutorialv2/epub_utils.py | 2 +- zds/tutorialv2/forms.py | 4 ++++ zds/tutorialv2/models/quizz.py | 13 +++++++++++ zds/tutorialv2/publish_container.py | 4 +++- zds/tutorialv2/urls/urls_contents.py | 8 ++++++- zds/tutorialv2/views/statistics.py | 18 +++++++++++++-- zds/utils/templatetags/dict_get.py | 0 12 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 templates/misc/quizz.graph.part.html create mode 100644 zds/tutorialv2/models/quizz.py create mode 100644 zds/utils/templatetags/dict_get.py diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index b1d30e7256..8b82fd7ef8 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -10,6 +10,7 @@ function extractAnswer(radio, answers) { rb.parentElement.parentElement.setAttribute('id', 'id-' + (index++)) } rb.setAttribute('name', rb.parentElement.parentElement.getAttribute('id')) + rb.parentElement.parentElement.parentElement.parentElement.setAttribute('data-name', rb.getAttribute('name')) if (!answers[rb.parentNode.parentNode.getAttribute('id')]) { answers[rb.parentNode.parentNode.getAttribute('id')] = [rb.checked] } else { @@ -61,15 +62,16 @@ function computeForm(formdata, answers) { function markBadAnswers(names, answers) { const toAdd = [] - names.forEach(({ name }) => { + names.forEach(({name}) => { document.querySelectorAll('input[name="' + name + '"]').forEach(field => { if (answers[name][parseInt(field.getAttribute('value'), 10)] && !field.checked) { field.parentElement.classList.add('quizz-forget') - toAdd.push({ name: name, value: field.getAttribute('value') }) + toAdd.push({name: name, value: field.getAttribute('value')}) } }) + document.querySelector(`.custom-block[data-name=${name}]`).classList.add('quizz-bad') }) - names.forEach(({ name, value }) => { + names.forEach(({name, value}) => { document.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`) .parentElement.classList.add('quizz-bad') }) @@ -92,7 +94,25 @@ document.querySelectorAll('form.quizz').forEach(form => { questions.push(result.name) } }) - alert('Vous mal avez répondu à ' + questions.length + ' questions.') + const statistics = {} + Object.keys(answers).forEach(name => { + const element = document.querySelector(`.custom-block[data-name="${name}"]`) + const title = document.querySelector(`.custom-block[data-name="${name}"] .custom-block-heading`).textContent + if (!element.classList.contains('quizz-bad')) { + element.classList.add('quizz-good') + statistics[title] = 'ok' + } else { + statistics[title] = 'bad' + } + }) + const csrfmiddlewaretoken = document.querySelector("input[name='csrfmiddlewaretoken']").value + const xhttp = new XMLHttpRequest() + xhttp.open('POST', form.getAttribute('action')) + xhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest') + xhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') + xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) + statistics.url = form.parentElement.previousElementSibling.firstElementChild.href + xhttp.send(Object.keys(statistics).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(statistics[k])).join('&')) // here send result console.log(result) }) diff --git a/assets/scss/base/_content.scss b/assets/scss/base/_content.scss index b9c7062f46..93b6e0c698 100644 --- a/assets/scss/base/_content.scss +++ b/assets/scss/base/_content.scss @@ -115,6 +115,24 @@ h6 { } +.quizz .custom-block { + .custom-block { + display: none; + } + &.quizz-bad { + background-color: $color-danger !important; + .custom-block { + display: block; + } + } + &.quizz-good { + background:$color-success !important; + .custom-block { + display: block; + } + } + +} .custom-block { margin: $length-24 0; diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html new file mode 100644 index 0000000000..d68e06479f --- /dev/null +++ b/templates/misc/quizz.graph.part.html @@ -0,0 +1,17 @@ +{% load i18n %} +{% load dict_get %} + + +
    +
    +

    {% trans graph_title %}

    + + +
    +
    diff --git a/templates/tutorialv2/export/chapter.html b/templates/tutorialv2/export/chapter.html index 60c2fd6bfc..07f2a06b45 100644 --- a/templates/tutorialv2/export/chapter.html +++ b/templates/tutorialv2/export/chapter.html @@ -26,8 +26,9 @@

    {% if rendered_extract.text %} - {% if rendered_extract.is_quizz %} -
    +
    + {% if rendered_extract.is_quizz %} + {% endif %} {{ rendered_extract.get_text|emarkdown:is_js }} {% if rendered_extract.is_quizz %} diff --git a/templates/tutorialv2/stats/index.html b/templates/tutorialv2/stats/index.html index eca63b1a25..38635111d1 100644 --- a/templates/tutorialv2/stats/index.html +++ b/templates/tutorialv2/stats/index.html @@ -47,11 +47,13 @@

    {% trans "Pages vues" %} {% trans "Temps moyen de lecture" %} {% trans "Nombre de visiteurs uniques" %} + {% trans "Quizz" %}

    {% include "misc/graph.part.html" with tab_name="tab-view-graph-content" graph_title="Évolution des pages vues sur le contenu" canvas_id="view-graph" report_key="nb_hits" y_label="Nombre de pages" %} {% include "misc/graph.part.html" with tab_name="tab-visit-time-graph-content" graph_title="Évolution du temps moyen lecture (en secondes)" canvas_id="visit-time-graph" report_key="avg_time_on_page" y_label="Secondes" %} {% include "misc/graph.part.html" with tab_name="tab-users-graph-content" graph_title="Évolution du nombre de visiteurs uniques" canvas_id="users-graph" report_key="nb_uniq_visitors" y_label="Nombre de visiteurs" %} + {% include "misc/graph.part.html" with tab_name="tab-quizz-content" graph_title="Réponses au quizz" canvas_id="quizz-graph" metric="quizz" %} {% endif %} {% if cumulative_stats %} diff --git a/zds/tutorialv2/epub_utils.py b/zds/tutorialv2/epub_utils.py index 82769479af..d73ad8bb97 100644 --- a/zds/tutorialv2/epub_utils.py +++ b/zds/tutorialv2/epub_utils.py @@ -63,7 +63,7 @@ def build_html_chapter_file(published_object, versioned_object, working_dir, roo :param versioned_object: the object representing the public version in git file system :type versioned_object: zds.tutorialv2.models.models_versioned.VersionedContent :param published_object: the published content as saved in database - :type published_object: zds.tutorialv2.models.models_database.PublishedContent + :type published_object: zds.tutorialv2.models.models_database.PublishableCOntent :type image_handler: ImageHandling :return: a generator of tuples composed as ``[splitted_html_file_relative_path, chapter-identifier, chapter-title]`` """ diff --git a/zds/tutorialv2/forms.py b/zds/tutorialv2/forms.py index d1088fbaa5..2cb914ec34 100644 --- a/zds/tutorialv2/forms.py +++ b/zds/tutorialv2/forms.py @@ -42,6 +42,10 @@ def clean(self): return cleaned_data +class QuizzStatsForm(forms.Form): + url = forms.CharField(required=True) + + class ReviewerTypeModelChoiceField(forms.ModelChoiceField): def label_from_instance(self, obj): return obj.title diff --git a/zds/tutorialv2/models/quizz.py b/zds/tutorialv2/models/quizz.py new file mode 100644 index 0000000000..29b7ae9fa6 --- /dev/null +++ b/zds/tutorialv2/models/quizz.py @@ -0,0 +1,13 @@ +from django.db import models + +from zds.tutorialv2.models.database import PublishableContent + + +class QuizzStat(models.Model): + related_content = models.ForeignKey(PublishableContent, + verbose_name='Tutoriel lié', + blank=True, null=True, + on_delete=models.CASCADE) + url = models.TextField(name='url', verbose_name='url', null=False, blank=False) + question = models.TextField(name="question", verbose_name="question", null=False, blank=False) + answer = models.TextField(name="answer", verbose_name="anwser", null=False, blank=False) diff --git a/zds/tutorialv2/publish_container.py b/zds/tutorialv2/publish_container.py index 28e2195bc0..bf8104e38d 100644 --- a/zds/tutorialv2/publish_container.py +++ b/zds/tutorialv2/publish_container.py @@ -122,7 +122,7 @@ def render_chapter_or_minituto(base_dir, container, ctx, rendered, template): def publish_container( - db_object, + db_object: PublishableContent, base_dir, container, template="tutorialv2/export/chapter.html", @@ -151,6 +151,8 @@ def publish_container( path_to_title_dict = collections.OrderedDict() ctx["relative"] = ctx.get("relative", ".") + ctx["content"] = db_object + ctx["public_content"] = db_object.public_version if not isinstance(container, Container): raise FailureDuringPublication(_("Le conteneur n'en est pas un !")) diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index e75a3afaba..22970e9f5d 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -31,7 +31,7 @@ from zds.tutorialv2.views.lists import TagsListView, ContentOfAuthor, ListContentReactions from zds.tutorialv2.views.alerts import SendContentAlert, SolveContentAlert from zds.tutorialv2.views.misc import RequestFeaturedContent, FollowNewContent, WarnTypo -from zds.tutorialv2.views.statistics import ContentStatisticsView +from zds.tutorialv2.views.statistics import ContentStatisticsView, ContentQuizzStatistics from zds.tutorialv2.views.comments import ( SendNoteFormView, UpdateNoteView, @@ -207,4 +207,10 @@ path("modifier-objectifs/", MassEditGoals.as_view(), name="mass-edit-goals"), path("modifier-objectifs//", EditGoals.as_view(), name="edit-goals"), path("objectifs/", ViewContentsByGoal.as_view(), name="view-goals"), + # quizz + path( + "reponses///", + ContentQuizzStatistics.as_view(), + name="answer-quizz" + ), ] diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index 90ecd4058e..f764bc55fe 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -10,8 +10,9 @@ from django.utils.translation import gettext_lazy as _ from django.views.generic import FormView -from zds.tutorialv2.forms import ContentCompareStatsURLForm -from zds.tutorialv2.mixins import SingleOnlineContentDetailViewMixin +from zds.tutorialv2.forms import ContentCompareStatsURLForm, QuizzStatsForm +from zds.tutorialv2.mixins import SingleOnlineContentDetailViewMixin, SingleOnlineContentFormViewMixin +from zds.tutorialv2.models.quizz import QuizzStat from zds.tutorialv2.utils import NamedUrl @@ -24,6 +25,19 @@ def __init__(self, logger, msg): super().__init__(logger, msg) +class ContentQuizzStatistics(SingleOnlineContentFormViewMixin): + form_class = QuizzStatsForm + + def form_valid(self, form): + url = form.cleaned_data['url'] + answers = {k: v for k, v in self.request.POST.items() if k != 'url'} + for question, answer in answers.items(): + stat = QuizzStat(related_content=self.object, url=url, answer=answer, question=question) + stat.save() + self.success_url = self.object.get_absolute_url_online() + return super(ContentQuizzStatistics, self).form_valid(form) + + class ContentStatisticsView(SingleOnlineContentDetailViewMixin, FormView): template_name = "tutorialv2/stats/index.html" form_class = ContentCompareStatsURLForm diff --git a/zds/utils/templatetags/dict_get.py b/zds/utils/templatetags/dict_get.py new file mode 100644 index 0000000000..e69de29bb2 From 3b570b26d1e96862b844374034594cad4b8bbae5 Mon Sep 17 00:00:00 2001 From: artragis Date: Sat, 20 Feb 2021 18:00:16 +0100 Subject: [PATCH 05/78] =?UTF-8?q?Permet=20de=20bien=20corriger=20les=20qui?= =?UTF-8?q?zz=20o=C3=B9=20une=20r=C3=A9ponse=20n'a=20pas=20=C3=A9t=C3=A9?= =?UTF-8?q?=20donn=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/charts.js | 40 +++++++++-- assets/js/content-quizz.js | 35 +++++++--- assets/scss/base/_content.scss | 1 + templates/misc/quizz.graph.part.html | 28 +++++--- templates/tutorialv2/export/chapter.html | 2 +- zds/tutorialv2/models/quizz.py | 33 +++++++-- zds/tutorialv2/views/statistics.py | 85 ++++++++++++++++++++++-- 7 files changed, 183 insertions(+), 41 deletions(-) diff --git a/assets/js/charts.js b/assets/js/charts.js index 888a427bbc..c6a12d20cd 100644 --- a/assets/js/charts.js +++ b/assets/js/charts.js @@ -37,12 +37,35 @@ const t = v * (1 - s * (1 - f)) switch (i) { - case 0: r = v; g = t; b = p; break - case 1: r = q; g = v; b = p; break - case 2: r = p; g = v; b = t; break - case 3: r = p; g = q; b = v; break - case 4: r = t; g = p; b = v; break - default: r = v; g = p; b = q + case 0: + r = v + g = t + b = p + break + case 1: + r = q + g = v + b = p + break + case 2: + r = p + g = v + b = t + break + case 3: + r = p + g = q + b = v + break + case 4: + r = t + g = p + b = v + break + default: + r = v + g = p + b = q } return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)] @@ -172,7 +195,12 @@ setupChart(el, chartFormatters[g]) } } + const quizzCharts = $('.quizz-chart') + if (quizzCharts.length) { + setupPie(quizzCharts) + } } + drawCharts() // Tab management diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 8b82fd7ef8..0b36afacae 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -62,16 +62,19 @@ function computeForm(formdata, answers) { function markBadAnswers(names, answers) { const toAdd = [] - names.forEach(({name}) => { + names.forEach(({ name }) => { document.querySelectorAll('input[name="' + name + '"]').forEach(field => { if (answers[name][parseInt(field.getAttribute('value'), 10)] && !field.checked) { field.parentElement.classList.add('quizz-forget') - toAdd.push({name: name, value: field.getAttribute('value')}) + toAdd.push({ + name: name, + value: field.getAttribute('value') + }) } }) document.querySelector(`.custom-block[data-name=${name}]`).classList.add('quizz-bad') }) - names.forEach(({name, value}) => { + names.forEach(({ name, value }) => { document.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`) .parentElement.classList.add('quizz-bad') }) @@ -94,25 +97,37 @@ document.querySelectorAll('form.quizz').forEach(form => { questions.push(result.name) } }) - const statistics = {} + const statistics = { + expected: {}, + result: {} + } Object.keys(answers).forEach(name => { const element = document.querySelector(`.custom-block[data-name="${name}"]`) const title = document.querySelector(`.custom-block[data-name="${name}"] .custom-block-heading`).textContent + statistics.result[title] = { + evaluation: 'bad', + labels: [] + } + statistics.expected[title] = {} + const availableResponses = element.querySelectorAll('input') + for (let i = 0; i < availableResponses.length; i++) { + statistics.expected[title][availableResponses[i].parentElement.textContent] = answers[name][i] + } + element.querySelectorAll('input:checked') + .forEach(node => statistics.result[title].labels.push(node.parentElement.textContent.trim())) if (!element.classList.contains('quizz-bad')) { element.classList.add('quizz-good') - statistics[title] = 'ok' - } else { - statistics[title] = 'bad' + statistics.result[title].evaluation = 'ok' } }) - const csrfmiddlewaretoken = document.querySelector("input[name='csrfmiddlewaretoken']").value + const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value const xhttp = new XMLHttpRequest() xhttp.open('POST', form.getAttribute('action')) xhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest') - xhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') + xhttp.setRequestHeader('Content-Type', 'application/json') xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) statistics.url = form.parentElement.previousElementSibling.firstElementChild.href - xhttp.send(Object.keys(statistics).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(statistics[k])).join('&')) + xhttp.send(JSON.stringify(statistics)) // here send result console.log(result) }) diff --git a/assets/scss/base/_content.scss b/assets/scss/base/_content.scss index 93b6e0c698..d4a6675805 100644 --- a/assets/scss/base/_content.scss +++ b/assets/scss/base/_content.scss @@ -242,6 +242,7 @@ h6 { } } .custom-block-neutral, +.custom-block-quizz, .custom-block-spoiler { background: $grey-100; diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index d68e06479f..ff7e5d2315 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -3,15 +3,21 @@
    -
    -

    {% trans graph_title %}

    - - -
    + {% for url, questions in quizz.items %} +
    +

    + + {% for question, stats in questions.items %} +
    +

    {% trans graph_title %} {{ question }}

    +
      + {% for answer_name, answer_stats in stats.responses.items %} +
    • {{ answer_name }} + +
    • + {% endfor %} +
    +
    + {% endfor %} +{% endfor %}
    diff --git a/templates/tutorialv2/export/chapter.html b/templates/tutorialv2/export/chapter.html index 07f2a06b45..f7ccea06de 100644 --- a/templates/tutorialv2/export/chapter.html +++ b/templates/tutorialv2/export/chapter.html @@ -28,7 +28,7 @@

    {% if rendered_extract.text %}
    {% if rendered_extract.is_quizz %} - + {% endif %} {{ rendered_extract.get_text|emarkdown:is_js }} {% if rendered_extract.is_quizz %} diff --git a/zds/tutorialv2/models/quizz.py b/zds/tutorialv2/models/quizz.py index 29b7ae9fa6..3de3418a5a 100644 --- a/zds/tutorialv2/models/quizz.py +++ b/zds/tutorialv2/models/quizz.py @@ -3,11 +3,32 @@ from zds.tutorialv2.models.database import PublishableContent -class QuizzStat(models.Model): - related_content = models.ForeignKey(PublishableContent, - verbose_name='Tutoriel lié', - blank=True, null=True, - on_delete=models.CASCADE) - url = models.TextField(name='url', verbose_name='url', null=False, blank=False) +class QuizzQuestion(models.Model): + url = models.TextField(name="url", verbose_name="url", null=False, blank=False) question = models.TextField(name="question", verbose_name="question", null=False, blank=False) + + +class QuizzAvailableAnswer(models.Model): + label = models.TextField(name="label", verbose_name="Intitulé de la réponse") + good_answer = models.BooleanField(name="is_good", verbose_name="Est une réponse attendue", default=False) + related_question = models.ForeignKey( + QuizzQuestion, name="related_question", verbose_name="Question liée", on_delete=models.CASCADE + ) + + +class QuizzUserAnswer(models.Model): + related_content = models.ForeignKey( + PublishableContent, verbose_name="Tutoriel lié", blank=True, null=True, on_delete=models.CASCADE + ) answer = models.TextField(name="answer", verbose_name="anwser", null=False, blank=False) + date_answer = models.DateField(name="date_answer", verbose_name="Date of answer", null=False, auto_now=True) + related_question = models.ForeignKey( + QuizzQuestion, name="related_question", verbose_name="Question liée", on_delete=models.CASCADE + ) + full_answer_id = models.CharField( + name="full_answer_id", + verbose_name="Indentifiant de la réponse utilisateur", + blank=False, + max_length=64, + default="id", + ) diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index f764bc55fe..dc40ad3b66 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -1,18 +1,22 @@ import itertools +import uuid +from collections import OrderedDict import logging import urllib.parse from datetime import timedelta, datetime, date +from json import loads import requests from django.conf import settings from django.contrib import messages from django.core.exceptions import PermissionDenied +from django.db.models import Count from django.utils.translation import gettext_lazy as _ from django.views.generic import FormView from zds.tutorialv2.forms import ContentCompareStatsURLForm, QuizzStatsForm from zds.tutorialv2.mixins import SingleOnlineContentDetailViewMixin, SingleOnlineContentFormViewMixin -from zds.tutorialv2.models.quizz import QuizzStat +from zds.tutorialv2.models.quizz import QuizzUserAnswer, QuizzQuestion, QuizzAvailableAnswer from zds.tutorialv2.utils import NamedUrl @@ -28,12 +32,37 @@ def __init__(self, logger, msg): class ContentQuizzStatistics(SingleOnlineContentFormViewMixin): form_class = QuizzStatsForm + def post(self, request, *args, **kwargs): + request.POST = loads(request.body) + super(ContentQuizzStatistics, self).post(request, *args, **kwargs) + def form_valid(self, form): - url = form.cleaned_data['url'] - answers = {k: v for k, v in self.request.POST.items() if k != 'url'} - for question, answer in answers.items(): - stat = QuizzStat(related_content=self.object, url=url, answer=answer, question=question) - stat.save() + url = form.cleaned_data["url"] + answers = {k: v for k, v in self.request.POST["result"].items()} + resp_id = str(uuid.uuid4()) + for question, answers in answers.items(): + db_question = QuizzQuestion.objects.filter(question=question, url=url).first() + if not db_question: + db_question = QuizzQuestion(question=question, url=url) + db_question.save() + given_available_answers = self.request.POST["expected"][question] + answers_labels = list(given_available_answers.keys()) + known_labels = QuizzAvailableAnswer.objects.filter( + related_question=db_question, label__in=answers_labels + ).values_list("label", flat=True) + not_existing_answers = [l for l in answers_labels if l not in known_labels] + QuizzAvailableAnswer.objects.exclude(label__in=answers_labels).filter(related_question=db_question).delete() + + for label in not_existing_answers: + db_answer = QuizzAvailableAnswer( + related_question=db_question, label=label, is_good=given_available_answers[label] + ) + db_answer.save() + for answer in answers["labels"]: + stat = QuizzUserAnswer( + related_content=self.object, related_question=db_question, full_answer_id=resp_id, answer=answer + ) + stat.save() self.success_url = self.object.get_absolute_url_online() return super(ContentQuizzStatistics, self).form_valid(form) @@ -131,7 +160,7 @@ def get_stat_metrics(data, metric_name): else: y.append(val[0].get(metric_name, 0)) - return (x, y) + return x, y @staticmethod def get_ref_metrics(data): @@ -277,6 +306,47 @@ def get_context_data(self, **kwargs): if display_mode.lower() == "global": reports = {NamedUrl(display_mode, "", 0): self.merge_report_to_global(reports, report_field)} + quizz_stats = {} + base_questions = list( + QuizzUserAnswer.objects.filter( + date_answer__range=(start_date, end_date), related_content__pk=self.object.pk + ).values_list("related_question", flat=True) + ) + total_per_question = list( + QuizzUserAnswer.objects.values("related_question__pk") + .filter(related_question__pk__in=base_questions, date_answer__range=(start_date, end_date)) + .annotate(nb=Count("full_answer_id")) + ) + total_per_question = {a["related_question__pk"]: a["nb"] for a in total_per_question} + total_per_label = list( + QuizzUserAnswer.objects.values( + "related_question__pk", "related_question__question", "related_question__url", "answer" + ) + .filter(related_question__in=base_questions, date_answer__range=(start_date, end_date)) + .annotate(nb=Count("answer")) + ) + + for base_question in set(base_questions): + full_answers_total = {} + url = "" + question = "" + for available_answer in ( + QuizzAvailableAnswer.objects.filter(related_question__pk=base_question) + .prefetch_related("related_question") + .all() + ): + full_answers_total[available_answer.label] = {"good": available_answer.is_good, "nb": 0} + url = available_answer.related_question.url + question = available_answer.related_question.question + for r in total_per_label: + if ( + r["related_question__pk"] == base_question + and r["answer"].strip() == available_answer.label.strip() + ): + full_answers_total[available_answer.label]["nb"] = r["nb"] + if url not in quizz_stats: + quizz_stats[url] = OrderedDict() + quizz_stats[url][question] = {"total": total_per_question[base_question], "responses": full_answers_total} context.update( { "display": display_mode, @@ -286,6 +356,7 @@ def get_context_data(self, **kwargs): "referrers": referrers, "type_referrers": type_referrers, "keywords": keywords, + "quizz": quizz_stats, } ) return context From 9fd81bbd6b92c2f71abbdc82710a461f9e84e90b Mon Sep 17 00:00:00 2001 From: artragis Date: Sun, 27 Sep 2020 18:20:48 +0200 Subject: [PATCH 06/78] =?UTF-8?q?mod=C3=A8les=20de=20stats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/tutorialv2/migrations/0031_quizzstat.py | 25 ++++++++++ .../migrations/0032_auto_20200927_1015.py | 48 +++++++++++++++++++ .../0033_quizzuseranswer_full_answer_id.py | 18 +++++++ .../migrations/0034_auto_20200927_1105.py | 18 +++++++ 4 files changed, 109 insertions(+) create mode 100644 zds/tutorialv2/migrations/0031_quizzstat.py create mode 100644 zds/tutorialv2/migrations/0032_auto_20200927_1015.py create mode 100644 zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py create mode 100644 zds/tutorialv2/migrations/0034_auto_20200927_1105.py diff --git a/zds/tutorialv2/migrations/0031_quizzstat.py b/zds/tutorialv2/migrations/0031_quizzstat.py new file mode 100644 index 0000000000..606a26c593 --- /dev/null +++ b/zds/tutorialv2/migrations/0031_quizzstat.py @@ -0,0 +1,25 @@ +# Generated by Django 2.2.16 on 2020-09-26 15:35 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tutorialv2', '0030_contentsuggestion'), + ] + + operations = [ + migrations.CreateModel( + name='QuizzStat', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.TextField(verbose_name='url')), + ('question', models.TextField(verbose_name='question')), + ('answer', models.TextField(verbose_name='anwser')), + ('date_answer', models.DateField(auto_now=True, verbose_name='Date of answer')), + ('related_content', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.PublishableContent', verbose_name='Tutoriel lié')), + ], + ), + ] diff --git a/zds/tutorialv2/migrations/0032_auto_20200927_1015.py b/zds/tutorialv2/migrations/0032_auto_20200927_1015.py new file mode 100644 index 0000000000..14cc1a37a3 --- /dev/null +++ b/zds/tutorialv2/migrations/0032_auto_20200927_1015.py @@ -0,0 +1,48 @@ +# Generated by Django 2.2.16 on 2020-09-27 10:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tutorialv2', '0031_quizzstat'), + ] + + operations = [ + migrations.CreateModel( + name='QuizzAvailableAnswer', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('answer_label', models.TextField(verbose_name='Intitulé de la réponse')), + ('is_good', models.BooleanField(default=False, verbose_name='Est une réponse attendue')), + ], + ), + migrations.CreateModel( + name='QuizzQuestion', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.TextField(verbose_name='url')), + ('question', models.TextField(verbose_name='question')), + ], + ), + migrations.CreateModel( + name='QuizzUserAnswer', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('answer', models.TextField(verbose_name='anwser')), + ('date_answer', models.DateField(auto_now=True, verbose_name='Date of answer')), + ('related_content', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.PublishableContent', verbose_name='Tutoriel lié')), + ('related_question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.QuizzQuestion', verbose_name='Question liée')), + ], + ), + migrations.DeleteModel( + name='QuizzStat', + ), + migrations.AddField( + model_name='quizzavailableanswer', + name='related_question', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.QuizzQuestion', verbose_name='Question liée'), + ), + ] diff --git a/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py b/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py new file mode 100644 index 0000000000..8672c4500a --- /dev/null +++ b/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.16 on 2020-09-27 11:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tutorialv2', '0032_auto_20200927_1015'), + ] + + operations = [ + migrations.AddField( + model_name='quizzuseranswer', + name='full_answer_id', + field=models.CharField(default='id', max_length=64, verbose_name='Indentifiant de la réponse utilisateur'), + ), + ] diff --git a/zds/tutorialv2/migrations/0034_auto_20200927_1105.py b/zds/tutorialv2/migrations/0034_auto_20200927_1105.py new file mode 100644 index 0000000000..3214a25163 --- /dev/null +++ b/zds/tutorialv2/migrations/0034_auto_20200927_1105.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.16 on 2020-09-27 11:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tutorialv2', '0033_quizzuseranswer_full_answer_id'), + ] + + operations = [ + migrations.RenameField( + model_name='quizzavailableanswer', + old_name='answer_label', + new_name='label', + ), + ] From 20645513f7e07469b89a02d2e1dc2490ff9a0ba0 Mon Sep 17 00:00:00 2001 From: artragis Date: Fri, 9 Oct 2020 17:58:21 +0200 Subject: [PATCH 07/78] Utilise la fonction get_or_create de django qui est plus robuste --- zds/notification/managers.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/zds/notification/managers.py b/zds/notification/managers.py index 9d020938b2..91a5a24413 100644 --- a/zds/notification/managers.py +++ b/zds/notification/managers.py @@ -73,14 +73,13 @@ def get_or_create_active(self, user, content_object): :return: subscription """ content_type = ContentType.objects.get_for_model(content_object) - try: - subscription = self.get(object_id=content_object.pk, content_type__pk=content_type.pk, user=user) - if not subscription.is_active: - subscription.activate() - except ObjectDoesNotExist: - subscription = self.model(user=user, content_object=content_object) - subscription.save() - + # the get_or_create boolean flag is kind of useless for us + subscription = self.get_or_create( + object_id=content_object.pk, + content_type__pk=content_type.pk, + user=user)[0] + if not subscription.is_active: + subscription.activate() return subscription def get_subscriptions(self, content_object, is_active=True): From cd7fc9594185d257077bee4668f8bad8e4107bbf Mon Sep 17 00:00:00 2001 From: artragis Date: Sat, 14 Nov 2020 17:44:24 +0100 Subject: [PATCH 08/78] ne pas fusionner la correction --- assets/js/content-quizz.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 0b36afacae..7e872fa05a 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -103,7 +103,11 @@ document.querySelectorAll('form.quizz').forEach(form => { } Object.keys(answers).forEach(name => { const element = document.querySelector(`.custom-block[data-name="${name}"]`) - const title = document.querySelector(`.custom-block[data-name="${name}"] .custom-block-heading`).textContent + let title = element.querySelector('.custom-block-heading').textContent + const correction = element.querySelector('.custom-block-heading+div') + if (correction) { + title = title.substr(0, title.indexOf(correction.textContent)) + } statistics.result[title] = { evaluation: 'bad', labels: [] From 2d5471b64290275900b9ca0a6fc93d20f072e4f8 Mon Sep 17 00:00:00 2001 From: artragis Date: Sat, 19 Dec 2020 19:41:28 +0100 Subject: [PATCH 09/78] reformat --- zds/notification/managers.py | 5 +- zds/tutorialv2/migrations/0031_quizzstat.py | 25 ++++++--- .../migrations/0032_auto_20200927_1015.py | 56 ++++++++++++------- .../0033_quizzuseranswer_full_answer_id.py | 8 +-- .../migrations/0034_auto_20200927_1105.py | 8 +-- zds/tutorialv2/views/containers_extracts.py | 3 +- 6 files changed, 64 insertions(+), 41 deletions(-) diff --git a/zds/notification/managers.py b/zds/notification/managers.py index 91a5a24413..4d23928c94 100644 --- a/zds/notification/managers.py +++ b/zds/notification/managers.py @@ -74,10 +74,7 @@ def get_or_create_active(self, user, content_object): """ content_type = ContentType.objects.get_for_model(content_object) # the get_or_create boolean flag is kind of useless for us - subscription = self.get_or_create( - object_id=content_object.pk, - content_type__pk=content_type.pk, - user=user)[0] + subscription = self.get_or_create(object_id=content_object.pk, content_type__pk=content_type.pk, user=user)[0] if not subscription.is_active: subscription.activate() return subscription diff --git a/zds/tutorialv2/migrations/0031_quizzstat.py b/zds/tutorialv2/migrations/0031_quizzstat.py index 606a26c593..23ed354d75 100644 --- a/zds/tutorialv2/migrations/0031_quizzstat.py +++ b/zds/tutorialv2/migrations/0031_quizzstat.py @@ -7,19 +7,28 @@ class Migration(migrations.Migration): dependencies = [ - ('tutorialv2', '0030_contentsuggestion'), + ("tutorialv2", "0030_contentsuggestion"), ] operations = [ migrations.CreateModel( - name='QuizzStat', + name="QuizzStat", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.TextField(verbose_name='url')), - ('question', models.TextField(verbose_name='question')), - ('answer', models.TextField(verbose_name='anwser')), - ('date_answer', models.DateField(auto_now=True, verbose_name='Date of answer')), - ('related_content', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.PublishableContent', verbose_name='Tutoriel lié')), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("url", models.TextField(verbose_name="url")), + ("question", models.TextField(verbose_name="question")), + ("answer", models.TextField(verbose_name="anwser")), + ("date_answer", models.DateField(auto_now=True, verbose_name="Date of answer")), + ( + "related_content", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="tutorialv2.PublishableContent", + verbose_name="Tutoriel lié", + ), + ), ], ), ] diff --git a/zds/tutorialv2/migrations/0032_auto_20200927_1015.py b/zds/tutorialv2/migrations/0032_auto_20200927_1015.py index 14cc1a37a3..df5e093c6b 100644 --- a/zds/tutorialv2/migrations/0032_auto_20200927_1015.py +++ b/zds/tutorialv2/migrations/0032_auto_20200927_1015.py @@ -7,42 +7,60 @@ class Migration(migrations.Migration): dependencies = [ - ('tutorialv2', '0031_quizzstat'), + ("tutorialv2", "0031_quizzstat"), ] operations = [ migrations.CreateModel( - name='QuizzAvailableAnswer', + name="QuizzAvailableAnswer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('answer_label', models.TextField(verbose_name='Intitulé de la réponse')), - ('is_good', models.BooleanField(default=False, verbose_name='Est une réponse attendue')), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("answer_label", models.TextField(verbose_name="Intitulé de la réponse")), + ("is_good", models.BooleanField(default=False, verbose_name="Est une réponse attendue")), ], ), migrations.CreateModel( - name='QuizzQuestion', + name="QuizzQuestion", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.TextField(verbose_name='url')), - ('question', models.TextField(verbose_name='question')), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("url", models.TextField(verbose_name="url")), + ("question", models.TextField(verbose_name="question")), ], ), migrations.CreateModel( - name='QuizzUserAnswer', + name="QuizzUserAnswer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('answer', models.TextField(verbose_name='anwser')), - ('date_answer', models.DateField(auto_now=True, verbose_name='Date of answer')), - ('related_content', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.PublishableContent', verbose_name='Tutoriel lié')), - ('related_question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.QuizzQuestion', verbose_name='Question liée')), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("answer", models.TextField(verbose_name="anwser")), + ("date_answer", models.DateField(auto_now=True, verbose_name="Date of answer")), + ( + "related_content", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="tutorialv2.PublishableContent", + verbose_name="Tutoriel lié", + ), + ), + ( + "related_question", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="tutorialv2.QuizzQuestion", + verbose_name="Question liée", + ), + ), ], ), migrations.DeleteModel( - name='QuizzStat', + name="QuizzStat", ), migrations.AddField( - model_name='quizzavailableanswer', - name='related_question', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.QuizzQuestion', verbose_name='Question liée'), + model_name="quizzavailableanswer", + name="related_question", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="tutorialv2.QuizzQuestion", verbose_name="Question liée" + ), ), ] diff --git a/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py b/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py index 8672c4500a..1796f4fd8b 100644 --- a/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py +++ b/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('tutorialv2', '0032_auto_20200927_1015'), + ("tutorialv2", "0032_auto_20200927_1015"), ] operations = [ migrations.AddField( - model_name='quizzuseranswer', - name='full_answer_id', - field=models.CharField(default='id', max_length=64, verbose_name='Indentifiant de la réponse utilisateur'), + model_name="quizzuseranswer", + name="full_answer_id", + field=models.CharField(default="id", max_length=64, verbose_name="Indentifiant de la réponse utilisateur"), ), ] diff --git a/zds/tutorialv2/migrations/0034_auto_20200927_1105.py b/zds/tutorialv2/migrations/0034_auto_20200927_1105.py index 3214a25163..0c31ee2e15 100644 --- a/zds/tutorialv2/migrations/0034_auto_20200927_1105.py +++ b/zds/tutorialv2/migrations/0034_auto_20200927_1105.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('tutorialv2', '0033_quizzuseranswer_full_answer_id'), + ("tutorialv2", "0033_quizzuseranswer_full_answer_id"), ] operations = [ migrations.RenameField( - model_name='quizzavailableanswer', - old_name='answer_label', - new_name='label', + model_name="quizzavailableanswer", + old_name="answer_label", + new_name="label", ), ] diff --git a/zds/tutorialv2/views/containers_extracts.py b/zds/tutorialv2/views/containers_extracts.py index 96589d7c62..2e3bb528cf 100644 --- a/zds/tutorialv2/views/containers_extracts.py +++ b/zds/tutorialv2/views/containers_extracts.py @@ -217,8 +217,7 @@ def form_valid(self, form): parent = search_container_or_404(self.versioned_object, self.kwargs) sha = parent.repo_add_extract( - form.cleaned_data["title"], form.cleaned_data["text"], form.cleaned_data["msg_commit"], - quizz=self.quizz + form.cleaned_data["title"], form.cleaned_data["text"], form.cleaned_data["msg_commit"], quizz=self.quizz ) # then save From 109edc7fe5dea1731b9681e4046ce60bd377c674 Mon Sep 17 00:00:00 2001 From: artragis Date: Sat, 19 Dec 2020 20:01:01 +0100 Subject: [PATCH 10/78] merge migrations --- .../migrations/0035_merge_20201219_1959.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 zds/tutorialv2/migrations/0035_merge_20201219_1959.py diff --git a/zds/tutorialv2/migrations/0035_merge_20201219_1959.py b/zds/tutorialv2/migrations/0035_merge_20201219_1959.py new file mode 100644 index 0000000000..a1d799f858 --- /dev/null +++ b/zds/tutorialv2/migrations/0035_merge_20201219_1959.py @@ -0,0 +1,13 @@ +# Generated by Django 2.2.17 on 2020-12-19 19:59 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("tutorialv2", "0031_source_is_url"), + ("tutorialv2", "0034_auto_20200927_1105"), + ] + + operations = [] From 15772e7fb2c72e8610a3fd8afc8e9b074fc0e276 Mon Sep 17 00:00:00 2001 From: artragis Date: Sat, 19 Dec 2020 20:10:30 +0100 Subject: [PATCH 11/78] Revert bad modif --- zds/notification/managers.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/zds/notification/managers.py b/zds/notification/managers.py index 4d23928c94..9d020938b2 100644 --- a/zds/notification/managers.py +++ b/zds/notification/managers.py @@ -73,10 +73,14 @@ def get_or_create_active(self, user, content_object): :return: subscription """ content_type = ContentType.objects.get_for_model(content_object) - # the get_or_create boolean flag is kind of useless for us - subscription = self.get_or_create(object_id=content_object.pk, content_type__pk=content_type.pk, user=user)[0] - if not subscription.is_active: - subscription.activate() + try: + subscription = self.get(object_id=content_object.pk, content_type__pk=content_type.pk, user=user) + if not subscription.is_active: + subscription.activate() + except ObjectDoesNotExist: + subscription = self.model(user=user, content_object=content_object) + subscription.save() + return subscription def get_subscriptions(self, content_object, is_active=True): From b2a0561126ec3c886eb4d293539c8f03ddcee374 Mon Sep 17 00:00:00 2001 From: artragis Date: Sat, 19 Dec 2020 20:22:48 +0100 Subject: [PATCH 12/78] Update templates/tutorialv2/view/content.html Co-authored-by: Situphen --- templates/tutorialv2/view/content.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/tutorialv2/view/content.html b/templates/tutorialv2/view/content.html index 78c9d5ef6b..58d7343ecd 100644 --- a/templates/tutorialv2/view/content.html +++ b/templates/tutorialv2/view/content.html @@ -237,7 +237,7 @@

    {% trans "Ajouter une section" %} {% trans 'ou'%} - {% trans "un quizz" %} + {% trans "ajouter un quizz" %}
    From 15c9c8ed3f6f5ea6fa009b4bff41e7c12e9fae23 Mon Sep 17 00:00:00 2001 From: artragis Date: Sat, 19 Dec 2020 20:23:10 +0100 Subject: [PATCH 13/78] Update templates/tutorialv2/view/container.html Co-authored-by: Situphen --- assets/js/content-quizz.js | 160 ++++++++++++++++-- assets/scss/base/_content.scss | 29 +++- assets/scss/pages/_stats.scss | 14 ++ doc/source/back-end/contents_manifest.rst | 10 ++ doc/source/back-end/quizz.rst | 41 +++++ package.json | 2 +- templates/misc/quizz.graph.part.html | 15 +- templates/tutorialv2/create/extract.html | 6 +- templates/tutorialv2/export/chapter.html | 14 +- templates/tutorialv2/includes/child.part.html | 2 +- templates/tutorialv2/stats/index.html | 2 +- templates/tutorialv2/view/container.html | 2 +- zds/tutorialv2/forms.py | 11 +- .../migrations/0035_merge_20210601_1744.py | 14 ++ .../0036_quizzquestion_question_type.py | 20 +++ .../migrations/0037_merge_20210601_1830.py | 14 ++ zds/tutorialv2/models/quizz.py | 30 ++++ zds/tutorialv2/models/versioned.py | 1 + zds/tutorialv2/publish_container.py | 2 +- zds/tutorialv2/utils.py | 4 +- zds/tutorialv2/views/statistics.py | 72 ++++---- 21 files changed, 384 insertions(+), 81 deletions(-) create mode 100644 doc/source/back-end/quizz.rst create mode 100644 zds/tutorialv2/migrations/0035_merge_20210601_1744.py create mode 100644 zds/tutorialv2/migrations/0036_quizzquestion_question_type.py create mode 100644 zds/tutorialv2/migrations/0037_merge_20210601_1830.py diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 7e872fa05a..51f2ce678a 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -16,7 +16,7 @@ function extractAnswer(radio, answers) { } else { answers[rb.parentNode.parentNode.getAttribute('id')].push(rb.checked) } - console.log(index - 1) + console.log('is checked') console.log(rb.checked) rb.setAttribute('value', answers[rb.parentNode.parentNode.getAttribute('id')].length - 1) rb.disabled = false @@ -24,24 +24,26 @@ function extractAnswer(radio, answers) { }) } -function initializeCheckboxes(answers) { +function initializeCheckboxes(answers, blockIds) { const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') - extractAnswer(checkboxes, answers) + extractAnswer(checkboxes, answers, blockIds) } -function initializeRadio(answers) { +function initializeRadio(answers, blockIds) { const radio = document.querySelectorAll('.quizz ul li input[type=radio]') - extractAnswer(radio, answers) + extractAnswer(radio, answers, blockIds) } const initializePipeline = [initializeCheckboxes, initializeRadio] function computeForm(formdata, answers) { const badAnswers = [] + const allAnswerNames = [] for (const entry of formdata.entries()) { const name = entry[0] const values = parseInt(entry[1], 10) + allAnswerNames.push(name) if (!answers[name]) { console.log('not found ' + name) continue @@ -57,11 +59,26 @@ function computeForm(formdata, answers) { } } } - return badAnswers + return [badAnswers, allAnswerNames] } function markBadAnswers(names, answers) { const toAdd = [] + if (names.length === 0) { + Object.keys(answers).forEach(answer => { + let mustMarkBad = false + answers[answer].forEach((value, index) => { + const inputAnswer = document.querySelector(`#${answer} input[value="${index}"]`) + if (value && !inputAnswer.checked) { + inputAnswer.parentElement.classList.add('quizz-forget') + mustMarkBad = true + } + }) + if (mustMarkBad) { + document.querySelector(`div[data-name="${answer}"]`).classList.add('quizz-bad') + } + }) + } names.forEach(({ name }) => { document.querySelectorAll('input[name="' + name + '"]').forEach(field => { if (answers[name][parseInt(field.getAttribute('value'), 10)] && !field.checked) { @@ -74,25 +91,115 @@ function markBadAnswers(names, answers) { }) document.querySelector(`.custom-block[data-name=${name}]`).classList.add('quizz-bad') }) - names.forEach(({ name, value }) => { + names.forEach(({ + name, + value + }) => { document.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`) .parentElement.classList.add('quizz-bad') }) toAdd.forEach(name => names.push(name)) } +function getWantedHeading(questionNode, nodeName, attr) { + let potentialHeading = questionNode + while (potentialHeading[attr] && + potentialHeading.nodeName !== nodeName.toUpperCase()) { + potentialHeading = potentialHeading[attr] + } + if (potentialHeading.nodeName !== nodeName.toUpperCase()) { + return null + } + return potentialHeading +} + +function injectForms(quizz, answers) { + const searchedTitle = quizz.getAttribute('data-heading-level') || 'h3' + const submitLabel = quizz.getAttribute('data-quizz-validate') || 'Validate' + const headings = {} + let idBias = 0 + const wrappers = [] + Object.keys(answers).forEach(blockId => { + const blockNode = document.getElementById(blockId) + if (!blockNode) { + // if the node was treated and therefore the clone has not been reinserted yet + return + } + const questionNode = blockNode.parentElement.parentElement + const heading = getWantedHeading(questionNode, searchedTitle, 'previousSibling') || quizz + if (!heading.getAttribute('id')) { + console.log('new id') + heading.setAttribute('id', `quizz-form-${idBias}`) + idBias++ + } + if (heading && !headings[heading.getAttribute('id')]) { + const wrapper = document.createElement('div') + + headings[heading.getAttribute('id')] = true + const form = document.createElement('form') + form.classList.add('quizz') + + const submit = document.createElement('button') + submit.innerText = submitLabel + submit.classList.add('btn-submit') + submit.classList.add('btn') + const result = document.createElement('p') + result.classList.add('result') + let nodeToAddToForm = heading + if (heading === quizz) { + nodeToAddToForm = quizz.firstChild + } else { + nodeToAddToForm = heading.nextSibling + } + form.method = 'POST' + form.setAttribute('action', quizz.getAttribute('data-answer-url')) + + while (nodeToAddToForm && nodeToAddToForm.nodeName !== searchedTitle.toUpperCase()) { + const current = nodeToAddToForm + nodeToAddToForm = nodeToAddToForm.nextSibling + form.appendChild(current.cloneNode(true)) + current.parentNode.removeChild(current) + } + form.appendChild(result) + form.appendChild(submit) + wrappers.push(wrapper) + if (nodeToAddToForm && nodeToAddToForm.nodeName === searchedTitle.toUpperCase()) { + wrapper.append(form) + wrappers.push(nodeToAddToForm.cloneNode(true)) + } else { + wrapper.appendChild(form) + if (heading.nextSibling) { + wrapper.appendChild(heading.nextSibling.cloneNode(true)) + heading.parentNode.removeChild(heading.nextSibling) + } + } + } + if (heading.nodeName === searchedTitle.toUpperCase()) { + quizz.removeChild(heading) + } + }) + console.log(wrappers) + wrappers.forEach((wrapper) => quizz.appendChild(wrapper)) +} + const answers = {} initializePipeline.forEach(func => func(answers)) +document.querySelectorAll('div.quizz').forEach(div => { + injectForms(div, answers) +}) document.querySelectorAll('form.quizz').forEach(form => { form.addEventListener('submit', e => { e.preventDefault() e.stopPropagation() const formData = new FormData(form) // result = name of bad answers - const result = computeForm(formData, answers) - markBadAnswers(result, answers) + const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) + markBadAnswers(badAnswerNames, answers) + allAnswerNames.forEach(name => { + document.getElementById(name).parentElement.parentElement.classList.add('hasAnswer') + }) const questions = [] - result.forEach(result => { + badAnswerNames.forEach(result => { if (questions.indexOf(result.name) === -1) { questions.push(result.name) } @@ -101,11 +208,13 @@ document.querySelectorAll('form.quizz').forEach(form => { expected: {}, result: {} } + let nbGood = 0 + let nbTotal = 0 Object.keys(answers).forEach(name => { const element = document.querySelector(`.custom-block[data-name="${name}"]`) let title = element.querySelector('.custom-block-heading').textContent const correction = element.querySelector('.custom-block-heading+div') - if (correction) { + if (correction && title.indexOf(correction.textContent) > 0) { title = title.substr(0, title.indexOf(correction.textContent)) } statistics.result[title] = { @@ -118,10 +227,22 @@ document.querySelectorAll('form.quizz').forEach(form => { statistics.expected[title][availableResponses[i].parentElement.textContent] = answers[name][i] } element.querySelectorAll('input:checked') - .forEach(node => statistics.result[title].labels.push(node.parentElement.textContent.trim())) - if (!element.classList.contains('quizz-bad')) { - element.classList.add('quizz-good') - statistics.result[title].evaluation = 'ok' + .forEach(node => { + let label = node.parentElement.textContent + if (correction && label.indexOf(correction.textContent) !== -1) { + label = label.substr(0, label.indexOf(correction.textContent)) + } + statistics.result[title].labels.push(label.trim()) + }) + if (element.classList.contains('hasAnswer')) { + nbTotal++ + + if (!element.classList.contains('quizz-bad') && + !element.classList.contains('quizz-forget')) { + element.classList.add('quizz-good') + statistics.result[title].evaluation = 'ok' + nbGood++ + } } }) const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value @@ -130,9 +251,12 @@ document.querySelectorAll('form.quizz').forEach(form => { xhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest') xhttp.setRequestHeader('Content-Type', 'application/json') xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) - statistics.url = form.parentElement.previousElementSibling.firstElementChild.href + statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href xhttp.send(JSON.stringify(statistics)) - // here send result - console.log(result) + const percentOfAnswers = (100 * 1.0 * nbGood / nbTotal).toLocaleString('fr-FR', { + minimumIntegerDigits: 1, + useGrouping: false + }) + form.querySelector('.result').innerText = `Vous avez bien répondu à ${percentOfAnswers}% des questions.` }) }) diff --git a/assets/scss/base/_content.scss b/assets/scss/base/_content.scss index d4a6675805..42b3ba3a2d 100644 --- a/assets/scss/base/_content.scss +++ b/assets/scss/base/_content.scss @@ -120,20 +120,32 @@ h6 { display: none; } &.quizz-bad { - background-color: $color-danger !important; + display: block; + & .custom-block-body { + background-color: $accent-500 ; + } .custom-block { display: block; + border: solid 1px $grey-600; } } &.quizz-good { - background:$color-success !important; + display: block; + & .custom-block-body{ + background: $green-500; + } .custom-block { display: block; + border: solid 1px $grey-600; + .custom-block-body { + font-weight: normal; + } } } } + .custom-block { margin: $length-24 0; } @@ -257,6 +269,15 @@ h6 { &:after { display: none; } + + .custom-block { + display: none; + /* hide the correction while not answered */ + &.quizz-good, &.quizz-bad, &.quizz-missing { + display: block; + } + } + } } @@ -634,6 +655,9 @@ li.task-list-item { background-color: $true-white !important; } + &.quizz-forget { + font-weight: bolder; + } &.quizz-forget { font-weight: bolder; } @@ -642,6 +666,7 @@ li.task-list-item { } } + .warn-typo { height: $length-20; } diff --git a/assets/scss/pages/_stats.scss b/assets/scss/pages/_stats.scss index 0b130f004f..f1afba9663 100644 --- a/assets/scss/pages/_stats.scss +++ b/assets/scss/pages/_stats.scss @@ -50,3 +50,17 @@ margin: 0 auto; } } + +.stat-graph ul.quizz-answers { + max-width: 50%; + li.quizz-stats { + display: grid; + grid-template-columns: 3fr 3fr 1fr; + grid-gap: 1px; + .over-progress { + display: block; + position: relative; + left: -50%; + } + } +} diff --git a/doc/source/back-end/contents_manifest.rst b/doc/source/back-end/contents_manifest.rst index 1ed2ca261e..c6ff17ae63 100644 --- a/doc/source/back-end/contents_manifest.rst +++ b/doc/source/back-end/contents_manifest.rst @@ -100,7 +100,17 @@ Version 2.0 6. ``children`` : tableau vers les enfants de niveau inférieur si l'enfant est de type *container*. **Obligatoire** 7. ``text`` : nom du fichier contenant le texte quand l'enfant est de type *extract*. Nous conseillons de garder la convention ``nom de fichier = slug.md`` mais rien n'est obligatoire à ce sujet. **Obligatoire** +Version 2.1 +----------- + +Cette version ajoute le paramètre "ready_to_publish" dans les ``Container`` qui vaut ``True`` par défaut. +Cet attribut permet d'implémenter la fonctionnalité de "publication partielle". + +Version 2.2 +----------- +Cette version ajoute le paramètre ``is_quizz`` dans les ``Extract`` afin de créer des extraits de type Quizz qui auront leur +correction automatiquement calculée par le client. Version 1.0 diff --git a/doc/source/back-end/quizz.rst b/doc/source/back-end/quizz.rst new file mode 100644 index 0000000000..849296a393 --- /dev/null +++ b/doc/source/back-end/quizz.rst @@ -0,0 +1,41 @@ +=============================== +Ajout de Quizz à Zest de savoir +=============================== + +Zeste de savoir est capablede présenter des quizz à l'utilisateur. La philosophie qui a été retenue est : + +- L'auteur écrit la **correction** du quizz dans un extrait qui est marqué comme étant un quizz. +- Lorsque le tutoriel est publié, côté client, un script parcourt cette correction et la transforme en formulaire. +- Une fois que le lecteur a rempli son quizz, la correction est automatiquement calculée et rendue au lecteur. +- Des statistiques sont alors envoyées à l'auteur pour qu'il voit où sont les erreurs de chacun. + +L'ajout de quizz n'est pas une mince affaire dans notre code d'autant qu'il est important de les rendre **faciles** à rédiger. +En effet, rédiger sur ZDS demande pas mal d'effort et l'ajout de quizz avec les méthodes habituelles à base +de formulaires spéciiques, qui permettent de "configurer le rendu" de la question est peu pertinent. + +La première version du module de quizz permet d'ajouter des QCM mais a été conçue pour permettre l'ajout d'autres types de réponse. + +Liens avec ZMarkdown +==================== + +Le module des quizz nécessite ZMarkdown 9 pour être fonctionnel sur la version web et ZMarkdown 10 pour être proprement exporté en PDF. +Les epub affichent uniquement la correction pour l'instant. + +Utiliser Zmarkdown > 10 +----------------------- + +Avec l'utilisation de zmarkdown > 10 vous pouvez utiliser le bloc ``[[quizz | Intitulé de la question]]``. +Ce bloc a le même comportement que le bloc neutre. Cependant il permet d'ajouter des informations de contexte nécessaire à l'export Latex. +De même on devrait pouvoir tirer partie du fait que ce bloc a une classe précise dans les implémentations des futurs types de questions. + +Ajouter des types de questions +------------------------------ + +Globalement, l'ajout de type de questions, disons "texte libre" pour l'exemple se décompose en trois parties : + +- Décider de quelle syntaxe Markdown on tirera partie pour déinir la correction. Imaginons ici qu'on utilise la syntaxe +des codes inlines (deux `) +- Implémenter dans zMarkdown un preprocessor de ``quizzCustomBlock`` qui permettra de remplacer les ``inlineCode`` par un texte +composé de ``______`` dans le quizz et les laissera intact dans la correction +- Adapter ``content_quizz.js`` et ``statistics.py`` pour que la correction se fasse et que les statistiques remontent + diff --git a/package.json b/package.json index 8f231386d3..a0c59f7743 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "gulp-concat": "2.6.1", "gulp-dart-sass": "1.0.2", "gulp-if": "3.0.0", - "gulp-imagemin": "^7.1.0", + "gulp-imagemin": "7.1.0", "gulp-options": "1.1.1", "gulp-postcss": "9.0.1", "gulp-terser-js": "5.2.2", diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index ff7e5d2315..a42cb0804c 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -1,19 +1,22 @@ {% load i18n %} -{% load dict_get %} -
    {% for url, questions in quizz.items %}
    -

    +

    {{ url }}

    {% for question, stats in questions.items %}

    {% trans graph_title %} {{ question }}

    -
    {% endif %} @@ -212,13 +203,6 @@

    {% url "content:create-extract" content.pk content.slug container.parent.slug container.slug %} {% endif %}"> {% trans "Ajouter une section" %} - {% trans 'ou' %} - - {% trans "ajouter un quizz" %}
    {% trans "Ajouter une section" %} -
  • - - {% trans "Ajouter un quizz" %} - -
  • {% endif %} {% endif %} @@ -235,9 +230,6 @@

    {% trans "Ajouter une section" %} - {% trans 'ou'%} - - {% trans "ajouter un quizz" %}
    @@ -291,9 +283,6 @@

    {% trans "Ajouter une section" %} - - {% trans "Ajouter un quizz" %} - {% endif %} diff --git a/yarn.lock b/yarn.lock index 92b766147a..8d2e433f9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,7 +5,6 @@ "@es-joy/jsdoccomment@~0.31.0": version "0.31.0" resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.31.0.tgz#dbc342cc38eb6878c12727985e693eaef34302bc" - integrity sha512-tc1/iuQcnaiSIUVad72PBierDFpsxdUHtEF/OrfqvM1CBAsIoMP51j52jTMb3dXriwhieTo289InzZj72jL3EQ== dependencies: comment-parser "1.3.1" esquery "^1.4.0" @@ -14,7 +13,6 @@ "@eslint/eslintrc@^1.3.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.1.tgz#de0807bfeffc37b964a7d0400e0c348ce5a2543d" - integrity sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -29,7 +27,6 @@ "@humanwhocodes/config-array@^0.10.4": version "0.10.4" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.4.tgz#01e7366e57d2ad104feea63e72248f22015c520c" - integrity sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" @@ -38,22 +35,18 @@ "@humanwhocodes/gitignore-to-minimatch@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d" - integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" @@ -61,12 +54,10 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.7" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz#94c23db18ee4653e129abd26fb06f870ac9e1ee2" - integrity sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA== dependencies: "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" @@ -74,29 +65,24 @@ "@sindresorhus/is@^0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" - integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" - integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== "@types/codemirror@^5.60.4": version "5.60.5" resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-5.60.5.tgz#5b989a3b4bbe657458cf372c92b6bfda6061a2b7" - integrity sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg== dependencies: "@types/tern" "*" "@types/estree@*": version "0.0.51" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" - integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== "@types/glob@^7.1.1": version "7.2.0" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" - integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== dependencies: "@types/minimatch" "*" "@types/node" "*" @@ -104,49 +90,40 @@ "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= "@types/marked@^4.0.1": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/marked/-/marked-4.0.2.tgz#cb2dbf10da2f41cf20bd91fb5f89b67540c282f7" - integrity sha512-auNrZ/c0w6wsM9DccwVxWHssrMDezHUAXNesdp2RQrCVCyrQbOiSq7yqdJKrUQQpw9VTm7CGYJH2A/YG7jjrjQ== "@types/minimatch@*": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" - integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/node@*": version "17.0.10" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.10.tgz#616f16e9d3a2a3d618136b1be244315d95bd7cab" - integrity sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog== "@types/q@^1.5.1": version "1.5.5" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" - integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== "@types/tern@*": version "0.23.4" resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.4.tgz#03926eb13dbeaf3ae0d390caf706b2643a0127fb" - integrity sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg== dependencies: "@types/estree" "*" acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn@^8.8.0: version "8.8.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" indent-string "^4.0.0" @@ -154,7 +131,6 @@ aggregate-error@^3.0.0: ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -164,60 +140,50 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: ansi-colors@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" - integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== dependencies: ansi-wrap "^0.1.0" ansi-gray@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= dependencies: ansi-wrap "0.1.0" ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-wrap@0.1.0, ansi-wrap@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== dependencies: micromatch "^3.1.4" normalize-path "^2.1.1" @@ -225,7 +191,6 @@ anymatch@^2.0.0: anymatch@~3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -233,82 +198,68 @@ anymatch@~3.1.1: append-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" - integrity sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE= dependencies: buffer-equal "^1.0.0" arch@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" - integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== archive-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/archive-type/-/archive-type-4.0.0.tgz#f92e72233056dfc6969472749c267bdb046b1d70" - integrity sha1-+S5yIzBW38aWlHJ0nCZ72wRrHXA= dependencies: file-type "^4.2.0" archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= arr-filter@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/arr-filter/-/arr-filter-1.1.2.tgz#43fdddd091e8ef11aa4c45d9cdc18e2dff1711ee" - integrity sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4= dependencies: make-iterator "^1.0.0" arr-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== arr-map@^2.0.0, arr-map@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/arr-map/-/arr-map-2.0.2.tgz#3a77345ffc1cf35e2a91825601f9e58f2e24cac4" - integrity sha1-Onc0X/wc814qkYJWAfnljy4kysQ= dependencies: make-iterator "^1.0.0" arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= array-each@^1.0.0, array-each@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" - integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= array-includes@^3.1.4: version "3.1.4" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" - integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" @@ -319,7 +270,6 @@ array-includes@^3.1.4: array-initial@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" - integrity sha1-L6dLJnOTccOUe9enrcc74zSz15U= dependencies: array-slice "^1.0.0" is-number "^4.0.0" @@ -327,19 +277,16 @@ array-initial@^1.0.0: array-last@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336" - integrity sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg== dependencies: is-number "^4.0.0" array-slice@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" - integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== array-sort@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" - integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== dependencies: default-compare "^1.0.0" get-value "^2.0.6" @@ -348,17 +295,14 @@ array-sort@^1.0.0: array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= array.prototype.flat@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" - integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" @@ -367,24 +311,20 @@ array.prototype.flat@^1.2.5: asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= async-done@^1.2.0, async-done@^1.2.2: version "1.3.2" resolved "https://registry.yarnpkg.com/async-done/-/async-done-1.3.2.tgz#5e15aa729962a4b07414f528a88cdf18e0b290a2" - integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== dependencies: end-of-stream "^1.1.0" once "^1.3.2" @@ -394,39 +334,32 @@ async-done@^1.2.0, async-done@^1.2.2: async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== async-settle@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b" - integrity sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs= dependencies: async-done "^1.2.2" async@~0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" - integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= async@~3.2.3: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== autoprefixer@10.4.8: version "10.4.8" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.8.tgz#92c7a0199e1cfb2ad5d9427bd585a3d75895b9e5" - integrity sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw== dependencies: browserslist "^4.21.3" caniuse-lite "^1.0.30001373" @@ -438,17 +371,14 @@ autoprefixer@10.4.8: aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: version "1.11.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== bach@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" - integrity sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA= dependencies: arr-filter "^1.1.1" arr-flatten "^1.0.1" @@ -463,17 +393,14 @@ bach@^1.0.0: balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: cache-base "^1.0.1" class-utils "^0.3.5" @@ -486,14 +413,12 @@ base@^0.11.1: bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" bin-build@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bin-build/-/bin-build-3.0.0.tgz#c5780a25a8a9f966d8244217e6c1f5082a143861" - integrity sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA== dependencies: decompress "^4.0.0" download "^6.2.2" @@ -504,7 +429,6 @@ bin-build@^3.0.0: bin-check@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bin-check/-/bin-check-4.1.0.tgz#fc495970bdc88bb1d5a35fc17e65c4a149fc4a49" - integrity sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA== dependencies: execa "^0.7.0" executable "^4.1.0" @@ -512,12 +436,10 @@ bin-check@^4.1.0: bin-pack@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/bin-pack/-/bin-pack-1.0.2.tgz#c2a014edbf0bed70a3292062ed46577b96120679" - integrity sha1-wqAU7b8L7XCjKSBi7UZXe5YSBnk= bin-version-check@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/bin-version-check/-/bin-version-check-4.0.0.tgz#7d819c62496991f80d893e6e02a3032361608f71" - integrity sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ== dependencies: bin-version "^3.0.0" semver "^5.6.0" @@ -526,7 +448,6 @@ bin-version-check@^4.0.0: bin-version@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bin-version/-/bin-version-3.1.0.tgz#5b09eb280752b1bd28f0c9db3f96f2f43b6c0839" - integrity sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ== dependencies: execa "^1.0.0" find-versions "^3.0.0" @@ -534,7 +455,6 @@ bin-version@^3.0.0: bin-wrapper@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bin-wrapper/-/bin-wrapper-4.1.0.tgz#99348f2cf85031e3ef7efce7e5300aeaae960605" - integrity sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q== dependencies: bin-check "^4.1.0" bin-version-check "^4.0.0" @@ -546,24 +466,20 @@ bin-wrapper@^4.0.0: binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: file-uri-to-path "1.0.0" bl@^1.0.0: version "1.2.3" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" - integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== dependencies: readable-stream "^2.3.5" safe-buffer "^5.1.1" @@ -571,12 +487,10 @@ bl@^1.0.0: boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" @@ -584,7 +498,6 @@ brace-expansion@^1.1.7: braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== dependencies: arr-flatten "^1.1.0" array-unique "^0.3.2" @@ -600,35 +513,12 @@ braces@^2.3.1, braces@^2.3.2: braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" -browserslist@^4.0.0, browserslist@^4.16.6: - version "4.16.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" - integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== - dependencies: - caniuse-lite "^1.0.30001219" - colorette "^1.2.2" - electron-to-chromium "^1.3.723" - escalade "^3.1.1" - node-releases "^1.1.71" - -browserslist@^4.20.3: - version "4.21.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.0.tgz#7ab19572361a140ecd1e023e2c1ed95edda0cefe" - integrity sha512-UQxE0DIhRB5z/zDz9iA03BOfxaN2+GQdBYH/2WrSIWEUrnpzTPJbhqt+umq6r3acaPRTW1FNTkrcp0PXgtFkvA== - dependencies: - caniuse-lite "^1.0.30001358" - electron-to-chromium "^1.4.164" - node-releases "^2.0.5" - update-browserslist-db "^1.0.0" - -browserslist@^4.21.3: +browserslist@^4.0.0, browserslist@^4.16.6, browserslist@^4.20.3, browserslist@^4.21.3: version "4.21.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" - integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== dependencies: caniuse-lite "^1.0.30001370" electron-to-chromium "^1.4.202" @@ -638,12 +528,10 @@ browserslist@^4.21.3: buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" - integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== buffer-alloc@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" - integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== dependencies: buffer-alloc-unsafe "^1.1.0" buffer-fill "^1.0.0" @@ -651,27 +539,22 @@ buffer-alloc@^1.2.0: buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= buffer-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" - integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= buffer-fill@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" - integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== buffer@^5.2.1: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" ieee754 "^1.1.13" @@ -679,14 +562,12 @@ buffer@^5.2.1: builtins@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" - integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== dependencies: semver "^7.0.0" cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== dependencies: collection-visit "^1.0.0" component-emitter "^1.2.1" @@ -701,7 +582,6 @@ cache-base@^1.0.1: cacheable-request@^2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" - integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0= dependencies: clone-response "1.0.2" get-stream "3.0.0" @@ -714,7 +594,6 @@ cacheable-request@^2.1.1: call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" get-intrinsic "^1.0.2" @@ -722,12 +601,10 @@ call-bind@^1.0.0, call-bind@^1.0.2: callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= dependencies: camelcase "^2.0.0" map-obj "^1.0.0" @@ -735,37 +612,31 @@ camelcase-keys@^2.0.0: camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== dependencies: browserslist "^4.0.0" caniuse-lite "^1.0.0" lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001358, caniuse-lite@^1.0.30001370, caniuse-lite@^1.0.30001373: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001370, caniuse-lite@^1.0.30001373: version "1.0.30001390" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001390.tgz#158a43011e7068ef7fc73590e9fd91a7cece5e7f" - integrity sha512-sS4CaUM+/+vqQUlCvCJ2WtDlV81aWtHhqeEVkLokVJJa3ViN4zDxAGfq9R8i1m90uGHxo99cy10Od+lvn3hf0g== caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= caw@^2.0.0, caw@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95" - integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA== dependencies: get-proxy "^2.0.0" isurl "^1.0.0-alpha5" @@ -775,7 +646,6 @@ caw@^2.0.0, caw@^2.0.1: chalk@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= dependencies: ansi-styles "^2.2.1" escape-string-regexp "^1.0.2" @@ -786,7 +656,6 @@ chalk@^1.0.0: chalk@^2.3.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" @@ -795,7 +664,6 @@ chalk@^2.3.0, chalk@^2.4.1: chalk@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -803,7 +671,6 @@ chalk@^3.0.0: chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -811,17 +678,14 @@ chalk@^4.0.0: chart.js@3.9.1: version "3.9.1" resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.9.1.tgz#3abf2c775169c4c71217a107163ac708515924b8" - integrity sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w== chartjs-adapter-moment@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.0.tgz#9174b1093c68bcfe285aff24f7388ad60d44e8f7" - integrity sha512-PqlerEvQcc5hZLQ/NQWgBxgVQ4TRdvkW3c/t+SUEQSj78ia3hgLkf2VZ2yGJtltNbEEFyYGm+cA6XXevodYvWA== "chokidar@>=3.0.0 <4.0.0": version "3.5.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== dependencies: anymatch "~3.1.1" braces "~3.0.2" @@ -836,7 +700,6 @@ chartjs-adapter-moment@1.0.0: chokidar@^2.0.0: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== dependencies: anymatch "^2.0.0" async-each "^1.0.1" @@ -855,7 +718,6 @@ chokidar@^2.0.0: class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== dependencies: arr-union "^3.1.0" define-property "^0.2.5" @@ -865,12 +727,10 @@ class-utils@^0.3.5: clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== cliui@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= dependencies: string-width "^1.0.1" strip-ansi "^3.0.1" @@ -879,39 +739,32 @@ cliui@^3.2.0: clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= clone-response@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= dependencies: mimic-response "^1.0.0" clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" - integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= clone-stats@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= clone@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= clone@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= cloneable-readable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" - integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== dependencies: inherits "^2.0.1" process-nextick-args "^2.0.0" @@ -920,7 +773,6 @@ cloneable-readable@^1.0.0: coa@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== dependencies: "@types/q" "^1.5.1" chalk "^2.4.1" @@ -929,24 +781,20 @@ coa@^2.0.2: code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= codemirror-spell-checker@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz#1c660f9089483ccb5113b9ba9ca19c3f4993371e" - integrity sha1-HGYPkIlIPMtRE7m6nKGcP0mTNx4= dependencies: typo-js "*" codemirror@^5.63.1: version "5.65.1" resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.1.tgz#5988a812c974c467f964bcc1a00c944e373de502" - integrity sha512-s6aac+DD+4O2u1aBmdxhB7yz2XU7tG3snOyQ05Kxifahz7hoxnfxIRHxiCSEv3TUC38dIVH8G+lZH9UWSfGQxA== collection-map@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" - integrity sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw= dependencies: arr-map "^2.0.2" for-own "^1.0.0" @@ -955,7 +803,6 @@ collection-map@^1.0.0: collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= dependencies: map-visit "^1.0.0" object-visit "^1.0.0" @@ -963,78 +810,60 @@ collection-visit@^1.0.0: color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== colord@^2.9.1: version "2.9.2" resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1" - integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ== - -colorette@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" - integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" commander@^2.20.0, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== comment-parser@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.3.1.tgz#3d7ea3adaf9345594aedee6563f422348f165c1b" - integrity sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA== component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= concat-stream@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: buffer-from "^1.0.0" inherits "^2.0.3" @@ -1044,7 +873,6 @@ concat-stream@^1.6.0: concat-stream@~1.5.1: version "1.5.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" - integrity sha1-cIl4Yk2FavQaWnQd790mHadSwmY= dependencies: inherits "~2.0.1" readable-stream "~2.0.0" @@ -1053,14 +881,12 @@ concat-stream@~1.5.1: concat-with-sourcemaps@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" - integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== dependencies: source-map "^0.6.1" config-chain@^1.1.11: version "1.1.13" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" - integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== dependencies: ini "^1.3.4" proto-list "~1.2.1" @@ -1068,38 +894,32 @@ config-chain@^1.1.11: console-stream@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/console-stream/-/console-stream-0.1.1.tgz#a095fe07b20465955f2fafd28b5d72bccd949d44" - integrity sha1-oJX+B7IEZZVfL6/Si11yvM2UnUQ= content-disposition@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== dependencies: safe-buffer "5.1.2" contentstream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/contentstream/-/contentstream-1.0.0.tgz#0bdcfa46da30464a86ce8fa7ece565410dc6f9a5" - integrity sha1-C9z6RtowRkqGzo+n7OVlQQ3G+aU= dependencies: readable-stream "~1.0.33-1" convert-source-map@^1.5.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== dependencies: safe-buffer "~5.1.1" copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= copy-props@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.5.tgz#03cf9ae328d4ebb36f8f1d804448a6af9ee3f2d2" - integrity sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw== dependencies: each-props "^1.3.2" is-plain-object "^5.0.0" @@ -1107,12 +927,10 @@ copy-props@^2.0.1: core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= dependencies: lru-cache "^4.0.1" shebang-command "^1.2.0" @@ -1121,7 +939,6 @@ cross-spawn@^5.0.1: cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" path-key "^2.0.1" @@ -1132,7 +949,6 @@ cross-spawn@^6.0.0: cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -1141,17 +957,14 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: css-declaration-sorter@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz#72ebd995c8f4532ff0036631f7365cce9759df14" - integrity sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og== css-select-base-adapter@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== css-select@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" - integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== dependencies: boolbase "^1.0.0" css-what "^3.2.1" @@ -1161,7 +974,6 @@ css-select@^2.0.0: css-select@^4.1.3: version "4.2.1" resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.2.1.tgz#9e665d6ae4c7f9d65dbe69d0316e3221fb274cdd" - integrity sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ== dependencies: boolbase "^1.0.0" css-what "^5.1.0" @@ -1172,7 +984,6 @@ css-select@^4.1.3: css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== dependencies: mdn-data "2.0.4" source-map "^0.6.1" @@ -1180,7 +991,6 @@ css-tree@1.0.0-alpha.37: css-tree@^1.1.2, css-tree@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== dependencies: mdn-data "2.0.14" source-map "^0.6.1" @@ -1188,22 +998,18 @@ css-tree@^1.1.2, css-tree@^1.1.3: css-what@^3.2.1: version "3.4.2" resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" - integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== css-what@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" - integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== cssnano-preset-default@^5.2.12: version "5.2.12" resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz#ebe6596ec7030e62c3eb2b3c09f533c0644a9a97" - integrity sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew== dependencies: css-declaration-sorter "^6.3.0" cssnano-utils "^3.1.0" @@ -1238,12 +1044,10 @@ cssnano-preset-default@^5.2.12: cssnano-utils@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" - integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== cssnano@5.1.13: version "5.1.13" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.13.tgz#83d0926e72955332dc4802a7070296e6258efc0a" - integrity sha512-S2SL2ekdEz6w6a2epXn4CmMKU4K3KpcyXLKfAYc9UQQqJRkD/2eLUG0vJ3Db/9OvO5GuAdgXw3pFbR6abqghDQ== dependencies: cssnano-preset-default "^5.2.12" lilconfig "^2.0.3" @@ -1252,28 +1056,24 @@ cssnano@5.1.13: csso@^4.0.2, csso@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== dependencies: css-tree "^1.1.2" currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= dependencies: array-find-index "^1.0.1" cwise-compiler@^1.0.0, cwise-compiler@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/cwise-compiler/-/cwise-compiler-1.1.3.tgz#f4d667410e850d3a313a7d2db7b1e505bb034cc5" - integrity sha1-9NZnQQ6FDToxOn0tt7HlBbsDTMU= dependencies: uniq "^1.0.0" d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== dependencies: es5-ext "^0.10.50" type "^1.0.1" @@ -1281,71 +1081,54 @@ d@1, d@^1.0.1: dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" data-uri-to-buffer@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz#18ae979a6a0ca994b0625853916d2662bbae0b1a" - integrity sha1-GK6XmmoMqZSwYlhTkW0mYruuCxo= debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" -debug@^4.1.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== +debug@^4.1.1, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" dependencies: ms "2.1.2" debug@^4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== - dependencies: - ms "2.1.2" - -debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= decompress-response@^3.2.0, decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= dependencies: mimic-response "^1.0.0" decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" - integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== dependencies: file-type "^5.2.0" is-stream "^1.1.0" @@ -1354,7 +1137,6 @@ decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: decompress-tarbz2@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" - integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== dependencies: decompress-tar "^4.1.0" file-type "^6.1.0" @@ -1365,7 +1147,6 @@ decompress-tarbz2@^4.0.0: decompress-targz@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" - integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== dependencies: decompress-tar "^4.1.1" file-type "^5.2.0" @@ -1374,7 +1155,6 @@ decompress-targz@^4.0.0: decompress-unzip@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" - integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k= dependencies: file-type "^3.8.0" get-stream "^2.2.0" @@ -1384,7 +1164,6 @@ decompress-unzip@^4.0.1: decompress@^4.0.0, decompress@^4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" - integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== dependencies: decompress-tar "^4.0.0" decompress-tarbz2 "^4.0.0" @@ -1398,45 +1177,38 @@ decompress@^4.0.0, decompress@^4.2.0: deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== default-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" - integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== dependencies: kind-of "^5.0.2" default-resolution@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" - integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= dependencies: is-descriptor "^1.0.0" define-property@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== dependencies: is-descriptor "^1.0.2" isobject "^3.0.1" @@ -1444,7 +1216,6 @@ define-property@^2.0.2: del@6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" - integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== dependencies: globby "^11.0.1" graceful-fs "^4.2.4" @@ -1458,38 +1229,32 @@ del@6.1.1: delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== dependencies: domelementtype "^2.0.1" entities "^2.0.0" @@ -1497,7 +1262,6 @@ dom-serializer@0: dom-serializer@^1.0.1: version "1.3.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" - integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== dependencies: domelementtype "^2.0.1" domhandler "^4.2.0" @@ -1506,31 +1270,20 @@ dom-serializer@^1.0.1: domelementtype@1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" - integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== -domhandler@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" - integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== - dependencies: - domelementtype "^2.2.0" - -domhandler@^4.3.0: +domhandler@^4.2.0, domhandler@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" - integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== dependencies: domelementtype "^2.2.0" domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== dependencies: dom-serializer "0" domelementtype "1" @@ -1538,7 +1291,6 @@ domutils@^1.7.0: domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== dependencies: dom-serializer "^1.0.1" domelementtype "^2.2.0" @@ -1547,7 +1299,6 @@ domutils@^2.8.0: download@^6.2.2: version "6.2.5" resolved "https://registry.yarnpkg.com/download/-/download-6.2.5.tgz#acd6a542e4cd0bb42ca70cfc98c9e43b07039714" - integrity sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA== dependencies: caw "^2.0.0" content-disposition "^0.5.2" @@ -1564,7 +1315,6 @@ download@^6.2.2: download@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/download/-/download-7.1.0.tgz#9059aa9d70b503ee76a132897be6dec8e5587233" - integrity sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ== dependencies: archive-type "^4.0.0" caw "^2.0.1" @@ -1582,12 +1332,10 @@ download@^7.1.0: duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== dependencies: end-of-stream "^1.0.0" inherits "^2.0.1" @@ -1597,7 +1345,6 @@ duplexify@^3.6.0: duplexify@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.1.tgz#7027dc374f157b122a8ae08c2d3ea4d2d953aa61" - integrity sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA== dependencies: end-of-stream "^1.4.1" inherits "^2.0.3" @@ -1607,7 +1354,6 @@ duplexify@^4.1.1: each-props@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" - integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA== dependencies: is-plain-object "^2.0.1" object.defaults "^1.1.0" @@ -1615,7 +1361,6 @@ each-props@^1.3.2: easymde@2.17.0: version "2.17.0" resolved "https://registry.yarnpkg.com/easymde/-/easymde-2.17.0.tgz#07954ec374b2e4f873c505c2b7d82b08e8d7053c" - integrity sha512-xerjhBh6G+FDfU2EBfKNEVqawYGqnK2zACKtyQlZKnxPoaesncRbHiSX5Yrf3Ur8KjEX1BvG7Ysccrd8hKTkig== dependencies: "@types/codemirror" "^5.60.4" "@types/marked" "^4.0.1" @@ -1626,49 +1371,33 @@ easymde@2.17.0: ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" safer-buffer "^2.1.0" -electron-to-chromium@^1.3.723: - version "1.3.752" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz#0728587f1b9b970ec9ffad932496429aef750d09" - integrity sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A== - -electron-to-chromium@^1.4.164: - version "1.4.170" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.170.tgz#0415fc489402e09bfbe1f0c99bbf4d73f31d48d4" - integrity sha512-rZ8PZLhK4ORPjFqLp9aqC4/S1j4qWFsPPz13xmWdrbBkU/LlxMcok+f+6f8YnQ57MiZwKtOaW15biZZsY5Igvw== - electron-to-chromium@^1.4.202: version "1.4.243" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.243.tgz#9d5f1b10eafd6e434b687a88f1e7b0441dee168a" - integrity sha512-BgLD2gBX43OSXwlT01oYRRD5NIB4n3okTRxkzEAC6G0SZG4TTlyrWMjbOo0fajCwqwpRtMHXQNMjtRN6qpNtfw== end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" entities@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== error-ex@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" - integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" @@ -1691,32 +1420,9 @@ es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" -es-abstract@^1.18.2: - version "1.18.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" - integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - get-intrinsic "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.2" - is-callable "^1.2.3" - is-negative-zero "^2.0.1" - is-regex "^1.1.3" - is-string "^1.0.6" - object-inspect "^1.10.3" - object-keys "^1.1.1" - object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.1" - es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" @@ -1725,7 +1431,6 @@ es-to-primitive@^1.2.1: es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50: version "0.10.53" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" - integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== dependencies: es6-iterator "~2.0.3" es6-symbol "~3.1.3" @@ -1734,7 +1439,6 @@ es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50: es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= dependencies: d "1" es5-ext "^0.10.35" @@ -1743,7 +1447,6 @@ es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3: es6-symbol@^3.1.1, es6-symbol@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== dependencies: d "^1.0.1" ext "^1.1.2" @@ -1751,7 +1454,6 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: es6-weak-map@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" - integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== dependencies: d "1" es5-ext "^0.10.46" @@ -1761,27 +1463,22 @@ es6-weak-map@^2.0.1: escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-standard@17.0.0: version "17.0.0" resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz#fd5b6cf1dcf6ba8d29f200c461de2e19069888cf" - integrity sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg== eslint-import-resolver-node@^0.3.6: version "0.3.6" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== dependencies: debug "^3.2.7" resolve "^1.20.0" @@ -1789,7 +1486,6 @@ eslint-import-resolver-node@^0.3.6: eslint-module-utils@^2.7.3: version "2.7.3" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" - integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== dependencies: debug "^3.2.7" find-up "^2.1.0" @@ -1797,7 +1493,6 @@ eslint-module-utils@^2.7.3: eslint-plugin-es@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz#f0822f0c18a535a97c3e714e89f88586a7641ec9" - integrity sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ== dependencies: eslint-utils "^2.0.0" regexpp "^3.0.0" @@ -1805,7 +1500,6 @@ eslint-plugin-es@^4.1.0: eslint-plugin-import@2.26.0: version "2.26.0" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== dependencies: array-includes "^3.1.4" array.prototype.flat "^1.2.5" @@ -1824,7 +1518,6 @@ eslint-plugin-import@2.26.0: eslint-plugin-jsdoc@39.3.6: version "39.3.6" resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.3.6.tgz#6ba29f32368d72a51335a3dc9ccd22ad0437665d" - integrity sha512-R6dZ4t83qPdMhIOGr7g2QII2pwCjYyKP+z0tPOfO1bbAbQyKC20Y2Rd6z1te86Lq3T7uM8bNo+VD9YFpE8HU/g== dependencies: "@es-joy/jsdoccomment" "~0.31.0" comment-parser "1.3.1" @@ -1837,7 +1530,6 @@ eslint-plugin-jsdoc@39.3.6: eslint-plugin-n@15.2.5: version "15.2.5" resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.2.5.tgz#aa7ff8d45bb8bf2df8ea3b7d3774ae570cb794b8" - integrity sha512-8+BYsqiyZfpu6NXmdLOXVUfk8IocpCjpd8nMRRH0A9ulrcemhb2VI9RSJMEy5udx++A/YcVPD11zT8hpFq368g== dependencies: builtins "^5.0.1" eslint-plugin-es "^4.1.0" @@ -1851,12 +1543,10 @@ eslint-plugin-n@15.2.5: eslint-plugin-promise@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.0.1.tgz#a8cddf96a67c4059bdabf4d724a29572188ae423" - integrity sha512-uM4Tgo5u3UWQiroOyDEsYcVMOo7re3zmno0IZmB5auxoaQNIceAbXEkSt8RNrKtaYehARHG06pYK6K1JhtP0Zw== eslint-scope@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -1864,36 +1554,30 @@ eslint-scope@^7.1.1: eslint-utils@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-utils@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== dependencies: eslint-visitor-keys "^2.0.0" eslint-visitor-keys@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint-visitor-keys@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@8.23.0: version "8.23.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.23.0.tgz#a184918d288820179c6041bb3ddcc99ce6eea040" - integrity sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA== dependencies: "@eslint/eslintrc" "^1.3.1" "@humanwhocodes/config-array" "^0.10.4" @@ -1938,7 +1622,6 @@ eslint@8.23.0: espree@^9.4.0: version "9.4.0" resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" - integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" @@ -1947,36 +1630,30 @@ espree@^9.4.0: esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^5.1.0, estraverse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== exec-buffer@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/exec-buffer/-/exec-buffer-3.2.0.tgz#b1686dbd904c7cf982e652c1f5a79b1e5573082b" - integrity sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA== dependencies: execa "^0.7.0" p-finally "^1.0.0" @@ -1987,7 +1664,6 @@ exec-buffer@^3.0.0: execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= dependencies: cross-spawn "^5.0.1" get-stream "^3.0.0" @@ -2000,7 +1676,6 @@ execa@^0.7.0: execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== dependencies: cross-spawn "^6.0.0" get-stream "^4.0.0" @@ -2013,7 +1688,6 @@ execa@^1.0.0: execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" get-stream "^6.0.0" @@ -2028,14 +1702,12 @@ execa@^5.0.0: executable@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== dependencies: pify "^2.2.0" expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: debug "^2.3.3" define-property "^0.2.5" @@ -2048,21 +1720,18 @@ expand-brackets@^2.1.4: expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= dependencies: homedir-polyfill "^1.0.1" ext-list@^2.0.0: version "2.2.2" resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" - integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== dependencies: mime-db "^1.28.0" ext-name@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" - integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== dependencies: ext-list "^2.0.0" sort-keys-length "^1.0.0" @@ -2070,21 +1739,18 @@ ext-name@^5.0.0: ext@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" - integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== dependencies: type "^2.0.0" extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= dependencies: assign-symbols "^1.0.0" is-extendable "^1.0.1" @@ -2092,12 +1758,10 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: extend@^3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== dependencies: array-unique "^0.3.2" define-property "^1.0.0" @@ -2108,20 +1772,13 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extsprintf@1.3.0: +extsprintf@1.3.0, extsprintf@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= fancy-log@^1.3.2, fancy-log@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" - integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== dependencies: ansi-gray "^0.1.1" color-support "^1.1.3" @@ -2131,12 +1788,10 @@ fancy-log@^1.3.2, fancy-log@^1.3.3: fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.0.3, fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -2147,7 +1802,6 @@ fast-glob@^3.0.3, fast-glob@^3.2.9: fast-glob@^3.1.1: version "3.2.5" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" - integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -2159,41 +1813,34 @@ fast-glob@^3.1.1: fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9" - integrity sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk= fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fast-xml-parser@^3.19.0: version "3.19.0" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" - integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg== fastq@^1.6.0: version "1.11.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" - integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== dependencies: reusify "^1.0.4" fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= dependencies: pend "~1.2.0" figures@^1.3.5: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= dependencies: escape-string-regexp "^1.0.5" object-assign "^4.1.0" @@ -2201,59 +1848,48 @@ figures@^1.3.5: file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" file-type@5.2.0, file-type@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" - integrity sha1-LdvqfHP/42No365J3DOMBYwritY= file-type@^10.4.0: version "10.11.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-10.11.0.tgz#2961d09e4675b9fb9a3ee6b69e9cd23f43fd1890" - integrity sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw== file-type@^12.0.0: version "12.4.2" resolved "https://registry.yarnpkg.com/file-type/-/file-type-12.4.2.tgz#a344ea5664a1d01447ee7fb1b635f72feb6169d9" - integrity sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg== file-type@^3.8.0: version "3.9.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" - integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= file-type@^4.2.0: version "4.4.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-4.4.0.tgz#1b600e5fca1fbdc6e80c0a70c71c8dba5f7906c5" - integrity sha1-G2AOX8ofvcboDApwxxyNul95BsU= file-type@^6.1.0: version "6.2.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" - integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== file-type@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-8.1.0.tgz#244f3b7ef641bbe0cca196c7276e4b332399f68c" - integrity sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ== file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== filename-reserved-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" - integrity sha1-q/c9+rc10EVECr/qLZHzieu/oik= filenamify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9" - integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== dependencies: filename-reserved-regex "^2.0.0" strip-outer "^1.0.0" @@ -2262,7 +1898,6 @@ filenamify@^2.0.0: fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= dependencies: extend-shallow "^2.0.1" is-number "^3.0.0" @@ -2272,14 +1907,12 @@ fill-range@^4.0.0: fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= dependencies: path-exists "^2.0.0" pinkie-promise "^2.0.0" @@ -2287,14 +1920,12 @@ find-up@^1.0.0: find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= dependencies: locate-path "^2.0.0" find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" path-exists "^4.0.0" @@ -2302,14 +1933,12 @@ find-up@^5.0.0: find-versions@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" - integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww== dependencies: semver-regex "^2.0.0" findup-sync@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" - integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= dependencies: detect-file "^1.0.0" is-glob "^3.1.0" @@ -2319,7 +1948,6 @@ findup-sync@^2.0.0: findup-sync@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== dependencies: detect-file "^1.0.0" is-glob "^4.0.0" @@ -2329,7 +1957,6 @@ findup-sync@^3.0.0: fined@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" - integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== dependencies: expand-tilde "^2.0.2" is-plain-object "^2.0.3" @@ -2340,17 +1967,14 @@ fined@^1.0.1: first-chunk-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" - integrity sha1-Wb+1DNkF9g18OUzT2ayqtOatk04= flagged-respawn@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" - integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" rimraf "^3.0.2" @@ -2358,12 +1982,10 @@ flat-cache@^3.0.4: flatted@^3.1.0: version "3.2.5" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" - integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== flush-write-stream@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== dependencies: inherits "^2.0.3" readable-stream "^2.3.6" @@ -2371,29 +1993,24 @@ flush-write-stream@^1.0.2: for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= for-own@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" - integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= dependencies: for-in "^1.0.1" forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= fork-stream@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/fork-stream/-/fork-stream-0.0.4.tgz#db849fce77f6708a5f8f386ae533a0907b54ae70" - integrity sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA= form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" combined-stream "^1.0.6" @@ -2402,19 +2019,16 @@ form-data@~2.3.2: fraction.js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" - integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= dependencies: map-cache "^0.2.2" from2@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= dependencies: inherits "^2.0.1" readable-stream "^2.0.0" @@ -2422,12 +2036,10 @@ from2@^2.1.1: fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== fs-mkdirp-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" - integrity sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes= dependencies: graceful-fs "^4.1.11" through2 "^2.0.3" @@ -2435,12 +2047,10 @@ fs-mkdirp-stream@^1.0.0: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^1.2.7: version "1.2.13" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== dependencies: bindings "^1.5.0" nan "^2.12.1" @@ -2448,27 +2058,22 @@ fsevents@^1.2.7: fsevents@~2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" has "^1.0.3" @@ -2477,7 +2082,6 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: get-pixels@~3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/get-pixels/-/get-pixels-3.3.2.tgz#3f62fb8811932c69f262bba07cba72b692b4ff03" - integrity sha512-6ar+8yPxRd1pskEcl2GSEu1La0+xYRjjnkby6AYiRDDwZ0tJbPQmHnSeH9fGLskT8kvR0OukVgtZLcsENF9YKQ== dependencies: data-uri-to-buffer "0.0.3" jpeg-js "^0.3.2" @@ -2494,24 +2098,20 @@ get-pixels@~3.3.0: get-proxy@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/get-proxy/-/get-proxy-2.1.0.tgz#349f2b4d91d44c4d4d4e9cba2ad90143fac5ef93" - integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw== dependencies: npm-conf "^1.1.0" get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= get-stream@3.0.0, get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= get-stream@^2.2.0: version "2.3.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" - integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4= dependencies: object-assign "^4.0.1" pinkie-promise "^2.0.0" @@ -2519,19 +2119,16 @@ get-stream@^2.2.0: get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: call-bind "^1.0.2" get-intrinsic "^1.1.1" @@ -2539,26 +2136,22 @@ get-symbol-description@^1.0.0: get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" gif-encoder@~0.4.1: version "0.4.3" resolved "https://registry.yarnpkg.com/gif-encoder/-/gif-encoder-0.4.3.tgz#8a2b4fe8ca895a48e3a0b6cbb340a0a6a3571899" - integrity sha1-iitP6MqJWkjjoLbLs0CgpqNXGJk= dependencies: readable-stream "~1.1.9" -gifsicle@^5.0.0, gifsicle@^5.1.0: +gifsicle@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/gifsicle/-/gifsicle-5.2.0.tgz#b06b25ed7530f033f6ed2c545d6f9b546cc182fb" - integrity sha512-vOIS3j0XoTCxq9pkGj43gEix82RkI5FveNgaFZutjbaui/HH+4fR8Y56dwXDuxYo8hR4xOo6/j2h1WHoQW6XLw== dependencies: bin-build "^3.0.0" bin-wrapper "^4.0.0" @@ -2568,7 +2161,6 @@ gifsicle@^5.0.0, gifsicle@^5.1.0: glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= dependencies: is-glob "^3.1.0" path-dirname "^1.0.0" @@ -2576,21 +2168,18 @@ glob-parent@^3.1.0: glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-parent@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" glob-stream@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" - integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= dependencies: extend "^3.0.0" glob "^7.1.1" @@ -2606,7 +2195,6 @@ glob-stream@^6.1.0: glob-watcher@^5.0.3: version "5.0.5" resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.5.tgz#aa6bce648332924d9a8489be41e3e5c52d4186dc" - integrity sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw== dependencies: anymatch "^2.0.0" async-done "^1.2.0" @@ -2619,7 +2207,6 @@ glob-watcher@^5.0.3: glob@^7.1.1, glob@^7.1.3: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -2631,7 +2218,6 @@ glob@^7.1.1, glob@^7.1.3: global-modules@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== dependencies: global-prefix "^1.0.1" is-windows "^1.0.1" @@ -2640,7 +2226,6 @@ global-modules@^1.0.0: global-prefix@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= dependencies: expand-tilde "^2.0.2" homedir-polyfill "^1.0.1" @@ -2651,14 +2236,12 @@ global-prefix@^1.0.1: globals@^13.15.0: version "13.15.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" - integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== dependencies: type-fest "^0.20.2" globby@^10.0.0: version "10.0.2" resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" - integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== dependencies: "@types/glob" "^7.1.1" array-union "^2.1.0" @@ -2672,7 +2255,6 @@ globby@^10.0.0: globby@^11.0.1: version "11.0.3" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" - integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" @@ -2684,7 +2266,6 @@ globby@^11.0.1: globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" @@ -2696,14 +2277,12 @@ globby@^11.1.0: glogg@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.2.tgz#2d7dd702beda22eb3bffadf880696da6d846313f" - integrity sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA== dependencies: sparkles "^1.0.0" got@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" - integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== dependencies: decompress-response "^3.2.0" duplexer3 "^0.1.4" @@ -2723,7 +2302,6 @@ got@^7.0.0: got@^8.3.1: version "8.3.2" resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" - integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== dependencies: "@sindresorhus/is" "^0.7.0" cacheable-request "^2.1.1" @@ -2743,25 +2321,17 @@ got@^8.3.1: url-parse-lax "^3.0.0" url-to-options "^1.0.1" -graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4: +graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.2, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -graceful-fs@^4.2.2: - version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== gulp-cli@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.3.0.tgz#ec0d380e29e52aa45e47977f0d32e18fd161122f" - integrity sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A== dependencies: ansi-colors "^1.0.1" archy "^1.0.0" @@ -2785,7 +2355,6 @@ gulp-cli@^2.2.0: gulp-concat@2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/gulp-concat/-/gulp-concat-2.6.1.tgz#633d16c95d88504628ad02665663cee5a4793353" - integrity sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M= dependencies: concat-with-sourcemaps "^1.0.0" through2 "^2.0.0" @@ -2794,7 +2363,6 @@ gulp-concat@2.6.1: gulp-dart-sass@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/gulp-dart-sass/-/gulp-dart-sass-1.0.2.tgz#20e33c236b48d557c91e8dfe67a2aef2b8a2e328" - integrity sha512-8fLttA824mbuc0jRVlGs00zWYZXBckat6INawx5kp66Eqsz5srNWTA51t0mbfB4C8a/a/GZ9muYLwXGklgAHlw== dependencies: chalk "^2.3.0" lodash.clonedeep "^4.3.2" @@ -2808,16 +2376,14 @@ gulp-dart-sass@1.0.2: gulp-if@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/gulp-if/-/gulp-if-3.0.0.tgz#6c3e7edc8bafadc34f2ebecb314bf43324ba1e40" - integrity sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw== dependencies: gulp-match "^1.1.0" ternary-stream "^3.0.0" through2 "^3.0.1" -gulp-imagemin@^7.1.0: +gulp-imagemin@7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/gulp-imagemin/-/gulp-imagemin-7.1.0.tgz#d1810a908fb64b4fbf15a750d303d988443e68cf" - integrity sha512-6xBTNybmPY2YrvrhhlS8Mxi0zn0ypusLon63p9XXxDtIf7U7c6KcViz94K7Skosucr3378A6IY2kJSjJyuwylQ== dependencies: chalk "^3.0.0" fancy-log "^1.3.2" @@ -2835,19 +2401,16 @@ gulp-imagemin@^7.1.0: gulp-match@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/gulp-match/-/gulp-match-1.1.0.tgz#552b7080fc006ee752c90563f9fec9d61aafdf4f" - integrity sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ== dependencies: minimatch "^3.0.3" gulp-options@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/gulp-options/-/gulp-options-1.1.1.tgz#589676e1adfaa48deef633b79b86c169f2dbbb99" - integrity sha1-WJZ24a36pI3u9jO3m4bBafLbu5k= gulp-postcss@9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/gulp-postcss/-/gulp-postcss-9.0.1.tgz#d43caa2f2ce1018f889f7c1296faf82e9928b66f" - integrity sha512-9QUHam5JyXwGUxaaMvoFQVT44tohpEFpM8xBdPfdwTYGM0AItS1iTQz0MpsF8Jroh7GF5Jt2GVPaYgvy8qD2Fw== dependencies: fancy-log "^1.3.3" plugin-error "^1.0.1" @@ -2857,7 +2420,6 @@ gulp-postcss@9.0.1: gulp-terser-js@5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/gulp-terser-js/-/gulp-terser-js-5.2.2.tgz#3d93db9b2b83f35dfcc5b209af6b1762756eb6a3" - integrity sha512-4ull0HzTWeWjRPiGmAFmdhRcEDOG+r7aXivNHOBQzElLzMaeVKQwmCPDi2juBzUUkjAkPkKb1jHVoJN/PKTvcA== dependencies: plugin-error "^1.0.1" source-map "^0.7.3" @@ -2868,7 +2430,6 @@ gulp-terser-js@5.2.2: gulp.spritesmith@6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/gulp.spritesmith/-/gulp.spritesmith-6.13.0.tgz#9cfcff00288657050feb57639fb9356c55b37cea" - integrity sha512-maHC3FgCiOV+9wuLAgcqzq+oZjI8b57CgFVi0/r8Ax1AM2i51nEoKXtXM8RUh9ybTjT1mcGWNYzACDOK2hC8RQ== dependencies: async "~3.2.3" minimatch "~3.0.3" @@ -2882,7 +2443,6 @@ gulp.spritesmith@6.13.0: gulp@4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa" - integrity sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA== dependencies: glob-watcher "^5.0.3" gulp-cli "^2.2.0" @@ -2892,19 +2452,16 @@ gulp@4.0.2: gulplog@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= dependencies: glogg "^1.0.0" handlebars-layouts@^3.1.4: version "3.1.4" resolved "https://registry.yarnpkg.com/handlebars-layouts/-/handlebars-layouts-3.1.4.tgz#26b3beb931b4b877dfbf7e6feaf4058ee6228b02" - integrity sha1-JrO+uTG0uHffv35v6vQFjuYiiwI= handlebars@^4.6.0: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== dependencies: minimist "^1.2.5" neo-async "^2.6.0" @@ -2916,12 +2473,10 @@ handlebars@^4.6.0: har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= har-validator@~5.1.3: version "5.1.5" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== dependencies: ajv "^6.12.3" har-schema "^2.0.0" @@ -2929,53 +2484,44 @@ har-validator@~5.1.3: has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= dependencies: ansi-regex "^2.0.0" has-bigints@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbol-support-x@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== has-to-string-tag-x@^1.2.0: version "1.4.1" resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== dependencies: has-symbol-support-x "^1.4.1" has-tostringtag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== dependencies: has-symbols "^1.0.2" has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: get-value "^2.0.3" has-values "^0.1.4" @@ -2984,7 +2530,6 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= dependencies: get-value "^2.0.6" has-values "^1.0.0" @@ -2993,12 +2538,10 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= has-values@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= dependencies: is-number "^3.0.0" kind-of "^4.0.0" @@ -3006,31 +2549,26 @@ has-values@^1.0.0: has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== dependencies: parse-passwd "^1.0.0" hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== http-cache-semantics@3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: assert-plus "^1.0.0" jsprim "^1.2.2" @@ -3039,27 +2577,18 @@ http-signature@~1.2.0: human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -ignore@^5.1.1, ignore@^5.1.4: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== -ignore@^5.2.0: +ignore@^5.1.1, ignore@^5.1.4, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== imagemin-gifsicle@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/imagemin-gifsicle/-/imagemin-gifsicle-7.0.0.tgz#1a7ab136a144c4678657ba3b6c412f80805d26b0" - integrity sha512-LaP38xhxAwS3W8PFh4y5iQ6feoTSF+dTAXFRUEYQWYst6Xd+9L/iPk34QGgK/VO/objmIlmq9TStGfVY2IcHIA== dependencies: execa "^1.0.0" gifsicle "^5.0.0" @@ -3068,7 +2597,6 @@ imagemin-gifsicle@^7.0.0: imagemin-mozjpeg@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.0.tgz#d2ca4e8c982c7c6eda55069af89dee4c1cebcdfd" - integrity sha512-+EciPiIjCb8JWjQNr1q8sYWYf7GDCNDxPYnkD11TNIjjWNzaV+oTg4DpOPQjl5ZX/KRCPMEgS79zLYAQzLitIA== dependencies: execa "^1.0.0" is-jpg "^2.0.0" @@ -3077,7 +2605,6 @@ imagemin-mozjpeg@^8.0.0: imagemin-optipng@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/imagemin-optipng/-/imagemin-optipng-7.1.0.tgz#2225c82c35e5c29b7fa98d4f9ecee1161a68e888" - integrity sha512-JNORTZ6j6untH7e5gF4aWdhDCxe3ODsSLKs/f7Grewy3ebZpl1ZsU+VUTPY4rzeHgaFA8GSWOoA8V2M3OixWZQ== dependencies: exec-buffer "^3.0.0" is-png "^2.0.0" @@ -3086,7 +2613,6 @@ imagemin-optipng@^7.0.0: imagemin-svgo@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/imagemin-svgo/-/imagemin-svgo-7.1.0.tgz#528a42fd3d55eff5d4af8fd1113f25fb61ad6d9a" - integrity sha512-0JlIZNWP0Luasn1HT82uB9nU9aa+vUj6kpT+MjPW11LbprXC+iC4HDwn1r4Q2/91qj4iy9tRZNsFySMlEpLdpg== dependencies: is-svg "^4.2.1" svgo "^1.3.2" @@ -3094,7 +2620,6 @@ imagemin-svgo@^7.0.0: imagemin@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/imagemin/-/imagemin-7.0.1.tgz#f6441ca647197632e23db7d971fffbd530c87dbf" - integrity sha512-33AmZ+xjZhg2JMCe+vDf6a9mzWukE7l+wAtesjE7KyteqqKjzxv7aVQeWnul1Ve26mWvEQqyPwl0OctNBfSR9w== dependencies: file-type "^12.0.0" globby "^10.0.0" @@ -3107,7 +2632,6 @@ imagemin@^7.0.0: import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" @@ -3115,29 +2639,24 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: import-lazy@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-3.1.0.tgz#891279202c8a2280fdbd6674dbd8da1a1dfc67cc" - integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ== imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= dependencies: repeating "^2.0.0" indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" @@ -3145,17 +2664,14 @@ inflight@^1.0.4: inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== dependencies: get-intrinsic "^1.1.0" has "^1.0.3" @@ -3164,12 +2680,10 @@ internal-slot@^1.0.3: interpret@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== into-stream@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" - integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY= dependencies: from2 "^2.1.1" p-is-promise "^1.1.0" @@ -3177,22 +2691,18 @@ into-stream@^3.1.0: invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= iota-array@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/iota-array/-/iota-array-1.0.0.tgz#81ef57fe5d05814cd58c2483632a99c30a0e8087" - integrity sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc= irregular-plurals@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-2.0.0.tgz#39d40f05b00f656d0b7fa471230dd3b714af2872" - integrity sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw== is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" - integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== dependencies: is-relative "^1.0.0" is-windows "^1.0.1" @@ -3200,114 +2710,80 @@ is-absolute@^1.0.0: is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= dependencies: kind-of "^3.0.2" is-accessor-descriptor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== dependencies: kind-of "^6.0.0" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-bigint@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" - integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= dependencies: binary-extensions "^1.0.0" is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" - integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== dependencies: call-bind "^1.0.2" is-buffer@^1.0.2, is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" - integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== - -is-callable@^1.2.4: +is-callable@^1.1.4, is-callable@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== -is-core-module@^2.10.0: +is-core-module@^2.10.0, is-core-module@^2.9.0: version "2.10.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" - integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== - dependencies: - has "^1.0.3" - -is-core-module@^2.2.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" - integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== dependencies: has "^1.0.3" is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== - dependencies: - has "^1.0.3" - -is-core-module@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== dependencies: has "^1.0.3" is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= dependencies: kind-of "^3.0.2" is-data-descriptor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== dependencies: kind-of "^6.0.0" is-date-object@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" - integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== dependencies: is-accessor-descriptor "^0.1.6" is-data-descriptor "^0.1.4" @@ -3316,7 +2792,6 @@ is-descriptor@^0.1.0: is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== dependencies: is-accessor-descriptor "^1.0.0" is-data-descriptor "^1.0.0" @@ -3325,151 +2800,118 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-finite@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" - integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-gif@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-gif/-/is-gif-3.0.0.tgz#c4be60b26a301d695bb833b20d9b5d66c6cf83b1" - integrity sha512-IqJ/jlbw5WJSNfwQ/lHEDXF8rxhRgF6ythk2oiEvhpG29F704eX9NO6TvPfMiq9DrbwgcEDnETYNcZDPewQoVw== dependencies: file-type "^10.4.0" is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" -is-glob@^4.0.3: +is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-jpg@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-jpg/-/is-jpg-2.0.0.tgz#2e1997fa6e9166eaac0242daae443403e4ef1d97" - integrity sha1-LhmX+m6RZuqsAkLarkQ0A+TvHZc= is-natural-number@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" - integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= is-negated-glob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" - integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" - integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== is-number-object@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" - integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= dependencies: kind-of "^3.0.2" is-number@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-object@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" - integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== is-path-cwd@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== is-path-inside@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== is-png@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-png/-/is-png-2.0.0.tgz#ee8cbc9e9b050425cedeeb4a6fb74a649b0a4a8d" - integrity sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g== - -is-regex@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" - integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== - dependencies: - call-bind "^1.0.2" - has-symbols "^1.0.2" is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" @@ -3477,126 +2919,100 @@ is-regex@^1.1.4: is-relative@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" - integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== dependencies: is-unc-path "^1.0.0" is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== is-shared-array-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" - integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== is-stream@^1.0.0, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== - -is-string@^1.0.5, is-string@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" - integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== -is-string@^1.0.7: +is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" is-svg@^4.2.1: version "4.3.1" resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.3.1.tgz#8c63ec8c67c8c7f0a8de0a71c8c7d58eccf4406b" - integrity sha512-h2CGs+yPUyvkgTJQS9cJzo9lYK06WgRiXUqBBHtglSzVKAuH4/oWsqk7LGfbSa1hGk9QcZ0SyQtVggvBA8LZXA== dependencies: fast-xml-parser "^3.19.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-unc-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" - integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== dependencies: unc-path-regex "^0.1.2" is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= is-valid-glob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" - integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= is-weakref@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: call-bind "^1.0.2" is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= isurl@^1.0.0-alpha5: version "1.0.0" resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== dependencies: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" @@ -3604,22 +3020,18 @@ isurl@^1.0.0-alpha5: jpeg-js@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.0.4.tgz#06aaf47efec7af0b1924a59cd695a6d2b5ed870e" - integrity sha1-Bqr0fv7HrwsZJKWc1pWm0rXthw4= jpeg-js@^0.3.2: version "0.3.7" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.7.tgz#471a89d06011640592d314158608690172b1028d" - integrity sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ== jquery@3.6.1: version "3.6.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.1.tgz#fab0408f8b45fc19f956205773b62b292c147a16" - integrity sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw== js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -3627,61 +3039,50 @@ js-yaml@^3.13.1: js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsdoc-type-pratt-parser@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz#a4a56bdc6e82e5865ffd9febc5b1a227ff28e67e" - integrity sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw== json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= json-content-demux@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/json-content-demux/-/json-content-demux-0.1.4.tgz#6d573fbb5a2b224ae225736e287f32ea91a4273d" - integrity sha512-3GqPH2O0+8qBMTa1YTuL+7L24YJYNDjdXfa798y9S6GetScZAY2iAOGCdFkEPZJZdafPKv8ZUnp18VCCPTs0Nw== json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== dependencies: minimist "^1.2.0" jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: assert-plus "1.0.0" extsprintf "1.3.0" @@ -3691,48 +3092,40 @@ jsprim@^1.2.2: junk@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" - integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== just-debounce@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.1.0.tgz#2f81a3ad4121a76bc7cb45dbf704c0d76a8e5ddf" - integrity sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ== keyv@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" - integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== dependencies: json-buffer "3.0.0" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= dependencies: is-buffer "^1.1.5" kind-of@^5.0.0, kind-of@^5.0.2: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== last-run@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" - integrity sha1-RblpQsF7HHnHchmCWbqUO+v4yls= dependencies: default-resolution "^2.0.0" es6-weak-map "^2.0.1" @@ -3740,35 +3133,30 @@ last-run@^1.1.0: layout@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/layout/-/layout-2.2.0.tgz#31e44bfc18dd1019b3ffb208e402a4bbfe2e6af4" - integrity sha1-MeRL/BjdEBmz/7II5AKku/4uavQ= dependencies: bin-pack "~1.0.1" lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" - integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= dependencies: readable-stream "^2.0.5" lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= dependencies: invert-kv "^1.0.0" lead@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" - integrity sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI= dependencies: flush-write-stream "^1.0.2" levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" type-check "~0.4.0" @@ -3776,7 +3164,6 @@ levn@^0.4.1: liftoff@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" - integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== dependencies: extend "^3.0.0" findup-sync "^3.0.0" @@ -3790,12 +3177,10 @@ liftoff@^3.1.0: lilconfig@^2.0.3, lilconfig@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" - integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" @@ -3806,7 +3191,6 @@ load-json-file@^1.0.0: locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= dependencies: p-locate "^2.0.0" path-exists "^3.0.0" @@ -3814,34 +3198,28 @@ locate-path@^2.0.0: locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash.clonedeep@^4.3.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= logalot@^2.0.0, logalot@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/logalot/-/logalot-2.1.0.tgz#5f8e8c90d304edf12530951a5554abb8c5e3f552" - integrity sha1-X46MkNME7fElMJUaVVSruMXj9VI= dependencies: figures "^1.3.5" squeak "^1.0.0" @@ -3849,12 +3227,10 @@ logalot@^2.0.0, logalot@^2.1.0: longest@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= dependencies: currently-unhandled "^0.4.1" signal-exit "^3.0.0" @@ -3862,17 +3238,14 @@ loud-rejection@^1.0.0: lowercase-keys@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= lowercase-keys@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lpad-align@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/lpad-align/-/lpad-align-1.1.2.tgz#21f600ac1c3095c3c6e497ee67271ee08481fe9e" - integrity sha1-IfYArBwwlcPG5JfuZyce4ISB/p4= dependencies: get-stdin "^4.0.1" indent-string "^2.1.0" @@ -3882,7 +3255,6 @@ lpad-align@^1.0.1: lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== dependencies: pseudomap "^1.0.2" yallist "^2.1.2" @@ -3890,57 +3262,48 @@ lru-cache@^4.0.1: lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" make-dir@^1.0.0, make-dir@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== dependencies: pify "^3.0.0" make-dir@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" make-iterator@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" - integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== dependencies: kind-of "^6.0.2" map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: object-visit "^1.0.0" marked@^4.0.18: version "4.1.0" resolved "https://registry.yarnpkg.com/marked/-/marked-4.1.0.tgz#3fc6e7485f21c1ca5d6ec4a39de820e146954796" - integrity sha512-+Z6KDjSPa6/723PQYyc1axYZpYYpDnECDaU6hkaf5gqBieBkMKYReL5hteF2QizhlMbgbo8umXl/clZ67+GlsA== matchdep@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" - integrity sha1-xvNINKDY28OzfCfui7yyfHd1WC4= dependencies: findup-sync "^2.0.0" micromatch "^3.0.4" @@ -3950,17 +3313,14 @@ matchdep@^2.0.0: mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== mdn-data@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= dependencies: camelcase-keys "^2.0.0" decamelize "^1.1.2" @@ -3976,17 +3336,14 @@ meow@^3.3.0: merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -4005,7 +3362,6 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: braces "^3.0.1" picomatch "^2.2.3" @@ -4013,53 +3369,40 @@ micromatch@^4.0.2, micromatch@^4.0.4: mime-db@1.48.0, mime-db@^1.28.0: version "1.48.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" - integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== mime-types@^2.0.1, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.7: version "2.1.31" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" - integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== dependencies: mime-db "1.48.0" mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-response@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^3.1.2: +minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimatch@~3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" -minimist@^1.2.6: +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" @@ -4067,19 +3410,16 @@ mixin-deep@^1.2.0: mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" moment@2.29.4: version "2.29.4" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" - integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== mozjpeg@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/mozjpeg/-/mozjpeg-6.0.1.tgz#56969dddb5741ef2bcb1af066cae21e61a91a27b" - integrity sha512-9Z59pJMi8ni+IUvSH5xQwK5tNLw7p3dwDNCZ3o1xE+of3G5Hc/yOz6Ue/YuLiBXU3ZB5oaHPURyPdqfBX/QYJA== dependencies: bin-build "^3.0.0" bin-wrapper "^4.0.0" @@ -4088,37 +3428,26 @@ mozjpeg@^6.0.0: ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.2: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== mute-stdout@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" - integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== nan@^2.12.1: version "2.14.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== nanoid@^3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -4135,19 +3464,16 @@ nanomatch@^1.2.9: natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= ndarray-ops@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/ndarray-ops/-/ndarray-ops-1.2.2.tgz#59e88d2c32a7eebcb1bc690fae141579557a614e" - integrity sha1-WeiNLDKn7ryxvGkPrhQVeVV6YU4= dependencies: cwise-compiler "^1.0.0" ndarray-pack@^1.1.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ndarray-pack/-/ndarray-pack-1.2.1.tgz#8caebeaaa24d5ecf70ff86020637977da8ee585a" - integrity sha1-jK6+qqJNXs9w/4YCBjeXfajuWFo= dependencies: cwise-compiler "^1.1.2" ndarray "^1.0.13" @@ -4155,7 +3481,6 @@ ndarray-pack@^1.1.1: ndarray@^1.0.13, ndarray@^1.0.18, ndarray@~1.0.15: version "1.0.19" resolved "https://registry.yarnpkg.com/ndarray/-/ndarray-1.0.19.tgz#6785b5f5dfa58b83e31ae5b2a058cfd1ab3f694e" - integrity sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ== dependencies: iota-array "^1.0.0" is-buffer "^1.0.2" @@ -4163,42 +3488,26 @@ ndarray@^1.0.13, ndarray@^1.0.18, ndarray@~1.0.15: neo-async@^2.6.0: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== next-tick@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" - integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== node-bitmap@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/node-bitmap/-/node-bitmap-0.0.1.tgz#180eac7003e0c707618ef31368f62f84b2a69091" - integrity sha1-GA6scAPgxwdhjvMTaPYvhLKmkJE= - -node-releases@^1.1.71: - version "1.1.73" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" - integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== - -node-releases@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" - integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== node-releases@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" resolve "^1.10.0" @@ -4208,24 +3517,20 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-range@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= normalize-url@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" - integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== dependencies: prepend-http "^2.0.0" query-string "^5.0.1" @@ -4234,24 +3539,20 @@ normalize-url@2.0.1: normalize-url@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.0.1.tgz#a4f27f58cf8c7b287b440b8a8201f42d0b00d256" - integrity sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ== normalize.css@8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" - integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== now-and-later@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c" - integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== dependencies: once "^1.3.2" npm-conf@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== dependencies: config-chain "^1.1.11" pify "^3.0.0" @@ -4259,86 +3560,68 @@ npm-conf@^1.1.0: npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= dependencies: path-key "^2.0.0" npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== dependencies: boolbase "~1.0.0" nth-check@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" - integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== dependencies: boolbase "^1.0.0" number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== obj-extend@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/obj-extend/-/obj-extend-0.1.0.tgz#bb448a4775fb95eb34a781f908bbac2df23dbb5b" - integrity sha1-u0SKR3X7les0p4H5CLusLfI9u1s= object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= dependencies: copy-descriptor "^0.1.0" define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" - integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== - object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.12.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" - integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= dependencies: isobject "^3.0.0" object.assign@^4.0.4, object.assign@^4.1.0, object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: call-bind "^1.0.0" define-properties "^1.1.3" @@ -4348,7 +3631,6 @@ object.assign@^4.0.4, object.assign@^4.1.0, object.assign@^4.1.2: object.defaults@^1.0.0, object.defaults@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" - integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= dependencies: array-each "^1.0.1" array-slice "^1.0.0" @@ -4358,7 +3640,6 @@ object.defaults@^1.0.0, object.defaults@^1.1.0: object.getownpropertydescriptors@^2.1.0: version "2.1.3" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" - integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" @@ -4367,7 +3648,6 @@ object.getownpropertydescriptors@^2.1.0: object.map@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" - integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= dependencies: for-own "^1.0.0" make-iterator "^1.0.0" @@ -4375,31 +3655,19 @@ object.map@^1.0.0: object.pick@^1.2.0, object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: isobject "^3.0.1" object.reduce@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object.reduce/-/object.reduce-1.0.1.tgz#6fe348f2ac7fa0f95ca621226599096825bb03ad" - integrity sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60= dependencies: for-own "^1.0.0" make-iterator "^1.0.0" -object.values@^1.1.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" - integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.2" - -object.values@^1.1.5: +object.values@^1.1.0, object.values@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" @@ -4408,26 +3676,22 @@ object.values@^1.1.5: omggif@^1.0.5: version "1.0.10" resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19" - integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw== once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: deep-is "^0.1.3" fast-levenshtein "^2.0.6" @@ -4439,7 +3703,6 @@ optionator@^0.9.1: optipng-bin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/optipng-bin/-/optipng-bin-6.0.0.tgz#376120fa79d5e71eee2f524176efdd3a5eabd316" - integrity sha512-95bB4y8IaTsa/8x6QH4bLUuyvyOoGBCLDA7wOgDL8UFqJpSUh1Hob8JRJhit+wC1ZLN3tQ7mFt7KuBj0x8F2Wg== dependencies: bin-build "^3.0.0" bin-wrapper "^4.0.0" @@ -4448,147 +3711,124 @@ optipng-bin@^6.0.0: ordered-read-streams@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" - integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= dependencies: readable-stream "^2.0.1" os-filter-obj@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/os-filter-obj/-/os-filter-obj-2.0.0.tgz#1c0b62d5f3a2442749a2d139e6dddee6e81d8d16" - integrity sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg== dependencies: arch "^2.1.0" os-locale@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= dependencies: lcid "^1.0.0" p-cancelable@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== p-cancelable@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" - integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== p-event@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-event/-/p-event-1.3.0.tgz#8e6b4f4f65c72bc5b6fe28b75eda874f96a4a085" - integrity sha1-jmtPT2XHK8W2/ii3XtqHT5akoIU= dependencies: p-timeout "^1.1.1" p-event@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/p-event/-/p-event-2.3.1.tgz#596279ef169ab2c3e0cae88c1cfbb08079993ef6" - integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA== dependencies: p-timeout "^2.0.1" p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-is-promise@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" - integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== dependencies: p-try "^1.0.0" p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= dependencies: p-limit "^1.1.0" p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" p-map-series@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" - integrity sha1-v5j+V1cFZYqeE1G++4WuTB8Hvco= dependencies: p-reduce "^1.0.0" p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" p-pipe@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" - integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== p-reduce@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" - integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= p-timeout@^1.1.1: version "1.2.1" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" - integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= dependencies: p-finally "^1.0.0" p-timeout@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" - integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== dependencies: p-finally "^1.0.0" p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-data-uri@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/parse-data-uri/-/parse-data-uri-0.2.0.tgz#bf04d851dd5c87b0ab238e5d01ace494b604b4c9" - integrity sha1-vwTYUd1ch7CrI45dAazklLYEtMk= dependencies: data-uri-to-buffer "0.0.3" parse-filepath@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" - integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= dependencies: is-absolute "^1.0.0" map-cache "^0.2.0" @@ -4597,83 +3837,68 @@ parse-filepath@^1.0.1: parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= dependencies: error-ex "^1.2.0" parse-node-version@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" - integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= dependencies: pinkie-promise "^2.0.0" path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6, path-parse@^1.0.7: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-root-regex@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= path-root@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= dependencies: path-root-regex "^0.1.0" path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= dependencies: graceful-fs "^4.1.2" pify "^2.0.0" @@ -4682,59 +3907,48 @@ path-type@^1.0.0: path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= pixelsmith@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/pixelsmith/-/pixelsmith-2.4.1.tgz#4ccfecf6eb5447da1d38fe26f2d70d246a40179e" - integrity sha512-6lVOPf9eBd9bWfxo5efmJcAiF6y65Ui9Ir8IR8jocrj/v/8QoLWZmgnhO7KGUfqkwPLNlCBfxVdjp4QihdPmPQ== dependencies: async "~0.9.0" concat-stream "~1.5.1" @@ -4748,7 +3962,6 @@ pixelsmith@^2.3.0: plugin-error@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" - integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== dependencies: ansi-colors "^1.0.1" arr-diff "^4.0.0" @@ -4758,29 +3971,24 @@ plugin-error@^1.0.1: plur@^3.0.1: version "3.1.1" resolved "https://registry.yarnpkg.com/plur/-/plur-3.1.1.tgz#60267967866a8d811504fe58f2faaba237546a5b" - integrity sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w== dependencies: irregular-plurals "^2.0.0" pngjs-nozlib@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/pngjs-nozlib/-/pngjs-nozlib-1.0.0.tgz#9e64d602cfe9cce4d9d5997d0687429a73f0b7d7" - integrity sha1-nmTWAs/pzOTZ1Zl9BodCmnPwt9c= pngjs@^3.3.3: version "3.4.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" - integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= postcss-calc@^8.2.3: version "8.2.4" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" - integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== dependencies: postcss-selector-parser "^6.0.9" postcss-value-parser "^4.2.0" @@ -4788,7 +3996,6 @@ postcss-calc@^8.2.3: postcss-colormin@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.0.tgz#3cee9e5ca62b2c27e84fce63affc0cfb5901956a" - integrity sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg== dependencies: browserslist "^4.16.6" caniuse-api "^3.0.0" @@ -4798,7 +4005,6 @@ postcss-colormin@^5.3.0: postcss-convert-values@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz#31586df4e184c2e8890e8b34a0b9355313f503ab" - integrity sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g== dependencies: browserslist "^4.20.3" postcss-value-parser "^4.2.0" @@ -4806,27 +4012,22 @@ postcss-convert-values@^5.1.2: postcss-discard-comments@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696" - integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ== postcss-discard-duplicates@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848" - integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw== postcss-discard-empty@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c" - integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A== postcss-discard-overridden@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" - integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== postcss-load-config@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.1.tgz#2f53a17f2f543d9e63864460af42efdac0d41f87" - integrity sha512-c/9XYboIbSEUZpiD1UQD0IKiUe8n9WHYV7YFe7X7J+ZwCsEKkUJSFWjS9hBU1RR9THR7jMXst8sxiqP0jjo2mg== dependencies: lilconfig "^2.0.4" yaml "^1.10.2" @@ -4834,7 +4035,6 @@ postcss-load-config@^3.0.0: postcss-merge-longhand@^5.1.6: version "5.1.6" resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz#f378a8a7e55766b7b644f48e5d8c789ed7ed51ce" - integrity sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw== dependencies: postcss-value-parser "^4.2.0" stylehacks "^5.1.0" @@ -4842,7 +4042,6 @@ postcss-merge-longhand@^5.1.6: postcss-merge-rules@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz#7049a14d4211045412116d79b751def4484473a5" - integrity sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ== dependencies: browserslist "^4.16.6" caniuse-api "^3.0.0" @@ -4852,14 +4051,12 @@ postcss-merge-rules@^5.1.2: postcss-minify-font-values@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b" - integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA== dependencies: postcss-value-parser "^4.2.0" postcss-minify-gradients@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c" - integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw== dependencies: colord "^2.9.1" cssnano-utils "^3.1.0" @@ -4868,7 +4065,6 @@ postcss-minify-gradients@^5.1.1: postcss-minify-params@^5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz#ac41a6465be2db735099bbd1798d85079a6dc1f9" - integrity sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg== dependencies: browserslist "^4.16.6" cssnano-utils "^3.1.0" @@ -4877,54 +4073,46 @@ postcss-minify-params@^5.1.3: postcss-minify-selectors@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6" - integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg== dependencies: postcss-selector-parser "^6.0.5" postcss-normalize-charset@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" - integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg== postcss-normalize-display-values@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8" - integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-positions@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92" - integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-repeat-style@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2" - integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-string@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228" - integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-timing-functions@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb" - integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-unicode@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz#3d23aede35e160089a285e27bf715de11dc9db75" - integrity sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ== dependencies: browserslist "^4.16.6" postcss-value-parser "^4.2.0" @@ -4932,7 +4120,6 @@ postcss-normalize-unicode@^5.1.0: postcss-normalize-url@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc" - integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew== dependencies: normalize-url "^6.0.1" postcss-value-parser "^4.2.0" @@ -4940,14 +4127,12 @@ postcss-normalize-url@^5.1.0: postcss-normalize-whitespace@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa" - integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA== dependencies: postcss-value-parser "^4.2.0" postcss-ordered-values@^5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38" - integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ== dependencies: cssnano-utils "^3.1.0" postcss-value-parser "^4.2.0" @@ -4955,7 +4140,6 @@ postcss-ordered-values@^5.1.3: postcss-reduce-initial@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz#fc31659ea6e85c492fb2a7b545370c215822c5d6" - integrity sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw== dependencies: browserslist "^4.16.6" caniuse-api "^3.0.0" @@ -4963,22 +4147,12 @@ postcss-reduce-initial@^5.1.0: postcss-reduce-transforms@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9" - integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ== dependencies: postcss-value-parser "^4.2.0" -postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5: - version "6.0.6" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" - integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - -postcss-selector-parser@^6.0.9: +postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: version "6.0.9" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" - integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -4986,7 +4160,6 @@ postcss-selector-parser@^6.0.9: postcss-svgo@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" - integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA== dependencies: postcss-value-parser "^4.2.0" svgo "^2.7.0" @@ -4994,19 +4167,16 @@ postcss-svgo@^5.1.0: postcss-unique-selectors@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6" - integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA== dependencies: postcss-selector-parser "^6.0.5" postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" - integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@8.4.16: version "8.4.16" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c" - integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ== dependencies: nanoid "^3.3.4" picocolors "^1.0.0" @@ -5015,57 +4185,46 @@ postcss@8.4.16: prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= pretty-bytes@^5.3.0: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" - integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== pretty-hrtime@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" - integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pump@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -5073,7 +4232,6 @@ pump@^2.0.0: pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -5081,7 +4239,6 @@ pump@^3.0.0: pumpify@^1.3.5: version "1.5.1" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== dependencies: duplexify "^3.6.0" inherits "^2.0.3" @@ -5090,27 +4247,22 @@ pumpify@^1.3.5: punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== query-string@^5.0.1: version "5.1.1" resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" - integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== dependencies: decode-uri-component "^0.2.0" object-assign "^4.1.0" @@ -5119,12 +4271,10 @@ query-string@^5.0.1: queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= dependencies: find-up "^1.0.0" read-pkg "^1.0.0" @@ -5132,25 +4282,14 @@ read-pkg-up@^1.0.1: read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= dependencies: load-json-file "^1.0.0" normalize-package-data "^2.3.2" path-type "^1.0.0" -"readable-stream@2 || 3", readable-stream@^3.1.1: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@2 || 3", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -5160,10 +4299,17 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.1.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@~1.0.33-1: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -5173,7 +4319,6 @@ readable-stream@~1.0.33-1: readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -5183,7 +4328,6 @@ readable-stream@~1.1.9: readable-stream@~2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" - integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -5195,7 +4339,6 @@ readable-stream@~2.0.0: readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== dependencies: graceful-fs "^4.1.11" micromatch "^3.1.10" @@ -5204,21 +4347,18 @@ readdirp@^2.2.1: readdirp@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== dependencies: picomatch "^2.2.1" rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= dependencies: resolve "^1.1.6" redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= dependencies: indent-string "^2.1.0" strip-indent "^1.0.1" @@ -5226,25 +4366,17 @@ redent@^1.0.0: regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== dependencies: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpp@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" - integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== - -regexpp@^3.2.0: +regexpp@^3.0.0, regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== remove-bom-buffer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" - integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== dependencies: is-buffer "^1.1.5" is-utf8 "^0.2.1" @@ -5252,7 +4384,6 @@ remove-bom-buffer@^3.0.0: remove-bom-stream@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" - integrity sha1-BfGlk/FuQuH7kOv1nejlaVJflSM= dependencies: remove-bom-buffer "^3.0.0" safe-buffer "^5.1.0" @@ -5261,39 +4392,32 @@ remove-bom-stream@^1.2.0: remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= dependencies: is-finite "^1.0.0" replace-ext@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" - integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= replace-ext@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" - integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== replace-homedir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" - integrity sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw= dependencies: homedir-polyfill "^1.0.1" is-absolute "^1.0.0" @@ -5302,7 +4426,6 @@ replace-homedir@^1.0.0: request@^2.44.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -5328,17 +4451,14 @@ request@^2.44.0: require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= resolve-dir@^1.0.0, resolve-dir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= dependencies: expand-tilde "^2.0.0" global-modules "^1.0.0" @@ -5346,41 +4466,20 @@ resolve-dir@^1.0.0, resolve-dir@^1.0.1: resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-options@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" - integrity sha1-MrueOcBtZzONyTeMDW1gdFZq0TE= dependencies: value-or-function "^3.0.0" resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.4.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -resolve@^1.20.0: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== - dependencies: - is-core-module "^2.8.1" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.22.0, resolve@^1.22.1: +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.4.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== dependencies: is-core-module "^2.9.0" path-parse "^1.0.7" @@ -5389,74 +4488,58 @@ resolve@^1.22.0, resolve@^1.22.1: responselike@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= dependencies: lowercase-keys "^1.0.0" ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rimraf@^2.5.4: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= dependencies: ret "~0.1.10" safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sass@^1.26.3: version "1.34.1" resolved "https://registry.yarnpkg.com/sass/-/sass-1.34.1.tgz#30f45c606c483d47b634f1e7371e13ff773c96ef" - integrity sha512-scLA7EIZM+MmYlej6sdVr0HRbZX5caX5ofDT9asWnUJj21oqgsC+1LuNfm0eg+vM0fCTZHhwImTiCU0sx9h9CQ== dependencies: chokidar ">=3.0.0 <4.0.0" save-pixels@~2.3.0: version "2.3.4" resolved "https://registry.yarnpkg.com/save-pixels/-/save-pixels-2.3.4.tgz#49d349c06b8d7c0127dbf0da24b44aca5afb59fe" - integrity sha1-SdNJwGuNfAEn2/DaJLRKylr7Wf4= dependencies: contentstream "^1.0.0" gif-encoder "~0.4.1" @@ -5469,65 +4552,54 @@ save-pixels@~2.3.0: sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== seek-bzip@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" - integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== dependencies: commander "^2.8.1" semver-greatest-satisfied-range@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" - integrity sha1-E+jCZYq5aRywzXEJMkAoDTb3els= dependencies: sver-compat "^1.5.0" semver-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" - integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== semver-truncate@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/semver-truncate/-/semver-truncate-1.1.2.tgz#57f41de69707a62709a7e0104ba2117109ea47e8" - integrity sha1-V/Qd5pcHpicJp+AQS6IRcQnqR+g= dependencies: semver "^5.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" + +semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^6.0.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.0.0, semver@^7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" -semver@~5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" - integrity sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no= - set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -5537,31 +4609,26 @@ set-value@^2.0.0, set-value@^2.0.1: shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" get-intrinsic "^1.0.2" @@ -5570,17 +4637,14 @@ side-channel@^1.0.4: signal-exit@^3.0.0, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: define-property "^1.0.0" isobject "^3.0.0" @@ -5589,14 +4653,12 @@ snapdragon-node@^2.0.1: snapdragon-util@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: kind-of "^3.2.0" snapdragon@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== dependencies: base "^0.11.1" debug "^2.2.0" @@ -5610,33 +4672,28 @@ snapdragon@^0.8.1: sort-keys-length@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" - integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= dependencies: sort-keys "^1.0.0" sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= dependencies: is-plain-obj "^1.0.0" sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" - integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= dependencies: is-plain-obj "^1.0.0" source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: atob "^2.1.2" decode-uri-component "^0.2.0" @@ -5647,7 +4704,6 @@ source-map-resolve@^0.5.0: source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -5655,32 +4711,26 @@ source-map-support@~0.5.12: source-map-url@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== source-map@^0.5.1, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== sparkles@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" - integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" @@ -5688,12 +4738,10 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0, spdx-expression-parse@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" @@ -5701,29 +4749,24 @@ spdx-expression-parse@^3.0.0, spdx-expression-parse@^3.0.1: spdx-license-ids@^3.0.0: version "3.0.9" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" - integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: extend-shallow "^3.0.0" sprintf-js@^1.0.3: version "1.1.2" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= spritesheet-templates@^10.3.0: version "10.5.2" resolved "https://registry.yarnpkg.com/spritesheet-templates/-/spritesheet-templates-10.5.2.tgz#b7526d1cdcede3c2cbfccb2963be9bec6066ec49" - integrity sha512-dMrLgS5eHCEDWqo1c3mDM5rGdJpBNf1JAJrxTKA4qR54trNTtxqGZlH3ZppS5FHTgjKgOtEmycqE2vGSkCYiVw== dependencies: handlebars "^4.6.0" handlebars-layouts "^3.1.4" @@ -5734,7 +4777,6 @@ spritesheet-templates@^10.3.0: spritesmith@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/spritesmith/-/spritesmith-3.4.0.tgz#5cfa004278c9072ab4900a0cd800141c7cf296e2" - integrity sha512-epa/Ib2GzkrzOA6ZMKH+YOX4ooBlRz8JwIV5NQDt9FvqXVHTh4dVn/0oA+n5eeu6wem1CCrtZWODlOqvwXXpyA== dependencies: concat-stream "~1.5.1" layout "~2.2.0" @@ -5745,7 +4787,6 @@ spritesmith@^3.4.0: squeak@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/squeak/-/squeak-1.3.0.tgz#33045037b64388b567674b84322a6521073916c3" - integrity sha1-MwRQN7ZDiLVnZ0uEMiplIQc5FsM= dependencies: chalk "^1.0.0" console-stream "^0.1.1" @@ -5754,7 +4795,6 @@ squeak@^1.0.0: sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -5769,17 +4809,14 @@ sshpk@^1.7.0: stable@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== stack-trace@0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= dependencies: define-property "^0.2.5" object-copy "^0.1.0" @@ -5787,22 +4824,18 @@ static-extend@^0.1.1: stream-exhaust@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d" - integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== stream-shift@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" is-fullwidth-code-point "^1.0.0" @@ -5811,7 +4844,6 @@ string-width@^1.0.1, string-width@^1.0.2: string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" @@ -5819,55 +4851,41 @@ string.prototype.trimend@^1.0.4: string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== +string_decoder@^1.1.1, string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" dependencies: - safe-buffer "~5.2.0" + safe-buffer "~5.1.0" string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= dependencies: ansi-regex "^3.0.0" strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-bom-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz#e7144398577d51a6bed0fa1994fa05f43fd988ee" - integrity sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4= dependencies: first-chunk-stream "^1.0.0" strip-bom "^2.0.0" @@ -5875,55 +4893,46 @@ strip-bom-stream@^1.0.0: strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= dependencies: is-utf8 "^0.2.0" strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-dirs@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" - integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== dependencies: is-natural-number "^4.0.1" strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= dependencies: get-stdin "^4.0.1" strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" - integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== dependencies: escape-string-regexp "^1.0.2" stylehacks@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520" - integrity sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q== dependencies: browserslist "^4.16.6" postcss-selector-parser "^6.0.4" @@ -5931,31 +4940,26 @@ stylehacks@^5.1.0: supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== sver-compat@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" - integrity sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg= dependencies: es6-iterator "^2.0.1" es6-symbol "^3.1.1" @@ -5963,7 +4967,6 @@ sver-compat@^1.5.0: svgo@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" - integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== dependencies: chalk "^2.4.1" coa "^2.0.2" @@ -5982,7 +4985,6 @@ svgo@^1.3.2: svgo@^2.7.0: version "2.8.0" resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" - integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== dependencies: "@trysound/sax" "0.2.0" commander "^7.2.0" @@ -5995,7 +4997,6 @@ svgo@^2.7.0: tar-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" - integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== dependencies: bl "^1.0.0" buffer-alloc "^1.2.0" @@ -6008,12 +5009,10 @@ tar-stream@^1.5.2: temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" - integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= tempfile@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-2.0.0.tgz#6b0446856a9b1114d1856ffcbe509cccb0977265" - integrity sha1-awRGhWqbERTRhW/8vlCczLCXcmU= dependencies: temp-dir "^1.0.0" uuid "^3.0.1" @@ -6021,7 +5020,6 @@ tempfile@^2.0.0: ternary-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ternary-stream/-/ternary-stream-3.0.0.tgz#7951930ea9e823924d956f03d516151a2d516253" - integrity sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ== dependencies: duplexify "^4.1.1" fork-stream "^0.0.4" @@ -6031,7 +5029,6 @@ ternary-stream@^3.0.0: terser@^4.6.12: version "4.8.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" - integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -6040,19 +5037,16 @@ terser@^4.6.12: text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= through2-concurrent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/through2-concurrent/-/through2-concurrent-2.0.0.tgz#c9dd2c146504ec9962dbc86a5168b63d662669fa" - integrity sha512-R5/jLkfMvdmDD+seLwN7vB+mhbqzWop5fAjx5IX8/yQq7VhBhzDmhXgaHAOnhnWkCpRMM7gToYHycB0CS/pd+A== dependencies: through2 "^2.0.0" through2-filter@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" - integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== dependencies: through2 "~2.0.0" xtend "~4.0.0" @@ -6060,7 +5054,6 @@ through2-filter@^3.0.0: through2@^2.0.0, through2@^2.0.3, through2@~2.0.0, through2@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== dependencies: readable-stream "~2.3.6" xtend "~4.0.1" @@ -6068,7 +5061,6 @@ through2@^2.0.0, through2@^2.0.3, through2@~2.0.0, through2@~2.0.3: through2@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" - integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== dependencies: inherits "^2.0.4" readable-stream "2 || 3" @@ -6076,22 +5068,18 @@ through2@^3.0.1: through@^2.3.4, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= time-stamp@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= timed-out@^4.0.0, timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= to-absolute-glob@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" - integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= dependencies: is-absolute "^1.0.0" is-negated-glob "^1.0.0" @@ -6099,19 +5087,16 @@ to-absolute-glob@^2.0.0: to-buffer@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" - integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: kind-of "^3.0.2" to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: is-number "^3.0.0" repeat-string "^1.6.1" @@ -6119,14 +5104,12 @@ to-regex-range@^2.1.0: to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== dependencies: define-property "^2.0.2" extend-shallow "^3.0.2" @@ -6136,14 +5119,12 @@ to-regex@^3.0.1, to-regex@^3.0.2: to-through@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" - integrity sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY= dependencies: through2 "^2.0.3" tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: psl "^1.1.28" punycode "^2.1.1" @@ -6151,19 +5132,16 @@ tough-cookie@~2.5.0: trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= trim-repeated@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" - integrity sha1-42RqLqTokTEr9+rObPsFOAvAHCE= dependencies: escape-string-regexp "^1.0.2" tsconfig-paths@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" @@ -6173,56 +5151,46 @@ tsconfig-paths@^3.14.1: tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== type@^2.0.0: version "2.5.0" resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" - integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typo-js@*: version "1.2.1" resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.2.1.tgz#334a0d8c3f6c56f2f1e15fdf6c31677793cbbe9b" - integrity sha512-bTGLjbD3WqZDR3CgEFkyi9Q/SS2oM29ipXrWfDb4M74ea69QwKAECVceYpaBu0GfdnASMg9Qfl67ttB23nePHg== uglify-js@^3.1.4: version "3.13.9" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.9.tgz#4d8d21dcd497f29cfd8e9378b9df123ad025999b" - integrity sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g== unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: function-bind "^1.1.1" has-bigints "^1.0.1" @@ -6232,7 +5200,6 @@ unbox-primitive@^1.0.1: unbzip2-stream@^1.0.9: version "1.4.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" - integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== dependencies: buffer "^5.2.1" through "^2.3.8" @@ -6240,12 +5207,10 @@ unbzip2-stream@^1.0.9: unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= underscore.string@~3.3.0: version "3.3.5" resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.5.tgz#fc2ad255b8bd309e239cbc5816fd23a9b7ea4023" - integrity sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg== dependencies: sprintf-js "^1.0.3" util-deprecate "^1.0.2" @@ -6253,17 +5218,14 @@ underscore.string@~3.3.0: underscore@~1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" - integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== undertaker-registry@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" - integrity sha1-XkvaMI5KiirlhPm5pDWaSZglzFA= undertaker@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.3.0.tgz#363a6e541f27954d5791d6fa3c1d321666f86d18" - integrity sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg== dependencies: arr-flatten "^1.0.1" arr-map "^2.0.0" @@ -6279,7 +5241,6 @@ undertaker@^1.2.1: union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== dependencies: arr-union "^3.1.0" get-value "^2.0.6" @@ -6289,12 +5250,10 @@ union-value@^1.0.0: uniq@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= unique-stream@^2.0.2: version "2.3.1" resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" - integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== dependencies: json-stable-stringify-without-jsonify "^1.0.1" through2-filter "^3.0.0" @@ -6302,12 +5261,10 @@ unique-stream@^2.0.2: unquote@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= dependencies: has-value "^0.3.1" isobject "^3.0.0" @@ -6315,20 +5272,10 @@ unset-value@^1.0.0: upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - -update-browserslist-db@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz#dbfc5a789caa26b1db8990796c2c8ebbce304824" - integrity sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" update-browserslist-db@^1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.7.tgz#16279639cff1d0f800b14792de43d97df2d11b7d" - integrity sha512-iN/XYesmZ2RmmWAiI4Z5rq0YqSiv0brj9Ce9CfhNE4xIW2h+MFxcgkxIzZ+ShkFPUkjU3gQ+3oypadD3RAMtrg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -6336,62 +5283,52 @@ update-browserslist-db@^1.0.5: uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= url-parse-lax@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= dependencies: prepend-http "^1.0.1" url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= dependencies: prepend-http "^2.0.0" url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= url2@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/url2/-/url2-1.0.4.tgz#df2284ae15c749ba809751510e5e25da9ebb80d8" - integrity sha1-3yKErhXHSbqAl1FRDl4l2p67gNg= dependencies: url "0.10.2" url@0.10.2: version "0.10.2" resolved "https://registry.yarnpkg.com/url/-/url-0.10.2.tgz#68621d6929ea1cad344ebf135d82fcf7eb1a7469" - integrity sha1-aGIdaSnqHK00Tr8TXYL89+sadGk= dependencies: punycode "1.3.2" use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= util.promisify@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== dependencies: define-properties "^1.1.3" es-abstract "^1.17.2" @@ -6401,19 +5338,16 @@ util.promisify@~1.0.0: uuid@^3.0.1, uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== v8flags@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" - integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== dependencies: homedir-polyfill "^1.0.1" validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" @@ -6421,12 +5355,10 @@ validate-npm-package-license@^3.0.1: value-or-function@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" - integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" @@ -6435,7 +5367,6 @@ verror@1.10.0: vinyl-file@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-1.3.0.tgz#aa05634d3a867ba91447bedbb34afcb26f44f6e7" - integrity sha1-qgVjTTqGe6kUR77bs0r8sm9E9uc= dependencies: graceful-fs "^4.1.2" strip-bom "^2.0.0" @@ -6445,7 +5376,6 @@ vinyl-file@~1.3.0: vinyl-fs@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" - integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== dependencies: fs-mkdirp-stream "^1.0.0" glob-stream "^6.1.0" @@ -6468,7 +5398,6 @@ vinyl-fs@^3.0.0: vinyl-sourcemap@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" - integrity sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY= dependencies: append-buffer "^1.0.2" convert-source-map "^1.5.0" @@ -6481,14 +5410,12 @@ vinyl-sourcemap@^1.1.0: vinyl-sourcemaps-apply@^0.2.0, vinyl-sourcemaps-apply@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" - integrity sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU= dependencies: source-map "^0.5.1" vinyl@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" - integrity sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ= dependencies: clone "^1.0.0" clone-stats "^0.0.1" @@ -6497,7 +5424,6 @@ vinyl@^1.1.0: vinyl@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" - integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== dependencies: clone "^2.1.1" clone-buffer "^1.0.0" @@ -6509,7 +5435,6 @@ vinyl@^2.0.0: vinyl@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.1.0.tgz#021f9c2cf951d6b939943c89eb5ee5add4fd924c" - integrity sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw= dependencies: clone "^2.1.1" clone-buffer "^1.0.0" @@ -6521,7 +5446,6 @@ vinyl@~2.1.0: which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" is-boolean-object "^1.1.0" @@ -6532,36 +5456,30 @@ which-boxed-primitive@^1.0.2: which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= which@^1.2.14, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= dependencies: string-width "^1.0.1" strip-ansi "^3.0.1" @@ -6569,37 +5487,30 @@ wrap-ansi@^2.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^3.2.1: version "3.2.2" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" - integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.1.tgz#7ede329c1d8cdbbe209bd25cdb990e9b1ebbb394" - integrity sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA== dependencies: camelcase "^3.0.0" object.assign "^4.1.0" @@ -6607,7 +5518,6 @@ yargs-parser@^5.0.1: yargs@^7.1.0: version "7.1.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.2.tgz#63a0a5d42143879fdbb30370741374e0641d55db" - integrity sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA== dependencies: camelcase "^3.0.0" cliui "^3.2.0" @@ -6626,7 +5536,6 @@ yargs@^7.1.0: yauzl@^2.4.2: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" @@ -6634,4 +5543,3 @@ yauzl@^2.4.2: yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/zds/tutorialv2/migrations/0035_merge_20210601_1744.py b/zds/tutorialv2/migrations/0035_merge_20210601_1744.py index 57e47263ca..d9af1801a3 100644 --- a/zds/tutorialv2/migrations/0035_merge_20210601_1744.py +++ b/zds/tutorialv2/migrations/0035_merge_20210601_1744.py @@ -6,9 +6,8 @@ class Migration(migrations.Migration): dependencies = [ - ('tutorialv2', '0034_auto_20200927_1105'), - ('tutorialv2', '0031_source_is_url'), + ("tutorialv2", "0034_auto_20200927_1105"), + ("tutorialv2", "0031_source_is_url"), ] - operations = [ - ] + operations = [] diff --git a/zds/tutorialv2/migrations/0037_merge_20210601_1830.py b/zds/tutorialv2/migrations/0037_merge_20210601_1830.py index 306a92a150..9d16e64521 100644 --- a/zds/tutorialv2/migrations/0037_merge_20210601_1830.py +++ b/zds/tutorialv2/migrations/0037_merge_20210601_1830.py @@ -6,9 +6,8 @@ class Migration(migrations.Migration): dependencies = [ - ('tutorialv2', '0036_quizzquestion_question_type'), - ('tutorialv2', '0035_merge_20210601_1744'), + ("tutorialv2", "0036_quizzquestion_question_type"), + ("tutorialv2", "0035_merge_20210601_1744"), ] - operations = [ - ] + operations = [] diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index 22970e9f5d..f742b0f7d1 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -120,22 +120,15 @@ path( "nouveau-quizz/////", CreateExtract.as_view(quizz=True), - name="create-quizz" + name="create-quizz", ), path( "nouveau-quizz/(///", CreateExtract.as_view(quizz=True), - name='create-quizz' - ), - path( - "nouvelle-section///", CreateExtract.as_view(), - name="create-extract" - ), - path( - "nouveau-quizz///", - CreateExtract.as_view(quizz=True), - name='create-quizz' + name="create-quizz", ), + path("nouvelle-section///", CreateExtract.as_view(), name="create-extract"), + path("nouveau-quizz///", CreateExtract.as_view(quizz=True), name="create-quizz"), # edit: path( "editer-conteneur////" r"/", @@ -208,9 +201,5 @@ path("modifier-objectifs//", EditGoals.as_view(), name="edit-goals"), path("objectifs/", ViewContentsByGoal.as_view(), name="view-goals"), # quizz - path( - "reponses///", - ContentQuizzStatistics.as_view(), - name="answer-quizz" - ), + path("reponses///", ContentQuizzStatistics.as_view(), name="answer-quizz"), ] diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index c550f8c06d..89e8325a8f 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -1,6 +1,6 @@ import itertools import uuid -from collections import OrderedDict +from collections import OrderedDict, Counter import logging import urllib.parse from datetime import timedelta, datetime, date @@ -339,28 +339,28 @@ def build_quizz_stats(self, end_date, start_date): QuizzUserAnswer.objects.values( "related_question__pk", "related_question__question", "related_question__url", "answer" ) - .filter(related_question__in=base_questions, date_answer__range=(start_date, end_date)) - .annotate(nb=Count("answer")) + .filter(related_question__in=base_questions, date_answer__range=(start_date, end_date)) + .annotate(nb=Count("answer")) ) for base_question in set(base_questions): full_answers_total = {} url = "" question = "" for available_answer in ( - QuizzAvailableAnswer.objects.filter(related_question__pk=base_question) - .prefetch_related("related_question") - .all() + QuizzAvailableAnswer.objects.filter(related_question__pk=base_question) + .prefetch_related("related_question") + .all() ): full_answers_total[available_answer.label] = {"good": available_answer.is_good, "nb": 0} url = available_answer.related_question.url question = available_answer.related_question.question for r in total_per_label: if ( - r["related_question__pk"] == base_question - and r["answer"].strip() == available_answer.label.strip() + r["related_question__pk"] == base_question + and r["answer"].strip() == available_answer.label.strip() ): full_answers_total[available_answer.label]["nb"] = r["nb"] if url not in quizz_stats: quizz_stats[url] = OrderedDict() quizz_stats[url][question] = {"total": total_per_question[base_question], "responses": full_answers_total} - return quizz_stats \ No newline at end of file + return quizz_stats From cb5eecab2c8b34a19fadf563a6250b9799162718 Mon Sep 17 00:00:00 2001 From: artragis Date: Sun, 30 Oct 2022 21:27:53 +0100 Subject: [PATCH 15/78] doc lint --- doc/source/back-end/quizz.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/back-end/quizz.rst b/doc/source/back-end/quizz.rst index 849296a393..74255dbab9 100644 --- a/doc/source/back-end/quizz.rst +++ b/doc/source/back-end/quizz.rst @@ -34,8 +34,8 @@ Ajouter des types de questions Globalement, l'ajout de type de questions, disons "texte libre" pour l'exemple se décompose en trois parties : - Décider de quelle syntaxe Markdown on tirera partie pour déinir la correction. Imaginons ici qu'on utilise la syntaxe -des codes inlines (deux `) + des codes inlines (deux `) - Implémenter dans zMarkdown un preprocessor de ``quizzCustomBlock`` qui permettra de remplacer les ``inlineCode`` par un texte -composé de ``______`` dans le quizz et les laissera intact dans la correction + composé de ``______`` dans le quizz et les laissera intact dans la correction - Adapter ``content_quizz.js`` et ``statistics.py`` pour que la correction se fasse et que les statistiques remontent From a2588acc4b4169ad028490d99918247fbcfde5b5 Mon Sep 17 00:00:00 2001 From: artragis Date: Sun, 30 Oct 2022 21:36:56 +0100 Subject: [PATCH 16/78] doc lint --- doc/source/back-end/quizz.rst | 6 ++---- .../migrations/0038_merge_20221030_2030.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 zds/tutorialv2/migrations/0038_merge_20221030_2030.py diff --git a/doc/source/back-end/quizz.rst b/doc/source/back-end/quizz.rst index 74255dbab9..cb400c7bf2 100644 --- a/doc/source/back-end/quizz.rst +++ b/doc/source/back-end/quizz.rst @@ -33,9 +33,7 @@ Ajouter des types de questions Globalement, l'ajout de type de questions, disons "texte libre" pour l'exemple se décompose en trois parties : -- Décider de quelle syntaxe Markdown on tirera partie pour déinir la correction. Imaginons ici qu'on utilise la syntaxe - des codes inlines (deux `) -- Implémenter dans zMarkdown un preprocessor de ``quizzCustomBlock`` qui permettra de remplacer les ``inlineCode`` par un texte - composé de ``______`` dans le quizz et les laissera intact dans la correction +- Décider de quelle syntaxe Markdown on tirera partie pour déinir la correction. Imaginons ici qu'on utilise la syntaxe des codes inlines (deux `````) +- Implémenter dans zMarkdown un preprocessor de ``quizzCustomBlock`` qui permettra de remplacer les ``inlineCode`` par un texte composé de ``______`` dans le quizz et les laissera intact dans la correction - Adapter ``content_quizz.js`` et ``statistics.py`` pour que la correction se fasse et que les statistiques remontent diff --git a/zds/tutorialv2/migrations/0038_merge_20221030_2030.py b/zds/tutorialv2/migrations/0038_merge_20221030_2030.py new file mode 100644 index 0000000000..81b84e508d --- /dev/null +++ b/zds/tutorialv2/migrations/0038_merge_20221030_2030.py @@ -0,0 +1,13 @@ +# Generated by Django 3.2.15 on 2022-10-30 20:30 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("tutorialv2", "0035_alter_publishablecontent_goals"), + ("tutorialv2", "0037_merge_20210601_1830"), + ] + + operations = [] From 496cc305dbd0de8d47984392de830cb53ca681a9 Mon Sep 17 00:00:00 2001 From: artragis Date: Sun, 30 Oct 2022 21:51:59 +0100 Subject: [PATCH 17/78] clean up models --- zds/tutorialv2/models/quizz.py | 18 ------------------ zds/tutorialv2/views/statistics.py | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/zds/tutorialv2/models/quizz.py b/zds/tutorialv2/models/quizz.py index e483b8f70c..ac45a7dbdd 100644 --- a/zds/tutorialv2/models/quizz.py +++ b/zds/tutorialv2/models/quizz.py @@ -44,21 +44,3 @@ class QuizzUserAnswer(models.Model): max_length=64, default="id", ) - - -class QuizzStat(models.Model): - related_content = models.ForeignKey( - PublishableContent, verbose_name="Tutoriel lié", blank=True, null=True, on_delete=models.CASCADE - ) - answer = models.TextField(name="answer", verbose_name="anwser", null=False, blank=False) - date_answer = models.DateField(name="date_answer", verbose_name="Date of answer", null=False, auto_now=True) - related_question = models.ForeignKey( - QuizzQuestion, name="related_question", verbose_name="Question liée", on_delete=models.CASCADE - ) - full_answer_id = models.CharField( - name="full_answer_id", - verbose_name="Indentifiant de la réponse utilisateur", - blank=False, - max_length=64, - default="id", - ) diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index 89e8325a8f..2af315603d 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -18,7 +18,7 @@ from zds.tutorialv2.forms import ContentCompareStatsURLForm, QuizzStatsForm from zds.tutorialv2.mixins import SingleOnlineContentDetailViewMixin, SingleOnlineContentFormViewMixin -from zds.tutorialv2.models.quizz import QuizzUserAnswer, QuizzQuestion, QuizzAvailableAnswer, QuizzStat +from zds.tutorialv2.models.quizz import QuizzUserAnswer, QuizzQuestion, QuizzAvailableAnswer from zds.tutorialv2.utils import NamedUrl From 5bf27a01749fb5e47a7728ddab0c6286a726016a Mon Sep 17 00:00:00 2001 From: artragis Date: Mon, 31 Oct 2022 07:43:24 +0100 Subject: [PATCH 18/78] Apply suggestions from code review Co-authored-by: Situphen --- assets/js/content-quizz.js | 3 +-- doc/source/back-end/quizz.rst | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index cb4a93ecfa..d8769bc36b 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -137,8 +137,7 @@ function injectForms(quizz, answers) { const submit = document.createElement('button') submit.innerText = submitLabel - submit.classList.add('btn-submit') - submit.classList.add('btn') + submit.classList.add('btn', 'btn-submit') const result = document.createElement('p') result.classList.add('result') let nodeToAddToForm = heading diff --git a/doc/source/back-end/quizz.rst b/doc/source/back-end/quizz.rst index cb400c7bf2..feadde563d 100644 --- a/doc/source/back-end/quizz.rst +++ b/doc/source/back-end/quizz.rst @@ -2,7 +2,7 @@ Ajout de Quizz à Zest de savoir =============================== -Zeste de savoir est capablede présenter des quizz à l'utilisateur. La philosophie qui a été retenue est : +Zeste de savoir est capable de présenter des quizz à l'utilisateur. La philosophie qui a été retenue est : - L'auteur écrit la **correction** du quizz dans un extrait qui est marqué comme étant un quizz. - Lorsque le tutoriel est publié, côté client, un script parcourt cette correction et la transforme en formulaire. @@ -11,7 +11,7 @@ Zeste de savoir est capablede présenter des quizz à l'utilisateur. La philosop L'ajout de quizz n'est pas une mince affaire dans notre code d'autant qu'il est important de les rendre **faciles** à rédiger. En effet, rédiger sur ZDS demande pas mal d'effort et l'ajout de quizz avec les méthodes habituelles à base -de formulaires spéciiques, qui permettent de "configurer le rendu" de la question est peu pertinent. +de formulaires spécifiques, qui permettent de "configurer le rendu" de la question est peu pertinent. La première version du module de quizz permet d'ajouter des QCM mais a été conçue pour permettre l'ajout d'autres types de réponse. @@ -25,7 +25,7 @@ Utiliser Zmarkdown > 10 ----------------------- Avec l'utilisation de zmarkdown > 10 vous pouvez utiliser le bloc ``[[quizz | Intitulé de la question]]``. -Ce bloc a le même comportement que le bloc neutre. Cependant il permet d'ajouter des informations de contexte nécessaire à l'export Latex. +Ce bloc a le même comportement que le bloc neutre. Cependant il permet d'ajouter des informations de contexte nécessaires à l'export Latex. De même on devrait pouvoir tirer partie du fait que ce bloc a une classe précise dans les implémentations des futurs types de questions. Ajouter des types de questions @@ -33,7 +33,7 @@ Ajouter des types de questions Globalement, l'ajout de type de questions, disons "texte libre" pour l'exemple se décompose en trois parties : -- Décider de quelle syntaxe Markdown on tirera partie pour déinir la correction. Imaginons ici qu'on utilise la syntaxe des codes inlines (deux `````) -- Implémenter dans zMarkdown un preprocessor de ``quizzCustomBlock`` qui permettra de remplacer les ``inlineCode`` par un texte composé de ``______`` dans le quizz et les laissera intact dans la correction +- Décider de quelle syntaxe Markdown on tirera partie pour définir la correction. Imaginons ici qu'on utilise la syntaxe des codes inlines (deux `````) +- Implémenter dans zMarkdown un préprocesseur de ``quizzCustomBlock`` qui permettra de remplacer les ``inlineCode`` par un texte composé de ``______`` dans le quizz et les laissera intacts dans la correction - Adapter ``content_quizz.js`` et ``statistics.py`` pour que la correction se fasse et que les statistiques remontent From 458f1c5194a3a5a0b077b5c778e93a249f2d0b0a Mon Sep 17 00:00:00 2001 From: artragis Date: Mon, 31 Oct 2022 21:42:08 +0100 Subject: [PATCH 19/78] Rend le JS plus lisible (et qui sait? plus digeste) --- assets/js/charts.js | 4 -- assets/js/content-quizz.js | 144 ++++++++++++++++++++++++++----------- 2 files changed, 104 insertions(+), 44 deletions(-) diff --git a/assets/js/charts.js b/assets/js/charts.js index c6a12d20cd..7f60c4b93f 100644 --- a/assets/js/charts.js +++ b/assets/js/charts.js @@ -195,10 +195,6 @@ setupChart(el, chartFormatters[g]) } } - const quizzCharts = $('.quizz-chart') - if (quizzCharts.length) { - setupPie(quizzCharts) - } } drawCharts() diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index d8769bc36b..afa4d9f948 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -4,33 +4,76 @@ let index = 0 -function extractAnswer(radio, answers) { - radio.forEach((rb) => { - if (!rb.parentElement.parentElement.getAttribute('id')) { - rb.parentElement.parentElement.setAttribute('id', 'id-' + (index++)) +function extractAnswer(inputDomElementList, answers) { + inputDomElementList.forEach((rb) => { + + let ulWrapperElement = rb.parentElement.parentElement; + // we give the ui an id to find the element in a more effective way later when the users answer the questions + if (!ulWrapperElement.getAttribute('id')) { + ulWrapperElement.setAttribute('id', 'id-' + (index++)) } - rb.setAttribute('name', rb.parentElement.parentElement.getAttribute('id')) - rb.parentElement.parentElement.parentElement.parentElement.setAttribute('data-name', rb.getAttribute('name')) - if (!answers[rb.parentNode.parentNode.getAttribute('id')]) { - answers[rb.parentNode.parentNode.getAttribute('id')] = [rb.checked] + rb.setAttribute('name', ulWrapperElement.getAttribute('id')) + let questionBlock = ulWrapperElement.parentElement.parentElement; + questionBlock.setAttribute('data-name', rb.getAttribute('name')) + if (!answers[ulWrapperElement.getAttribute('id')]) { + answers[ulWrapperElement.getAttribute('id')] = [rb.checked] } else { - answers[rb.parentNode.parentNode.getAttribute('id')].push(rb.checked) + answers[ulWrapperElement.getAttribute('id')].push(rb.checked) } - rb.setAttribute('value', answers[rb.parentNode.parentNode.getAttribute('id')].length - 1) + rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) rb.disabled = false rb.checked = false }) } -function initializeCheckboxes(answers, blockIds) { +/** + * The full quizz is contained in a div or article that has class "quizz". + * Then one question is inside a zmarkdown "custom-block" of type "custom-block-quizz". Two possibilities : + * + * Without explanation for correction : + * + * + *
    + *
    The question
    + *
    + *
    • the answer
    • + *
    • the good answer
    • + *
    + *
    + *
    + *
    + * + * With an explanation inside another custom block most of time a custom-block-neutral + * + * + *
    + *
    The question
    + *
    + *
    • the answer
    • + *
    • the good answer + *
      + *
      Explanation
      + *
      a formatted text
      + *
    • + *
    + + *
    + *
    + *
    + * + * Note that the correction MAY be inside the last li due to the way custom-block plugin works, this is not a bug + * + * @param answers the answer dictionary, it will be modified by the process + */ +function initializeCheckboxes(answers) { const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') - extractAnswer(checkboxes, answers, blockIds) + extractAnswer(checkboxes, answers) } -function initializeRadio(answers, blockIds) { +function initializeRadio(answers) { const radio = document.querySelectorAll('.quizz ul li input[type=radio]') - extractAnswer(radio, answers, blockIds) + extractAnswer(radio, answers) } const initializePipeline = [initializeCheckboxes, initializeRadio] @@ -43,7 +86,6 @@ function computeForm(formdata, answers) { const values = parseInt(entry[1], 10) allAnswerNames.push(name) if (!answers[name]) { - console.log('not found ' + name) continue } else { // for poc we assume we only deal with lists @@ -109,6 +151,19 @@ function getWantedHeading(questionNode, nodeName, attr) { return potentialHeading } +/** + * As we are using forms to capture the answer and send the result back to user (and flushing stats for authors), + * we need to add an html form. + * + * The main issue is that it was asked to have many quizz inside a tutorial section, not just a list of questions in a + * specific section. As a result we had to find an heuristic : + * - if the current section has no h3 headings, we just span the form from the beggining to the end of section + * - if the current section has one or more h3 headings, we start the form just after it and end it at just before the + * next h3 heading if there is one or the end of form + * + * @param quizz current quizz container + * @param answers answers dictionary + */ function injectForms(quizz, answers) { const searchedTitle = quizz.getAttribute('data-heading-level') || 'h3' const submitLabel = quizz.getAttribute('data-quizz-validate') || 'Validate' @@ -121,14 +176,15 @@ function injectForms(quizz, answers) { // if the node was treated and therefore the clone has not been reinserted yet return } + // this is the custom-block-quizz node const questionNode = blockNode.parentElement.parentElement const heading = getWantedHeading(questionNode, searchedTitle, 'previousSibling') || quizz if (!heading.getAttribute('id')) { - console.log('new id') heading.setAttribute('id', `quizz-form-${idBias}`) idBias++ } if (heading && !headings[heading.getAttribute('id')]) { + // this is just for convenience, this add a "known" element that will always be there const wrapper = document.createElement('div') headings[heading.getAttribute('id')] = true @@ -140,7 +196,7 @@ function injectForms(quizz, answers) { submit.classList.add('btn', 'btn-submit') const result = document.createElement('p') result.classList.add('result') - let nodeToAddToForm = heading + let nodeToAddToForm if (heading === quizz) { nodeToAddToForm = quizz.firstChild } else { @@ -148,7 +204,7 @@ function injectForms(quizz, answers) { } form.method = 'POST' form.setAttribute('action', quizz.getAttribute('data-answer-url')) - + // gather all the questions of this current subsection, until the next h3 heading while (nodeToAddToForm && nodeToAddToForm.nodeName !== searchedTitle.toUpperCase()) { const current = nodeToAddToForm nodeToAddToForm = nodeToAddToForm.nextSibling @@ -169,11 +225,11 @@ function injectForms(quizz, answers) { } } } + // avoid doubly if (heading.nodeName === searchedTitle.toUpperCase()) { quizz.removeChild(heading) } }) - console.log(wrappers) wrappers.forEach((wrapper) => quizz.appendChild(wrapper)) } @@ -182,7 +238,28 @@ initializePipeline.forEach(func => func(answers)) document.querySelectorAll('div.quizz').forEach(div => { injectForms(div, answers) }) + +function sendQuizzStatistics(form, statistics) { + const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value + const xhttp = new XMLHttpRequest() + xhttp.open('POST', form.getAttribute('action')) + xhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest') + xhttp.setRequestHeader('Content-Type', 'application/json') + xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) + statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href + xhttp.send(JSON.stringify(statistics)) +} + +function displayResultAfterSubmitButton(nbGood, nbTotal, form) { + const percentOfAnswers = (100 * 1.0 * nbGood / nbTotal).toLocaleString('fr-FR', { + minimumIntegerDigits: 1, + useGrouping: false + }) + form.querySelector('.result').innerText = `Vous avez bien répondu à ${percentOfAnswers}% des questions.` +} + document.querySelectorAll('form.quizz').forEach(form => { + // compute the answer and send stats form.addEventListener('submit', e => { e.preventDefault() e.stopPropagation() @@ -191,7 +268,9 @@ document.querySelectorAll('form.quizz').forEach(form => { const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) markBadAnswers(badAnswerNames, answers) allAnswerNames.forEach(name => { - document.getElementById(name).parentElement.parentElement.classList.add('hasAnswer') + const ulWrapper = document.getElementById(name); + const quizzCustomBlock = ulWrapper.parentElement.parentElement; + quizzCustomBlock.classList.add('hasAnswer') }) const questions = [] badAnswerNames.forEach(result => { @@ -205,13 +284,10 @@ document.querySelectorAll('form.quizz').forEach(form => { } let nbGood = 0 let nbTotal = 0 - console.log(answers) Object.keys(answers).forEach(name => { const element = document.querySelector(`.custom-block[data-name="${name}"]`) let title = element.querySelector('.custom-block-heading').textContent const correction = element.querySelector('.custom-block-body .custom-block') - console.log(correction) - console.log(title) if (correction && title.indexOf(correction.textContent) > 0) { title = title.substr(0, title.indexOf(correction.textContent)) } @@ -222,9 +298,9 @@ document.querySelectorAll('form.quizz').forEach(form => { statistics.expected[title] = {} const availableResponses = element.querySelectorAll('input') for (let i = 0; i < availableResponses.length; i++) { - let questionLabel = availableResponses[i].parentElement.textContent; - console.log(questionLabel) - console.log(questionLabel.indexOf(correction.textContent)) + // wee need to get the question label for statistics + const liWrapper = availableResponses[i].parentElement; + let questionLabel = liWrapper.textContent if (correction && questionLabel.indexOf(correction.textContent) !== -1) { questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) } @@ -238,7 +314,6 @@ document.querySelectorAll('form.quizz').forEach(form => { if (correction && label.indexOf(correction.textContent) !== -1) { label = label.substr(0, label.indexOf(correction.textContent)) } - statistics.result[title].labels.push(label.trim()) }) if (element.classList.contains('hasAnswer')) { @@ -252,18 +327,7 @@ document.querySelectorAll('form.quizz').forEach(form => { } } }) - const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value - const xhttp = new XMLHttpRequest() - xhttp.open('POST', form.getAttribute('action')) - xhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest') - xhttp.setRequestHeader('Content-Type', 'application/json') - xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) - statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href - xhttp.send(JSON.stringify(statistics)) - const percentOfAnswers = (100 * 1.0 * nbGood / nbTotal).toLocaleString('fr-FR', { - minimumIntegerDigits: 1, - useGrouping: false - }) - form.querySelector('.result').innerText = `Vous avez bien répondu à ${percentOfAnswers}% des questions.` + sendQuizzStatistics(form, statistics); + displayResultAfterSubmitButton(nbGood, nbTotal, form); }) }) From f76544be478d108ee014e2234414aeab6e71e2d8 Mon Sep 17 00:00:00 2001 From: artragis Date: Mon, 31 Oct 2022 21:48:10 +0100 Subject: [PATCH 20/78] Indente le span --- templates/misc/quizz.graph.part.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index a42cb0804c..b52e514688 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -15,8 +15,8 @@

    {% trans graph_title %} {{ question }}

    - - {{answer_stats.nb}} / {{ stats.total }} + + {{answer_stats.nb}} / {{ stats.total }} {% endfor %} From 7961f52fac2f47b57f368183ced7a3363f3e8a8c Mon Sep 17 00:00:00 2001 From: artragis Date: Mon, 31 Oct 2022 21:55:03 +0100 Subject: [PATCH 21/78] linter hell --- assets/js/content-quizz.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index afa4d9f948..deaa07a07b 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -6,14 +6,13 @@ let index = 0 function extractAnswer(inputDomElementList, answers) { inputDomElementList.forEach((rb) => { - - let ulWrapperElement = rb.parentElement.parentElement; + const ulWrapperElement = rb.parentElement.parentElement // we give the ui an id to find the element in a more effective way later when the users answer the questions if (!ulWrapperElement.getAttribute('id')) { ulWrapperElement.setAttribute('id', 'id-' + (index++)) } rb.setAttribute('name', ulWrapperElement.getAttribute('id')) - let questionBlock = ulWrapperElement.parentElement.parentElement; + const questionBlock = ulWrapperElement.parentElement.parentElement questionBlock.setAttribute('data-name', rb.getAttribute('name')) if (!answers[ulWrapperElement.getAttribute('id')]) { answers[ulWrapperElement.getAttribute('id')] = [rb.checked] @@ -56,7 +55,6 @@ function extractAnswer(inputDomElementList, answers) { *
    a formatted text
    *

    * - *

    *

    * @@ -268,8 +266,8 @@ document.querySelectorAll('form.quizz').forEach(form => { const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) markBadAnswers(badAnswerNames, answers) allAnswerNames.forEach(name => { - const ulWrapper = document.getElementById(name); - const quizzCustomBlock = ulWrapper.parentElement.parentElement; + const ulWrapper = document.getElementById(name) + const quizzCustomBlock = ulWrapper.parentElement.parentElement quizzCustomBlock.classList.add('hasAnswer') }) const questions = [] @@ -299,7 +297,7 @@ document.querySelectorAll('form.quizz').forEach(form => { const availableResponses = element.querySelectorAll('input') for (let i = 0; i < availableResponses.length; i++) { // wee need to get the question label for statistics - const liWrapper = availableResponses[i].parentElement; + const liWrapper = availableResponses[i].parentElement let questionLabel = liWrapper.textContent if (correction && questionLabel.indexOf(correction.textContent) !== -1) { questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) @@ -327,7 +325,7 @@ document.querySelectorAll('form.quizz').forEach(form => { } } }) - sendQuizzStatistics(form, statistics); - displayResultAfterSubmitButton(nbGood, nbTotal, form); + sendQuizzStatistics(form, statistics) + displayResultAfterSubmitButton(nbGood, nbTotal, form) }) }) From f7306b5ebb9f24a0acff3daa66dd14ff6a4ebd04 Mon Sep 17 00:00:00 2001 From: atmGLM <96666415+atman0eirb@users.noreply.github.com> Date: Fri, 30 Dec 2022 11:36:20 +0100 Subject: [PATCH 22/78] =?UTF-8?q?Billets=20pr=C3=A9c=C3=A9dents=20et=20sui?= =?UTF-8?q?vants=20:=20uniquement=20ceux=20choisis=20par=20le=20Staff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/tutorialv2/views/display.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zds/tutorialv2/views/display.py b/zds/tutorialv2/views/display.py index ad7804d058..3541281902 100644 --- a/zds/tutorialv2/views/display.py +++ b/zds/tutorialv2/views/display.py @@ -1,5 +1,5 @@ import logging - +from django.db.models import F from django.conf import settings from django.http import Http404 from django.utils.translation import gettext_lazy as _ @@ -86,6 +86,8 @@ def get_context_data(self, **kwargs): content_type=self.current_content_type, must_redirect=False ) + if self.current_content_type == "OPINION": + queryset_pagination = queryset_pagination.filter(content__sha_picked=F("sha_public")) context["previous_content"] = ( queryset_pagination.filter(publication_date__lt=self.public_content_object.publication_date) .order_by("-publication_date") From 83a14cde1e6d3dc46315c12a45fec7b4d98cb268 Mon Sep 17 00:00:00 2001 From: atmGLM <96666415+atman0eirb@users.noreply.github.com> Date: Fri, 13 Jan 2023 17:22:59 +0100 Subject: [PATCH 23/78] modify test to accept the Fix #5793 modify test : test_opinion_link_is_not_related_to_the_author in file : zds/tutorialv2/tests/tests_views/tests_published.py to accept the Fix #5793 --- .../tests/tests_views/tests_published.py | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/zds/tutorialv2/tests/tests_views/tests_published.py b/zds/tutorialv2/tests/tests_views/tests_published.py index e8817e7448..44320c0482 100644 --- a/zds/tutorialv2/tests/tests_views/tests_published.py +++ b/zds/tutorialv2/tests/tests_views/tests_published.py @@ -1877,13 +1877,57 @@ def test_opinion_link_is_not_related_to_the_author(self): Test that the next and previous link in the opinion page take all the opinions into accounts and not only the ones of the author. """ + + opinion_not_picked = PublishedContentFactory(author_list=[self.user_author], type="OPINION") + opinion_not_picked.save() user_1_opinion_1 = PublishedContentFactory(author_list=[self.user_author], type="OPINION") - user_2_opinion_1 = PublishedContentFactory(author_list=[self.user_guest], type="OPINION") - user_1_opinion_2 = PublishedContentFactory(author_list=[self.user_author], type="OPINION") + user_1_opinion_1.sha_picked = user_1_opinion_1.sha_public user_1_opinion_1.save() + + + opinion_not_picked = PublishedContentFactory(author_list=[self.user_author], type="OPINION") + opinion_not_picked.save() + + user_2_opinion_1 = PublishedContentFactory(author_list=[self.user_guest], type="OPINION") + user_2_opinion_1.sha_picked = user_2_opinion_1.sha_public user_2_opinion_1.save() + + + opinion_not_picked = PublishedContentFactory(author_list=[self.user_author], type="OPINION") + opinion_not_picked.save() + + user_1_opinion_2 = PublishedContentFactory(author_list=[self.user_author], type="OPINION") + user_1_opinion_2.sha_picked = user_1_opinion_2.sha_public user_1_opinion_2.save() + + opinion_not_picked = PublishedContentFactory(author_list=[self.user_author], type="OPINION") + opinion_not_picked.save() + + + + PickListOperation.objects.create( + content=user_1_opinion_1, + operation="PICK", + staff_user=self.user_staff, + operation_date=datetime.datetime.now(), + version=user_1_opinion_1.sha_public, + ) + PickListOperation.objects.create( + content=user_2_opinion_1, + operation="PICK", + staff_user=self.user_staff, + operation_date=datetime.datetime.now(), + version=user_2_opinion_1.sha_public, + ) + PickListOperation.objects.create( + content=user_1_opinion_2, + operation="PICK", + staff_user=self.user_staff, + operation_date=datetime.datetime.now(), + version=user_1_opinion_2.sha_public, + ) + result = self.client.get( reverse("opinion:view", kwargs={"pk": user_1_opinion_2.pk, "slug": user_1_opinion_2.slug}) From e721ba2e8e01e2a8d0b70f92584036a661c031f1 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Fri, 13 Jan 2023 20:00:22 +0100 Subject: [PATCH 24/78] import de PickListOperation pour le test --- .../tests/tests_views/tests_published.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/zds/tutorialv2/tests/tests_views/tests_published.py b/zds/tutorialv2/tests/tests_views/tests_published.py index 44320c0482..f57c019d9e 100644 --- a/zds/tutorialv2/tests/tests_views/tests_published.py +++ b/zds/tutorialv2/tests/tests_views/tests_published.py @@ -22,6 +22,7 @@ HelpWritingFactory, ) from zds.tutorialv2.models.database import ( + PickListOperation, PublishableContent, Validation, PublishedContent, @@ -1877,35 +1878,31 @@ def test_opinion_link_is_not_related_to_the_author(self): Test that the next and previous link in the opinion page take all the opinions into accounts and not only the ones of the author. """ - + opinion_not_picked = PublishedContentFactory(author_list=[self.user_author], type="OPINION") opinion_not_picked.save() user_1_opinion_1 = PublishedContentFactory(author_list=[self.user_author], type="OPINION") user_1_opinion_1.sha_picked = user_1_opinion_1.sha_public user_1_opinion_1.save() - - + opinion_not_picked = PublishedContentFactory(author_list=[self.user_author], type="OPINION") opinion_not_picked.save() - + user_2_opinion_1 = PublishedContentFactory(author_list=[self.user_guest], type="OPINION") user_2_opinion_1.sha_picked = user_2_opinion_1.sha_public user_2_opinion_1.save() - - + opinion_not_picked = PublishedContentFactory(author_list=[self.user_author], type="OPINION") opinion_not_picked.save() - + user_1_opinion_2 = PublishedContentFactory(author_list=[self.user_author], type="OPINION") user_1_opinion_2.sha_picked = user_1_opinion_2.sha_public user_1_opinion_2.save() - + opinion_not_picked = PublishedContentFactory(author_list=[self.user_author], type="OPINION") opinion_not_picked.save() - - - + PickListOperation.objects.create( content=user_1_opinion_1, operation="PICK", @@ -1927,7 +1924,6 @@ def test_opinion_link_is_not_related_to_the_author(self): operation_date=datetime.datetime.now(), version=user_1_opinion_2.sha_public, ) - result = self.client.get( reverse("opinion:view", kwargs={"pk": user_1_opinion_2.pk, "slug": user_1_opinion_2.slug}) From 26d76bef74783eab087ead8cf98dbd372371d317 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sat, 18 Mar 2023 15:53:54 +0100 Subject: [PATCH 25/78] add explanation, validaion of quizz if there is answer --- assets/js/content-quizz.js | 210 +++++++++++++++++++++++++++------- assets/scss/pages/_stats.scss | 52 +++++++++ 2 files changed, 220 insertions(+), 42 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index deaa07a07b..165ef11a9c 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -5,24 +5,28 @@ let index = 0 function extractAnswer(inputDomElementList, answers) { - inputDomElementList.forEach((rb) => { - const ulWrapperElement = rb.parentElement.parentElement - // we give the ui an id to find the element in a more effective way later when the users answer the questions - if (!ulWrapperElement.getAttribute('id')) { - ulWrapperElement.setAttribute('id', 'id-' + (index++)) - } - rb.setAttribute('name', ulWrapperElement.getAttribute('id')) - const questionBlock = ulWrapperElement.parentElement.parentElement - questionBlock.setAttribute('data-name', rb.getAttribute('name')) - if (!answers[ulWrapperElement.getAttribute('id')]) { - answers[ulWrapperElement.getAttribute('id')] = [rb.checked] - } else { - answers[ulWrapperElement.getAttribute('id')].push(rb.checked) - } - rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) - rb.disabled = false - rb.checked = false - }) + + + if (Object.keys(answers).length === 0) { + inputDomElementList.forEach((rb) => { + const ulWrapperElement = rb.parentElement.parentElement + // we give the ui an id to find the element in a more effective way later when the users answer the questions + if (!ulWrapperElement.getAttribute('id')) { + ulWrapperElement.setAttribute('id', 'id-' + (index++)) + } + rb.setAttribute('name', ulWrapperElement.getAttribute('id')) + const questionBlock = ulWrapperElement.parentElement.parentElement + questionBlock.setAttribute('data-name', rb.getAttribute('name')) + if (!answers[ulWrapperElement.getAttribute('id')]) { + answers[ulWrapperElement.getAttribute('id')] = [rb.checked] + } else { + answers[ulWrapperElement.getAttribute('id')].push(rb.checked) + } + rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) + rb.disabled = false + rb.checked = false + }) + } } /** @@ -64,8 +68,10 @@ function extractAnswer(inputDomElementList, answers) { * @param answers the answer dictionary, it will be modified by the process */ function initializeCheckboxes(answers) { - const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') - extractAnswer(checkboxes, answers) + + const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') + extractAnswer(checkboxes, answers) + sessionStorage.setItem('answers', JSON.stringify(answers)); } @@ -74,12 +80,13 @@ function initializeRadio(answers) { extractAnswer(radio, answers) } -const initializePipeline = [initializeCheckboxes, initializeRadio] +const initializePipeline = [initializeCheckboxes] function computeForm(formdata, answers) { const badAnswers = [] const allAnswerNames = [] for (const entry of formdata.entries()) { + // console.log(entry); const name = entry[0] const values = parseInt(entry[1], 10) allAnswerNames.push(name) @@ -99,38 +106,41 @@ function computeForm(formdata, answers) { } function markBadAnswers(names, answers) { + const toAdd = [] - if (names.length === 0) { - Object.keys(answers).forEach(answer => { - let mustMarkBad = false - answers[answer].forEach((value, index) => { - const inputAnswer = document.querySelector(`#${answer} input[value="${index}"]`) - if (value && !inputAnswer.checked) { - inputAnswer.parentElement.classList.add('quizz-forget') - mustMarkBad = true - } - }) - if (mustMarkBad) { - document.querySelector(`div[data-name="${answer}"]`).classList.add('quizz-bad') - } - }) - } + // if (names.length === 0) { + // Object.keys(answers).forEach(answer => { + // let mustMarkBad = false + // answers[answer].forEach((value, index) => { + // const inputAnswer = document.querySelector(`#${answer} input[value="${index}"]`) + // if (value && !inputAnswer.checked) { + // inputAnswer.parentElement.classList.add('quizz-forget') + // mustMarkBad = true + // } + // }) + // if (mustMarkBad) { + // document.querySelector(`div[data-name="${answer}"]`).classList.add('quizz-bad') + // } + // }) + // } names.forEach(({ name }) => { document.querySelectorAll('input[name="' + name + '"]').forEach(field => { if (answers[name][parseInt(field.getAttribute('value'), 10)] && !field.checked) { - field.parentElement.classList.add('quizz-forget') + toAdd.push({ name: name, value: field.getAttribute('value') }) } }) + document.querySelector(`.custom-block[data-name=${name}]`).classList.add('quizz-bad') }) names.forEach(({ name, value }) => { + document.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`) .parentElement.classList.add('quizz-bad') }) @@ -163,12 +173,34 @@ function getWantedHeading(questionNode, nodeName, attr) { * @param answers answers dictionary */ function injectForms(quizz, answers) { + + + const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); + quizzDivs.forEach(quizzDiv => { + const ul = quizzDiv.querySelector('ul') + const lastLi = ul.lastElementChild + const explanationText = lastLi.innerText + + const explanation = document.createElement('p') + explanation.classList.add('explanation_off') + explanation.innerText = explanationText + lastLi.parentNode.removeChild(lastLi); + quizzDiv.appendChild(explanation) + + + }); + + + + const searchedTitle = quizz.getAttribute('data-heading-level') || 'h3' const submitLabel = quizz.getAttribute('data-quizz-validate') || 'Validate' const headings = {} - let idBias = 0 const wrappers = [] + Object.keys(answers).forEach(blockId => { + + const blockNode = document.getElementById(blockId) if (!blockNode) { // if the node was treated and therefore the clone has not been reinserted yet @@ -177,11 +209,14 @@ function injectForms(quizz, answers) { // this is the custom-block-quizz node const questionNode = blockNode.parentElement.parentElement const heading = getWantedHeading(questionNode, searchedTitle, 'previousSibling') || quizz + console.log(heading); if (!heading.getAttribute('id')) { heading.setAttribute('id', `quizz-form-${idBias}`) idBias++ } if (heading && !headings[heading.getAttribute('id')]) { + + // this is just for convenience, this add a "known" element that will always be there const wrapper = document.createElement('div') @@ -189,11 +224,15 @@ function injectForms(quizz, answers) { const form = document.createElement('form') form.classList.add('quizz') + const submit = document.createElement('button') submit.innerText = submitLabel + submit.classList.add('btn', 'btn-submit') const result = document.createElement('p') result.classList.add('result') + + let nodeToAddToForm if (heading === quizz) { nodeToAddToForm = quizz.firstChild @@ -202,6 +241,8 @@ function injectForms(quizz, answers) { } form.method = 'POST' form.setAttribute('action', quizz.getAttribute('data-answer-url')) + form.setAttribute('id', `my-form-${idCounter}`); + idCounter++; // gather all the questions of this current subsection, until the next h3 heading while (nodeToAddToForm && nodeToAddToForm.nodeName !== searchedTitle.toUpperCase()) { const current = nodeToAddToForm @@ -210,6 +251,7 @@ function injectForms(quizz, answers) { current.parentNode.removeChild(current) } form.appendChild(result) + // form.appendChild(explanation) form.appendChild(submit) wrappers.push(wrapper) if (nodeToAddToForm && nodeToAddToForm.nodeName === searchedTitle.toUpperCase()) { @@ -231,12 +273,30 @@ function injectForms(quizz, answers) { wrappers.forEach((wrapper) => quizz.appendChild(wrapper)) } + + +// let storedAnswers = sessionStorage.getItem('answers'); + +// const answers = storedAnswers ? JSON.parse(storedAnswers) : {}; + const answers = {} + +console.log(answers); + initializePipeline.forEach(func => func(answers)) + +let idCounter = 0 +let idBias = 0 + document.querySelectorAll('div.quizz').forEach(div => { + + injectForms(div, answers) + }) + + function sendQuizzStatistics(form, statistics) { const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value const xhttp = new XMLHttpRequest() @@ -245,25 +305,83 @@ function sendQuizzStatistics(form, statistics) { xhttp.setRequestHeader('Content-Type', 'application/json') xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href + // console.log(JSON.stringify(statistics)); xhttp.send(JSON.stringify(statistics)) + } function displayResultAfterSubmitButton(nbGood, nbTotal, form) { + + + const explanationElement = form.querySelector('.explanation_off') ? form.querySelector('.explanation_off') : form.querySelector('.explanation_on'); + const resultElement = form.querySelector('.result'); + + if (nbTotal === 0) { + explanationElement.innerText = `please answer first`; + // explanationElement.classList.add('error'); + resultElement.innerText = ''; + return; + } + const percentOfAnswers = (100 * 1.0 * nbGood / nbTotal).toLocaleString('fr-FR', { minimumIntegerDigits: 1, useGrouping: false - }) - form.querySelector('.result').innerText = `Vous avez bien répondu à ${percentOfAnswers}% des questions.` + }); + + if (percentOfAnswers == 100) { + resultElement.innerText = `La reponse est correcte :)`; + explanationElement.classList.remove('explanation_on'); + explanationElement.classList.add('explanation_off'); + + } else { + resultElement.innerText = ''; + explanationElement.classList.remove('explanation_off'); + explanationElement.classList.add('explanation_on'); + } +} + +function validateQuizz(form) { + + const questions = form.querySelectorAll('.custom-block-quizz'); + for (let question of questions) { + const checkboxes = question.querySelectorAll('input[type="checkbox"]'); + let isQuestionValid = false; + for (let checkbox of checkboxes) { + if (checkbox.checked) { + isQuestionValid = true; + break; + } + } + if (!isQuestionValid) { + alert(1); + break; + } + } } document.querySelectorAll('form.quizz').forEach(form => { - // compute the answer and send stats + + const submitBtn = form.querySelector('.btn-submit'); + submitBtn.onclick = () => validateQuizz(form); + form.addEventListener('submit', e => { + e.preventDefault() e.stopPropagation() const formData = new FormData(form) // result = name of bad answers const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) + + + const length = form.children.length + + const current_quizz = form.firstElementChild.getAttribute('data-name') + + const elementt = document.querySelector(`.custom-block[data-name="${current_quizz}"]`) + elementt.classList.remove('quizz-bad') + elementt.classList.remove('quizz-good') + elementt.classList.remove('hasAnswer') + markBadAnswers(badAnswerNames, answers) allAnswerNames.forEach(name => { const ulWrapper = document.getElementById(name) @@ -307,6 +425,7 @@ document.querySelectorAll('form.quizz').forEach(form => { // now determine answers and their labels element.querySelectorAll('input:checked') .forEach(node => { + // remove eventual glued corretion let label = node.parentElement.textContent if (correction && label.indexOf(correction.textContent) !== -1) { @@ -317,15 +436,22 @@ document.querySelectorAll('form.quizz').forEach(form => { if (element.classList.contains('hasAnswer')) { nbTotal++ + if (!element.classList.contains('quizz-bad') && !element.classList.contains('quizz-forget')) { element.classList.add('quizz-good') statistics.result[title].evaluation = 'ok' nbGood++ + } } }) - sendQuizzStatistics(form, statistics) + + if (nbTotal!=0) { + sendQuizzStatistics(form, statistics) + } displayResultAfterSubmitButton(nbGood, nbTotal, form) }) }) + + diff --git a/assets/scss/pages/_stats.scss b/assets/scss/pages/_stats.scss index f1afba9663..19c25564b4 100644 --- a/assets/scss/pages/_stats.scss +++ b/assets/scss/pages/_stats.scss @@ -64,3 +64,55 @@ } } } + +.error { + color: red; +} + + +.result { + margin-top: 1rem; + padding: 1rem; + + border: 1px solid #f5c6cb; /* dark red */ + color: #06b300; /* dark text */ + border-radius: 0.25rem; + font-size: 1.2rem; + line-height: 1.5; + &.active { + background-color: #02ca44; /* light red */ + } + &:empty { + display: none; + } +} + +.explanation_off { + display: none; +} + + +.explanation_on { + + margin-top: 1rem; + padding: 1rem; + + border: 1px solid #f5c6cb; /* dark red */ + color: #fc3605; /* dark text */ + border-radius: 0.25rem; + font-size: 1.2rem; + line-height: 1.5; + &.active { + background-color: #02ca44; /* light red */ + } + &:empty { + display: none; + } +} + +.explanation{ + display: none; +} + + + \ No newline at end of file From 6aa48a07da6555a00b114fa4ade4504e1f2ed223 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 19 Mar 2023 13:22:03 +0100 Subject: [PATCH 26/78] quik fix --- assets/js/content-quizz.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 165ef11a9c..1debce328a 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -6,8 +6,8 @@ let index = 0 function extractAnswer(inputDomElementList, answers) { - - if (Object.keys(answers).length === 0) { + + // if (Object.keys(answers).length === 0) { inputDomElementList.forEach((rb) => { const ulWrapperElement = rb.parentElement.parentElement // we give the ui an id to find the element in a more effective way later when the users answer the questions @@ -26,7 +26,7 @@ function extractAnswer(inputDomElementList, answers) { rb.disabled = false rb.checked = false }) - } + // } } /** @@ -71,7 +71,7 @@ function initializeCheckboxes(answers) { const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') extractAnswer(checkboxes, answers) - sessionStorage.setItem('answers', JSON.stringify(answers)); + // sessionStorage.setItem('answers', JSON.stringify(answers)); } @@ -190,16 +190,13 @@ function injectForms(quizz, answers) { }); - - - const searchedTitle = quizz.getAttribute('data-heading-level') || 'h3' const submitLabel = quizz.getAttribute('data-quizz-validate') || 'Validate' const headings = {} const wrappers = [] Object.keys(answers).forEach(blockId => { - + const blockNode = document.getElementById(blockId) if (!blockNode) { @@ -209,14 +206,17 @@ function injectForms(quizz, answers) { // this is the custom-block-quizz node const questionNode = blockNode.parentElement.parentElement const heading = getWantedHeading(questionNode, searchedTitle, 'previousSibling') || quizz - console.log(heading); + if (!heading.getAttribute('id')) { heading.setAttribute('id', `quizz-form-${idBias}`) idBias++ } + + if (heading && !headings[heading.getAttribute('id')]) { + // console.log(1); // this is just for convenience, this add a "known" element that will always be there const wrapper = document.createElement('div') @@ -305,7 +305,6 @@ function sendQuizzStatistics(form, statistics) { xhttp.setRequestHeader('Content-Type', 'application/json') xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href - // console.log(JSON.stringify(statistics)); xhttp.send(JSON.stringify(statistics)) } From 0052db69d0c6c08dbbcc5baa94b20114867bda0f Mon Sep 17 00:00:00 2001 From: Alexandre Bertin Date: Sun, 19 Mar 2023 18:51:57 +0100 Subject: [PATCH 27/78] reglage de bug d'affichage apres le submit de la reponse --- assets/js/content-quizz.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 1debce328a..7cdd68e8b1 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -316,9 +316,9 @@ function displayResultAfterSubmitButton(nbGood, nbTotal, form) { const resultElement = form.querySelector('.result'); if (nbTotal === 0) { - explanationElement.innerText = `please answer first`; + explanationElement.innerText = ``; // explanationElement.classList.add('error'); - resultElement.innerText = ''; + resultElement.innerText = 'please answer first'; return; } @@ -330,11 +330,13 @@ function displayResultAfterSubmitButton(nbGood, nbTotal, form) { if (percentOfAnswers == 100) { resultElement.innerText = `La reponse est correcte :)`; explanationElement.classList.remove('explanation_on'); + explanationElement.classList.remove('please answer first'); explanationElement.classList.add('explanation_off'); } else { - resultElement.innerText = ''; + resultElement.innerText = 'Il existe au moins une réponse fausse'; explanationElement.classList.remove('explanation_off'); + explanationElement.classList.remove('please answer first'); explanationElement.classList.add('explanation_on'); } } From 9093ffa1935591f4da312c51c298eab451de4ff2 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Thu, 23 Mar 2023 18:27:43 +0100 Subject: [PATCH 28/78] fixed explanation and result display --- assets/js/content-quizz.js | 325 +++++++++++++++++++--------------- assets/scss/pages/_stats.scss | 29 ++- 2 files changed, 203 insertions(+), 151 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 1debce328a..0cac4cab38 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -5,16 +5,18 @@ let index = 0 function extractAnswer(inputDomElementList, answers) { - - - // if (Object.keys(answers).length === 0) { + + inputDomElementList.forEach((rb) => { + const ulWrapperElement = rb.parentElement.parentElement // we give the ui an id to find the element in a more effective way later when the users answer the questions if (!ulWrapperElement.getAttribute('id')) { ulWrapperElement.setAttribute('id', 'id-' + (index++)) } + rb.setAttribute('name', ulWrapperElement.getAttribute('id')) + const questionBlock = ulWrapperElement.parentElement.parentElement questionBlock.setAttribute('data-name', rb.getAttribute('name')) if (!answers[ulWrapperElement.getAttribute('id')]) { @@ -25,8 +27,8 @@ function extractAnswer(inputDomElementList, answers) { rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) rb.disabled = false rb.checked = false + }) - // } } /** @@ -69,9 +71,33 @@ function extractAnswer(inputDomElementList, answers) { */ function initializeCheckboxes(answers) { + + + document.querySelectorAll('div.quizz').forEach(quizz => { + + + const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); + quizzDivs.forEach(quizzDiv => { + const ul = quizzDiv.querySelector('ul') + const lastLi = ul.lastElementChild + const explanationText = lastLi.innerText + + const explanation = document.createElement('p') + explanation.classList.add('explanation_off') + explanation.innerText = explanationText + lastLi.parentNode.removeChild(lastLi); + quizzDiv.appendChild(explanation) + + + }); + + + }) + const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') + extractAnswer(checkboxes, answers) - // sessionStorage.setItem('answers', JSON.stringify(answers)); + } @@ -86,7 +112,6 @@ function computeForm(formdata, answers) { const badAnswers = [] const allAnswerNames = [] for (const entry of formdata.entries()) { - // console.log(entry); const name = entry[0] const values = parseInt(entry[1], 10) allAnswerNames.push(name) @@ -105,26 +130,42 @@ function computeForm(formdata, answers) { return [badAnswers, allAnswerNames] } -function markBadAnswers(names, answers) { +function markBadAnswers(form,names, answers) { const toAdd = [] - // if (names.length === 0) { - // Object.keys(answers).forEach(answer => { - // let mustMarkBad = false - // answers[answer].forEach((value, index) => { - // const inputAnswer = document.querySelector(`#${answer} input[value="${index}"]`) - // if (value && !inputAnswer.checked) { - // inputAnswer.parentElement.classList.add('quizz-forget') - // mustMarkBad = true - // } - // }) - // if (mustMarkBad) { - // document.querySelector(`div[data-name="${answer}"]`).classList.add('quizz-bad') - // } - // }) - // } + + Object.keys(answers).forEach(answer => { + + const inputs = form.querySelectorAll(`#${answer} input`); + + let numChecked = 0; + inputs.forEach(input => { + if (input.hasAttribute('checked')) { + numChecked++; + } + }); + + // question with more than one correct answer + if(numChecked > 1){ + + let AnsweredWell = true + answers[answer].forEach((value, index) => { + const inputAnswer = document.querySelector(`#${answer} input[value="${index}"]`) + if (value && !inputAnswer.checked) { + AnsweredWell = false + } + }) + if (!AnsweredWell) { + form.querySelector(`div[data-name="${answer}"]`).classList.add('quizz-multiple') + } + } + }) + names.forEach(({ name }) => { - document.querySelectorAll('input[name="' + name + '"]').forEach(field => { + + form.querySelectorAll('input[name="' + name + '"]').forEach(field => { + + if (answers[name][parseInt(field.getAttribute('value'), 10)] && !field.checked) { toAdd.push({ @@ -134,16 +175,17 @@ function markBadAnswers(names, answers) { } }) - document.querySelector(`.custom-block[data-name=${name}]`).classList.add('quizz-bad') + form.querySelector(`.custom-block[data-name=${name}]`).classList.add('quizz-bad') }) + names.forEach(({ name, value }) => { - document.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`) - .parentElement.classList.add('quizz-bad') + form.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`).parentElement.classList.add('quizz-bad') }) + toAdd.forEach(name => names.push(name)) } @@ -174,21 +216,6 @@ function getWantedHeading(questionNode, nodeName, attr) { */ function injectForms(quizz, answers) { - - const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); - quizzDivs.forEach(quizzDiv => { - const ul = quizzDiv.querySelector('ul') - const lastLi = ul.lastElementChild - const explanationText = lastLi.innerText - - const explanation = document.createElement('p') - explanation.classList.add('explanation_off') - explanation.innerText = explanationText - lastLi.parentNode.removeChild(lastLi); - quizzDiv.appendChild(explanation) - - - }); const searchedTitle = quizz.getAttribute('data-heading-level') || 'h3' const submitLabel = quizz.getAttribute('data-quizz-validate') || 'Validate' @@ -199,6 +226,7 @@ function injectForms(quizz, answers) { const blockNode = document.getElementById(blockId) + if (!blockNode) { // if the node was treated and therefore the clone has not been reinserted yet return @@ -216,7 +244,6 @@ function injectForms(quizz, answers) { if (heading && !headings[heading.getAttribute('id')]) { - // console.log(1); // this is just for convenience, this add a "known" element that will always be there const wrapper = document.createElement('div') @@ -229,10 +256,16 @@ function injectForms(quizz, answers) { submit.innerText = submitLabel submit.classList.add('btn', 'btn-submit') + submit.setAttribute('id', `my-button-${idBias}`); const result = document.createElement('p') result.classList.add('result') + const notAnswered = document.createElement('p') + notAnswered.classList.add('notAnswered') + + + let nodeToAddToForm if (heading === quizz) { nodeToAddToForm = quizz.firstChild @@ -251,8 +284,8 @@ function injectForms(quizz, answers) { current.parentNode.removeChild(current) } form.appendChild(result) - // form.appendChild(explanation) form.appendChild(submit) + form.appendChild(notAnswered) wrappers.push(wrapper) if (nodeToAddToForm && nodeToAddToForm.nodeName === searchedTitle.toUpperCase()) { wrapper.append(form) @@ -274,14 +307,8 @@ function injectForms(quizz, answers) { } +let answers = {} -// let storedAnswers = sessionStorage.getItem('answers'); - -// const answers = storedAnswers ? JSON.parse(storedAnswers) : {}; - -const answers = {} - -console.log(answers); initializePipeline.forEach(func => func(answers)) @@ -289,14 +316,11 @@ let idCounter = 0 let idBias = 0 document.querySelectorAll('div.quizz').forEach(div => { - injectForms(div, answers) }) - - function sendQuizzStatistics(form, statistics) { const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value const xhttp = new XMLHttpRequest() @@ -311,35 +335,26 @@ function sendQuizzStatistics(form, statistics) { function displayResultAfterSubmitButton(nbGood, nbTotal, form) { - - const explanationElement = form.querySelector('.explanation_off') ? form.querySelector('.explanation_off') : form.querySelector('.explanation_on'); const resultElement = form.querySelector('.result'); - if (nbTotal === 0) { - explanationElement.innerText = `please answer first`; - // explanationElement.classList.add('error'); - resultElement.innerText = ''; - return; - } + const questions = form.querySelectorAll('.custom-block-quizz'); - const percentOfAnswers = (100 * 1.0 * nbGood / nbTotal).toLocaleString('fr-FR', { - minimumIntegerDigits: 1, - useGrouping: false - }); - if (percentOfAnswers == 100) { - resultElement.innerText = `La reponse est correcte :)`; - explanationElement.classList.remove('explanation_on'); - explanationElement.classList.add('explanation_off'); + for (let question of questions) { + + const explanationElement = question.querySelector('.explanation_off') + if(explanationElement!==null){ + explanationElement.classList.remove('explanation_off'); + explanationElement.classList.add('explanation_on'); + } - } else { - resultElement.innerText = ''; - explanationElement.classList.remove('explanation_off'); - explanationElement.classList.add('explanation_on'); } + + resultElement.innerText = `Vous avez répondu correctement à ${nbGood}/${nbTotal} des questions`; } -function validateQuizz(form) { +// test if the quizz is totally answered or not +function QuizzAnswered(form) { const questions = form.querySelectorAll('.custom-block-quizz'); for (let question of questions) { @@ -352,105 +367,125 @@ function validateQuizz(form) { } } if (!isQuestionValid) { - alert(1); - break; + return false } } + return true } document.querySelectorAll('form.quizz').forEach(form => { const submitBtn = form.querySelector('.btn-submit'); - submitBtn.onclick = () => validateQuizz(form); - + form.addEventListener('submit', e => { e.preventDefault() e.stopPropagation() - const formData = new FormData(form) - // result = name of bad answers - const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) - - const length = form.children.length + const notAnswered = form.querySelector('.notAnswered'); - const current_quizz = form.firstElementChild.getAttribute('data-name') + if (QuizzAnswered(form)) { - const elementt = document.querySelector(`.custom-block[data-name="${current_quizz}"]`) - elementt.classList.remove('quizz-bad') - elementt.classList.remove('quizz-good') - elementt.classList.remove('hasAnswer') - - markBadAnswers(badAnswerNames, answers) - allAnswerNames.forEach(name => { - const ulWrapper = document.getElementById(name) - const quizzCustomBlock = ulWrapper.parentElement.parentElement - quizzCustomBlock.classList.add('hasAnswer') - }) - const questions = [] - badAnswerNames.forEach(result => { - if (questions.indexOf(result.name) === -1) { - questions.push(result.name) - } - }) - const statistics = { - expected: {}, - result: {} - } - let nbGood = 0 - let nbTotal = 0 - Object.keys(answers).forEach(name => { - const element = document.querySelector(`.custom-block[data-name="${name}"]`) - let title = element.querySelector('.custom-block-heading').textContent - const correction = element.querySelector('.custom-block-body .custom-block') - if (correction && title.indexOf(correction.textContent) > 0) { - title = title.substr(0, title.indexOf(correction.textContent)) - } - statistics.result[title] = { - evaluation: 'bad', - labels: [] - } - statistics.expected[title] = {} - const availableResponses = element.querySelectorAll('input') - for (let i = 0; i < availableResponses.length; i++) { - // wee need to get the question label for statistics - const liWrapper = availableResponses[i].parentElement - let questionLabel = liWrapper.textContent - if (correction && questionLabel.indexOf(correction.textContent) !== -1) { - questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) + const formData = new FormData(form) + // result = name of bad answers + const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) + + // Select all the custom-block-quizz divs + const quizzDivs = form.querySelectorAll('.custom-block-quizz'); + + quizzDivs.forEach(div => { + + div.classList.remove('quizz-bad'); + div.classList.remove('quizz-good'); + div.classList.remove('hasAnswer'); + div.classList.remove('quizz-multiple'); + }); + + + markBadAnswers(form,badAnswerNames, answers) + + allAnswerNames.forEach(name => { + const ulWrapper = document.getElementById(name) + const quizzCustomBlock = ulWrapper.parentElement.parentElement + quizzCustomBlock.classList.add('hasAnswer') + }) + const questions = [] + badAnswerNames.forEach(result => { + if (questions.indexOf(result.name) === -1) { + questions.push(result.name) } - statistics.expected[title][questionLabel] = answers[name][i] + }) + const statistics = { + expected: {}, + result: {} } - // now determine answers and their labels - element.querySelectorAll('input:checked') - .forEach(node => { - - // remove eventual glued corretion + let nbGood = 0 + let nbTotal = 0 + Object.keys(answers).forEach(name => { + const element = document.querySelector(`.custom-block[data-name="${name}"]`) + let title = element.querySelector('.custom-block-heading').textContent + const correction = element.querySelector('.custom-block-body .custom-block') + if (correction && title.indexOf(correction.textContent) > 0) { + title = title.substr(0, title.indexOf(correction.textContent)) + } + statistics.result[title] = { + evaluation: 'bad', + labels: [] + } + statistics.expected[title] = {} + const availableResponses = element.querySelectorAll('input') + for (let i = 0; i < availableResponses.length; i++) { + // wee need to get the question label for statistics + const liWrapper = availableResponses[i].parentElement + let questionLabel = liWrapper.textContent + if (correction && questionLabel.indexOf(correction.textContent) !== -1) { + questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) + } + statistics.expected[title][questionLabel] = answers[name][i] + } + // now determine answers and their labels + element.querySelectorAll('input:checked').forEach(node => { + + // remove eventual glued corretion let label = node.parentElement.textContent if (correction && label.indexOf(correction.textContent) !== -1) { label = label.substr(0, label.indexOf(correction.textContent)) } statistics.result[title].labels.push(label.trim()) }) - if (element.classList.contains('hasAnswer')) { - nbTotal++ + if (element.classList.contains('hasAnswer')) { + nbTotal++ + + if (!element.classList.contains('quizz-bad') && !element.classList.contains('quizz-multiple')) { + + element.classList.add('quizz-good') + statistics.result[title].evaluation = 'ok' + nbGood++ - if (!element.classList.contains('quizz-bad') && - !element.classList.contains('quizz-forget')) { - element.classList.add('quizz-good') - statistics.result[title].evaluation = 'ok' - nbGood++ - + } + } - } - }) - if (nbTotal!=0) { + }) + + sendQuizzStatistics(form, statistics) - } - displayResultAfterSubmitButton(nbGood, nbTotal, form) + // submitBtn.setAttribute('disabled', true); + displayResultAfterSubmitButton(nbGood, nbTotal, form) + notAnswered.innerText = '' + + + // not all questions answered + }else { + + notAnswered.innerText = 'Veuillez répondre à toutes les questions' + + } + }) }) + + diff --git a/assets/scss/pages/_stats.scss b/assets/scss/pages/_stats.scss index 19c25564b4..142a0a0f59 100644 --- a/assets/scss/pages/_stats.scss +++ b/assets/scss/pages/_stats.scss @@ -8,6 +8,10 @@ } } +.quizz-multiple.custom-block-body{ + color: #fc3605; +} + /* Style the tab */ .tab { border: $length-1 solid $grey-100; @@ -65,9 +69,6 @@ } } -.error { - color: red; -} .result { @@ -98,7 +99,7 @@ padding: 1rem; border: 1px solid #f5c6cb; /* dark red */ - color: #fc3605; /* dark text */ + color: #0a0706; /* dark text */ border-radius: 0.25rem; font-size: 1.2rem; line-height: 1.5; @@ -110,9 +111,25 @@ } } -.explanation{ - display: none; +.notAnswered{ + margin-top: 1rem; + padding: 1rem; + margin-right: 17rem; + + border: 1px solid #f5c6cb; /* dark red */ + color: #f72f0c; /* dark text */ + border-radius: 0.25rem; + font-size: 1.2rem; + line-height: 1.5; + &.active { + background-color: #02ca44; /* light red */ + } + &:empty { + display: none; + } } + + \ No newline at end of file From 3ce3f5d3da09d94f0c35841c2e3e721baed159b2 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 27 Mar 2023 14:43:26 +0200 Subject: [PATCH 29/78] display corrects answers with green --- assets/js/content-quizz.js | 50 ++++++++++++++++------------------ assets/scss/base/_content.scss | 12 ++++---- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 0cac4cab38..174d1d234c 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -141,6 +141,7 @@ function markBadAnswers(form,names, answers) { let numChecked = 0; inputs.forEach(input => { if (input.hasAttribute('checked')) { + input.parentElement.classList.add('answer-good') numChecked++; } }); @@ -156,7 +157,7 @@ function markBadAnswers(form,names, answers) { } }) if (!AnsweredWell) { - form.querySelector(`div[data-name="${answer}"]`).classList.add('quizz-multiple') + form.querySelector(`div[data-name="${answer}"]`).classList.add('quizz-bad') } } }) @@ -183,7 +184,7 @@ function markBadAnswers(form,names, answers) { value }) => { - form.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`).parentElement.classList.add('quizz-bad') + form.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`).parentElement.classList.add('answer-bad') }) toAdd.forEach(name => names.push(name)) @@ -336,7 +337,6 @@ function sendQuizzStatistics(form, statistics) { function displayResultAfterSubmitButton(nbGood, nbTotal, form) { const resultElement = form.querySelector('.result'); - const questions = form.querySelectorAll('.custom-block-quizz'); @@ -373,35 +373,36 @@ function QuizzAnswered(form) { return true } -document.querySelectorAll('form.quizz').forEach(form => { - const submitBtn = form.querySelector('.btn-submit'); +document.querySelectorAll('form.quizz').forEach(form => { form.addEventListener('submit', e => { + // Select all the custom-block-quizz divs + const quizzDivs = form.querySelectorAll('.custom-block-quizz'); + + quizzDivs.forEach(div => { + + div.classList.remove('quizz-bad'); + div.classList.remove('quizz-good'); + div.classList.remove('hasAnswer'); + div.classList.remove('quizz-multiple'); + }); + e.preventDefault() e.stopPropagation() const notAnswered = form.querySelector('.notAnswered'); + const submitBtn = form.querySelector('.btn-submit'); if (QuizzAnswered(form)) { + + let nbTotal = 0; + let nbGood = 0; const formData = new FormData(form) - // result = name of bad answers const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) - // Select all the custom-block-quizz divs - const quizzDivs = form.querySelectorAll('.custom-block-quizz'); - - quizzDivs.forEach(div => { - - div.classList.remove('quizz-bad'); - div.classList.remove('quizz-good'); - div.classList.remove('hasAnswer'); - div.classList.remove('quizz-multiple'); - }); - - markBadAnswers(form,badAnswerNames, answers) allAnswerNames.forEach(name => { @@ -419,9 +420,9 @@ document.querySelectorAll('form.quizz').forEach(form => { expected: {}, result: {} } - let nbGood = 0 - let nbTotal = 0 + Object.keys(answers).forEach(name => { + const element = document.querySelector(`.custom-block[data-name="${name}"]`) let title = element.querySelector('.custom-block-heading').textContent const correction = element.querySelector('.custom-block-body .custom-block') @@ -446,18 +447,18 @@ document.querySelectorAll('form.quizz').forEach(form => { // now determine answers and their labels element.querySelectorAll('input:checked').forEach(node => { - // remove eventual glued corretion + // remove eventual glued corretion let label = node.parentElement.textContent if (correction && label.indexOf(correction.textContent) !== -1) { label = label.substr(0, label.indexOf(correction.textContent)) } statistics.result[title].labels.push(label.trim()) }) - + console.log(element); if (element.classList.contains('hasAnswer')) { nbTotal++ - if (!element.classList.contains('quizz-bad') && !element.classList.contains('quizz-multiple')) { + if (!element.classList.contains('quizz-bad')) { element.classList.add('quizz-good') statistics.result[title].evaluation = 'ok' @@ -468,14 +469,11 @@ document.querySelectorAll('form.quizz').forEach(form => { } }) - sendQuizzStatistics(form, statistics) // submitBtn.setAttribute('disabled', true); displayResultAfterSubmitButton(nbGood, nbTotal, form) notAnswered.innerText = '' - - // not all questions answered }else { diff --git a/assets/scss/base/_content.scss b/assets/scss/base/_content.scss index 846ec51455..fc16de0594 100644 --- a/assets/scss/base/_content.scss +++ b/assets/scss/base/_content.scss @@ -659,15 +659,13 @@ li.task-list-item { background-color: $true-white !important; } - &.quizz-forget { - font-weight: bolder; - } - &.quizz-forget { - font-weight: bolder; - } - &.quizz-bad { + &.answer-bad { color: $color-danger; } + &.answer-good { + color:green; + } + } From 4d0e68df5ce98f36d32a7180dd4ebffb9cf03073 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 27 Mar 2023 15:07:04 +0200 Subject: [PATCH 30/78] no result display any more, result is clear by colors --- assets/js/content-quizz.js | 12 ++++++------ assets/scss/pages/_stats.scss | 18 ------------------ 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 174d1d234c..e4c46306ea 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -258,8 +258,6 @@ function injectForms(quizz, answers) { submit.classList.add('btn', 'btn-submit') submit.setAttribute('id', `my-button-${idBias}`); - const result = document.createElement('p') - result.classList.add('result') const notAnswered = document.createElement('p') @@ -284,7 +282,6 @@ function injectForms(quizz, answers) { form.appendChild(current.cloneNode(true)) current.parentNode.removeChild(current) } - form.appendChild(result) form.appendChild(submit) form.appendChild(notAnswered) wrappers.push(wrapper) @@ -334,7 +331,7 @@ function sendQuizzStatistics(form, statistics) { } -function displayResultAfterSubmitButton(nbGood, nbTotal, form) { +function displayResultAfterSubmitButton(form) { const resultElement = form.querySelector('.result'); const questions = form.querySelectorAll('.custom-block-quizz'); @@ -350,7 +347,6 @@ function displayResultAfterSubmitButton(nbGood, nbTotal, form) { } - resultElement.innerText = `Vous avez répondu correctement à ${nbGood}/${nbTotal} des questions`; } // test if the quizz is totally answered or not @@ -374,10 +370,12 @@ function QuizzAnswered(form) { } + document.querySelectorAll('form.quizz').forEach(form => { form.addEventListener('submit', e => { + // Select all the custom-block-quizz divs const quizzDivs = form.querySelectorAll('.custom-block-quizz'); @@ -472,8 +470,10 @@ document.querySelectorAll('form.quizz').forEach(form => { sendQuizzStatistics(form, statistics) // submitBtn.setAttribute('disabled', true); - displayResultAfterSubmitButton(nbGood, nbTotal, form) + displayResultAfterSubmitButton(form) notAnswered.innerText = '' + nbGood = 0; + nbTotal = 0; // not all questions answered }else { diff --git a/assets/scss/pages/_stats.scss b/assets/scss/pages/_stats.scss index 142a0a0f59..c02d8fd3eb 100644 --- a/assets/scss/pages/_stats.scss +++ b/assets/scss/pages/_stats.scss @@ -70,24 +70,6 @@ } - -.result { - margin-top: 1rem; - padding: 1rem; - - border: 1px solid #f5c6cb; /* dark red */ - color: #06b300; /* dark text */ - border-radius: 0.25rem; - font-size: 1.2rem; - line-height: 1.5; - &.active { - background-color: #02ca44; /* light red */ - } - &:empty { - display: none; - } -} - .explanation_off { display: none; } From 266f4929d565ef60c5a1699a4767e6f9ec3d304f Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 3 Apr 2023 14:20:37 +0200 Subject: [PATCH 31/78] =?UTF-8?q?=20problem=20des=20statistiques=20doubl?= =?UTF-8?q?=C3=A9es=20r=C3=A9solu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/content-quizz.js | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index e4c46306ea..ffeec70f4c 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -6,7 +6,6 @@ let index = 0 function extractAnswer(inputDomElementList, answers) { - inputDomElementList.forEach((rb) => { const ulWrapperElement = rb.parentElement.parentElement @@ -73,8 +72,8 @@ function initializeCheckboxes(answers) { + // add explanation to all questions document.querySelectorAll('div.quizz').forEach(quizz => { - const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); quizzDivs.forEach(quizzDiv => { @@ -307,7 +306,6 @@ function injectForms(quizz, answers) { let answers = {} - initializePipeline.forEach(func => func(answers)) let idCounter = 0 @@ -320,6 +318,13 @@ document.querySelectorAll('div.quizz').forEach(div => { }) function sendQuizzStatistics(form, statistics) { + + const Result = statistics.result; + const questions = [...form.querySelectorAll('.custom-block-heading')].map(question => question.innerText); + Object.keys(Result).forEach(key => { + if (!questions.includes(key)) Result[key].labels = []; + }); + const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value const xhttp = new XMLHttpRequest() xhttp.open('POST', form.getAttribute('action')) @@ -333,7 +338,6 @@ function sendQuizzStatistics(form, statistics) { function displayResultAfterSubmitButton(form) { - const resultElement = form.querySelector('.result'); const questions = form.querySelectorAll('.custom-block-quizz'); @@ -369,8 +373,6 @@ function QuizzAnswered(form) { return true } - - document.querySelectorAll('form.quizz').forEach(form => { form.addEventListener('submit', e => { @@ -395,8 +397,6 @@ document.querySelectorAll('form.quizz').forEach(form => { if (QuizzAnswered(form)) { - let nbTotal = 0; - let nbGood = 0; const formData = new FormData(form) const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) @@ -452,28 +452,19 @@ document.querySelectorAll('form.quizz').forEach(form => { } statistics.result[title].labels.push(label.trim()) }) - console.log(element); - if (element.classList.contains('hasAnswer')) { - nbTotal++ - - if (!element.classList.contains('quizz-bad')) { - - element.classList.add('quizz-good') - statistics.result[title].evaluation = 'ok' - nbGood++ + if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { - } - - } + element.classList.add('quizz-good') + statistics.result[title].evaluation = 'ok' + } + }) sendQuizzStatistics(form, statistics) // submitBtn.setAttribute('disabled', true); displayResultAfterSubmitButton(form) notAnswered.innerText = '' - nbGood = 0; - nbTotal = 0; // not all questions answered }else { From c9793a58c99d47b247be59d5d2af659da0afd13a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?FACEN=20th=C3=A9o?= Date: Mon, 3 Apr 2023 14:20:55 +0200 Subject: [PATCH 32/78] fix le titre des quizzs dans la section statistique --- templates/misc/quizz.graph.part.html | 4 ++-- zds/tutorialv2/views/statistics.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index b52e514688..a382902a8b 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -1,9 +1,9 @@ {% load i18n %}
    - {% for url, questions in quizz.items %} + {% for quizz_name, questions in quizz.items %}
    -

    {{ url }}

    +

    {{ quizz_name }}

    {% for question, stats in questions.items %}
    diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index 2af315603d..5b08d086f2 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -344,7 +344,7 @@ def build_quizz_stats(self, end_date, start_date): ) for base_question in set(base_questions): full_answers_total = {} - url = "" + name = "" question = "" for available_answer in ( QuizzAvailableAnswer.objects.filter(related_question__pk=base_question) @@ -352,7 +352,7 @@ def build_quizz_stats(self, end_date, start_date): .all() ): full_answers_total[available_answer.label] = {"good": available_answer.is_good, "nb": 0} - url = available_answer.related_question.url + name = available_answer.related_question.url.split("#")[-1] question = available_answer.related_question.question for r in total_per_label: if ( @@ -360,7 +360,7 @@ def build_quizz_stats(self, end_date, start_date): and r["answer"].strip() == available_answer.label.strip() ): full_answers_total[available_answer.label]["nb"] = r["nb"] - if url not in quizz_stats: - quizz_stats[url] = OrderedDict() - quizz_stats[url][question] = {"total": total_per_question[base_question], "responses": full_answers_total} + if name not in quizz_stats: + quizz_stats[name] = OrderedDict() + quizz_stats[name][question] = {"total": total_per_question[base_question], "responses": full_answers_total} return quizz_stats From e6415af0a7a4ae04625e0feebdf692b6ae88a7e5 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 3 Apr 2023 14:20:37 +0200 Subject: [PATCH 33/78] =?UTF-8?q?=20problem=20des=20statistiques=20doubl?= =?UTF-8?q?=C3=A9es=20r=C3=A9solu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/content-quizz.js | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index e4c46306ea..ffeec70f4c 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -6,7 +6,6 @@ let index = 0 function extractAnswer(inputDomElementList, answers) { - inputDomElementList.forEach((rb) => { const ulWrapperElement = rb.parentElement.parentElement @@ -73,8 +72,8 @@ function initializeCheckboxes(answers) { + // add explanation to all questions document.querySelectorAll('div.quizz').forEach(quizz => { - const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); quizzDivs.forEach(quizzDiv => { @@ -307,7 +306,6 @@ function injectForms(quizz, answers) { let answers = {} - initializePipeline.forEach(func => func(answers)) let idCounter = 0 @@ -320,6 +318,13 @@ document.querySelectorAll('div.quizz').forEach(div => { }) function sendQuizzStatistics(form, statistics) { + + const Result = statistics.result; + const questions = [...form.querySelectorAll('.custom-block-heading')].map(question => question.innerText); + Object.keys(Result).forEach(key => { + if (!questions.includes(key)) Result[key].labels = []; + }); + const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value const xhttp = new XMLHttpRequest() xhttp.open('POST', form.getAttribute('action')) @@ -333,7 +338,6 @@ function sendQuizzStatistics(form, statistics) { function displayResultAfterSubmitButton(form) { - const resultElement = form.querySelector('.result'); const questions = form.querySelectorAll('.custom-block-quizz'); @@ -369,8 +373,6 @@ function QuizzAnswered(form) { return true } - - document.querySelectorAll('form.quizz').forEach(form => { form.addEventListener('submit', e => { @@ -395,8 +397,6 @@ document.querySelectorAll('form.quizz').forEach(form => { if (QuizzAnswered(form)) { - let nbTotal = 0; - let nbGood = 0; const formData = new FormData(form) const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) @@ -452,28 +452,19 @@ document.querySelectorAll('form.quizz').forEach(form => { } statistics.result[title].labels.push(label.trim()) }) - console.log(element); - if (element.classList.contains('hasAnswer')) { - nbTotal++ - - if (!element.classList.contains('quizz-bad')) { - - element.classList.add('quizz-good') - statistics.result[title].evaluation = 'ok' - nbGood++ + if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { - } - - } + element.classList.add('quizz-good') + statistics.result[title].evaluation = 'ok' + } + }) sendQuizzStatistics(form, statistics) // submitBtn.setAttribute('disabled', true); displayResultAfterSubmitButton(form) notAnswered.innerText = '' - nbGood = 0; - nbTotal = 0; // not all questions answered }else { From f4f2353078d9a6b581f5402db0ecdecf5c8b3f8e Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Wed, 5 Apr 2023 13:34:48 +0200 Subject: [PATCH 34/78] =?UTF-8?q?=20probl=C3=A8me=20des=20statistiques=20d?= =?UTF-8?q?oubles=20r=C3=A9solu=20=C3=A0=20l'origine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/content-quizz.js | 70 +++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index ffeec70f4c..871aeda142 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -256,7 +256,7 @@ function injectForms(quizz, answers) { submit.innerText = submitLabel submit.classList.add('btn', 'btn-submit') - submit.setAttribute('id', `my-button-${idBias}`); + submit.setAttribute('id', `my-button`); const notAnswered = document.createElement('p') @@ -319,11 +319,6 @@ document.querySelectorAll('div.quizz').forEach(div => { function sendQuizzStatistics(form, statistics) { - const Result = statistics.result; - const questions = [...form.querySelectorAll('.custom-block-heading')].map(question => question.innerText); - Object.keys(Result).forEach(key => { - if (!questions.includes(key)) Result[key].labels = []; - }); const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value const xhttp = new XMLHttpRequest() @@ -332,6 +327,7 @@ function sendQuizzStatistics(form, statistics) { xhttp.setRequestHeader('Content-Type', 'application/json') xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href + xhttp.send(JSON.stringify(statistics)) } @@ -373,6 +369,7 @@ function QuizzAnswered(form) { return true } + document.querySelectorAll('form.quizz').forEach(form => { form.addEventListener('submit', e => { @@ -414,6 +411,8 @@ document.querySelectorAll('form.quizz').forEach(form => { questions.push(result.name) } }) + + const statistics = { expected: {}, result: {} @@ -432,35 +431,42 @@ document.querySelectorAll('form.quizz').forEach(form => { labels: [] } statistics.expected[title] = {} - const availableResponses = element.querySelectorAll('input') - for (let i = 0; i < availableResponses.length; i++) { - // wee need to get the question label for statistics - const liWrapper = availableResponses[i].parentElement - let questionLabel = liWrapper.textContent - if (correction && questionLabel.indexOf(correction.textContent) !== -1) { - questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) + + //make statistics of concerned form only + + const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => question.textContent); + + if (CurrentFormQuestions.includes(title)){ + + const availableResponses = element.querySelectorAll('input') + for (let i = 0; i < availableResponses.length; i++) { + // wee need to get the question label for statistics + const liWrapper = availableResponses[i].parentElement + let questionLabel = liWrapper.textContent + if (correction && questionLabel.indexOf(correction.textContent) !== -1) { + questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) + } + statistics.expected[title][questionLabel] = answers[name][i] } - statistics.expected[title][questionLabel] = answers[name][i] - } - // now determine answers and their labels - element.querySelectorAll('input:checked').forEach(node => { - - // remove eventual glued corretion - let label = node.parentElement.textContent - if (correction && label.indexOf(correction.textContent) !== -1) { - label = label.substr(0, label.indexOf(correction.textContent)) - } - statistics.result[title].labels.push(label.trim()) - }) - if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { + // now determine answers and their labels + element.querySelectorAll('input:checked').forEach(node => { + + // remove eventual glued corretion + let label = node.parentElement.textContent + if (correction && label.indexOf(correction.textContent) !== -1) { + label = label.substr(0, label.indexOf(correction.textContent)) + } + statistics.result[title].labels.push(label.trim()) + }) + if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { + + element.classList.add('quizz-good') + statistics.result[title].evaluation = 'ok' - element.classList.add('quizz-good') - statistics.result[title].evaluation = 'ok' - - } - + } + } }) - + sendQuizzStatistics(form, statistics) // submitBtn.setAttribute('disabled', true); displayResultAfterSubmitButton(form) From 4bc540405b7ce43536f0c16050cddc21e7463ec1 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Wed, 5 Apr 2023 23:56:53 +0200 Subject: [PATCH 35/78] disabled submit after answer&& non connected user can't answer a quizz and some other quick fixes --- assets/js/content-quizz.js | 183 ++++++++++++++++----------------- assets/scss/base/_content.scss | 7 ++ assets/scss/base/_forms.scss | 1 + assets/scss/pages/_stats.scss | 13 +-- 4 files changed, 98 insertions(+), 106 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 6fae9477fe..1981861dda 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -55,10 +55,12 @@ function extractAnswer(inputDomElementList, answers) { *
    *
    • the answer
    • *
    • the good answer + * *
      - *
      Explanation
      + *
      Explanation
      *
      a formatted text
      *
    • + * *
    *
    *
    @@ -70,8 +72,6 @@ function extractAnswer(inputDomElementList, answers) { */ function initializeCheckboxes(answers) { - - // add explanation to all questions document.querySelectorAll('div.quizz').forEach(quizz => { @@ -87,7 +87,6 @@ function initializeCheckboxes(answers) { lastLi.parentNode.removeChild(lastLi); quizzDiv.appendChild(explanation) - }); @@ -99,7 +98,6 @@ function initializeCheckboxes(answers) { } - function initializeRadio(answers) { const radio = document.querySelectorAll('.quizz ul li input[type=radio]') extractAnswer(radio, answers) @@ -137,6 +135,7 @@ function markBadAnswers(form,names, answers) { const inputs = form.querySelectorAll(`#${answer} input`); + let numChecked = 0; inputs.forEach(input => { if (input.hasAttribute('checked')) { @@ -155,8 +154,12 @@ function markBadAnswers(form,names, answers) { AnsweredWell = false } }) + + const divquizz = form.querySelector(`div[data-name="${answer}"]`) if (!AnsweredWell) { - form.querySelector(`div[data-name="${answer}"]`).classList.add('quizz-bad') + divquizz.classList.add('quizz-bad') + }else { + divquizz.classList.add('quizz-good') } } }) @@ -174,8 +177,9 @@ function markBadAnswers(form,names, answers) { }) } }) - - form.querySelector(`.custom-block[data-name=${name}]`).classList.add('quizz-bad') + + const divquizz = form.querySelector(`.custom-block[data-name=${name}]`) + if(!divquizz.classList.contains('quizz-good')) divquizz.classList.add('quizz-bad'); }) names.forEach(({ @@ -243,7 +247,6 @@ function injectForms(quizz, answers) { if (heading && !headings[heading.getAttribute('id')]) { - // this is just for convenience, this add a "known" element that will always be there const wrapper = document.createElement('div') @@ -319,7 +322,6 @@ document.querySelectorAll('div.quizz').forEach(div => { function sendQuizzStatistics(form, statistics) { - const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value const xhttp = new XMLHttpRequest() xhttp.open('POST', form.getAttribute('action')) @@ -336,7 +338,6 @@ function displayResultAfterSubmitButton(form) { const questions = form.querySelectorAll('.custom-block-quizz'); - for (let question of questions) { const explanationElement = question.querySelector('.explanation_off') @@ -372,110 +373,104 @@ function QuizzAnswered(form) { document.querySelectorAll('form.quizz').forEach(form => { form.addEventListener('submit', e => { - - - // Select all the custom-block-quizz divs - const quizzDivs = form.querySelectorAll('.custom-block-quizz'); - - quizzDivs.forEach(div => { - - div.classList.remove('quizz-bad'); - div.classList.remove('quizz-good'); - div.classList.remove('hasAnswer'); - div.classList.remove('quizz-multiple'); - }); - + e.preventDefault() e.stopPropagation() const notAnswered = form.querySelector('.notAnswered'); const submitBtn = form.querySelector('.btn-submit'); - if (QuizzAnswered(form)) { - - const formData = new FormData(form) - const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) + // test if the user is connected + if(document.querySelector('input[name=\'csrfmiddlewaretoken\']')){ - markBadAnswers(form,badAnswerNames, answers) + if (QuizzAnswered(form)) { - allAnswerNames.forEach(name => { - const ulWrapper = document.getElementById(name) - const quizzCustomBlock = ulWrapper.parentElement.parentElement - quizzCustomBlock.classList.add('hasAnswer') - }) - const questions = [] - badAnswerNames.forEach(result => { - if (questions.indexOf(result.name) === -1) { - questions.push(result.name) - } - }) - + const formData = new FormData(form) + const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) - const statistics = { - expected: {}, - result: {} - } + markBadAnswers(form,badAnswerNames, answers) - Object.keys(answers).forEach(name => { + allAnswerNames.forEach(name => { + const ulWrapper = document.getElementById(name) + const quizzCustomBlock = ulWrapper.parentElement.parentElement + quizzCustomBlock.classList.add('hasAnswer') + }) + const questions = [] + badAnswerNames.forEach(result => { + if (questions.indexOf(result.name) === -1) { + questions.push(result.name) + } + }) - const element = document.querySelector(`.custom-block[data-name="${name}"]`) - let title = element.querySelector('.custom-block-heading').textContent - const correction = element.querySelector('.custom-block-body .custom-block') - if (correction && title.indexOf(correction.textContent) > 0) { - title = title.substr(0, title.indexOf(correction.textContent)) - } - statistics.result[title] = { - evaluation: 'bad', - labels: [] + const statistics = { + expected: {}, + result: {} } - statistics.expected[title] = {} - - //make statistics of concerned form only - const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => question.textContent); + Object.keys(answers).forEach(name => { + + const element = document.querySelector(`.custom-block[data-name="${name}"]`) + let title = element.querySelector('.custom-block-heading').textContent + const correction = element.querySelector('.custom-block-body .custom-block') + if (correction && title.indexOf(correction.textContent) > 0) { + title = title.substr(0, title.indexOf(correction.textContent)) + } + statistics.result[title] = { + evaluation: 'bad', + labels: [] + } + statistics.expected[title] = {} + + //make statistics of concerned form only - if (CurrentFormQuestions.includes(title)){ + const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => question.textContent); - const availableResponses = element.querySelectorAll('input') - for (let i = 0; i < availableResponses.length; i++) { - // wee need to get the question label for statistics - const liWrapper = availableResponses[i].parentElement - let questionLabel = liWrapper.textContent - if (correction && questionLabel.indexOf(correction.textContent) !== -1) { - questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) - } - statistics.expected[title][questionLabel] = answers[name][i] - } - // now determine answers and their labels - element.querySelectorAll('input:checked').forEach(node => { - - // remove eventual glued corretion - let label = node.parentElement.textContent - if (correction && label.indexOf(correction.textContent) !== -1) { - label = label.substr(0, label.indexOf(correction.textContent)) - } - statistics.result[title].labels.push(label.trim()) - }) - if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { + if (CurrentFormQuestions.includes(title)){ - element.classList.add('quizz-good') - statistics.result[title].evaluation = 'ok' + const availableResponses = element.querySelectorAll('input') + for (let i = 0; i < availableResponses.length; i++) { + // wee need to get the question label for statistics + const liWrapper = availableResponses[i].parentElement + let questionLabel = liWrapper.textContent + if (correction && questionLabel.indexOf(correction.textContent) !== -1) { + questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) + } + statistics.expected[title][questionLabel] = answers[name][i] + } + // now determine answers and their labels + element.querySelectorAll('input:checked').forEach(node => { + + // remove eventual glued corretion + let label = node.parentElement.textContent + if (correction && label.indexOf(correction.textContent) !== -1) { + label = label.substr(0, label.indexOf(correction.textContent)) + } + statistics.result[title].labels.push(label.trim()) + }) + if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { + + element.classList.add('quizz-good') + statistics.result[title].evaluation = 'ok' - } - } - }) - - sendQuizzStatistics(form, statistics) - // submitBtn.setAttribute('disabled', true); - displayResultAfterSubmitButton(form) - notAnswered.innerText = '' - // not all questions answered - }else { + } + } + }) + sendQuizzStatistics(form, statistics) + submitBtn.setAttribute('disabled', true); + displayResultAfterSubmitButton(form) + notAnswered.innerText = '' + // not all questions answered + }else { + notAnswered.innerText = 'Veuillez répondre à toutes les questions' - + } + }else { + alert('Veuillez se connecter d\'abord'); + window.location = '/membres/connexion/?next=' + window.location.pathname + window.location.search; + } }) }) diff --git a/assets/scss/base/_content.scss b/assets/scss/base/_content.scss index fc16de0594..0438da9666 100644 --- a/assets/scss/base/_content.scss +++ b/assets/scss/base/_content.scss @@ -659,6 +659,13 @@ li.task-list-item { background-color: $true-white !important; } + input[type=radio] { + position: absolute; + top: $length-2; + left: -$length-24; + + background-color: $true-white !important; + } &.answer-bad { color: $color-danger; } diff --git a/assets/scss/base/_forms.scss b/assets/scss/base/_forms.scss index 38c4003f7f..3baeac19be 100644 --- a/assets/scss/base/_forms.scss +++ b/assets/scss/base/_forms.scss @@ -169,6 +169,7 @@ &:not(.btn-holder){ float: right; + margin-top: -2rem; } } diff --git a/assets/scss/pages/_stats.scss b/assets/scss/pages/_stats.scss index c02d8fd3eb..16af8fe06f 100644 --- a/assets/scss/pages/_stats.scss +++ b/assets/scss/pages/_stats.scss @@ -80,17 +80,9 @@ margin-top: 1rem; padding: 1rem; - border: 1px solid #f5c6cb; /* dark red */ + border: 1px solid #d19ea4; /* dark red */ color: #0a0706; /* dark text */ border-radius: 0.25rem; - font-size: 1.2rem; - line-height: 1.5; - &.active { - background-color: #02ca44; /* light red */ - } - &:empty { - display: none; - } } .notAnswered{ @@ -103,9 +95,6 @@ border-radius: 0.25rem; font-size: 1.2rem; line-height: 1.5; - &.active { - background-color: #02ca44; /* light red */ - } &:empty { display: none; } From 0cb115472fb2e0b6ff750d6892895d3785634af6 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Thu, 6 Apr 2023 13:25:36 +0200 Subject: [PATCH 36/78] survey start Js file --- assets/js/content-quizz.js | 4 +- assets/js/content-sondage.js | 142 +++++++++++++++++++++++++++++++++++ assets/scss/base/_forms.scss | 1 - 3 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 assets/js/content-sondage.js diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 1981861dda..1a88aa4b30 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -81,7 +81,7 @@ function initializeCheckboxes(answers) { const lastLi = ul.lastElementChild const explanationText = lastLi.innerText - const explanation = document.createElement('p') + const explanation = document.createElement('div') explanation.classList.add('explanation_off') explanation.innerText = explanationText lastLi.parentNode.removeChild(lastLi); @@ -329,7 +329,6 @@ function sendQuizzStatistics(form, statistics) { xhttp.setRequestHeader('Content-Type', 'application/json') xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href - xhttp.send(JSON.stringify(statistics)) } @@ -384,6 +383,7 @@ document.querySelectorAll('form.quizz').forEach(form => { // test if the user is connected if(document.querySelector('input[name=\'csrfmiddlewaretoken\']')){ + // test if the whole quizz is answered if (QuizzAnswered(form)) { const formData = new FormData(form) diff --git a/assets/js/content-sondage.js b/assets/js/content-sondage.js new file mode 100644 index 0000000000..281eddbc06 --- /dev/null +++ b/assets/js/content-sondage.js @@ -0,0 +1,142 @@ +/** + * The full quizz is contained in a div or article that has class "quizz". + * Then one question is inside a zmarkdown "custom-block" of type "custom-block-quizz". Two possibilities : + * + * Without explanation for correction : + * + * + *
    + *
    The question
    + *
    + *
    • the answer
    • + *
    • the good answer
    • + *
    + *
    + *
    + *
    + + */ + + +var currentURL = window.location.href; + +if (currentURL.includes("forums")) { + + let indeX = 0; + function Makesurvey(inputDomElementList) { + + + inputDomElementList.forEach((rb) => { + + + const ulWrapperElement = rb.parentElement.parentElement + // we give the ui an id to find the element in a more effective way later when the users answer the questions + if (!ulWrapperElement.getAttribute('id')) { + ulWrapperElement.setAttribute('id', 'id-' + (indeX++)) + } + + rb.setAttribute('name', ulWrapperElement.getAttribute('id')) + rb.setAttribute('type', 'radio') + + const questionBlock = ulWrapperElement.parentElement.parentElement + questionBlock.setAttribute('data-name', rb.getAttribute('name')) + + + rb.disabled = false + rb.checked = false + + }) + } + + + function initializeRadio() { + const radio = document.querySelectorAll('.custom-block-quizz input'); + Makesurvey(radio) + + } + + const initializePipeline = [initializeRadio] + + +/** + * As we are using forms to capture the answer and send the result back to user (and flushing stats for authors), + * we need to add an html form. + * + * The main issue is that it was asked to have many quizz inside a tutorial section, not just a list of questions in a + * specific section. As a result we had to find an heuristic : + * - if the current section has no h3 headings, we just span the form from the beggining to the end of section + * - if the current section has one or more h3 headings, we start the form just after it and end it at just before the + * next h3 heading if there is one or the end of form + * + * @param quizz current quizz container + * @param answers answers dictionary + */ + + let idCounter = 0 + + function injectForms(survey) { + + + const form = document.createElement('form') + form.classList.add('quizz') + + + const submit = document.createElement('button') + submit.innerText = 'Voter' + + const cancel = document.createElement('button') + cancel.innerText = 'Annuler' + + submit.classList.add('btn', 'btn-submit') + submit.setAttribute('id', `my-button-${idCounter}`); + + cancel.classList.add('btn', 'btn-cancel') + cancel.setAttribute('id', `my-button-cancel-${idCounter}`); + + const notAnswered = document.createElement('p') + notAnswered.classList.add('notAnswered') + + // form.method = 'POST' + // form.setAttribute('action', '') + form.setAttribute('id', `my-form-${idCounter}`); + idCounter++; + + form.appendChild(submit) + form.appendChild(cancel) + form.appendChild(notAnswered) + survey.appendChild(form) + + } + + + function displayResultAfterSubmitButton(quizz,result) { + + + const inputs = quizz.querySelectorAll('.custom-block-quizz input'); + + for (let input of inputs) { + + } + + } + + initializePipeline.forEach(func => func()) + + document.querySelectorAll('div.custom-block-quizz').forEach(div => { + injectForms(div) + }) + + document.querySelectorAll('div.custom-block-quizz').forEach(div => { + div.addEventListener('click',() => { + const id = event.target.id; + const button = div.querySelector(id); + // const submitBtn = div.querySelector('.btn-submit'); + // const cancelBtn = div.querySelector('.btn-cancel'); + console.log(id); + + }) + }) + +} + + diff --git a/assets/scss/base/_forms.scss b/assets/scss/base/_forms.scss index 3baeac19be..38c4003f7f 100644 --- a/assets/scss/base/_forms.scss +++ b/assets/scss/base/_forms.scss @@ -169,7 +169,6 @@ &:not(.btn-holder){ float: right; - margin-top: -2rem; } } From bce2090eac1d8923db4f56b030ea3bd94d4561e7 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Thu, 6 Apr 2023 18:13:22 +0200 Subject: [PATCH 37/78] added a quizz markdown code generator --- templates/tutorialv2/create/extract.html | 5 + templates/tutorialv2/edit/content.html | 5 + .../tutorialv2/view/quizz_generator.html | 122 ++++++++++++++++++ zds/tutorialv2/urls/urls_contents.py | 10 +- zds/tutorialv2/views/contents.py | 5 + 5 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 templates/tutorialv2/view/quizz_generator.html diff --git a/templates/tutorialv2/create/extract.html b/templates/tutorialv2/create/extract.html index 46efdb3562..8959b979ce 100644 --- a/templates/tutorialv2/create/extract.html +++ b/templates/tutorialv2/create/extract.html @@ -60,4 +60,9 @@

    {% trans "Ajouter une image à la galerie" %} +
  • + + {% trans "Créer un quizz" %} + +
  • {% endblock %} diff --git a/templates/tutorialv2/edit/content.html b/templates/tutorialv2/edit/content.html index 21f42caf28..8d73e3a09d 100644 --- a/templates/tutorialv2/edit/content.html +++ b/templates/tutorialv2/edit/content.html @@ -42,4 +42,9 @@

    {% trans "Ajouter une image à la galerie" %} +
  • + + {% trans "Créer un quizz" %} + +
  • {% endblock %} diff --git a/templates/tutorialv2/view/quizz_generator.html b/templates/tutorialv2/view/quizz_generator.html new file mode 100644 index 0000000000..ed8a652532 --- /dev/null +++ b/templates/tutorialv2/view/quizz_generator.html @@ -0,0 +1,122 @@ +{% extends "tutorialv2/base.html" %} + +{% block content %} +

    Générateur de quizz

    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    + + + + + + + +
    + +

    Code Markdown :

    + +
    + + + + + +{% endblock %} \ No newline at end of file diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index f742b0f7d1..fdf9ed6912 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -1,7 +1,14 @@ from django.urls import path, re_path from django.views.generic.base import RedirectView -from zds.tutorialv2.views.contents import DisplayContent, CreateContent, EditContent, EditContentLicense, DeleteContent +from zds.tutorialv2.views.contents import ( + DisplayContent, + CreateContent, + EditContent, + EditContentLicense, + DeleteContent, + Generate_quizz, +) from zds.tutorialv2.views.events import EventsList from zds.tutorialv2.views.goals import EditGoals, MassEditGoals, ViewContentsByGoal from zds.tutorialv2.views.validations_contents import ActivateJSFiddleInContent @@ -202,4 +209,5 @@ path("objectifs/", ViewContentsByGoal.as_view(), name="view-goals"), # quizz path("reponses///", ContentQuizzStatistics.as_view(), name="answer-quizz"), + path("quizz_generateur//", Generate_quizz.as_view(), name="generate-quizz"), ] diff --git a/zds/tutorialv2/views/contents.py b/zds/tutorialv2/views/contents.py index d5192459c5..2496e5e70e 100644 --- a/zds/tutorialv2/views/contents.py +++ b/zds/tutorialv2/views/contents.py @@ -452,3 +452,8 @@ def delete(self, request, *args, **kwargs): messages.success(self.request, _("Vous avez bien supprimé {}.").format(_type)) return redirect(reverse(object_type + ":find-" + object_type, args=[request.user.username])) + + +class Generate_quizz(SingleContentDetailViewMixin): + + template_name = "tutorialv2/view/quizz_generator.html" From 77fc9528c140cbf2d1e9742d2c504c5768084771 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sat, 8 Apr 2023 15:30:23 +0200 Subject: [PATCH 38/78] made quizz statistics in other page than contenent statistics --- templates/tutorialv2/stats/index.html | 23 ++++++------- templates/tutorialv2/stats/quizz_stats.html | 37 +++++++++++++++++++++ zds/tutorialv2/urls/urls_contents.py | 3 +- zds/tutorialv2/views/statistics.py | 10 +++++- 4 files changed, 58 insertions(+), 15 deletions(-) create mode 100644 templates/tutorialv2/stats/quizz_stats.html diff --git a/templates/tutorialv2/stats/index.html b/templates/tutorialv2/stats/index.html index 8277637ebc..1ed12c0be7 100644 --- a/templates/tutorialv2/stats/index.html +++ b/templates/tutorialv2/stats/index.html @@ -47,18 +47,13 @@

    {% trans "Pages vues" %} {% trans "Temps moyen de lecture" %} {% trans "Nombre de visiteurs uniques" %} - {% trans "Quizz" %} +

    {% include "misc/graph.part.html" with tab_name="tab-view-graph-content" graph_title="Évolution des pages vues sur le contenu" canvas_id="view-graph" report_key="nb_hits" y_label="Nombre de pages" %} {% include "misc/graph.part.html" with tab_name="tab-visit-time-graph-content" graph_title="Évolution du temps moyen lecture (en secondes)" canvas_id="visit-time-graph" report_key="avg_time_on_page" y_label="Secondes" %} {% include "misc/graph.part.html" with tab_name="tab-users-graph-content" graph_title="Évolution du nombre de visiteurs uniques" canvas_id="users-graph" report_key="nb_uniq_visitors" y_label="Nombre de visiteurs" %} - {% include "misc/quizz.graph.part.html" with tab_name="tab-quizz-content" graph_title="Réponses au quizz" canvas_id="quizz-graph" metric="quizz" %} - {% else %} -
    - {% trans "Quizz" %} -
    - {% include "misc/quizz.graph.part.html" with tab_name="tab-quizz-content" graph_title="Réponses au quizz" canvas_id="quizz-graph" metric="quizz" %} + {% endif %} @@ -183,6 +178,14 @@

    {% trans "Vues" %}

    {% block sidebar %} {% endblock sidebar %} diff --git a/templates/tutorialv2/stats/quizz_stats.html b/templates/tutorialv2/stats/quizz_stats.html new file mode 100644 index 0000000000..cdcf21a07d --- /dev/null +++ b/templates/tutorialv2/stats/quizz_stats.html @@ -0,0 +1,37 @@ +{% extends "tutorialv2/base.html" %} +{% load i18n %} +{% load crispy_forms_tags %} +{% load append_query_params %} +{% load datedelta_from_day %} +{% load seconds_to_duration %} +{% load static %} + +{% block title %} + {% trans "Statistiques du contenu" %} +{% endblock %} + +{% block breadcrumb %} +
  • {% trans "Statistiques" %}
  • +
  • {{ content.title }}
  • +{% endblock %} + +{% block content_out %} +
    + {% trans "Quizz" %} +
    + {% include "misc/quizz.graph.part.html" with tab_name="tab-quizz-content" graph_title="Réponses à la question :" canvas_id="quizz-graph" metric="quizz" %} +{% endblock %} + + +{% block sidebar %} + +{% endblock sidebar %} \ No newline at end of file diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index fdf9ed6912..3189b4302f 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -38,7 +38,7 @@ from zds.tutorialv2.views.lists import TagsListView, ContentOfAuthor, ListContentReactions from zds.tutorialv2.views.alerts import SendContentAlert, SolveContentAlert from zds.tutorialv2.views.misc import RequestFeaturedContent, FollowNewContent, WarnTypo -from zds.tutorialv2.views.statistics import ContentStatisticsView, ContentQuizzStatistics +from zds.tutorialv2.views.statistics import ContentStatisticsView, ContentQuizzStatistics, QuizzContentStatistics from zds.tutorialv2.views.comments import ( SendNoteFormView, UpdateNoteView, @@ -210,4 +210,5 @@ # quizz path("reponses///", ContentQuizzStatistics.as_view(), name="answer-quizz"), path("quizz_generateur//", Generate_quizz.as_view(), name="generate-quizz"), + path("stats_quizz///", QuizzContentStatistics.as_view(), name="stats-quizz"), ] diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index 5b08d086f2..af3afe1e70 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -17,7 +17,11 @@ from django.views.generic import FormView from zds.tutorialv2.forms import ContentCompareStatsURLForm, QuizzStatsForm -from zds.tutorialv2.mixins import SingleOnlineContentDetailViewMixin, SingleOnlineContentFormViewMixin +from zds.tutorialv2.mixins import ( + SingleContentDetailViewMixin, + SingleOnlineContentDetailViewMixin, + SingleOnlineContentFormViewMixin, +) from zds.tutorialv2.models.quizz import QuizzUserAnswer, QuizzQuestion, QuizzAvailableAnswer from zds.tutorialv2.utils import NamedUrl @@ -364,3 +368,7 @@ def build_quizz_stats(self, end_date, start_date): quizz_stats[name] = OrderedDict() quizz_stats[name][question] = {"total": total_per_question[base_question], "responses": full_answers_total} return quizz_stats + + +class QuizzContentStatistics(ContentStatisticsView): + template_name = "tutorialv2/stats/quizz_stats.html" From a32d1f1533af4045bba867c39df8b7a0408e8137 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Wed, 12 Apr 2023 11:43:28 +0200 Subject: [PATCH 39/78] non connected user can responses only one time per session, addes quizz bloc in editor --- assets/js/content-quizz.js | 310 ++++++++++++++++++++----------------- assets/js/editor-new.js | 15 +- 2 files changed, 180 insertions(+), 145 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 1a88aa4b30..eee2c3b636 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -6,28 +6,28 @@ let index = 0 function extractAnswer(inputDomElementList, answers) { - inputDomElementList.forEach((rb) => { - - const ulWrapperElement = rb.parentElement.parentElement - // we give the ui an id to find the element in a more effective way later when the users answer the questions - if (!ulWrapperElement.getAttribute('id')) { - ulWrapperElement.setAttribute('id', 'id-' + (index++)) - } + inputDomElementList.forEach((rb) => { - rb.setAttribute('name', ulWrapperElement.getAttribute('id')) + const ulWrapperElement = rb.parentElement.parentElement + // we give the ui an id to find the element in a more effective way later when the users answer the questions + if (!ulWrapperElement.getAttribute('id')) { + ulWrapperElement.setAttribute('id', 'id-' + (index++)) + } - const questionBlock = ulWrapperElement.parentElement.parentElement - questionBlock.setAttribute('data-name', rb.getAttribute('name')) - if (!answers[ulWrapperElement.getAttribute('id')]) { - answers[ulWrapperElement.getAttribute('id')] = [rb.checked] - } else { - answers[ulWrapperElement.getAttribute('id')].push(rb.checked) - } - rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) - rb.disabled = false - rb.checked = false + rb.setAttribute('name', ulWrapperElement.getAttribute('id')) - }) + const questionBlock = ulWrapperElement.parentElement.parentElement + questionBlock.setAttribute('data-name', rb.getAttribute('name')) + if (!answers[ulWrapperElement.getAttribute('id')]) { + answers[ulWrapperElement.getAttribute('id')] = [rb.checked] + } else { + answers[ulWrapperElement.getAttribute('id')].push(rb.checked) + } + rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) + rb.disabled = false + rb.checked = false + + }) } /** @@ -71,31 +71,45 @@ function extractAnswer(inputDomElementList, answers) { * @param answers the answer dictionary, it will be modified by the process */ function initializeCheckboxes(answers) { - - // add explanation to all questions - document.querySelectorAll('div.quizz').forEach(quizz => { - - const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); - quizzDivs.forEach(quizzDiv => { - const ul = quizzDiv.querySelector('ul') - const lastLi = ul.lastElementChild - const explanationText = lastLi.innerText - - const explanation = document.createElement('div') - explanation.classList.add('explanation_off') - explanation.innerText = explanationText - lastLi.parentNode.removeChild(lastLi); - quizzDiv.appendChild(explanation) - - }); - - - }) - const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') + // add explanation to all questions + document.querySelectorAll('div.quizz').forEach(quizz => { + + const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); + quizzDivs.forEach(quizzDiv => { + const ul = quizzDiv.querySelector('ul') + const lastLi = ul.lastElementChild + const explanationText = lastLi.innerText + + const explanation = document.createElement('div') + explanation.classList.add('explanation_off') + explanation.innerText = explanationText + lastLi.parentNode.removeChild(lastLi); + quizzDiv.appendChild(explanation) + + }); + + + // const listItemElems = .querySelectorAll('.task-list-item'); + + // listItemElems.forEach((listItemElem) => { + // const inputElem = listItemElem.querySelector('input[type="checkbox"]'); + // const labelElem = document.createElement('label'); + // const id = inputElem.id; + // labelElem.setAttribute('for', id); + // labelElem.textContent = listItemElem.textContent.trim(); + // inputElem.parentNode.insertBefore(labelElem, inputElem); + // labelElem.appendChild(inputElem); + // listItemElem.removeChild(listItemElem.lastChild); + // }); + + }) + + + const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') + + extractAnswer(checkboxes, answers) - extractAnswer(checkboxes, answers) - } function initializeRadio(answers) { @@ -127,10 +141,10 @@ function computeForm(formdata, answers) { return [badAnswers, allAnswerNames] } -function markBadAnswers(form,names, answers) { - +function markBadAnswers(form, names, answers) { + const toAdd = [] - + Object.keys(answers).forEach(answer => { const inputs = form.querySelectorAll(`#${answer} input`); @@ -145,7 +159,7 @@ function markBadAnswers(form,names, answers) { }); // question with more than one correct answer - if(numChecked > 1){ + if (numChecked > 1) { let AnsweredWell = true answers[answer].forEach((value, index) => { @@ -158,19 +172,19 @@ function markBadAnswers(form,names, answers) { const divquizz = form.querySelector(`div[data-name="${answer}"]`) if (!AnsweredWell) { divquizz.classList.add('quizz-bad') - }else { + } else { divquizz.classList.add('quizz-good') } } }) - + names.forEach(({ name }) => { form.querySelectorAll('input[name="' + name + '"]').forEach(field => { - - + + if (answers[name][parseInt(field.getAttribute('value'), 10)] && !field.checked) { - + toAdd.push({ name: name, value: field.getAttribute('value') @@ -179,14 +193,14 @@ function markBadAnswers(form,names, answers) { }) const divquizz = form.querySelector(`.custom-block[data-name=${name}]`) - if(!divquizz.classList.contains('quizz-good')) divquizz.classList.add('quizz-bad'); + if (!divquizz.classList.contains('quizz-good')) divquizz.classList.add('quizz-bad'); }) names.forEach(({ name, value }) => { - + form.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`).parentElement.classList.add('answer-bad') }) @@ -196,7 +210,7 @@ function markBadAnswers(form,names, answers) { function getWantedHeading(questionNode, nodeName, attr) { let potentialHeading = questionNode while (potentialHeading[attr] && - potentialHeading.nodeName !== nodeName.toUpperCase()) { + potentialHeading.nodeName !== nodeName.toUpperCase()) { potentialHeading = potentialHeading[attr] } if (potentialHeading.nodeName !== nodeName.toUpperCase()) { @@ -228,7 +242,7 @@ function injectForms(quizz, answers) { Object.keys(answers).forEach(blockId => { - + const blockNode = document.getElementById(blockId) if (!blockNode) { @@ -254,17 +268,17 @@ function injectForms(quizz, answers) { const form = document.createElement('form') form.classList.add('quizz') - + const submit = document.createElement('button') submit.innerText = submitLabel - + submit.classList.add('btn', 'btn-submit') - submit.setAttribute('id', `my-button`); + submit.setAttribute('id', `my-button-${idBias}`); const notAnswered = document.createElement('p') notAnswered.classList.add('notAnswered') - + let nodeToAddToForm @@ -315,13 +329,14 @@ let idCounter = 0 let idBias = 0 document.querySelectorAll('div.quizz').forEach(div => { - + injectForms(div, answers) }) function sendQuizzStatistics(form, statistics) { + const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value const xhttp = new XMLHttpRequest() xhttp.open('POST', form.getAttribute('action')) @@ -340,7 +355,7 @@ function displayResultAfterSubmitButton(form) { for (let question of questions) { const explanationElement = question.querySelector('.explanation_off') - if(explanationElement!==null){ + if (explanationElement !== null) { explanationElement.classList.remove('explanation_off'); explanationElement.classList.add('explanation_on'); } @@ -369,10 +384,12 @@ function QuizzAnswered(form) { return true } + + document.querySelectorAll('form.quizz').forEach(form => { - + form.addEventListener('submit', e => { - + e.preventDefault() e.stopPropagation() @@ -380,98 +397,105 @@ document.querySelectorAll('form.quizz').forEach(form => { const submitBtn = form.querySelector('.btn-submit'); - // test if the user is connected - if(document.querySelector('input[name=\'csrfmiddlewaretoken\']')){ + if (!document.querySelector('input[name=\'csrfmiddlewaretoken\']')) { + if (!sessionStorage.getItem(submitBtn.id)) { + // If the quiz has not been submitted before, store a flag in session storage to prevent multiple submissions + sessionStorage.setItem(`${submitBtn.id}`, true); + } else { + // If the quiz has already been submitted, disable the submit button + submitBtn.setAttribute('disabled', true); + alert('Vous avez deja repondu, Veuillez se connecter'); + return; + } + } - // test if the whole quizz is answered - if (QuizzAnswered(form)) { + // test if the whole quizz is answered + if (QuizzAnswered(form)) { - const formData = new FormData(form) - const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) + const formData = new FormData(form) + const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) - markBadAnswers(form,badAnswerNames, answers) + markBadAnswers(form, badAnswerNames, answers) - allAnswerNames.forEach(name => { - const ulWrapper = document.getElementById(name) - const quizzCustomBlock = ulWrapper.parentElement.parentElement - quizzCustomBlock.classList.add('hasAnswer') - }) - const questions = [] - badAnswerNames.forEach(result => { - if (questions.indexOf(result.name) === -1) { - questions.push(result.name) - } - }) - - const statistics = { - expected: {}, - result: {} + allAnswerNames.forEach(name => { + const ulWrapper = document.getElementById(name) + const quizzCustomBlock = ulWrapper.parentElement.parentElement + quizzCustomBlock.classList.add('hasAnswer') + }) + const questions = [] + badAnswerNames.forEach(result => { + if (questions.indexOf(result.name) === -1) { + questions.push(result.name) } + }) - Object.keys(answers).forEach(name => { - - const element = document.querySelector(`.custom-block[data-name="${name}"]`) - let title = element.querySelector('.custom-block-heading').textContent - const correction = element.querySelector('.custom-block-body .custom-block') - if (correction && title.indexOf(correction.textContent) > 0) { - title = title.substr(0, title.indexOf(correction.textContent)) - } - statistics.result[title] = { - evaluation: 'bad', - labels: [] - } - statistics.expected[title] = {} - - //make statistics of concerned form only - - const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => question.textContent); - - if (CurrentFormQuestions.includes(title)){ - - const availableResponses = element.querySelectorAll('input') - for (let i = 0; i < availableResponses.length; i++) { - // wee need to get the question label for statistics - const liWrapper = availableResponses[i].parentElement - let questionLabel = liWrapper.textContent - if (correction && questionLabel.indexOf(correction.textContent) !== -1) { - questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) - } - statistics.expected[title][questionLabel] = answers[name][i] + const statistics = { + expected: {}, + result: {} + } + + Object.keys(answers).forEach(name => { + + const element = document.querySelector(`.custom-block[data-name="${name}"]`) + let title = element.querySelector('.custom-block-heading').textContent + const correction = element.querySelector('.custom-block-body .custom-block') + if (correction && title.indexOf(correction.textContent) > 0) { + title = title.substr(0, title.indexOf(correction.textContent)) + } + statistics.result[title] = { + evaluation: 'bad', + labels: [] + } + statistics.expected[title] = {} + + //make statistics of concerned form only + + const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => question.textContent); + + if (CurrentFormQuestions.includes(title)) { + + const availableResponses = element.querySelectorAll('input') + for (let i = 0; i < availableResponses.length; i++) { + // wee need to get the question label for statistics + const liWrapper = availableResponses[i].parentElement + let questionLabel = liWrapper.textContent + if (correction && questionLabel.indexOf(correction.textContent) !== -1) { + questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) } - // now determine answers and their labels - element.querySelectorAll('input:checked').forEach(node => { - - // remove eventual glued corretion - let label = node.parentElement.textContent - if (correction && label.indexOf(correction.textContent) !== -1) { - label = label.substr(0, label.indexOf(correction.textContent)) - } - statistics.result[title].labels.push(label.trim()) - }) - if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { - - element.classList.add('quizz-good') - statistics.result[title].evaluation = 'ok' + statistics.expected[title][questionLabel] = answers[name][i] + } + // now determine answers and their labels + element.querySelectorAll('input:checked').forEach(node => { + // remove eventual glued corretion + let label = node.parentElement.textContent + if (correction && label.indexOf(correction.textContent) !== -1) { + label = label.substr(0, label.indexOf(correction.textContent)) } - } - }) - + statistics.result[title].labels.push(label.trim()) + }) + if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { + + element.classList.add('quizz-good') + statistics.result[title].evaluation = 'ok' + + } + } + }) + + if (document.querySelector('input[name=\'csrfmiddlewaretoken\']')) { + sendQuizzStatistics(form, statistics) - submitBtn.setAttribute('disabled', true); - displayResultAfterSubmitButton(form) - notAnswered.innerText = '' - // not all questions answered - }else { - - notAnswered.innerText = 'Veuillez répondre à toutes les questions' - } - }else { - alert('Veuillez se connecter d\'abord'); - window.location = '/membres/connexion/?next=' + window.location.pathname + window.location.search; + // submitBtn.setAttribute('disabled', true); + displayResultAfterSubmitButton(form) + notAnswered.innerText = '' + // not all questions answered + } else { + + notAnswered.innerText = 'Veuillez répondre à toutes les questions' + } - }) }) diff --git a/assets/js/editor-new.js b/assets/js/editor-new.js index ed610b2a79..d6c994e702 100644 --- a/assets/js/editor-new.js +++ b/assets/js/editor-new.js @@ -169,7 +169,7 @@ start = text.slice(0, startPoint.ch) end = text.slice(startPoint.ch) let offset = 0 - if (type === 'blocInformation' || type === 'blocQuestion' || type === 'blocWarning' || type === 'blocError' || type === 'blocSecret' || type === 'blocNeutral') { + if (type === 'blocInformation' || type === 'blocQuestion' || type === 'blocWarning' || type === 'blocError' || type === 'blocSecret' || type === 'blocNeutral' || type === 'blocquizz') { unShiftLines(cm, startPoint.line, endPoint.line) startPoint.ch = 0 } else if (type === 'checklist') { @@ -228,7 +228,7 @@ function enableBlockZmd(cm, type, start, end, startPoint, endPoint) { let i, text - if (type === 'blocInformation' || type === 'blocQuestion' || type === 'blocWarning' || type === 'blocError' || type === 'blocSecret' || type === 'blocNeutral') { + if (type === 'blocInformation' || type === 'blocQuestion' || type === 'blocWarning' || type === 'blocError' || type === 'blocSecret' || type === 'blocNeutral'|| type === 'blocquizz') { // blocs for (i = startPoint.line; i <= endPoint.line; i++) { text = start + cm.getLine(i) @@ -247,6 +247,9 @@ } else if (type === 'blocNeutral') { shiftLines(cm, startPoint.line, '[[neutre|titre]]') } + else if (type === 'blocquizz') { + shiftLines(cm, startPoint.line, '\n[[quizz | question ?]]\n| - [ ] réponse\n| - [x] bonne réponse\n| - [ ] réponse\n| - explication : \n') + } startPoint.ch = 0 endPoint.line += 1 endPoint.ch = endPoint.line.length @@ -832,6 +835,14 @@ } ] }, + { + name: 'blocquizz', + action: (e) => { + _toggleBlockZmd(e, 'blocquizz', '') + }, + className: 'fas fa-question', + title: 'Bloc quizz' + }, '|', { name: 'blocMenu', From 034dc5eab1755b2315bfbaf492cadfedad6ade97 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Wed, 12 Apr 2023 19:46:43 +0200 Subject: [PATCH 40/78] deleted that quizz generator --- templates/tutorialv2/create/extract.html | 5 - templates/tutorialv2/edit/content.html | 5 - .../tutorialv2/view/quizz_generator.html | 122 ------------------ zds/tutorialv2/urls/urls_contents.py | 2 - zds/tutorialv2/views/contents.py | 5 - 5 files changed, 139 deletions(-) delete mode 100644 templates/tutorialv2/view/quizz_generator.html diff --git a/templates/tutorialv2/create/extract.html b/templates/tutorialv2/create/extract.html index 8959b979ce..46efdb3562 100644 --- a/templates/tutorialv2/create/extract.html +++ b/templates/tutorialv2/create/extract.html @@ -60,9 +60,4 @@

    {% trans "Ajouter une image à la galerie" %} -
  • - - {% trans "Créer un quizz" %} - -
  • {% endblock %} diff --git a/templates/tutorialv2/edit/content.html b/templates/tutorialv2/edit/content.html index 8d73e3a09d..21f42caf28 100644 --- a/templates/tutorialv2/edit/content.html +++ b/templates/tutorialv2/edit/content.html @@ -42,9 +42,4 @@

    {% trans "Ajouter une image à la galerie" %} -
  • - - {% trans "Créer un quizz" %} - -
  • {% endblock %} diff --git a/templates/tutorialv2/view/quizz_generator.html b/templates/tutorialv2/view/quizz_generator.html deleted file mode 100644 index ed8a652532..0000000000 --- a/templates/tutorialv2/view/quizz_generator.html +++ /dev/null @@ -1,122 +0,0 @@ -{% extends "tutorialv2/base.html" %} - -{% block content %} -

    Générateur de quizz

    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    - - - - - - - -
    - -

    Code Markdown :

    - -
    - - - - - -{% endblock %} \ No newline at end of file diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index 3189b4302f..233ef217ca 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -7,7 +7,6 @@ EditContent, EditContentLicense, DeleteContent, - Generate_quizz, ) from zds.tutorialv2.views.events import EventsList from zds.tutorialv2.views.goals import EditGoals, MassEditGoals, ViewContentsByGoal @@ -209,6 +208,5 @@ path("objectifs/", ViewContentsByGoal.as_view(), name="view-goals"), # quizz path("reponses///", ContentQuizzStatistics.as_view(), name="answer-quizz"), - path("quizz_generateur//", Generate_quizz.as_view(), name="generate-quizz"), path("stats_quizz///", QuizzContentStatistics.as_view(), name="stats-quizz"), ] diff --git a/zds/tutorialv2/views/contents.py b/zds/tutorialv2/views/contents.py index 2496e5e70e..d5192459c5 100644 --- a/zds/tutorialv2/views/contents.py +++ b/zds/tutorialv2/views/contents.py @@ -452,8 +452,3 @@ def delete(self, request, *args, **kwargs): messages.success(self.request, _("Vous avez bien supprimé {}.").format(_type)) return redirect(reverse(object_type + ":find-" + object_type, args=[request.user.username])) - - -class Generate_quizz(SingleContentDetailViewMixin): - - template_name = "tutorialv2/view/quizz_generator.html" From 02648d81670d17da51bf750c8b46511ac7dd2c7e Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 16 Apr 2023 19:40:16 +0200 Subject: [PATCH 41/78] fixed issue of text between quizz and submit button --- assets/js/content-quizz.js | 108 ++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index eee2c3b636..85223b7a33 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -5,7 +5,7 @@ let index = 0 function extractAnswer(inputDomElementList, answers) { - + inputDomElementList.forEach((rb) => { const ulWrapperElement = rb.parentElement.parentElement @@ -25,8 +25,7 @@ function extractAnswer(inputDomElementList, answers) { } rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) rb.disabled = false - rb.checked = false - + rb.checked = false }) } @@ -74,7 +73,6 @@ function initializeCheckboxes(answers) { // add explanation to all questions document.querySelectorAll('div.quizz').forEach(quizz => { - const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); quizzDivs.forEach(quizzDiv => { const ul = quizzDiv.querySelector('ul') @@ -88,26 +86,10 @@ function initializeCheckboxes(answers) { quizzDiv.appendChild(explanation) }); - - - // const listItemElems = .querySelectorAll('.task-list-item'); - - // listItemElems.forEach((listItemElem) => { - // const inputElem = listItemElem.querySelector('input[type="checkbox"]'); - // const labelElem = document.createElement('label'); - // const id = inputElem.id; - // labelElem.setAttribute('for', id); - // labelElem.textContent = listItemElem.textContent.trim(); - // inputElem.parentNode.insertBefore(labelElem, inputElem); - // labelElem.appendChild(inputElem); - // listItemElem.removeChild(listItemElem.lastChild); - // }); - }) const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') - extractAnswer(checkboxes, answers) } @@ -219,18 +201,17 @@ function getWantedHeading(questionNode, nodeName, attr) { return potentialHeading } + +function generateUUID() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = Math.random() * 16 | 0; + const v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} + /** - * As we are using forms to capture the answer and send the result back to user (and flushing stats for authors), - * we need to add an html form. - * - * The main issue is that it was asked to have many quizz inside a tutorial section, not just a list of questions in a - * specific section. As a result we had to find an heuristic : - * - if the current section has no h3 headings, we just span the form from the beggining to the end of section - * - if the current section has one or more h3 headings, we start the form just after it and end it at just before the - * next h3 heading if there is one or the end of form - * - * @param quizz current quizz container - * @param answers answers dictionary + */ function injectForms(quizz, answers) { @@ -240,9 +221,12 @@ function injectForms(quizz, answers) { const headings = {} const wrappers = [] - Object.keys(answers).forEach(blockId => { + const QuizzQstNum = quizz.querySelectorAll('.custom-block-quizz').length + + Object.keys(answers).forEach(blockId => { + const blockNode = document.getElementById(blockId) if (!blockNode) { @@ -254,6 +238,7 @@ function injectForms(quizz, answers) { const heading = getWantedHeading(questionNode, searchedTitle, 'previousSibling') || quizz if (!heading.getAttribute('id')) { + heading.setAttribute('id', `quizz-form-${idBias}`) idBias++ } @@ -271,9 +256,11 @@ function injectForms(quizz, answers) { const submit = document.createElement('button') submit.innerText = submitLabel + submit.style.marginTop = '-22px' submit.classList.add('btn', 'btn-submit') - submit.setAttribute('id', `my-button-${idBias}`); + + submit.setAttribute('id', `my-button-${generateUUID()}`); const notAnswered = document.createElement('p') @@ -283,34 +270,53 @@ function injectForms(quizz, answers) { let nodeToAddToForm if (heading === quizz) { + nodeToAddToForm = quizz.firstChild } else { nodeToAddToForm = heading.nextSibling } + + + // add any element before the first question to the wrapper + while (nodeToAddToForm.nodeName !== "DIV" && nodeToAddToForm.className !== "custom-block custom-block-quizz"){ + + const current = nodeToAddToForm + nodeToAddToForm = nodeToAddToForm.nextSibling + wrapper.appendChild(current.cloneNode(true)) + current.parentNode.removeChild(current) + + } + form.method = 'POST' form.setAttribute('action', quizz.getAttribute('data-answer-url')) form.setAttribute('id', `my-form-${idCounter}`); idCounter++; - // gather all the questions of this current subsection, until the next h3 heading + + + // add elements between 1st and last question to form + let num = 0; while (nodeToAddToForm && nodeToAddToForm.nodeName !== searchedTitle.toUpperCase()) { const current = nodeToAddToForm + if ( current.nodeName === "DIV" && current.className === "custom-block custom-block-quizz") num++; nodeToAddToForm = nodeToAddToForm.nextSibling form.appendChild(current.cloneNode(true)) current.parentNode.removeChild(current) + if ( num === QuizzQstNum ) {form.appendChild(submit) ; break}; } - form.appendChild(submit) - form.appendChild(notAnswered) - wrappers.push(wrapper) - if (nodeToAddToForm && nodeToAddToForm.nodeName === searchedTitle.toUpperCase()) { - wrapper.append(form) - wrappers.push(nodeToAddToForm.cloneNode(true)) - } else { - wrapper.appendChild(form) - if (heading.nextSibling) { - wrapper.appendChild(heading.nextSibling.cloneNode(true)) - heading.parentNode.removeChild(heading.nextSibling) - } + wrapper.append(form) + + // add elements after last question until next h3 to wrapper + while (nodeToAddToForm && nodeToAddToForm.nodeName !== searchedTitle.toUpperCase()) { + + const current = nodeToAddToForm + nodeToAddToForm = nodeToAddToForm.nextSibling + wrapper.appendChild(current.cloneNode(true)) + current.parentNode.removeChild(current) } + + wrapper.appendChild(notAnswered) + wrappers.push(wrapper) + } // avoid doubly if (heading.nodeName === searchedTitle.toUpperCase()) { @@ -320,7 +326,6 @@ function injectForms(quizz, answers) { wrappers.forEach((wrapper) => quizz.appendChild(wrapper)) } - let answers = {} initializePipeline.forEach(func => func(answers)) @@ -330,7 +335,10 @@ let idBias = 0 document.querySelectorAll('div.quizz').forEach(div => { - injectForms(div, answers) + const quizInside = div.querySelector('.custom-block-quizz ul li input[type=checkbox]'); + if (quizInside) { + injectForms(div, answers) + } }) @@ -393,7 +401,7 @@ document.querySelectorAll('form.quizz').forEach(form => { e.preventDefault() e.stopPropagation() - const notAnswered = form.querySelector('.notAnswered'); + const notAnswered = form.parentElement.querySelector('.notAnswered'); const submitBtn = form.querySelector('.btn-submit'); @@ -487,7 +495,7 @@ document.querySelectorAll('form.quizz').forEach(form => { sendQuizzStatistics(form, statistics) } - // submitBtn.setAttribute('disabled', true); + submitBtn.setAttribute('disabled', true); displayResultAfterSubmitButton(form) notAnswered.innerText = '' // not all questions answered From 44f86269d266e110ac2e4aaa049cf8adb5f8a66e Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 16 Apr 2023 19:42:20 +0200 Subject: [PATCH 42/78] added a button to create quizz squelette on editor; still have an issue to fix --- assets/js/editor-new.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assets/js/editor-new.js b/assets/js/editor-new.js index d6c994e702..b0ef9a8ce2 100644 --- a/assets/js/editor-new.js +++ b/assets/js/editor-new.js @@ -118,6 +118,10 @@ } else if (_type === 'n' || _type === 'neutre') { ret.blocNeutral = true } + // else if (_type === 'q' || _type === 'quizz') { + // ret.blocNeutral = true + // } + } } else { // find checklist @@ -248,7 +252,7 @@ shiftLines(cm, startPoint.line, '[[neutre|titre]]') } else if (type === 'blocquizz') { - shiftLines(cm, startPoint.line, '\n[[quizz | question ?]]\n| - [ ] réponse\n| - [x] bonne réponse\n| - [ ] réponse\n| - explication : \n') + shiftLines(cm, startPoint.line, '[[quizz|Question]]\n|- [ ] réponse\n| - [x] bonne réponse\n| - [ ] réponse\n| - Explication : '); } startPoint.ch = 0 endPoint.line += 1 From 9d9c10c265780d60fb28e1f291092b72f4e33f9f Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 17 Apr 2023 13:25:21 +0200 Subject: [PATCH 43/78] fix explanation dislay --- assets/js/content-quizz.js | 22 ++++++++++++---------- assets/scss/pages/_stats.scss | 2 -- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 85223b7a33..314371290c 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -73,17 +73,16 @@ function initializeCheckboxes(answers) { // add explanation to all questions document.querySelectorAll('div.quizz').forEach(quizz => { - const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); + const quizzDivs = quizz.querySelectorAll('div.custom-block-quizz'); quizzDivs.forEach(quizzDiv => { const ul = quizzDiv.querySelector('ul') const lastLi = ul.lastElementChild - const explanationText = lastLi.innerText - + const explanationText = 'Explication :' + lastLi.innerText const explanation = document.createElement('div') explanation.classList.add('explanation_off') - explanation.innerText = explanationText + explanation.innerHTML = explanationText lastLi.parentNode.removeChild(lastLi); - quizzDiv.appendChild(explanation) + quizzDiv.querySelector('div.custom-block-body').appendChild(explanation) }); }) @@ -260,14 +259,12 @@ function injectForms(quizz, answers) { submit.classList.add('btn', 'btn-submit') - submit.setAttribute('id', `my-button-${generateUUID()}`); - + submit.setAttribute('id', `my-button-${idButton}`); + idButton++ const notAnswered = document.createElement('p') notAnswered.classList.add('notAnswered') - - let nodeToAddToForm if (heading === quizz) { @@ -286,7 +283,7 @@ function injectForms(quizz, answers) { current.parentNode.removeChild(current) } - + form.method = 'POST' form.setAttribute('action', quizz.getAttribute('data-answer-url')) form.setAttribute('id', `my-form-${idCounter}`); @@ -333,6 +330,11 @@ initializePipeline.forEach(func => func(answers)) let idCounter = 0 let idBias = 0 + +let idButton = 0; + +// parseInt(sessionStorage.getItem('idButton')) || + document.querySelectorAll('div.quizz').forEach(div => { const quizInside = div.querySelector('.custom-block-quizz ul li input[type=checkbox]'); diff --git a/assets/scss/pages/_stats.scss b/assets/scss/pages/_stats.scss index 16af8fe06f..9c43103f5b 100644 --- a/assets/scss/pages/_stats.scss +++ b/assets/scss/pages/_stats.scss @@ -79,8 +79,6 @@ margin-top: 1rem; padding: 1rem; - - border: 1px solid #d19ea4; /* dark red */ color: #0a0706; /* dark text */ border-radius: 0.25rem; } From 78f8174f239daaa9a40d4b984e235d72293a99d9 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 17 Apr 2023 17:13:41 +0200 Subject: [PATCH 44/78] added icons for a best result display for daltoniens --- assets/js/content-quizz.js | 40 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 314371290c..9a1780f522 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -153,6 +153,13 @@ function markBadAnswers(form, names, answers) { const divquizz = form.querySelector(`div[data-name="${answer}"]`) if (!AnsweredWell) { divquizz.classList.add('quizz-bad') + const icon = document.createElement('i'); + icon.classList.add('fas', 'fa-exclamation-triangle'); + icon.style.fontSize = '24px'; + icon.style.transform = 'scale(2)'; + icon.style.marginLeft='-92px' + // Append the icon to the element + divquizz.querySelector('div.custom-block-body').appendChild(icon); } else { divquizz.classList.add('quizz-good') } @@ -174,7 +181,18 @@ function markBadAnswers(form, names, answers) { }) const divquizz = form.querySelector(`.custom-block[data-name=${name}]`) - if (!divquizz.classList.contains('quizz-good')) divquizz.classList.add('quizz-bad'); + if (!divquizz.classList.contains('quizz-good')) { + + divquizz.classList.add('quizz-bad') + // Create a new icon element + const icon = document.createElement('i'); + icon.classList.add('fas', 'fa-exclamation-triangle'); + icon.style.fontSize = '24px'; + icon.style.transform = 'scale(2)'; + icon.style.marginLeft='-92px' + // Append the icon to the element + divquizz.querySelector('div.custom-block-body').appendChild(icon); + } }) names.forEach(({ @@ -200,15 +218,6 @@ function getWantedHeading(questionNode, nodeName, attr) { return potentialHeading } - -function generateUUID() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - const r = Math.random() * 16 | 0; - const v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); -} - /** */ @@ -487,6 +496,17 @@ document.querySelectorAll('form.quizz').forEach(form => { if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { element.classList.add('quizz-good') + + // Create a new icon element + const icon = document.createElement('i'); + icon.classList.add('fas', 'fa-check'); // Use Font Awesome checkmark icon + icon.style.fontSize = '24px'; // Set font size to 24 pixels + icon.style.transform = 'scale(2)'; // Scale icon to twice its + icon.style.marginLeft='-92px' + // Append the icon to the element + element.querySelector('div.custom-block-body').appendChild(icon); + + statistics.result[title].evaluation = 'ok' } From c50d72b85a8b6145ec92433154743db38b4a7819 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Tue, 18 Apr 2023 16:25:34 +0200 Subject: [PATCH 45/78] factorisaton of icon code --- assets/js/content-quizz.js | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 9a1780f522..17a0522e66 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -153,12 +153,7 @@ function markBadAnswers(form, names, answers) { const divquizz = form.querySelector(`div[data-name="${answer}"]`) if (!AnsweredWell) { divquizz.classList.add('quizz-bad') - const icon = document.createElement('i'); - icon.classList.add('fas', 'fa-exclamation-triangle'); - icon.style.fontSize = '24px'; - icon.style.transform = 'scale(2)'; - icon.style.marginLeft='-92px' - // Append the icon to the element + const icon = iconMaker(false) divquizz.querySelector('div.custom-block-body').appendChild(icon); } else { divquizz.classList.add('quizz-good') @@ -185,12 +180,7 @@ function markBadAnswers(form, names, answers) { divquizz.classList.add('quizz-bad') // Create a new icon element - const icon = document.createElement('i'); - icon.classList.add('fas', 'fa-exclamation-triangle'); - icon.style.fontSize = '24px'; - icon.style.transform = 'scale(2)'; - icon.style.marginLeft='-92px' - // Append the icon to the element + const icon = iconMaker(false) divquizz.querySelector('div.custom-block-body').appendChild(icon); } }) @@ -403,7 +393,16 @@ function QuizzAnswered(form) { return true } - +function iconMaker(isGood){ + // Create a new icon element + const icon = document.createElement('i'); + icon.classList.add('fas'); + isGood ? icon.classList.add('fa-check') : icon.classList.add('fa-exclamation-triangle') + icon.style.fontSize = '24px'; + icon.style.transform = 'scale(2)'; + icon.style.marginLeft='-92px' + return icon +} document.querySelectorAll('form.quizz').forEach(form => { @@ -497,12 +496,7 @@ document.querySelectorAll('form.quizz').forEach(form => { element.classList.add('quizz-good') - // Create a new icon element - const icon = document.createElement('i'); - icon.classList.add('fas', 'fa-check'); // Use Font Awesome checkmark icon - icon.style.fontSize = '24px'; // Set font size to 24 pixels - icon.style.transform = 'scale(2)'; // Scale icon to twice its - icon.style.marginLeft='-92px' + const icon = iconMaker(true) // Append the icon to the element element.querySelector('div.custom-block-body').appendChild(icon); From 4e369a11dd96a5d380c1cd161e5343af08d14abb Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Thu, 20 Apr 2023 16:12:06 +0200 Subject: [PATCH 46/78] used mathjax for a good display of formula --- templates/misc/quizz.graph.part.html | 20 ++++++- templates/tutorialv2/stats/quizz_stats.html | 64 +++++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index a382902a8b..655783a162 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -1,17 +1,21 @@ {% load i18n %} - + +
    + + {% for quizz_name, questions in quizz.items %}

    {{ quizz_name }}

    +
    {% for question, stats in questions.items %}
    -

    {% trans graph_title %} {{ question }}

    +

    {% trans graph_title %} {{ question }}

      {% for answer_name, answer_stats in stats.responses.items %}
    • - {{ answer_name }} + {{ answer_name }} @@ -24,3 +28,13 @@

      {% trans graph_title %} {{ question }}

      {% endfor %} {% endfor %}
    + + + diff --git a/templates/tutorialv2/stats/quizz_stats.html b/templates/tutorialv2/stats/quizz_stats.html index cdcf21a07d..101e53152c 100644 --- a/templates/tutorialv2/stats/quizz_stats.html +++ b/templates/tutorialv2/stats/quizz_stats.html @@ -32,6 +32,70 @@

    Type de graphe

    +

    {% trans "Échelle de temps" %}

    + {% endblock sidebar %} \ No newline at end of file From 50bd56c46b9f9b6870f8f1b65a416ee4f376687e Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sat, 22 Apr 2023 19:08:20 +0200 Subject: [PATCH 47/78] fixed issue of bad display in quizz statistics --- assets/js/content-quizz.js | 58 +++++++------------ templates/misc/quizz.graph.part.html | 20 +------ templates/tutorialv2/stats/quizz_stats.html | 64 --------------------- 3 files changed, 24 insertions(+), 118 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 17a0522e66..85223b7a33 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -73,16 +73,17 @@ function initializeCheckboxes(answers) { // add explanation to all questions document.querySelectorAll('div.quizz').forEach(quizz => { - const quizzDivs = quizz.querySelectorAll('div.custom-block-quizz'); + const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); quizzDivs.forEach(quizzDiv => { const ul = quizzDiv.querySelector('ul') const lastLi = ul.lastElementChild - const explanationText = 'Explication :' + lastLi.innerText + const explanationText = lastLi.innerText + const explanation = document.createElement('div') explanation.classList.add('explanation_off') - explanation.innerHTML = explanationText + explanation.innerText = explanationText lastLi.parentNode.removeChild(lastLi); - quizzDiv.querySelector('div.custom-block-body').appendChild(explanation) + quizzDiv.appendChild(explanation) }); }) @@ -153,8 +154,6 @@ function markBadAnswers(form, names, answers) { const divquizz = form.querySelector(`div[data-name="${answer}"]`) if (!AnsweredWell) { divquizz.classList.add('quizz-bad') - const icon = iconMaker(false) - divquizz.querySelector('div.custom-block-body').appendChild(icon); } else { divquizz.classList.add('quizz-good') } @@ -176,13 +175,7 @@ function markBadAnswers(form, names, answers) { }) const divquizz = form.querySelector(`.custom-block[data-name=${name}]`) - if (!divquizz.classList.contains('quizz-good')) { - - divquizz.classList.add('quizz-bad') - // Create a new icon element - const icon = iconMaker(false) - divquizz.querySelector('div.custom-block-body').appendChild(icon); - } + if (!divquizz.classList.contains('quizz-good')) divquizz.classList.add('quizz-bad'); }) names.forEach(({ @@ -208,6 +201,15 @@ function getWantedHeading(questionNode, nodeName, attr) { return potentialHeading } + +function generateUUID() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = Math.random() * 16 | 0; + const v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} + /** */ @@ -258,12 +260,14 @@ function injectForms(quizz, answers) { submit.classList.add('btn', 'btn-submit') - submit.setAttribute('id', `my-button-${idButton}`); - idButton++ + submit.setAttribute('id', `my-button-${generateUUID()}`); + const notAnswered = document.createElement('p') notAnswered.classList.add('notAnswered') + + let nodeToAddToForm if (heading === quizz) { @@ -282,7 +286,7 @@ function injectForms(quizz, answers) { current.parentNode.removeChild(current) } - + form.method = 'POST' form.setAttribute('action', quizz.getAttribute('data-answer-url')) form.setAttribute('id', `my-form-${idCounter}`); @@ -329,11 +333,6 @@ initializePipeline.forEach(func => func(answers)) let idCounter = 0 let idBias = 0 - -let idButton = 0; - -// parseInt(sessionStorage.getItem('idButton')) || - document.querySelectorAll('div.quizz').forEach(div => { const quizInside = div.querySelector('.custom-block-quizz ul li input[type=checkbox]'); @@ -393,16 +392,7 @@ function QuizzAnswered(form) { return true } -function iconMaker(isGood){ - // Create a new icon element - const icon = document.createElement('i'); - icon.classList.add('fas'); - isGood ? icon.classList.add('fa-check') : icon.classList.add('fa-exclamation-triangle') - icon.style.fontSize = '24px'; - icon.style.transform = 'scale(2)'; - icon.style.marginLeft='-92px' - return icon -} + document.querySelectorAll('form.quizz').forEach(form => { @@ -495,12 +485,6 @@ document.querySelectorAll('form.quizz').forEach(form => { if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { element.classList.add('quizz-good') - - const icon = iconMaker(true) - // Append the icon to the element - element.querySelector('div.custom-block-body').appendChild(icon); - - statistics.result[title].evaluation = 'ok' } diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index 655783a162..a382902a8b 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -1,21 +1,17 @@ {% load i18n %} - - -
    - +
    {% for quizz_name, questions in quizz.items %}

    {{ quizz_name }}

    -
    {% for question, stats in questions.items %}
    -

    {% trans graph_title %} {{ question }}

    +

    {% trans graph_title %} {{ question }}

      {% for answer_name, answer_stats in stats.responses.items %}
    • - {{ answer_name }} + {{ answer_name }} @@ -28,13 +24,3 @@

      {% trans graph_title %} {{ question }}

      {% endfor %} {% endfor %}
    - - - diff --git a/templates/tutorialv2/stats/quizz_stats.html b/templates/tutorialv2/stats/quizz_stats.html index 101e53152c..cdcf21a07d 100644 --- a/templates/tutorialv2/stats/quizz_stats.html +++ b/templates/tutorialv2/stats/quizz_stats.html @@ -32,70 +32,6 @@

    Type de graphe

    -

    {% trans "Échelle de temps" %}

    -
    {% endblock sidebar %} \ No newline at end of file From 60edb12faf87b8c0d8fca70adf34e72c1dba99f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?FACEN=20th=C3=A9o?= Date: Sun, 23 Apr 2023 18:42:00 +0200 Subject: [PATCH 48/78] rajout chemin pour titre dans statistique des quizzs --- zds/tutorialv2/views/statistics.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index af3afe1e70..9c1187773e 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -356,7 +356,11 @@ def build_quizz_stats(self, end_date, start_date): .all() ): full_answers_total[available_answer.label] = {"good": available_answer.is_good, "nb": 0} - name = available_answer.related_question.url.split("#")[-1] + name = ( + available_answer.related_question.url.split("/")[-2] + + "/" + + available_answer.related_question.url.split("/")[-1].split("#")[-1] + ) question = available_answer.related_question.question for r in total_per_label: if ( From d265bace208f7d4b1786e54d45d88d4e70b9bd7f Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 23 Apr 2023 20:36:20 +0200 Subject: [PATCH 49/78] changed display of explication --- assets/js/content-quizz.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 85223b7a33..56403768a3 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -77,18 +77,17 @@ function initializeCheckboxes(answers) { quizzDivs.forEach(quizzDiv => { const ul = quizzDiv.querySelector('ul') const lastLi = ul.lastElementChild - const explanationText = lastLi.innerText + const explanationText = 'Explication : ' + lastLi.innerText const explanation = document.createElement('div') explanation.classList.add('explanation_off') - explanation.innerText = explanationText + explanation.innerHTML = explanationText lastLi.parentNode.removeChild(lastLi); - quizzDiv.appendChild(explanation) + quizzDiv.querySelector('div.custom-block-body').appendChild(explanation) }); }) - const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') extractAnswer(checkboxes, answers) From eee00467be91e7f0aee120590d5fb78cc5f22e7d Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 17 Apr 2023 13:25:21 +0200 Subject: [PATCH 50/78] fix explanation dislay --- assets/js/content-quizz.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 85223b7a33..314371290c 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -73,17 +73,16 @@ function initializeCheckboxes(answers) { // add explanation to all questions document.querySelectorAll('div.quizz').forEach(quizz => { - const quizzDivs = quizz.querySelectorAll('div.custom-block.custom-block-quizz'); + const quizzDivs = quizz.querySelectorAll('div.custom-block-quizz'); quizzDivs.forEach(quizzDiv => { const ul = quizzDiv.querySelector('ul') const lastLi = ul.lastElementChild - const explanationText = lastLi.innerText - + const explanationText = 'Explication :' + lastLi.innerText const explanation = document.createElement('div') explanation.classList.add('explanation_off') - explanation.innerText = explanationText + explanation.innerHTML = explanationText lastLi.parentNode.removeChild(lastLi); - quizzDiv.appendChild(explanation) + quizzDiv.querySelector('div.custom-block-body').appendChild(explanation) }); }) @@ -260,14 +259,12 @@ function injectForms(quizz, answers) { submit.classList.add('btn', 'btn-submit') - submit.setAttribute('id', `my-button-${generateUUID()}`); - + submit.setAttribute('id', `my-button-${idButton}`); + idButton++ const notAnswered = document.createElement('p') notAnswered.classList.add('notAnswered') - - let nodeToAddToForm if (heading === quizz) { @@ -286,7 +283,7 @@ function injectForms(quizz, answers) { current.parentNode.removeChild(current) } - + form.method = 'POST' form.setAttribute('action', quizz.getAttribute('data-answer-url')) form.setAttribute('id', `my-form-${idCounter}`); @@ -333,6 +330,11 @@ initializePipeline.forEach(func => func(answers)) let idCounter = 0 let idBias = 0 + +let idButton = 0; + +// parseInt(sessionStorage.getItem('idButton')) || + document.querySelectorAll('div.quizz').forEach(div => { const quizInside = div.querySelector('.custom-block-quizz ul li input[type=checkbox]'); From a07fb125a1de6b5f848e0bc133af35594ae3e108 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 17 Apr 2023 17:13:41 +0200 Subject: [PATCH 51/78] added icons for a best result display for daltoniens --- assets/js/content-quizz.js | 40 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 314371290c..9a1780f522 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -153,6 +153,13 @@ function markBadAnswers(form, names, answers) { const divquizz = form.querySelector(`div[data-name="${answer}"]`) if (!AnsweredWell) { divquizz.classList.add('quizz-bad') + const icon = document.createElement('i'); + icon.classList.add('fas', 'fa-exclamation-triangle'); + icon.style.fontSize = '24px'; + icon.style.transform = 'scale(2)'; + icon.style.marginLeft='-92px' + // Append the icon to the element + divquizz.querySelector('div.custom-block-body').appendChild(icon); } else { divquizz.classList.add('quizz-good') } @@ -174,7 +181,18 @@ function markBadAnswers(form, names, answers) { }) const divquizz = form.querySelector(`.custom-block[data-name=${name}]`) - if (!divquizz.classList.contains('quizz-good')) divquizz.classList.add('quizz-bad'); + if (!divquizz.classList.contains('quizz-good')) { + + divquizz.classList.add('quizz-bad') + // Create a new icon element + const icon = document.createElement('i'); + icon.classList.add('fas', 'fa-exclamation-triangle'); + icon.style.fontSize = '24px'; + icon.style.transform = 'scale(2)'; + icon.style.marginLeft='-92px' + // Append the icon to the element + divquizz.querySelector('div.custom-block-body').appendChild(icon); + } }) names.forEach(({ @@ -200,15 +218,6 @@ function getWantedHeading(questionNode, nodeName, attr) { return potentialHeading } - -function generateUUID() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - const r = Math.random() * 16 | 0; - const v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); -} - /** */ @@ -487,6 +496,17 @@ document.querySelectorAll('form.quizz').forEach(form => { if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { element.classList.add('quizz-good') + + // Create a new icon element + const icon = document.createElement('i'); + icon.classList.add('fas', 'fa-check'); // Use Font Awesome checkmark icon + icon.style.fontSize = '24px'; // Set font size to 24 pixels + icon.style.transform = 'scale(2)'; // Scale icon to twice its + icon.style.marginLeft='-92px' + // Append the icon to the element + element.querySelector('div.custom-block-body').appendChild(icon); + + statistics.result[title].evaluation = 'ok' } From e20561593296a7d0c65535d57a1442b3f1c59127 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Tue, 18 Apr 2023 16:25:34 +0200 Subject: [PATCH 52/78] factorisaton of icon code --- assets/js/content-quizz.js | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 9a1780f522..17a0522e66 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -153,12 +153,7 @@ function markBadAnswers(form, names, answers) { const divquizz = form.querySelector(`div[data-name="${answer}"]`) if (!AnsweredWell) { divquizz.classList.add('quizz-bad') - const icon = document.createElement('i'); - icon.classList.add('fas', 'fa-exclamation-triangle'); - icon.style.fontSize = '24px'; - icon.style.transform = 'scale(2)'; - icon.style.marginLeft='-92px' - // Append the icon to the element + const icon = iconMaker(false) divquizz.querySelector('div.custom-block-body').appendChild(icon); } else { divquizz.classList.add('quizz-good') @@ -185,12 +180,7 @@ function markBadAnswers(form, names, answers) { divquizz.classList.add('quizz-bad') // Create a new icon element - const icon = document.createElement('i'); - icon.classList.add('fas', 'fa-exclamation-triangle'); - icon.style.fontSize = '24px'; - icon.style.transform = 'scale(2)'; - icon.style.marginLeft='-92px' - // Append the icon to the element + const icon = iconMaker(false) divquizz.querySelector('div.custom-block-body').appendChild(icon); } }) @@ -403,7 +393,16 @@ function QuizzAnswered(form) { return true } - +function iconMaker(isGood){ + // Create a new icon element + const icon = document.createElement('i'); + icon.classList.add('fas'); + isGood ? icon.classList.add('fa-check') : icon.classList.add('fa-exclamation-triangle') + icon.style.fontSize = '24px'; + icon.style.transform = 'scale(2)'; + icon.style.marginLeft='-92px' + return icon +} document.querySelectorAll('form.quizz').forEach(form => { @@ -497,12 +496,7 @@ document.querySelectorAll('form.quizz').forEach(form => { element.classList.add('quizz-good') - // Create a new icon element - const icon = document.createElement('i'); - icon.classList.add('fas', 'fa-check'); // Use Font Awesome checkmark icon - icon.style.fontSize = '24px'; // Set font size to 24 pixels - icon.style.transform = 'scale(2)'; // Scale icon to twice its - icon.style.marginLeft='-92px' + const icon = iconMaker(true) // Append the icon to the element element.querySelector('div.custom-block-body').appendChild(icon); From 32a0fef51152a3b38052c6314ed15a4875836c23 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Thu, 20 Apr 2023 16:12:06 +0200 Subject: [PATCH 53/78] used mathjax for a good display of formula --- templates/misc/quizz.graph.part.html | 20 ++++++- templates/tutorialv2/stats/quizz_stats.html | 64 +++++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index a382902a8b..655783a162 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -1,17 +1,21 @@ {% load i18n %} - + +
    + + {% for quizz_name, questions in quizz.items %}

    {{ quizz_name }}

    +
    {% for question, stats in questions.items %}
    -

    {% trans graph_title %} {{ question }}

    +

    {% trans graph_title %} {{ question }}

      {% for answer_name, answer_stats in stats.responses.items %}
    • - {{ answer_name }} + {{ answer_name }} @@ -24,3 +28,13 @@

      {% trans graph_title %} {{ question }}

      {% endfor %} {% endfor %}
    + + + diff --git a/templates/tutorialv2/stats/quizz_stats.html b/templates/tutorialv2/stats/quizz_stats.html index cdcf21a07d..101e53152c 100644 --- a/templates/tutorialv2/stats/quizz_stats.html +++ b/templates/tutorialv2/stats/quizz_stats.html @@ -32,6 +32,70 @@

    Type de graphe

    +

    {% trans "Échelle de temps" %}

    + {% endblock sidebar %} \ No newline at end of file From 51d951e990e172b990e851ce556c7e7084666c58 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 23 Apr 2023 22:56:49 +0200 Subject: [PATCH 54/78] fixed content of statistics sent from client --- assets/js/content-quizz.js | 49 ++++++++++++++++++++-------- templates/misc/quizz.graph.part.html | 11 +++++-- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 17a0522e66..4a9d1693e4 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -353,6 +353,7 @@ function sendQuizzStatistics(form, statistics) { xhttp.setRequestHeader('Content-Type', 'application/json') xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href + console.log(statistics); xhttp.send(JSON.stringify(statistics)) } @@ -404,6 +405,34 @@ function iconMaker(isGood){ return icon } + +function getQuestionText(question){ + + let title + if (question.querySelector('.math')) { + const annotation = question.querySelector('.math annotation'); + title = annotation.textContent.trim(); + } + else { + title = question.textContent; + } + return title +} + + +function getAnswerText(liWrapper){ + + const mathElement = liWrapper.querySelector('span.math'); + let answer; + if (mathElement) { + const annotationElement = mathElement.querySelector('annotation'); + answer = annotationElement.textContent.trim(); + } else { + answer = liWrapper.textContent; + } + return answer +} + document.querySelectorAll('form.quizz').forEach(form => { form.addEventListener('submit', e => { @@ -455,11 +484,9 @@ document.querySelectorAll('form.quizz').forEach(form => { Object.keys(answers).forEach(name => { const element = document.querySelector(`.custom-block[data-name="${name}"]`) - let title = element.querySelector('.custom-block-heading').textContent - const correction = element.querySelector('.custom-block-body .custom-block') - if (correction && title.indexOf(correction.textContent) > 0) { - title = title.substr(0, title.indexOf(correction.textContent)) - } + + let title = getQuestionText(element.querySelector('.custom-block-heading')) + statistics.result[title] = { evaluation: 'bad', labels: [] @@ -468,7 +495,7 @@ document.querySelectorAll('form.quizz').forEach(form => { //make statistics of concerned form only - const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => question.textContent); + const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => getQuestionText(question)); if (CurrentFormQuestions.includes(title)) { @@ -476,10 +503,8 @@ document.querySelectorAll('form.quizz').forEach(form => { for (let i = 0; i < availableResponses.length; i++) { // wee need to get the question label for statistics const liWrapper = availableResponses[i].parentElement - let questionLabel = liWrapper.textContent - if (correction && questionLabel.indexOf(correction.textContent) !== -1) { - questionLabel = questionLabel.substring(0, questionLabel.indexOf(correction.textContent)) - } + let questionLabel = getAnswerText(liWrapper) + statistics.expected[title][questionLabel] = answers[name][i] } // now determine answers and their labels @@ -487,9 +512,7 @@ document.querySelectorAll('form.quizz').forEach(form => { // remove eventual glued corretion let label = node.parentElement.textContent - if (correction && label.indexOf(correction.textContent) !== -1) { - label = label.substr(0, label.indexOf(correction.textContent)) - } + statistics.result[title].labels.push(label.trim()) }) if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index 655783a162..e8366f7fda 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -31,10 +31,17 @@

    {% trans graph_title %} {{ question }}

    From 6234832ae6aa91380eeced9cd41308f0ad012d97 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Tue, 25 Apr 2023 10:09:45 +0200 Subject: [PATCH 55/78] quizzs works only on turorials pages --- assets/js/content-quizz.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 16bb2a4ba4..9e717d1ff0 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -1,3 +1,8 @@ +var currentURL = window.location.href; + +if (currentURL.includes("/tutoriels/")) { + + // to handle a new type of answer, you just need to create a method called // initializeXXX(answers) that will add the reset the field content and mark good answer // then add the two methods in the callback lists @@ -401,7 +406,7 @@ function iconMaker(isGood){ isGood ? icon.classList.add('fa-check') : icon.classList.add('fa-exclamation-triangle') icon.style.fontSize = '24px'; icon.style.transform = 'scale(2)'; - icon.style.marginLeft='-92px' + icon.style.marginLeft='700px' return icon } @@ -547,5 +552,4 @@ document.querySelectorAll('form.quizz').forEach(form => { }) - - +} \ No newline at end of file From eed909a504cbb26f22eb8320a6bb93b67b168d3c Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Thu, 27 Apr 2023 20:20:51 +0200 Subject: [PATCH 56/78] in one session a non connected user can answers only one time --- assets/js/content-quizz.js | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 9e717d1ff0..b93e30b119 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -2,7 +2,6 @@ var currentURL = window.location.href; if (currentURL.includes("/tutoriels/")) { - // to handle a new type of answer, you just need to create a method called // initializeXXX(answers) that will add the reset the field content and mark good answer // then add the two methods in the callback lists @@ -262,9 +261,12 @@ function injectForms(quizz, answers) { submit.style.marginTop = '-22px' submit.classList.add('btn', 'btn-submit') - + + var parts = quizz.previousElementSibling.firstElementChild.href.split("/"); + var idButton = parts[parts.length - 4] + "/" + parts[parts.length - 3] + "/" + parts[parts.length - 2] + "/" + parts[parts.length - 1]; + submit.setAttribute('id', `my-button-${idButton}`); - idButton++ + const notAnswered = document.createElement('p') notAnswered.classList.add('notAnswered') @@ -334,11 +336,6 @@ initializePipeline.forEach(func => func(answers)) let idCounter = 0 let idBias = 0 - -let idButton = 0; - -// parseInt(sessionStorage.getItem('idButton')) || - document.querySelectorAll('div.quizz').forEach(div => { const quizInside = div.querySelector('.custom-block-quizz ul li input[type=checkbox]'); @@ -358,7 +355,6 @@ function sendQuizzStatistics(form, statistics) { xhttp.setRequestHeader('Content-Type', 'application/json') xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href - console.log(statistics); xhttp.send(JSON.stringify(statistics)) } @@ -456,7 +452,7 @@ document.querySelectorAll('form.quizz').forEach(form => { } else { // If the quiz has already been submitted, disable the submit button submitBtn.setAttribute('disabled', true); - alert('Vous avez deja repondu, Veuillez se connecter'); + alert('Vous avez deja répondu, Veuillez se connecter'); return; } } @@ -527,8 +523,6 @@ document.querySelectorAll('form.quizz').forEach(form => { const icon = iconMaker(true) // Append the icon to the element element.querySelector('div.custom-block-body').appendChild(icon); - - statistics.result[title].evaluation = 'ok' } @@ -536,7 +530,6 @@ document.querySelectorAll('form.quizz').forEach(form => { }) if (document.querySelector('input[name=\'csrfmiddlewaretoken\']')) { - sendQuizzStatistics(form, statistics) } submitBtn.setAttribute('disabled', true); From 0ac7e4de0cb5ff26a3417905a221d14647b03708 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sat, 29 Apr 2023 01:35:02 +0200 Subject: [PATCH 57/78] good display of statistics, possibility to delete quizzs and questions , --- assets/js/content-quizz.js | 22 ++-- templates/misc/quizz.graph.part.html | 172 +++++++++++++++++++++------ zds/tutorialv2/urls/urls_contents.py | 8 +- zds/tutorialv2/views/statistics.py | 83 +++++++++++-- 4 files changed, 230 insertions(+), 55 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index b93e30b119..a0d2499ef5 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -412,7 +412,7 @@ function getQuestionText(question){ let title if (question.querySelector('.math')) { const annotation = question.querySelector('.math annotation'); - title = annotation.textContent.trim(); + title = '$'+annotation.textContent.trim()+'$'; } else { title = question.textContent; @@ -427,7 +427,7 @@ function getAnswerText(liWrapper){ let answer; if (mathElement) { const annotationElement = mathElement.querySelector('annotation'); - answer = annotationElement.textContent.trim(); + answer = '$'+annotationElement.textContent.trim()+'$'; } else { answer = liWrapper.textContent; } @@ -482,24 +482,24 @@ document.querySelectorAll('form.quizz').forEach(form => { result: {} } + const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => getQuestionText(question)); + Object.keys(answers).forEach(name => { const element = document.querySelector(`.custom-block[data-name="${name}"]`) let title = getQuestionText(element.querySelector('.custom-block-heading')) - statistics.result[title] = { - evaluation: 'bad', - labels: [] - } - statistics.expected[title] = {} //make statistics of concerned form only - - const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => getQuestionText(question)); - if (CurrentFormQuestions.includes(title)) { + statistics.result[title] = { + evaluation: 'bad', + labels: [] + } + statistics.expected[title] = {} + const availableResponses = element.querySelectorAll('input') for (let i = 0; i < availableResponses.length; i++) { // wee need to get the question label for statistics @@ -512,7 +512,7 @@ document.querySelectorAll('form.quizz').forEach(form => { element.querySelectorAll('input:checked').forEach(node => { // remove eventual glued corretion - let label = node.parentElement.textContent + let label = getAnswerText(node.parentElement) statistics.result[title].labels.push(label.trim()) }) diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index e8366f7fda..c6037ae6f8 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -1,47 +1,151 @@ {% load i18n %} - - +{% load emarkdown%} + +
    - {% for quizz_name, questions in quizz.items %} -
    -

    {{ quizz_name }}

    - -
    - {% for question, stats in questions.items %} -
    -

    {% trans graph_title %} {{ question }}

    -
      - {% for answer_name, answer_stats in stats.responses.items %} -
    • - {{ answer_name }} - - - - {{answer_stats.nb}} / {{ stats.total }} -
    • - {% endfor %} -
    -
    - {% endfor %} -{% endfor %} +
    +

    {{ quizz_name }}

    + +
    + {% for question, stats in questions.items %} +
    +

    {% trans graph_title %} {{ question|emarkdown }}

    + +
      + {% for answer_name, answer_stats in stats.responses.items %} +
    • + {{ answer_name|emarkdown }} + + + + {{answer_stats.nb}} / {{ stats.total }} +
    • + {% endfor %} +
    +
    + {% endfor %} + {% endfor %}
    + \ No newline at end of file diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index 233ef217ca..c653f8f45c 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -37,7 +37,12 @@ from zds.tutorialv2.views.lists import TagsListView, ContentOfAuthor, ListContentReactions from zds.tutorialv2.views.alerts import SendContentAlert, SolveContentAlert from zds.tutorialv2.views.misc import RequestFeaturedContent, FollowNewContent, WarnTypo -from zds.tutorialv2.views.statistics import ContentStatisticsView, ContentQuizzStatistics, QuizzContentStatistics +from zds.tutorialv2.views.statistics import ( + ContentStatisticsView, + ContentQuizzStatistics, + DeleteQuizz, + QuizzContentStatistics, +) from zds.tutorialv2.views.comments import ( SendNoteFormView, UpdateNoteView, @@ -209,4 +214,5 @@ # quizz path("reponses///", ContentQuizzStatistics.as_view(), name="answer-quizz"), path("stats_quizz///", QuizzContentStatistics.as_view(), name="stats-quizz"), + path("delete_quizz/", DeleteQuizz.as_view(), name="delete_quizz"), ] diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index 9c1187773e..14355378ee 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -1,24 +1,29 @@ import itertools +import json import uuid from collections import OrderedDict, Counter import logging import urllib.parse from datetime import timedelta, datetime, date from json import loads, dumps - +from django.shortcuts import get_object_or_404, redirect +from django.views import View +from django.db.models import Subquery import requests from django.conf import settings from django.contrib import messages from django.core.exceptions import PermissionDenied from django.db.models import Count from django.forms.utils import ErrorDict -from django.http import StreamingHttpResponse +from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse, StreamingHttpResponse from django.utils.translation import gettext_lazy as _ -from django.views.generic import FormView +from django.views.generic import FormView, DeleteView +from zds.member.decorator import LoggedWithReadWriteHability from zds.tutorialv2.forms import ContentCompareStatsURLForm, QuizzStatsForm from zds.tutorialv2.mixins import ( SingleContentDetailViewMixin, + SingleContentViewMixin, SingleOnlineContentDetailViewMixin, SingleOnlineContentFormViewMixin, ) @@ -356,11 +361,7 @@ def build_quizz_stats(self, end_date, start_date): .all() ): full_answers_total[available_answer.label] = {"good": available_answer.is_good, "nb": 0} - name = ( - available_answer.related_question.url.split("/")[-2] - + "/" - + available_answer.related_question.url.split("/")[-1].split("#")[-1] - ) + name = available_answer.related_question.url question = available_answer.related_question.question for r in total_per_label: if ( @@ -371,8 +372,72 @@ def build_quizz_stats(self, end_date, start_date): if name not in quizz_stats: quizz_stats[name] = OrderedDict() quizz_stats[name][question] = {"total": total_per_question[base_question], "responses": full_answers_total} - return quizz_stats + sorted_quizz_stats = {} + for name in sorted(quizz_stats.keys()): + sorted_quizz_stats[name] = quizz_stats[name] + return sorted_quizz_stats class QuizzContentStatistics(ContentStatisticsView): template_name = "tutorialv2/stats/quizz_stats.html" + + +class DeleteQuizz(View): + def get_start_and_end_dates(self): + end_date = self.request.GET.get("end_date", None) + try: + end_date = datetime.strptime(end_date, "%Y-%m-%d").date() + except TypeError: + end_date = date.today() + except ValueError: + end_date = date.today() + messages.error(self.request, _("La date de fin fournie est invalide.")) + + start_date = self.request.GET.get("start_date", None) + try: + start_date = datetime.strptime(start_date, "%Y-%m-%d").date() + except TypeError: + start_date = end_date - timedelta(days=7) + except ValueError: + start_date = end_date - timedelta(days=7) + messages.error(self.request, _("La date de début fournie est invalide.")) + + if start_date > end_date: + end_date, start_date = start_date, end_date + + return start_date, end_date + + def post(self, request): + + start_date, end_date = self.get_start_and_end_dates() + + data = json.loads(request.body) + + # Extract the quizzName from the data + quizz_name = data.get("quizzName") + question = data.get("question") + + if question: + related_question_ids = QuizzQuestion.objects.filter(url=quizz_name, question=question).values_list( + "id", flat=True + ) + else: + related_question_ids = QuizzQuestion.objects.filter(url=quizz_name).values_list("id", flat=True) + + try: + QuizzUserAnswer.objects.filter( + related_question_id__in=Subquery(related_question_ids), date_answer__range=(start_date, end_date) + ).delete() + except Exception as e: + return HttpResponseBadRequest(f"An error occurred while deleting the quiz: {str(e)}") + + # Delete all QuizzUserAnswer objects + QuizzUserAnswer.objects.all().delete() + + # Delete all QuizzAvailableAnswer objects + QuizzAvailableAnswer.objects.all().delete() + + # Delete all QuizzQuestion objects + QuizzQuestion.objects.all().delete() + + return StreamingHttpResponse(dumps({"status": "ok"})) From 3512eb3157fa5a51327ea83fd547a353ce6b1692 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sat, 29 Apr 2023 01:35:51 +0200 Subject: [PATCH 58/78] quick fix --- zds/tutorialv2/views/statistics.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index 14355378ee..f58ce67bda 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -431,13 +431,4 @@ def post(self, request): except Exception as e: return HttpResponseBadRequest(f"An error occurred while deleting the quiz: {str(e)}") - # Delete all QuizzUserAnswer objects - QuizzUserAnswer.objects.all().delete() - - # Delete all QuizzAvailableAnswer objects - QuizzAvailableAnswer.objects.all().delete() - - # Delete all QuizzQuestion objects - QuizzQuestion.objects.all().delete() - return StreamingHttpResponse(dumps({"status": "ok"})) From 33243c09d300cc01a7e4b44e03608746f4eda2a9 Mon Sep 17 00:00:00 2001 From: Alexandre Bertin Date: Sat, 29 Apr 2023 11:36:31 +0200 Subject: [PATCH 59/78] correction faute de francais --- assets/js/content-quizz.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index a0d2499ef5..7fb2141b35 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -452,7 +452,7 @@ document.querySelectorAll('form.quizz').forEach(form => { } else { // If the quiz has already been submitted, disable the submit button submitBtn.setAttribute('disabled', true); - alert('Vous avez deja répondu, Veuillez se connecter'); + alert('Vous avez deja répondu, Veuillez vous connecter'); return; } } From 6ed7cffd38321b642569e6896df33ba75d5ea2a9 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 30 Apr 2023 15:12:57 +0200 Subject: [PATCH 60/78] fixed issues on authors statistics page --- assets/js/author_delete_stats.js | 48 ++ assets/js/content-quizz.js | 819 +++++++++++++-------------- assets/scss/pages/_stats.scss | 184 +++--- templates/misc/quizz.graph.part.html | 136 +---- zds/tutorialv2/views/statistics.py | 76 +-- 5 files changed, 599 insertions(+), 664 deletions(-) create mode 100644 assets/js/author_delete_stats.js diff --git a/assets/js/author_delete_stats.js b/assets/js/author_delete_stats.js new file mode 100644 index 0000000000..65655e71e6 --- /dev/null +++ b/assets/js/author_delete_stats.js @@ -0,0 +1,48 @@ +/* ===== Zeste de Savoir ==================================================== + Nom du fichier : author_stats.js + Description : Script JS pour supprimer les statistiques des quizzs par l'auteur ou staff + Auteur : Atman BOZ + ========================================================================== */ + + +$('.quizz_name').each(function () { + var quizzName = $(this).text(); + quizzName = quizzName.replace("#", ""); + var parts = quizzName.split("/"); + var statsPart = parts[parts.length - 2] + "/" + parts[parts.length - 1]; + $(this).text(statsPart); +}); + + +$(document).ready(function () { + // Add an event listener to the quizz delete button + $(".delete-quizz-button").click(function () { + var quizzName = $(this).attr('data-quizz'); + deleteQuizz(quizzName, ''); + }); + + // Add an event listener to the question delete button + $(".delete-question-button").click(function () { + var quizzName = $(this).attr('data-quizz'); + var question = $(this).attr('data-question'); + deleteQuizz(quizzName, question); + }); + +}); + +function deleteQuizz(quizzName, question) { + const csrfmiddlewaretoken = $("input[name='csrfmiddlewaretoken']").val(); + $.ajax({ + url: '/contenus/delete_quizz/', + type: 'POST', + data: question ? JSON.stringify({ 'quizzName': quizzName, 'question': question }) : JSON.stringify({ 'quizzName': quizzName }), + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Content-Type': 'application/json', + 'X-CSRFToken': csrfmiddlewaretoken + }, + success: function (response) { + location.reload(); + } + }); +} diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index a0d2499ef5..dc97e00529 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -2,547 +2,546 @@ var currentURL = window.location.href; if (currentURL.includes("/tutoriels/")) { -// to handle a new type of answer, you just need to create a method called -// initializeXXX(answers) that will add the reset the field content and mark good answer -// then add the two methods in the callback lists + // to handle a new type of answer, you just need to create a method called + // initializeXXX(answers) that will add the reset the field content and mark good answer + // then add the two methods in the callback lists -let index = 0 + let index = 0 -function extractAnswer(inputDomElementList, answers) { - - inputDomElementList.forEach((rb) => { + function extractAnswer(inputDomElementList, answers) { - const ulWrapperElement = rb.parentElement.parentElement - // we give the ui an id to find the element in a more effective way later when the users answer the questions - if (!ulWrapperElement.getAttribute('id')) { - ulWrapperElement.setAttribute('id', 'id-' + (index++)) - } + inputDomElementList.forEach((rb) => { - rb.setAttribute('name', ulWrapperElement.getAttribute('id')) + const ulWrapperElement = rb.parentElement.parentElement + // we give the ui an id to find the element in a more effective way later when the users answer the questions + if (!ulWrapperElement.getAttribute('id')) { + ulWrapperElement.setAttribute('id', 'id-' + (index++)) + } - const questionBlock = ulWrapperElement.parentElement.parentElement - questionBlock.setAttribute('data-name', rb.getAttribute('name')) - if (!answers[ulWrapperElement.getAttribute('id')]) { - answers[ulWrapperElement.getAttribute('id')] = [rb.checked] - } else { - answers[ulWrapperElement.getAttribute('id')].push(rb.checked) - } - rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) - rb.disabled = false - rb.checked = false - }) -} - -/** - * The full quizz is contained in a div or article that has class "quizz". - * Then one question is inside a zmarkdown "custom-block" of type "custom-block-quizz". Two possibilities : - * - * Without explanation for correction : - * - * - *
    - *
    The question
    - *
    - *
    • the answer
    • - *
    • the good answer
    • - *
    - *
    - *
    - *
    - * - * With an explanation inside another custom block most of time a custom-block-neutral - * - * - *
    - *
    The question
    - *
    - *
    • the answer
    • - *
    • the good answer - * - *
      - *
      Explanation
      - *
      a formatted text
      - *
    • - * - *
    - *
    - *
    - *
    - * - * Note that the correction MAY be inside the last li due to the way custom-block plugin works, this is not a bug - * - * @param answers the answer dictionary, it will be modified by the process - */ -function initializeCheckboxes(answers) { - - // add explanation to all questions - document.querySelectorAll('div.quizz').forEach(quizz => { - const quizzDivs = quizz.querySelectorAll('div.custom-block-quizz'); - quizzDivs.forEach(quizzDiv => { - const ul = quizzDiv.querySelector('ul') - const lastLi = ul.lastElementChild - const explanationText = 'Explication : ' + lastLi.innerText - - const explanation = document.createElement('div') - explanation.classList.add('explanation_off') - explanation.innerHTML = explanationText - lastLi.parentNode.removeChild(lastLi); - quizzDiv.querySelector('div.custom-block-body').appendChild(explanation) - - }); - }) + rb.setAttribute('name', ulWrapperElement.getAttribute('id')) - const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') - extractAnswer(checkboxes, answers) + const questionBlock = ulWrapperElement.parentElement.parentElement + questionBlock.setAttribute('data-name', rb.getAttribute('name')) + if (!answers[ulWrapperElement.getAttribute('id')]) { + answers[ulWrapperElement.getAttribute('id')] = [rb.checked] + } else { + answers[ulWrapperElement.getAttribute('id')].push(rb.checked) + } + rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) + rb.disabled = false + rb.checked = false + }) + } -} + /** + * The full quizz is contained in a div or article that has class "quizz". + * Then one question is inside a zmarkdown "custom-block" of type "custom-block-quizz". Two possibilities : + * + * Without explanation for correction : + * + * + *
    + *
    The question
    + *
    + *
    • the answer
    • + *
    • the good answer
    • + *
    + *
    + *
    + *
    + * + * With an explanation inside another custom block most of time a custom-block-neutral + * + * + *
    + *
    The question
    + *
    + *
    • the answer
    • + *
    • the good answer + * + *
      + *
      Explanation
      + *
      a formatted text
      + *
    • + * + *
    + *
    + *
    + *
    + * + * Note that the correction MAY be inside the last li due to the way custom-block plugin works, this is not a bug + * + * @param answers the answer dictionary, it will be modified by the process + */ + function initializeCheckboxes(answers) { + + // add explanation to all questions + document.querySelectorAll('div.quizz').forEach(quizz => { + const quizzDivs = quizz.querySelectorAll('div.custom-block-quizz'); + quizzDivs.forEach(quizzDiv => { + const ul = quizzDiv.querySelector('ul') + const lastLi = ul.lastElementChild + const explanationText = 'Explication : ' + lastLi.innerText + + const explanation = document.createElement('div') + explanation.classList.add('explanation_off') + explanation.innerHTML = explanationText + lastLi.parentNode.removeChild(lastLi); + quizzDiv.querySelector('div.custom-block-body').appendChild(explanation) + + }); + }) -function initializeRadio(answers) { - const radio = document.querySelectorAll('.quizz ul li input[type=radio]') - extractAnswer(radio, answers) -} + const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') + extractAnswer(checkboxes, answers) -const initializePipeline = [initializeCheckboxes] + } -function computeForm(formdata, answers) { - const badAnswers = [] - const allAnswerNames = [] - for (const entry of formdata.entries()) { - const name = entry[0] - const values = parseInt(entry[1], 10) - allAnswerNames.push(name) - if (!answers[name]) { - continue - } else { - // for poc we assume we only deal with lists - if (!answers[name][values]) { - badAnswers.push({ - name: name, - value: values - }) + function initializeRadio(answers) { + const radio = document.querySelectorAll('.quizz ul li input[type=radio]') + extractAnswer(radio, answers) + } + + const initializePipeline = [initializeCheckboxes] + + function computeForm(formdata, answers) { + const badAnswers = [] + const allAnswerNames = [] + for (const entry of formdata.entries()) { + const name = entry[0] + const values = parseInt(entry[1], 10) + allAnswerNames.push(name) + if (!answers[name]) { + continue + } else { + // for poc we assume we only deal with lists + if (!answers[name][values]) { + badAnswers.push({ + name: name, + value: values + }) + } } } + return [badAnswers, allAnswerNames] } - return [badAnswers, allAnswerNames] -} -function markBadAnswers(form, names, answers) { + function markBadAnswers(form, names, answers) { + + const toAdd = [] - const toAdd = [] + Object.keys(answers).forEach(answer => { - Object.keys(answers).forEach(answer => { + const inputs = form.querySelectorAll(`#${answer} input`); - const inputs = form.querySelectorAll(`#${answer} input`); + let numChecked = 0; + inputs.forEach(input => { + if (input.hasAttribute('checked')) { + input.parentElement.classList.add('answer-good') + numChecked++; + } + }); + + // question with more than one correct answer + if (numChecked > 1) { - let numChecked = 0; - inputs.forEach(input => { - if (input.hasAttribute('checked')) { - input.parentElement.classList.add('answer-good') - numChecked++; + let AnsweredWell = true + answers[answer].forEach((value, index) => { + const inputAnswer = document.querySelector(`#${answer} input[value="${index}"]`) + if (value && !inputAnswer.checked) { + AnsweredWell = false + } + }) + + const divquizz = form.querySelector(`div[data-name="${answer}"]`) + if (!AnsweredWell) { + divquizz.classList.add('quizz-bad') + const icon = iconMaker(false) + divquizz.querySelector('div.custom-block-body').appendChild(icon); + } else { + divquizz.classList.add('quizz-good') + } } - }); + }) + + names.forEach(({ name }) => { + + form.querySelectorAll('input[name="' + name + '"]').forEach(field => { - // question with more than one correct answer - if (numChecked > 1) { - let AnsweredWell = true - answers[answer].forEach((value, index) => { - const inputAnswer = document.querySelector(`#${answer} input[value="${index}"]`) - if (value && !inputAnswer.checked) { - AnsweredWell = false + if (answers[name][parseInt(field.getAttribute('value'), 10)] && !field.checked) { + + toAdd.push({ + name: name, + value: field.getAttribute('value') + }) } }) - const divquizz = form.querySelector(`div[data-name="${answer}"]`) - if (!AnsweredWell) { + const divquizz = form.querySelector(`.custom-block[data-name=${name}]`) + if (!divquizz.classList.contains('quizz-good')) { + divquizz.classList.add('quizz-bad') + // Create a new icon element const icon = iconMaker(false) divquizz.querySelector('div.custom-block-body').appendChild(icon); - } else { - divquizz.classList.add('quizz-good') } - } - }) - - names.forEach(({ name }) => { - - form.querySelectorAll('input[name="' + name + '"]').forEach(field => { - + }) - if (answers[name][parseInt(field.getAttribute('value'), 10)] && !field.checked) { + names.forEach(({ + name, + value + }) => { - toAdd.push({ - name: name, - value: field.getAttribute('value') - }) - } + form.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`).parentElement.classList.add('answer-bad') }) - const divquizz = form.querySelector(`.custom-block[data-name=${name}]`) - if (!divquizz.classList.contains('quizz-good')) { - - divquizz.classList.add('quizz-bad') - // Create a new icon element - const icon = iconMaker(false) - divquizz.querySelector('div.custom-block-body').appendChild(icon); + toAdd.forEach(name => names.push(name)) } - }) - names.forEach(({ - name, - value - }) => { + function getWantedHeading(questionNode, nodeName, attr) { + let potentialHeading = questionNode + while (potentialHeading[attr] && + potentialHeading.nodeName !== nodeName.toUpperCase()) { + potentialHeading = potentialHeading[attr] + } + if (potentialHeading.nodeName !== nodeName.toUpperCase()) { + return null + } + return potentialHeading + } - form.querySelector(`input[type=checkbox][name="${name}"][value="${value}"]`).parentElement.classList.add('answer-bad') - }) + /** + + */ + function injectForms(quizz, answers) { - toAdd.forEach(name => names.push(name)) -} -function getWantedHeading(questionNode, nodeName, attr) { - let potentialHeading = questionNode - while (potentialHeading[attr] && - potentialHeading.nodeName !== nodeName.toUpperCase()) { - potentialHeading = potentialHeading[attr] - } - if (potentialHeading.nodeName !== nodeName.toUpperCase()) { - return null - } - return potentialHeading -} + const searchedTitle = quizz.getAttribute('data-heading-level') || 'h3' + const submitLabel = quizz.getAttribute('data-quizz-validate') || 'Validate' + const headings = {} + const wrappers = [] -/** - */ -function injectForms(quizz, answers) { + const QuizzQstNum = quizz.querySelectorAll('.custom-block-quizz').length + Object.keys(answers).forEach(blockId => { - const searchedTitle = quizz.getAttribute('data-heading-level') || 'h3' - const submitLabel = quizz.getAttribute('data-quizz-validate') || 'Validate' - const headings = {} - const wrappers = [] + const blockNode = document.getElementById(blockId) - const QuizzQstNum = quizz.querySelectorAll('.custom-block-quizz').length + if (!blockNode) { + // if the node was treated and therefore the clone has not been reinserted yet + return + } + // this is the custom-block-quizz node + const questionNode = blockNode.parentElement.parentElement + const heading = getWantedHeading(questionNode, searchedTitle, 'previousSibling') || quizz - Object.keys(answers).forEach(blockId => { + if (!heading.getAttribute('id')) { - - const blockNode = document.getElementById(blockId) + heading.setAttribute('id', `quizz-form-${idBias}`) + idBias++ + } - if (!blockNode) { - // if the node was treated and therefore the clone has not been reinserted yet - return - } - // this is the custom-block-quizz node - const questionNode = blockNode.parentElement.parentElement - const heading = getWantedHeading(questionNode, searchedTitle, 'previousSibling') || quizz - if (!heading.getAttribute('id')) { - - heading.setAttribute('id', `quizz-form-${idBias}`) - idBias++ - } + if (heading && !headings[heading.getAttribute('id')]) { + // this is just for convenience, this add a "known" element that will always be there + const wrapper = document.createElement('div') - if (heading && !headings[heading.getAttribute('id')]) { + headings[heading.getAttribute('id')] = true + const form = document.createElement('form') + form.classList.add('quizz') - // this is just for convenience, this add a "known" element that will always be there - const wrapper = document.createElement('div') - headings[heading.getAttribute('id')] = true - const form = document.createElement('form') - form.classList.add('quizz') + const submit = document.createElement('button') + submit.innerText = submitLabel + submit.style.marginTop = '-22px' + submit.classList.add('btn', 'btn-submit') - const submit = document.createElement('button') - submit.innerText = submitLabel - submit.style.marginTop = '-22px' + var parts = quizz.previousElementSibling.firstElementChild.href.split("/"); + var idButton = parts[parts.length - 4] + "/" + parts[parts.length - 3] + "/" + parts[parts.length - 2] + "/" + parts[parts.length - 1]; - submit.classList.add('btn', 'btn-submit') + submit.setAttribute('id', `my-button-${idButton}`); - var parts = quizz.previousElementSibling.firstElementChild.href.split("/"); - var idButton = parts[parts.length - 4] + "/" + parts[parts.length - 3] + "/" + parts[parts.length - 2] + "/" + parts[parts.length - 1]; - submit.setAttribute('id', `my-button-${idButton}`); - + const notAnswered = document.createElement('p') + notAnswered.classList.add('notAnswered') - const notAnswered = document.createElement('p') - notAnswered.classList.add('notAnswered') + let nodeToAddToForm + if (heading === quizz) { - let nodeToAddToForm - if (heading === quizz) { + nodeToAddToForm = quizz.firstChild + } else { + nodeToAddToForm = heading.nextSibling + } - nodeToAddToForm = quizz.firstChild - } else { - nodeToAddToForm = heading.nextSibling - } + // add any element before the first question to the wrapper + while (nodeToAddToForm.nodeName !== "DIV" && nodeToAddToForm.className !== "custom-block custom-block-quizz") { - // add any element before the first question to the wrapper - while (nodeToAddToForm.nodeName !== "DIV" && nodeToAddToForm.className !== "custom-block custom-block-quizz"){ + const current = nodeToAddToForm + nodeToAddToForm = nodeToAddToForm.nextSibling + wrapper.appendChild(current.cloneNode(true)) + current.parentNode.removeChild(current) - const current = nodeToAddToForm - nodeToAddToForm = nodeToAddToForm.nextSibling - wrapper.appendChild(current.cloneNode(true)) - current.parentNode.removeChild(current) + } - } - - form.method = 'POST' - form.setAttribute('action', quizz.getAttribute('data-answer-url')) - form.setAttribute('id', `my-form-${idCounter}`); - idCounter++; - - - // add elements between 1st and last question to form - let num = 0; - while (nodeToAddToForm && nodeToAddToForm.nodeName !== searchedTitle.toUpperCase()) { - const current = nodeToAddToForm - if ( current.nodeName === "DIV" && current.className === "custom-block custom-block-quizz") num++; - nodeToAddToForm = nodeToAddToForm.nextSibling - form.appendChild(current.cloneNode(true)) - current.parentNode.removeChild(current) - if ( num === QuizzQstNum ) {form.appendChild(submit) ; break}; - } - wrapper.append(form) + form.method = 'POST' + form.setAttribute('action', quizz.getAttribute('data-answer-url')) + form.setAttribute('id', `my-form-${idCounter}`); + idCounter++; + + + // add elements between 1st and last question to form + let num = 0; + while (nodeToAddToForm && nodeToAddToForm.nodeName !== searchedTitle.toUpperCase()) { + const current = nodeToAddToForm + if (current.nodeName === "DIV" && current.className === "custom-block custom-block-quizz") num++; + nodeToAddToForm = nodeToAddToForm.nextSibling + form.appendChild(current.cloneNode(true)) + current.parentNode.removeChild(current) + if (num === QuizzQstNum) { form.appendChild(submit); break }; + } + wrapper.append(form) - // add elements after last question until next h3 to wrapper - while (nodeToAddToForm && nodeToAddToForm.nodeName !== searchedTitle.toUpperCase()) { + // add elements after last question until next h3 to wrapper + while (nodeToAddToForm && nodeToAddToForm.nodeName !== searchedTitle.toUpperCase()) { - const current = nodeToAddToForm - nodeToAddToForm = nodeToAddToForm.nextSibling - wrapper.appendChild(current.cloneNode(true)) - current.parentNode.removeChild(current) - } + const current = nodeToAddToForm + nodeToAddToForm = nodeToAddToForm.nextSibling + wrapper.appendChild(current.cloneNode(true)) + current.parentNode.removeChild(current) + } - wrapper.appendChild(notAnswered) - wrappers.push(wrapper) - - } - // avoid doubly - if (heading.nodeName === searchedTitle.toUpperCase()) { - quizz.removeChild(heading) - } - }) - wrappers.forEach((wrapper) => quizz.appendChild(wrapper)) -} + wrapper.appendChild(notAnswered) + wrappers.push(wrapper) -let answers = {} + } + // avoid doubly + if (heading.nodeName === searchedTitle.toUpperCase()) { + quizz.removeChild(heading) + } + }) + wrappers.forEach((wrapper) => quizz.appendChild(wrapper)) + } -initializePipeline.forEach(func => func(answers)) + let answers = {} -let idCounter = 0 -let idBias = 0 + initializePipeline.forEach(func => func(answers)) -document.querySelectorAll('div.quizz').forEach(div => { + let idCounter = 0 + let idBias = 0 - const quizInside = div.querySelector('.custom-block-quizz ul li input[type=checkbox]'); - if (quizInside) { - injectForms(div, answers) - } + document.querySelectorAll('div.quizz').forEach(div => { + + const quizInside = div.querySelector('.custom-block-quizz ul li input[type=checkbox]'); + if (quizInside) { + injectForms(div, answers) + } -}) + }) -function sendQuizzStatistics(form, statistics) { + function sendQuizzStatistics(form, statistics) { + const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value + const xhttp = new XMLHttpRequest() + xhttp.open('POST', form.getAttribute('action')) + xhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest') + xhttp.setRequestHeader('Content-Type', 'application/json') + xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) + statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href + xhttp.send(JSON.stringify(statistics)) - const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value - const xhttp = new XMLHttpRequest() - xhttp.open('POST', form.getAttribute('action')) - xhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest') - xhttp.setRequestHeader('Content-Type', 'application/json') - xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) - statistics.url = form.parentElement.parentElement.previousElementSibling.firstElementChild.href - xhttp.send(JSON.stringify(statistics)) + } -} + function displayResultAfterSubmitButton(form) { -function displayResultAfterSubmitButton(form) { + const questions = form.querySelectorAll('.custom-block-quizz'); - const questions = form.querySelectorAll('.custom-block-quizz'); + for (let question of questions) { - for (let question of questions) { + const explanationElement = question.querySelector('.explanation_off') + if (explanationElement !== null) { + explanationElement.classList.remove('explanation_off'); + explanationElement.classList.add('explanation_on'); + } - const explanationElement = question.querySelector('.explanation_off') - if (explanationElement !== null) { - explanationElement.classList.remove('explanation_off'); - explanationElement.classList.add('explanation_on'); } } -} - -// test if the quizz is totally answered or not -function QuizzAnswered(form) { - - const questions = form.querySelectorAll('.custom-block-quizz'); - for (let question of questions) { - const checkboxes = question.querySelectorAll('input[type="checkbox"]'); - let isQuestionValid = false; - for (let checkbox of checkboxes) { - if (checkbox.checked) { - isQuestionValid = true; - break; + // test if the quizz is totally answered or not + function QuizzAnswered(form) { + + const questions = form.querySelectorAll('.custom-block-quizz'); + for (let question of questions) { + const checkboxes = question.querySelectorAll('input[type="checkbox"]'); + let isQuestionValid = false; + for (let checkbox of checkboxes) { + if (checkbox.checked) { + isQuestionValid = true; + break; + } + } + if (!isQuestionValid) { + return false } } - if (!isQuestionValid) { - return false - } + return true } - return true -} -function iconMaker(isGood){ + function iconMaker(isGood) { // Create a new icon element const icon = document.createElement('i'); icon.classList.add('fas'); isGood ? icon.classList.add('fa-check') : icon.classList.add('fa-exclamation-triangle') icon.style.fontSize = '24px'; icon.style.transform = 'scale(2)'; - icon.style.marginLeft='700px' + icon.style.marginLeft = '700px' return icon -} + } -function getQuestionText(question){ + function getQuestionText(question) { - let title - if (question.querySelector('.math')) { - const annotation = question.querySelector('.math annotation'); - title = '$'+annotation.textContent.trim()+'$'; - } - else { - title = question.textContent; + let title + if (question.querySelector('.math')) { + const annotation = question.querySelector('.math annotation'); + title = '$' + annotation.textContent.trim() + '$'; + } + else { + title = question.textContent; + } + return title } - return title -} -function getAnswerText(liWrapper){ + function getAnswerText(liWrapper) { - const mathElement = liWrapper.querySelector('span.math'); - let answer; - if (mathElement) { - const annotationElement = mathElement.querySelector('annotation'); - answer = '$'+annotationElement.textContent.trim()+'$'; - } else { - answer = liWrapper.textContent; + const mathElement = liWrapper.querySelector('span.math'); + let answer; + if (mathElement) { + const annotationElement = mathElement.querySelector('annotation'); + answer = '$' + annotationElement.textContent.trim() + '$'; + } else { + answer = liWrapper.textContent; + } + return answer } - return answer -} -document.querySelectorAll('form.quizz').forEach(form => { + document.querySelectorAll('form.quizz').forEach(form => { - form.addEventListener('submit', e => { + form.addEventListener('submit', e => { - e.preventDefault() - e.stopPropagation() + e.preventDefault() + e.stopPropagation() - const notAnswered = form.parentElement.querySelector('.notAnswered'); - const submitBtn = form.querySelector('.btn-submit'); + const notAnswered = form.parentElement.querySelector('.notAnswered'); + const submitBtn = form.querySelector('.btn-submit'); - if (!document.querySelector('input[name=\'csrfmiddlewaretoken\']')) { - if (!sessionStorage.getItem(submitBtn.id)) { - // If the quiz has not been submitted before, store a flag in session storage to prevent multiple submissions - sessionStorage.setItem(`${submitBtn.id}`, true); - } else { - // If the quiz has already been submitted, disable the submit button - submitBtn.setAttribute('disabled', true); - alert('Vous avez deja répondu, Veuillez se connecter'); - return; + if (!document.querySelector('input[name=\'csrfmiddlewaretoken\']')) { + if (!sessionStorage.getItem(submitBtn.id)) { + // If the quiz has not been submitted before, store a flag in session storage to prevent multiple submissions + sessionStorage.setItem(`${submitBtn.id}`, true); + } else { + // If the quiz has already been submitted, disable the submit button + submitBtn.setAttribute('disabled', true); + alert('Vous avez deja répondu, Veuillez se connecter'); + return; + } } - } - // test if the whole quizz is answered - if (QuizzAnswered(form)) { + // test if the whole quizz is answered + if (QuizzAnswered(form)) { - const formData = new FormData(form) - const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) + const formData = new FormData(form) + const [badAnswerNames, allAnswerNames] = computeForm(formData, answers) - markBadAnswers(form, badAnswerNames, answers) + markBadAnswers(form, badAnswerNames, answers) - allAnswerNames.forEach(name => { - const ulWrapper = document.getElementById(name) - const quizzCustomBlock = ulWrapper.parentElement.parentElement - quizzCustomBlock.classList.add('hasAnswer') - }) - const questions = [] - badAnswerNames.forEach(result => { - if (questions.indexOf(result.name) === -1) { - questions.push(result.name) + allAnswerNames.forEach(name => { + const ulWrapper = document.getElementById(name) + const quizzCustomBlock = ulWrapper.parentElement.parentElement + quizzCustomBlock.classList.add('hasAnswer') + }) + const questions = [] + badAnswerNames.forEach(result => { + if (questions.indexOf(result.name) === -1) { + questions.push(result.name) + } + }) + + const statistics = { + expected: {}, + result: {} } - }) - const statistics = { - expected: {}, - result: {} - } + const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => getQuestionText(question)); - const CurrentFormQuestions = [...form.querySelectorAll('.custom-block-heading')].map(question => getQuestionText(question)); - - Object.keys(answers).forEach(name => { + Object.keys(answers).forEach(name => { - const element = document.querySelector(`.custom-block[data-name="${name}"]`) + const element = document.querySelector(`.custom-block[data-name="${name}"]`) - let title = getQuestionText(element.querySelector('.custom-block-heading')) + let title = getQuestionText(element.querySelector('.custom-block-heading')) - //make statistics of concerned form only - if (CurrentFormQuestions.includes(title)) { + //make statistics of concerned form only + if (CurrentFormQuestions.includes(title)) { - statistics.result[title] = { - evaluation: 'bad', - labels: [] - } - statistics.expected[title] = {} - - const availableResponses = element.querySelectorAll('input') - for (let i = 0; i < availableResponses.length; i++) { - // wee need to get the question label for statistics - const liWrapper = availableResponses[i].parentElement - let questionLabel = getAnswerText(liWrapper) - - statistics.expected[title][questionLabel] = answers[name][i] - } - // now determine answers and their labels - element.querySelectorAll('input:checked').forEach(node => { + statistics.result[title] = { + evaluation: 'bad', + labels: [] + } + statistics.expected[title] = {} - // remove eventual glued corretion - let label = getAnswerText(node.parentElement) - - statistics.result[title].labels.push(label.trim()) - }) - if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { + const availableResponses = element.querySelectorAll('input') + for (let i = 0; i < availableResponses.length; i++) { + // wee need to get the question label for statistics + const liWrapper = availableResponses[i].parentElement + let questionLabel = getAnswerText(liWrapper) + + statistics.expected[title][questionLabel] = answers[name][i] + } + // now determine answers and their labels + element.querySelectorAll('input:checked').forEach(node => { - element.classList.add('quizz-good') - - const icon = iconMaker(true) - // Append the icon to the element - element.querySelector('div.custom-block-body').appendChild(icon); - statistics.result[title].evaluation = 'ok' + // remove eventual glued corretion + let label = getAnswerText(node.parentElement) + statistics.result[title].labels.push(label.trim()) + }) + if (element.classList.contains('hasAnswer') && !element.classList.contains('quizz-bad')) { + + element.classList.add('quizz-good') + + const icon = iconMaker(true) + // Append the icon to the element + element.querySelector('div.custom-block-body').appendChild(icon); + statistics.result[title].evaluation = 'ok' + + } } - } - }) + }) - if (document.querySelector('input[name=\'csrfmiddlewaretoken\']')) { - sendQuizzStatistics(form, statistics) - } - submitBtn.setAttribute('disabled', true); - displayResultAfterSubmitButton(form) - notAnswered.innerText = '' - // not all questions answered - } else { + if (document.querySelector('input[name=\'csrfmiddlewaretoken\']')) { + sendQuizzStatistics(form, statistics) + } + submitBtn.setAttribute('disabled', true); + displayResultAfterSubmitButton(form) + notAnswered.innerText = '' + // not all questions answered + } else { - notAnswered.innerText = 'Veuillez répondre à toutes les questions' + notAnswered.innerText = 'Veuillez répondre à toutes les questions' - } + } + }) }) -}) } \ No newline at end of file diff --git a/assets/scss/pages/_stats.scss b/assets/scss/pages/_stats.scss index 9c43103f5b..5f3c300109 100644 --- a/assets/scss/pages/_stats.scss +++ b/assets/scss/pages/_stats.scss @@ -1,104 +1,142 @@ -.stat-table { - .level-1 { - padding-left: $length-20; - } - - .level-2 { - padding-left: $length-38; - } +/* Style for the header of each quiz */ +.header-stats-quiz { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + background-color: #f2f2f2; + border: 1px solid #ddd; } -.quizz-multiple.custom-block-body{ - color: #fc3605; +.header-stats-quiz h3 { + font-size: 24px; + margin: 0; } - /* Style the tab */ -.tab { - border: $length-1 solid $grey-100; - border-bottom: none; - - background-color: $grey-100; - color: $color-primary; +/* Style for the initialize stats button */ +.delete-quizz-button, +.delete-question-button { + background-color: #e44833; + border: none; + color: white; + padding: 10px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin-left: 10px; + cursor: pointer; } -/* Style the buttons that are used to open the tab content */ -.tab span { - display: inline-block; - padding: $length-8 $length-16; +.delete-quizz-button:hover, +.delete-question-button:hover { + background-color: #3e8e41; +} - cursor: pointer; +/* Style for each question and its stats */ +.stat-graph { + margin-bottom: 20px; + border: 1px solid #ddd; + padding: 10px; } -/* Change background color of buttons on hover */ -.tab span:hover { - background-color: $white; +.Question { + display: flex; + align-items: center; + justify-content: space-between; } -/* Create an active/current tablink class */ -.tab span.active { - background-color: $white; - font-weight: bold; +.question-stats { + font-size: 15px; + margin: 0; + margin-bottom: 5px; } -/* Style the tab content */ -.tabcontent { - /* Tab has to be displayed by default, otherwise Chart.js doesn't render the graph. */ +.question-stats span { + display: inline-block; +} - padding: $length-6 $length-12; +.quizz-answers { + list-style: none; + padding: 0; + margin: 0; + border: 1px solid #ddd; + +} - border: $length-1 solid $grey-100; - border-top: none; +.quizz-answers li { + display: flex; + align-items: center; + width: 100%; + margin-bottom: 10px; + justify-content: space-evenly; +} - .stat-graph { - width: 60%; - margin: 0 auto; - } +.quizz-answers li span:first-child { + width: 70%; } -.stat-graph ul.quizz-answers { - max-width: 50%; - li.quizz-stats { - display: grid; - grid-template-columns: 3fr 3fr 1fr; - grid-gap: 1px; - .over-progress { - display: block; - position: relative; - left: -50%; - } - } +.quizz-answers li span:last-child { + font-weight: bold; + font-size: 16px; } -.explanation_off { - display: none; +.quizz-answers li.quizz-stats-good { + background-color: #a6f566; + border-color: #155724; + color: #155724; } +.progress-wrapper { + position: relative; + height: 20px; + border-radius: 10px; + background-color: #f2f2f2; +} -.explanation_on { - - margin-top: 1rem; - padding: 1rem; - color: #0a0706; /* dark text */ - border-radius: 0.25rem; +.progress-bar { + position: absolute; + top: 0; + left: 0; + height: 100%; + border-radius: 10px; + background-color: #4CAF50; } -.notAnswered{ - margin-top: 1rem; - padding: 1rem; - margin-right: 17rem; - - border: 1px solid #f5c6cb; /* dark red */ - color: #f72f0c; /* dark text */ - border-radius: 0.25rem; - font-size: 1.2rem; - line-height: 1.5; - &:empty { - display: none; - } +.progress-label { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; + font-size: 16px; + font-weight: bold; } +.explanation_off { + display: none; +} +.explanation_on { + margin-top: 1rem; + padding: 1rem; + color: #0a0706; /* dark text */ + border-radius: 0.25rem; +} - \ No newline at end of file +.notAnswered { + margin-top: 1rem; + padding: 1rem; + margin-right: 17rem; + + border: 1px solid #f5c6cb; /* dark red */ + color: #f72f0c; /* dark text */ + border-radius: 0.25rem; + font-size: 1.2rem; + line-height: 1.5; + &:empty { + display: none; + } +} diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index c6037ae6f8..5dbb52b929 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -2,21 +2,22 @@ {% load emarkdown%} -
    - +
    {% for quizz_name, questions in quizz.items %} -
    +

    {{ quizz_name }}

    - +
    {% for question, stats in questions.items %}
    -

    {% trans graph_title %} {{ question|emarkdown }}

    - +
    +

    {% trans graph_title %} {{ question|emarkdown }}

    + +
      {% for answer_name, answer_stats in stats.responses.items %} -
    • +
    • {{ answer_name|emarkdown }} {% trans graph_title %} {{ question|emarkdown }} {% endfor %} {% endfor %}
    - - - \ No newline at end of file diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index f58ce67bda..d25a6cb9d2 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -1,29 +1,24 @@ import itertools -import json import uuid from collections import OrderedDict, Counter import logging import urllib.parse from datetime import timedelta, datetime, date from json import loads, dumps -from django.shortcuts import get_object_or_404, redirect +import requests + from django.views import View from django.db.models import Subquery -import requests from django.conf import settings from django.contrib import messages from django.core.exceptions import PermissionDenied from django.db.models import Count -from django.forms.utils import ErrorDict -from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse, StreamingHttpResponse +from django.http import StreamingHttpResponse from django.utils.translation import gettext_lazy as _ -from django.views.generic import FormView, DeleteView -from zds.member.decorator import LoggedWithReadWriteHability +from django.views.generic import FormView from zds.tutorialv2.forms import ContentCompareStatsURLForm, QuizzStatsForm from zds.tutorialv2.mixins import ( - SingleContentDetailViewMixin, - SingleContentViewMixin, SingleOnlineContentDetailViewMixin, SingleOnlineContentFormViewMixin, ) @@ -186,26 +181,14 @@ def get_ref_metrics(data): return refs def get_start_and_end_dates(self): - end_date = self.request.GET.get("end_date", None) - try: - end_date = datetime.strptime(end_date, "%Y-%m-%d").date() - except TypeError: - end_date = date.today() - except ValueError: - end_date = date.today() - messages.error(self.request, _("La date de fin fournie est invalide.")) - - start_date = self.request.GET.get("start_date", None) - try: - start_date = datetime.strptime(start_date, "%Y-%m-%d").date() - except TypeError: - start_date = end_date - timedelta(days=7) - except ValueError: - start_date = end_date - timedelta(days=7) - messages.error(self.request, _("La date de début fournie est invalide.")) - if start_date > end_date: - end_date, start_date = start_date, end_date + end_date = self.request.GET.get("end_date", None) or date.today() + + end_date = datetime.strptime(str(end_date), "%Y-%m-%d").date() + + start_date = self.request.GET.get("start_date", None) or (end_date - timedelta(days=7)) + + start_date = datetime.strptime(str(start_date), "%Y-%m-%d").date() return start_date, end_date @@ -384,26 +367,14 @@ class QuizzContentStatistics(ContentStatisticsView): class DeleteQuizz(View): def get_start_and_end_dates(self): - end_date = self.request.GET.get("end_date", None) - try: - end_date = datetime.strptime(end_date, "%Y-%m-%d").date() - except TypeError: - end_date = date.today() - except ValueError: - end_date = date.today() - messages.error(self.request, _("La date de fin fournie est invalide.")) - - start_date = self.request.GET.get("start_date", None) - try: - start_date = datetime.strptime(start_date, "%Y-%m-%d").date() - except TypeError: - start_date = end_date - timedelta(days=7) - except ValueError: - start_date = end_date - timedelta(days=7) - messages.error(self.request, _("La date de début fournie est invalide.")) - if start_date > end_date: - end_date, start_date = start_date, end_date + end_date = self.request.GET.get("end_date", None) or date.today() + + end_date = datetime.strptime(str(end_date), "%Y-%m-%d").date() + + start_date = self.request.GET.get("start_date", None) or (end_date - timedelta(days=7)) + + start_date = datetime.strptime(str(start_date), "%Y-%m-%d").date() return start_date, end_date @@ -411,7 +382,7 @@ def post(self, request): start_date, end_date = self.get_start_and_end_dates() - data = json.loads(request.body) + data = loads(request.body) # Extract the quizzName from the data quizz_name = data.get("quizzName") @@ -424,11 +395,8 @@ def post(self, request): else: related_question_ids = QuizzQuestion.objects.filter(url=quizz_name).values_list("id", flat=True) - try: - QuizzUserAnswer.objects.filter( - related_question_id__in=Subquery(related_question_ids), date_answer__range=(start_date, end_date) - ).delete() - except Exception as e: - return HttpResponseBadRequest(f"An error occurred while deleting the quiz: {str(e)}") + QuizzUserAnswer.objects.filter( + related_question_id__in=Subquery(related_question_ids), date_answer__range=(start_date, end_date) + ).delete() return StreamingHttpResponse(dumps({"status": "ok"})) From 729bdb6ece8f8a4ccfcf2552f53c8d8c55e89d55 Mon Sep 17 00:00:00 2001 From: Alexandre Bertin Date: Sun, 30 Apr 2023 20:40:02 +0200 Subject: [PATCH 61/78] ajout des premiers tests --- zds/tutorialv2/models/quizz.py | 2 +- zds/tutorialv2/tests/factories.py | 29 +++++++++++ .../tests/tests_views/tests_quizz.py | 49 +++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 zds/tutorialv2/tests/tests_views/tests_quizz.py diff --git a/zds/tutorialv2/models/quizz.py b/zds/tutorialv2/models/quizz.py index ac45a7dbdd..cf5ee7a485 100644 --- a/zds/tutorialv2/models/quizz.py +++ b/zds/tutorialv2/models/quizz.py @@ -32,7 +32,7 @@ class QuizzUserAnswer(models.Model): related_content = models.ForeignKey( PublishableContent, verbose_name="Tutoriel lié", blank=True, null=True, on_delete=models.CASCADE ) - answer = models.TextField(name="answer", verbose_name="anwser", null=False, blank=False) + answer = models.TextField(name="answer", verbose_name="answer", null=False, blank=False) date_answer = models.DateField(name="date_answer", verbose_name="Date of answer", null=False, auto_now=True) related_question = models.ForeignKey( QuizzQuestion, name="related_question", verbose_name="Question liée", on_delete=models.CASCADE diff --git a/zds/tutorialv2/tests/factories.py b/zds/tutorialv2/tests/factories.py index c3c275f074..0fd99c4b2b 100644 --- a/zds/tutorialv2/tests/factories.py +++ b/zds/tutorialv2/tests/factories.py @@ -8,6 +8,7 @@ from zds.forum.tests.factories import PostFactory, TopicFactory from zds.gallery.tests.factories import GalleryFactory, UserGalleryFactory from zds.tutorialv2.models.goals import Goal +from zds.tutorialv2.models.quizz import QuizzQuestion, QuizzAvailableAnswer, QuizzUserAnswer from zds.tutorialv2.models.help_requests import HelpWriting from zds.utils import old_slugify from zds.utils.tests.factories import LicenceFactory, SubCategoryFactory @@ -314,6 +315,34 @@ class Meta: slug = factory.Sequence("mon-objectif-{}".format) +class QuizzQuestionFactory(factory.django.DjangoModelFactory): + class Meta: + model = QuizzQuestion + + url = factory.Faker("url") + question = factory.Faker("text", max_nb_chars=200) + question_type = factory.Faker("random_element", elements=("qcm", "open", "bool")) + + +class QuizzAvailableAnswerFactory(factory.django.DjangoModelFactory): + class Meta: + model = QuizzAvailableAnswer + + label = factory.Faker("text", max_nb_chars=100) + is_good = factory.Faker("boolean") + related_question = factory.SubFactory(QuizzQuestionFactory) + + +class QuizzUserAnswerFactory(factory.django.DjangoModelFactory): + class Meta: + model = QuizzUserAnswer + + related_content = factory.SubFactory(PublishableContentFactory) + answer = factory.Faker("text", max_nb_chars=200) + related_question = factory.SubFactory(QuizzQuestionFactory) + full_answer_id = factory.Faker("uuid4") + + class ContentContributionRoleFactory(factory.django.DjangoModelFactory): """Factory that create a role in contributions to contents, for use in tests.""" diff --git a/zds/tutorialv2/tests/tests_views/tests_quizz.py b/zds/tutorialv2/tests/tests_views/tests_quizz.py new file mode 100644 index 0000000000..6211cc2715 --- /dev/null +++ b/zds/tutorialv2/tests/tests_views/tests_quizz.py @@ -0,0 +1,49 @@ +from django.test import TestCase +from zds.tutorialv2.models.quizz import QuizzAvailableAnswer, QuizzQuestion, QuizzUserAnswer +from zds.tutorialv2.tests.factories import QuizzQuestionFactory, QuizzAvailableAnswerFactory, QuizzUserAnswerFactory + + +class QuizzQuestionTestCase(TestCase): + def setUp(self): + self.question_1 = QuizzQuestionFactory() + self.question_2 = QuizzQuestionFactory() + + self.answer_1 = QuizzAvailableAnswerFactory(related_question=self.question_1, is_good=True) + self.answer_2 = QuizzAvailableAnswerFactory(related_question=self.question_1, is_good=False) + self.answer_3 = QuizzAvailableAnswerFactory(related_question=self.question_2, is_good=True) + self.answer_4 = QuizzAvailableAnswerFactory(related_question=self.question_2, is_good=False) + + self.user_answer_1 = QuizzUserAnswerFactory(related_question=self.question_1) + self.user_answer_2 = QuizzUserAnswerFactory(related_question=self.question_2) + + def test_question_count(self): + # Vérifiez qu'il y a bien deux questions créées dans la base de données + self.assertEqual(QuizzQuestion.objects.count(), 2) + + def test_answer_count(self): + # Vérifiez qu'il y a bien quatre réponses créées dans la base de données + self.assertEqual(QuizzAvailableAnswer.objects.count(), 4) + + def test_user_answer_count(self): + # Vérifiez qu'il y a bien deux réponses d'utilisateur créées dans la base de données + self.assertEqual(QuizzUserAnswer.objects.count(), 2) + + def test_related_user_answers(self): + # Vérifiez que les réponses d'utilisateur sont bien liées aux bonnes questions + self.assertEqual(self.user_answer_1.related_question, self.question_1) + self.assertEqual(self.user_answer_2.related_question, self.question_2) + + def test_related_answers(self): + self.assertEqual(self.answer_1.related_question, self.question_1) + self.assertEqual(self.answer_2.related_question, self.question_1) + self.assertEqual(self.answer_3.related_question, self.question_2) + self.assertEqual(self.answer_4.related_question, self.question_2) + + def test_answer_is_good(self): + # Vérifiez qu'au moins une réponse disponible pour chaque question est marquée comme bonne + self.assertTrue(self.question_1.quizzavailableanswer_set.filter(is_good=True).exists()) + self.assertTrue(self.question_2.quizzavailableanswer_set.filter(is_good=True).exists()) + + def test_user_answer_unique_id(self): + # Vérifiez que chaque réponse d'utilisateur a un ID unique + self.assertNotEqual(self.user_answer_1.full_answer_id, self.user_answer_2.full_answer_id) From 314caa10743175eb0d860d3b3df5ee181e217574 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 30 Apr 2023 22:44:05 +0200 Subject: [PATCH 62/78] fixed display of explanation in tutorial draft version --- assets/js/content-quizz.js | 94 ++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 3f0d2d04ea..cfa322fa48 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -1,39 +1,4 @@ -var currentURL = window.location.href; - -if (currentURL.includes("/tutoriels/")) { - - // to handle a new type of answer, you just need to create a method called - // initializeXXX(answers) that will add the reset the field content and mark good answer - // then add the two methods in the callback lists - - let index = 0 - - function extractAnswer(inputDomElementList, answers) { - - inputDomElementList.forEach((rb) => { - - const ulWrapperElement = rb.parentElement.parentElement - // we give the ui an id to find the element in a more effective way later when the users answer the questions - if (!ulWrapperElement.getAttribute('id')) { - ulWrapperElement.setAttribute('id', 'id-' + (index++)) - } - - rb.setAttribute('name', ulWrapperElement.getAttribute('id')) - - const questionBlock = ulWrapperElement.parentElement.parentElement - questionBlock.setAttribute('data-name', rb.getAttribute('name')) - if (!answers[ulWrapperElement.getAttribute('id')]) { - answers[ulWrapperElement.getAttribute('id')] = [rb.checked] - } else { - answers[ulWrapperElement.getAttribute('id')].push(rb.checked) - } - rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) - rb.disabled = false - rb.checked = false - }) - } - - /** + /** * The full quizz is contained in a div or article that has class "quizz". * Then one question is inside a zmarkdown "custom-block" of type "custom-block-quizz". Two possibilities : * @@ -70,7 +35,57 @@ if (currentURL.includes("/tutoriels/")) { * * * Note that the correction MAY be inside the last li due to the way custom-block plugin works, this is not a bug - * + * */ + +var currentURL = window.location.href; + +if (currentURL.includes("/contenus/")) { + + // Get the last list item in the unordered list + var lastListItem = document.querySelector('.custom-block-quizz ul li:last-child'); + + // Create a new div element + var newDiv = document.createElement('div'); + newDiv.classList.add('explanation_on') + + // Set the text content of the div to match the text content of the last list item + newDiv.innerHTML = 'Explication : ' + lastListItem.textContent; + + // Replace the last list item with the new div element + lastListItem.parentNode.replaceChild(newDiv, lastListItem); + +} + +if (currentURL.includes("/tutoriels/")) { + + let index = 0 + + function extractAnswer(inputDomElementList, answers) { + + inputDomElementList.forEach((rb) => { + + const ulWrapperElement = rb.parentElement.parentElement + // we give the ui an id to find the element in a more effective way later when the users answer the questions + if (!ulWrapperElement.getAttribute('id')) { + ulWrapperElement.setAttribute('id', 'id-' + (index++)) + } + + rb.setAttribute('name', ulWrapperElement.getAttribute('id')) + + const questionBlock = ulWrapperElement.parentElement.parentElement + questionBlock.setAttribute('data-name', rb.getAttribute('name')) + if (!answers[ulWrapperElement.getAttribute('id')]) { + answers[ulWrapperElement.getAttribute('id')] = [rb.checked] + } else { + answers[ulWrapperElement.getAttribute('id')].push(rb.checked) + } + rb.setAttribute('value', answers[ulWrapperElement.getAttribute('id')].length - 1) + rb.disabled = false + rb.checked = false + }) + } + + /** * @param answers the answer dictionary, it will be modified by the process */ function initializeCheckboxes(answers) { @@ -212,9 +227,7 @@ if (currentURL.includes("/tutoriels/")) { return potentialHeading } - /** - - */ + function injectForms(quizz, answers) { @@ -433,6 +446,7 @@ if (currentURL.includes("/tutoriels/")) { return answer } + document.querySelectorAll('form.quizz').forEach(form => { form.addEventListener('submit', e => { From 4bbec24c261b4942bbcbf3a24c0c9d71dff626e7 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 30 Apr 2023 23:00:16 +0200 Subject: [PATCH 63/78] quick fix of last commit --- assets/js/content-quizz.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index cfa322fa48..0b2271879e 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -41,18 +41,21 @@ var currentURL = window.location.href; if (currentURL.includes("/contenus/")) { - // Get the last list item in the unordered list - var lastListItem = document.querySelector('.custom-block-quizz ul li:last-child'); + let lastListItems = document.querySelectorAll('.custom-block-quizz ul li:last-child'); - // Create a new div element - var newDiv = document.createElement('div'); - newDiv.classList.add('explanation_on') + for (let i = 0; i < lastListItems.length; i++) { + + // Create a new div element + let newDiv = document.createElement('div'); + newDiv.classList.add('explanation_on') - // Set the text content of the div to match the text content of the last list item - newDiv.innerHTML = 'Explication : ' + lastListItem.textContent; + // Set the text content of the div to match the text content of the last list item + newDiv.innerHTML = 'Explication : ' + lastListItems[i].textContent; - // Replace the last list item with the new div element - lastListItem.parentNode.replaceChild(newDiv, lastListItem); + // Replace the last list item with the new div element + lastListItems[i].parentNode.replaceChild(newDiv, lastListItems[i]); + } + } From 91e89b619ab52dfead6bb028b5bf04f0697254b6 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 30 Apr 2023 23:36:31 +0200 Subject: [PATCH 64/78] quizz squelette on editor --- assets/js/editor-new.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/assets/js/editor-new.js b/assets/js/editor-new.js index b0ef9a8ce2..a8d2550bcc 100644 --- a/assets/js/editor-new.js +++ b/assets/js/editor-new.js @@ -118,9 +118,6 @@ } else if (_type === 'n' || _type === 'neutre') { ret.blocNeutral = true } - // else if (_type === 'q' || _type === 'quizz') { - // ret.blocNeutral = true - // } } } else { @@ -252,7 +249,11 @@ shiftLines(cm, startPoint.line, '[[neutre|titre]]') } else if (type === 'blocquizz') { - shiftLines(cm, startPoint.line, '[[quizz|Question]]\n|- [ ] réponse\n| - [x] bonne réponse\n| - [ ] réponse\n| - Explication : '); + shiftLines(cm, startPoint.line, '| - Explication : '); + shiftLines(cm, startPoint.line, '| - [ ] réponse 3'); + shiftLines(cm, startPoint.line, '| - [x] bonne réponse'); + shiftLines(cm, startPoint.line, '| - [ ] réponse 1'); + shiftLines(cm, startPoint.line, '[[quizz|Question]]'); } startPoint.ch = 0 endPoint.line += 1 @@ -839,14 +840,6 @@ } ] }, - { - name: 'blocquizz', - action: (e) => { - _toggleBlockZmd(e, 'blocquizz', '') - }, - className: 'fas fa-question', - title: 'Bloc quizz' - }, '|', { name: 'blocMenu', @@ -960,7 +953,16 @@ action: EasyMDE.toggleFullScreen, className: 'fa fa-arrows-alt no-disable no-mobile disable-for-textarea-mode', title: 'Plein écran' - } + }, + '|', + { + name: 'blocquizz', + action: (e) => { + _toggleBlockZmd(e, 'blocquizz', '') + }, + className: 'fas fa-question', + title: 'Bloc quizz' + }, ] }) From e9c39bf18c1b0f4242a5db487e8de80ec1c1adaf Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 1 May 2023 02:21:47 +0200 Subject: [PATCH 65/78] maked answers element as labels , still need some style :) --- assets/js/content-quizz.js | 145 ++++++++++++++++++--------------- assets/scss/base/_content.scss | 37 ++++----- 2 files changed, 97 insertions(+), 85 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 0b2271879e..126bf71d5e 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -1,41 +1,41 @@ - /** - * The full quizz is contained in a div or article that has class "quizz". - * Then one question is inside a zmarkdown "custom-block" of type "custom-block-quizz". Two possibilities : - * - * Without explanation for correction : - * - * - *
    - *
    The question
    - *
    - *
    • the answer
    • - *
    • the good answer
    • - *
    - *
    - *
    - *
    - * - * With an explanation inside another custom block most of time a custom-block-neutral - * - * - *
    - *
    The question
    - *
    - *
    • the answer
    • - *
    • the good answer - * - *
      - *
      Explanation
      - *
      a formatted text
      - *
    • - * - *
    - *
    - *
    - *
    - * - * Note that the correction MAY be inside the last li due to the way custom-block plugin works, this is not a bug - * */ +/** + * The full quizz is contained in a div or article that has class "quizz". + * Then one question is inside a zmarkdown "custom-block" of type "custom-block-quizz". Two possibilities : + * + * Without explanation for correction : + * + * + *
    + *
    The question
    + *
    + *
    • the answer
    • + *
    • the good answer
    • + *
    + *
    + *
    + *
    + * + * With an explanation inside another custom block most of time a custom-block-neutral + * + * + *
    + *
    The question
    + *
    + *
    • the answer
    • + *
    • the good answer + * + *
      + *
      Explanation
      + *
      a formatted text
      + *
    • + * + *
    + *
    + *
    + *
    + * + * Note that the correction MAY be inside the last li due to the way custom-block plugin works, this is not a bug + * */ var currentURL = window.location.href; @@ -44,7 +44,7 @@ if (currentURL.includes("/contenus/")) { let lastListItems = document.querySelectorAll('.custom-block-quizz ul li:last-child'); for (let i = 0; i < lastListItems.length; i++) { - + // Create a new div element let newDiv = document.createElement('div'); newDiv.classList.add('explanation_on') @@ -54,8 +54,8 @@ if (currentURL.includes("/contenus/")) { // Replace the last list item with the new div element lastListItems[i].parentNode.replaceChild(newDiv, lastListItems[i]); - } - + } + } @@ -65,6 +65,7 @@ if (currentURL.includes("/tutoriels/")) { function extractAnswer(inputDomElementList, answers) { + let idli = 0 inputDomElementList.forEach((rb) => { const ulWrapperElement = rb.parentElement.parentElement @@ -74,6 +75,8 @@ if (currentURL.includes("/tutoriels/")) { } rb.setAttribute('name', ulWrapperElement.getAttribute('id')) + rb.setAttribute('id', ulWrapperElement.getAttribute('id') + '-' + idli) + idli++ const questionBlock = ulWrapperElement.parentElement.parentElement questionBlock.setAttribute('data-name', rb.getAttribute('name')) @@ -93,31 +96,45 @@ if (currentURL.includes("/tutoriels/")) { */ function initializeCheckboxes(answers) { - // add explanation to all questions - document.querySelectorAll('div.quizz').forEach(quizz => { - const quizzDivs = quizz.querySelectorAll('div.custom-block-quizz'); - quizzDivs.forEach(quizzDiv => { - const ul = quizzDiv.querySelector('ul') - const lastLi = ul.lastElementChild - const explanationText = 'Explication : ' + lastLi.innerText - - const explanation = document.createElement('div') - explanation.classList.add('explanation_off') - explanation.innerHTML = explanationText - lastLi.parentNode.removeChild(lastLi); - quizzDiv.querySelector('div.custom-block-body').appendChild(explanation) - - }); - }) + ExplanationMaker() const checkboxes = document.querySelectorAll('.quizz ul li input[type=checkbox]') extractAnswer(checkboxes, answers) + AnswersAsLabels() + } - function initializeRadio(answers) { - const radio = document.querySelectorAll('.quizz ul li input[type=radio]') - extractAnswer(radio, answers) + function AnswersAsLabels() { + + var checkboxli = document.querySelectorAll('.quizz ul li') + + checkboxli.forEach((li) => { + var input = li.querySelector('input[type=checkbox]') + var label = document.createElement('label') + label.setAttribute('for', input.getAttribute('id')) + label.classList.add('answer-label') + label.innerHTML = li.querySelector('span.math') ? li.querySelector('span.math').innerHTML : li.innerText + li.textContent = '' + li.appendChild(input) + li.appendChild(label) + }) + } + + function ExplanationMaker() { + // add explanation to all questions + document.querySelectorAll('div.custom-block-quizz').forEach(quizzDiv => { + + const ul = quizzDiv.querySelector('ul') + const lastLi = ul.lastElementChild + const explanationText = 'Explication : ' + lastLi.innerText + const explanation = document.createElement('div') + explanation.classList.add('explanation_off') + explanation.innerHTML = explanationText + lastLi.parentNode.removeChild(lastLi) + quizzDiv.querySelector('div.custom-block-body').appendChild(explanation) + + }) } const initializePipeline = [initializeCheckboxes] @@ -438,11 +455,11 @@ if (currentURL.includes("/tutoriels/")) { function getAnswerText(liWrapper) { - const mathElement = liWrapper.querySelector('span.math'); + let label = liWrapper.querySelector('.answer-label') + let MathAnnotation = label.querySelector('annotation') let answer; - if (mathElement) { - const annotationElement = mathElement.querySelector('annotation'); - answer = '$' + annotationElement.textContent.trim() + '$'; + if (MathAnnotation) { + answer = '$' + MathAnnotation.textContent.trim() + '$'; } else { answer = liWrapper.textContent; } diff --git a/assets/scss/base/_content.scss b/assets/scss/base/_content.scss index 0438da9666..de0bf07f2a 100644 --- a/assets/scss/base/_content.scss +++ b/assets/scss/base/_content.scss @@ -644,36 +644,31 @@ div.align-left, figure pre code.hljs { // checkbox menu -ul li p { - margin: 0; -} - li.task-list-item { + list-style-type: none; position: relative; - - input[type=checkbox] { - position: absolute; - top: $length-2; - left: -$length-24; - - background-color: $true-white !important; - } - input[type=radio] { - position: absolute; - top: $length-2; - left: -$length-24; - - background-color: $true-white !important; + display: flex; + align-items: center; + + input[type="checkbox"] { + margin-right: 10px; } + &.answer-bad { + label { color: $color-danger; + } } + + &.answer-good { - color:green; - } + label { + color: green; + } -} + } + } .warn-typo { From b2c6328352adef3e1a971d25b158498e6b8604bd Mon Sep 17 00:00:00 2001 From: Alexandre Bertin Date: Mon, 1 May 2023 14:27:39 +0200 Subject: [PATCH 66/78] =?UTF-8?q?ajout=20de=20tests=20suppl=C3=A9mentaires?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/tests_views/tests_quizz.py | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/zds/tutorialv2/tests/tests_views/tests_quizz.py b/zds/tutorialv2/tests/tests_views/tests_quizz.py index 6211cc2715..b500a1fa14 100644 --- a/zds/tutorialv2/tests/tests_views/tests_quizz.py +++ b/zds/tutorialv2/tests/tests_views/tests_quizz.py @@ -8,7 +8,7 @@ def setUp(self): self.question_1 = QuizzQuestionFactory() self.question_2 = QuizzQuestionFactory() - self.answer_1 = QuizzAvailableAnswerFactory(related_question=self.question_1, is_good=True) + self.answer_1 = QuizzAvailableAnswerFactory(related_question=self.question_1, is_good=False) self.answer_2 = QuizzAvailableAnswerFactory(related_question=self.question_1, is_good=False) self.answer_3 = QuizzAvailableAnswerFactory(related_question=self.question_2, is_good=True) self.answer_4 = QuizzAvailableAnswerFactory(related_question=self.question_2, is_good=False) @@ -41,9 +41,38 @@ def test_related_answers(self): def test_answer_is_good(self): # Vérifiez qu'au moins une réponse disponible pour chaque question est marquée comme bonne - self.assertTrue(self.question_1.quizzavailableanswer_set.filter(is_good=True).exists()) + self.assertFalse(self.question_1.quizzavailableanswer_set.filter(is_good=True).exists()) self.assertTrue(self.question_2.quizzavailableanswer_set.filter(is_good=True).exists()) + # def test_at_least_one_good_answer(self): + # # Vérifiez qu'au moins une réponse disponible pour chaque question est marquée comme bonne + # question_1_answers = QuizzAvailableAnswer.objects.filter(related_question=self.question_1) + # question_2_answers = QuizzAvailableAnswer.objects.filter(related_question=self.question_2) + + # self.assertFalse(any(answer.is_good for answer in question_1_answers)) + # self.assertTrue(any(answer.is_good for answer in question_2_answers)) + def test_user_answer_unique_id(self): # Vérifiez que chaque réponse d'utilisateur a un ID unique self.assertNotEqual(self.user_answer_1.full_answer_id, self.user_answer_2.full_answer_id) + + +class QuizzSeveralGoodAnswers(TestCase): + def setUp(self): + self.question_1 = QuizzQuestionFactory() + + self.answer_1 = QuizzAvailableAnswerFactory(related_question=self.question_1, is_good=False) + self.answer_2 = QuizzAvailableAnswerFactory(related_question=self.question_1, is_good=False) + self.answer_3 = QuizzAvailableAnswerFactory(related_question=self.question_1, is_good=True) + self.answer_4 = QuizzAvailableAnswerFactory(related_question=self.question_1, is_good=True) + + self.user_answer_1 = QuizzUserAnswerFactory(related_question=self.question_1) + + def test_answer_count(self): + # Vérifiez qu'il y a bien quatre réponses créées dans la base de données + self.assertEqual(QuizzAvailableAnswer.objects.count(), 4) + + def test_nb_good_answers(self): + # Vérifier qu'il y a bien 2 bonnes et 2 mauvaises réponses + self.assertEqual(self.question_1.quizzavailableanswer_set.filter(is_good=True).count(), 2) + self.assertEqual(self.question_1.quizzavailableanswer_set.filter(is_good=False).count(), 2) From 76ea273a84795ff72b3798b572a35c19a7209dd6 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Wed, 3 May 2023 09:00:48 +0200 Subject: [PATCH 67/78] quizz squelette dans l'editeur n'est possible que dans contenus et sondage squelette que dans les forums --- assets/js/editor-new.js | 1057 ++++++++++++++++++++------------------- 1 file changed, 536 insertions(+), 521 deletions(-) diff --git a/assets/js/editor-new.js b/assets/js/editor-new.js index a8d2550bcc..0edbee8b2f 100644 --- a/assets/js/editor-new.js +++ b/assets/js/editor-new.js @@ -249,9 +249,18 @@ shiftLines(cm, startPoint.line, '[[neutre|titre]]') } else if (type === 'blocquizz') { - shiftLines(cm, startPoint.line, '| - Explication : '); - shiftLines(cm, startPoint.line, '| - [ ] réponse 3'); - shiftLines(cm, startPoint.line, '| - [x] bonne réponse'); + + if(window.location.href.includes('/contenus/')){ + shiftLines(cm, startPoint.line, '| - Explication : '); + shiftLines(cm, startPoint.line, '| - [ ] réponse 3'); + shiftLines(cm, startPoint.line, '| - [x] bonne réponse'); + } + else { + shiftLines(cm, startPoint.line, '| - [ ] réponse 3'); + shiftLines(cm, startPoint.line, '| - [ ] réponse 2'); + } + + shiftLines(cm, startPoint.line, '| - [ ] réponse 1'); shiftLines(cm, startPoint.line, '[[quizz|Question]]'); } @@ -391,579 +400,585 @@ return null } - /* global EasyMDE */ - const easyMDE = new EasyMDE({ - autoDownloadFontAwesome: false, - element: this, - forceSync: true, - autosave: { - enabled: true, - uniqueId: mdeUniqueKey, - delay: 1000 + const BaseToolbar = [ + { + name: 'bold', + action: EasyMDE.toggleBold, + className: 'fa fa-bold', + title: 'Gras' }, - indentWithTabs: false, - minHeight: minHeight + 'px', - placeholder: 'Votre message au format Markdown', - promptURLs: true, - promptTexts: { - image: 'Url de votre image', - link: 'Url de votre lien' + { + name: 'italic', + action: EasyMDE.toggleItalic, + className: 'fa fa-italic', + title: 'Italique' }, - uploadImage: true, - imageUploadFunction: uploadImage, - imageTexts: { - sbInit: 'Joindre des images par glisser-déposer ou coller depuis le presse-papiers.', - sbOnDragEnter: 'Déposez l\'image pour l\'envoyer dans votre galerie.', - sbOnDrop: 'Téléchargement d\'images #images_names#', - sbProgress: 'Téléchargement #file_name#: #progress#%', - sbOnUploaded: 'Image téléchargée #image_name#' + { + name: 'strikethrough', + action: EasyMDE.toggleStrikethrough, + className: 'fa fa-strikethrough', + title: 'Barré' }, - spellChecker: false, - inputStyle: 'contenteditable', - nativeSpellcheck: true, - sideBySideFullscreen: false, - promptAbbrv: true, - theme: 'zest', - previewRender: customMarkdownParser, - syncSideBySidePreviewScroll: false, - toolbar: [ - { - name: 'bold', - action: EasyMDE.toggleBold, - className: 'fa fa-bold', - title: 'Gras' - }, - { - name: 'italic', - action: EasyMDE.toggleItalic, - className: 'fa fa-italic', - title: 'Italique' - }, - { - name: 'strikethrough', - action: EasyMDE.toggleStrikethrough, - className: 'fa fa-strikethrough', - title: 'Barré' - }, - { - name: 'abbr', - action: (e) => { - const options = e.options - const cm = e.codemirror - let abbr = cm.getSelection() - let description = '' - const lastLine = cm.lastLine() - const lastCh = cm.getLine(lastLine).length - - const startPoint = cm.getCursor('start') - const endPoint = cm.getCursor('end') - - if (options.promptAbbrv) { + { + name: 'abbr', + action: (e) => { + const options = e.options + const cm = e.codemirror + let abbr = cm.getSelection() + let description = '' + const lastLine = cm.lastLine() + const lastCh = cm.getLine(lastLine).length + + const startPoint = cm.getCursor('start') + const endPoint = cm.getCursor('end') + + if (options.promptAbbrv) { + if (abbr.length === 0) { + abbr = prompt('Mot abrégé', '') if (abbr.length === 0) { - abbr = prompt('Mot abrégé', '') - if (abbr.length === 0) { - return false - } + return false } - description = prompt('Description de l\'abbréviation', '') } + description = prompt('Description de l\'abbréviation', '') + } - cm.replaceRange(cm.lineSeparator() + cm.lineSeparator() + '*[' + abbr + ']: ' + description, { line: lastLine, ch: lastCh }, { line: lastLine, ch: maxRange }) - cm.setSelection(startPoint, endPoint) - cm.focus() - }, - className: 'fa fa-text-width', - title: 'Abbréviation' - }, - { - name: 'keyboard', - action: (e) => { - _toggleBlockZmd(e, 'keyboard', '||') - }, - className: 'far fa-keyboard', - title: 'Touche clavier' - }, - { - name: 'codeInline', - action: (e) => { - _toggleBlockZmd(e, 'codeInline', '`') - }, - className: 'fa fa-terminal', - title: 'Code inline' - }, - '|', - { - name: 'superscript', - action: (e) => { - _toggleBlockZmd(e, 'superscript', '^') - }, - className: 'fa fa-superscript', - title: 'Exposant' - }, - { - name: 'subscript', - action: (e) => { - _toggleBlockZmd(e, 'subscript', '~') - }, - className: 'fa fa-subscript', - title: 'Indice' - }, - '|', - { - name: 'alignCenter', - action: (e) => { - _toggleBlockZmd(e, 'alignCenter', '-> ', ' <-') - }, - className: 'fa fa-align-center', - title: 'Aligner au centre' - }, - { - name: 'alignRight', - action: (e) => { - _toggleBlockZmd(e, 'alignRight', '-> ', ' ->') - }, - className: 'fa fa-align-right', - title: 'Aligner a droite' + cm.replaceRange(cm.lineSeparator() + cm.lineSeparator() + '*[' + abbr + ']: ' + description, { line: lastLine, ch: lastCh }, { line: lastLine, ch: maxRange }) + cm.setSelection(startPoint, endPoint) + cm.focus() }, - '|', - { - name: 'list-ul', - action: EasyMDE.toggleUnorderedList, - className: 'fa fa-list-ul', - title: 'Liste à puces' - }, - { - name: 'ordered-list', - action: EasyMDE.toggleOrderedList, - className: 'fa fa-list-ol', - title: 'Liste ordonnée' - }, - { - name: 'checklist', - action: (e) => { - _toggleBlockZmd(e, 'checklist', '- [ ] ') - }, - className: 'far fa-check-square', - title: 'Liste de taches' + className: 'fa fa-text-width', + title: 'Abbréviation' + }, + { + name: 'keyboard', + action: (e) => { + _toggleBlockZmd(e, 'keyboard', '||') }, - '|', - { - name: 'heading', - action: EasyMDE.toggleHeadingSmaller, - className: 'fas fa-heading', - title: 'Titres' + className: 'far fa-keyboard', + title: 'Touche clavier' + }, + { + name: 'codeInline', + action: (e) => { + _toggleBlockZmd(e, 'codeInline', '`') }, - '|', - { - name: 'image', - action: EasyMDE.drawImage, - className: 'far fa-image', - title: 'Image' + className: 'fa fa-terminal', + title: 'Code inline' + }, + '|', + { + name: 'superscript', + action: (e) => { + _toggleBlockZmd(e, 'superscript', '^') }, - { - name: 'link_btn', - action: EasyMDE.drawLink, - className: 'fa fa-link', - title: 'Lien' + className: 'fa fa-superscript', + title: 'Exposant' + }, + { + name: 'subscript', + action: (e) => { + _toggleBlockZmd(e, 'subscript', '~') }, - '|', - { - name: 'quote', - action: EasyMDE.toggleBlockquote, - className: 'fa fa-quote-left', - title: 'Citation' + className: 'fa fa-subscript', + title: 'Indice' + }, + '|', + { + name: 'alignCenter', + action: (e) => { + _toggleBlockZmd(e, 'alignCenter', '-> ', ' <-') }, - { - name: 'code', - action: EasyMDE.toggleCodeBlock, - className: 'fa fa-code', - title: 'Bloc de code coloré' + className: 'fa fa-align-center', + title: 'Aligner au centre' + }, + { + name: 'alignRight', + action: (e) => { + _toggleBlockZmd(e, 'alignRight', '-> ', ' ->') }, - { - name: 'math', - action: (e) => { - _toggleBlockZmd(e, 'math', '$$') - }, - className: 'fa fa-percent', - title: 'Formule mathématique' + className: 'fa fa-align-right', + title: 'Aligner a droite' + }, + '|', + { + name: 'list-ul', + action: EasyMDE.toggleUnorderedList, + className: 'fa fa-list-ul', + title: 'Liste à puces' + }, + { + name: 'ordered-list', + action: EasyMDE.toggleOrderedList, + className: 'fa fa-list-ol', + title: 'Liste ordonnée' + }, + { + name: 'checklist', + action: (e) => { + _toggleBlockZmd(e, 'checklist', '- [ ] ') }, - { - name: 'table', - action: EasyMDE.drawTable, - className: 'fa fa-table', - title: 'Table' + className: 'far fa-check-square', + title: 'Liste de taches' + }, + '|', + { + name: 'heading', + action: EasyMDE.toggleHeadingSmaller, + className: 'fas fa-heading', + title: 'Titres' + }, + '|', + { + name: 'image', + action: EasyMDE.drawImage, + className: 'far fa-image', + title: 'Image' + }, + { + name: 'link_btn', + action: EasyMDE.drawLink, + className: 'fa fa-link', + title: 'Lien' + }, + '|', + { + name: 'quote', + action: EasyMDE.toggleBlockquote, + className: 'fa fa-quote-left', + title: 'Citation' + }, + { + name: 'code', + action: EasyMDE.toggleCodeBlock, + className: 'fa fa-code', + title: 'Bloc de code coloré' + }, + { + name: 'math', + action: (e) => { + _toggleBlockZmd(e, 'math', '$$') }, - '|', - { - name: 'blocMenu', - action: (e) => { + className: 'fa fa-percent', + title: 'Formule mathématique' + }, + { + name: 'table', + action: EasyMDE.drawTable, + className: 'fa fa-table', + title: 'Table' + }, + '|', + { + name: 'blocMenu', + action: (e) => { + }, + className: 'fa fa-smile', + title: 'Emojis heureux', + children: [ + { + name: 'char1', + action: (e) => { + toggleEmoji(e.codemirror, ':)') + }, + className: 'emoji smile', + title: ':)' }, - className: 'fa fa-smile', - title: 'Emojis heureux', - children: [ - { - name: 'char1', - action: (e) => { - toggleEmoji(e.codemirror, ':)') - }, - className: 'emoji smile', - title: ':)' + { + name: 'char2', + action: (e) => { + toggleEmoji(e.codemirror, ':D') }, - { - name: 'char2', - action: (e) => { - toggleEmoji(e.codemirror, ':D') - }, - className: 'emoji heureux', - title: ':D' + className: 'emoji heureux', + title: ':D' + }, + { + name: 'char3', + action: (e) => { + toggleEmoji(e.codemirror, ';)') }, - { - name: 'char3', - action: (e) => { - toggleEmoji(e.codemirror, ';)') - }, - className: 'emoji clin', - title: ';)' + className: 'emoji clin', + title: ';)' + }, + { + name: 'char8', + action: (e) => { + toggleEmoji(e.codemirror, ':soleil:') }, - { - name: 'char8', - action: (e) => { - toggleEmoji(e.codemirror, ':soleil:') - }, - className: 'emoji soleil', - title: ':soleil:' + className: 'emoji soleil', + title: ':soleil:' + }, + { + name: 'char4', + action: (e) => { + toggleEmoji(e.codemirror, ':p') }, - { - name: 'char4', - action: (e) => { - toggleEmoji(e.codemirror, ':p') - }, - className: 'emoji langue', - title: ':P' + className: 'emoji langue', + title: ':P' + }, + { + name: 'char5', + action: (e) => { + toggleEmoji(e.codemirror, ':lol:') }, - { - name: 'char5', - action: (e) => { - toggleEmoji(e.codemirror, ':lol:') - }, - className: 'emoji rire', - title: ':lol:' + className: 'emoji rire', + title: ':lol:' + }, + { + name: 'char6', + action: (e) => { + toggleEmoji(e.codemirror, '^^') }, - { - name: 'char6', - action: (e) => { - toggleEmoji(e.codemirror, '^^') - }, - className: 'emoji hihi', - title: '^^' + className: 'emoji hihi', + title: '^^' + }, + { + name: 'char7', + action: (e) => { + toggleEmoji(e.codemirror, ':ange:') }, - { - name: 'char7', - action: (e) => { - toggleEmoji(e.codemirror, ':ange:') - }, - className: 'emoji ange', - title: ':ange:' + className: 'emoji ange', + title: ':ange:' + }, + { + name: 'char9', + action: (e) => { + toggleEmoji(e.codemirror, ':popcorn:') }, - { - name: 'char9', - action: (e) => { - toggleEmoji(e.codemirror, ':popcorn:') - }, - className: 'emoji popcorn', - title: ':popcorn:' + className: 'emoji popcorn', + title: ':popcorn:' + }, + { + name: 'char10', + action: (e) => { + toggleEmoji(e.codemirror, ':bounce:') }, - { - name: 'char10', - action: (e) => { - toggleEmoji(e.codemirror, ':bounce:') - }, - className: 'emoji bounce', - title: ':bounce:' - } - ] - }, - { - name: 'blocMenu', - action: (e) => { + className: 'emoji bounce', + title: ':bounce:' + } + ] + }, + { + name: 'blocMenu', + action: (e) => { + }, + className: 'fa fa-frown', + title: 'Emojis mécontents', + children: [ + { + name: 'char1', + action: (e) => { + toggleEmoji(e.codemirror, ':(') + }, + className: 'emoji triste', + title: ':(' }, - className: 'fa fa-frown', - title: 'Emojis mécontents', - children: [ - { - name: 'char1', - action: (e) => { - toggleEmoji(e.codemirror, ':(') - }, - className: 'emoji triste', - title: ':(' + { + name: 'char2', + action: (e) => { + toggleEmoji(e.codemirror, ':colere:') }, - { - name: 'char2', - action: (e) => { - toggleEmoji(e.codemirror, ':colere:') - }, - className: 'emoji angry', - title: ':colere:' + className: 'emoji angry', + title: ':colere:' + }, + { + name: 'char3', + action: (e) => { + toggleEmoji(e.codemirror, ':colere2:') }, - { - name: 'char3', - action: (e) => { - toggleEmoji(e.codemirror, ':colere2:') - }, - className: 'emoji mechant', - title: ':colere2:' + className: 'emoji mechant', + title: ':colere2:' + }, + { + name: 'char4', + action: (e) => { + toggleEmoji(e.codemirror, ':diable:') }, - { - name: 'char4', - action: (e) => { - toggleEmoji(e.codemirror, ':diable:') - }, - className: 'emoji diable', - title: ':diable:' + className: 'emoji diable', + title: ':diable:' + }, + { + name: 'char5', + action: (e) => { + toggleEmoji(e.codemirror, ':\'(') }, - { - name: 'char5', - action: (e) => { - toggleEmoji(e.codemirror, ':\'(') - }, - className: 'emoji pleure', - title: ':\'(' + className: 'emoji pleure', + title: ':\'(' + }, + { + name: 'char6', + action: (e) => { + toggleEmoji(e.codemirror, '>_<') }, - { - name: 'char6', - action: (e) => { - toggleEmoji(e.codemirror, '>_<') - }, - className: 'emoji pinch', - title: '>_<' - } - ] - }, - { - name: 'blocMenu', - action: (e) => { + className: 'emoji pinch', + title: '>_<' + } + ] + }, + { + name: 'blocMenu', + action: (e) => { - }, - className: 'fa fa-meh', - title: 'Emojis autres', - children: [ - { - name: 'char1', - action: (e) => { - toggleEmoji(e.codemirror, ':euh:') - }, - className: 'emoji unsure', - title: ':euh:' + }, + className: 'fa fa-meh', + title: 'Emojis autres', + children: [ + { + name: 'char1', + action: (e) => { + toggleEmoji(e.codemirror, ':euh:') }, - { - name: 'char2', - action: (e) => { - toggleEmoji(e.codemirror, ':o') - }, - className: 'emoji huh', - title: ':o' + className: 'emoji unsure', + title: ':euh:' + }, + { + name: 'char2', + action: (e) => { + toggleEmoji(e.codemirror, ':o') }, - { - name: 'char3', - action: (e) => { - toggleEmoji(e.codemirror, ':B') - }, - className: 'emoji b', - title: ':B' + className: 'emoji huh', + title: ':o' + }, + { + name: 'char3', + action: (e) => { + toggleEmoji(e.codemirror, ':B') }, - { - name: 'char4', - action: (e) => { - toggleEmoji(e.codemirror, 'o_O') - }, - className: 'emoji blink', - title: 'o_O' + className: 'emoji b', + title: ':B' + }, + { + name: 'char4', + action: (e) => { + toggleEmoji(e.codemirror, 'o_O') }, - { - name: 'char5', - action: (e) => { - toggleEmoji(e.codemirror, ':-°') - }, - className: 'emoji siffle', - title: ':-°' + className: 'emoji blink', + title: 'o_O' + }, + { + name: 'char5', + action: (e) => { + toggleEmoji(e.codemirror, ':-°') }, - { - name: 'char6', - action: (e) => { - toggleEmoji(e.codemirror, ':magicien:') - }, - className: 'emoji magicien', - title: ':magicien:' + className: 'emoji siffle', + title: ':-°' + }, + { + name: 'char6', + action: (e) => { + toggleEmoji(e.codemirror, ':magicien:') }, - { - name: 'char8', - action: (e) => { - toggleEmoji(e.codemirror, ':pirate:') - }, - className: 'emoji pirate', - title: ':pirate:' + className: 'emoji magicien', + title: ':magicien:' + }, + { + name: 'char8', + action: (e) => { + toggleEmoji(e.codemirror, ':pirate:') }, - { - name: 'char9', - action: (e) => { - toggleEmoji(e.codemirror, ':honte:') - }, - className: 'emoji rouge', - title: ':honte:' + className: 'emoji pirate', + title: ':pirate:' + }, + { + name: 'char9', + action: (e) => { + toggleEmoji(e.codemirror, ':honte:') }, - { - name: 'char10', - action: (e) => { - toggleEmoji(e.codemirror, ':waw:') - }, - className: 'emoji waw', - title: ':waw:' + className: 'emoji rouge', + title: ':honte:' + }, + { + name: 'char10', + action: (e) => { + toggleEmoji(e.codemirror, ':waw:') }, - { - name: 'char11', - action: (e) => { - toggleEmoji(e.codemirror, ':zorro:') - }, - className: 'emoji zorro', - title: ':zorro:' + className: 'emoji waw', + title: ':waw:' + }, + { + name: 'char11', + action: (e) => { + toggleEmoji(e.codemirror, ':zorro:') }, - { - name: 'char7', - action: (e) => { - toggleEmoji(e.codemirror, ':ninja:') - }, - className: 'emoji ninja', - title: ':ninja:' - } - ] - }, - '|', - { - name: 'blocMenu', - action: (e) => { - _toggleBlockZmd(e, 'blocInformation', '| ') + className: 'emoji zorro', + title: ':zorro:' }, - className: 'fa fa-info', - title: 'Bloc spéciaux', - children: [ - { - name: 'blocInformation', - action: (e) => { - _toggleBlockZmd(e, 'blocInformation', '| ') - }, - className: 'fa fa-info bloc_information', - title: 'Bloc information' + { + name: 'char7', + action: (e) => { + toggleEmoji(e.codemirror, ':ninja:') }, - { - name: 'blocQuestion', - action: (e) => { - _toggleBlockZmd(e, 'blocQuestion', '| ') - }, - className: 'fa fa-question bloc_question', - title: 'Bloc question' + className: 'emoji ninja', + title: ':ninja:' + } + ] + }, + '|', + { + name: 'blocMenu', + action: (e) => { + _toggleBlockZmd(e, 'blocInformation', '| ') + }, + className: 'fa fa-info', + title: 'Bloc spéciaux', + children: [ + { + name: 'blocInformation', + action: (e) => { + _toggleBlockZmd(e, 'blocInformation', '| ') }, - { - name: 'blocWarning', - action: (e) => { - _toggleBlockZmd(e, 'blocWarning', '| ') - }, - className: 'fas fa-exclamation-triangle bloc_warning', - title: 'Bloc attention' + className: 'fa fa-info bloc_information', + title: 'Bloc information' + }, + { + name: 'blocQuestion', + action: (e) => { + _toggleBlockZmd(e, 'blocQuestion', '| ') }, - { - name: 'blocError', - action: (e) => { - _toggleBlockZmd(e, 'blocError', '| ') - }, - className: 'fas fa-times-circle bloc_error', - title: 'Bloc erreur' + className: 'fa fa-question bloc_question', + title: 'Bloc question' + }, + { + name: 'blocWarning', + action: (e) => { + _toggleBlockZmd(e, 'blocWarning', '| ') }, - { - name: 'blocSecret', - action: (e) => { - _toggleBlockZmd(e, 'blocSecret', '| ') - }, - className: 'fa fa-eye-slash', - title: 'Bloc secret' + className: 'fas fa-exclamation-triangle bloc_warning', + title: 'Bloc attention' + }, + { + name: 'blocError', + action: (e) => { + _toggleBlockZmd(e, 'blocError', '| ') }, - { - name: 'blocNeutral', - action: (e) => { - _toggleBlockZmd(e, 'blocNeutral', '| ') - }, - className: 'fa fa-sticky-note', - title: 'Bloc neutre' - } - ] - }, - '|', - { - name: 'abc-spellchecker', - action: (evt) => {}, - className: 'fas fa-spell-check', - title: 'Correcteur orthographique externe' - }, - { - name: 'abc-grammalecte', - action: (evt) => { - oGrammalecteAPI.openPanelForText(easyMDE.codemirror.getValue(), easyMDE.codemirror.display.lineDiv) + className: 'fas fa-times-circle bloc_error', + title: 'Bloc erreur' }, - className: 'fa zdsicon zi-grammalecte', - title: 'Correcteur orthographique externe' - }, - { - name: 'switch-contentAreaStyle', - action: (evt) => { - if (easyMDE.isFullscreenActive()) { - easyMDE.toggleFullScreen() - } - const wrapper = easyMDE.codemirror.getWrapperElement() - $(wrapper.parentElement).children('.textarea-multivers').toggle() - $(wrapper).toggle() - // deactivating buttons incompatible with the textarea mode - const $toolbar = $(easyMDE.element.parentElement).children('.editor-toolbar') - if ($toolbar.hasClass('disabled-for-textarea-mode')) { - $toolbar.removeClass('disabled-for-textarea-mode') - } else { - $toolbar.addClass('disabled-for-textarea-mode') - } - easyMDE.codemirror.refresh() + { + name: 'blocSecret', + action: (e) => { + _toggleBlockZmd(e, 'blocSecret', '| ') + }, + className: 'fa fa-eye-slash', + title: 'Bloc secret' }, - className: 'fas fa-remove-format', - title: 'Zone de texte sans mise en forme' - }, - '|', - { - name: 'preview', - action: EasyMDE.togglePreview, - className: 'fa fa-eye no-disable disable-for-textarea-mode', - title: 'Aperçu' - }, - { - name: 'side-by-side', - action: EasyMDE.toggleSideBySide, - className: 'fa fa-columns no-disable no-mobile disable-for-textarea-mode', - title: 'Aperçu sur le coté' - }, - { - name: 'fullscreen', - action: EasyMDE.toggleFullScreen, - className: 'fa fa-arrows-alt no-disable no-mobile disable-for-textarea-mode', - title: 'Plein écran' + { + name: 'blocNeutral', + action: (e) => { + _toggleBlockZmd(e, 'blocNeutral', '| ') + }, + className: 'fa fa-sticky-note', + title: 'Bloc neutre' + } + ] + }, + '|', + { + name: 'abc-spellchecker', + action: (evt) => {}, + className: 'fas fa-spell-check', + title: 'Correcteur orthographique externe' + }, + { + name: 'abc-grammalecte', + action: (evt) => { + oGrammalecteAPI.openPanelForText(easyMDE.codemirror.getValue(), easyMDE.codemirror.display.lineDiv) }, - '|', - { - name: 'blocquizz', - action: (e) => { - _toggleBlockZmd(e, 'blocquizz', '') - }, - className: 'fas fa-question', - title: 'Bloc quizz' + className: 'fa zdsicon zi-grammalecte', + title: 'Correcteur orthographique externe' + }, + { + name: 'switch-contentAreaStyle', + action: (evt) => { + if (easyMDE.isFullscreenActive()) { + easyMDE.toggleFullScreen() + } + const wrapper = easyMDE.codemirror.getWrapperElement() + $(wrapper.parentElement).children('.textarea-multivers').toggle() + $(wrapper).toggle() + // deactivating buttons incompatible with the textarea mode + const $toolbar = $(easyMDE.element.parentElement).children('.editor-toolbar') + if ($toolbar.hasClass('disabled-for-textarea-mode')) { + $toolbar.removeClass('disabled-for-textarea-mode') + } else { + $toolbar.addClass('disabled-for-textarea-mode') + } + easyMDE.codemirror.refresh() }, - ] + className: 'fas fa-remove-format', + title: 'Zone de texte sans mise en forme' + }, + '|', + { + name: 'preview', + action: EasyMDE.togglePreview, + className: 'fa fa-eye no-disable disable-for-textarea-mode', + title: 'Aperçu' + }, + { + name: 'side-by-side', + action: EasyMDE.toggleSideBySide, + className: 'fa fa-columns no-disable no-mobile disable-for-textarea-mode', + title: 'Aperçu sur le coté' + }, + { + name: 'fullscreen', + action: EasyMDE.toggleFullScreen, + className: 'fa fa-arrows-alt no-disable no-mobile disable-for-textarea-mode', + title: 'Plein écran' + }, + '|' + ] + + const QuizzButton = { + name: 'blocquizz', + action: (e) => { + _toggleBlockZmd(e, 'blocquizz', '') + }, + className: 'fas fa-question', + title: 'Bloc quizz' + }; + + const currentURL = window.location.href + const Toolbar = ( currentURL.includes('/contenus/') || currentURL.includes('/forums/') ) ? [...BaseToolbar, QuizzButton] : BaseToolbar; + + /* global EasyMDE */ + const easyMDE = new EasyMDE({ + autoDownloadFontAwesome: false, + element: this, + forceSync: true, + autosave: { + enabled: true, + uniqueId: mdeUniqueKey, + delay: 1000 + }, + indentWithTabs: false, + minHeight: minHeight + 'px', + placeholder: 'Votre message au format Markdown', + promptURLs: true, + promptTexts: { + image: 'Url de votre image', + link: 'Url de votre lien' + }, + uploadImage: true, + imageUploadFunction: uploadImage, + imageTexts: { + sbInit: 'Joindre des images par glisser-déposer ou coller depuis le presse-papiers.', + sbOnDragEnter: 'Déposez l\'image pour l\'envoyer dans votre galerie.', + sbOnDrop: 'Téléchargement d\'images #images_names#', + sbProgress: 'Téléchargement #file_name#: #progress#%', + sbOnUploaded: 'Image téléchargée #image_name#' + }, + spellChecker: false, + inputStyle: 'contenteditable', + nativeSpellcheck: true, + sideBySideFullscreen: false, + promptAbbrv: true, + theme: 'zest', + previewRender: customMarkdownParser, + syncSideBySidePreviewScroll: false, + toolbar: Toolbar }) if (smdeUniqueContent != null && localStorage['smde_' + mdeUniqueKey] !== textarea.value) { From e1003f233742cdaf41ab6bc592a3f8108a9fdada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?FACEN=20th=C3=A9o?= Date: Wed, 3 May 2023 14:33:16 +0200 Subject: [PATCH 68/78] modification de style pour quizz et stat --- assets/js/content-quizz.js | 8 ++++---- assets/scss/pages/_stats.scss | 8 +++++--- templates/misc/quizz.graph.part.html | 6 +++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 126bf71d5e..51e9a78765 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -429,12 +429,12 @@ if (currentURL.includes("/tutoriels/")) { function iconMaker(isGood) { // Create a new icon element - const icon = document.createElement('i'); + const icon = document.createElement('div'); icon.classList.add('fas'); isGood ? icon.classList.add('fa-check') : icon.classList.add('fa-exclamation-triangle') - icon.style.fontSize = '24px'; + icon.style.fontSize = '10px'; icon.style.transform = 'scale(2)'; - icon.style.marginLeft = '700px' + icon.style.margin = '10px' return icon } @@ -578,4 +578,4 @@ if (currentURL.includes("/tutoriels/")) { }) -} \ No newline at end of file +} diff --git a/assets/scss/pages/_stats.scss b/assets/scss/pages/_stats.scss index 5f3c300109..0441e55304 100644 --- a/assets/scss/pages/_stats.scss +++ b/assets/scss/pages/_stats.scss @@ -69,13 +69,15 @@ align-items: center; width: 100%; margin-bottom: 10px; - justify-content: space-evenly; + justify-content: space-around; } -.quizz-answers li span:first-child { - width: 70%; +.progress-stat { + width : 20vw; + text-align: center; } + .quizz-answers li span:last-child { font-weight: bold; font-size: 16px; diff --git a/templates/misc/quizz.graph.part.html b/templates/misc/quizz.graph.part.html index 5dbb52b929..139d3a6eb1 100644 --- a/templates/misc/quizz.graph.part.html +++ b/templates/misc/quizz.graph.part.html @@ -18,12 +18,12 @@

    {% trans graph_title %} {{ question|emarkdown }
      {% for answer_name, answer_stats in stats.responses.items %}
    • - {{ answer_name|emarkdown }} + {{ answer_name|emarkdown }} - - {{answer_stats.nb}} / {{ stats.total }} + {{answer_stats.nb}} / {{ stats.total }}
    • {% endfor %}
    From 53509b472a9a270851f227d9860538b1aad727c1 Mon Sep 17 00:00:00 2001 From: Alexandre Bertin Date: Wed, 3 May 2023 15:19:16 +0200 Subject: [PATCH 69/78] =?UTF-8?q?compl=C3=A9ment=20dans=20la=20documentati?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/source/back-end/quizz.rst | 95 +++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/doc/source/back-end/quizz.rst b/doc/source/back-end/quizz.rst index feadde563d..da5d238227 100644 --- a/doc/source/back-end/quizz.rst +++ b/doc/source/back-end/quizz.rst @@ -37,3 +37,98 @@ Globalement, l'ajout de type de questions, disons "texte libre" pour l'exemple s - Implémenter dans zMarkdown un préprocesseur de ``quizzCustomBlock`` qui permettra de remplacer les ``inlineCode`` par un texte composé de ``______`` dans le quizz et les laissera intacts dans la correction - Adapter ``content_quizz.js`` et ``statistics.py`` pour que la correction se fasse et que les statistiques remontent +Comportement attendu des quiz +============================= + +- Les quiz peuvent être créés dans les tutoriels. +- Dans une session, un utilisateur non connecté ne peut répondre qu’une seule fois à un quiz +- Un utilisateur connecté peut répondre plusieurs fois à un quiz +- Le propriétaire du quiz et le staff ont accès à une page contenant les statistiques (nombre de réponses à chaque question) +- Le propriétaire et le staff ont la possibilité de réinitialiser les statistiques de chaque quiz et de chaque question d’un quiz indépendamment à l’aide d’un bouton +- Une question de quiz contient au moins une bonne réponse (pas de test) +- Une question quiz doit avoir une explication qui s’affiche lorsqu'il répond au quiz +- Lors de la modification du tutoriel, les statistiques doivent rester cohérentes avec les quiz qui sont encore présents dans le tutoriel +- Lors d’une bonne réponse, la couleur de fond passe en vert, en orange pour une bonne réponse partielle (ex : une des deux bonnes réponses a été choisie mais pas l’autre). Enfin rouge pour une mauvaise réponse. + +Comment créer un quiz +===================== + +la Création d’un quiz se fait directement en zmarkdown de la façon suivante : + +[[quizz | la question1]] +| - [ ] réponse 1 +| - [x] bonne réponse +| - [ ] réponse 2 +| - réponse 2 est correcte, car Elle est correcte + +[[quizz | la question2]] +| - [x] réponse 1 +| - [x] bonne réponse +| - [ ] réponse 2 +| - réponse 1 et 2 sont correcte car Elle sont bonnes + +Contenu du fichier +================== + +Variables globales +------------------ + +- answers: un objet vide qui contiendra les réponses des utilisateurs aux questions du quizz. +- idCounter: un compteur pour générer des identifiants uniques pour chaque question du quizz. +- idBias: une valeur qui sert à décaler la numérotation des questions du quizz en cas de suppression d'une question. + +Fonctions +--------- + +- initializePipeline(answers): une fonction qui initialise une série de fonctions pour traiter les réponses des utilisateurs. +- injectForms(div, answers): une fonction qui ajoute les éléments de formulaire pour les questions du quizz à un élément de la page HTML. +- sendQuizzStatistics(form, statistics): une fonction qui envoie les statistiques de la réponse du quizz à un serveur à l'aide d'une requête XMLHttpRequest. +- displayResultAfterSubmitButton(form): une fonction qui affiche la réponse correcte pour chaque question du quizz après que l'utilisateur a soumis ses réponses. +- QuizzAnswered(form): une fonction qui teste si toutes les questions du quizz ont une réponse. +- iconMaker(isGood): une fonction qui crée un élément d'icône avec l'icône "check" ou "exclamation-triangle" de Font Awesome en fonction de si la réponse de l'utilisateur est correcte ou non. +- getQuestionText(question): une fonction qui retourne le texte de la question. +- getAnswerText(liWrapper): une fonction qui retourne le texte de la réponse. +- computeForm(formData, answers): une fonction qui compare les réponses de l'utilisateur avec les réponses attendues et retourne une liste des réponses incorrectes et une liste de toutes les réponses. +- markBadAnswers(form, badAnswerNames, answers): une fonction qui marque les réponses incorrectes pour chaque question du quizz. + +Événements +---------- + +- 'submit' pour chaque formulaire de quizz: lorsque l'utilisateur soumet ses réponses, le code traite les réponses et envoie les statistiques à un serveur. + +Sélectionneurs DOM (Document Object Model) +------------------------------------------ + +- document.querySelectorAll('div.quizz'): sélectionne tous les éléments HTML avec la classe quizz qui contiennent des questions de quizz. +- document.querySelectorAll('form.quizz'): sélectionne tous les éléments HTML avec la classe quizz qui sont des formulaires de quizz. + + +Base de données +=============== + +Représentation en base de donnée des quiz +----------------------------------------- + +- Le modèle QuizzQuestion contient les champs pour stocker une URL, la question et le type de question. Le type de question est un champ de texte avec une taille maximale de 15 caractères et une valeur par défaut de "qcm". +- Le modèle QuizzAvailableAnswer contient les champs pour stocker le libellé de la réponse, un indicateur pour savoir si c'est la bonne réponse, et une clé étrangère pour lier la réponse à la question correspondante dans le modèle QuizzQuestion. +- Le modèle QuizzUserAnswer contient les champs pour stocker une réponse donnée par l'utilisateur, la date de la réponse, et des clés étrangères pour lier la réponse à la question correspondante dans le modèle QuizzQuestion et au contenu publié associé. + +Commentaires sur les factories +------------------------------ + +- La classe QuizzQuestionFactory utilise la bibliothèque Python faker pour générer une URL, une question et un type de questions aléatoires. Le type de question est choisi parmi une liste de valeurs possibles ('qcm', 'open', 'bool'). +- La classe QuizzAvailableAnswerFactory génère des réponses possibles à une question de quiz. Elle utilise également faker pour générer une étiquette de réponse et un booléen pour indiquer si la réponse est correcte ou non. La clé étrangère related_question est une sous-factory qui crée une instance de QuizzQuestion. +- La classe QuizzUserAnswerFactory génère des réponses d'utilisateurs à des questions de quiz. Elle utilise une sous-factory pour créer une instance de PublishableContentFactory, un champ de réponse aléatoire et une clé étrangère related_question qui est une sous-factory pour créer une instance de QuizzQuestion. Le champ full_answer_id est généré à l'aide de faker pour simuler un identifiant unique. + + +Commentaires sur les tests +-------------------------- + +- test_question_count() : vérifie que deux questions ont été créées dans la base de données. +- test_answer_count() : vérifie que quatre réponses ont été créées dans la base de données. +- test_user_answer_count() : vérifie que deux réponses d'utilisateur ont été créées dans la base de données. +- test_related_user_answers() : vérifie que les réponses d'utilisateur sont bien liées aux bonnes questions. +- test_related_answers() : vérifie que les réponses disponibles sont bien liées aux bonnes questions. +- test_answer_is_good() : vérifie qu'au moins une réponse disponible pour chaque question est marquée comme bonne. +- test_user_answer_unique_id() : vérifie que chaque réponse d'utilisateur a un ID unique. +- test_nb_good_answers() : vérifie qu'il y a bien 2 bonnes et 2 mauvaises réponses pour une question qui a plusieurs réponses possibles. \ No newline at end of file From e71fb829fd29281dff051095514eacbe02e6f4d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?FACEN=20th=C3=A9o?= Date: Wed, 3 May 2023 15:41:49 +0200 Subject: [PATCH 70/78] fix -> boutton submit confondu avec la ligne de fin de section --- assets/js/content-quizz.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index 51e9a78765..ef2c59aa67 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -287,6 +287,7 @@ if (currentURL.includes("/tutoriels/")) { headings[heading.getAttribute('id')] = true const form = document.createElement('form') form.classList.add('quizz') + form.style.marginBottom = '50px' const submit = document.createElement('button') From 079d5e7d7354851d73ce6ff797f4a1c975db5956 Mon Sep 17 00:00:00 2001 From: Alexandre Bertin Date: Wed, 3 May 2023 15:46:06 +0200 Subject: [PATCH 71/78] remise de la doc --- doc/source/back-end/quizz.rst | 95 +++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/doc/source/back-end/quizz.rst b/doc/source/back-end/quizz.rst index feadde563d..da5d238227 100644 --- a/doc/source/back-end/quizz.rst +++ b/doc/source/back-end/quizz.rst @@ -37,3 +37,98 @@ Globalement, l'ajout de type de questions, disons "texte libre" pour l'exemple s - Implémenter dans zMarkdown un préprocesseur de ``quizzCustomBlock`` qui permettra de remplacer les ``inlineCode`` par un texte composé de ``______`` dans le quizz et les laissera intacts dans la correction - Adapter ``content_quizz.js`` et ``statistics.py`` pour que la correction se fasse et que les statistiques remontent +Comportement attendu des quiz +============================= + +- Les quiz peuvent être créés dans les tutoriels. +- Dans une session, un utilisateur non connecté ne peut répondre qu’une seule fois à un quiz +- Un utilisateur connecté peut répondre plusieurs fois à un quiz +- Le propriétaire du quiz et le staff ont accès à une page contenant les statistiques (nombre de réponses à chaque question) +- Le propriétaire et le staff ont la possibilité de réinitialiser les statistiques de chaque quiz et de chaque question d’un quiz indépendamment à l’aide d’un bouton +- Une question de quiz contient au moins une bonne réponse (pas de test) +- Une question quiz doit avoir une explication qui s’affiche lorsqu'il répond au quiz +- Lors de la modification du tutoriel, les statistiques doivent rester cohérentes avec les quiz qui sont encore présents dans le tutoriel +- Lors d’une bonne réponse, la couleur de fond passe en vert, en orange pour une bonne réponse partielle (ex : une des deux bonnes réponses a été choisie mais pas l’autre). Enfin rouge pour une mauvaise réponse. + +Comment créer un quiz +===================== + +la Création d’un quiz se fait directement en zmarkdown de la façon suivante : + +[[quizz | la question1]] +| - [ ] réponse 1 +| - [x] bonne réponse +| - [ ] réponse 2 +| - réponse 2 est correcte, car Elle est correcte + +[[quizz | la question2]] +| - [x] réponse 1 +| - [x] bonne réponse +| - [ ] réponse 2 +| - réponse 1 et 2 sont correcte car Elle sont bonnes + +Contenu du fichier +================== + +Variables globales +------------------ + +- answers: un objet vide qui contiendra les réponses des utilisateurs aux questions du quizz. +- idCounter: un compteur pour générer des identifiants uniques pour chaque question du quizz. +- idBias: une valeur qui sert à décaler la numérotation des questions du quizz en cas de suppression d'une question. + +Fonctions +--------- + +- initializePipeline(answers): une fonction qui initialise une série de fonctions pour traiter les réponses des utilisateurs. +- injectForms(div, answers): une fonction qui ajoute les éléments de formulaire pour les questions du quizz à un élément de la page HTML. +- sendQuizzStatistics(form, statistics): une fonction qui envoie les statistiques de la réponse du quizz à un serveur à l'aide d'une requête XMLHttpRequest. +- displayResultAfterSubmitButton(form): une fonction qui affiche la réponse correcte pour chaque question du quizz après que l'utilisateur a soumis ses réponses. +- QuizzAnswered(form): une fonction qui teste si toutes les questions du quizz ont une réponse. +- iconMaker(isGood): une fonction qui crée un élément d'icône avec l'icône "check" ou "exclamation-triangle" de Font Awesome en fonction de si la réponse de l'utilisateur est correcte ou non. +- getQuestionText(question): une fonction qui retourne le texte de la question. +- getAnswerText(liWrapper): une fonction qui retourne le texte de la réponse. +- computeForm(formData, answers): une fonction qui compare les réponses de l'utilisateur avec les réponses attendues et retourne une liste des réponses incorrectes et une liste de toutes les réponses. +- markBadAnswers(form, badAnswerNames, answers): une fonction qui marque les réponses incorrectes pour chaque question du quizz. + +Événements +---------- + +- 'submit' pour chaque formulaire de quizz: lorsque l'utilisateur soumet ses réponses, le code traite les réponses et envoie les statistiques à un serveur. + +Sélectionneurs DOM (Document Object Model) +------------------------------------------ + +- document.querySelectorAll('div.quizz'): sélectionne tous les éléments HTML avec la classe quizz qui contiennent des questions de quizz. +- document.querySelectorAll('form.quizz'): sélectionne tous les éléments HTML avec la classe quizz qui sont des formulaires de quizz. + + +Base de données +=============== + +Représentation en base de donnée des quiz +----------------------------------------- + +- Le modèle QuizzQuestion contient les champs pour stocker une URL, la question et le type de question. Le type de question est un champ de texte avec une taille maximale de 15 caractères et une valeur par défaut de "qcm". +- Le modèle QuizzAvailableAnswer contient les champs pour stocker le libellé de la réponse, un indicateur pour savoir si c'est la bonne réponse, et une clé étrangère pour lier la réponse à la question correspondante dans le modèle QuizzQuestion. +- Le modèle QuizzUserAnswer contient les champs pour stocker une réponse donnée par l'utilisateur, la date de la réponse, et des clés étrangères pour lier la réponse à la question correspondante dans le modèle QuizzQuestion et au contenu publié associé. + +Commentaires sur les factories +------------------------------ + +- La classe QuizzQuestionFactory utilise la bibliothèque Python faker pour générer une URL, une question et un type de questions aléatoires. Le type de question est choisi parmi une liste de valeurs possibles ('qcm', 'open', 'bool'). +- La classe QuizzAvailableAnswerFactory génère des réponses possibles à une question de quiz. Elle utilise également faker pour générer une étiquette de réponse et un booléen pour indiquer si la réponse est correcte ou non. La clé étrangère related_question est une sous-factory qui crée une instance de QuizzQuestion. +- La classe QuizzUserAnswerFactory génère des réponses d'utilisateurs à des questions de quiz. Elle utilise une sous-factory pour créer une instance de PublishableContentFactory, un champ de réponse aléatoire et une clé étrangère related_question qui est une sous-factory pour créer une instance de QuizzQuestion. Le champ full_answer_id est généré à l'aide de faker pour simuler un identifiant unique. + + +Commentaires sur les tests +-------------------------- + +- test_question_count() : vérifie que deux questions ont été créées dans la base de données. +- test_answer_count() : vérifie que quatre réponses ont été créées dans la base de données. +- test_user_answer_count() : vérifie que deux réponses d'utilisateur ont été créées dans la base de données. +- test_related_user_answers() : vérifie que les réponses d'utilisateur sont bien liées aux bonnes questions. +- test_related_answers() : vérifie que les réponses disponibles sont bien liées aux bonnes questions. +- test_answer_is_good() : vérifie qu'au moins une réponse disponible pour chaque question est marquée comme bonne. +- test_user_answer_unique_id() : vérifie que chaque réponse d'utilisateur a un ID unique. +- test_nb_good_answers() : vérifie qu'il y a bien 2 bonnes et 2 mauvaises réponses pour une question qui a plusieurs réponses possibles. \ No newline at end of file From 59dcda2e51aaa8f28a06d0915e6a4d498e7adb10 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Thu, 4 May 2023 14:32:29 +0200 Subject: [PATCH 72/78] quick fix --- templates/tutorialv2/stats/quizz_stats.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/templates/tutorialv2/stats/quizz_stats.html b/templates/tutorialv2/stats/quizz_stats.html index 101e53152c..c1ca595c8f 100644 --- a/templates/tutorialv2/stats/quizz_stats.html +++ b/templates/tutorialv2/stats/quizz_stats.html @@ -16,9 +16,6 @@ {% endblock %} {% block content_out %} -
    - {% trans "Quizz" %} -
    {% include "misc/quizz.graph.part.html" with tab_name="tab-quizz-content" graph_title="Réponses à la question :" canvas_id="quizz-graph" metric="quizz" %} {% endblock %} From 23ca81b79084ff93932a52c1cbe462f448c37154 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 7 May 2023 13:18:05 +0200 Subject: [PATCH 73/78] added Http404 for invalid date format --- zds/tutorialv2/urls/urls_contents.py | 2 +- zds/tutorialv2/views/statistics.py | 32 ++++++++++++++++++---------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index c653f8f45c..9b172617d9 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -1,4 +1,4 @@ -from django.urls import path, re_path +from django.urls import path from django.views.generic.base import RedirectView from zds.tutorialv2.views.contents import ( diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index d25a6cb9d2..43eb8b55ff 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -13,7 +13,7 @@ from django.contrib import messages from django.core.exceptions import PermissionDenied from django.db.models import Count -from django.http import StreamingHttpResponse +from django.http import Http404, StreamingHttpResponse from django.utils.translation import gettext_lazy as _ from django.views.generic import FormView @@ -182,13 +182,18 @@ def get_ref_metrics(data): def get_start_and_end_dates(self): - end_date = self.request.GET.get("end_date", None) or date.today() - - end_date = datetime.strptime(str(end_date), "%Y-%m-%d").date() + try: + end_date = self.request.GET.get("end_date", None) or date.today() + end_date = datetime.strptime(str(end_date), "%Y-%m-%d").date() + except (TypeError, ValueError) as e: + raise Http404("Invalid end date format") from e - start_date = self.request.GET.get("start_date", None) or (end_date - timedelta(days=7)) + try: + start_date = self.request.GET.get("start_date", None) or (end_date - timedelta(days=7)) + start_date = datetime.strptime(str(start_date), "%Y-%m-%d").date() - start_date = datetime.strptime(str(start_date), "%Y-%m-%d").date() + except (TypeError, ValueError) as e: + raise Http404("Invalid start date format") from e return start_date, end_date @@ -368,13 +373,18 @@ class QuizzContentStatistics(ContentStatisticsView): class DeleteQuizz(View): def get_start_and_end_dates(self): - end_date = self.request.GET.get("end_date", None) or date.today() - - end_date = datetime.strptime(str(end_date), "%Y-%m-%d").date() + try: + end_date = self.request.GET.get("end_date", None) or date.today() + end_date = datetime.strptime(str(end_date), "%Y-%m-%d").date() + except (TypeError, ValueError) as e: + raise Http404("Invalid end date format") from e - start_date = self.request.GET.get("start_date", None) or (end_date - timedelta(days=7)) + try: + start_date = self.request.GET.get("start_date", None) or (end_date - timedelta(days=7)) + start_date = datetime.strptime(str(start_date), "%Y-%m-%d").date() - start_date = datetime.strptime(str(start_date), "%Y-%m-%d").date() + except (TypeError, ValueError) as e: + raise Http404("Invalid start date format") from e return start_date, end_date From 275dc4c89fd1ee138c4ac3be40dd2766b155e4d6 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Sun, 7 May 2023 21:14:22 +0200 Subject: [PATCH 74/78] added tests of statistics deletting view --- .../tests/tests_views/tests_stats.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/zds/tutorialv2/tests/tests_views/tests_stats.py b/zds/tutorialv2/tests/tests_views/tests_stats.py index d05c1b38ed..e57d0a1d3b 100644 --- a/zds/tutorialv2/tests/tests_views/tests_stats.py +++ b/zds/tutorialv2/tests/tests_views/tests_stats.py @@ -1,9 +1,12 @@ import datetime from copy import deepcopy +import json from random import randint from unittest import mock + from django.conf import settings +from django.http import Http404 from django.test import TestCase from django.test.utils import override_settings from django.urls import reverse @@ -11,10 +14,12 @@ from zds.gallery.tests.factories import UserGalleryFactory from zds.member.tests.factories import ProfileFactory, StaffProfileFactory +from zds.tutorialv2.models.quizz import QuizzQuestion, QuizzUserAnswer from zds.tutorialv2.tests.factories import PublishableContentFactory, ContainerFactory, ExtractFactory from zds.tutorialv2.models.database import Validation, PublishedContent from zds.tutorialv2.publication_utils import publish_content from zds.tutorialv2.tests import TutorialTestMixin +from zds.tutorialv2.views.statistics import DeleteQuizz from zds.utils.tests.factories import LicenceFactory overridden_zds_app = deepcopy(settings.ZDS_APP) @@ -375,3 +380,77 @@ def get_small_opinion(self): opinion.save() opinion_draft = opinion.load_version() return publish_content(opinion, opinion_draft) + + +class DeleteQuizzTestCase(TestCase): + def setUp(self): + self.view = DeleteQuizz() + self.url = "/contenus/delete_quizz/" + + def test_invalid_start_date_format(self): + # Edge case test for invalid start date format + view = self.view + view.request = type("Request", (), {"GET": {"start_date": "invalid_date_format", "end_date": "2022-01-07"}})() + + with self.assertRaises(Http404): + start_date, end_date = view.get_start_and_end_dates() + + def test_invalid_end_date_format(self): + # Edge case test for invalid end date format + view = self.view + view.request = type("Request", (), {"GET": {"start_date": "2022-01-07", "end_date": "invalid_date_format"}})() + + with self.assertRaises(Http404): + start_date, end_date = view.get_start_and_end_dates() + + def test_get_start_and_end_dates_no_dates(self): + view = self.view + view.request = type("Request", (), {"GET": {}})() + start_date, end_date = view.get_start_and_end_dates() + assert end_date == datetime.date.today() + assert start_date == (end_date - datetime.timedelta(days=7)) + + def test_get_start_and_end_dates_valid_dates(self): + view = self.view + view.request = type("Request", (), {"GET": {"start_date": "2022-01-01", "end_date": "2022-01-07"}})() + start_date, end_date = view.get_start_and_end_dates() + assert start_date == datetime.date(2022, 1, 1) + assert end_date == datetime.date(2022, 1, 7) + + def test_delete_quizz_with_question(self): + + quizz_name = "example" + question_text = "What is your name?" + question = QuizzQuestion.objects.create(url=quizz_name, question=question_text) + QuizzUserAnswer.objects.create(related_question=question, date_answer=datetime.date.today()) + QuizzUserAnswer.objects.create( + related_question=question, date_answer=datetime.date.today() - datetime.timedelta(days=1) + ) + data = {"quizzName": quizz_name, "question": question_text} + # Send a request to delete the user answers for the question + response = self.client.post(self.url, data=json.dumps(data), content_type="application/json") + + # Assert that the response status code is 200 OK + self.assertEqual(response.status_code, 200) + # Assert that the user answers have been deleted + self.assertEqual(QuizzUserAnswer.objects.filter(related_question=question).count(), 0) + + def test_delete_quizz_without_question(self): + + quizz_name = "example" + question1 = QuizzQuestion.objects.create(url=quizz_name, question="What is your name?") + QuizzUserAnswer.objects.create(related_question=question1, date_answer=datetime.date.today()) + question2 = QuizzQuestion.objects.create(url=quizz_name, question="What is your age?") + QuizzUserAnswer.objects.create( + related_question=question2, date_answer=datetime.date.today() - datetime.timedelta(days=1) + ) + + data = {"quizzName": quizz_name} + # Send a request to delete all user answers for the quizz + response = self.client.post(self.url, data=json.dumps(data), content_type="application/json") + + # Assert that the response status code is 200 OK + self.assertEqual(response.status_code, 200) + + # Assert that all user answers have been deleted + self.assertEqual(QuizzUserAnswer.objects.filter(related_question__url=quizz_name).count(), 0) From 40d2967fe851ca5af89dd7830b6cd5b7bad57bbf Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 8 May 2023 11:36:58 +0200 Subject: [PATCH 75/78] quick fix of quizz squelette --- assets/js/editor-new.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/assets/js/editor-new.js b/assets/js/editor-new.js index 0edbee8b2f..a3b0876a87 100644 --- a/assets/js/editor-new.js +++ b/assets/js/editor-new.js @@ -251,9 +251,9 @@ else if (type === 'blocquizz') { if(window.location.href.includes('/contenus/')){ - shiftLines(cm, startPoint.line, '| - Explication : '); + shiftLines(cm, startPoint.line, '| - Votre explication'); shiftLines(cm, startPoint.line, '| - [ ] réponse 3'); - shiftLines(cm, startPoint.line, '| - [x] bonne réponse'); + shiftLines(cm, startPoint.line, '| - [x] réponse 2 ( bonne )'); } else { shiftLines(cm, startPoint.line, '| - [ ] réponse 3'); @@ -930,7 +930,6 @@ }, '|' ] - const QuizzButton = { name: 'blocquizz', action: (e) => { From 317b47f221c2a55d9f14f4d841fa97602da8b51c Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Mon, 8 May 2023 11:51:04 +0200 Subject: [PATCH 76/78] sondage in other branch Forum_sondage --- assets/js/content-sondage.js | 142 ----------------------------------- 1 file changed, 142 deletions(-) delete mode 100644 assets/js/content-sondage.js diff --git a/assets/js/content-sondage.js b/assets/js/content-sondage.js deleted file mode 100644 index 281eddbc06..0000000000 --- a/assets/js/content-sondage.js +++ /dev/null @@ -1,142 +0,0 @@ -/** - * The full quizz is contained in a div or article that has class "quizz". - * Then one question is inside a zmarkdown "custom-block" of type "custom-block-quizz". Two possibilities : - * - * Without explanation for correction : - * - * - *
    - *
    The question
    - *
    - *
    • the answer
    • - *
    • the good answer
    • - *
    - *
    - *
    - *
    - - */ - - -var currentURL = window.location.href; - -if (currentURL.includes("forums")) { - - let indeX = 0; - function Makesurvey(inputDomElementList) { - - - inputDomElementList.forEach((rb) => { - - - const ulWrapperElement = rb.parentElement.parentElement - // we give the ui an id to find the element in a more effective way later when the users answer the questions - if (!ulWrapperElement.getAttribute('id')) { - ulWrapperElement.setAttribute('id', 'id-' + (indeX++)) - } - - rb.setAttribute('name', ulWrapperElement.getAttribute('id')) - rb.setAttribute('type', 'radio') - - const questionBlock = ulWrapperElement.parentElement.parentElement - questionBlock.setAttribute('data-name', rb.getAttribute('name')) - - - rb.disabled = false - rb.checked = false - - }) - } - - - function initializeRadio() { - const radio = document.querySelectorAll('.custom-block-quizz input'); - Makesurvey(radio) - - } - - const initializePipeline = [initializeRadio] - - -/** - * As we are using forms to capture the answer and send the result back to user (and flushing stats for authors), - * we need to add an html form. - * - * The main issue is that it was asked to have many quizz inside a tutorial section, not just a list of questions in a - * specific section. As a result we had to find an heuristic : - * - if the current section has no h3 headings, we just span the form from the beggining to the end of section - * - if the current section has one or more h3 headings, we start the form just after it and end it at just before the - * next h3 heading if there is one or the end of form - * - * @param quizz current quizz container - * @param answers answers dictionary - */ - - let idCounter = 0 - - function injectForms(survey) { - - - const form = document.createElement('form') - form.classList.add('quizz') - - - const submit = document.createElement('button') - submit.innerText = 'Voter' - - const cancel = document.createElement('button') - cancel.innerText = 'Annuler' - - submit.classList.add('btn', 'btn-submit') - submit.setAttribute('id', `my-button-${idCounter}`); - - cancel.classList.add('btn', 'btn-cancel') - cancel.setAttribute('id', `my-button-cancel-${idCounter}`); - - const notAnswered = document.createElement('p') - notAnswered.classList.add('notAnswered') - - // form.method = 'POST' - // form.setAttribute('action', '') - form.setAttribute('id', `my-form-${idCounter}`); - idCounter++; - - form.appendChild(submit) - form.appendChild(cancel) - form.appendChild(notAnswered) - survey.appendChild(form) - - } - - - function displayResultAfterSubmitButton(quizz,result) { - - - const inputs = quizz.querySelectorAll('.custom-block-quizz input'); - - for (let input of inputs) { - - } - - } - - initializePipeline.forEach(func => func()) - - document.querySelectorAll('div.custom-block-quizz').forEach(div => { - injectForms(div) - }) - - document.querySelectorAll('div.custom-block-quizz').forEach(div => { - div.addEventListener('click',() => { - const id = event.target.id; - const button = div.querySelector(id); - // const submitBtn = div.querySelector('.btn-submit'); - // const cancelBtn = div.querySelector('.btn-cancel'); - console.log(id); - - }) - }) - -} - - From d9d29b82da3a65a63bc622b7bda5254331f4eafa Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Tue, 9 May 2023 15:13:52 +0200 Subject: [PATCH 77/78] quick fix --- assets/js/content-quizz.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/content-quizz.js b/assets/js/content-quizz.js index ef2c59aa67..cef28e24bd 100644 --- a/assets/js/content-quizz.js +++ b/assets/js/content-quizz.js @@ -486,7 +486,7 @@ if (currentURL.includes("/tutoriels/")) { } else { // If the quiz has already been submitted, disable the submit button submitBtn.setAttribute('disabled', true); - alert('Vous avez deja répondu, Veuillez vous connecter'); + alert('Vous avez déjà répondu, Veuillez vous connecter'); return; } } From 3a0fe77a70ba345af4de8fd46bd1122eaabc0cb9 Mon Sep 17 00:00:00 2001 From: BOZ Atman Date: Wed, 10 May 2023 00:34:32 +0200 Subject: [PATCH 78/78] deletting users responses when quizz question answers were updated // last commit :) --- zds/tutorialv2/views/statistics.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index 43eb8b55ff..3b20e3fcbc 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -57,7 +57,10 @@ def form_valid(self, form): known_labels = QuizzAvailableAnswer.objects.filter( related_question=db_question, label__in=answers_labels ).values_list("label", flat=True) + not_existing_answers = [label for label in answers_labels if label not in known_labels] + + QuizzUserAnswer.objects.exclude(answer__in=answers_labels).filter(related_question=db_question).delete() QuizzAvailableAnswer.objects.exclude(label__in=answers_labels).filter(related_question=db_question).delete() for label in not_existing_answers: