From 969fff1a3c9418b8d61c2a958047411e96f05957 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 13 Jun 2022 09:25:34 +0200 Subject: [PATCH 01/30] COMPATIBILITY: Support tag-group-chooser interface && add base CSS to wizard app --- .../templates/components/wizard-custom-field.hbs | 8 +++++--- assets/stylesheets/wizard/wizard_custom.scss | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index f51b9fbb..6866f3ef 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -19,7 +19,7 @@
- +
{{i18n "admin.wizard.field.required_label"}} {{input type="checkbox" checked=field.required}} @@ -54,7 +54,7 @@
- +
{{combo-box value=field.type @@ -216,7 +216,9 @@
{{tag-group-chooser + id=(concat field.id "-tag-groups") tagGroups=field.tag_groups + onChange=(action (mut field.tag_groups)) }}
@@ -257,7 +259,7 @@
- +
{{combo-box value=field.property diff --git a/assets/stylesheets/wizard/wizard_custom.scss b/assets/stylesheets/wizard/wizard_custom.scss index 659c97f5..6c91385f 100644 --- a/assets/stylesheets/wizard/wizard_custom.scss +++ b/assets/stylesheets/wizard/wizard_custom.scss @@ -1,5 +1,6 @@ @import "common/foundation/colors"; @import "common/foundation/variables"; +@import "common/foundation/base"; @import "common/base/code_highlighting"; @import "common/base/modal"; @import "common/base/onebox"; From a19a1fa3b1627b7847ff7fa07abdf710003c80d4 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 12:33:34 +0100 Subject: [PATCH 02/30] Refactor wizard client and add tests --- app/controllers/custom_wizard/wizard.rb | 115 +++-- app/views/layouts/qunit.html.erb | 28 ++ app/views/layouts/wizard.html.erb | 6 +- assets/javascripts/wizard-custom-globals.js | 6 - assets/javascripts/wizard-custom-start.js | 2 +- assets/javascripts/wizard-custom.js | 14 +- assets/javascripts/wizard-qunit.js | 16 + assets/javascripts/wizard/application.js.es6 | 19 + .../components/custom-user-selector.js.es6 | 9 +- .../wizard/components/field-validators.js.es6 | 3 + .../similar-topics-validator.js.es6 | 1 + .../wizard/components/validator.js.es6 | 3 +- .../components/wizard-composer-editor.js.es6 | 1 + .../wizard-composer-hyperlink.js.es6 | 1 + .../components/wizard-date-input.js.es6 | 1 + .../components/wizard-date-time-input.js.es6 | 2 + .../components/wizard-field-category.js.es6 | 2 + .../components/wizard-field-checkbox.js.es6 | 5 + .../wizard-field-composer-preview.js.es6 | 2 + .../components/wizard-field-composer.js.es6 | 2 + .../components/wizard-field-date-time.js.es6 | 2 + .../components/wizard-field-date.js.es6 | 2 + .../components/wizard-field-dropdown.js.es6 | 15 + .../components/wizard-field-group.js.es6 | 5 + .../components/wizard-field-number.js.es6 | 5 + .../wizard/components/wizard-field-tag.js.es6 | 5 + .../components/wizard-field-text.js.es6 | 9 + .../components/wizard-field-textarea.js.es6 | 9 + .../components/wizard-field-time.js.es6 | 2 + .../components/wizard-field-upload.js.es6 | 4 + .../wizard/components/wizard-field-url.js.es6 | 5 + .../wizard-field-user-selector.js.es6 | 5 + .../wizard/components/wizard-field.js.es6 | 34 ++ .../components/wizard-group-selector.js.es6 | 1 + .../wizard/components/wizard-no-access.js.es6 | 23 +- .../components/wizard-similar-topics.js.es6 | 1 + .../wizard/components/wizard-step-form.js.es6 | 9 + .../wizard/components/wizard-step.js.es6 | 247 +++++++++ .../components/wizard-text-field.js.es6 | 6 +- .../components/wizard-time-input.js.es6 | 4 +- .../wizard/controllers/custom.js.es6 | 3 - .../{custom-step.js.es6 => step.js.es6} | 11 +- .../wizard/controllers/wizard-index.js.es6 | 24 + .../wizard/controllers/wizard.js.es6 | 5 + .../javascripts/wizard/custom-wizard.js.es6 | 39 -- .../initializers/custom-wizard-step.js.es6 | 199 -------- .../wizard/initializers/custom-wizard.js.es6 | 126 ----- .../lib/initialize/create-contexts.js.es6 | 12 + .../lib/initialize/inject-objects.js.es6 | 49 ++ .../initialize/patch-components.js.es6} | 116 ++--- .../lib/initialize/register-files.js.es6 | 26 + .../wizard/lib/initialize/wizard.js.es6 | 49 ++ .../wizard/lib/utilities-lite.js.es6 | 71 --- assets/javascripts/wizard/models/field.js.es6 | 79 +++ assets/javascripts/wizard/models/site.js.es6 | 3 +- assets/javascripts/wizard/models/step.js.es6 | 114 +++++ .../models/{custom.js.es6 => wizard.js.es6} | 10 +- assets/javascripts/wizard/router.js.es6 | 17 + .../wizard/routes/application.js.es6 | 3 + .../wizard/routes/custom-steps.js.es6 | 5 - assets/javascripts/wizard/routes/index.js.es6 | 9 + .../{custom-step.js.es6 => step.js.es6} | 11 +- assets/javascripts/wizard/routes/steps.js.es6 | 7 + ...ustom-index.js.es6 => wizard-index.js.es6} | 26 +- .../routes/{custom.js.es6 => wizard.js.es6} | 59 ++- .../components/wizard-field-dropdown.hbs | 1 + .../wizard/templates/custom.index.hbs | 15 - assets/javascripts/wizard/templates/index.hbs | 1 + .../templates/{custom.step.hbs => step.hbs} | 0 .../wizard/templates/wizard-index.hbs | 3 + .../templates/{custom.hbs => wizard.hbs} | 4 - .../wizard/tests/acceptance/field-test.js.es6 | 135 +++++ .../wizard/tests/acceptance/step-test.js.es6 | 47 ++ .../tests/acceptance/wizard-test.js.es6 | 72 +++ .../javascripts/wizard/tests/bootstrap.js.es6 | 17 + .../wizard/tests/fixtures/categories.js.es6 | 209 ++++++++ .../wizard/tests/fixtures/groups.js.es6 | 313 ++++++++++++ .../tests/fixtures/site-settings.js.es6 | 283 +++++++++++ .../wizard/tests/fixtures/tags.js.es6 | 22 + .../wizard/tests/fixtures/update.js.es6 | 5 + .../wizard/tests/fixtures/user.js.es6 | 34 ++ .../wizard/tests/fixtures/users.js.es6 | 14 + .../wizard/tests/fixtures/wizard.js.es6 | 469 ++++++++++++++++++ .../wizard/tests/helpers/acceptance.js.es6 | 53 ++ .../wizard/tests/helpers/start-app.js.es6 | 19 + .../wizard/tests/helpers/step.js.es6 | 20 + .../wizard/tests/helpers/test.js.es6 | 7 + .../wizard/tests/helpers/wizard.js.es6 | 52 ++ .../javascripts/wizard/tests/pretender.js.es6 | 53 ++ .../stylesheets/wizard/custom/composer.scss | 2 +- config/routes.rb | 1 + .../extensions/extra_locales_controller.rb | 1 + lib/custom_wizard/validators/update.rb | 2 +- plugin.rb | 3 +- 94 files changed, 2887 insertions(+), 674 deletions(-) create mode 100644 app/views/layouts/qunit.html.erb delete mode 100644 assets/javascripts/wizard-custom-globals.js create mode 100644 assets/javascripts/wizard-qunit.js create mode 100644 assets/javascripts/wizard/application.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-group.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-number.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-tag.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-text.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-textarea.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-url.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-step-form.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-step.js.es6 delete mode 100644 assets/javascripts/wizard/controllers/custom.js.es6 rename assets/javascripts/wizard/controllers/{custom-step.js.es6 => step.js.es6} (76%) create mode 100644 assets/javascripts/wizard/controllers/wizard-index.js.es6 create mode 100644 assets/javascripts/wizard/controllers/wizard.js.es6 delete mode 100644 assets/javascripts/wizard/custom-wizard.js.es6 delete mode 100644 assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 delete mode 100644 assets/javascripts/wizard/initializers/custom-wizard.js.es6 create mode 100644 assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 create mode 100644 assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 rename assets/javascripts/wizard/{initializers/custom-wizard-field.js.es6 => lib/initialize/patch-components.js.es6} (60%) create mode 100644 assets/javascripts/wizard/lib/initialize/register-files.js.es6 create mode 100644 assets/javascripts/wizard/lib/initialize/wizard.js.es6 delete mode 100644 assets/javascripts/wizard/lib/utilities-lite.js.es6 create mode 100644 assets/javascripts/wizard/models/field.js.es6 create mode 100644 assets/javascripts/wizard/models/step.js.es6 rename assets/javascripts/wizard/models/{custom.js.es6 => wizard.js.es6} (94%) create mode 100644 assets/javascripts/wizard/router.js.es6 create mode 100644 assets/javascripts/wizard/routes/application.js.es6 delete mode 100644 assets/javascripts/wizard/routes/custom-steps.js.es6 create mode 100644 assets/javascripts/wizard/routes/index.js.es6 rename assets/javascripts/wizard/routes/{custom-step.js.es6 => step.js.es6} (78%) create mode 100644 assets/javascripts/wizard/routes/steps.js.es6 rename assets/javascripts/wizard/routes/{custom-index.js.es6 => wizard-index.js.es6} (50%) rename assets/javascripts/wizard/routes/{custom.js.es6 => wizard.js.es6} (59%) delete mode 100644 assets/javascripts/wizard/templates/custom.index.hbs create mode 100644 assets/javascripts/wizard/templates/index.hbs rename assets/javascripts/wizard/templates/{custom.step.hbs => step.hbs} (100%) create mode 100644 assets/javascripts/wizard/templates/wizard-index.hbs rename assets/javascripts/wizard/templates/{custom.hbs => wizard.hbs} (86%) create mode 100644 assets/javascripts/wizard/tests/acceptance/field-test.js.es6 create mode 100644 assets/javascripts/wizard/tests/acceptance/step-test.js.es6 create mode 100644 assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 create mode 100644 assets/javascripts/wizard/tests/bootstrap.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/categories.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/groups.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/tags.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/update.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/user.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/users.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/wizard.js.es6 create mode 100644 assets/javascripts/wizard/tests/helpers/acceptance.js.es6 create mode 100644 assets/javascripts/wizard/tests/helpers/start-app.js.es6 create mode 100644 assets/javascripts/wizard/tests/helpers/step.js.es6 create mode 100644 assets/javascripts/wizard/tests/helpers/test.js.es6 create mode 100644 assets/javascripts/wizard/tests/helpers/wizard.js.es6 create mode 100644 assets/javascripts/wizard/tests/pretender.js.es6 diff --git a/app/controllers/custom_wizard/wizard.rb b/app/controllers/custom_wizard/wizard.rb index 12e6bdff..43db5df8 100644 --- a/app/controllers/custom_wizard/wizard.rb +++ b/app/controllers/custom_wizard/wizard.rb @@ -1,54 +1,39 @@ # frozen_string_literal: true -class CustomWizard::WizardController < ::ApplicationController - include ApplicationHelper - prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'app', 'views')) - layout 'wizard' +class CustomWizard::WizardController < ::ActionController::Base + helper ApplicationHelper + include CurrentUser + include CanonicalURL::ControllerExtensions + include GlobalPath + + prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'views')) + layout :set_wizard_layout + + before_action :preload_wizard_json before_action :ensure_plugin_enabled before_action :ensure_logged_in, only: [:skip] + helper_method :wizard_page_title helper_method :wizard_theme_id helper_method :wizard_theme_lookup helper_method :wizard_theme_translations_lookup - def wizard - @builder = CustomWizard::Builder.new(params[:wizard_id].underscore, current_user) - @wizard ||= @builder.build - @wizard - end - - def wizard_page_title - wizard ? (wizard.name || wizard.id) : I18n.t('wizard.custom_title') - end - - def wizard_theme_id - wizard ? wizard.theme_id : nil - end - - def wizard_theme_lookup(name) - Theme.lookup_field(wizard_theme_id, mobile_view? ? :mobile : :desktop, name) - end - - def wizard_theme_translations_lookup - Theme.lookup_field(wizard_theme_id, :translations, I18n.locale) + def set_wizard_layout + action_name === 'qunit' ? 'qunit' : 'wizard' end def index respond_to do |format| format.json do - builder = CustomWizard::Builder.new(params[:wizard_id].underscore, current_user) - - if builder.wizard.present? - builder_opts = {} - builder_opts[:reset] = params[:reset] - built_wizard = builder.build(builder_opts, params) - - render_serialized(built_wizard, ::CustomWizard::WizardSerializer, root: false) + if wizard.present? + render json: CustomWizard::WizardSerializer.new(wizard, scope: guardian, root: false).as_json, status: 200 else render json: { error: I18n.t('wizard.none') } end end - format.html {} + format.html do + render "default/empty" + end end end @@ -62,8 +47,10 @@ class CustomWizard::WizardController < ::ApplicationController result = success_json if current_user && wizard.can_access? - if redirect_to = wizard.current_submission&.redirect_to - result.merge!(redirect_to: redirect_to) + submission = wizard.current_submission + + if submission.present? && submission.redirect_to + result.merge!(redirect_to: submission.redirect_to) end wizard.cleanup_on_skip! @@ -72,6 +59,64 @@ class CustomWizard::WizardController < ::ApplicationController render json: result end + def qunit + raise Discourse::InvalidAccess.new if Rails.env.production? + + respond_to do |format| + format.html do + render "default/empty" + end + end + end + + protected + + def ensure_logged_in + raise Discourse::NotLoggedIn.new unless current_user.present? + end + + def guardian + @guardian ||= Guardian.new(current_user, request) + end + + def wizard + @wizard ||= begin + builder = CustomWizard::Builder.new(params[:wizard_id].underscore, current_user) + return nil unless builder.present? + opts = {} + opts[:reset] = params[:reset] + builder.build(opts, params) + end + end + + def wizard_page_title + wizard ? (wizard.name || wizard.id) : I18n.t('wizard.custom_title') + end + + def wizard_theme_id + wizard ? wizard.theme_id : nil + end + + def wizard_theme_lookup(name) + Theme.lookup_field(wizard_theme_id, view_context.mobile_view? ? :mobile : :desktop, name) + end + + def wizard_theme_translations_lookup + Theme.lookup_field(wizard_theme_id, :translations, I18n.locale) + end + + def preload_wizard_json + return if request.xhr? || request.format.json? + return if request.method != "GET" + + store_preloaded("siteSettings", SiteSetting.client_settings_json) + end + + def store_preloaded(key, json) + @preloaded ||= {} + @preloaded[key] = json.gsub(" + + + Custom Wizard QUnit Test Runner + <%= discourse_stylesheet_link_tag(:test_helper, theme_id: nil) %> + <%= discourse_stylesheet_link_tag :wizard, theme_id: nil %> + <%= discourse_stylesheet_link_tag :wizard_custom %> + <%= preload_script "locales/en" %> + <%= preload_script "ember_jquery" %> + <%= preload_script "wizard-vendor" %> + <%= preload_script "wizard-custom" %> + <%= preload_script "wizard-raw-templates" %> + <%= preload_script "wizard-plugin" %> + <%= preload_script "pretty-text-bundle" %> + <%= preload_script "wizard-qunit" %> + <%= csrf_meta_tags %> + + + + <%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %> + + + + +
+
+ + diff --git a/app/views/layouts/wizard.html.erb b/app/views/layouts/wizard.html.erb index 16d119b7..f909ae84 100644 --- a/app/views/layouts/wizard.html.erb +++ b/app/views/layouts/wizard.html.erb @@ -11,10 +11,8 @@ <%= preload_script "locales/#{I18n.locale}" %> <%= preload_script "ember_jquery" %> <%= preload_script "wizard-vendor" %> - <%= preload_script "wizard-application" %> - <%= preload_script "wizard-custom-globals" %> - <%= preload_script "wizard-raw-templates" %> <%= preload_script "wizard-custom" %> + <%= preload_script "wizard-raw-templates" %> <%= preload_script "wizard-plugin" %> <%= preload_script "pretty-text-bundle" %> @@ -58,5 +56,7 @@ <%= raw SvgSprite.bundle %>
+ + diff --git a/assets/javascripts/wizard-custom-globals.js b/assets/javascripts/wizard-custom-globals.js deleted file mode 100644 index 83923cba..00000000 --- a/assets/javascripts/wizard-custom-globals.js +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint no-undef: 0*/ -window.Discourse = {}; -window.Wizard = {}; -Wizard.SiteSettings = {}; -Discourse.__widget_helpers = {}; -Discourse.SiteSettings = Wizard.SiteSettings; diff --git a/assets/javascripts/wizard-custom-start.js b/assets/javascripts/wizard-custom-start.js index 3ffa9c5a..b92d2871 100644 --- a/assets/javascripts/wizard-custom-start.js +++ b/assets/javascripts/wizard-custom-start.js @@ -1,4 +1,4 @@ (function () { - let wizard = require("discourse/plugins/discourse-custom-wizard/wizard/custom-wizard").default.create(); + let wizard = require("discourse/plugins/discourse-custom-wizard/wizard/application").default.create(); wizard.start(); })(); diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js index 99a96a00..667d23fd 100644 --- a/assets/javascripts/wizard-custom.js +++ b/assets/javascripts/wizard-custom.js @@ -1,10 +1,13 @@ +//= require_tree_discourse truth-helpers/addon +//= require_tree_discourse discourse-common/addon +//= require_tree_discourse select-kit/addon +//= require_tree_discourse wizard/lib +//= require_tree_discourse wizard/mixins //= require_tree_discourse discourse/app/lib //= require_tree_discourse discourse/app/mixins //= require discourse/app/adapters/rest - //= require message-bus - //= require_tree_discourse discourse/app/models //= require discourse/app/helpers/category-link @@ -12,9 +15,6 @@ //= require discourse/app/helpers/format-username //= require discourse/app/helpers/share-url //= require discourse/app/helpers/decorate-username-selector -//= require discourse-common/addon/helpers/component-for-collection -//= require discourse-common/addon/helpers/component-for-row -//= require discourse-common/addon/lib/raw-templates //= require discourse/app/helpers/discourse-tag //= require discourse/app/services/app-events @@ -70,11 +70,11 @@ //= require bootbox.js //= require discourse-shims -//= require ./wizard/custom-wizard +//= require ./wizard/application +//= require ./wizard/router //= require_tree ./wizard/components //= require_tree ./wizard/controllers //= require_tree ./wizard/helpers -//= require_tree ./wizard/initializers //= require_tree ./wizard/lib //= require_tree ./wizard/models //= require_tree ./wizard/routes diff --git a/assets/javascripts/wizard-qunit.js b/assets/javascripts/wizard-qunit.js new file mode 100644 index 00000000..2866cb5d --- /dev/null +++ b/assets/javascripts/wizard-qunit.js @@ -0,0 +1,16 @@ +//= require route-recognizer +//= require fake_xml_http_request +//= require pretender +//= require qunit +//= require ember-qunit +//= require test-shims +//= require jquery.debug +//= require ember.debug +//= require ember-template-compiler + +//= require_tree ./wizard/tests/fixtures +//= require ./wizard/tests/pretender +//= require_tree ./wizard/tests/helpers +//= require_tree ./wizard/tests/acceptance + +//= require ./wizard/tests/bootstrap diff --git a/assets/javascripts/wizard/application.js.es6 b/assets/javascripts/wizard/application.js.es6 new file mode 100644 index 00000000..012949b3 --- /dev/null +++ b/assets/javascripts/wizard/application.js.es6 @@ -0,0 +1,19 @@ +import { buildResolver } from "discourse-common/resolver"; +import Application from "@ember/application"; +import WizardInitializer from "./lib/initialize/wizard"; +import { isTesting } from "discourse-common/config/environment"; + +export default Application.extend({ + rootElement: "#custom-wizard-main", + Resolver: buildResolver("discourse/plugins/discourse-custom-wizard/wizard"), + + customEvents: { + paste: "paste", + }, + + start() { + if (!isTesting()) { + this.initializer(WizardInitializer); + } + }, +}); diff --git a/assets/javascripts/wizard/components/custom-user-selector.js.es6 b/assets/javascripts/wizard/components/custom-user-selector.js.es6 index b2f08ede..1698a008 100644 --- a/assets/javascripts/wizard/components/custom-user-selector.js.es6 +++ b/assets/javascripts/wizard/components/custom-user-selector.js.es6 @@ -1,6 +1,6 @@ import { default as computed, - observes, + observes } from "discourse-common/utils/decorators"; import { renderAvatar } from "discourse/helpers/user-avatar"; import userSearch from "../lib/user-search"; @@ -55,7 +55,6 @@ export default Ember.TextField.extend({ let self = this, selected = [], groups = [], - currentUser = this.currentUser, includeMentionableGroups = this.get("includeMentionableGroups") === "true", includeMessageableGroups = @@ -66,13 +65,8 @@ export default Ember.TextField.extend({ function excludedUsernames() { // hack works around some issues with allowAny eventing const usernames = self.get("single") ? [] : selected; - - if (currentUser && self.get("excludeCurrentUser")) { - return usernames.concat([currentUser.get("username")]); - } return usernames; } - $(this.element) .val(this.get("usernames")) .autocomplete({ @@ -84,7 +78,6 @@ export default Ember.TextField.extend({ dataSource(term) { const termRegex = /[^a-zA-Z0-9_\-\.@\+]/; - let results = userSearch({ term: term.replace(termRegex, ""), topicId: self.get("topicId"), diff --git a/assets/javascripts/wizard/components/field-validators.js.es6 b/assets/javascripts/wizard/components/field-validators.js.es6 index a315020d..15cfc181 100644 --- a/assets/javascripts/wizard/components/field-validators.js.es6 +++ b/assets/javascripts/wizard/components/field-validators.js.es6 @@ -1,5 +1,8 @@ import Component from "@ember/component"; + export default Component.extend({ + layoutName: 'wizard/templates/components/field-validators', + actions: { perform() { this.appEvents.trigger("custom-wizard:validate"); diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 index 98ea9270..e5133d4f 100644 --- a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 +++ b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 @@ -10,6 +10,7 @@ import { dasherize } from "@ember/string"; export default WizardFieldValidator.extend({ classNames: ["similar-topics-validator"], + layoutName: 'wizard/templates/components/similar-topics-validator', similarTopics: null, hasInput: notEmpty("field.value"), hasSimilarTopics: notEmpty("similarTopics"), diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index 6227cc64..ab442d97 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -6,11 +6,12 @@ import { getToken } from "wizard/lib/ajax"; export default Component.extend({ classNames: ["validator"], classNameBindings: ["isValid", "isInvalid"], + layoutName: 'wizard/templates/components/validator', validMessageKey: null, invalidMessageKey: null, isValid: null, isInvalid: equal("isValid", false), - layoutName: "components/validator", // useful for sharing the template with extending components + layoutName: "wizard/templates/components/validator", init() { this._super(...arguments); diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 index faada1f4..e4ce3ec0 100644 --- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 @@ -13,6 +13,7 @@ import { uploadIcon } from "discourse/lib/uploads"; import { dasherize } from "@ember/string"; export default ComposerEditor.extend({ + layoutName: 'wizard/templates/components/wizard-composer-editor', classNameBindings: ["fieldClass"], allowUpload: true, showLink: false, diff --git a/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 b/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 index a56b7aff..c700d3ce 100644 --- a/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 @@ -2,6 +2,7 @@ import Component from "@ember/component"; export default Component.extend({ classNames: ["wizard-composer-hyperlink"], + layoutName: 'wizard/templates/components/wizard-composer-hyperlink', actions: { addLink() { diff --git a/assets/javascripts/wizard/components/wizard-date-input.js.es6 b/assets/javascripts/wizard/components/wizard-date-input.js.es6 index 9c8e4bff..375b7195 100644 --- a/assets/javascripts/wizard/components/wizard-date-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-date-input.js.es6 @@ -3,6 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators"; export default DateInput.extend({ useNativePicker: false, + layoutName: 'wizard/templates/components/wizard-date-input', @discourseComputed() placeholder() { diff --git a/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 b/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 index 44b675b0..0fe1e68a 100644 --- a/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 @@ -2,6 +2,8 @@ import DateTimeInput from "discourse/components/date-time-input"; import discourseComputed from "discourse-common/utils/decorators"; export default DateTimeInput.extend({ + layoutName: 'wizard/templates/components/wizard-date-time-input', + @discourseComputed("timeFirst", "tabindex") timeTabindex(timeFirst, tabindex) { return timeFirst ? tabindex : tabindex + 1; diff --git a/assets/javascripts/wizard/components/wizard-field-category.js.es6 b/assets/javascripts/wizard/components/wizard-field-category.js.es6 index a7452214..dc20538e 100644 --- a/assets/javascripts/wizard/components/wizard-field-category.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-category.js.es6 @@ -2,6 +2,8 @@ import { observes } from "discourse-common/utils/decorators"; import Category from "discourse/models/category"; export default Ember.Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-category', + didInsertElement() { const property = this.field.property || "id"; const value = this.field.value; diff --git a/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 b/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 new file mode 100644 index 00000000..f9653cd2 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-checkbox' +}); diff --git a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 index b49233f2..0aee0d13 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 @@ -7,6 +7,8 @@ import { ajax } from "discourse/lib/ajax"; import { on } from "discourse-common/utils/decorators"; export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-composer-preview', + @on("init") updatePreview() { if (this.isDestroyed) { diff --git a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer.js.es6 index 8b9ecb82..0ef5fe20 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-composer.js.es6 @@ -5,6 +5,8 @@ import { import EmberObject from "@ember/object"; export default Ember.Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-composer', + showPreview: false, classNameBindings: [ ":wizard-field-composer", diff --git a/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 b/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 index 2d918636..a916f18e 100644 --- a/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 @@ -2,6 +2,8 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-date-time', + @observes("dateTime") setValue() { this.set("field.value", this.dateTime.format(this.field.format)); diff --git a/assets/javascripts/wizard/components/wizard-field-date.js.es6 b/assets/javascripts/wizard/components/wizard-field-date.js.es6 index d5d0a830..a06d582a 100644 --- a/assets/javascripts/wizard/components/wizard-field-date.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-date.js.es6 @@ -2,6 +2,8 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-date', + @observes("date") setValue() { this.set("field.value", this.date.format(this.field.format)); diff --git a/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 b/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 new file mode 100644 index 00000000..4b8b7e63 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 @@ -0,0 +1,15 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-dropdown', + + keyPress(e) { + e.stopPropagation(); + }, + + actions: { + onChangeValue(value) { + this.set("field.value", value); + }, + }, +}); diff --git a/assets/javascripts/wizard/components/wizard-field-group.js.es6 b/assets/javascripts/wizard/components/wizard-field-group.js.es6 new file mode 100644 index 00000000..93538071 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-group.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-group' +}); diff --git a/assets/javascripts/wizard/components/wizard-field-number.js.es6 b/assets/javascripts/wizard/components/wizard-field-number.js.es6 new file mode 100644 index 00000000..e7c4d77f --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-number.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-number' +}); diff --git a/assets/javascripts/wizard/components/wizard-field-tag.js.es6 b/assets/javascripts/wizard/components/wizard-field-tag.js.es6 new file mode 100644 index 00000000..45343522 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-tag.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-tag' +}); diff --git a/assets/javascripts/wizard/components/wizard-field-text.js.es6 b/assets/javascripts/wizard/components/wizard-field-text.js.es6 new file mode 100644 index 00000000..f9d5b056 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-text.js.es6 @@ -0,0 +1,9 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-text', + + keyPress(e) { + e.stopPropagation(); + }, +}); diff --git a/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 b/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 new file mode 100644 index 00000000..54865d3c --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 @@ -0,0 +1,9 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-textarea', + + keyPress(e) { + e.stopPropagation(); + }, +}); diff --git a/assets/javascripts/wizard/components/wizard-field-time.js.es6 b/assets/javascripts/wizard/components/wizard-field-time.js.es6 index 82f9c68b..bf954ec4 100644 --- a/assets/javascripts/wizard/components/wizard-field-time.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-time.js.es6 @@ -2,6 +2,8 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-time', + @observes("time") setValue() { this.set("field.value", this.time.format(this.field.format)); diff --git a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 b/assets/javascripts/wizard/components/wizard-field-upload.js.es6 index 876cdf0e..edadb4f0 100644 --- a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-upload.js.es6 @@ -3,12 +3,16 @@ import Component from "@ember/component"; import { computed } from "@ember/object"; export default Component.extend(UppyUploadMixin, { + layoutName: 'wizard/templates/components/wizard-field-upload', classNames: ["wizard-field-upload"], classNameBindings: ["isImage"], uploading: false, type: computed(function () { return `wizard_${this.field.id}`; }), + id: computed(function () { + return `wizard_field_upload_${this.field.id}`; + }), isImage: computed("field.value.extension", function () { return ( this.field.value && diff --git a/assets/javascripts/wizard/components/wizard-field-url.js.es6 b/assets/javascripts/wizard/components/wizard-field-url.js.es6 new file mode 100644 index 00000000..411b5fe9 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-url.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-url' +}); diff --git a/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 b/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 new file mode 100644 index 00000000..c2a32f44 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-user-selector' +}); diff --git a/assets/javascripts/wizard/components/wizard-field.js.es6 b/assets/javascripts/wizard/components/wizard-field.js.es6 new file mode 100644 index 00000000..372ee182 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field.js.es6 @@ -0,0 +1,34 @@ +import Component from "@ember/component"; +import { dasherize } from "@ember/string"; +import discourseComputed from "discourse-common/utils/decorators"; +import { cook } from "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field', + classNameBindings: [":wizard-field", "typeClasses", "field.invalid", "field.id"], + + @discourseComputed("field.type", "field.id") + typeClasses: (type, id) => + `${dasherize(type)}-field ${dasherize(type)}-${dasherize(id)}`, + + @discourseComputed("field.id") + fieldClass: (id) => `field-${dasherize(id)} wizard-focusable`, + + @discourseComputed("field.type", "field.id") + inputComponentName(type, id) { + if (["text_only"].includes(type)) { + return false; + } + return dasherize(type === "component" ? id : `wizard-field-${type}`); + }, + + @discourseComputed("field.translatedDescription") + cookedDescription(description) { + return cook(description); + }, + + @discourseComputed("field.type") + textType(fieldType) { + return ["text", "textarea"].includes(fieldType); + }, +}); diff --git a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 b/assets/javascripts/wizard/components/wizard-group-selector.js.es6 index cb613107..b7523f9a 100644 --- a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 +++ b/assets/javascripts/wizard/components/wizard-group-selector.js.es6 @@ -3,6 +3,7 @@ import { computed } from "@ember/object"; import { makeArray } from "discourse-common/lib/helpers"; export default ComboBox.extend({ + layoutName: 'wizard/templates/components/wizard-group-selector', content: computed("groups.[]", "field.content.[]", function () { const whitelist = makeArray(this.field.content); return this.groups diff --git a/assets/javascripts/wizard/components/wizard-no-access.js.es6 b/assets/javascripts/wizard/components/wizard-no-access.js.es6 index 57fe9111..78a23aa8 100644 --- a/assets/javascripts/wizard/components/wizard-no-access.js.es6 +++ b/assets/javascripts/wizard/components/wizard-no-access.js.es6 @@ -1,10 +1,21 @@ -import CustomWizard from "../models/custom"; +import CustomWizard from "../models/wizard"; +import discourseComputed from "discourse-common/utils/decorators"; +import Component from "@ember/component"; +import { dasherize } from "@ember/string"; -export default Ember.Component.extend({ - siteName: function () { - /*eslint no-undef:0*/ - return Wizard.SiteSettings.title; - }.property(), +export default Component.extend({ + classNameBindings: [':wizard-no-access', 'reasonClass'], + layoutName: 'wizard/templates/components/wizard-no-access', + + @discourseComputed('reason') + reasonClass(reason) { + return dasherize(reason); + }, + + @discourseComputed + siteName() { + return (this.siteSettings.title || ''); + }, actions: { skip() { diff --git a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 b/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 index 687cfa86..c52bfeae 100644 --- a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 +++ b/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 @@ -4,6 +4,7 @@ import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ classNames: ["wizard-similar-topics"], + layoutName: 'wizard/templates/components/wizard-similar-topics', showTopics: true, didInsertElement() { diff --git a/assets/javascripts/wizard/components/wizard-step-form.js.es6 b/assets/javascripts/wizard/components/wizard-step-form.js.es6 new file mode 100644 index 00000000..73406b4f --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-step-form.js.es6 @@ -0,0 +1,9 @@ +import Component from "@ember/component"; +import discourseComputed from "discourse-common/utils/decorators"; + +export default Component.extend({ + classNameBindings: [":wizard-step-form", "customStepClass"], + + @discourseComputed("step.id") + customStepClass: (stepId) => `wizard-step-${stepId}`, +}); diff --git a/assets/javascripts/wizard/components/wizard-step.js.es6 b/assets/javascripts/wizard/components/wizard-step.js.es6 new file mode 100644 index 00000000..4518afee --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-step.js.es6 @@ -0,0 +1,247 @@ +import discourseComputed, { observes } from "discourse-common/utils/decorators"; +import Component from "@ember/component"; +import I18n from "I18n"; +import getUrl from "discourse-common/lib/get-url"; +import { htmlSafe } from "@ember/template"; +import { schedule } from "@ember/runloop"; +import { cook } from "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"; +import { updateCachedWizard } from "discourse/plugins/discourse-custom-wizard/wizard/models/wizard"; +import { alias, not } from "@ember/object/computed"; +import CustomWizard from "../models/wizard"; + +const alreadyWarned = {}; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-step', + classNameBindings: [":wizard-step", "step.id"], + saving: null, + + init() { + this._super(...arguments); + this.set("stylingDropdown", {}); + }, + + didInsertElement() { + this._super(...arguments); + this.autoFocus(); + }, + + @discourseComputed("step.index", "wizard.required") + showQuitButton: (index, required) => (index === 0 && !required), + + showNextButton: not("step.final"), + showDoneButton: alias("step.final"), + + @discourseComputed("step.translatedTitle") + cookedTitle(title) { + return cook(title); + }, + + @discourseComputed("step.translatedDescription") + cookedDescription(description) { + return cook(description); + }, + + @discourseComputed( + "step.index", + "step.displayIndex", + "wizard.totalSteps", + "wizard.completed" + ) + showFinishButton: (index, displayIndex, total, completed) => { + return index !== 0 && displayIndex !== total && completed; + }, + + @discourseComputed("step.index") + showBackButton: (index) => index > 0, + + @discourseComputed("step.banner") + bannerImage(src) { + if (!src) { + return; + } + return getUrl(src); + }, + + @discourseComputed("step.id") + bannerAndDescriptionClass(id) { + return `wizard-banner-and-description wizard-banner-and-description-${id}`; + }, + + @discourseComputed("step.fields.[]") + primaryButtonIndex(fields) { + return fields.length + 1; + }, + + @discourseComputed("step.fields.[]") + secondaryButtonIndex(fields) { + return fields.length + 2; + }, + + @observes("step.id") + _stepChanged() { + this.set("saving", false); + this.autoFocus(); + }, + + @observes("step.message") + _handleMessage: function () { + const message = this.get("step.message"); + this.sendAction("showMessage", message); + }, + + keyPress(event) { + if (event.key === "Enter") { + if (this.showDoneButton) { + this.send("quit"); + } else { + this.send("nextStep"); + } + } + }, + + @discourseComputed("step.index", "wizard.totalSteps") + barStyle(displayIndex, totalSteps) { + let ratio = parseFloat(displayIndex) / parseFloat(totalSteps - 1); + if (ratio < 0) { + ratio = 0; + } + if (ratio > 1) { + ratio = 1; + } + + return htmlSafe(`width: ${ratio * 200}px`); + }, + + @discourseComputed("step.fields") + includeSidebar(fields) { + return !!fields.findBy("show_in_sidebar"); + }, + + autoFocus() { + schedule("afterRender", () => { + const $invalid = $( + ".wizard-field.invalid:nth-of-type(1) .wizard-focusable" + ); + + if ($invalid.length) { + return $invalid.focus(); + } + + $(".wizard-focusable:first").focus(); + }); + }, + + animateInvalidFields() { + schedule("afterRender", () => { + let $element = $( + ".invalid input[type=text],.invalid textarea,.invalid input[type=checkbox],.invalid .select-kit" + ); + + if ($element.length) { + $([document.documentElement, document.body]).animate( + { + scrollTop: $element.offset().top - 200, + }, + 400, + function () { + $element.wiggle(2, 100); + } + ); + } + }); + }, + + advance() { + this.set("saving", true); + this.get("step") + .save() + .then((response) => { + updateCachedWizard(CustomWizard.build(response["wizard"])); + + if (response["final"]) { + CustomWizard.finished(response); + } else { + this.sendAction("goNext", response); + } + }) + .catch(() => this.animateInvalidFields()) + .finally(() => this.set("saving", false)); + }, + + keyPress() {}, + + actions: { + quit() { + this.get("wizard").skip(); + }, + + done() { + this.send("nextStep"); + }, + + showMessage(message) { + this.sendAction("showMessage", message); + }, + + stylingDropdownChanged(id, value) { + this.set("stylingDropdown", { id, value }); + }, + + exitEarly() { + const step = this.step; + step.validate(); + + if (step.get("valid")) { + this.set("saving", true); + + step + .save() + .then(() => this.send("quit")) + .finally(() => this.set("saving", false)); + } else { + this.autoFocus(); + } + }, + + backStep() { + if (this.saving) { + return; + } + + this.goBack(); + }, + + nextStep() { + if (this.saving) { + return; + } + + const step = this.step; + const result = step.validate(); + + if (result.warnings.length) { + const unwarned = result.warnings.filter((w) => !alreadyWarned[w]); + if (unwarned.length) { + unwarned.forEach((w) => (alreadyWarned[w] = true)); + return window.bootbox.confirm( + unwarned.map((w) => I18n.t(`wizard.${w}`)).join("\n"), + I18n.t("no_value"), + I18n.t("yes_value"), + (confirmed) => { + if (confirmed) { + this.advance(); + } + } + ); + } + } + + if (step.get("valid")) { + this.advance(); + } else { + this.autoFocus(); + } + }, + }, +}); diff --git a/assets/javascripts/wizard/components/wizard-text-field.js.es6 b/assets/javascripts/wizard/components/wizard-text-field.js.es6 index c522eb2c..3d87be09 100644 --- a/assets/javascripts/wizard/components/wizard-text-field.js.es6 +++ b/assets/javascripts/wizard/components/wizard-text-field.js.es6 @@ -1,5 +1,3 @@ -/* eslint no-undef: 0*/ - import computed from "discourse-common/utils/decorators"; import { isLTR, isRTL, siteDir } from "discourse/lib/text-direction"; import WizardI18n from "../lib/wizard-i18n"; @@ -15,7 +13,7 @@ export default Ember.TextField.extend({ @computed dir() { - if (Wizard.SiteSettings.support_mixed_text_direction) { + if (this.siteSettings.support_mixed_text_direction) { let val = this.value; if (val) { return isRTL(val) ? "rtl" : "ltr"; @@ -26,7 +24,7 @@ export default Ember.TextField.extend({ }, keyUp() { - if (Wizard.SiteSettings.support_mixed_text_direction) { + if (this.siteSettings.support_mixed_text_direction) { let val = this.value; if (isRTL(val)) { this.set("dir", "rtl"); diff --git a/assets/javascripts/wizard/components/wizard-time-input.js.es6 b/assets/javascripts/wizard/components/wizard-time-input.js.es6 index 0bca244d..ec121002 100644 --- a/assets/javascripts/wizard/components/wizard-time-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-time-input.js.es6 @@ -1,3 +1,5 @@ import TimeInput from "discourse/components/time-input"; -export default TimeInput.extend(); +export default TimeInput.extend({ + layoutName: 'wizard/templates/components/wizard-time-input' +}); diff --git a/assets/javascripts/wizard/controllers/custom.js.es6 b/assets/javascripts/wizard/controllers/custom.js.es6 deleted file mode 100644 index 5f168f3a..00000000 --- a/assets/javascripts/wizard/controllers/custom.js.es6 +++ /dev/null @@ -1,3 +0,0 @@ -export default Ember.Controller.extend({ - queryParams: ["reset"], -}); diff --git a/assets/javascripts/wizard/controllers/custom-step.js.es6 b/assets/javascripts/wizard/controllers/step.js.es6 similarity index 76% rename from assets/javascripts/wizard/controllers/custom-step.js.es6 rename to assets/javascripts/wizard/controllers/step.js.es6 index b44c0fca..4b321173 100644 --- a/assets/javascripts/wizard/controllers/custom-step.js.es6 +++ b/assets/javascripts/wizard/controllers/step.js.es6 @@ -1,7 +1,10 @@ -import StepController from "wizard/controllers/step"; +import Controller from "@ember/controller"; import getUrl from "discourse-common/lib/get-url"; -export default StepController.extend({ +export default Controller.extend({ + wizard: null, + step: null, + actions: { goNext(response) { let nextStepId = response["next_step_id"]; @@ -12,12 +15,12 @@ export default StepController.extend({ const wizardId = this.get("wizard.id"); window.location.href = getUrl(`/w/${wizardId}/steps/${nextStepId}`); } else { - this.transitionToRoute("custom.step", nextStepId); + this.transitionToRoute("step", nextStepId); } }, goBack() { - this.transitionToRoute("custom.step", this.get("step.previous")); + this.transitionToRoute("step", this.get("step.previous")); }, showMessage(message) { diff --git a/assets/javascripts/wizard/controllers/wizard-index.js.es6 b/assets/javascripts/wizard/controllers/wizard-index.js.es6 new file mode 100644 index 00000000..2dfdee40 --- /dev/null +++ b/assets/javascripts/wizard/controllers/wizard-index.js.es6 @@ -0,0 +1,24 @@ +import Controller from "@ember/controller"; +import { or } from "@ember/object/computed"; +import discourseComputed from "discourse-common/utils/decorators"; + +const reasons = { + noWizard: "none", + requiresLogin: "requires_login", + notPermitted: "not_permitted", + completed: "completed" +} + +export default Controller.extend({ + noAccess: or('noWizard', 'requiresLogin', 'notPermitted', 'completed'), + + @discourseComputed('noAccessReason') + noAccessI18nKey(reason) { + return reason ? `wizard.${reasons[reason]}` : 'wizard.none'; + }, + + @discourseComputed + noAccessReason() { + return Object.keys(reasons).find(reason => this.get(reason)); + } +}); diff --git a/assets/javascripts/wizard/controllers/wizard.js.es6 b/assets/javascripts/wizard/controllers/wizard.js.es6 new file mode 100644 index 00000000..35f964e2 --- /dev/null +++ b/assets/javascripts/wizard/controllers/wizard.js.es6 @@ -0,0 +1,5 @@ +import Controller from "@ember/controller"; + +export default Controller.extend({ + queryParams: ["reset"], +}); diff --git a/assets/javascripts/wizard/custom-wizard.js.es6 b/assets/javascripts/wizard/custom-wizard.js.es6 deleted file mode 100644 index 8c0a473c..00000000 --- a/assets/javascripts/wizard/custom-wizard.js.es6 +++ /dev/null @@ -1,39 +0,0 @@ -import { buildResolver } from "discourse-common/resolver"; - -export default Ember.Application.extend({ - rootElement: "#custom-wizard-main", - Resolver: buildResolver("wizard"), - - customEvents: { - paste: "paste", - }, - - start() { - Object.keys(requirejs._eak_seen).forEach((key) => { - if (/\/pre\-initializers\//.test(key)) { - const module = requirejs(key, null, null, true); - if (!module) { - throw new Error(key + " must export an initializer."); - } - - const init = module.default; - const oldInitialize = init.initialize; - init.initialize = () => { - oldInitialize.call(this, this.__container__, this); - }; - - this.initializer(init); - } - }); - - Object.keys(requirejs._eak_seen).forEach((key) => { - if (/\/initializers\//.test(key)) { - const module = requirejs(key, null, null, true); - if (!module) { - throw new Error(key + " must export an initializer."); - } - this.initializer(module.default); - } - }); - }, -}); diff --git a/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 b/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 deleted file mode 100644 index fbbe7d8b..00000000 --- a/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 +++ /dev/null @@ -1,199 +0,0 @@ -export default { - name: "custom-wizard-step", - initialize() { - if (window.location.pathname.indexOf("/w/") < 0) { - return; - } - - const CustomWizard = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/models/custom" - ).default; - const updateCachedWizard = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/models/custom" - ).updateCachedWizard; - const StepModel = requirejs("wizard/models/step").default; - const StepComponent = requirejs("wizard/components/wizard-step").default; - const ajax = requirejs("wizard/lib/ajax").ajax; - const getUrl = requirejs("discourse-common/lib/get-url").default; - const discourseComputed = requirejs("discourse-common/utils/decorators") - .default; - const cook = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite" - ).cook; - const { schedule } = requirejs("@ember/runloop"); - const { alias, not } = requirejs("@ember/object/computed"); - - StepModel.reopen({ - save() { - const wizardId = this.get("wizardId"); - const fields = {}; - - this.get("fields").forEach((f) => { - if (f.type !== "text_only") { - fields[f.id] = f.value; - } - }); - - return ajax({ - url: `/w/${wizardId}/steps/${this.get("id")}`, - type: "PUT", - data: { fields }, - }).catch((response) => { - if ( - response && - response.responseJSON && - response.responseJSON.errors - ) { - let wizardErrors = []; - response.responseJSON.errors.forEach((err) => { - if (err.field === wizardId) { - wizardErrors.push(err.description); - } else if (err.field) { - this.fieldError(err.field, err.description); - } else if (err) { - wizardErrors.push(err); - } - }); - if (wizardErrors.length) { - this.handleWizardError(wizardErrors.join("\n")); - } - this.animateInvalidFields(); - throw response; - } - - if (response && response.responseText) { - const responseText = response.responseText; - const start = responseText.indexOf(">") + 1; - const end = responseText.indexOf("plugins"); - const message = responseText.substring(start, end); - this.handleWizardError(message); - throw message; - } - }); - }, - - handleWizardError(message) { - this.set("message", { - state: "error", - text: message, - }); - Ember.run.later(() => this.set("message", null), 6000); - }, - }); - - StepComponent.reopen({ - classNameBindings: ["step.id"], - - autoFocus() { - schedule("afterRender", () => { - const $invalid = $( - ".wizard-field.invalid:nth-of-type(1) .wizard-focusable" - ); - - if ($invalid.length) { - return $invalid.focus(); - } - - $(".wizard-focusable:first").focus(); - }); - }, - - animateInvalidFields() { - schedule("afterRender", () => { - let $element = $( - ".invalid input[type=text],.invalid textarea,.invalid input[type=checkbox],.invalid .select-kit" - ); - - if ($element.length) { - $([document.documentElement, document.body]).animate( - { - scrollTop: $element.offset().top - 200, - }, - 400, - function () { - $element.wiggle(2, 100); - } - ); - } - }); - }, - - ensureStartsAtTop: function () { - window.scrollTo(0, 0); - }.observes("step.id"), - - showQuitButton: function () { - const index = this.get("step.index"); - const required = this.get("wizard.required"); - return index === 0 && !required; - }.property("step.index", "wizard.required"), - - cookedTitle: function () { - return cook(this.get("step.title")); - }.property("step.title"), - - cookedDescription: function () { - return cook(this.get("step.description")); - }.property("step.description"), - - bannerImage: function () { - const src = this.get("step.banner"); - if (!src) { - return; - } - return getUrl(src); - }.property("step.banner"), - - @discourseComputed("step.fields.[]") - primaryButtonIndex(fields) { - return fields.length + 1; - }, - - @discourseComputed("step.fields.[]") - secondaryButtonIndex(fields) { - return fields.length + 2; - }, - - handleMessage: function () { - const message = this.get("step.message"); - this.sendAction("showMessage", message); - }.observes("step.message"), - - showNextButton: not("step.final"), - showDoneButton: alias("step.final"), - - advance() { - this.set("saving", true); - this.get("step") - .save() - .then((response) => { - updateCachedWizard(CustomWizard.build(response["wizard"])); - - if (response["final"]) { - CustomWizard.finished(response); - } else { - this.sendAction("goNext", response); - } - }) - .catch(() => this.animateInvalidFields()) - .finally(() => this.set("saving", false)); - }, - - keyPress() {}, - - actions: { - quit() { - this.get("wizard").skip(); - }, - - done() { - this.send("nextStep"); - }, - - showMessage(message) { - this.sendAction("showMessage", message); - }, - }, - }); - }, -}; diff --git a/assets/javascripts/wizard/initializers/custom-wizard.js.es6 b/assets/javascripts/wizard/initializers/custom-wizard.js.es6 deleted file mode 100644 index f2095827..00000000 --- a/assets/javascripts/wizard/initializers/custom-wizard.js.es6 +++ /dev/null @@ -1,126 +0,0 @@ -export default { - name: "custom-routes", - initialize(app) { - if (window.location.pathname.indexOf("/w/") < 0) { - return; - } - - const EmberObject = requirejs("@ember/object").default; - const Router = requirejs("wizard/router").default; - const ApplicationRoute = requirejs("wizard/routes/application").default; - const getUrl = requirejs("discourse-common/lib/get-url").default; - const Store = requirejs("discourse/services/store").default; - const registerRawHelpers = requirejs( - "discourse-common/lib/raw-handlebars-helpers" - ).registerRawHelpers; - const createHelperContext = requirejs("discourse-common/lib/helpers") - .createHelperContext; - const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars") - .default; - const Handlebars = requirejs("handlebars").default; - const Site = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/models/site" - ).default; - const RestAdapter = requirejs("discourse/adapters/rest").default; - const Session = requirejs("discourse/models/session").default; - const setDefaultOwner = requirejs("discourse-common/lib/get-owner") - .setDefaultOwner; - const messageBus = requirejs("message-bus-client").default; - const getToken = requirejs("wizard/lib/ajax").getToken; - const setEnvironment = requirejs("discourse-common/config/environment") - .setEnvironment; - const container = app.__container__; - Discourse.Model = EmberObject.extend(); - Discourse.__container__ = container; - setDefaultOwner(container); - registerRawHelpers(RawHandlebars, Handlebars); - - // IE11 Polyfill - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill - if (!Object.entries) { - Object.entries = function (obj) { - let ownProps = Object.keys(obj), - i = ownProps.length, - resArray = new Array(i); // preallocate the Array - while (i--) { - resArray[i] = [ownProps[i], obj[ownProps[i]]]; - } - - return resArray; - }; - } - - Object.keys(Ember.TEMPLATES).forEach((k) => { - if (k.indexOf("select-kit") === 0) { - let template = Ember.TEMPLATES[k]; - define(k, () => template); - } - }); - - const targets = ["controller", "component", "route", "model", "adapter"]; - /*eslint no-undef: 0*/ - const siteSettings = Wizard.SiteSettings; - app.register("site-settings:main", siteSettings, { instantiate: false }); - createHelperContext({ siteSettings }); - targets.forEach((t) => app.inject(t, "siteSettings", "site-settings:main")); - - app.register("message-bus:main", messageBus, { instantiate: false }); - targets.forEach((t) => app.inject(t, "messageBus", "message-bus:main")); - - app.register("service:store", Store); - targets.forEach((t) => app.inject(t, "store", "service:store")); - targets.forEach((t) => app.inject(t, "appEvents", "service:app-events")); - - app.register("adapter:rest", RestAdapter); - - const site = Site.current(); - app.register("site:main", site, { instantiate: false }); - targets.forEach((t) => app.inject(t, "site", "site:main")); - - site.set("can_create_tag", false); - app.register("session:main", Session.current(), { instantiate: false }); - targets.forEach((t) => app.inject(t, "session", "session:main")); - - createHelperContext({ - siteSettings: container.lookup("site-settings:main"), - currentUser: container.lookup("current-user:main"), - site: container.lookup("site:main"), - session: container.lookup("session:main"), - capabilities: container.lookup("capabilities:main"), - }); - - const session = container.lookup("session:main"); - const setupData = document.getElementById("data-discourse-setup").dataset; - session.set("highlightJsPath", setupData.highlightJsPath); - setEnvironment(setupData.environment); - - Router.reopen({ - rootURL: getUrl("/w/"), - }); - - Router.map(function () { - this.route("custom", { path: "/:wizard_id" }, function () { - this.route("steps"); - this.route("step", { path: "/steps/:step_id" }); - }); - }); - - ApplicationRoute.reopen({ - redirect() { - this.transitionTo("custom"); - }, - model() {}, - }); - - // Add a CSRF token to all AJAX requests - let token = getToken(); - session.set("csrfToken", token); - let callbacks = $.Callbacks(); - $.ajaxPrefilter(callbacks.fire); - - callbacks.add(function (options, originalOptions, xhr) { - if (!options.crossDomain) { - xhr.setRequestHeader("X-CSRF-Token", session.get("csrfToken")); - } - }); - }, -}; diff --git a/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 b/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 new file mode 100644 index 00000000..022ac48e --- /dev/null +++ b/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 @@ -0,0 +1,12 @@ +export default { + run(app, container) { + const { createHelperContext } = requirejs("discourse-common/lib/helpers"); + + createHelperContext({ + siteSettings: container.lookup('site-settings:main'), + site: container.lookup("site:main"), + session: container.lookup("session:main"), + capabilities: container.lookup("capabilities:main"), + }); + } +} diff --git a/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 new file mode 100644 index 00000000..d31efb4d --- /dev/null +++ b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 @@ -0,0 +1,49 @@ +export default { + run(app, container) { + const Store = requirejs("discourse/services/store").default; + const Site = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/models/site" + ).default; + const Session = requirejs("discourse/models/session").default; + const RestAdapter = requirejs("discourse/adapters/rest").default; + const messageBus = requirejs("message-bus-client").default; + const sniffCapabilites = requirejs("discourse/pre-initializers/sniff-capabilities").default; + const site = Site.current(); + const session = Session.current(); + + const registrations = [ + ["site-settings:main", app.SiteSettings, false], + ["message-bus:main", messageBus, false], + ["site:main", site, false], + ["session:main", session, false], + ["service:store", Store, true], + ["adapter:rest", RestAdapter, true] + ]; + + registrations.forEach(registration => { + if (!app.hasRegistration(registration[0])) { + app.register(registration[0], registration[1], { instantiate: registration[2] }); + } + }); + + const targets = ["controller", "component", "route", "model", "adapter", "mixin"]; + const injections = [ + ["siteSettings", "site-settings:main"], + ["messageBus", "message-bus:main"], + ["site", "site:main"], + ["session", "session:main"], + ["store", "service:store"], + ["appEvents", "service:app-events"] + ]; + + injections.forEach(injection => { + targets.forEach((t) => app.inject(t, injection[0], injection[1])); + }); + + if (!app.hasRegistration("capabilities:main")) { + sniffCapabilites.initialize(null, app); + } + + site.set("can_create_tag", false); + } +} diff --git a/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 similarity index 60% rename from assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 rename to assets/javascripts/wizard/lib/initialize/patch-components.js.es6 index e397af5f..529a4cd9 100644 --- a/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 @@ -1,93 +1,25 @@ -import { dasherize } from "@ember/string"; -import discourseComputed from "discourse-common/utils/decorators"; - export default { - name: "custom-wizard-field", - initialize() { - if (window.location.pathname.indexOf("/w/") < 0) { - return; + run(app, container) { + const getToken = requirejs("wizard/lib/ajax").getToken; + const isTesting = requirejs("discourse-common/config/environment").isTesting; + + if (!isTesting) { + // Add a CSRF token to all AJAX requests + let token = getToken(); + session.set("csrfToken", token); + let callbacks = $.Callbacks(); + $.ajaxPrefilter(callbacks.fire); + + callbacks.add(function (options, originalOptions, xhr) { + if (!options.crossDomain) { + xhr.setRequestHeader("X-CSRF-Token", session.get("csrfToken")); + } + }); } - const FieldComponent = requirejs("wizard/components/wizard-field").default; - const FieldModel = requirejs("wizard/models/wizard-field").default; - const { cook } = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite" - ); const DEditor = requirejs("discourse/components/d-editor").default; const { clipboardHelpers } = requirejs("discourse/lib/utilities"); const toMarkdown = requirejs("discourse/lib/to-markdown").default; - - FieldComponent.reopen({ - classNameBindings: ["field.id"], - - @discourseComputed("field.type") - textType(fieldType) { - return ["text", "textarea"].includes(fieldType); - }, - - cookedDescription: function () { - return cook(this.get("field.description")); - }.property("field.description"), - - inputComponentName: function () { - const type = this.get("field.type"); - const id = this.get("field.id"); - if (["text_only"].includes(type)) { - return false; - } - return dasherize(type === "component" ? id : `wizard-field-${type}`); - }.property("field.type", "field.id"), - }); - - const StandardFieldValidation = [ - "text", - "number", - "textarea", - "dropdown", - "tag", - "image", - "user_selector", - "text_only", - "composer", - "category", - "group", - "date", - "time", - "date_time", - ]; - - FieldModel.reopen({ - check() { - if (this.customCheck) { - return this.customCheck(); - } - - let valid = this.valid; - - if (!this.required) { - this.setValid(true); - return true; - } - - const val = this.get("value"); - const type = this.get("type"); - - if (type === "checkbox") { - valid = val; - } else if (type === "upload") { - valid = val && val.id > 0; - } else if (StandardFieldValidation.indexOf(type) > -1) { - valid = val && val.toString().length > 0; - } else if (type === "url") { - valid = true; - } - - this.setValid(valid); - - return valid; - }, - }); - const isInside = (text, regex) => { const matches = text.match(regex); return matches && matches.length % 2; @@ -194,5 +126,19 @@ export default { } }, }); - }, + + // IE11 Polyfill - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill + if (!Object.entries) { + Object.entries = function (obj) { + let ownProps = Object.keys(obj), + i = ownProps.length, + resArray = new Array(i); // preallocate the Array + while (i--) { + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + } + + return resArray; + }; + } + } }; diff --git a/assets/javascripts/wizard/lib/initialize/register-files.js.es6 b/assets/javascripts/wizard/lib/initialize/register-files.js.es6 new file mode 100644 index 00000000..8d4b850e --- /dev/null +++ b/assets/javascripts/wizard/lib/initialize/register-files.js.es6 @@ -0,0 +1,26 @@ +export default { + run(app, container) { + const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars").default; + const Handlebars = requirejs("handlebars").default; + const registerRawHelpers = requirejs("discourse-common/lib/raw-handlebars-helpers").registerRawHelpers; + const { registerHelpers } = requirejs("discourse-common/lib/helpers"); + const jqueryPlugins = requirejs("discourse/initializers/jquery-plugins").default; + + Object.keys(Ember.TEMPLATES).forEach((k) => { + if (k.indexOf("select-kit") === 0) { + let template = Ember.TEMPLATES[k]; + define(k, () => template); + } + }); + + Object.keys(requirejs.entries).forEach((entry) => { + if (/\/helpers\//.test(entry)) { + requirejs(entry, null, null, true); + } + }); + + registerRawHelpers(RawHandlebars, Handlebars); + registerHelpers(app); + jqueryPlugins.initialize(container, app); + } +} diff --git a/assets/javascripts/wizard/lib/initialize/wizard.js.es6 b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 new file mode 100644 index 00000000..134257a1 --- /dev/null +++ b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 @@ -0,0 +1,49 @@ +export default { + name: "custom-wizard", + initialize(app) { + const isTesting = requirejs("discourse-common/config/environment").isTesting; + const isWizard = window.location.pathname.indexOf("/w/") > -1; + + if (!isWizard && !isTesting()) { + return; + } + + const container = app.__container__; + const setDefaultOwner = requirejs("discourse-common/lib/get-owner").setDefaultOwner; + setDefaultOwner(container); + + if (!isTesting()) { + const PreloadStore = requirejs("discourse/lib/preload-store").default; + + let preloaded; + const preloadedDataElement = document.getElementById("data-preloaded-wizard"); + if (preloadedDataElement) { + preloaded = JSON.parse(preloadedDataElement.dataset.preloadedWizard); + } + + Object.keys(preloaded).forEach(function (key) { + PreloadStore.store(key, JSON.parse(preloaded[key])); + }); + + app.SiteSettings = PreloadStore.get("siteSettings"); + } + + const setEnvironment = requirejs("discourse-common/config/environment").setEnvironment; + const setupData = document.getElementById("data-discourse-setup").dataset; + setEnvironment(setupData.environment); + + const Session = requirejs("discourse/models/session").default; + const session = Session.current(); + session.set("highlightJsPath", setupData.highlightJsPath); + + [ + 'register-files', + 'inject-objects', + 'create-contexts', + 'patch-components' + ].forEach(fileName => { + const initializer = requirejs(`discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/${fileName}`).default; + initializer.run(app, container); + }); + } +}; diff --git a/assets/javascripts/wizard/lib/utilities-lite.js.es6 b/assets/javascripts/wizard/lib/utilities-lite.js.es6 deleted file mode 100644 index 19517b2b..00000000 --- a/assets/javascripts/wizard/lib/utilities-lite.js.es6 +++ /dev/null @@ -1,71 +0,0 @@ -// lite version of discourse/lib/utilities - -export function determinePostReplaceSelection({ - selection, - needle, - replacement, -}) { - const diff = - replacement.end - replacement.start - (needle.end - needle.start); - - if (selection.end <= needle.start) { - // Selection ends (and starts) before needle. - return { start: selection.start, end: selection.end }; - } else if (selection.start <= needle.start) { - // Selection starts before needle... - if (selection.end < needle.end) { - // ... and ends inside needle. - return { start: selection.start, end: needle.start }; - } else { - // ... and spans needle completely. - return { start: selection.start, end: selection.end + diff }; - } - } else if (selection.start < needle.end) { - // Selection starts inside needle... - if (selection.end <= needle.end) { - // ... and ends inside needle. - return { start: replacement.end, end: replacement.end }; - } else { - // ... and spans end of needle. - return { start: replacement.end, end: selection.end + diff }; - } - } else { - // Selection starts (and ends) behind needle. - return { start: selection.start + diff, end: selection.end + diff }; - } -} - -const toArray = (items) => { - items = items || []; - - if (!Array.isArray(items)) { - return Array.from(items); - } - - return items; -}; - -export function clipboardData(e, canUpload) { - const clipboard = - e.clipboardData || - e.originalEvent.clipboardData || - e.delegatedEvent.originalEvent.clipboardData; - - const types = toArray(clipboard.types); - let files = toArray(clipboard.files); - - if (types.includes("Files") && files.length === 0) { - // for IE - files = toArray(clipboard.items).filter((i) => i.kind === "file"); - } - - canUpload = files && canUpload && !types.includes("text/plain"); - const canUploadImage = - canUpload && files.filter((f) => f.type.match("^image/"))[0]; - const canPasteHtml = - Discourse.SiteSettings.enable_rich_text_paste && - types.includes("text/html") && - !canUploadImage; - - return { clipboard, types, canUpload, canPasteHtml }; -} diff --git a/assets/javascripts/wizard/models/field.js.es6 b/assets/javascripts/wizard/models/field.js.es6 new file mode 100644 index 00000000..5f76074e --- /dev/null +++ b/assets/javascripts/wizard/models/field.js.es6 @@ -0,0 +1,79 @@ +import EmberObject from "@ember/object"; +import ValidState from "wizard/mixins/valid-state"; +import discourseComputed from "discourse-common/utils/decorators"; +import { translatedText } from "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n"; + +const StandardFieldValidation = [ + "text", + "number", + "textarea", + "dropdown", + "tag", + "image", + "user_selector", + "text_only", + "composer", + "category", + "group", + "date", + "time", + "date_time", +]; + +export default EmberObject.extend(ValidState, { + id: null, + type: null, + value: null, + required: null, + warning: null, + + @discourseComputed("wizardId", "stepId", "id") + i18nKey(wizardId, stepId, id) { + return `${wizardId}.${stepId}.${id}`; + }, + + @discourseComputed("i18nKey", "label") + translatedLabel(i18nKey, label) { + return translatedText(`${i18nKey}.label`, label); + }, + + @discourseComputed("i18nKey", "placeholder") + translatedPlaceholder(i18nKey, placeholder) { + return translatedText(`${i18nKey}.placeholder`, placeholder); + }, + + @discourseComputed("i18nKey", "description") + translatedDescription(i18nKey, description) { + return translatedText(`${i18nKey}.description`, description); + }, + + check() { + if (this.customCheck) { + return this.customCheck(); + } + + let valid = this.valid; + + if (!this.required) { + this.setValid(true); + return true; + } + + const val = this.get("value"); + const type = this.get("type"); + + if (type === "checkbox") { + valid = val; + } else if (type === "upload") { + valid = val && val.id > 0; + } else if (StandardFieldValidation.indexOf(type) > -1) { + valid = val && val.toString().length > 0; + } else if (type === "url") { + valid = true; + } + + this.setValid(valid); + + return valid; + } +}); diff --git a/assets/javascripts/wizard/models/site.js.es6 b/assets/javascripts/wizard/models/site.js.es6 index c15ce98f..96837ff2 100644 --- a/assets/javascripts/wizard/models/site.js.es6 +++ b/assets/javascripts/wizard/models/site.js.es6 @@ -1,10 +1,11 @@ import Site from "discourse/models/site"; +import { getOwner } from "discourse-common/lib/get-owner"; export default Site.reopenClass({ // There is no site data actually loaded by the CW yet. This placeholder is // needed by imported classes createCurrent() { - const store = Discourse.__container__.lookup("service:store"); + const store = getOwner(this).lookup("service:store"); return store.createRecord("site", {}); }, }); diff --git a/assets/javascripts/wizard/models/step.js.es6 b/assets/javascripts/wizard/models/step.js.es6 new file mode 100644 index 00000000..e18657b5 --- /dev/null +++ b/assets/javascripts/wizard/models/step.js.es6 @@ -0,0 +1,114 @@ +import EmberObject from "@ember/object"; +import ValidState from "wizard/mixins/valid-state"; +import { ajax } from "wizard/lib/ajax"; +import discourseComputed from "discourse-common/utils/decorators"; +import { translatedText } from "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n"; + +export default EmberObject.extend(ValidState, { + id: null, + + @discourseComputed("wizardId", "id") + i18nKey(wizardId, stepId) { + return `${wizardId}.${stepId}`; + }, + + @discourseComputed("i18nKey", "title") + translatedTitle(i18nKey, title) { + return translatedText(`${i18nKey}.title`, title); + }, + + @discourseComputed("i18nKey", "description") + translatedDescription(i18nKey, description) { + return translatedText(`${i18nKey}.description`, description); + }, + + @discourseComputed("index") + displayIndex: (index) => index + 1, + + @discourseComputed("fields.[]") + fieldsById(fields) { + const lookup = {}; + fields.forEach((field) => (lookup[field.get("id")] = field)); + return lookup; + }, + + validate() { + let allValid = true; + const result = { warnings: [] }; + + this.fields.forEach((field) => { + allValid = allValid && field.check(); + const warning = field.get("warning"); + if (warning) { + result.warnings.push(warning); + } + }); + + this.setValid(allValid); + + return result; + }, + + fieldError(id, description) { + const field = this.fields.findBy("id", id); + if (field) { + field.setValid(false, description); + } + }, + + save() { + const wizardId = this.get("wizardId"); + const fields = {}; + + this.get("fields").forEach((f) => { + if (f.type !== "text_only") { + fields[f.id] = f.value; + } + }); + + return ajax({ + url: `/w/${wizardId}/steps/${this.get("id")}`, + type: "PUT", + data: { fields }, + }).catch((response) => { + if ( + response && + response.responseJSON && + response.responseJSON.errors + ) { + let wizardErrors = []; + response.responseJSON.errors.forEach((err) => { + if (err.field === wizardId) { + wizardErrors.push(err.description); + } else if (err.field) { + this.fieldError(err.field, err.description); + } else if (err) { + wizardErrors.push(err); + } + }); + if (wizardErrors.length) { + this.handleWizardError(wizardErrors.join("\n")); + } + this.animateInvalidFields(); + throw response; + } + + if (response && response.responseText) { + const responseText = response.responseText; + const start = responseText.indexOf(">") + 1; + const end = responseText.indexOf("plugins"); + const message = responseText.substring(start, end); + this.handleWizardError(message); + throw message; + } + }); + }, + + handleWizardError(message) { + this.set("message", { + state: "error", + text: message, + }); + Ember.run.later(() => this.set("message", null), 6000); + }, +}); diff --git a/assets/javascripts/wizard/models/custom.js.es6 b/assets/javascripts/wizard/models/wizard.js.es6 similarity index 94% rename from assets/javascripts/wizard/models/custom.js.es6 rename to assets/javascripts/wizard/models/wizard.js.es6 index 0e7c557f..7fbe2c10 100644 --- a/assets/javascripts/wizard/models/custom.js.es6 +++ b/assets/javascripts/wizard/models/wizard.js.es6 @@ -1,9 +1,9 @@ import { default as computed } from "discourse-common/utils/decorators"; import getUrl from "discourse-common/lib/get-url"; -import WizardField from "wizard/models/wizard-field"; +import Field from "./field"; import { ajax } from "wizard/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; -import Step from "wizard/models/step"; +import Step from "./step"; import EmberObject from "@ember/object"; import Site from "./site"; @@ -73,7 +73,11 @@ CustomWizard.reopenClass({ } }); - stepObj.fields = stepObj.fields.map((f) => WizardField.create(f)); + stepObj.fields = stepObj.fields.map((f) => { + f.wizardId = wizardJson.id; + f.stepId = stepObj.id; + return Field.create(f); + }); return stepObj; }) diff --git a/assets/javascripts/wizard/router.js.es6 b/assets/javascripts/wizard/router.js.es6 new file mode 100644 index 00000000..5178c91f --- /dev/null +++ b/assets/javascripts/wizard/router.js.es6 @@ -0,0 +1,17 @@ +import EmberRouter from "@ember/routing/router"; +import getUrl from "discourse-common/lib/get-url"; +import { isTesting } from "discourse-common/config/environment"; + +const Router = EmberRouter.extend({ + rootURL: isTesting() ? getUrl("/") : getUrl("/w/"), + location: isTesting() ? "none" : "history", +}); + +Router.map(function () { + this.route("wizard", { path: "/:wizard_id" }, function () { + this.route("steps", { path: "/steps", resetNamespace: true }); + this.route("step", { path: "/steps/:step_id", resetNamespace: true }); + }); +}); + +export default Router; diff --git a/assets/javascripts/wizard/routes/application.js.es6 b/assets/javascripts/wizard/routes/application.js.es6 new file mode 100644 index 00000000..0051f5ce --- /dev/null +++ b/assets/javascripts/wizard/routes/application.js.es6 @@ -0,0 +1,3 @@ +import Route from "@ember/routing/route"; + +export default Route.extend(); diff --git a/assets/javascripts/wizard/routes/custom-steps.js.es6 b/assets/javascripts/wizard/routes/custom-steps.js.es6 deleted file mode 100644 index c58d1251..00000000 --- a/assets/javascripts/wizard/routes/custom-steps.js.es6 +++ /dev/null @@ -1,5 +0,0 @@ -export default Ember.Route.extend({ - redirect() { - this.transitionTo("custom.index"); - }, -}); diff --git a/assets/javascripts/wizard/routes/index.js.es6 b/assets/javascripts/wizard/routes/index.js.es6 new file mode 100644 index 00000000..b24b3631 --- /dev/null +++ b/assets/javascripts/wizard/routes/index.js.es6 @@ -0,0 +1,9 @@ +import Route from "@ember/routing/route"; + +export default Route.extend({ + beforeModel(transition) { + if (transition.intent.params) { + this.transitionTo("wizard"); + } + }, +}); diff --git a/assets/javascripts/wizard/routes/custom-step.js.es6 b/assets/javascripts/wizard/routes/step.js.es6 similarity index 78% rename from assets/javascripts/wizard/routes/custom-step.js.es6 rename to assets/javascripts/wizard/routes/step.js.es6 index 8088727a..2454fc95 100644 --- a/assets/javascripts/wizard/routes/custom-step.js.es6 +++ b/assets/javascripts/wizard/routes/step.js.es6 @@ -1,7 +1,8 @@ import WizardI18n from "../lib/wizard-i18n"; -import { getCachedWizard } from "../models/custom"; +import { getCachedWizard } from "../models/wizard"; +import Route from "@ember/routing/route"; -export default Ember.Route.extend({ +export default Route.extend({ beforeModel() { this.set("wizard", getCachedWizard()); }, @@ -19,11 +20,15 @@ export default Ember.Route.extend({ afterModel(model) { if (model.completed) { - return this.transitionTo("index"); + return this.transitionTo("wizard.index"); } return model.set("wizardId", this.wizard.id); }, + renderTemplate() { + this.render('wizard/templates/step'); + }, + setupController(controller, model) { let props = { step: model, diff --git a/assets/javascripts/wizard/routes/steps.js.es6 b/assets/javascripts/wizard/routes/steps.js.es6 new file mode 100644 index 00000000..6f35d152 --- /dev/null +++ b/assets/javascripts/wizard/routes/steps.js.es6 @@ -0,0 +1,7 @@ +import Route from "@ember/routing/route"; + +export default Route.extend({ + redirect() { + this.transitionTo("wizard.index"); + }, +}); diff --git a/assets/javascripts/wizard/routes/custom-index.js.es6 b/assets/javascripts/wizard/routes/wizard-index.js.es6 similarity index 50% rename from assets/javascripts/wizard/routes/custom-index.js.es6 rename to assets/javascripts/wizard/routes/wizard-index.js.es6 index a8abc152..16b1140a 100644 --- a/assets/javascripts/wizard/routes/custom-index.js.es6 +++ b/assets/javascripts/wizard/routes/wizard-index.js.es6 @@ -1,10 +1,11 @@ -import { getCachedWizard } from "../models/custom"; +import { getCachedWizard } from "../models/wizard"; +import Route from "@ember/routing/route"; -export default Ember.Route.extend({ +export default Route.extend({ beforeModel() { const wizard = getCachedWizard(); - if (wizard && wizard.permitted && !wizard.completed && wizard.start) { - this.replaceWith("custom.step", wizard.start); + if (wizard && wizard.user && wizard.permitted && !wizard.completed && wizard.start) { + this.replaceWith("step", wizard.start); } }, @@ -12,6 +13,10 @@ export default Ember.Route.extend({ return getCachedWizard(); }, + renderTemplate() { + this.render('wizard/templates/wizard-index'); + }, + setupController(controller, model) { if (model && model.id) { const completed = model.get("completed"); @@ -19,17 +24,20 @@ export default Ember.Route.extend({ const wizardId = model.get("id"); const user = model.get("user"); const name = model.get("name"); + const requiresLogin = !user; + const notPermitted = !permitted; - controller.setProperties({ - requiresLogin: !user, + const props = { + requiresLogin, user, name, completed, - notPermitted: !permitted, + notPermitted, wizardId, - }); + }; + controller.setProperties(props); } else { controller.set("noWizard", true); } - }, + } }); diff --git a/assets/javascripts/wizard/routes/custom.js.es6 b/assets/javascripts/wizard/routes/wizard.js.es6 similarity index 59% rename from assets/javascripts/wizard/routes/custom.js.es6 rename to assets/javascripts/wizard/routes/wizard.js.es6 index a312db3a..2910ee6d 100644 --- a/assets/javascripts/wizard/routes/custom.js.es6 +++ b/assets/javascripts/wizard/routes/wizard.js.es6 @@ -1,37 +1,26 @@ -/* eslint no-undef: 0*/ - -import { findCustomWizard, updateCachedWizard } from "../models/custom"; +import { findCustomWizard, updateCachedWizard } from "../models/wizard"; import { ajax } from "wizard/lib/ajax"; import WizardI18n from "../lib/wizard-i18n"; +import Route from "@ember/routing/route"; +import { scheduleOnce } from "@ember/runloop"; +import { getOwner } from "discourse-common/lib/get-owner"; -export default Ember.Route.extend({ +export default Route.extend({ beforeModel(transition) { - this.set("queryParams", transition.intent.queryParams); + if (transition.intent.queryParams) { + this.set("queryParams", transition.intent.queryParams); + } }, model(params) { return findCustomWizard(params.wizard_id, this.get("queryParams")); }, - renderTemplate() { - this.render("custom"); - const wizardModel = this.modelFor("custom"); - const stepModel = this.modelFor("custom.step"); - - if ( - wizardModel.resume_on_revisit && - wizardModel.submission_last_updated_at && - stepModel.index > 0 - ) { - this.showDialog(wizardModel); - } - }, - showDialog(wizardModel) { const title = WizardI18n("wizard.incomplete_submission.title", { date: moment(wizardModel.submission_last_updated_at).format( "MMMM Do YYYY" - ), + ) }); const buttons = [ @@ -57,28 +46,36 @@ export default Ember.Route.extend({ afterModel(model) { updateCachedWizard(model); + }, - return ajax({ - url: `/site/settings`, - type: "GET", - }).then((result) => { - $.extend(Wizard.SiteSettings, result); - }); + renderTemplate() { + this.render('wizard/templates/wizard'); }, setupController(controller, model) { - const background = model ? model.get("background") : "AliceBlue"; - Ember.run.scheduleOnce("afterRender", this, function () { - $("body.custom-wizard").css("background", background); + const background = model ? model.get("background") : ""; + + scheduleOnce("afterRender", this, function () { + $("body").css("background", background); if (model && model.id) { - $("#custom-wizard-main").addClass(model.id.dasherize()); + $(getOwner(this).rootElement).addClass(model.id.dasherize()); } }); + controller.setProperties({ customWizard: true, - logoUrl: Wizard.SiteSettings.logo_small, + logoUrl: this.siteSettings.logo_small, reset: null, }); + + const stepModel = this.modelFor("step"); + if ( + model.resume_on_revisit && + model.submission_last_updated_at && + stepModel.index > 0 + ) { + this.showDialog(model); + } }, }); diff --git a/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs b/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs index 7ce4c298..42fc63e8 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs @@ -3,6 +3,7 @@ value=field.value content=field.content tabindex=field.tabindex + onChange=(action "onChangeValue") options=(hash none="select_kit.default_header_text" )}} diff --git a/assets/javascripts/wizard/templates/custom.index.hbs b/assets/javascripts/wizard/templates/custom.index.hbs deleted file mode 100644 index 3cd02e05..00000000 --- a/assets/javascripts/wizard/templates/custom.index.hbs +++ /dev/null @@ -1,15 +0,0 @@ -{{#if noWizard}} - {{wizard-no-access text=(wizard-i18n "wizard.none") wizardId=wizardId}} -{{else}} - {{#if requiresLogin}} - {{wizard-no-access text=(wizard-i18n "wizard.requires_login") wizardId=wizardId}} - {{else}} - {{#if notPermitted}} - {{wizard-no-access text=(wizard-i18n "wizard.not_permitted") wizardId=wizardId}} - {{else}} - {{#if completed}} - {{wizard-no-access text=(wizard-i18n "wizard.completed") wizardId=wizardId}} - {{/if}} - {{/if}} - {{/if}} -{{/if}} diff --git a/assets/javascripts/wizard/templates/index.hbs b/assets/javascripts/wizard/templates/index.hbs new file mode 100644 index 00000000..c24cd689 --- /dev/null +++ b/assets/javascripts/wizard/templates/index.hbs @@ -0,0 +1 @@ +{{outlet}} diff --git a/assets/javascripts/wizard/templates/custom.step.hbs b/assets/javascripts/wizard/templates/step.hbs similarity index 100% rename from assets/javascripts/wizard/templates/custom.step.hbs rename to assets/javascripts/wizard/templates/step.hbs diff --git a/assets/javascripts/wizard/templates/wizard-index.hbs b/assets/javascripts/wizard/templates/wizard-index.hbs new file mode 100644 index 00000000..a8f1b9ba --- /dev/null +++ b/assets/javascripts/wizard/templates/wizard-index.hbs @@ -0,0 +1,3 @@ +{{#if noAccess}} + {{wizard-no-access text=(wizard-i18n noAccessI18nKey) wizardId=wizardId reason=noAccessReason}} +{{/if}} diff --git a/assets/javascripts/wizard/templates/custom.hbs b/assets/javascripts/wizard/templates/wizard.hbs similarity index 86% rename from assets/javascripts/wizard/templates/custom.hbs rename to assets/javascripts/wizard/templates/wizard.hbs index 4701fec2..f6d6127e 100644 --- a/assets/javascripts/wizard/templates/custom.hbs +++ b/assets/javascripts/wizard/templates/wizard.hbs @@ -1,7 +1,3 @@ -{{#if showCanvas}} - {{wizard-canvas}} -{{/if}} -
{{outlet}} diff --git a/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 new file mode 100644 index 00000000..f73d1ab7 --- /dev/null +++ b/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 @@ -0,0 +1,135 @@ +import { + visit, + click, + fillIn, + triggerKeyEvent +} from "@ember/test-helpers"; +import { test } from "qunit"; +import { exists } from "../helpers/test"; +import acceptance, { + query, + count, + visible, + server +} from "../helpers/acceptance"; +import { + allFieldsWizard, + getWizard +} from "../helpers/wizard"; +import tagsJson from "../fixtures/tags"; +import usersJson from "../fixtures/users"; +import { response } from "../pretender"; + +acceptance("Field | Fields", [ getWizard(allFieldsWizard) ], + function(hooks) { + test("Text", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-field.text-field input.wizard-focusable")); + }); + + test("Textarea", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.textarea-field textarea.wizard-focusable")); + }); + + test("Composer", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.composer-field .wizard-field-composer textarea")); + assert.strictEqual(count(".wizard-field.composer-field .d-editor-button-bar button"), 8); + assert.ok(visible(".wizard-btn.toggle-preview")); + + await fillIn(".wizard-field.composer-field .wizard-field-composer textarea", "Input in composer"); + await click(".wizard-btn.toggle-preview"); + assert.strictEqual(query('.wizard-field.composer-field .wizard-field-composer .d-editor-preview-wrapper p').textContent.trim(), "Input in composer"); + }); + + test("Text Only", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.text-only-field label.field-label")); + }); + + test("Date", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.date-field input.date-picker")); + await click(".wizard-field.date-field input.date-picker"); + assert.ok(visible(".wizard-field.date-field .pika-single")); + }); + + test("Time", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.time-field .d-time-input .select-kit")); + await click(".wizard-field.time-field .d-time-input .select-kit .select-kit-header"); + assert.ok(visible(".wizard-field.time-field .select-kit-collection")); + }); + + test("Date Time", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.date-time-field .d-date-time-input .select-kit")); + await click(".wizard-field.date-time-field .d-date-input input.date-picker"); + assert.ok(visible(".wizard-field.date-time-field .d-date-input .pika-single")); + await click(".wizard-field.date-time-field .d-time-input .select-kit .select-kit-header"); + assert.ok(visible(".wizard-field.date-time-field .select-kit-collection")); + }); + + test("Number", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.number-field input[type='number']")); + }); + + test("Checkbox", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.checkbox-field input[type='checkbox']")); + }); + + test("Url", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.url-field input[type='text']")); + }); + + test("Upload", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.upload-field label.wizard-btn-upload-file")); + assert.ok(exists(".wizard-field.upload-field input.hidden-upload-field")); + }); + + test("Dropdown", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.dropdown-field .single-select-header")); + await click(".wizard-field.dropdown-field .select-kit-header"); + assert.strictEqual(count(".wizard-field.dropdown-field .select-kit-collection li"), 3); + }); + + test("Tag", async function (assert) { + server.get("/tags/filter/search", () => (response(200, { results: tagsJson['tags']}))); + await visit("/wizard"); + assert.ok(visible(".wizard-field.tag-field .multi-select-header")); + await click(".wizard-field.tag-field .select-kit-header"); + assert.strictEqual(count(".wizard-field.tag-field .select-kit-collection li"), 2); + }); + + test("Category", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.category-field .multi-select-header")); + await click(".wizard-field.category-field .select-kit-header"); + assert.strictEqual(count(".wizard-field.category-field .select-kit-collection li"), 5); + }); + + test("Group", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.group-field .single-select-header")); + await click(".wizard-field.group-field .select-kit-header"); + assert.strictEqual(count(".wizard-field.group-field .select-kit-collection li"), 10); + }); + + test("User", async function (assert) { + server.get("/u/search/users", () => (response(200, usersJson))); + + await visit("/wizard"); + await fillIn(".wizard-field.user-selector-field input.ember-text-field", "a"); + await triggerKeyEvent(".wizard-field.user-selector-field input.ember-text-field", "keyup", "a".charCodeAt(0)); + + assert.ok(visible(".wizard-field.user-selector-field .ac-wrap")); + // TODO: add assertion for ac results. autocomplete does not appear in time. + }); + } +); diff --git a/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 new file mode 100644 index 00000000..731c9b76 --- /dev/null +++ b/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 @@ -0,0 +1,47 @@ +import { visit, click } from "@ember/test-helpers"; +import { test } from "qunit"; +import { exists } from "../helpers/test"; +import acceptance, { + query, + count, + visible +} from "../helpers/acceptance"; +import { + stepNotPermitted, + wizard, + getWizard +} from "../helpers/wizard"; +import { + saveStep, + update +} from "../helpers/step"; + +acceptance("Step | Not permitted", [ getWizard(stepNotPermitted) ], + function(hooks) { + test("Shows not permitted message", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".step-message.not-permitted")); + }); + } +); + +acceptance("Step | Step", [ getWizard(wizard), saveStep(update) ], + function(hooks) { + test("Renders the step", async function (assert) { + await visit("/wizard"); + assert.strictEqual(query('.wizard-step-title p').textContent.trim(), "Text"); + assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); + assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); + assert.strictEqual(count('.wizard-step-form .wizard-field'), 6); + assert.ok(visible('.wizard-step-footer .wizard-progress'), true); + assert.ok(visible('.wizard-step-footer .wizard-buttons'), true); + }); + + test("Goes to the next step", async function (assert) { + await visit("/wizard"); + assert.ok(visible('.wizard-step.step_1'), true); + await click('.wizard-btn.next'); + assert.ok(visible('.wizard-step.step_2'), true); + }); + } +); diff --git a/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 new file mode 100644 index 00000000..e68f59ae --- /dev/null +++ b/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 @@ -0,0 +1,72 @@ +import { visit } from "@ember/test-helpers"; +import { test } from "qunit"; +import { exists } from "../helpers/test"; +import acceptance, { + query, + count, + visible +} from "../helpers/acceptance"; +import { + wizardNoUser, + wizardNotPermitted, + wizardCompleted, + wizard, + getWizard +} from "../helpers/wizard"; + +acceptance("Wizard | Not logged in", [ getWizard(wizardNoUser) ], + function(hooks) { + test("Wizard no access requires login", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-no-access.requires-login")); + }); + } +); + +acceptance("Wizard | Not permitted", [ getWizard(wizardNotPermitted) ], + function(hooks) { + test("Wizard no access not permitted", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-no-access.not-permitted")); + }); + } +); + +acceptance("Wizard | Completed", [ getWizard(wizardCompleted) ], + function(hooks) { + test("Wizard no access completed", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-no-access.completed")); + }); + } +); + +acceptance("Wizard | Wizard", [ getWizard(wizard) ], + function(hooks) { + test("Starts", async function (assert) { + await visit("/wizard"); + assert.ok(query('.wizard-column'), true); + }); + + test("Applies the body background color", async function (assert) { + await visit("/wizard"); + assert.ok($("body")[0].style.background); + }); + + test("Renders the wizard form", async function (assert) { + await visit("/wizard"); + assert.ok(visible('.wizard-column-contents .wizard-step'), true); + assert.ok(visible('.wizard-footer img'), true); + }); + + test("Renders the first step", async function (assert) { + await visit("/wizard"); + assert.strictEqual(query('.wizard-step-title p').textContent.trim(), "Text"); + assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); + assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); + assert.strictEqual(count('.wizard-step-form .wizard-field'), 6); + assert.ok(visible('.wizard-step-footer .wizard-progress'), true); + assert.ok(visible('.wizard-step-footer .wizard-buttons'), true); + }); + } +); diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 new file mode 100644 index 00000000..d2c503ad --- /dev/null +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -0,0 +1,17 @@ +// discourse-skip-module + +document.addEventListener("DOMContentLoaded", function () { + document.body.insertAdjacentHTML( + "afterbegin", + ` +
+ + ` + ); +}); + +Object.keys(requirejs.entries).forEach(function (entry) { + if (/\-test/.test(entry)) { + requirejs(entry); + } +}); diff --git a/assets/javascripts/wizard/tests/fixtures/categories.js.es6 b/assets/javascripts/wizard/tests/fixtures/categories.js.es6 new file mode 100644 index 00000000..e862f54a --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/categories.js.es6 @@ -0,0 +1,209 @@ +export default { + "categories": [ + { + "id": 1, + "name": "Uncategorized", + "color": "0088CC", + "text_color": "FFFFFF", + "slug": "uncategorized", + "topic_count": 1, + "post_count": 1, + "position": 0, + "description": "Topics that don't need a category, or don't fit into any other existing category.", + "description_text": "Topics that don't need a category, or don't fit into any other existing category.", + "description_excerpt": "Topics that don't need a category, or don't fit into any other existing category.", + "topic_url": "/t/", + "read_restricted": false, + "permission": 1, + "notification_level": 0, + "topic_template": null, + "has_children": false, + "sort_order": null, + "sort_ascending": null, + "show_subcategory_list": false, + "num_featured_topics": 3, + "default_view": null, + "subcategory_list_style": "rows_with_featured_topics", + "default_top_period": "all", + "default_list_filter": "all", + "minimum_required_tags": 0, + "navigate_to_first_post_after_read": false, + "custom_fields": { + "create_topic_wizard": null + }, + "allowed_tags": [], + "allowed_tag_groups": [], + "allow_global_tags": false, + "min_tags_from_required_group": 1, + "required_tag_group_name": null, + "read_only_banner": null, + "uploaded_logo": null, + "uploaded_background": null, + "can_edit": true + }, + { + "id": 2, + "name": "Site Feedback", + "color": "808281", + "text_color": "FFFFFF", + "slug": "site-feedback", + "topic_count": 20, + "post_count": 21, + "position": 1, + "description": "

Discussion about this site, its organization, how it works, and how we can improve it.

", + "description_text": "Discussion about this site, its organization, how it works, and how we can improve it.", + "description_excerpt": "Discussion about this site, its organization, how it works, and how we can improve it.", + "topic_url": "/t/about-the-site-feedback-category/1", + "read_restricted": false, + "permission": 1, + "notification_level": 0, + "topic_template": null, + "has_children": false, + "sort_order": null, + "sort_ascending": null, + "show_subcategory_list": false, + "num_featured_topics": 3, + "default_view": null, + "subcategory_list_style": "rows_with_featured_topics", + "default_top_period": "all", + "default_list_filter": "all", + "minimum_required_tags": 0, + "navigate_to_first_post_after_read": false, + "custom_fields": { + "create_topic_wizard": null + }, + "allowed_tags": [], + "allowed_tag_groups": [], + "allow_global_tags": false, + "min_tags_from_required_group": 1, + "required_tag_group_name": null, + "read_only_banner": null, + "uploaded_logo": null, + "uploaded_background": null, + "can_edit": true + }, + { + "id": 3, + "name": "Staff", + "color": "E45735", + "text_color": "FFFFFF", + "slug": "staff", + "topic_count": 4, + "post_count": 7, + "position": 2, + "description": "

Private category for staff discussions. Topics are only visible to admins and moderators.

", + "description_text": "Private category for staff discussions. Topics are only visible to admins and moderators.", + "description_excerpt": "Private category for staff discussions. Topics are only visible to admins and moderators.", + "topic_url": "/t/about-the-staff-category/2", + "read_restricted": true, + "permission": 1, + "notification_level": 0, + "topic_template": null, + "has_children": false, + "sort_order": null, + "sort_ascending": null, + "show_subcategory_list": false, + "num_featured_topics": 3, + "default_view": null, + "subcategory_list_style": "rows_with_featured_topics", + "default_top_period": "all", + "default_list_filter": "all", + "minimum_required_tags": 0, + "navigate_to_first_post_after_read": false, + "custom_fields": { + "create_topic_wizard": null + }, + "allowed_tags": [], + "allowed_tag_groups": [], + "allow_global_tags": false, + "min_tags_from_required_group": 1, + "required_tag_group_name": null, + "read_only_banner": null, + "uploaded_logo": null, + "uploaded_background": null, + "can_edit": true + }, + { + "id": 4, + "name": "Lounge", + "color": "A461EF", + "text_color": "652D90", + "slug": "lounge", + "topic_count": 1, + "post_count": 1, + "position": 3, + "description": "

A category exclusive to members with trust level 3 and higher.

", + "description_text": "A category exclusive to members with trust level 3 and higher.", + "description_excerpt": "A category exclusive to members with trust level 3 and higher.", + "topic_url": "/t/about-the-lounge-category/3", + "read_restricted": true, + "permission": 1, + "notification_level": 0, + "topic_template": null, + "has_children": false, + "sort_order": null, + "sort_ascending": null, + "show_subcategory_list": false, + "num_featured_topics": 3, + "default_view": null, + "subcategory_list_style": "rows_with_featured_topics", + "default_top_period": "all", + "default_list_filter": "all", + "minimum_required_tags": 0, + "navigate_to_first_post_after_read": false, + "custom_fields": { + "create_topic_wizard": null + }, + "allowed_tags": [], + "allowed_tag_groups": [], + "allow_global_tags": false, + "min_tags_from_required_group": 1, + "required_tag_group_name": null, + "read_only_banner": null, + "uploaded_logo": null, + "uploaded_background": null, + "can_edit": true + }, + { + "id": 5, + "name": "Custom Categories", + "color": "0088CC", + "text_color": "FFFFFF", + "slug": "custom-category", + "topic_count": 0, + "post_count": 0, + "position": 10, + "description": "Description of custom category", + "description_text": "Description of custom category", + "description_excerpt": "Description of custom category", + "topic_url": "/t/about-the-custom-category/5", + "read_restricted": false, + "permission": 1, + "notification_level": 0, + "topic_template": null, + "has_children": false, + "sort_order": null, + "sort_ascending": null, + "show_subcategory_list": false, + "num_featured_topics": 3, + "default_view": null, + "subcategory_list_style": "rows_with_featured_topics", + "default_top_period": "all", + "default_list_filter": "all", + "minimum_required_tags": 0, + "navigate_to_first_post_after_read": false, + "custom_fields": { + "create_topic_wizard": null + }, + "allowed_tags": [], + "allowed_tag_groups": [], + "allow_global_tags": false, + "min_tags_from_required_group": 1, + "required_tag_group_name": null, + "read_only_banner": null, + "uploaded_logo": null, + "uploaded_background": null, + "can_edit": true + } + ] +} diff --git a/assets/javascripts/wizard/tests/fixtures/groups.js.es6 b/assets/javascripts/wizard/tests/fixtures/groups.js.es6 new file mode 100644 index 00000000..16f8150d --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/groups.js.es6 @@ -0,0 +1,313 @@ +export default { + "groups": [ + { + "id": 1, + "automatic": true, + "name": "admins", + "display_name": "admins", + "user_count": 1, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 0, + "automatic": true, + "name": "everyone", + "display_name": "everyone", + "user_count": 0, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 3, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 15, + "automatic": false, + "name": "custom_group", + "user_count": 1, + "mentionable_level": 1, + "messageable_level": 2, + "visibility_level": 3, + "primary_group": false, + "title": "Custom Group", + "grant_trust_level": 3, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": "I am prefilled", + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 99, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 2, + "automatic": true, + "name": "moderators", + "display_name": "moderators", + "user_count": 0, + "mentionable_level": 0, + "messageable_level": 99, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 2, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 3, + "automatic": true, + "name": "staff", + "display_name": "staff", + "user_count": 1, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 10, + "automatic": true, + "name": "trust_level_0", + "display_name": "trust_level_0", + "user_count": 2, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 11, + "automatic": true, + "name": "trust_level_1", + "display_name": "trust_level_1", + "user_count": 2, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 12, + "automatic": true, + "name": "trust_level_2", + "display_name": "trust_level_2", + "user_count": 1, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 13, + "automatic": true, + "name": "trust_level_3", + "display_name": "trust_level_3", + "user_count": 1, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 14, + "automatic": true, + "name": "trust_level_4", + "display_name": "trust_level_4", + "user_count": 0, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + } + ] +} diff --git a/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 b/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 new file mode 100644 index 00000000..f71ace8e --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 @@ -0,0 +1,283 @@ +export default { + "default_locale": "en", + "title": "Discourse", + "short_site_description": "", + "exclude_rel_nofollow_domains": "", + "logo": "/images/discourse-logo-sketch.png", + "logo_small": "/images/discourse-logo-sketch-small.png", + "digest_logo": "", + "mobile_logo": "", + "logo_dark": "", + "logo_small_dark": "", + "mobile_logo_dark": "", + "large_icon": "", + "favicon": "", + "apple_touch_icon": "", + "display_local_time_in_user_card": false, + "allow_user_locale": false, + "set_locale_from_accept_language_header": false, + "support_mixed_text_direction": false, + "suggested_topics": 5, + "ga_universal_tracking_code": "", + "ga_universal_domain_name": "auto", + "gtm_container_id": "", + "top_menu": "categories|latest", + "post_menu": "read|like|share|flag|edit|bookmark|delete|admin|reply", + "post_menu_hidden_items": "flag|bookmark|edit|delete|admin", + "share_links": "twitter|facebook|email", + "share_quote_visibility": "anonymous", + "share_quote_buttons": "twitter|email", + "desktop_category_page_style": "categories_and_latest_topics", + "category_colors": "BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|E45735", + "category_style": "bullet", + "max_category_nesting": 2, + "enable_mobile_theme": true, + "enable_direct_s3_uploads": false, + "enable_upload_debug_mode": false, + "default_dark_mode_color_scheme_id": 1, + "relative_date_duration": 30, + "fixed_category_positions": false, + "fixed_category_positions_on_create": false, + "enable_badges": true, + "enable_badge_sql": true, + "max_favorite_badges": 2, + "enable_whispers": false, + "enable_bookmarks_with_reminders": true, + "push_notifications_prompt": true, + "vapid_public_key_bytes": "4|29|219|88|202|66|198|62|182|204|66|176|229|200|131|26|141|21|178|231|150|161|2|128|228|200|179|126|118|232|196|19|232|76|108|189|54|211|210|155|55|228|173|112|38|158|114|127|18|95|7|56|110|183|192|92|43|0|243|249|233|89|9|207|255", + "invite_only": false, + "login_required": false, + "must_approve_users": false, + "enable_local_logins": true, + "enable_local_logins_via_email": true, + "allow_new_registrations": true, + "enable_signup_cta": true, + "facebook_app_id": "", + "auth_skip_create_confirm": false, + "auth_overrides_email": false, + "enable_discourse_connect": true, + "discourse_connect_overrides_avatar": false, + "hide_email_address_taken": false, + "min_username_length": 3, + "max_username_length": 20, + "unicode_usernames": false, + "min_password_length": 10, + "min_admin_password_length": 15, + "email_editable": true, + "logout_redirect": "", + "full_name_required": false, + "enable_names": true, + "invite_expiry_days": 90, + "invites_per_page": 40, + "delete_user_max_post_age": 60, + "delete_all_posts_max": 15, + "prioritize_username_in_ux": true, + "enable_user_directory": true, + "allow_anonymous_posting": false, + "anonymous_posting_min_trust_level": 1, + "allow_users_to_hide_profile": true, + "hide_user_profiles_from_public": false, + "allow_featured_topic_on_user_profiles": true, + "hide_suspension_reasons": false, + "ignored_users_count_message_threshold": 5, + "ignored_users_message_gap_days": 365, + "user_selected_primary_groups": false, + "gravatar_name": "Gravatar", + "gravatar_base_url": "www.gravatar.com", + "gravatar_login_url": "/emails", + "enable_group_directory": true, + "enable_category_group_moderation": false, + "min_post_length": 20, + "min_first_post_length": 20, + "min_personal_message_post_length": 10, + "max_post_length": 32000, + "topic_featured_link_enabled": true, + "min_topic_views_for_delete_confirm": 5000, + "min_topic_title_length": 15, + "max_topic_title_length": 255, + "enable_filtered_replies_view": false, + "min_personal_message_title_length": 2, + "allow_uncategorized_topics": true, + "min_title_similar_length": 10, + "enable_personal_messages": true, + "edit_history_visible_to_public": true, + "delete_removed_posts_after": 24, + "traditional_markdown_linebreaks": false, + "enable_markdown_typographer": true, + "enable_markdown_linkify": true, + "markdown_linkify_tlds": "com|net|org|io|onion|co|tv|ru|cn|us|uk|me|de|fr|fi|gov", + "markdown_typographer_quotation_marks": "“|”|‘|’", + "enable_rich_text_paste": true, + "suppress_reply_directly_below": true, + "suppress_reply_directly_above": true, + "max_reply_history": 1, + "enable_mentions": true, + "here_mention": "here", + "newuser_max_embedded_media": 1, + "newuser_max_attachments": 0, + "show_pinned_excerpt_mobile": true, + "show_pinned_excerpt_desktop": true, + "display_name_on_posts": false, + "show_time_gap_days": 7, + "short_progress_text_threshold": 10000, + "default_code_lang": "auto", + "autohighlight_all_code": false, + "highlighted_languages": "apache|bash|cs|cpp|css|coffeescript|diff|xml|http|ini|json|java|javascript|makefile|markdown|nginx|objectivec|ruby|perl|php|python|sql|handlebars", + "show_copy_button_on_codeblocks": false, + "enable_emoji": true, + "enable_emoji_shortcuts": true, + "emoji_set": "twitter", + "emoji_autocomplete_min_chars": 0, + "enable_inline_emoji_translation": false, + "code_formatting_style": "code-fences", + "allowed_href_schemes": "", + "watched_words_regular_expressions": false, + "enable_diffhtml_preview": false, + "enable_fast_edit": true, + "old_post_notice_days": 14, + "blur_tl0_flagged_posts_media": true, + "email_time_window_mins": 10, + "disable_digest_emails": false, + "email_in": false, + "enable_imap": false, + "enable_smtp": false, + "disable_emails": "no", + "bounce_score_threshold": 4, + "enable_secondary_emails": true, + "max_image_size_kb": 4096, + "max_attachment_size_kb": 4096, + "authorized_extensions": "jpg|jpeg|png|gif|heic|heif|webp", + "authorized_extensions_for_staff": "", + "max_image_width": 690, + "max_image_height": 500, + "prevent_anons_from_downloading_files": false, + "secure_media": false, + "enable_s3_uploads": false, + "allow_profile_backgrounds": true, + "allow_uploaded_avatars": "0", + "default_avatars": "", + "external_system_avatars_enabled": true, + "external_system_avatars_url": "/letter_avatar_proxy/v4/letter/{first_letter}/{color}/{size}.png", + "external_emoji_url": "", + "selectable_avatars_mode": "disabled", + "selectable_avatars": "", + "allow_staff_to_upload_any_file_in_pm": true, + "simultaneous_uploads": 5, + "composer_media_optimization_image_enabled": true, + "composer_media_optimization_image_bytes_optimization_threshold": 524288, + "composer_media_optimization_image_resize_dimensions_threshold": 1920, + "composer_media_optimization_image_resize_width_target": 1920, + "composer_media_optimization_image_resize_pre_multiply": false, + "composer_media_optimization_image_resize_linear_rgb": false, + "composer_media_optimization_image_encode_quality": 75, + "composer_media_optimization_debug_mode": false, + "min_trust_level_to_allow_profile_background": 0, + "min_trust_level_to_allow_user_card_background": 0, + "min_trust_level_to_allow_ignore": 2, + "tl1_requires_read_posts": 30, + "tl3_links_no_follow": false, + "enforce_second_factor": "no", + "moderators_change_post_ownership": false, + "moderators_view_emails": false, + "use_admin_ip_allowlist": false, + "allowed_iframes": "https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?|https://calendar.google.com/calendar/embed?|https://codepen.io/|https://www.instagram.com|http://localhost:3000/discobot/certificate.svg", + "can_permanently_delete": false, + "max_oneboxes_per_post": 50, + "reviewable_claiming": "disabled", + "reviewable_default_topics": false, + "reviewable_default_visibility": "low", + "alert_admins_if_errors_per_minute": 0, + "alert_admins_if_errors_per_hour": 0, + "max_prints_per_hour_per_user": 5, + "invite_link_max_redemptions_limit": 5000, + "invite_link_max_redemptions_limit_users": 10, + "enable_long_polling": true, + "enable_chunked_encoding": true, + "long_polling_base_url": "/", + "background_polling_interval": 60000, + "polling_interval": 3000, + "anon_polling_interval": 25000, + "flush_timings_secs": 60, + "verbose_localization": false, + "max_new_topics": 500, + "enable_safe_mode": true, + "tos_url": "", + "privacy_policy_url": "", + "faq_url": "", + "enable_backups": true, + "backup_location": "local", + "maximum_backups": 5, + "use_pg_headlines_for_excerpt": false, + "min_search_term_length": 3, + "log_search_queries": true, + "version_checks": true, + "suppress_uncategorized_badge": true, + "header_dropdown_category_count": 8, + "slug_generation_method": "ascii", + "summary_timeline_button": false, + "topic_views_heat_low": 1000, + "topic_views_heat_medium": 2000, + "topic_views_heat_high": 3500, + "topic_post_like_heat_low": 0.5, + "topic_post_like_heat_medium": 1, + "topic_post_like_heat_high": 2, + "history_hours_low": 12, + "history_hours_medium": 24, + "history_hours_high": 48, + "cold_age_days_low": 14, + "cold_age_days_medium": 90, + "cold_age_days_high": 180, + "global_notice": "", + "show_create_topics_notice": true, + "bootstrap_mode_min_users": 50, + "bootstrap_mode_enabled": true, + "automatically_unpin_topics": true, + "read_time_word_count": 500, + "topic_page_title_includes_category": true, + "svg_icon_subset": "", + "allow_bulk_invite": true, + "disable_mailing_list_mode": true, + "default_topics_automatic_unpin": true, + "mute_all_categories_by_default": false, + "tagging_enabled": true, + "tag_style": "simple", + "max_tags_per_topic": 5, + "max_tag_length": 20, + "min_trust_level_to_tag_topics": "0", + "max_tag_search_results": 5, + "max_tags_in_filter_list": 30, + "tags_sort_alphabetically": false, + "tags_listed_by_group": false, + "suppress_overlapping_tags_in_list": false, + "remove_muted_tags_from_latest": "always", + "force_lowercase_tags": true, + "dashboard_hidden_reports": "", + "dashboard_visible_tabs": "moderation|security|reports", + "dashboard_general_tab_activity_metrics": "page_view_total_reqs|visits|time_to_first_response|likes|flags|user_to_user_private_messages_with_replies", + "discourse_narrative_bot_enabled": true, + "details_enabled": true, + "custom_wizard_enabled": true, + "wizard_redirect_exclude_paths": "admin", + "wizard_recognised_image_upload_formats": "jpg|jpeg|png|gif", + "wizard_important_notices_on_dashboard": true, + "discourse_local_dates_email_format": "YYYY-MM-DDTHH:mm:ss[Z]", + "discourse_local_dates_enabled": true, + "discourse_local_dates_default_formats": "LLL|LTS|LL|LLLL", + "discourse_local_dates_default_timezones": "Europe/Paris|America/Los_Angeles", + "poll_enabled": true, + "poll_maximum_options": 20, + "poll_minimum_trust_level_to_create": 1, + "poll_groupable_user_fields": "", + "poll_export_data_explorer_query_id": -16, + "presence_enabled": true, + "presence_max_users_shown": 5, + "available_locales": "[{\"name\":\"اللغة العربية\",\"value\":\"ar\"},{\"name\":\"беларуская мова\",\"value\":\"be\"},{\"name\":\"български език\",\"value\":\"bg\"},{\"name\":\"bosanski jezik\",\"value\":\"bs_BA\"},{\"name\":\"català\",\"value\":\"ca\"},{\"name\":\"čeština\",\"value\":\"cs\"},{\"name\":\"dansk\",\"value\":\"da\"},{\"name\":\"Deutsch\",\"value\":\"de\"},{\"name\":\"ελληνικά\",\"value\":\"el\"},{\"name\":\"English (US)\",\"value\":\"en\"},{\"name\":\"English (UK)\",\"value\":\"en_GB\"},{\"name\":\"Español\",\"value\":\"es\"},{\"name\":\"eesti\",\"value\":\"et\"},{\"name\":\"فارسی\",\"value\":\"fa_IR\"},{\"name\":\"suomi\",\"value\":\"fi\"},{\"name\":\"Français\",\"value\":\"fr\"},{\"name\":\"galego\",\"value\":\"gl\"},{\"name\":\"עברית\",\"value\":\"he\"},{\"name\":\"magyar\",\"value\":\"hu\"},{\"name\":\"Հայերեն\",\"value\":\"hy\"},{\"name\":\"Indonesian\",\"value\":\"id\"},{\"name\":\"Italiano\",\"value\":\"it\"},{\"name\":\"日本語\",\"value\":\"ja\"},{\"name\":\"한국어\",\"value\":\"ko\"},{\"name\":\"lietuvių kalba\",\"value\":\"lt\"},{\"name\":\"latviešu valoda\",\"value\":\"lv\"},{\"name\":\"Norsk bokmål\",\"value\":\"nb_NO\"},{\"name\":\"Nederlands\",\"value\":\"nl\"},{\"name\":\"polski\",\"value\":\"pl_PL\"},{\"name\":\"Português\",\"value\":\"pt\"},{\"name\":\"Português (BR)\",\"value\":\"pt_BR\"},{\"name\":\"limba română\",\"value\":\"ro\"},{\"name\":\"Русский\",\"value\":\"ru\"},{\"name\":\"slovenčina\",\"value\":\"sk\"},{\"name\":\"slovenščina\",\"value\":\"sl\"},{\"name\":\"Shqip\",\"value\":\"sq\"},{\"name\":\"српски језик\",\"value\":\"sr\"},{\"name\":\"svenska\",\"value\":\"sv\"},{\"name\":\"Kiswahili\",\"value\":\"sw\"},{\"name\":\"తెలుగు\",\"value\":\"te\"},{\"name\":\"ไทย\",\"value\":\"th\"},{\"name\":\"Türkçe\",\"value\":\"tr_TR\"},{\"name\":\"українська мова\",\"value\":\"uk\"},{\"name\":\"اردو\",\"value\":\"ur\"},{\"name\":\"Việt Nam\",\"value\":\"vi\"},{\"name\":\"简体中文\",\"value\":\"zh_CN\"},{\"name\":\"繁體中文\",\"value\":\"zh_TW\"}]", + "require_invite_code": false, + "site_logo_url": "http://localhost:3000/images/discourse-logo-sketch.png", + "site_logo_small_url": "http://localhost:3000/images/discourse-logo-sketch-small.png", + "site_mobile_logo_url": "http://localhost:3000/images/discourse-logo-sketch.png", + "site_favicon_url": "http://localhost:3000/uploads/default/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_32x32.png", + "site_logo_dark_url": "", + "site_logo_small_dark_url": "", + "site_mobile_logo_dark_url": "" +} diff --git a/assets/javascripts/wizard/tests/fixtures/tags.js.es6 b/assets/javascripts/wizard/tests/fixtures/tags.js.es6 new file mode 100644 index 00000000..a072772e --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/tags.js.es6 @@ -0,0 +1,22 @@ +export default { + "tags": [ + { + "id": "tag1", + "text": "tag1", + "name": "tag1", + "description": null, + "count": 1, + "pm_count": 0, + "target_tag": null + }, + { + "id": "tag2", + "text": "tag2", + "name": "tag2", + "description": null, + "count": 1, + "pm_count": 0, + "target_tag": null + } + ] +} diff --git a/assets/javascripts/wizard/tests/fixtures/update.js.es6 b/assets/javascripts/wizard/tests/fixtures/update.js.es6 new file mode 100644 index 00000000..3908525c --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/update.js.es6 @@ -0,0 +1,5 @@ +export default { + "final": false, + "next_step_id": "step_2", + "wizard": {} +} diff --git a/assets/javascripts/wizard/tests/fixtures/user.js.es6 b/assets/javascripts/wizard/tests/fixtures/user.js.es6 new file mode 100644 index 00000000..8acd7392 --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/user.js.es6 @@ -0,0 +1,34 @@ +export default { + id: 19, + username: "angus", + uploaded_avatar_id: 5275, + avatar_template: "/user_avatar/localhost/angus/{size}/5275.png", + name: "Angus McLeod", + unread_notifications: 0, + unread_private_messages: 0, + unread_high_priority_notifications: 0, + admin: true, + notification_channel_position: null, + site_flagged_posts_count: 1, + moderator: true, + staff: true, + can_create_group: true, + title: "", + reply_count: 859, + topic_count: 36, + enable_quoting: true, + external_links_in_new_tab: false, + dynamic_favicon: true, + trust_level: 4, + can_edit: true, + can_invite_to_forum: true, + should_be_redirected_to_top: false, + custom_fields: {}, + muted_category_ids: [], + dismissed_banner_key: null, + akismet_review_count: 0, + title_count_mode: "notifications", + timezone: "Australia/Perth", + skip_new_user_tips: false, + can_review: true +} diff --git a/assets/javascripts/wizard/tests/fixtures/users.js.es6 b/assets/javascripts/wizard/tests/fixtures/users.js.es6 new file mode 100644 index 00000000..267d4909 --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/users.js.es6 @@ -0,0 +1,14 @@ +export default { + "users": [ + { + "username": "angus", + "name": "Angus", + "avatar_template": "/user_avatar/localhost/angus/{size}/12_2.png" + }, + { + "username": "angus_2", + "name": "Angus 2", + "avatar_template": "/letter_avatar_proxy/v4/letter/a/e9a140/{size}.png" + } + ] +} diff --git a/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 b/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 new file mode 100644 index 00000000..be4fa8b2 --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 @@ -0,0 +1,469 @@ +export default { + "id": "wizard", + "name": "Wizard", + "start": "step_1", + "background": "#333333", + "submission_last_updated_at": "2022-03-15T21:11:01+01:00", + "theme_id": 2, + "required": false, + "permitted": true, + "uncategorized_category_id": 1, + "categories": [], + "subscribed": false, + "resume_on_revisit": false, + "steps": [ + { + "id": "step_1", + "index": 0, + "next": "step_2", + "description": "

Text inputs!

", + "title": "Text", + "permitted": true, + "permitted_message": null, + "final": false, + "fields": [ + { + "id": "step_1_field_1", + "index": 0, + "type": "text", + "required": false, + "value": "I am prefilled", + "label": "

Text

", + "description": "Text field description.", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 1, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + }, + { + "id": "step_1_field_2", + "index": 0, + "type": "textarea", + "required": false, + "value": "", + "label": "

Textarea

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 2, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + }, + { + "id": "step_1_field_3", + "index": 2, + "type": "composer", + "required": false, + "value": "", + "label": "

Composer

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 3, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + }, + { + "id": "step_1_field_4", + "index": 3, + "type": "text_only", + "required": false, + "value": null, + "label": "

I’m only text

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 4, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + }, + { + "id": "step_1_field_5", + "index": 4, + "type": "composer_preview", + "required": false, + "value": "", + "label": "

I’m a preview

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": "

I am prefilled

", + "tabindex": 5, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + }, + { + "id": "step_1_field_6", + "index": 5, + "type": "composer_preview", + "required": false, + "value": "", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": "

This is the preview of the composer

", + "tabindex": 6, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + } + ], + "_validState": 0, + "wizardId": "super_mega_fun_wizard" + }, + { + "id": "step_2", + "index": 1, + "next": "step_3", + "previous": "step_1", + "description": "

Because I couldn’t think of another name for this step \":slight_smile:\"

", + "title": "Values", + "permitted": true, + "permitted_message": null, + "final": false, + "fields": [ + { + "id": "step_2_field_1", + "index": 0, + "type": "date", + "required": false, + "value": "", + "label": "

Date

", + "file_types": null, + "format": "YYYY-MM-DD", + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 1, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_2", + "index": 0, + "type": "time", + "required": false, + "value": "", + "label": "

Time

", + "file_types": null, + "format": "HH:mm", + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 2, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_3", + "index": 2, + "type": "date_time", + "required": false, + "value": "", + "label": "

Date & Time

", + "file_types": null, + "format": "", + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 3, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_4", + "index": 3, + "type": "number", + "required": false, + "value": "", + "label": "

Number

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 5, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_5", + "index": 4, + "type": "checkbox", + "required": false, + "value": false, + "label": "

Checkbox

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 6, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_6", + "index": 5, + "type": "url", + "required": false, + "value": "", + "label": "

Url

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 7, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_7", + "index": 6, + "type": "upload", + "required": false, + "value": "", + "label": "

Upload

", + "file_types": ".jpg,.jpeg,.png", + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 8, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + } + ], + "_validState": 0, + "wizardId": "super_mega_fun_wizard" + }, + { + "id": "step_3", + "index": 2, + "previous": "step_2", + "description": "

Unfortunately not the edible type \":sushi:\"

", + "title": "Combo-boxes", + "permitted": true, + "permitted_message": null, + "final": true, + "fields": [ + { + "id": "step_3_field_1", + "index": 0, + "type": "dropdown", + "required": false, + "value": "choice1", + "label": "

Custom Dropdown

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": [ + { + "id": "one", + "name": "One" + }, + { + "id": "two", + "name": "Two" + } + ], + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 1, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + }, + { + "id": "step_3_field_2", + "index": 0, + "type": "tag", + "required": false, + "value": null, + "label": "

Tag

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 2, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + }, + { + "id": "step_3_field_3", + "index": 2, + "type": "category", + "required": false, + "value": null, + "label": "

Category

", + "file_types": null, + "format": null, + "limit": 1, + "property": "id", + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 3, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + }, + { + "id": "step_3_field_4", + "index": 3, + "type": "group", + "required": false, + "value": null, + "label": "

Group

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 4, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + }, + { + "id": "step_3_field_5", + "index": 4, + "type": "user_selector", + "required": false, + "value": null, + "label": "

User Selector

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 5, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + }, + { + "id": "step_3_field_6", + "index": 5, + "type": "user_selector", + "required": false, + "value": null, + "label": "

Conditional User Selector

", + "description": "Shown when checkbox in step_2_field_5 is true", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 6, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + } + ], + "_validState": 0, + "wizardId": "super_mega_fun_wizard" + } + ], + "groups": [] +} diff --git a/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 b/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 new file mode 100644 index 00000000..f5c1175f --- /dev/null +++ b/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 @@ -0,0 +1,53 @@ +import { module } from "qunit"; +import setupPretender, { response } from "../pretender"; +import startApp from "../helpers/start-app"; + +let server; +let app; + +function acceptance(name, requests, cb) { + module(`Acceptance: ${name}`, function(hooks) { + hooks.beforeEach(function() { + server = setupPretender(function(pretender) { + requests.forEach(req => { + pretender[req.verb](req.path, () => (response(req.status, req.response))); + }); + return pretender; + }); + app = startApp(); + }); + + hooks.afterEach(function() { + app.destroy(); + server.shutdown(); + }); + + cb(hooks); + }); +} + +export default acceptance; + +export { + server +}; + +// The discourse/test/helpers/qunit-helpers file has many functions and imports +// we don't need, so there will be some duplciation here. + +export function queryAll(selector, context) { + context = context || "#ember-testing"; + return $(selector, context); +} + +export function query() { + return document.querySelector("#ember-testing").querySelector(...arguments); +} + +export function visible(selector) { + return queryAll(selector + ":visible").length > 0; +} + +export function count(selector) { + return queryAll(selector).length; +} diff --git a/assets/javascripts/wizard/tests/helpers/start-app.js.es6 b/assets/javascripts/wizard/tests/helpers/start-app.js.es6 new file mode 100644 index 00000000..6afe6eb9 --- /dev/null +++ b/assets/javascripts/wizard/tests/helpers/start-app.js.es6 @@ -0,0 +1,19 @@ +const CustomWizard = requirejs("discourse/plugins/discourse-custom-wizard/wizard/application").default; +const initializer = requirejs("discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/wizard").default; +const siteSettings = requirejs("discourse/plugins/discourse-custom-wizard/wizard/tests/fixtures/site-settings").default; +const { cloneJSON } = requirejs("discourse-common/lib/object").default; + +let app; + +export default function () { + app = CustomWizard.create({ rootElement: "#ember-testing" }); + app.start(); + + app.SiteSettings = cloneJSON(siteSettings); + initializer.initialize(app); + + app.setupForTesting(); + app.injectTestHelpers(); + + return app; +} diff --git a/assets/javascripts/wizard/tests/helpers/step.js.es6 b/assets/javascripts/wizard/tests/helpers/step.js.es6 new file mode 100644 index 00000000..a24e04e1 --- /dev/null +++ b/assets/javascripts/wizard/tests/helpers/step.js.es6 @@ -0,0 +1,20 @@ +import updateJson from "../fixtures/update"; +import { cloneJSON } from "discourse-common/lib/object"; +import wizardJson from "../fixtures/wizard"; + +const update = cloneJSON(updateJson); +update.wizard = cloneJSON(wizardJson); + +const saveStep = function(response) { + return { + verb: "put", + path: '/w/wizard/steps/:step_id', + status: 200, + response + } +} + +export { + saveStep, + update +} diff --git a/assets/javascripts/wizard/tests/helpers/test.js.es6 b/assets/javascripts/wizard/tests/helpers/test.js.es6 new file mode 100644 index 00000000..c7401fd5 --- /dev/null +++ b/assets/javascripts/wizard/tests/helpers/test.js.es6 @@ -0,0 +1,7 @@ +function exists(selector) { + return document.querySelector(selector) !== null; +} + +export { + exists +} diff --git a/assets/javascripts/wizard/tests/helpers/wizard.js.es6 b/assets/javascripts/wizard/tests/helpers/wizard.js.es6 new file mode 100644 index 00000000..997f6c36 --- /dev/null +++ b/assets/javascripts/wizard/tests/helpers/wizard.js.es6 @@ -0,0 +1,52 @@ +import wizardJson from "../fixtures/wizard"; +import userJson from "../fixtures/user"; +import categoriesJson from "../fixtures/categories"; +import groupsJson from "../fixtures/groups"; +import { cloneJSON } from "discourse-common/lib/object"; + +const wizardNoUser = cloneJSON(wizardJson); +const wizard = cloneJSON(wizardJson); +wizard.user = cloneJSON(userJson); + +const wizardNotPermitted = cloneJSON(wizard); +wizardNotPermitted.permitted = false; + +const wizardCompleted = cloneJSON(wizard); +wizardCompleted.completed = true; + +wizard.start = "step_1"; +wizard.resume_on_revisit = false; +wizard.submission_last_updated_at = "2022-03-11T20:00:18+01:00"; +wizard.subscribed = false; + +const stepNotPermitted = cloneJSON(wizard); +stepNotPermitted.steps[0].permitted = false; + +const allFieldsWizard = cloneJSON(wizard); +allFieldsWizard.steps[0].fields = [ + ...allFieldsWizard.steps[0].fields, + ...allFieldsWizard.steps[1].fields, + ...allFieldsWizard.steps[2].fields +]; +allFieldsWizard.steps = [cloneJSON(allFieldsWizard.steps[0])]; +allFieldsWizard.categories = cloneJSON(categoriesJson['categories']); +allFieldsWizard.groups = cloneJSON(groupsJson['groups']); + +const getWizard = function(response) { + return { + verb: "get", + path: "/w/wizard", + status: 200, + response + } +} + +export { + getWizard, + wizardNoUser, + wizardNotPermitted, + wizardCompleted, + stepNotPermitted, + allFieldsWizard, + wizard +} diff --git a/assets/javascripts/wizard/tests/pretender.js.es6 b/assets/javascripts/wizard/tests/pretender.js.es6 new file mode 100644 index 00000000..88eae666 --- /dev/null +++ b/assets/javascripts/wizard/tests/pretender.js.es6 @@ -0,0 +1,53 @@ +import Pretender from "pretender"; + +function parsePostData(query) { + const result = {}; + query.split("&").forEach(function (part) { + const item = part.split("="); + const firstSeg = decodeURIComponent(item[0]); + const m = /^([^\[]+)\[([^\]]+)\]/.exec(firstSeg); + + const val = decodeURIComponent(item[1]).replace(/\+/g, " "); + if (m) { + result[m[1]] = result[m[1]] || {}; + result[m[1]][m[2]] = val; + } else { + result[firstSeg] = val; + } + }); + return result; +} + +function response(code, obj) { + if (typeof code === "object") { + obj = code; + code = 200; + } + return [code, { "Content-Type": "application/json" }, obj]; +} + +export { response }; + +export default function (cb) { + let server = new Pretender(); + + if (cb) { + server = cb(server); + } + + server.prepareBody = function (body) { + if (body && typeof body === "object") { + return JSON.stringify(body); + } + return body; + }; + + server.unhandledRequest = function (verb, path, request) { + const error = + "Unhandled request in test environment: " + path + " (" + verb + ")"; + window.console.error(error); + throw error; + }; + + return server; +} diff --git a/assets/stylesheets/wizard/custom/composer.scss b/assets/stylesheets/wizard/custom/composer.scss index be866b8a..aea17d63 100644 --- a/assets/stylesheets/wizard/custom/composer.scss +++ b/assets/stylesheets/wizard/custom/composer.scss @@ -46,7 +46,7 @@ position: relative; } -.wizard-field-composer.show-preview .d-editor-textarea-wrapper { +.wizard-field-composer.show-preview .d-editor-textarea-column { display: none; } diff --git a/config/routes.rb b/config/routes.rb index 28fcbb82..1948b799 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true CustomWizard::Engine.routes.draw do + get 'qunit' => 'wizard#qunit' get ':wizard_id' => 'wizard#index' put ':wizard_id/skip' => 'wizard#skip' get ':wizard_id/steps' => 'wizard#index' diff --git a/lib/custom_wizard/extensions/extra_locales_controller.rb b/lib/custom_wizard/extensions/extra_locales_controller.rb index 6242f7ca..f6672f4a 100644 --- a/lib/custom_wizard/extensions/extra_locales_controller.rb +++ b/lib/custom_wizard/extensions/extra_locales_controller.rb @@ -6,6 +6,7 @@ module ExtraLocalesControllerCustomWizard path = URI(request.referer).path wizard_path = path.split('/w/').last wizard_id = wizard_path.split('/').first + return true if wizard_id == "qunit" CustomWizard::Template.exists?(wizard_id.underscore) end end diff --git a/lib/custom_wizard/validators/update.rb b/lib/custom_wizard/validators/update.rb index 2301760f..d71bded1 100644 --- a/lib/custom_wizard/validators/update.rb +++ b/lib/custom_wizard/validators/update.rb @@ -20,7 +20,7 @@ class ::CustomWizard::UpdateValidator field_id = field.id.to_s value = @updater.submission[field_id] min_length = false - label = field.raw[:label] || I18n.t("#{field.key}.label") + label = field.raw[:label] type = field.type required = field.required min_length = field.min_length if is_text_type(field) diff --git a/plugin.rb b/plugin.rb index a1b377ac..6f5719fb 100644 --- a/plugin.rb +++ b/plugin.rb @@ -21,9 +21,8 @@ config.assets.paths << "#{plugin_asset_path}/stylesheets/wizard" if Rails.env.production? config.assets.precompile += %w{ wizard-custom-guest.js - wizard-custom-globals.js - wizard-custom.js wizard-custom-start.js + wizard-custom.js wizard-plugin.js.erb wizard-raw-templates.js.erb } From a2106bf592447d4073af50534b98b2725f4866b4 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 14:09:23 +0100 Subject: [PATCH 03/30] Update workflow to add frontend tests && handle deprecations --- .github/workflows/plugin-tests.yml | 16 +------- .../components/custom-user-selector.js.es6 | 3 +- .../components/wizard-field-category.js.es6 | 5 ++- .../components/wizard-field-composer.js.es6 | 5 ++- .../wizard/components/wizard-step.js.es6 | 6 +-- .../components/wizard-text-field.js.es6 | 3 +- .../lib/initialize/inject-objects.js.es6 | 37 +++++++++++-------- .../lib/initialize/patch-components.js.es6 | 16 ++++++++ .../wizard/lib/initialize/wizard.js.es6 | 1 + .../javascripts/wizard/lib/load-script.js.es6 | 16 ++++---- .../javascripts/wizard/lib/text-lite.js.es6 | 16 +++++--- .../javascripts/wizard/lib/user-search.js.es6 | 3 +- assets/javascripts/wizard/models/step.js.es6 | 3 +- .../javascripts/wizard/models/wizard.js.es6 | 3 +- .../components/wizard-composer-editor.hbs | 2 +- assets/javascripts/wizard/templates/step.hbs | 5 +-- 16 files changed, 82 insertions(+), 58 deletions(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 782ebc4f..baf5e381 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -73,18 +73,6 @@ jobs: ref: "${{ github.base_ref }}" fetch-depth: 1 - - name: Check spec existence - id: check_spec - uses: andstor/file-existence-action@v1 - with: - files: "plugins/${{ steps.repo-name.outputs.value }}/spec" - - - name: Check qunit existence - id: check_qunit - uses: andstor/file-existence-action@v1 - with: - files: "plugins/${{ steps.repo-name.outputs.value }}/test/javascripts" - - name: Setup Git run: | git config --global user.email "ci@ci.invalid" @@ -140,7 +128,7 @@ jobs: bin/rake db:migrate - name: Plugin RSpec with Coverage - if: matrix.build_type == 'backend' && steps.check_spec.outputs.files_exists == 'true' + if: matrix.build_type == 'backend' run: | if [ -e plugins/${{ steps.repo-name.outputs.value }}/.simplecov ] then @@ -150,6 +138,6 @@ jobs: bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}] - name: Plugin QUnit - if: matrix.build_type == 'frontend' && steps.check_qunit.outputs.files_exists == 'true' + if: matrix.build_type == 'frontend' run: bundle exec rake plugin:qunit['${{ steps.repo-name.outputs.value }}','1200000'] timeout-minutes: 30 diff --git a/assets/javascripts/wizard/components/custom-user-selector.js.es6 b/assets/javascripts/wizard/components/custom-user-selector.js.es6 index 1698a008..6538cb42 100644 --- a/assets/javascripts/wizard/components/custom-user-selector.js.es6 +++ b/assets/javascripts/wizard/components/custom-user-selector.js.es6 @@ -7,6 +7,7 @@ import userSearch from "../lib/user-search"; import WizardI18n from "../lib/wizard-i18n"; import Handlebars from "handlebars"; import { isEmpty } from "@ember/utils"; +import TextField from "@ember/component/text-field"; const template = function (params) { const options = params.options; @@ -31,7 +32,7 @@ const template = function (params) { return new Handlebars.SafeString(html).string; }; -export default Ember.TextField.extend({ +export default TextField.extend({ attributeBindings: ["autofocus", "maxLength"], autocorrect: false, autocapitalize: false, diff --git a/assets/javascripts/wizard/components/wizard-field-category.js.es6 b/assets/javascripts/wizard/components/wizard-field-category.js.es6 index dc20538e..441f83d3 100644 --- a/assets/javascripts/wizard/components/wizard-field-category.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-category.js.es6 @@ -1,8 +1,9 @@ import { observes } from "discourse-common/utils/decorators"; import Category from "discourse/models/category"; +import Component from "@ember/component"; -export default Ember.Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-category', +export default Component.extend({ + layoutName: "wizard/templates/components/wizard-field-category", didInsertElement() { const property = this.field.property || "id"; diff --git a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer.js.es6 index 0ef5fe20..255982ea 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-composer.js.es6 @@ -3,9 +3,10 @@ import { observes, } from "discourse-common/utils/decorators"; import EmberObject from "@ember/object"; +import Component from "@ember/component"; -export default Ember.Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-composer', +export default Component.extend({ + layoutName: "wizard/templates/components/wizard-field-composer", showPreview: false, classNameBindings: [ diff --git a/assets/javascripts/wizard/components/wizard-step.js.es6 b/assets/javascripts/wizard/components/wizard-step.js.es6 index 4518afee..aa6a25bb 100644 --- a/assets/javascripts/wizard/components/wizard-step.js.es6 +++ b/assets/javascripts/wizard/components/wizard-step.js.es6 @@ -87,7 +87,7 @@ export default Component.extend({ @observes("step.message") _handleMessage: function () { const message = this.get("step.message"); - this.sendAction("showMessage", message); + this.showMessage(message); }, keyPress(event) { @@ -162,7 +162,7 @@ export default Component.extend({ if (response["final"]) { CustomWizard.finished(response); } else { - this.sendAction("goNext", response); + this.goNext(response); } }) .catch(() => this.animateInvalidFields()) @@ -181,7 +181,7 @@ export default Component.extend({ }, showMessage(message) { - this.sendAction("showMessage", message); + this.sendAction(message); }, stylingDropdownChanged(id, value) { diff --git a/assets/javascripts/wizard/components/wizard-text-field.js.es6 b/assets/javascripts/wizard/components/wizard-text-field.js.es6 index 3d87be09..5991eefc 100644 --- a/assets/javascripts/wizard/components/wizard-text-field.js.es6 +++ b/assets/javascripts/wizard/components/wizard-text-field.js.es6 @@ -1,8 +1,9 @@ import computed from "discourse-common/utils/decorators"; import { isLTR, isRTL, siteDir } from "discourse/lib/text-direction"; import WizardI18n from "../lib/wizard-i18n"; +import TextField from "@ember/component/text-field"; -export default Ember.TextField.extend({ +export default TextField.extend({ attributeBindings: [ "autocorrect", "autocapitalize", diff --git a/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 index d31efb4d..bd231ff9 100644 --- a/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 @@ -1,5 +1,11 @@ export default { - run(app, container) { + run(app) { + // siteSettings must always be registered first + if (!app.hasRegistration("site-settings:main")) { + const siteSettings = app.SiteSettings; + app.register("site-settings:main", siteSettings, { instantiate: false }); + } + const Store = requirejs("discourse/services/store").default; const Site = requirejs( "discourse/plugins/discourse-custom-wizard/wizard/models/site" @@ -7,12 +13,13 @@ export default { const Session = requirejs("discourse/models/session").default; const RestAdapter = requirejs("discourse/adapters/rest").default; const messageBus = requirejs("message-bus-client").default; - const sniffCapabilites = requirejs("discourse/pre-initializers/sniff-capabilities").default; + const sniffCapabilites = requirejs( + "discourse/pre-initializers/sniff-capabilities" + ).default; + const site = Site.current(); const session = Session.current(); - const registrations = [ - ["site-settings:main", app.SiteSettings, false], ["message-bus:main", messageBus, false], ["site:main", site, false], ["session:main", session, false], @@ -26,18 +33,18 @@ export default { } }); - const targets = ["controller", "component", "route", "model", "adapter", "mixin"]; - const injections = [ - ["siteSettings", "site-settings:main"], - ["messageBus", "message-bus:main"], - ["site", "site:main"], - ["session", "session:main"], - ["store", "service:store"], - ["appEvents", "service:app-events"] - ]; + const targets = ["controller", "component", "route", "model", "adapter"]; - injections.forEach(injection => { - targets.forEach((t) => app.inject(t, injection[0], injection[1])); + targets.forEach((t) => { + app.inject(t, "appEvents", "service:app-events"); + app.inject(t, "store", "service:store"); + app.inject(t, "site", "site:main"); + }); + + targets.concat("service").forEach((t) => { + app.inject(t, "session", "session:main"); + app.inject(t, "messageBus", "message-bus:main"); + app.inject(t, "siteSettings", "site-settings:main"); }); if (!app.hasRegistration("capabilities:main")) { diff --git a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 index 529a4cd9..10bbf700 100644 --- a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 @@ -20,6 +20,11 @@ export default { const DEditor = requirejs("discourse/components/d-editor").default; const { clipboardHelpers } = requirejs("discourse/lib/utilities"); const toMarkdown = requirejs("discourse/lib/to-markdown").default; + const discourseComputed = requirejs("discourse-common/utils/decorators") + .default; + const WizardI18n = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n" + ).default; const isInside = (text, regex) => { const matches = text.match(regex); return matches && matches.length % 2; @@ -44,6 +49,17 @@ export default { } }, + @discourseComputed("placeholder", "placeholderOverride") + placeholderTranslated(placeholder, placeholderOverride) { + if (placeholderOverride) { + return placeholderOverride; + } + if (placeholder) { + return WizardI18n(placeholder); + } + return null; + }, + _wizardInsertText(args = {}) { if (args.fieldId === this.fieldId) { this.insertText(args.text, args.options); diff --git a/assets/javascripts/wizard/lib/initialize/wizard.js.es6 b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 index 134257a1..20650ec9 100644 --- a/assets/javascripts/wizard/lib/initialize/wizard.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 @@ -35,6 +35,7 @@ export default { const Session = requirejs("discourse/models/session").default; const session = Session.current(); session.set("highlightJsPath", setupData.highlightJsPath); + session.set("markdownItUrl", setupData.markdownItUrl); [ 'register-files', diff --git a/assets/javascripts/wizard/lib/load-script.js.es6 b/assets/javascripts/wizard/lib/load-script.js.es6 index 43d97d0a..d0b07c26 100644 --- a/assets/javascripts/wizard/lib/load-script.js.es6 +++ b/assets/javascripts/wizard/lib/load-script.js.es6 @@ -1,5 +1,7 @@ import { ajax } from "wizard/lib/ajax"; -import getURL from "discourse-common/lib/get-url"; +import getURL, { getURLWithCDN } from "discourse-common/lib/get-url"; +import { run } from "@ember/runloop"; +import { Promise } from "rsvp"; const _loaded = {}; const _loading = {}; @@ -25,7 +27,7 @@ function loadWithTag(path, cb) { ) { s = s.onload = s.onreadystatechange = null; if (!abort) { - Ember.run(null, cb); + run(null, cb); } } }; @@ -38,7 +40,7 @@ export function loadCSS(url) { export default function loadScript(url, opts) { // TODO: Remove this once plugins have been updated not to use it: if (url === "defer/html-sanitizer-bundle") { - return Ember.RSVP.Promise.resolve(); + return Promise.resolve(); } opts = opts || {}; @@ -51,7 +53,7 @@ export default function loadScript(url, opts) { } }); - return new Ember.RSVP.Promise(function (resolve) { + return new Promise(function (resolve) { url = getURL(url); // If we already loaded this url @@ -63,7 +65,7 @@ export default function loadScript(url, opts) { } let done; - _loading[url] = new Ember.RSVP.Promise(function (_done) { + _loading[url] = new Promise(function (_done) { done = _done; }); @@ -84,8 +86,8 @@ export default function loadScript(url, opts) { // Scripts should always load from CDN // CSS is type text, to accept it from a CDN we would need to handle CORS - if (!opts.css && Discourse.CDN && url[0] === "/" && url[1] !== "/") { - cdnUrl = Discourse.CDN.replace(/\/$/, "") + url; + if (!opts.css) { + cdnUrl = getURLWithCDN(url); } // Some javascript depends on the path of where it is loaded (ace editor) diff --git a/assets/javascripts/wizard/lib/text-lite.js.es6 b/assets/javascripts/wizard/lib/text-lite.js.es6 index cc161426..26cfc27a 100644 --- a/assets/javascripts/wizard/lib/text-lite.js.es6 +++ b/assets/javascripts/wizard/lib/text-lite.js.es6 @@ -3,6 +3,8 @@ import { default as PrettyText, buildOptions } from "pretty-text/pretty-text"; import Handlebars from "handlebars"; import getURL from "discourse-common/lib/get-url"; import { getOwner } from "discourse-common/lib/get-owner"; +import { Promise } from "rsvp"; +import Session from "discourse/models/session"; export function cook(text, options) { if (!options) { @@ -18,11 +20,15 @@ export function cook(text, options) { // everything should eventually move to async API and this should be renamed // cook export function cookAsync(text, options) { - if (Discourse.MarkdownItURL) { - return loadScript(Discourse.MarkdownItURL) - .then(() => cook(text, options)) - .catch((e) => Ember.Logger.error(e)); + let markdownItURL = Session.currentProp("markdownItURL"); + if (markdownItURL) { + return ( + loadScript(markdownItURL) + .then(() => cook(text, options)) + // eslint-disable-next-line no-console + .catch((e) => console.error(e)) + ); } else { - return Ember.RSVP.Promise.resolve(cook(text)); + return Promise.resolve(cook(text)); } } diff --git a/assets/javascripts/wizard/lib/user-search.js.es6 b/assets/javascripts/wizard/lib/user-search.js.es6 index e7171f18..04b6f97c 100644 --- a/assets/javascripts/wizard/lib/user-search.js.es6 +++ b/assets/javascripts/wizard/lib/user-search.js.es6 @@ -1,6 +1,7 @@ import { CANCELLED_STATUS } from "discourse/lib/autocomplete"; import { debounce } from "@ember/runloop"; import getUrl from "discourse-common/lib/get-url"; +import { Promise } from "rsvp"; let cache = {}, cacheTopicId, @@ -120,7 +121,7 @@ export default function userSearch(options) { currentTerm = term; - return new Ember.RSVP.Promise(function (resolve) { + return new Promise(function (resolve) { // TODO site setting for allowed regex in username if (term.match(/[^\w_\-\.@\+]/)) { resolve([]); diff --git a/assets/javascripts/wizard/models/step.js.es6 b/assets/javascripts/wizard/models/step.js.es6 index e18657b5..b0829312 100644 --- a/assets/javascripts/wizard/models/step.js.es6 +++ b/assets/javascripts/wizard/models/step.js.es6 @@ -3,6 +3,7 @@ import ValidState from "wizard/mixins/valid-state"; import { ajax } from "wizard/lib/ajax"; import discourseComputed from "discourse-common/utils/decorators"; import { translatedText } from "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n"; +import { later } from "@ember/runloop"; export default EmberObject.extend(ValidState, { id: null, @@ -109,6 +110,6 @@ export default EmberObject.extend(ValidState, { state: "error", text: message, }); - Ember.run.later(() => this.set("message", null), 6000); + later(() => this.set("message", null), 6000); }, }); diff --git a/assets/javascripts/wizard/models/wizard.js.es6 b/assets/javascripts/wizard/models/wizard.js.es6 index 7fbe2c10..bc2bff14 100644 --- a/assets/javascripts/wizard/models/wizard.js.es6 +++ b/assets/javascripts/wizard/models/wizard.js.es6 @@ -112,8 +112,7 @@ CustomWizard.reopenClass({ } }); - Site.currentProp("categoriesList", categories); - Site.currentProp("sortedCategories", categories); + Site.currentProp("categories", categories); Site.currentProp("listByActivity", categories); Site.currentProp("categoriesById", categoriesById); Site.currentProp( diff --git a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs b/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs index be98db8e..7d453a0b 100644 --- a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs @@ -1,7 +1,7 @@ {{d-editor tabindex=field.tabindex value=composer.reply - placeholderTranslated=replyPlaceholder + placeholderOverride=replyPlaceholder previewUpdated=(action "previewUpdated") markdownOptions=markdownOptions extraButtons=(action "extraButtons") diff --git a/assets/javascripts/wizard/templates/step.hbs b/assets/javascripts/wizard/templates/step.hbs index 6456a59c..5ed14cdf 100644 --- a/assets/javascripts/wizard/templates/step.hbs +++ b/assets/javascripts/wizard/templates/step.hbs @@ -13,8 +13,7 @@ {{#if step.permitted}} {{wizard-step step=step wizard=wizard - goNext="goNext" + goNext=(action "goNext") goBack=(action "goBack") - finished="finished" - showMessage="showMessage"}} + showMessage=(action "showMessage")}} {{/if}} From 39defec8978ecaf96a30d86a3b807ad6a0e7e347 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 14:10:54 +0100 Subject: [PATCH 04/30] Remove duplicated functions --- assets/javascripts/wizard/components/validator.js.es6 | 1 - assets/javascripts/wizard/components/wizard-step.js.es6 | 2 -- 2 files changed, 3 deletions(-) diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index ab442d97..cd1c3abe 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -11,7 +11,6 @@ export default Component.extend({ invalidMessageKey: null, isValid: null, isInvalid: equal("isValid", false), - layoutName: "wizard/templates/components/validator", init() { this._super(...arguments); diff --git a/assets/javascripts/wizard/components/wizard-step.js.es6 b/assets/javascripts/wizard/components/wizard-step.js.es6 index aa6a25bb..44fcde26 100644 --- a/assets/javascripts/wizard/components/wizard-step.js.es6 +++ b/assets/javascripts/wizard/components/wizard-step.js.es6 @@ -169,8 +169,6 @@ export default Component.extend({ .finally(() => this.set("saving", false)); }, - keyPress() {}, - actions: { quit() { this.get("wizard").skip(); From 8fd07322d01af6be718aa59d798c2070b8ca39be Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 11:02:21 +0200 Subject: [PATCH 05/30] Minor config updates --- .github/workflows/plugin-tests.yml | 12 +- .../javascripts/wizard/tests/bootstrap.js.es6 | 7 + yarn.lock | 1639 +++++++++++++++++ 3 files changed, 1650 insertions(+), 8 deletions(-) create mode 100644 yarn.lock diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index baf5e381..f8105755 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -56,20 +56,16 @@ jobs: if_true: "stable" if_false: "tests-passed" - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: repository: discourse/discourse ref: ${{ steps.discourse_branch.outputs.value }} fetch-depth: 1 - - name: Fetch Repo Name - id: repo-name - run: echo "::set-output name=value::$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" - - name: Install plugin uses: actions/checkout@v2 with: - path: plugins/${{ steps.repo-name.outputs.value }} + path: plugins/${{ github.event.repository.name }} ref: "${{ github.base_ref }}" fetch-depth: 1 @@ -139,5 +135,5 @@ jobs: - name: Plugin QUnit if: matrix.build_type == 'frontend' - run: bundle exec rake plugin:qunit['${{ steps.repo-name.outputs.value }}','1200000'] - timeout-minutes: 30 + run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake qunit:test['1200000','/w/qunit'] + timeout-minutes: 10 diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 index d2c503ad..8579e884 100644 --- a/assets/javascripts/wizard/tests/bootstrap.js.es6 +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -1,4 +1,5 @@ // discourse-skip-module +/*global document, Logster, QUnit */ document.addEventListener("DOMContentLoaded", function () { document.body.insertAdjacentHTML( @@ -15,3 +16,9 @@ Object.keys(requirejs.entries).forEach(function (entry) { requirejs(entry); } }); + +if (window.Logster) { + Logster.enabled = false; +} else { + window.Logster = { enabled: false }; +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..b9d7fc0e --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1639 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/generator@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" + integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== + dependencies: + "@babel/types" "^7.17.0" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-function-name@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" + integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== + dependencies: + "@babel/template" "^7.16.7" + "@babel/types" "^7.17.0" + +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/highlight@^7.16.7": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" + integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.16.7", "@babel/parser@^7.17.9", "@babel/parser@^7.7.0": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" + integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== + +"@babel/template@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/traverse@^7.7.0": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" + integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.9" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.9" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.7.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + +"@ember-data/rfc395-data@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@ember-data/rfc395-data/-/rfc395-data-0.0.4.tgz#ecb86efdf5d7733a76ff14ea651a1b0ed1f8a843" + integrity sha512-tGRdvgC9/QMQSuSuJV45xoyhI0Pzjm7A9o/MVVA3HakXIImJbbzx/k/6dO9CUEQXIyS2y0fW6C1XaYOG7rY0FQ== + +"@glimmer/env@0.1.7", "@glimmer/env@^0.1.7": + version "0.1.7" + resolved "https://registry.yarnpkg.com/@glimmer/env/-/env-0.1.7.tgz#fd2d2b55a9029c6b37a6c935e8c8871ae70dfa07" + integrity sha1-/S0rVakCnGs3psk16MiHGucN+gc= + +"@glimmer/global-context@0.65.4": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/global-context/-/global-context-0.65.4.tgz#1da1d59dd4260ce912c40e474cd39c2e82de51b8" + integrity sha512-RSYCPG/uVR5XCDcPREBclncU7R0zkjACbADP+n3FWAH1TfWbXRMDIkvO/ZlwHkjHoCZf6tIM6p5S/MoFzfJEJA== + dependencies: + "@glimmer/env" "^0.1.7" + +"@glimmer/interfaces@0.65.4": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/interfaces/-/interfaces-0.65.4.tgz#d298cc2b12b8ebcf269f39246ca7ab92816f6680" + integrity sha512-R0kby79tGNKZOojVJa/7y0JH9Eq4SV+L1s6GcZy30QUZ1g1AAGS5XwCIXc9Sc09coGcv//q+6NLeSw7nlx1y4A== + dependencies: + "@simple-dom/interface" "^1.4.0" + +"@glimmer/reference@^0.65.0": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/reference/-/reference-0.65.4.tgz#bbc8becd6a1bf01fc189b6489e27446437194711" + integrity sha512-yuRVE4qyqrlCndDMrHKDWUbDmGDCjPzsFtlTmxxnhDMJAdQsnr2cRLITHvQRDm1tXfigVvyKnomeuYhRRbBqYQ== + dependencies: + "@glimmer/env" "^0.1.7" + "@glimmer/global-context" "0.65.4" + "@glimmer/interfaces" "0.65.4" + "@glimmer/util" "0.65.4" + "@glimmer/validator" "0.65.4" + +"@glimmer/syntax@^0.65.0": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/syntax/-/syntax-0.65.4.tgz#49164de5dc9e8b67084ec009bdd865e379d8a971" + integrity sha512-y+/C3e8w96efk3a/Z5If9o4ztKJwrr8RtDpbhV2J8X+DUsn5ic2N3IIdlThbt/Zn6tkP1K3dY6uaFUx3pGTvVQ== + dependencies: + "@glimmer/interfaces" "0.65.4" + "@glimmer/util" "0.65.4" + "@handlebars/parser" "^1.1.0" + simple-html-tokenizer "^0.5.10" + +"@glimmer/util@0.65.4": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/util/-/util-0.65.4.tgz#e464145078f3f40da9013ff2590a6016515455d2" + integrity sha512-aofe+rdBhkREKP2GZta6jy1UcbRRMfWx7M18zxGxspPoeD08NscD04Kx+WiOKXmC1TcrfITr8jvqMfrKrMzYWQ== + dependencies: + "@glimmer/env" "0.1.7" + "@glimmer/interfaces" "0.65.4" + "@simple-dom/interface" "^1.4.0" + +"@glimmer/validator@0.65.4", "@glimmer/validator@^0.65.0": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/validator/-/validator-0.65.4.tgz#12c27a9a63706c60e6499fd687940e9d1affb32c" + integrity sha512-0YUjAyo45DF5JkQxdv5kHn96nMNhvZiEwsAD4Jme0kk5Q9MQcPOUtN76pQAS4f+C6GdF9DeUr2yGXZLFMmb+LA== + dependencies: + "@glimmer/env" "^0.1.7" + "@glimmer/global-context" "0.65.4" + +"@handlebars/parser@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@handlebars/parser/-/parser-1.1.0.tgz#d6dbc7574774b238114582410e8fee0dc3532bdf" + integrity sha512-rR7tJoSwJ2eooOpYGxGGW95sLq6GXUaS1UtWvN7pei6n2/okYvCGld9vsUTvkl2migxbkszsycwtMf/GEc1k1A== + +"@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" + +"@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.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@simple-dom/interface@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@simple-dom/interface/-/interface-1.4.0.tgz#e8feea579232017f89b0138e2726facda6fbb71f" + integrity sha512-l5qumKFWU0S+4ZzMaLXFU8tQZsicHEMEyAxI5kDFGhJsRqDwe0a7/iPA/GdxlGyDKseQQAgIz5kzU7eXTrlSpA== + +acorn-jsx@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +ajv@^6.10.0, ajv@^6.10.2: + 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" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +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@^3.2.0, 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.0.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" + +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" + +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== + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-promise-queue@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/async-promise-queue/-/async-promise-queue-1.0.5.tgz#cb23bce9fce903a133946a700cc85f27f09ea49d" + integrity sha512-xi0aQ1rrjPWYmqbwr18rrSKbSaXIeIwSd1J4KAgVfkq8utNbdZoht7GfvfY6swFUAMJ9obkc4WPJmtGwl+B8dw== + dependencies: + async "^2.4.1" + debug "^2.6.8" + +async@^2.4.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +babel-eslint@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" + eslint-visitor-keys "^1.0.0" + resolve "^1.12.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== + +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +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" + +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" + +buffer@^5.5.0: + 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" + +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== + +chalk@^2.0.0, chalk@^2.1.0: + 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" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.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" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +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== + +colors@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +commander@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +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= + +cross-spawn@^6.0.5: + 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" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +debug@^2.6.8: + 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@^4.0.1, debug@^4.1.0: + 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" + +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== + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + +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@^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" + +ember-rfc176-data@^0.3.11: + version "0.3.17" + resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.17.tgz#d4fc6c33abd6ef7b3440c107a28e04417b49860a" + integrity sha512-EVzTTKqxv9FZbEh6Ktw56YyWRAA0MijKvl7H8C06wVF+8f/cRRz3dXxa4nkwjzyVwx4rzKGuIGq77hxJAQhWWw== + +ember-template-lint-plugin-discourse@latest: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ember-template-lint-plugin-discourse/-/ember-template-lint-plugin-discourse-2.0.0.tgz#9805dff60763fae68b5df82b92fb431eb739c13e" + integrity sha512-2bPz/47OfuYGj4w2RNyDcXCYA/4JtRAXRIsaA6PTm4Uc44exK/GBd4RfT2ywmq0CImvj2kGkqpuUgkAtVf6aZQ== + +ember-template-lint@^2.11.0: + version "2.21.0" + resolved "https://registry.yarnpkg.com/ember-template-lint/-/ember-template-lint-2.21.0.tgz#7e120abf309a8810eeed26c52377943faf15a95b" + integrity sha512-19QbEqJQdMfcRS7PsQsubflRowEtnkbD0tpYR4q/xq4lodmhU7hhOFvlTQgbxD/jwW5Ur+tkOwH4KFy9JwOyXA== + dependencies: + chalk "^4.0.0" + ember-template-recast "^5.0.1" + find-up "^5.0.0" + fuse.js "^6.4.6" + get-stdin "^8.0.0" + globby "^11.0.2" + is-glob "^4.0.1" + micromatch "^4.0.2" + resolve "^1.20.0" + v8-compile-cache "^2.2.0" + yargs "^16.2.0" + +ember-template-recast@^5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/ember-template-recast/-/ember-template-recast-5.0.3.tgz#79df27a70bdce7be17f14db13886afde1e9d02d6" + integrity sha512-qsJYQhf29Dk6QMfviXhUPE+byMOs6iRQxUDHgkj8yqjeppvjHaFG96hZi/NAXJTm/M7o3PpfF5YlmeaKtI9UeQ== + dependencies: + "@glimmer/reference" "^0.65.0" + "@glimmer/syntax" "^0.65.0" + "@glimmer/validator" "^0.65.0" + async-promise-queue "^1.0.5" + colors "^1.4.0" + commander "^6.2.1" + globby "^11.0.3" + ora "^5.4.0" + slash "^3.0.0" + tmp "^0.2.1" + workerpool "^6.1.4" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +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.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= + +eslint-config-discourse@^1.1.8: + version "1.1.9" + resolved "https://registry.yarnpkg.com/eslint-config-discourse/-/eslint-config-discourse-1.1.9.tgz#9a5ee6b3a4b986e5243f517e7945d1709c4e22df" + integrity sha512-a4KG+/9/7ZhYVV0URGK70K7QtxlydYKjie0ZssHEGxl2auOUTDcdRMBfZQBtIxQr9X8TF0+eeUUsScBXNU6xZw== + dependencies: + babel-eslint "^10.1.0" + ember-template-lint "^2.11.0" + ember-template-lint-plugin-discourse latest + eslint "^6.8.0" + eslint-plugin-discourse-ember latest + eslint-plugin-ember "^6.10.0" + eslint-plugin-lodash "^7.1.0" + eslint-plugin-node "^8.0.0" + prettier "2.2.1" + +eslint-plugin-discourse-ember@latest: + version "0.0.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-discourse-ember/-/eslint-plugin-discourse-ember-0.0.3.tgz#69e5876c2ece38ab3d6c4a05f0a20a7dc4c21e37" + integrity sha512-EFwWU4FlOSLBa4RolKZL8QD1eGOnvSkACLt4Big+o1ZUIpW7gGvfnJPtxkbaQ4XmhtZ5HetYt6862vVqhUMv9A== + dependencies: + requireindex "~1.1.0" + +eslint-plugin-ember@^6.10.0: + version "6.10.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-ember/-/eslint-plugin-ember-6.10.1.tgz#ca7a5cc28b91a247c31b1686421a66281467f238" + integrity sha512-RZI0+UoR4xeD6UE3KQCUwbN2nZOIIPaFCCXqBIRXDr0rFuwvknAHqYtDPJVZicvTzNHa4TEZvAKqfbE8t7SztQ== + dependencies: + "@ember-data/rfc395-data" "^0.0.4" + ember-rfc176-data "^0.3.11" + snake-case "^2.1.0" + +eslint-plugin-es@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz#12acae0f4953e76ba444bfd1b2271081ac620998" + integrity sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA== + dependencies: + eslint-utils "^1.4.2" + regexpp "^2.0.1" + +eslint-plugin-lodash@^7.1.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-lodash/-/eslint-plugin-lodash-7.4.0.tgz#14a761547f126c92ff56789662a20a44f8bb6290" + integrity sha512-Tl83UwVXqe1OVeBRKUeWcfg6/pCW1GTRObbdnbEJgYwjxp5Q92MEWQaH9+dmzbRt6kvYU1Mp893E79nJiCSM8A== + dependencies: + lodash "^4.17.21" + +eslint-plugin-node@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-8.0.1.tgz#55ae3560022863d141fa7a11799532340a685964" + integrity sha512-ZjOjbjEi6jd82rIpFSgagv4CHWzG9xsQAVp1ZPlhRnnYxcTgENUVBvhYmkQ7GvT1QFijUSo69RaiOJKhMu6i8w== + dependencies: + eslint-plugin-es "^1.3.1" + eslint-utils "^1.3.1" + ignore "^5.0.2" + minimatch "^3.0.4" + resolve "^1.8.1" + semver "^5.5.0" + +eslint-scope@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1, eslint-utils@^1.4.2, eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.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@^6.8.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.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.0.1: + 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@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +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== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-deep-equal@^3.1.1: + 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.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" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +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@~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= + +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +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@^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" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +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= + +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= + +fuse.js@^6.4.6: + version "6.5.3" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.5.3.tgz#7446c0acbc4ab0ab36fa602e97499bdb69452b93" + integrity sha512-sA5etGE7yD/pOqivZRBvUBd/NaL2sjAu6QuSaFoe1H2BrJSkH/T/UXAJ8CdXdw7DvY3Hs8CXKYkDWX7RiP5KOg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + +glob-parent@^5.0.0, glob-parent@^5.1.2: + 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@^7.1.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + +globby@^11.0.2, globby@^11.0.3: + 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" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +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@^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" + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +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@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.0.2, 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== + +import-fresh@^3.0.0: + 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" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +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" + +inherits@2, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inquirer@^7.0.0: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +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-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-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, 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-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +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-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +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= + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +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" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +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-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= + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +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@^4.17.14, lodash@^4.17.19, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lru-cache@^7.4.0: + version "7.8.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.8.1.tgz#68ee3f4807a57d2ba185b7fd90827d5c21ce82bb" + integrity sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg== + +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@^4.0.2, micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +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== + +minimatch@^3.0.4: + 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.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +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: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +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= + +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== + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +once@^1.3.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.0: + 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.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +ora@^5.4.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +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@^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" + +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" + +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.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +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-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== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prettier@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +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== + +readable-stream@^3.4.0: + 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" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +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= + +requireindex@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162" + integrity sha1-5UBLgVV+91225JxacgBIk/4D4WI= + +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@^1.12.0, resolve@^1.20.0, resolve@^1.8.1: + 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" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +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.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.0: + 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-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +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" + +rxjs@^6.6.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +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== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.1.2: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.5: + version "7.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.6.tgz#5d73886fb9c0c6602e79440b97165c29581cbb2b" + integrity sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w== + dependencies: + lru-cache "^7.4.0" + +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-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= + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-html-tokenizer@^0.5.10: + version "0.5.11" + resolved "https://registry.yarnpkg.com/simple-html-tokenizer/-/simple-html-tokenizer-0.5.11.tgz#4c5186083c164ba22a7b477b7687ac056ad6b1d9" + integrity sha512-C2WEK/Z3HoSFbYq8tI7ni3eOo/NneSPRoPpcM7WdLjFOArFuyXEjAoCdOC3DgMfRyziZQ1hCNR4mrNdWEvD0og== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= + dependencies: + no-case "^2.2.0" + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +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= + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +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== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.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-json-comments@^3.0.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== + +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== + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +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= + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +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" + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +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" + +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= + +v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + +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" + +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== + +workerpool@^6.1.4: + version "6.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.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== From 41cfdfb13576da07ff2dccd90c38c1e7a73ee10c Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 15:16:29 +0200 Subject: [PATCH 06/30] Fix tests --- .github/workflows/plugin-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index f8105755..497486ea 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -66,7 +66,7 @@ jobs: uses: actions/checkout@v2 with: path: plugins/${{ github.event.repository.name }} - ref: "${{ github.base_ref }}" + ref: "${{ github.head_ref }}" fetch-depth: 1 - name: Setup Git @@ -135,5 +135,5 @@ jobs: - name: Plugin QUnit if: matrix.build_type == 'frontend' - run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake qunit:test['1200000','/w/qunit'] + run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 QUNIT_SKIP_CORE=1 bin/rake qunit:test['600000','/w/qunit'] timeout-minutes: 10 From 576d96b2ba55db2a16d50641f0da233ac9ffafe2 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 15 Jun 2022 08:36:40 +0200 Subject: [PATCH 07/30] FIX: we're no longer extending the application controller --- app/controllers/custom_wizard/wizard.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/custom_wizard/wizard.rb b/app/controllers/custom_wizard/wizard.rb index 43db5df8..11df97d7 100644 --- a/app/controllers/custom_wizard/wizard.rb +++ b/app/controllers/custom_wizard/wizard.rb @@ -44,7 +44,7 @@ class CustomWizard::WizardController < ::ActionController::Base return render json: { error: I18n.t('wizard.no_skip') } end - result = success_json + result = { success: 'OK' } if current_user && wizard.can_access? submission = wizard.current_submission From 3abb65294c7f8c2635fbbdc745bb385512b5cce3 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 12:17:11 +0200 Subject: [PATCH 08/30] re-add qunit bootstrap conditional --- .../javascripts/wizard/tests/bootstrap.js.es6 | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 index 8579e884..29cdfe80 100644 --- a/assets/javascripts/wizard/tests/bootstrap.js.es6 +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -1,24 +1,26 @@ // discourse-skip-module /*global document, Logster, QUnit */ -document.addEventListener("DOMContentLoaded", function () { - document.body.insertAdjacentHTML( - "afterbegin", - ` -
- - ` - ); -}); +if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) { + document.addEventListener("DOMContentLoaded", function () { + document.body.insertAdjacentHTML( + "afterbegin", + ` +
+ + ` + ); + }); -Object.keys(requirejs.entries).forEach(function (entry) { - if (/\-test/.test(entry)) { - requirejs(entry); + Object.keys(requirejs.entries).forEach(function (entry) { + if (/\-test/.test(entry)) { + requirejs(entry); + } + }); + + if (window.Logster) { + Logster.enabled = false; + } else { + window.Logster = { enabled: false }; } -}); - -if (window.Logster) { - Logster.enabled = false; -} else { - window.Logster = { enabled: false }; } From f3c5eeb371f25bc664980f21bc23f52f7feed896 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 15 Jun 2022 08:59:09 +0200 Subject: [PATCH 09/30] Fix linting --- .../components/custom-user-selector.js.es6 | 2 +- .../wizard/components/field-validators.js.es6 | 2 +- .../similar-topics-validator.js.es6 | 2 +- .../wizard/components/validator.js.es6 | 2 +- .../components/wizard-composer-editor.js.es6 | 2 +- .../wizard-composer-hyperlink.js.es6 | 2 +- .../components/wizard-date-input.js.es6 | 2 +- .../components/wizard-date-time-input.js.es6 | 2 +- .../components/wizard-field-checkbox.js.es6 | 2 +- .../wizard-field-composer-preview.js.es6 | 2 +- .../components/wizard-field-date-time.js.es6 | 2 +- .../components/wizard-field-date.js.es6 | 2 +- .../components/wizard-field-dropdown.js.es6 | 2 +- .../components/wizard-field-group.js.es6 | 2 +- .../components/wizard-field-number.js.es6 | 2 +- .../wizard/components/wizard-field-tag.js.es6 | 2 +- .../components/wizard-field-text.js.es6 | 2 +- .../components/wizard-field-textarea.js.es6 | 2 +- .../components/wizard-field-time.js.es6 | 2 +- .../components/wizard-field-upload.js.es6 | 2 +- .../wizard/components/wizard-field-url.js.es6 | 2 +- .../wizard-field-user-selector.js.es6 | 2 +- .../wizard/components/wizard-field.js.es6 | 9 +- .../components/wizard-group-selector.js.es6 | 2 +- .../wizard/components/wizard-no-access.js.es6 | 8 +- .../components/wizard-similar-topics.js.es6 | 2 +- .../wizard/components/wizard-step.js.es6 | 4 +- .../components/wizard-time-input.js.es6 | 2 +- .../wizard/controllers/wizard-index.js.es6 | 14 +- .../lib/initialize/create-contexts.js.es6 | 6 +- .../lib/initialize/inject-objects.js.es6 | 12 +- .../lib/initialize/patch-components.js.es6 | 10 +- .../lib/initialize/register-files.js.es6 | 14 +- .../wizard/lib/initialize/wizard.js.es6 | 29 +- assets/javascripts/wizard/models/field.js.es6 | 2 +- assets/javascripts/wizard/models/step.js.es6 | 6 +- assets/javascripts/wizard/routes/step.js.es6 | 2 +- .../wizard/routes/wizard-index.js.es6 | 12 +- .../javascripts/wizard/routes/wizard.js.es6 | 5 +- .../wizard/tests/acceptance/field-test.js.es6 | 252 +++--- .../wizard/tests/acceptance/step-test.js.es6 | 78 +- .../tests/acceptance/wizard-test.js.es6 | 105 +-- .../javascripts/wizard/tests/bootstrap.js.es6 | 2 +- .../wizard/tests/fixtures/categories.js.es6 | 400 +++++---- .../wizard/tests/fixtures/groups.js.es6 | 586 ++++++------ .../tests/fixtures/site-settings.js.es6 | 575 ++++++------ .../wizard/tests/fixtures/tags.js.es6 | 36 +- .../wizard/tests/fixtures/update.js.es6 | 8 +- .../wizard/tests/fixtures/user.js.es6 | 4 +- .../wizard/tests/fixtures/users.js.es6 | 20 +- .../wizard/tests/fixtures/wizard.js.es6 | 842 +++++++++--------- .../wizard/tests/helpers/acceptance.js.es6 | 18 +- .../wizard/tests/helpers/start-app.js.es6 | 12 +- .../wizard/tests/helpers/step.js.es6 | 15 +- .../wizard/tests/helpers/test.js.es6 | 4 +- .../wizard/tests/helpers/wizard.js.es6 | 18 +- .../javascripts/wizard/tests/pretender.js.es6 | 20 +- 57 files changed, 1620 insertions(+), 1558 deletions(-) diff --git a/assets/javascripts/wizard/components/custom-user-selector.js.es6 b/assets/javascripts/wizard/components/custom-user-selector.js.es6 index 6538cb42..56eb8f57 100644 --- a/assets/javascripts/wizard/components/custom-user-selector.js.es6 +++ b/assets/javascripts/wizard/components/custom-user-selector.js.es6 @@ -1,6 +1,6 @@ import { default as computed, - observes + observes, } from "discourse-common/utils/decorators"; import { renderAvatar } from "discourse/helpers/user-avatar"; import userSearch from "../lib/user-search"; diff --git a/assets/javascripts/wizard/components/field-validators.js.es6 b/assets/javascripts/wizard/components/field-validators.js.es6 index 15cfc181..7284241c 100644 --- a/assets/javascripts/wizard/components/field-validators.js.es6 +++ b/assets/javascripts/wizard/components/field-validators.js.es6 @@ -1,7 +1,7 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/field-validators', + layoutName: "wizard/templates/components/field-validators", actions: { perform() { diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 index e5133d4f..4f722123 100644 --- a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 +++ b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 @@ -10,7 +10,7 @@ import { dasherize } from "@ember/string"; export default WizardFieldValidator.extend({ classNames: ["similar-topics-validator"], - layoutName: 'wizard/templates/components/similar-topics-validator', + layoutName: "wizard/templates/components/similar-topics-validator", similarTopics: null, hasInput: notEmpty("field.value"), hasSimilarTopics: notEmpty("similarTopics"), diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index cd1c3abe..aa68660c 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -6,7 +6,7 @@ import { getToken } from "wizard/lib/ajax"; export default Component.extend({ classNames: ["validator"], classNameBindings: ["isValid", "isInvalid"], - layoutName: 'wizard/templates/components/validator', + layoutName: "wizard/templates/components/validator", validMessageKey: null, invalidMessageKey: null, isValid: null, diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 index e4ce3ec0..2c04cd4f 100644 --- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 @@ -13,7 +13,7 @@ import { uploadIcon } from "discourse/lib/uploads"; import { dasherize } from "@ember/string"; export default ComposerEditor.extend({ - layoutName: 'wizard/templates/components/wizard-composer-editor', + layoutName: "wizard/templates/components/wizard-composer-editor", classNameBindings: ["fieldClass"], allowUpload: true, showLink: false, diff --git a/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 b/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 index c700d3ce..0eeeb176 100644 --- a/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 @@ -2,7 +2,7 @@ import Component from "@ember/component"; export default Component.extend({ classNames: ["wizard-composer-hyperlink"], - layoutName: 'wizard/templates/components/wizard-composer-hyperlink', + layoutName: "wizard/templates/components/wizard-composer-hyperlink", actions: { addLink() { diff --git a/assets/javascripts/wizard/components/wizard-date-input.js.es6 b/assets/javascripts/wizard/components/wizard-date-input.js.es6 index 375b7195..da2711c7 100644 --- a/assets/javascripts/wizard/components/wizard-date-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-date-input.js.es6 @@ -3,7 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators"; export default DateInput.extend({ useNativePicker: false, - layoutName: 'wizard/templates/components/wizard-date-input', + layoutName: "wizard/templates/components/wizard-date-input", @discourseComputed() placeholder() { diff --git a/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 b/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 index 0fe1e68a..84a2b03e 100644 --- a/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 @@ -2,7 +2,7 @@ import DateTimeInput from "discourse/components/date-time-input"; import discourseComputed from "discourse-common/utils/decorators"; export default DateTimeInput.extend({ - layoutName: 'wizard/templates/components/wizard-date-time-input', + layoutName: "wizard/templates/components/wizard-date-time-input", @discourseComputed("timeFirst", "tabindex") timeTabindex(timeFirst, tabindex) { diff --git a/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 b/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 index f9653cd2..6f9daba2 100644 --- a/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-checkbox' + layoutName: "wizard/templates/components/wizard-field-checkbox", }); diff --git a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 index 0aee0d13..a2056a86 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 @@ -7,7 +7,7 @@ import { ajax } from "discourse/lib/ajax"; import { on } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-composer-preview', + layoutName: "wizard/templates/components/wizard-field-composer-preview", @on("init") updatePreview() { diff --git a/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 b/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 index a916f18e..eee98892 100644 --- a/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 @@ -2,7 +2,7 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-date-time', + layoutName: "wizard/templates/components/wizard-field-date-time", @observes("dateTime") setValue() { diff --git a/assets/javascripts/wizard/components/wizard-field-date.js.es6 b/assets/javascripts/wizard/components/wizard-field-date.js.es6 index a06d582a..df35638c 100644 --- a/assets/javascripts/wizard/components/wizard-field-date.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-date.js.es6 @@ -2,7 +2,7 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-date', + layoutName: "wizard/templates/components/wizard-field-date", @observes("date") setValue() { diff --git a/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 b/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 index 4b8b7e63..e6b08102 100644 --- a/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 @@ -1,7 +1,7 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-dropdown', + layoutName: "wizard/templates/components/wizard-field-dropdown", keyPress(e) { e.stopPropagation(); diff --git a/assets/javascripts/wizard/components/wizard-field-group.js.es6 b/assets/javascripts/wizard/components/wizard-field-group.js.es6 index 93538071..65a19719 100644 --- a/assets/javascripts/wizard/components/wizard-field-group.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-group.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-group' + layoutName: "wizard/templates/components/wizard-field-group", }); diff --git a/assets/javascripts/wizard/components/wizard-field-number.js.es6 b/assets/javascripts/wizard/components/wizard-field-number.js.es6 index e7c4d77f..14e1bfcd 100644 --- a/assets/javascripts/wizard/components/wizard-field-number.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-number.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-number' + layoutName: "wizard/templates/components/wizard-field-number", }); diff --git a/assets/javascripts/wizard/components/wizard-field-tag.js.es6 b/assets/javascripts/wizard/components/wizard-field-tag.js.es6 index 45343522..473bba08 100644 --- a/assets/javascripts/wizard/components/wizard-field-tag.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-tag.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-tag' + layoutName: "wizard/templates/components/wizard-field-tag", }); diff --git a/assets/javascripts/wizard/components/wizard-field-text.js.es6 b/assets/javascripts/wizard/components/wizard-field-text.js.es6 index f9d5b056..d9e7cca8 100644 --- a/assets/javascripts/wizard/components/wizard-field-text.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-text.js.es6 @@ -1,7 +1,7 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-text', + layoutName: "wizard/templates/components/wizard-field-text", keyPress(e) { e.stopPropagation(); diff --git a/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 b/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 index 54865d3c..e59a1707 100644 --- a/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 @@ -1,7 +1,7 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-textarea', + layoutName: "wizard/templates/components/wizard-field-textarea", keyPress(e) { e.stopPropagation(); diff --git a/assets/javascripts/wizard/components/wizard-field-time.js.es6 b/assets/javascripts/wizard/components/wizard-field-time.js.es6 index bf954ec4..a2f2a10d 100644 --- a/assets/javascripts/wizard/components/wizard-field-time.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-time.js.es6 @@ -2,7 +2,7 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-time', + layoutName: "wizard/templates/components/wizard-field-time", @observes("time") setValue() { diff --git a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 b/assets/javascripts/wizard/components/wizard-field-upload.js.es6 index edadb4f0..4774e942 100644 --- a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-upload.js.es6 @@ -3,7 +3,7 @@ import Component from "@ember/component"; import { computed } from "@ember/object"; export default Component.extend(UppyUploadMixin, { - layoutName: 'wizard/templates/components/wizard-field-upload', + layoutName: "wizard/templates/components/wizard-field-upload", classNames: ["wizard-field-upload"], classNameBindings: ["isImage"], uploading: false, diff --git a/assets/javascripts/wizard/components/wizard-field-url.js.es6 b/assets/javascripts/wizard/components/wizard-field-url.js.es6 index 411b5fe9..96c10cc2 100644 --- a/assets/javascripts/wizard/components/wizard-field-url.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-url.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-url' + layoutName: "wizard/templates/components/wizard-field-url", }); diff --git a/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 b/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 index c2a32f44..7cf5b446 100644 --- a/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-user-selector' + layoutName: "wizard/templates/components/wizard-field-user-selector", }); diff --git a/assets/javascripts/wizard/components/wizard-field.js.es6 b/assets/javascripts/wizard/components/wizard-field.js.es6 index 372ee182..493d7676 100644 --- a/assets/javascripts/wizard/components/wizard-field.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field.js.es6 @@ -4,8 +4,13 @@ import discourseComputed from "discourse-common/utils/decorators"; import { cook } from "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field', - classNameBindings: [":wizard-field", "typeClasses", "field.invalid", "field.id"], + layoutName: "wizard/templates/components/wizard-field", + classNameBindings: [ + ":wizard-field", + "typeClasses", + "field.invalid", + "field.id", + ], @discourseComputed("field.type", "field.id") typeClasses: (type, id) => diff --git a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 b/assets/javascripts/wizard/components/wizard-group-selector.js.es6 index b7523f9a..4ff56ec9 100644 --- a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 +++ b/assets/javascripts/wizard/components/wizard-group-selector.js.es6 @@ -3,7 +3,7 @@ import { computed } from "@ember/object"; import { makeArray } from "discourse-common/lib/helpers"; export default ComboBox.extend({ - layoutName: 'wizard/templates/components/wizard-group-selector', + layoutName: "wizard/templates/components/wizard-group-selector", content: computed("groups.[]", "field.content.[]", function () { const whitelist = makeArray(this.field.content); return this.groups diff --git a/assets/javascripts/wizard/components/wizard-no-access.js.es6 b/assets/javascripts/wizard/components/wizard-no-access.js.es6 index 78a23aa8..492a41dc 100644 --- a/assets/javascripts/wizard/components/wizard-no-access.js.es6 +++ b/assets/javascripts/wizard/components/wizard-no-access.js.es6 @@ -4,17 +4,17 @@ import Component from "@ember/component"; import { dasherize } from "@ember/string"; export default Component.extend({ - classNameBindings: [':wizard-no-access', 'reasonClass'], - layoutName: 'wizard/templates/components/wizard-no-access', + classNameBindings: [":wizard-no-access", "reasonClass"], + layoutName: "wizard/templates/components/wizard-no-access", - @discourseComputed('reason') + @discourseComputed("reason") reasonClass(reason) { return dasherize(reason); }, @discourseComputed siteName() { - return (this.siteSettings.title || ''); + return this.siteSettings.title || ""; }, actions: { diff --git a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 b/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 index c52bfeae..6a56873e 100644 --- a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 +++ b/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 @@ -4,7 +4,7 @@ import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ classNames: ["wizard-similar-topics"], - layoutName: 'wizard/templates/components/wizard-similar-topics', + layoutName: "wizard/templates/components/wizard-similar-topics", showTopics: true, didInsertElement() { diff --git a/assets/javascripts/wizard/components/wizard-step.js.es6 b/assets/javascripts/wizard/components/wizard-step.js.es6 index 44fcde26..cc23c5bf 100644 --- a/assets/javascripts/wizard/components/wizard-step.js.es6 +++ b/assets/javascripts/wizard/components/wizard-step.js.es6 @@ -12,7 +12,7 @@ import CustomWizard from "../models/wizard"; const alreadyWarned = {}; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-step', + layoutName: "wizard/templates/components/wizard-step", classNameBindings: [":wizard-step", "step.id"], saving: null, @@ -27,7 +27,7 @@ export default Component.extend({ }, @discourseComputed("step.index", "wizard.required") - showQuitButton: (index, required) => (index === 0 && !required), + showQuitButton: (index, required) => index === 0 && !required, showNextButton: not("step.final"), showDoneButton: alias("step.final"), diff --git a/assets/javascripts/wizard/components/wizard-time-input.js.es6 b/assets/javascripts/wizard/components/wizard-time-input.js.es6 index ec121002..14b08288 100644 --- a/assets/javascripts/wizard/components/wizard-time-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-time-input.js.es6 @@ -1,5 +1,5 @@ import TimeInput from "discourse/components/time-input"; export default TimeInput.extend({ - layoutName: 'wizard/templates/components/wizard-time-input' + layoutName: "wizard/templates/components/wizard-time-input", }); diff --git a/assets/javascripts/wizard/controllers/wizard-index.js.es6 b/assets/javascripts/wizard/controllers/wizard-index.js.es6 index 2dfdee40..f56db02d 100644 --- a/assets/javascripts/wizard/controllers/wizard-index.js.es6 +++ b/assets/javascripts/wizard/controllers/wizard-index.js.es6 @@ -6,19 +6,19 @@ const reasons = { noWizard: "none", requiresLogin: "requires_login", notPermitted: "not_permitted", - completed: "completed" -} + completed: "completed", +}; export default Controller.extend({ - noAccess: or('noWizard', 'requiresLogin', 'notPermitted', 'completed'), + noAccess: or("noWizard", "requiresLogin", "notPermitted", "completed"), - @discourseComputed('noAccessReason') + @discourseComputed("noAccessReason") noAccessI18nKey(reason) { - return reason ? `wizard.${reasons[reason]}` : 'wizard.none'; + return reason ? `wizard.${reasons[reason]}` : "wizard.none"; }, @discourseComputed noAccessReason() { - return Object.keys(reasons).find(reason => this.get(reason)); - } + return Object.keys(reasons).find((reason) => this.get(reason)); + }, }); diff --git a/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 b/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 index 022ac48e..0e637f6c 100644 --- a/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 @@ -3,10 +3,10 @@ export default { const { createHelperContext } = requirejs("discourse-common/lib/helpers"); createHelperContext({ - siteSettings: container.lookup('site-settings:main'), + siteSettings: container.lookup("site-settings:main"), site: container.lookup("site:main"), session: container.lookup("session:main"), capabilities: container.lookup("capabilities:main"), }); - } -} + }, +}; diff --git a/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 index bd231ff9..51dff7e1 100644 --- a/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 @@ -24,12 +24,14 @@ export default { ["site:main", site, false], ["session:main", session, false], ["service:store", Store, true], - ["adapter:rest", RestAdapter, true] + ["adapter:rest", RestAdapter, true], ]; - registrations.forEach(registration => { + registrations.forEach((registration) => { if (!app.hasRegistration(registration[0])) { - app.register(registration[0], registration[1], { instantiate: registration[2] }); + app.register(registration[0], registration[1], { + instantiate: registration[2], + }); } }); @@ -52,5 +54,5 @@ export default { } site.set("can_create_tag", false); - } -} + }, +}; diff --git a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 index 10bbf700..90ded90c 100644 --- a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 @@ -1,11 +1,13 @@ export default { - run(app, container) { + run(_, container) { const getToken = requirejs("wizard/lib/ajax").getToken; - const isTesting = requirejs("discourse-common/config/environment").isTesting; + const isTesting = requirejs("discourse-common/config/environment") + .isTesting; - if (!isTesting) { + if (!isTesting()) { // Add a CSRF token to all AJAX requests let token = getToken(); + const session = container.lookup("session:main"); session.set("csrfToken", token); let callbacks = $.Callbacks(); $.ajaxPrefilter(callbacks.fire); @@ -156,5 +158,5 @@ export default { return resArray; }; } - } + }, }; diff --git a/assets/javascripts/wizard/lib/initialize/register-files.js.es6 b/assets/javascripts/wizard/lib/initialize/register-files.js.es6 index 8d4b850e..85079270 100644 --- a/assets/javascripts/wizard/lib/initialize/register-files.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/register-files.js.es6 @@ -1,10 +1,14 @@ export default { run(app, container) { - const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars").default; + const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars") + .default; const Handlebars = requirejs("handlebars").default; - const registerRawHelpers = requirejs("discourse-common/lib/raw-handlebars-helpers").registerRawHelpers; + const registerRawHelpers = requirejs( + "discourse-common/lib/raw-handlebars-helpers" + ).registerRawHelpers; const { registerHelpers } = requirejs("discourse-common/lib/helpers"); - const jqueryPlugins = requirejs("discourse/initializers/jquery-plugins").default; + const jqueryPlugins = requirejs("discourse/initializers/jquery-plugins") + .default; Object.keys(Ember.TEMPLATES).forEach((k) => { if (k.indexOf("select-kit") === 0) { @@ -22,5 +26,5 @@ export default { registerRawHelpers(RawHandlebars, Handlebars); registerHelpers(app); jqueryPlugins.initialize(container, app); - } -} + }, +}; diff --git a/assets/javascripts/wizard/lib/initialize/wizard.js.es6 b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 index 20650ec9..915e5f26 100644 --- a/assets/javascripts/wizard/lib/initialize/wizard.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 @@ -1,7 +1,8 @@ export default { name: "custom-wizard", initialize(app) { - const isTesting = requirejs("discourse-common/config/environment").isTesting; + const isTesting = requirejs("discourse-common/config/environment") + .isTesting; const isWizard = window.location.pathname.indexOf("/w/") > -1; if (!isWizard && !isTesting()) { @@ -9,14 +10,17 @@ export default { } const container = app.__container__; - const setDefaultOwner = requirejs("discourse-common/lib/get-owner").setDefaultOwner; + const setDefaultOwner = requirejs("discourse-common/lib/get-owner") + .setDefaultOwner; setDefaultOwner(container); if (!isTesting()) { const PreloadStore = requirejs("discourse/lib/preload-store").default; let preloaded; - const preloadedDataElement = document.getElementById("data-preloaded-wizard"); + const preloadedDataElement = document.getElementById( + "data-preloaded-wizard" + ); if (preloadedDataElement) { preloaded = JSON.parse(preloadedDataElement.dataset.preloadedWizard); } @@ -28,7 +32,8 @@ export default { app.SiteSettings = PreloadStore.get("siteSettings"); } - const setEnvironment = requirejs("discourse-common/config/environment").setEnvironment; + const setEnvironment = requirejs("discourse-common/config/environment") + .setEnvironment; const setupData = document.getElementById("data-discourse-setup").dataset; setEnvironment(setupData.environment); @@ -38,13 +43,15 @@ export default { session.set("markdownItUrl", setupData.markdownItUrl); [ - 'register-files', - 'inject-objects', - 'create-contexts', - 'patch-components' - ].forEach(fileName => { - const initializer = requirejs(`discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/${fileName}`).default; + "register-files", + "inject-objects", + "create-contexts", + "patch-components", + ].forEach((fileName) => { + const initializer = requirejs( + `discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/${fileName}` + ).default; initializer.run(app, container); }); - } + }, }; diff --git a/assets/javascripts/wizard/models/field.js.es6 b/assets/javascripts/wizard/models/field.js.es6 index 5f76074e..2b88140e 100644 --- a/assets/javascripts/wizard/models/field.js.es6 +++ b/assets/javascripts/wizard/models/field.js.es6 @@ -75,5 +75,5 @@ export default EmberObject.extend(ValidState, { this.setValid(valid); return valid; - } + }, }); diff --git a/assets/javascripts/wizard/models/step.js.es6 b/assets/javascripts/wizard/models/step.js.es6 index b0829312..36503276 100644 --- a/assets/javascripts/wizard/models/step.js.es6 +++ b/assets/javascripts/wizard/models/step.js.es6 @@ -72,11 +72,7 @@ export default EmberObject.extend(ValidState, { type: "PUT", data: { fields }, }).catch((response) => { - if ( - response && - response.responseJSON && - response.responseJSON.errors - ) { + if (response && response.responseJSON && response.responseJSON.errors) { let wizardErrors = []; response.responseJSON.errors.forEach((err) => { if (err.field === wizardId) { diff --git a/assets/javascripts/wizard/routes/step.js.es6 b/assets/javascripts/wizard/routes/step.js.es6 index 2454fc95..a076951f 100644 --- a/assets/javascripts/wizard/routes/step.js.es6 +++ b/assets/javascripts/wizard/routes/step.js.es6 @@ -26,7 +26,7 @@ export default Route.extend({ }, renderTemplate() { - this.render('wizard/templates/step'); + this.render("wizard/templates/step"); }, setupController(controller, model) { diff --git a/assets/javascripts/wizard/routes/wizard-index.js.es6 b/assets/javascripts/wizard/routes/wizard-index.js.es6 index 16b1140a..264cb0a2 100644 --- a/assets/javascripts/wizard/routes/wizard-index.js.es6 +++ b/assets/javascripts/wizard/routes/wizard-index.js.es6 @@ -4,7 +4,13 @@ import Route from "@ember/routing/route"; export default Route.extend({ beforeModel() { const wizard = getCachedWizard(); - if (wizard && wizard.user && wizard.permitted && !wizard.completed && wizard.start) { + if ( + wizard && + wizard.user && + wizard.permitted && + !wizard.completed && + wizard.start + ) { this.replaceWith("step", wizard.start); } }, @@ -14,7 +20,7 @@ export default Route.extend({ }, renderTemplate() { - this.render('wizard/templates/wizard-index'); + this.render("wizard/templates/wizard-index"); }, setupController(controller, model) { @@ -39,5 +45,5 @@ export default Route.extend({ } else { controller.set("noWizard", true); } - } + }, }); diff --git a/assets/javascripts/wizard/routes/wizard.js.es6 b/assets/javascripts/wizard/routes/wizard.js.es6 index 2910ee6d..a2c34f13 100644 --- a/assets/javascripts/wizard/routes/wizard.js.es6 +++ b/assets/javascripts/wizard/routes/wizard.js.es6 @@ -1,5 +1,4 @@ import { findCustomWizard, updateCachedWizard } from "../models/wizard"; -import { ajax } from "wizard/lib/ajax"; import WizardI18n from "../lib/wizard-i18n"; import Route from "@ember/routing/route"; import { scheduleOnce } from "@ember/runloop"; @@ -20,7 +19,7 @@ export default Route.extend({ const title = WizardI18n("wizard.incomplete_submission.title", { date: moment(wizardModel.submission_last_updated_at).format( "MMMM Do YYYY" - ) + ), }); const buttons = [ @@ -49,7 +48,7 @@ export default Route.extend({ }, renderTemplate() { - this.render('wizard/templates/wizard'); + this.render("wizard/templates/wizard"); }, setupController(controller, model) { diff --git a/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 index f73d1ab7..9300fc09 100644 --- a/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 +++ b/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 @@ -1,135 +1,173 @@ -import { - visit, - click, - fillIn, - triggerKeyEvent -} from "@ember/test-helpers"; +import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers"; import { test } from "qunit"; import { exists } from "../helpers/test"; import acceptance, { - query, count, + query, + server, visible, - server } from "../helpers/acceptance"; -import { - allFieldsWizard, - getWizard -} from "../helpers/wizard"; +import { allFieldsWizard, getWizard } from "../helpers/wizard"; import tagsJson from "../fixtures/tags"; import usersJson from "../fixtures/users"; import { response } from "../pretender"; -acceptance("Field | Fields", [ getWizard(allFieldsWizard) ], - function(hooks) { - test("Text", async function (assert) { - await visit("/wizard"); - assert.ok(exists(".wizard-field.text-field input.wizard-focusable")); - }); +acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () { + test("Text", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-field.text-field input.wizard-focusable")); + }); - test("Textarea", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.textarea-field textarea.wizard-focusable")); - }); + test("Textarea", async function (assert) { + await visit("/wizard"); + assert.ok( + visible(".wizard-field.textarea-field textarea.wizard-focusable") + ); + }); - test("Composer", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.composer-field .wizard-field-composer textarea")); - assert.strictEqual(count(".wizard-field.composer-field .d-editor-button-bar button"), 8); - assert.ok(visible(".wizard-btn.toggle-preview")); + test("Composer", async function (assert) { + await visit("/wizard"); + assert.ok( + visible(".wizard-field.composer-field .wizard-field-composer textarea") + ); + assert.strictEqual( + count(".wizard-field.composer-field .d-editor-button-bar button"), + 8 + ); + assert.ok(visible(".wizard-btn.toggle-preview")); - await fillIn(".wizard-field.composer-field .wizard-field-composer textarea", "Input in composer"); - await click(".wizard-btn.toggle-preview"); - assert.strictEqual(query('.wizard-field.composer-field .wizard-field-composer .d-editor-preview-wrapper p').textContent.trim(), "Input in composer"); - }); + await fillIn( + ".wizard-field.composer-field .wizard-field-composer textarea", + "Input in composer" + ); + await click(".wizard-btn.toggle-preview"); + assert.strictEqual( + query( + ".wizard-field.composer-field .wizard-field-composer .d-editor-preview-wrapper p" + ).textContent.trim(), + "Input in composer" + ); + }); - test("Text Only", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.text-only-field label.field-label")); - }); + test("Text Only", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.text-only-field label.field-label")); + }); - test("Date", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.date-field input.date-picker")); - await click(".wizard-field.date-field input.date-picker"); - assert.ok(visible(".wizard-field.date-field .pika-single")); - }); + test("Date", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.date-field input.date-picker")); + await click(".wizard-field.date-field input.date-picker"); + assert.ok(visible(".wizard-field.date-field .pika-single")); + }); - test("Time", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.time-field .d-time-input .select-kit")); - await click(".wizard-field.time-field .d-time-input .select-kit .select-kit-header"); - assert.ok(visible(".wizard-field.time-field .select-kit-collection")); - }); + test("Time", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.time-field .d-time-input .select-kit")); + await click( + ".wizard-field.time-field .d-time-input .select-kit .select-kit-header" + ); + assert.ok(visible(".wizard-field.time-field .select-kit-collection")); + }); - test("Date Time", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.date-time-field .d-date-time-input .select-kit")); - await click(".wizard-field.date-time-field .d-date-input input.date-picker"); - assert.ok(visible(".wizard-field.date-time-field .d-date-input .pika-single")); - await click(".wizard-field.date-time-field .d-time-input .select-kit .select-kit-header"); - assert.ok(visible(".wizard-field.date-time-field .select-kit-collection")); - }); + test("Date Time", async function (assert) { + await visit("/wizard"); + assert.ok( + visible(".wizard-field.date-time-field .d-date-time-input .select-kit") + ); + await click( + ".wizard-field.date-time-field .d-date-input input.date-picker" + ); + assert.ok( + visible(".wizard-field.date-time-field .d-date-input .pika-single") + ); + await click( + ".wizard-field.date-time-field .d-time-input .select-kit .select-kit-header" + ); + assert.ok(visible(".wizard-field.date-time-field .select-kit-collection")); + }); - test("Number", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.number-field input[type='number']")); - }); + test("Number", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.number-field input[type='number']")); + }); - test("Checkbox", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.checkbox-field input[type='checkbox']")); - }); + test("Checkbox", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.checkbox-field input[type='checkbox']")); + }); - test("Url", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.url-field input[type='text']")); - }); + test("Url", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.url-field input[type='text']")); + }); - test("Upload", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.upload-field label.wizard-btn-upload-file")); - assert.ok(exists(".wizard-field.upload-field input.hidden-upload-field")); - }); + test("Upload", async function (assert) { + await visit("/wizard"); + assert.ok( + visible(".wizard-field.upload-field label.wizard-btn-upload-file") + ); + assert.ok(exists(".wizard-field.upload-field input.hidden-upload-field")); + }); - test("Dropdown", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.dropdown-field .single-select-header")); - await click(".wizard-field.dropdown-field .select-kit-header"); - assert.strictEqual(count(".wizard-field.dropdown-field .select-kit-collection li"), 3); - }); + test("Dropdown", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.dropdown-field .single-select-header")); + await click(".wizard-field.dropdown-field .select-kit-header"); + assert.strictEqual( + count(".wizard-field.dropdown-field .select-kit-collection li"), + 3 + ); + }); - test("Tag", async function (assert) { - server.get("/tags/filter/search", () => (response(200, { results: tagsJson['tags']}))); - await visit("/wizard"); - assert.ok(visible(".wizard-field.tag-field .multi-select-header")); - await click(".wizard-field.tag-field .select-kit-header"); - assert.strictEqual(count(".wizard-field.tag-field .select-kit-collection li"), 2); - }); + test("Tag", async function (assert) { + server.get("/tags/filter/search", () => + response(200, { results: tagsJson["tags"] }) + ); + await visit("/wizard"); + assert.ok(visible(".wizard-field.tag-field .multi-select-header")); + await click(".wizard-field.tag-field .select-kit-header"); + assert.strictEqual( + count(".wizard-field.tag-field .select-kit-collection li"), + 2 + ); + }); - test("Category", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.category-field .multi-select-header")); - await click(".wizard-field.category-field .select-kit-header"); - assert.strictEqual(count(".wizard-field.category-field .select-kit-collection li"), 5); - }); + test("Category", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.category-field .multi-select-header")); + await click(".wizard-field.category-field .select-kit-header"); + assert.strictEqual( + count(".wizard-field.category-field .select-kit-collection li"), + 5 + ); + }); - test("Group", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.group-field .single-select-header")); - await click(".wizard-field.group-field .select-kit-header"); - assert.strictEqual(count(".wizard-field.group-field .select-kit-collection li"), 10); - }); + test("Group", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.group-field .single-select-header")); + await click(".wizard-field.group-field .select-kit-header"); + assert.strictEqual( + count(".wizard-field.group-field .select-kit-collection li"), + 10 + ); + }); - test("User", async function (assert) { - server.get("/u/search/users", () => (response(200, usersJson))); + test("User", async function (assert) { + server.get("/u/search/users", () => response(200, usersJson)); - await visit("/wizard"); - await fillIn(".wizard-field.user-selector-field input.ember-text-field", "a"); - await triggerKeyEvent(".wizard-field.user-selector-field input.ember-text-field", "keyup", "a".charCodeAt(0)); + await visit("/wizard"); + await fillIn( + ".wizard-field.user-selector-field input.ember-text-field", + "a" + ); + await triggerKeyEvent( + ".wizard-field.user-selector-field input.ember-text-field", + "keyup", + "a".charCodeAt(0) + ); - assert.ok(visible(".wizard-field.user-selector-field .ac-wrap")); - // TODO: add assertion for ac results. autocomplete does not appear in time. - }); - } -); + assert.ok(visible(".wizard-field.user-selector-field .ac-wrap")); + // TODO: add assertion for ac results. autocomplete does not appear in time. + }); +}); diff --git a/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 index 731c9b76..1537f1c7 100644 --- a/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 +++ b/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 @@ -1,47 +1,41 @@ -import { visit, click } from "@ember/test-helpers"; +import { click, visit } from "@ember/test-helpers"; import { test } from "qunit"; import { exists } from "../helpers/test"; -import acceptance, { - query, - count, - visible -} from "../helpers/acceptance"; -import { - stepNotPermitted, - wizard, - getWizard -} from "../helpers/wizard"; -import { - saveStep, - update -} from "../helpers/step"; +import acceptance, { count, query, visible } from "../helpers/acceptance"; +import { getWizard, stepNotPermitted, wizard } from "../helpers/wizard"; +import { saveStep, update } from "../helpers/step"; -acceptance("Step | Not permitted", [ getWizard(stepNotPermitted) ], - function(hooks) { - test("Shows not permitted message", async function (assert) { - await visit("/wizard"); - assert.ok(exists(".step-message.not-permitted")); - }); - } -); +acceptance("Step | Not permitted", [getWizard(stepNotPermitted)], function () { + test("Shows not permitted message", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".step-message.not-permitted")); + }); +}); -acceptance("Step | Step", [ getWizard(wizard), saveStep(update) ], - function(hooks) { - test("Renders the step", async function (assert) { - await visit("/wizard"); - assert.strictEqual(query('.wizard-step-title p').textContent.trim(), "Text"); - assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); - assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); - assert.strictEqual(count('.wizard-step-form .wizard-field'), 6); - assert.ok(visible('.wizard-step-footer .wizard-progress'), true); - assert.ok(visible('.wizard-step-footer .wizard-buttons'), true); - }); +acceptance("Step | Step", [getWizard(wizard), saveStep(update)], function () { + test("Renders the step", async function (assert) { + await visit("/wizard"); + assert.strictEqual( + query(".wizard-step-title p").textContent.trim(), + "Text" + ); + assert.strictEqual( + query(".wizard-step-description p").textContent.trim(), + "Text inputs!" + ); + assert.strictEqual( + query(".wizard-step-description p").textContent.trim(), + "Text inputs!" + ); + assert.strictEqual(count(".wizard-step-form .wizard-field"), 6); + assert.ok(visible(".wizard-step-footer .wizard-progress"), true); + assert.ok(visible(".wizard-step-footer .wizard-buttons"), true); + }); - test("Goes to the next step", async function (assert) { - await visit("/wizard"); - assert.ok(visible('.wizard-step.step_1'), true); - await click('.wizard-btn.next'); - assert.ok(visible('.wizard-step.step_2'), true); - }); - } -); + test("Goes to the next step", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-step.step_1"), true); + await click(".wizard-btn.next"); + assert.ok(visible(".wizard-step.step_2"), true); + }); +}); diff --git a/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 index e68f59ae..8fd82d11 100644 --- a/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 +++ b/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 @@ -1,30 +1,26 @@ import { visit } from "@ember/test-helpers"; import { test } from "qunit"; import { exists } from "../helpers/test"; -import acceptance, { - query, - count, - visible -} from "../helpers/acceptance"; +import acceptance, { count, query, visible } from "../helpers/acceptance"; import { + getWizard, + wizard, + wizardCompleted, wizardNoUser, wizardNotPermitted, - wizardCompleted, - wizard, - getWizard } from "../helpers/wizard"; -acceptance("Wizard | Not logged in", [ getWizard(wizardNoUser) ], - function(hooks) { - test("Wizard no access requires login", async function (assert) { - await visit("/wizard"); - assert.ok(exists(".wizard-no-access.requires-login")); - }); - } -); +acceptance("Wizard | Not logged in", [getWizard(wizardNoUser)], function () { + test("Wizard no access requires login", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-no-access.requires-login")); + }); +}); -acceptance("Wizard | Not permitted", [ getWizard(wizardNotPermitted) ], - function(hooks) { +acceptance( + "Wizard | Not permitted", + [getWizard(wizardNotPermitted)], + function () { test("Wizard no access not permitted", async function (assert) { await visit("/wizard"); assert.ok(exists(".wizard-no-access.not-permitted")); @@ -32,41 +28,46 @@ acceptance("Wizard | Not permitted", [ getWizard(wizardNotPermitted) ], } ); -acceptance("Wizard | Completed", [ getWizard(wizardCompleted) ], - function(hooks) { - test("Wizard no access completed", async function (assert) { - await visit("/wizard"); - assert.ok(exists(".wizard-no-access.completed")); - }); - } -); +acceptance("Wizard | Completed", [getWizard(wizardCompleted)], function () { + test("Wizard no access completed", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-no-access.completed")); + }); +}); -acceptance("Wizard | Wizard", [ getWizard(wizard) ], - function(hooks) { - test("Starts", async function (assert) { - await visit("/wizard"); - assert.ok(query('.wizard-column'), true); - }); +acceptance("Wizard | Wizard", [getWizard(wizard)], function () { + test("Starts", async function (assert) { + await visit("/wizard"); + assert.ok(query(".wizard-column"), true); + }); - test("Applies the body background color", async function (assert) { - await visit("/wizard"); - assert.ok($("body")[0].style.background); - }); + test("Applies the body background color", async function (assert) { + await visit("/wizard"); + assert.ok($("body")[0].style.background); + }); - test("Renders the wizard form", async function (assert) { - await visit("/wizard"); - assert.ok(visible('.wizard-column-contents .wizard-step'), true); - assert.ok(visible('.wizard-footer img'), true); - }); + test("Renders the wizard form", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-column-contents .wizard-step"), true); + assert.ok(visible(".wizard-footer img"), true); + }); - test("Renders the first step", async function (assert) { - await visit("/wizard"); - assert.strictEqual(query('.wizard-step-title p').textContent.trim(), "Text"); - assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); - assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); - assert.strictEqual(count('.wizard-step-form .wizard-field'), 6); - assert.ok(visible('.wizard-step-footer .wizard-progress'), true); - assert.ok(visible('.wizard-step-footer .wizard-buttons'), true); - }); - } -); + test("Renders the first step", async function (assert) { + await visit("/wizard"); + assert.strictEqual( + query(".wizard-step-title p").textContent.trim(), + "Text" + ); + assert.strictEqual( + query(".wizard-step-description p").textContent.trim(), + "Text inputs!" + ); + assert.strictEqual( + query(".wizard-step-description p").textContent.trim(), + "Text inputs!" + ); + assert.strictEqual(count(".wizard-step-form .wizard-field"), 6); + assert.ok(visible(".wizard-step-footer .wizard-progress"), true); + assert.ok(visible(".wizard-step-footer .wizard-buttons"), true); + }); +}); diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 index 29cdfe80..a3343b9a 100644 --- a/assets/javascripts/wizard/tests/bootstrap.js.es6 +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -1,5 +1,5 @@ // discourse-skip-module -/*global document, Logster, QUnit */ +/*global document, Logster */ if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) { document.addEventListener("DOMContentLoaded", function () { diff --git a/assets/javascripts/wizard/tests/fixtures/categories.js.es6 b/assets/javascripts/wizard/tests/fixtures/categories.js.es6 index e862f54a..e553f860 100644 --- a/assets/javascripts/wizard/tests/fixtures/categories.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/categories.js.es6 @@ -1,209 +1,221 @@ export default { - "categories": [ + categories: [ { - "id": 1, - "name": "Uncategorized", - "color": "0088CC", - "text_color": "FFFFFF", - "slug": "uncategorized", - "topic_count": 1, - "post_count": 1, - "position": 0, - "description": "Topics that don't need a category, or don't fit into any other existing category.", - "description_text": "Topics that don't need a category, or don't fit into any other existing category.", - "description_excerpt": "Topics that don't need a category, or don't fit into any other existing category.", - "topic_url": "/t/", - "read_restricted": false, - "permission": 1, - "notification_level": 0, - "topic_template": null, - "has_children": false, - "sort_order": null, - "sort_ascending": null, - "show_subcategory_list": false, - "num_featured_topics": 3, - "default_view": null, - "subcategory_list_style": "rows_with_featured_topics", - "default_top_period": "all", - "default_list_filter": "all", - "minimum_required_tags": 0, - "navigate_to_first_post_after_read": false, - "custom_fields": { - "create_topic_wizard": null + id: 1, + name: "Uncategorized", + color: "0088CC", + text_color: "FFFFFF", + slug: "uncategorized", + topic_count: 1, + post_count: 1, + position: 0, + description: + "Topics that don't need a category, or don't fit into any other existing category.", + description_text: + "Topics that don't need a category, or don't fit into any other existing category.", + description_excerpt: + "Topics that don't need a category, or don't fit into any other existing category.", + topic_url: "/t/", + read_restricted: false, + permission: 1, + notification_level: 0, + topic_template: null, + has_children: false, + sort_order: null, + sort_ascending: null, + show_subcategory_list: false, + num_featured_topics: 3, + default_view: null, + subcategory_list_style: "rows_with_featured_topics", + default_top_period: "all", + default_list_filter: "all", + minimum_required_tags: 0, + navigate_to_first_post_after_read: false, + custom_fields: { + create_topic_wizard: null, }, - "allowed_tags": [], - "allowed_tag_groups": [], - "allow_global_tags": false, - "min_tags_from_required_group": 1, - "required_tag_group_name": null, - "read_only_banner": null, - "uploaded_logo": null, - "uploaded_background": null, - "can_edit": true + allowed_tags: [], + allowed_tag_groups: [], + allow_global_tags: false, + min_tags_from_required_group: 1, + required_tag_group_name: null, + read_only_banner: null, + uploaded_logo: null, + uploaded_background: null, + can_edit: true, }, { - "id": 2, - "name": "Site Feedback", - "color": "808281", - "text_color": "FFFFFF", - "slug": "site-feedback", - "topic_count": 20, - "post_count": 21, - "position": 1, - "description": "

Discussion about this site, its organization, how it works, and how we can improve it.

", - "description_text": "Discussion about this site, its organization, how it works, and how we can improve it.", - "description_excerpt": "Discussion about this site, its organization, how it works, and how we can improve it.", - "topic_url": "/t/about-the-site-feedback-category/1", - "read_restricted": false, - "permission": 1, - "notification_level": 0, - "topic_template": null, - "has_children": false, - "sort_order": null, - "sort_ascending": null, - "show_subcategory_list": false, - "num_featured_topics": 3, - "default_view": null, - "subcategory_list_style": "rows_with_featured_topics", - "default_top_period": "all", - "default_list_filter": "all", - "minimum_required_tags": 0, - "navigate_to_first_post_after_read": false, - "custom_fields": { - "create_topic_wizard": null + id: 2, + name: "Site Feedback", + color: "808281", + text_color: "FFFFFF", + slug: "site-feedback", + topic_count: 20, + post_count: 21, + position: 1, + description: + "

Discussion about this site, its organization, how it works, and how we can improve it.

", + description_text: + "Discussion about this site, its organization, how it works, and how we can improve it.", + description_excerpt: + "Discussion about this site, its organization, how it works, and how we can improve it.", + topic_url: "/t/about-the-site-feedback-category/1", + read_restricted: false, + permission: 1, + notification_level: 0, + topic_template: null, + has_children: false, + sort_order: null, + sort_ascending: null, + show_subcategory_list: false, + num_featured_topics: 3, + default_view: null, + subcategory_list_style: "rows_with_featured_topics", + default_top_period: "all", + default_list_filter: "all", + minimum_required_tags: 0, + navigate_to_first_post_after_read: false, + custom_fields: { + create_topic_wizard: null, }, - "allowed_tags": [], - "allowed_tag_groups": [], - "allow_global_tags": false, - "min_tags_from_required_group": 1, - "required_tag_group_name": null, - "read_only_banner": null, - "uploaded_logo": null, - "uploaded_background": null, - "can_edit": true + allowed_tags: [], + allowed_tag_groups: [], + allow_global_tags: false, + min_tags_from_required_group: 1, + required_tag_group_name: null, + read_only_banner: null, + uploaded_logo: null, + uploaded_background: null, + can_edit: true, }, { - "id": 3, - "name": "Staff", - "color": "E45735", - "text_color": "FFFFFF", - "slug": "staff", - "topic_count": 4, - "post_count": 7, - "position": 2, - "description": "

Private category for staff discussions. Topics are only visible to admins and moderators.

", - "description_text": "Private category for staff discussions. Topics are only visible to admins and moderators.", - "description_excerpt": "Private category for staff discussions. Topics are only visible to admins and moderators.", - "topic_url": "/t/about-the-staff-category/2", - "read_restricted": true, - "permission": 1, - "notification_level": 0, - "topic_template": null, - "has_children": false, - "sort_order": null, - "sort_ascending": null, - "show_subcategory_list": false, - "num_featured_topics": 3, - "default_view": null, - "subcategory_list_style": "rows_with_featured_topics", - "default_top_period": "all", - "default_list_filter": "all", - "minimum_required_tags": 0, - "navigate_to_first_post_after_read": false, - "custom_fields": { - "create_topic_wizard": null + id: 3, + name: "Staff", + color: "E45735", + text_color: "FFFFFF", + slug: "staff", + topic_count: 4, + post_count: 7, + position: 2, + description: + "

Private category for staff discussions. Topics are only visible to admins and moderators.

", + description_text: + "Private category for staff discussions. Topics are only visible to admins and moderators.", + description_excerpt: + "Private category for staff discussions. Topics are only visible to admins and moderators.", + topic_url: "/t/about-the-staff-category/2", + read_restricted: true, + permission: 1, + notification_level: 0, + topic_template: null, + has_children: false, + sort_order: null, + sort_ascending: null, + show_subcategory_list: false, + num_featured_topics: 3, + default_view: null, + subcategory_list_style: "rows_with_featured_topics", + default_top_period: "all", + default_list_filter: "all", + minimum_required_tags: 0, + navigate_to_first_post_after_read: false, + custom_fields: { + create_topic_wizard: null, }, - "allowed_tags": [], - "allowed_tag_groups": [], - "allow_global_tags": false, - "min_tags_from_required_group": 1, - "required_tag_group_name": null, - "read_only_banner": null, - "uploaded_logo": null, - "uploaded_background": null, - "can_edit": true + allowed_tags: [], + allowed_tag_groups: [], + allow_global_tags: false, + min_tags_from_required_group: 1, + required_tag_group_name: null, + read_only_banner: null, + uploaded_logo: null, + uploaded_background: null, + can_edit: true, }, { - "id": 4, - "name": "Lounge", - "color": "A461EF", - "text_color": "652D90", - "slug": "lounge", - "topic_count": 1, - "post_count": 1, - "position": 3, - "description": "

A category exclusive to members with trust level 3 and higher.

", - "description_text": "A category exclusive to members with trust level 3 and higher.", - "description_excerpt": "A category exclusive to members with trust level 3 and higher.", - "topic_url": "/t/about-the-lounge-category/3", - "read_restricted": true, - "permission": 1, - "notification_level": 0, - "topic_template": null, - "has_children": false, - "sort_order": null, - "sort_ascending": null, - "show_subcategory_list": false, - "num_featured_topics": 3, - "default_view": null, - "subcategory_list_style": "rows_with_featured_topics", - "default_top_period": "all", - "default_list_filter": "all", - "minimum_required_tags": 0, - "navigate_to_first_post_after_read": false, - "custom_fields": { - "create_topic_wizard": null + id: 4, + name: "Lounge", + color: "A461EF", + text_color: "652D90", + slug: "lounge", + topic_count: 1, + post_count: 1, + position: 3, + description: + "

A category exclusive to members with trust level 3 and higher.

", + description_text: + "A category exclusive to members with trust level 3 and higher.", + description_excerpt: + "A category exclusive to members with trust level 3 and higher.", + topic_url: "/t/about-the-lounge-category/3", + read_restricted: true, + permission: 1, + notification_level: 0, + topic_template: null, + has_children: false, + sort_order: null, + sort_ascending: null, + show_subcategory_list: false, + num_featured_topics: 3, + default_view: null, + subcategory_list_style: "rows_with_featured_topics", + default_top_period: "all", + default_list_filter: "all", + minimum_required_tags: 0, + navigate_to_first_post_after_read: false, + custom_fields: { + create_topic_wizard: null, }, - "allowed_tags": [], - "allowed_tag_groups": [], - "allow_global_tags": false, - "min_tags_from_required_group": 1, - "required_tag_group_name": null, - "read_only_banner": null, - "uploaded_logo": null, - "uploaded_background": null, - "can_edit": true + allowed_tags: [], + allowed_tag_groups: [], + allow_global_tags: false, + min_tags_from_required_group: 1, + required_tag_group_name: null, + read_only_banner: null, + uploaded_logo: null, + uploaded_background: null, + can_edit: true, }, { - "id": 5, - "name": "Custom Categories", - "color": "0088CC", - "text_color": "FFFFFF", - "slug": "custom-category", - "topic_count": 0, - "post_count": 0, - "position": 10, - "description": "Description of custom category", - "description_text": "Description of custom category", - "description_excerpt": "Description of custom category", - "topic_url": "/t/about-the-custom-category/5", - "read_restricted": false, - "permission": 1, - "notification_level": 0, - "topic_template": null, - "has_children": false, - "sort_order": null, - "sort_ascending": null, - "show_subcategory_list": false, - "num_featured_topics": 3, - "default_view": null, - "subcategory_list_style": "rows_with_featured_topics", - "default_top_period": "all", - "default_list_filter": "all", - "minimum_required_tags": 0, - "navigate_to_first_post_after_read": false, - "custom_fields": { - "create_topic_wizard": null + id: 5, + name: "Custom Categories", + color: "0088CC", + text_color: "FFFFFF", + slug: "custom-category", + topic_count: 0, + post_count: 0, + position: 10, + description: "Description of custom category", + description_text: "Description of custom category", + description_excerpt: "Description of custom category", + topic_url: "/t/about-the-custom-category/5", + read_restricted: false, + permission: 1, + notification_level: 0, + topic_template: null, + has_children: false, + sort_order: null, + sort_ascending: null, + show_subcategory_list: false, + num_featured_topics: 3, + default_view: null, + subcategory_list_style: "rows_with_featured_topics", + default_top_period: "all", + default_list_filter: "all", + minimum_required_tags: 0, + navigate_to_first_post_after_read: false, + custom_fields: { + create_topic_wizard: null, }, - "allowed_tags": [], - "allowed_tag_groups": [], - "allow_global_tags": false, - "min_tags_from_required_group": 1, - "required_tag_group_name": null, - "read_only_banner": null, - "uploaded_logo": null, - "uploaded_background": null, - "can_edit": true - } - ] -} + allowed_tags: [], + allowed_tag_groups: [], + allow_global_tags: false, + min_tags_from_required_group: 1, + required_tag_group_name: null, + read_only_banner: null, + uploaded_logo: null, + uploaded_background: null, + can_edit: true, + }, + ], +}; diff --git a/assets/javascripts/wizard/tests/fixtures/groups.js.es6 b/assets/javascripts/wizard/tests/fixtures/groups.js.es6 index 16f8150d..a770cf58 100644 --- a/assets/javascripts/wizard/tests/fixtures/groups.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/groups.js.es6 @@ -1,313 +1,313 @@ export default { - "groups": [ + groups: [ { - "id": 1, - "automatic": true, - "name": "admins", - "display_name": "admins", - "user_count": 1, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 1, + automatic: true, + name: "admins", + display_name: "admins", + user_count: 1, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 0, - "automatic": true, - "name": "everyone", - "display_name": "everyone", - "user_count": 0, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 3, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 0, + automatic: true, + name: "everyone", + display_name: "everyone", + user_count: 0, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 3, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 15, - "automatic": false, - "name": "custom_group", - "user_count": 1, - "mentionable_level": 1, - "messageable_level": 2, - "visibility_level": 3, - "primary_group": false, - "title": "Custom Group", - "grant_trust_level": 3, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": "I am prefilled", - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 99, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 15, + automatic: false, + name: "custom_group", + user_count: 1, + mentionable_level: 1, + messageable_level: 2, + visibility_level: 3, + primary_group: false, + title: "Custom Group", + grant_trust_level: 3, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: "I am prefilled", + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 99, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 2, - "automatic": true, - "name": "moderators", - "display_name": "moderators", - "user_count": 0, - "mentionable_level": 0, - "messageable_level": 99, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 2, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 2, + automatic: true, + name: "moderators", + display_name: "moderators", + user_count: 0, + mentionable_level: 0, + messageable_level: 99, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 2, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 3, - "automatic": true, - "name": "staff", - "display_name": "staff", - "user_count": 1, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 3, + automatic: true, + name: "staff", + display_name: "staff", + user_count: 1, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 10, - "automatic": true, - "name": "trust_level_0", - "display_name": "trust_level_0", - "user_count": 2, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 10, + automatic: true, + name: "trust_level_0", + display_name: "trust_level_0", + user_count: 2, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 11, - "automatic": true, - "name": "trust_level_1", - "display_name": "trust_level_1", - "user_count": 2, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 11, + automatic: true, + name: "trust_level_1", + display_name: "trust_level_1", + user_count: 2, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 12, - "automatic": true, - "name": "trust_level_2", - "display_name": "trust_level_2", - "user_count": 1, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 12, + automatic: true, + name: "trust_level_2", + display_name: "trust_level_2", + user_count: 1, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 13, - "automatic": true, - "name": "trust_level_3", - "display_name": "trust_level_3", - "user_count": 1, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 13, + automatic: true, + name: "trust_level_3", + display_name: "trust_level_3", + user_count: 1, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 14, - "automatic": true, - "name": "trust_level_4", - "display_name": "trust_level_4", - "user_count": 0, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false - } - ] -} + id: 14, + automatic: true, + name: "trust_level_4", + display_name: "trust_level_4", + user_count: 0, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, + }, + ], +}; diff --git a/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 b/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 index f71ace8e..7568cd49 100644 --- a/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 @@ -1,283 +1,294 @@ export default { - "default_locale": "en", - "title": "Discourse", - "short_site_description": "", - "exclude_rel_nofollow_domains": "", - "logo": "/images/discourse-logo-sketch.png", - "logo_small": "/images/discourse-logo-sketch-small.png", - "digest_logo": "", - "mobile_logo": "", - "logo_dark": "", - "logo_small_dark": "", - "mobile_logo_dark": "", - "large_icon": "", - "favicon": "", - "apple_touch_icon": "", - "display_local_time_in_user_card": false, - "allow_user_locale": false, - "set_locale_from_accept_language_header": false, - "support_mixed_text_direction": false, - "suggested_topics": 5, - "ga_universal_tracking_code": "", - "ga_universal_domain_name": "auto", - "gtm_container_id": "", - "top_menu": "categories|latest", - "post_menu": "read|like|share|flag|edit|bookmark|delete|admin|reply", - "post_menu_hidden_items": "flag|bookmark|edit|delete|admin", - "share_links": "twitter|facebook|email", - "share_quote_visibility": "anonymous", - "share_quote_buttons": "twitter|email", - "desktop_category_page_style": "categories_and_latest_topics", - "category_colors": "BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|E45735", - "category_style": "bullet", - "max_category_nesting": 2, - "enable_mobile_theme": true, - "enable_direct_s3_uploads": false, - "enable_upload_debug_mode": false, - "default_dark_mode_color_scheme_id": 1, - "relative_date_duration": 30, - "fixed_category_positions": false, - "fixed_category_positions_on_create": false, - "enable_badges": true, - "enable_badge_sql": true, - "max_favorite_badges": 2, - "enable_whispers": false, - "enable_bookmarks_with_reminders": true, - "push_notifications_prompt": true, - "vapid_public_key_bytes": "4|29|219|88|202|66|198|62|182|204|66|176|229|200|131|26|141|21|178|231|150|161|2|128|228|200|179|126|118|232|196|19|232|76|108|189|54|211|210|155|55|228|173|112|38|158|114|127|18|95|7|56|110|183|192|92|43|0|243|249|233|89|9|207|255", - "invite_only": false, - "login_required": false, - "must_approve_users": false, - "enable_local_logins": true, - "enable_local_logins_via_email": true, - "allow_new_registrations": true, - "enable_signup_cta": true, - "facebook_app_id": "", - "auth_skip_create_confirm": false, - "auth_overrides_email": false, - "enable_discourse_connect": true, - "discourse_connect_overrides_avatar": false, - "hide_email_address_taken": false, - "min_username_length": 3, - "max_username_length": 20, - "unicode_usernames": false, - "min_password_length": 10, - "min_admin_password_length": 15, - "email_editable": true, - "logout_redirect": "", - "full_name_required": false, - "enable_names": true, - "invite_expiry_days": 90, - "invites_per_page": 40, - "delete_user_max_post_age": 60, - "delete_all_posts_max": 15, - "prioritize_username_in_ux": true, - "enable_user_directory": true, - "allow_anonymous_posting": false, - "anonymous_posting_min_trust_level": 1, - "allow_users_to_hide_profile": true, - "hide_user_profiles_from_public": false, - "allow_featured_topic_on_user_profiles": true, - "hide_suspension_reasons": false, - "ignored_users_count_message_threshold": 5, - "ignored_users_message_gap_days": 365, - "user_selected_primary_groups": false, - "gravatar_name": "Gravatar", - "gravatar_base_url": "www.gravatar.com", - "gravatar_login_url": "/emails", - "enable_group_directory": true, - "enable_category_group_moderation": false, - "min_post_length": 20, - "min_first_post_length": 20, - "min_personal_message_post_length": 10, - "max_post_length": 32000, - "topic_featured_link_enabled": true, - "min_topic_views_for_delete_confirm": 5000, - "min_topic_title_length": 15, - "max_topic_title_length": 255, - "enable_filtered_replies_view": false, - "min_personal_message_title_length": 2, - "allow_uncategorized_topics": true, - "min_title_similar_length": 10, - "enable_personal_messages": true, - "edit_history_visible_to_public": true, - "delete_removed_posts_after": 24, - "traditional_markdown_linebreaks": false, - "enable_markdown_typographer": true, - "enable_markdown_linkify": true, - "markdown_linkify_tlds": "com|net|org|io|onion|co|tv|ru|cn|us|uk|me|de|fr|fi|gov", - "markdown_typographer_quotation_marks": "“|”|‘|’", - "enable_rich_text_paste": true, - "suppress_reply_directly_below": true, - "suppress_reply_directly_above": true, - "max_reply_history": 1, - "enable_mentions": true, - "here_mention": "here", - "newuser_max_embedded_media": 1, - "newuser_max_attachments": 0, - "show_pinned_excerpt_mobile": true, - "show_pinned_excerpt_desktop": true, - "display_name_on_posts": false, - "show_time_gap_days": 7, - "short_progress_text_threshold": 10000, - "default_code_lang": "auto", - "autohighlight_all_code": false, - "highlighted_languages": "apache|bash|cs|cpp|css|coffeescript|diff|xml|http|ini|json|java|javascript|makefile|markdown|nginx|objectivec|ruby|perl|php|python|sql|handlebars", - "show_copy_button_on_codeblocks": false, - "enable_emoji": true, - "enable_emoji_shortcuts": true, - "emoji_set": "twitter", - "emoji_autocomplete_min_chars": 0, - "enable_inline_emoji_translation": false, - "code_formatting_style": "code-fences", - "allowed_href_schemes": "", - "watched_words_regular_expressions": false, - "enable_diffhtml_preview": false, - "enable_fast_edit": true, - "old_post_notice_days": 14, - "blur_tl0_flagged_posts_media": true, - "email_time_window_mins": 10, - "disable_digest_emails": false, - "email_in": false, - "enable_imap": false, - "enable_smtp": false, - "disable_emails": "no", - "bounce_score_threshold": 4, - "enable_secondary_emails": true, - "max_image_size_kb": 4096, - "max_attachment_size_kb": 4096, - "authorized_extensions": "jpg|jpeg|png|gif|heic|heif|webp", - "authorized_extensions_for_staff": "", - "max_image_width": 690, - "max_image_height": 500, - "prevent_anons_from_downloading_files": false, - "secure_media": false, - "enable_s3_uploads": false, - "allow_profile_backgrounds": true, - "allow_uploaded_avatars": "0", - "default_avatars": "", - "external_system_avatars_enabled": true, - "external_system_avatars_url": "/letter_avatar_proxy/v4/letter/{first_letter}/{color}/{size}.png", - "external_emoji_url": "", - "selectable_avatars_mode": "disabled", - "selectable_avatars": "", - "allow_staff_to_upload_any_file_in_pm": true, - "simultaneous_uploads": 5, - "composer_media_optimization_image_enabled": true, - "composer_media_optimization_image_bytes_optimization_threshold": 524288, - "composer_media_optimization_image_resize_dimensions_threshold": 1920, - "composer_media_optimization_image_resize_width_target": 1920, - "composer_media_optimization_image_resize_pre_multiply": false, - "composer_media_optimization_image_resize_linear_rgb": false, - "composer_media_optimization_image_encode_quality": 75, - "composer_media_optimization_debug_mode": false, - "min_trust_level_to_allow_profile_background": 0, - "min_trust_level_to_allow_user_card_background": 0, - "min_trust_level_to_allow_ignore": 2, - "tl1_requires_read_posts": 30, - "tl3_links_no_follow": false, - "enforce_second_factor": "no", - "moderators_change_post_ownership": false, - "moderators_view_emails": false, - "use_admin_ip_allowlist": false, - "allowed_iframes": "https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?|https://calendar.google.com/calendar/embed?|https://codepen.io/|https://www.instagram.com|http://localhost:3000/discobot/certificate.svg", - "can_permanently_delete": false, - "max_oneboxes_per_post": 50, - "reviewable_claiming": "disabled", - "reviewable_default_topics": false, - "reviewable_default_visibility": "low", - "alert_admins_if_errors_per_minute": 0, - "alert_admins_if_errors_per_hour": 0, - "max_prints_per_hour_per_user": 5, - "invite_link_max_redemptions_limit": 5000, - "invite_link_max_redemptions_limit_users": 10, - "enable_long_polling": true, - "enable_chunked_encoding": true, - "long_polling_base_url": "/", - "background_polling_interval": 60000, - "polling_interval": 3000, - "anon_polling_interval": 25000, - "flush_timings_secs": 60, - "verbose_localization": false, - "max_new_topics": 500, - "enable_safe_mode": true, - "tos_url": "", - "privacy_policy_url": "", - "faq_url": "", - "enable_backups": true, - "backup_location": "local", - "maximum_backups": 5, - "use_pg_headlines_for_excerpt": false, - "min_search_term_length": 3, - "log_search_queries": true, - "version_checks": true, - "suppress_uncategorized_badge": true, - "header_dropdown_category_count": 8, - "slug_generation_method": "ascii", - "summary_timeline_button": false, - "topic_views_heat_low": 1000, - "topic_views_heat_medium": 2000, - "topic_views_heat_high": 3500, - "topic_post_like_heat_low": 0.5, - "topic_post_like_heat_medium": 1, - "topic_post_like_heat_high": 2, - "history_hours_low": 12, - "history_hours_medium": 24, - "history_hours_high": 48, - "cold_age_days_low": 14, - "cold_age_days_medium": 90, - "cold_age_days_high": 180, - "global_notice": "", - "show_create_topics_notice": true, - "bootstrap_mode_min_users": 50, - "bootstrap_mode_enabled": true, - "automatically_unpin_topics": true, - "read_time_word_count": 500, - "topic_page_title_includes_category": true, - "svg_icon_subset": "", - "allow_bulk_invite": true, - "disable_mailing_list_mode": true, - "default_topics_automatic_unpin": true, - "mute_all_categories_by_default": false, - "tagging_enabled": true, - "tag_style": "simple", - "max_tags_per_topic": 5, - "max_tag_length": 20, - "min_trust_level_to_tag_topics": "0", - "max_tag_search_results": 5, - "max_tags_in_filter_list": 30, - "tags_sort_alphabetically": false, - "tags_listed_by_group": false, - "suppress_overlapping_tags_in_list": false, - "remove_muted_tags_from_latest": "always", - "force_lowercase_tags": true, - "dashboard_hidden_reports": "", - "dashboard_visible_tabs": "moderation|security|reports", - "dashboard_general_tab_activity_metrics": "page_view_total_reqs|visits|time_to_first_response|likes|flags|user_to_user_private_messages_with_replies", - "discourse_narrative_bot_enabled": true, - "details_enabled": true, - "custom_wizard_enabled": true, - "wizard_redirect_exclude_paths": "admin", - "wizard_recognised_image_upload_formats": "jpg|jpeg|png|gif", - "wizard_important_notices_on_dashboard": true, - "discourse_local_dates_email_format": "YYYY-MM-DDTHH:mm:ss[Z]", - "discourse_local_dates_enabled": true, - "discourse_local_dates_default_formats": "LLL|LTS|LL|LLLL", - "discourse_local_dates_default_timezones": "Europe/Paris|America/Los_Angeles", - "poll_enabled": true, - "poll_maximum_options": 20, - "poll_minimum_trust_level_to_create": 1, - "poll_groupable_user_fields": "", - "poll_export_data_explorer_query_id": -16, - "presence_enabled": true, - "presence_max_users_shown": 5, - "available_locales": "[{\"name\":\"اللغة العربية\",\"value\":\"ar\"},{\"name\":\"беларуская мова\",\"value\":\"be\"},{\"name\":\"български език\",\"value\":\"bg\"},{\"name\":\"bosanski jezik\",\"value\":\"bs_BA\"},{\"name\":\"català\",\"value\":\"ca\"},{\"name\":\"čeština\",\"value\":\"cs\"},{\"name\":\"dansk\",\"value\":\"da\"},{\"name\":\"Deutsch\",\"value\":\"de\"},{\"name\":\"ελληνικά\",\"value\":\"el\"},{\"name\":\"English (US)\",\"value\":\"en\"},{\"name\":\"English (UK)\",\"value\":\"en_GB\"},{\"name\":\"Español\",\"value\":\"es\"},{\"name\":\"eesti\",\"value\":\"et\"},{\"name\":\"فارسی\",\"value\":\"fa_IR\"},{\"name\":\"suomi\",\"value\":\"fi\"},{\"name\":\"Français\",\"value\":\"fr\"},{\"name\":\"galego\",\"value\":\"gl\"},{\"name\":\"עברית\",\"value\":\"he\"},{\"name\":\"magyar\",\"value\":\"hu\"},{\"name\":\"Հայերեն\",\"value\":\"hy\"},{\"name\":\"Indonesian\",\"value\":\"id\"},{\"name\":\"Italiano\",\"value\":\"it\"},{\"name\":\"日本語\",\"value\":\"ja\"},{\"name\":\"한국어\",\"value\":\"ko\"},{\"name\":\"lietuvių kalba\",\"value\":\"lt\"},{\"name\":\"latviešu valoda\",\"value\":\"lv\"},{\"name\":\"Norsk bokmål\",\"value\":\"nb_NO\"},{\"name\":\"Nederlands\",\"value\":\"nl\"},{\"name\":\"polski\",\"value\":\"pl_PL\"},{\"name\":\"Português\",\"value\":\"pt\"},{\"name\":\"Português (BR)\",\"value\":\"pt_BR\"},{\"name\":\"limba română\",\"value\":\"ro\"},{\"name\":\"Русский\",\"value\":\"ru\"},{\"name\":\"slovenčina\",\"value\":\"sk\"},{\"name\":\"slovenščina\",\"value\":\"sl\"},{\"name\":\"Shqip\",\"value\":\"sq\"},{\"name\":\"српски језик\",\"value\":\"sr\"},{\"name\":\"svenska\",\"value\":\"sv\"},{\"name\":\"Kiswahili\",\"value\":\"sw\"},{\"name\":\"తెలుగు\",\"value\":\"te\"},{\"name\":\"ไทย\",\"value\":\"th\"},{\"name\":\"Türkçe\",\"value\":\"tr_TR\"},{\"name\":\"українська мова\",\"value\":\"uk\"},{\"name\":\"اردو\",\"value\":\"ur\"},{\"name\":\"Việt Nam\",\"value\":\"vi\"},{\"name\":\"简体中文\",\"value\":\"zh_CN\"},{\"name\":\"繁體中文\",\"value\":\"zh_TW\"}]", - "require_invite_code": false, - "site_logo_url": "http://localhost:3000/images/discourse-logo-sketch.png", - "site_logo_small_url": "http://localhost:3000/images/discourse-logo-sketch-small.png", - "site_mobile_logo_url": "http://localhost:3000/images/discourse-logo-sketch.png", - "site_favicon_url": "http://localhost:3000/uploads/default/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_32x32.png", - "site_logo_dark_url": "", - "site_logo_small_dark_url": "", - "site_mobile_logo_dark_url": "" -} + default_locale: "en", + title: "Discourse", + short_site_description: "", + exclude_rel_nofollow_domains: "", + logo: "/images/discourse-logo-sketch.png", + logo_small: "/images/discourse-logo-sketch-small.png", + digest_logo: "", + mobile_logo: "", + logo_dark: "", + logo_small_dark: "", + mobile_logo_dark: "", + large_icon: "", + favicon: "", + apple_touch_icon: "", + display_local_time_in_user_card: false, + allow_user_locale: false, + set_locale_from_accept_language_header: false, + support_mixed_text_direction: false, + suggested_topics: 5, + ga_universal_tracking_code: "", + ga_universal_domain_name: "auto", + gtm_container_id: "", + top_menu: "categories|latest", + post_menu: "read|like|share|flag|edit|bookmark|delete|admin|reply", + post_menu_hidden_items: "flag|bookmark|edit|delete|admin", + share_links: "twitter|facebook|email", + share_quote_visibility: "anonymous", + share_quote_buttons: "twitter|email", + desktop_category_page_style: "categories_and_latest_topics", + category_colors: + "BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|E45735", + category_style: "bullet", + max_category_nesting: 2, + enable_mobile_theme: true, + enable_direct_s3_uploads: false, + enable_upload_debug_mode: false, + default_dark_mode_color_scheme_id: 1, + relative_date_duration: 30, + fixed_category_positions: false, + fixed_category_positions_on_create: false, + enable_badges: true, + enable_badge_sql: true, + max_favorite_badges: 2, + enable_whispers: false, + enable_bookmarks_with_reminders: true, + push_notifications_prompt: true, + vapid_public_key_bytes: + "4|29|219|88|202|66|198|62|182|204|66|176|229|200|131|26|141|21|178|231|150|161|2|128|228|200|179|126|118|232|196|19|232|76|108|189|54|211|210|155|55|228|173|112|38|158|114|127|18|95|7|56|110|183|192|92|43|0|243|249|233|89|9|207|255", + invite_only: false, + login_required: false, + must_approve_users: false, + enable_local_logins: true, + enable_local_logins_via_email: true, + allow_new_registrations: true, + enable_signup_cta: true, + facebook_app_id: "", + auth_skip_create_confirm: false, + auth_overrides_email: false, + enable_discourse_connect: true, + discourse_connect_overrides_avatar: false, + hide_email_address_taken: false, + min_username_length: 3, + max_username_length: 20, + unicode_usernames: false, + min_password_length: 10, + min_admin_password_length: 15, + email_editable: true, + logout_redirect: "", + full_name_required: false, + enable_names: true, + invite_expiry_days: 90, + invites_per_page: 40, + delete_user_max_post_age: 60, + delete_all_posts_max: 15, + prioritize_username_in_ux: true, + enable_user_directory: true, + allow_anonymous_posting: false, + anonymous_posting_min_trust_level: 1, + allow_users_to_hide_profile: true, + hide_user_profiles_from_public: false, + allow_featured_topic_on_user_profiles: true, + hide_suspension_reasons: false, + ignored_users_count_message_threshold: 5, + ignored_users_message_gap_days: 365, + user_selected_primary_groups: false, + gravatar_name: "Gravatar", + gravatar_base_url: "www.gravatar.com", + gravatar_login_url: "/emails", + enable_group_directory: true, + enable_category_group_moderation: false, + min_post_length: 20, + min_first_post_length: 20, + min_personal_message_post_length: 10, + max_post_length: 32000, + topic_featured_link_enabled: true, + min_topic_views_for_delete_confirm: 5000, + min_topic_title_length: 15, + max_topic_title_length: 255, + enable_filtered_replies_view: false, + min_personal_message_title_length: 2, + allow_uncategorized_topics: true, + min_title_similar_length: 10, + enable_personal_messages: true, + edit_history_visible_to_public: true, + delete_removed_posts_after: 24, + traditional_markdown_linebreaks: false, + enable_markdown_typographer: true, + enable_markdown_linkify: true, + markdown_linkify_tlds: + "com|net|org|io|onion|co|tv|ru|cn|us|uk|me|de|fr|fi|gov", + markdown_typographer_quotation_marks: "“|”|‘|’", + enable_rich_text_paste: true, + suppress_reply_directly_below: true, + suppress_reply_directly_above: true, + max_reply_history: 1, + enable_mentions: true, + here_mention: "here", + newuser_max_embedded_media: 1, + newuser_max_attachments: 0, + show_pinned_excerpt_mobile: true, + show_pinned_excerpt_desktop: true, + display_name_on_posts: false, + show_time_gap_days: 7, + short_progress_text_threshold: 10000, + default_code_lang: "auto", + autohighlight_all_code: false, + highlighted_languages: + "apache|bash|cs|cpp|css|coffeescript|diff|xml|http|ini|json|java|javascript|makefile|markdown|nginx|objectivec|ruby|perl|php|python|sql|handlebars", + show_copy_button_on_codeblocks: false, + enable_emoji: true, + enable_emoji_shortcuts: true, + emoji_set: "twitter", + emoji_autocomplete_min_chars: 0, + enable_inline_emoji_translation: false, + code_formatting_style: "code-fences", + allowed_href_schemes: "", + watched_words_regular_expressions: false, + enable_diffhtml_preview: false, + enable_fast_edit: true, + old_post_notice_days: 14, + blur_tl0_flagged_posts_media: true, + email_time_window_mins: 10, + disable_digest_emails: false, + email_in: false, + enable_imap: false, + enable_smtp: false, + disable_emails: "no", + bounce_score_threshold: 4, + enable_secondary_emails: true, + max_image_size_kb: 4096, + max_attachment_size_kb: 4096, + authorized_extensions: "jpg|jpeg|png|gif|heic|heif|webp", + authorized_extensions_for_staff: "", + max_image_width: 690, + max_image_height: 500, + prevent_anons_from_downloading_files: false, + secure_media: false, + enable_s3_uploads: false, + allow_profile_backgrounds: true, + allow_uploaded_avatars: "0", + default_avatars: "", + external_system_avatars_enabled: true, + external_system_avatars_url: + "/letter_avatar_proxy/v4/letter/{first_letter}/{color}/{size}.png", + external_emoji_url: "", + selectable_avatars_mode: "disabled", + selectable_avatars: "", + allow_staff_to_upload_any_file_in_pm: true, + simultaneous_uploads: 5, + composer_media_optimization_image_enabled: true, + composer_media_optimization_image_bytes_optimization_threshold: 524288, + composer_media_optimization_image_resize_dimensions_threshold: 1920, + composer_media_optimization_image_resize_width_target: 1920, + composer_media_optimization_image_resize_pre_multiply: false, + composer_media_optimization_image_resize_linear_rgb: false, + composer_media_optimization_image_encode_quality: 75, + composer_media_optimization_debug_mode: false, + min_trust_level_to_allow_profile_background: 0, + min_trust_level_to_allow_user_card_background: 0, + min_trust_level_to_allow_ignore: 2, + tl1_requires_read_posts: 30, + tl3_links_no_follow: false, + enforce_second_factor: "no", + moderators_change_post_ownership: false, + moderators_view_emails: false, + use_admin_ip_allowlist: false, + allowed_iframes: + "https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?|https://calendar.google.com/calendar/embed?|https://codepen.io/|https://www.instagram.com|http://localhost:3000/discobot/certificate.svg", + can_permanently_delete: false, + max_oneboxes_per_post: 50, + reviewable_claiming: "disabled", + reviewable_default_topics: false, + reviewable_default_visibility: "low", + alert_admins_if_errors_per_minute: 0, + alert_admins_if_errors_per_hour: 0, + max_prints_per_hour_per_user: 5, + invite_link_max_redemptions_limit: 5000, + invite_link_max_redemptions_limit_users: 10, + enable_long_polling: true, + enable_chunked_encoding: true, + long_polling_base_url: "/", + background_polling_interval: 60000, + polling_interval: 3000, + anon_polling_interval: 25000, + flush_timings_secs: 60, + verbose_localization: false, + max_new_topics: 500, + enable_safe_mode: true, + tos_url: "", + privacy_policy_url: "", + faq_url: "", + enable_backups: true, + backup_location: "local", + maximum_backups: 5, + use_pg_headlines_for_excerpt: false, + min_search_term_length: 3, + log_search_queries: true, + version_checks: true, + suppress_uncategorized_badge: true, + header_dropdown_category_count: 8, + slug_generation_method: "ascii", + summary_timeline_button: false, + topic_views_heat_low: 1000, + topic_views_heat_medium: 2000, + topic_views_heat_high: 3500, + topic_post_like_heat_low: 0.5, + topic_post_like_heat_medium: 1, + topic_post_like_heat_high: 2, + history_hours_low: 12, + history_hours_medium: 24, + history_hours_high: 48, + cold_age_days_low: 14, + cold_age_days_medium: 90, + cold_age_days_high: 180, + global_notice: "", + show_create_topics_notice: true, + bootstrap_mode_min_users: 50, + bootstrap_mode_enabled: true, + automatically_unpin_topics: true, + read_time_word_count: 500, + topic_page_title_includes_category: true, + svg_icon_subset: "", + allow_bulk_invite: true, + disable_mailing_list_mode: true, + default_topics_automatic_unpin: true, + mute_all_categories_by_default: false, + tagging_enabled: true, + tag_style: "simple", + max_tags_per_topic: 5, + max_tag_length: 20, + min_trust_level_to_tag_topics: "0", + max_tag_search_results: 5, + max_tags_in_filter_list: 30, + tags_sort_alphabetically: false, + tags_listed_by_group: false, + suppress_overlapping_tags_in_list: false, + remove_muted_tags_from_latest: "always", + force_lowercase_tags: true, + dashboard_hidden_reports: "", + dashboard_visible_tabs: "moderation|security|reports", + dashboard_general_tab_activity_metrics: + "page_view_total_reqs|visits|time_to_first_response|likes|flags|user_to_user_private_messages_with_replies", + discourse_narrative_bot_enabled: true, + details_enabled: true, + custom_wizard_enabled: true, + wizard_redirect_exclude_paths: "admin", + wizard_recognised_image_upload_formats: "jpg|jpeg|png|gif", + wizard_important_notices_on_dashboard: true, + discourse_local_dates_email_format: "YYYY-MM-DDTHH:mm:ss[Z]", + discourse_local_dates_enabled: true, + discourse_local_dates_default_formats: "LLL|LTS|LL|LLLL", + discourse_local_dates_default_timezones: "Europe/Paris|America/Los_Angeles", + poll_enabled: true, + poll_maximum_options: 20, + poll_minimum_trust_level_to_create: 1, + poll_groupable_user_fields: "", + poll_export_data_explorer_query_id: -16, + presence_enabled: true, + presence_max_users_shown: 5, + available_locales: + '[{"name":"اللغة العربية","value":"ar"},{"name":"беларуская мова","value":"be"},{"name":"български език","value":"bg"},{"name":"bosanski jezik","value":"bs_BA"},{"name":"català","value":"ca"},{"name":"čeština","value":"cs"},{"name":"dansk","value":"da"},{"name":"Deutsch","value":"de"},{"name":"ελληνικά","value":"el"},{"name":"English (US)","value":"en"},{"name":"English (UK)","value":"en_GB"},{"name":"Español","value":"es"},{"name":"eesti","value":"et"},{"name":"فارسی","value":"fa_IR"},{"name":"suomi","value":"fi"},{"name":"Français","value":"fr"},{"name":"galego","value":"gl"},{"name":"עברית","value":"he"},{"name":"magyar","value":"hu"},{"name":"Հայերեն","value":"hy"},{"name":"Indonesian","value":"id"},{"name":"Italiano","value":"it"},{"name":"日本語","value":"ja"},{"name":"한국어","value":"ko"},{"name":"lietuvių kalba","value":"lt"},{"name":"latviešu valoda","value":"lv"},{"name":"Norsk bokmål","value":"nb_NO"},{"name":"Nederlands","value":"nl"},{"name":"polski","value":"pl_PL"},{"name":"Português","value":"pt"},{"name":"Português (BR)","value":"pt_BR"},{"name":"limba română","value":"ro"},{"name":"Русский","value":"ru"},{"name":"slovenčina","value":"sk"},{"name":"slovenščina","value":"sl"},{"name":"Shqip","value":"sq"},{"name":"српски језик","value":"sr"},{"name":"svenska","value":"sv"},{"name":"Kiswahili","value":"sw"},{"name":"తెలుగు","value":"te"},{"name":"ไทย","value":"th"},{"name":"Türkçe","value":"tr_TR"},{"name":"українська мова","value":"uk"},{"name":"اردو","value":"ur"},{"name":"Việt Nam","value":"vi"},{"name":"简体中文","value":"zh_CN"},{"name":"繁體中文","value":"zh_TW"}]', + require_invite_code: false, + site_logo_url: "http://localhost:3000/images/discourse-logo-sketch.png", + site_logo_small_url: + "http://localhost:3000/images/discourse-logo-sketch-small.png", + site_mobile_logo_url: + "http://localhost:3000/images/discourse-logo-sketch.png", + site_favicon_url: + "http://localhost:3000/uploads/default/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_32x32.png", + site_logo_dark_url: "", + site_logo_small_dark_url: "", + site_mobile_logo_dark_url: "", +}; diff --git a/assets/javascripts/wizard/tests/fixtures/tags.js.es6 b/assets/javascripts/wizard/tests/fixtures/tags.js.es6 index a072772e..61e51994 100644 --- a/assets/javascripts/wizard/tests/fixtures/tags.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/tags.js.es6 @@ -1,22 +1,22 @@ export default { - "tags": [ + tags: [ { - "id": "tag1", - "text": "tag1", - "name": "tag1", - "description": null, - "count": 1, - "pm_count": 0, - "target_tag": null + id: "tag1", + text: "tag1", + name: "tag1", + description: null, + count: 1, + pm_count: 0, + target_tag: null, }, { - "id": "tag2", - "text": "tag2", - "name": "tag2", - "description": null, - "count": 1, - "pm_count": 0, - "target_tag": null - } - ] -} + id: "tag2", + text: "tag2", + name: "tag2", + description: null, + count: 1, + pm_count: 0, + target_tag: null, + }, + ], +}; diff --git a/assets/javascripts/wizard/tests/fixtures/update.js.es6 b/assets/javascripts/wizard/tests/fixtures/update.js.es6 index 3908525c..5b20788c 100644 --- a/assets/javascripts/wizard/tests/fixtures/update.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/update.js.es6 @@ -1,5 +1,5 @@ export default { - "final": false, - "next_step_id": "step_2", - "wizard": {} -} + final: false, + next_step_id: "step_2", + wizard: {}, +}; diff --git a/assets/javascripts/wizard/tests/fixtures/user.js.es6 b/assets/javascripts/wizard/tests/fixtures/user.js.es6 index 8acd7392..d954905c 100644 --- a/assets/javascripts/wizard/tests/fixtures/user.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/user.js.es6 @@ -30,5 +30,5 @@ export default { title_count_mode: "notifications", timezone: "Australia/Perth", skip_new_user_tips: false, - can_review: true -} + can_review: true, +}; diff --git a/assets/javascripts/wizard/tests/fixtures/users.js.es6 b/assets/javascripts/wizard/tests/fixtures/users.js.es6 index 267d4909..437631a2 100644 --- a/assets/javascripts/wizard/tests/fixtures/users.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/users.js.es6 @@ -1,14 +1,14 @@ export default { - "users": [ + users: [ { - "username": "angus", - "name": "Angus", - "avatar_template": "/user_avatar/localhost/angus/{size}/12_2.png" + username: "angus", + name: "Angus", + avatar_template: "/user_avatar/localhost/angus/{size}/12_2.png", }, { - "username": "angus_2", - "name": "Angus 2", - "avatar_template": "/letter_avatar_proxy/v4/letter/a/e9a140/{size}.png" - } - ] -} + username: "angus_2", + name: "Angus 2", + avatar_template: "/letter_avatar_proxy/v4/letter/a/e9a140/{size}.png", + }, + ], +}; diff --git a/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 b/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 index be4fa8b2..73fe45c1 100644 --- a/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 @@ -1,469 +1,471 @@ export default { - "id": "wizard", - "name": "Wizard", - "start": "step_1", - "background": "#333333", - "submission_last_updated_at": "2022-03-15T21:11:01+01:00", - "theme_id": 2, - "required": false, - "permitted": true, - "uncategorized_category_id": 1, - "categories": [], - "subscribed": false, - "resume_on_revisit": false, - "steps": [ + id: "wizard", + name: "Wizard", + start: "step_1", + background: "#333333", + submission_last_updated_at: "2022-03-15T21:11:01+01:00", + theme_id: 2, + required: false, + permitted: true, + uncategorized_category_id: 1, + categories: [], + subscribed: false, + resume_on_revisit: false, + steps: [ { - "id": "step_1", - "index": 0, - "next": "step_2", - "description": "

Text inputs!

", - "title": "Text", - "permitted": true, - "permitted_message": null, - "final": false, - "fields": [ + id: "step_1", + index: 0, + next: "step_2", + description: "

Text inputs!

", + title: "Text", + permitted: true, + permitted_message: null, + final: false, + fields: [ { - "id": "step_1_field_1", - "index": 0, - "type": "text", - "required": false, - "value": "I am prefilled", - "label": "

Text

", - "description": "Text field description.", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 1, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 + id: "step_1_field_1", + index: 0, + type: "text", + required: false, + value: "I am prefilled", + label: "

Text

", + description: "Text field description.", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 1, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, }, { - "id": "step_1_field_2", - "index": 0, - "type": "textarea", - "required": false, - "value": "", - "label": "

Textarea

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 2, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 + id: "step_1_field_2", + index: 0, + type: "textarea", + required: false, + value: "", + label: "

Textarea

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 2, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, }, { - "id": "step_1_field_3", - "index": 2, - "type": "composer", - "required": false, - "value": "", - "label": "

Composer

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 3, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 + id: "step_1_field_3", + index: 2, + type: "composer", + required: false, + value: "", + label: "

Composer

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 3, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, }, { - "id": "step_1_field_4", - "index": 3, - "type": "text_only", - "required": false, - "value": null, - "label": "

I’m only text

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 4, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 + id: "step_1_field_4", + index: 3, + type: "text_only", + required: false, + value: null, + label: "

I’m only text

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 4, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, }, { - "id": "step_1_field_5", - "index": 4, - "type": "composer_preview", - "required": false, - "value": "", - "label": "

I’m a preview

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": "

I am prefilled

", - "tabindex": 5, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 + id: "step_1_field_5", + index: 4, + type: "composer_preview", + required: false, + value: "", + label: "

I’m a preview

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: "

I am prefilled

", + tabindex: 5, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, }, { - "id": "step_1_field_6", - "index": 5, - "type": "composer_preview", - "required": false, - "value": "", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": "

This is the preview of the composer

", - "tabindex": 6, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 - } + id: "step_1_field_6", + index: 5, + type: "composer_preview", + required: false, + value: "", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: "

This is the preview of the composer

", + tabindex: 6, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, + }, ], - "_validState": 0, - "wizardId": "super_mega_fun_wizard" + _validState: 0, + wizardId: "super_mega_fun_wizard", }, { - "id": "step_2", - "index": 1, - "next": "step_3", - "previous": "step_1", - "description": "

Because I couldn’t think of another name for this step \":slight_smile:\"

", - "title": "Values", - "permitted": true, - "permitted_message": null, - "final": false, - "fields": [ + id: "step_2", + index: 1, + next: "step_3", + previous: "step_1", + description: + '

Because I couldn’t think of another name for this step :slight_smile:

', + title: "Values", + permitted: true, + permitted_message: null, + final: false, + fields: [ { - "id": "step_2_field_1", - "index": 0, - "type": "date", - "required": false, - "value": "", - "label": "

Date

", - "file_types": null, - "format": "YYYY-MM-DD", - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 1, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_1", + index: 0, + type: "date", + required: false, + value: "", + label: "

Date

", + file_types: null, + format: "YYYY-MM-DD", + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 1, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_2", - "index": 0, - "type": "time", - "required": false, - "value": "", - "label": "

Time

", - "file_types": null, - "format": "HH:mm", - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 2, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_2", + index: 0, + type: "time", + required: false, + value: "", + label: "

Time

", + file_types: null, + format: "HH:mm", + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 2, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_3", - "index": 2, - "type": "date_time", - "required": false, - "value": "", - "label": "

Date & Time

", - "file_types": null, - "format": "", - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 3, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_3", + index: 2, + type: "date_time", + required: false, + value: "", + label: "

Date & Time

", + file_types: null, + format: "", + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 3, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_4", - "index": 3, - "type": "number", - "required": false, - "value": "", - "label": "

Number

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 5, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_4", + index: 3, + type: "number", + required: false, + value: "", + label: "

Number

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 5, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_5", - "index": 4, - "type": "checkbox", - "required": false, - "value": false, - "label": "

Checkbox

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 6, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_5", + index: 4, + type: "checkbox", + required: false, + value: false, + label: "

Checkbox

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 6, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_6", - "index": 5, - "type": "url", - "required": false, - "value": "", - "label": "

Url

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 7, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_6", + index: 5, + type: "url", + required: false, + value: "", + label: "

Url

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 7, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_7", - "index": 6, - "type": "upload", - "required": false, - "value": "", - "label": "

Upload

", - "file_types": ".jpg,.jpeg,.png", - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 8, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 - } + id: "step_2_field_7", + index: 6, + type: "upload", + required: false, + value: "", + label: "

Upload

", + file_types: ".jpg,.jpeg,.png", + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 8, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, + }, ], - "_validState": 0, - "wizardId": "super_mega_fun_wizard" + _validState: 0, + wizardId: "super_mega_fun_wizard", }, { - "id": "step_3", - "index": 2, - "previous": "step_2", - "description": "

Unfortunately not the edible type \":sushi:\"

", - "title": "Combo-boxes", - "permitted": true, - "permitted_message": null, - "final": true, - "fields": [ + id: "step_3", + index: 2, + previous: "step_2", + description: + '

Unfortunately not the edible type :sushi:

', + title: "Combo-boxes", + permitted: true, + permitted_message: null, + final: true, + fields: [ { - "id": "step_3_field_1", - "index": 0, - "type": "dropdown", - "required": false, - "value": "choice1", - "label": "

Custom Dropdown

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": [ + id: "step_3_field_1", + index: 0, + type: "dropdown", + required: false, + value: "choice1", + label: "

Custom Dropdown

", + file_types: null, + format: null, + limit: null, + property: null, + content: [ { - "id": "one", - "name": "One" + id: "one", + name: "One", }, { - "id": "two", - "name": "Two" - } + id: "two", + name: "Two", + }, ], - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 1, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 1, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, }, { - "id": "step_3_field_2", - "index": 0, - "type": "tag", - "required": false, - "value": null, - "label": "

Tag

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 2, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 + id: "step_3_field_2", + index: 0, + type: "tag", + required: false, + value: null, + label: "

Tag

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 2, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, }, { - "id": "step_3_field_3", - "index": 2, - "type": "category", - "required": false, - "value": null, - "label": "

Category

", - "file_types": null, - "format": null, - "limit": 1, - "property": "id", - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 3, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 + id: "step_3_field_3", + index: 2, + type: "category", + required: false, + value: null, + label: "

Category

", + file_types: null, + format: null, + limit: 1, + property: "id", + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 3, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, }, { - "id": "step_3_field_4", - "index": 3, - "type": "group", - "required": false, - "value": null, - "label": "

Group

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 4, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 + id: "step_3_field_4", + index: 3, + type: "group", + required: false, + value: null, + label: "

Group

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 4, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, }, { - "id": "step_3_field_5", - "index": 4, - "type": "user_selector", - "required": false, - "value": null, - "label": "

User Selector

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 5, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 + id: "step_3_field_5", + index: 4, + type: "user_selector", + required: false, + value: null, + label: "

User Selector

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 5, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, }, { - "id": "step_3_field_6", - "index": 5, - "type": "user_selector", - "required": false, - "value": null, - "label": "

Conditional User Selector

", - "description": "Shown when checkbox in step_2_field_5 is true", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 6, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 - } + id: "step_3_field_6", + index: 5, + type: "user_selector", + required: false, + value: null, + label: "

Conditional User Selector

", + description: "Shown when checkbox in step_2_field_5 is true", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 6, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, + }, ], - "_validState": 0, - "wizardId": "super_mega_fun_wizard" - } + _validState: 0, + wizardId: "super_mega_fun_wizard", + }, ], - "groups": [] -} + groups: [], +}; diff --git a/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 b/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 index f5c1175f..913e8c7a 100644 --- a/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 +++ b/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 @@ -6,18 +6,20 @@ let server; let app; function acceptance(name, requests, cb) { - module(`Acceptance: ${name}`, function(hooks) { - hooks.beforeEach(function() { - server = setupPretender(function(pretender) { - requests.forEach(req => { - pretender[req.verb](req.path, () => (response(req.status, req.response))); + module(`Acceptance: ${name}`, function (hooks) { + hooks.beforeEach(function () { + server = setupPretender(function (pretender) { + requests.forEach((req) => { + pretender[req.verb](req.path, () => + response(req.status, req.response) + ); }); return pretender; }); app = startApp(); }); - hooks.afterEach(function() { + hooks.afterEach(function () { app.destroy(); server.shutdown(); }); @@ -28,9 +30,7 @@ function acceptance(name, requests, cb) { export default acceptance; -export { - server -}; +export { server }; // The discourse/test/helpers/qunit-helpers file has many functions and imports // we don't need, so there will be some duplciation here. diff --git a/assets/javascripts/wizard/tests/helpers/start-app.js.es6 b/assets/javascripts/wizard/tests/helpers/start-app.js.es6 index 6afe6eb9..821d8033 100644 --- a/assets/javascripts/wizard/tests/helpers/start-app.js.es6 +++ b/assets/javascripts/wizard/tests/helpers/start-app.js.es6 @@ -1,6 +1,12 @@ -const CustomWizard = requirejs("discourse/plugins/discourse-custom-wizard/wizard/application").default; -const initializer = requirejs("discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/wizard").default; -const siteSettings = requirejs("discourse/plugins/discourse-custom-wizard/wizard/tests/fixtures/site-settings").default; +const CustomWizard = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/application" +).default; +const initializer = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/wizard" +).default; +const siteSettings = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/tests/fixtures/site-settings" +).default; const { cloneJSON } = requirejs("discourse-common/lib/object").default; let app; diff --git a/assets/javascripts/wizard/tests/helpers/step.js.es6 b/assets/javascripts/wizard/tests/helpers/step.js.es6 index a24e04e1..41fe16cd 100644 --- a/assets/javascripts/wizard/tests/helpers/step.js.es6 +++ b/assets/javascripts/wizard/tests/helpers/step.js.es6 @@ -5,16 +5,13 @@ import wizardJson from "../fixtures/wizard"; const update = cloneJSON(updateJson); update.wizard = cloneJSON(wizardJson); -const saveStep = function(response) { +const saveStep = function (response) { return { verb: "put", - path: '/w/wizard/steps/:step_id', + path: "/w/wizard/steps/:step_id", status: 200, - response - } -} + response, + }; +}; -export { - saveStep, - update -} +export { saveStep, update }; diff --git a/assets/javascripts/wizard/tests/helpers/test.js.es6 b/assets/javascripts/wizard/tests/helpers/test.js.es6 index c7401fd5..360c883a 100644 --- a/assets/javascripts/wizard/tests/helpers/test.js.es6 +++ b/assets/javascripts/wizard/tests/helpers/test.js.es6 @@ -2,6 +2,4 @@ function exists(selector) { return document.querySelector(selector) !== null; } -export { - exists -} +export { exists }; diff --git a/assets/javascripts/wizard/tests/helpers/wizard.js.es6 b/assets/javascripts/wizard/tests/helpers/wizard.js.es6 index 997f6c36..4cd2e003 100644 --- a/assets/javascripts/wizard/tests/helpers/wizard.js.es6 +++ b/assets/javascripts/wizard/tests/helpers/wizard.js.es6 @@ -26,20 +26,20 @@ const allFieldsWizard = cloneJSON(wizard); allFieldsWizard.steps[0].fields = [ ...allFieldsWizard.steps[0].fields, ...allFieldsWizard.steps[1].fields, - ...allFieldsWizard.steps[2].fields + ...allFieldsWizard.steps[2].fields, ]; allFieldsWizard.steps = [cloneJSON(allFieldsWizard.steps[0])]; -allFieldsWizard.categories = cloneJSON(categoriesJson['categories']); -allFieldsWizard.groups = cloneJSON(groupsJson['groups']); +allFieldsWizard.categories = cloneJSON(categoriesJson["categories"]); +allFieldsWizard.groups = cloneJSON(groupsJson["groups"]); -const getWizard = function(response) { +const getWizard = function (response) { return { verb: "get", path: "/w/wizard", status: 200, - response - } -} + response, + }; +}; export { getWizard, @@ -48,5 +48,5 @@ export { wizardCompleted, stepNotPermitted, allFieldsWizard, - wizard -} + wizard, +}; diff --git a/assets/javascripts/wizard/tests/pretender.js.es6 b/assets/javascripts/wizard/tests/pretender.js.es6 index 88eae666..1f1d4a7d 100644 --- a/assets/javascripts/wizard/tests/pretender.js.es6 +++ b/assets/javascripts/wizard/tests/pretender.js.es6 @@ -1,23 +1,5 @@ import Pretender from "pretender"; -function parsePostData(query) { - const result = {}; - query.split("&").forEach(function (part) { - const item = part.split("="); - const firstSeg = decodeURIComponent(item[0]); - const m = /^([^\[]+)\[([^\]]+)\]/.exec(firstSeg); - - const val = decodeURIComponent(item[1]).replace(/\+/g, " "); - if (m) { - result[m[1]] = result[m[1]] || {}; - result[m[1]][m[2]] = val; - } else { - result[firstSeg] = val; - } - }); - return result; -} - function response(code, obj) { if (typeof code === "object") { obj = code; @@ -42,7 +24,7 @@ export default function (cb) { return body; }; - server.unhandledRequest = function (verb, path, request) { + server.unhandledRequest = function (verb, path) { const error = "Unhandled request in test environment: " + path + " (" + verb + ")"; window.console.error(error); From af93a67bb1b41d839de4d26989d0f9763e37328a Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 15 Jun 2022 09:08:28 +0200 Subject: [PATCH 10/30] Fixes from the cherry picks --- .../javascripts/wizard/components/wizard-composer-editor.js.es6 | 2 +- .../javascripts/wizard/lib/initialize/patch-components.js.es6 | 2 +- assets/javascripts/wizard/models/wizard.js.es6 | 1 + .../wizard/templates/components/wizard-field-text.hbs | 2 +- .../wizard/templates/components/wizard-field-textarea.hbs | 2 +- assets/javascripts/wizard/templates/components/wizard-field.hbs | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 index 2c04cd4f..4f44d439 100644 --- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 @@ -25,7 +25,7 @@ export default ComposerEditor.extend({ lastValidatedAt: "lastValidatedAt", popupMenuOptions: [], draftStatus: "null", - replyPlaceholder: alias("field.placeholder"), + replyPlaceholder: alias("field.translatedPlaceholder"), uploadingFieldId: null, @on("didInsertElement") diff --git a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 index 90ded90c..9d13160e 100644 --- a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 @@ -1,5 +1,5 @@ export default { - run(_, container) { + run(app, container) { const getToken = requirejs("wizard/lib/ajax").getToken; const isTesting = requirejs("discourse-common/config/environment") .isTesting; diff --git a/assets/javascripts/wizard/models/wizard.js.es6 b/assets/javascripts/wizard/models/wizard.js.es6 index bc2bff14..15bcbb8c 100644 --- a/assets/javascripts/wizard/models/wizard.js.es6 +++ b/assets/javascripts/wizard/models/wizard.js.es6 @@ -57,6 +57,7 @@ CustomWizard.reopenClass({ wizardJson.steps = wizardJson.steps .map((step) => { const stepObj = Step.create(step); + stepObj.wizardId = wizardJson.id; stepObj.fields.sort((a, b) => { return parseFloat(a.number) - parseFloat(b.number); diff --git a/assets/javascripts/wizard/templates/components/wizard-field-text.hbs b/assets/javascripts/wizard/templates/components/wizard-field-text.hbs index be44e8e7..08733d3f 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field-text.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field-text.hbs @@ -1 +1 @@ -{{input id=field.id value=field.value class=fieldClass placeholder=field.placeholder tabindex=field.tabindex autocomplete=autocomplete}} +{{input id=field.id value=field.value class=fieldClass placeholder=field.translatedPlaceholder tabindex=field.tabindex autocomplete=autocomplete}} diff --git a/assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs b/assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs index 6efb7560..dda299bc 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs @@ -1 +1 @@ -{{textarea id=field.id value=field.value class=fieldClass placeholder=field.placeholder tabindex=field.tabindex}} +{{textarea id=field.id value=field.value class=fieldClass placeholder=field.translatedPlaceholder tabindex=field.tabindex}} diff --git a/assets/javascripts/wizard/templates/components/wizard-field.hbs b/assets/javascripts/wizard/templates/components/wizard-field.hbs index e0dd1551..efda8629 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field.hbs @@ -1,5 +1,5 @@ {{#if field.image}} From 47eed48ad5d8d3c9b7b06cc07d8a33e86b7eae53 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 15 Jun 2022 09:10:33 +0200 Subject: [PATCH 11/30] Update wizard-i18n.js.es6 --- assets/javascripts/wizard/lib/wizard-i18n.js.es6 | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/assets/javascripts/wizard/lib/wizard-i18n.js.es6 b/assets/javascripts/wizard/lib/wizard-i18n.js.es6 index fdefab77..9b9f8dd9 100644 --- a/assets/javascripts/wizard/lib/wizard-i18n.js.es6 +++ b/assets/javascripts/wizard/lib/wizard-i18n.js.es6 @@ -13,6 +13,11 @@ const getThemeId = () => { } }; +const getThemeKey = (key) => { + const themeId = getThemeId(); + return `theme_translations.${themeId}.${key}`; +}; + const translationExists = (key) => { return ( I18n.findTranslation(key, { locale: I18n.locale }) || @@ -20,13 +25,20 @@ const translationExists = (key) => { ); }; +const translatedText = (key, value) => { + const themeKey = getThemeKey(key); + return translationExists(themeKey) ? I18n.t(themeKey) : value; +}; + +export { translatedText }; + const WizardI18n = (key, params = {}) => { const themeId = getThemeId(); if (!themeId) { return I18n.t(key, params); } - const themeKey = `theme_translations.${themeId}.${key}`; + let themeKey = getThemeKey(key); if (translationExists(themeKey)) { return I18n.t(themeKey, params); From 3136b779b40a0a8423acabfce7554e1b3e6339fe Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 15 Jun 2022 09:14:42 +0200 Subject: [PATCH 12/30] More fixes from the cherry picks --- assets/javascripts/wizard-qunit.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/wizard-qunit.js b/assets/javascripts/wizard-qunit.js index 2866cb5d..8aaf23ab 100644 --- a/assets/javascripts/wizard-qunit.js +++ b/assets/javascripts/wizard-qunit.js @@ -1,11 +1,12 @@ +//= require env +//= require jquery.debug +//= require ember.debug //= require route-recognizer //= require fake_xml_http_request //= require pretender //= require qunit //= require ember-qunit //= require test-shims -//= require jquery.debug -//= require ember.debug //= require ember-template-compiler //= require_tree ./wizard/tests/fixtures From 7cf99f2391af39e20d61fbcd990540a4ca2d8a97 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 15 Jun 2022 09:57:20 +0200 Subject: [PATCH 13/30] Final cherry pick fixes --- app/controllers/custom_wizard/wizard.rb | 8 +++----- plugin.rb | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/app/controllers/custom_wizard/wizard.rb b/app/controllers/custom_wizard/wizard.rb index 11df97d7..bb53670b 100644 --- a/app/controllers/custom_wizard/wizard.rb +++ b/app/controllers/custom_wizard/wizard.rb @@ -6,7 +6,7 @@ class CustomWizard::WizardController < ::ActionController::Base include CanonicalURL::ControllerExtensions include GlobalPath - prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'views')) + prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'app', 'views')) layout :set_wizard_layout before_action :preload_wizard_json @@ -47,10 +47,8 @@ class CustomWizard::WizardController < ::ActionController::Base result = { success: 'OK' } if current_user && wizard.can_access? - submission = wizard.current_submission - - if submission.present? && submission.redirect_to - result.merge!(redirect_to: submission.redirect_to) + if redirect_to = wizard.current_submission&.redirect_to + result.merge!(redirect_to: redirect_to) end wizard.cleanup_on_skip! diff --git a/plugin.rb b/plugin.rb index 6f5719fb..41d7a1db 100644 --- a/plugin.rb +++ b/plugin.rb @@ -52,6 +52,24 @@ class ::Sprockets::DirectiveProcessor end end +## Override necessary due to 'assets/javascripts/wizard', particularly its tests. +def each_globbed_asset + if @path + root_path = "#{File.dirname(@path)}/assets/javascripts/discourse" + + Dir.glob(["#{root_path}/**/*"]).sort.each do |f| + f_str = f.to_s + if File.directory?(f) + yield [f, true] + elsif f_str.end_with?(".js.es6") || f_str.end_with?(".hbs") || f_str.end_with?(".hbr") + yield [f, false] + elsif transpile_js && f_str.end_with?(".js") + yield [f, false] + end + end + end +end + after_initialize do %w[ ../lib/custom_wizard/engine.rb From aea2df9b06e91717f2d24cc5d85908c609f369fc Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 15 Jun 2022 09:59:40 +0200 Subject: [PATCH 14/30] Bump version --- plugin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.rb b/plugin.rb index 41d7a1db..bdb86b64 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-custom-wizard # about: Create custom wizards -# version: 1.18.4 +# version: 1.19.0 # authors: Angus McLeod # url: https://github.com/paviliondev/discourse-custom-wizard # contact emails: angus@thepavilion.io From 70329f209a88b64d1cb8121fbe4cbb6ad370dccb Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 17 Jun 2022 16:00:22 +0200 Subject: [PATCH 15/30] COMPATIBILITY: The core wizard is now an ember addon --- assets/javascripts/wizard-custom.js | 3 +- assets/javascripts/wizard-shims.js | 47 +++++++++++++++++++ assets/javascripts/wizard-vendor.js | 10 ++++ .../wizard/components/validator.js.es6 | 2 +- assets/javascripts/wizard/lib/ajax.js.es6 | 33 +++++++++++++ .../lib/initialize/patch-components.js.es6 | 4 +- .../javascripts/wizard/lib/load-script.js.es6 | 2 +- .../wizard/mixins/valid-state.js.es6 | 36 ++++++++++++++ assets/javascripts/wizard/models/field.js.es6 | 2 +- assets/javascripts/wizard/models/step.js.es6 | 4 +- .../javascripts/wizard/models/wizard.js.es6 | 2 +- plugin.rb | 2 +- 12 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 assets/javascripts/wizard-shims.js create mode 100644 assets/javascripts/wizard-vendor.js create mode 100644 assets/javascripts/wizard/lib/ajax.js.es6 create mode 100644 assets/javascripts/wizard/mixins/valid-state.js.es6 diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js index 667d23fd..1e487273 100644 --- a/assets/javascripts/wizard-custom.js +++ b/assets/javascripts/wizard-custom.js @@ -1,8 +1,6 @@ //= require_tree_discourse truth-helpers/addon //= require_tree_discourse discourse-common/addon //= require_tree_discourse select-kit/addon -//= require_tree_discourse wizard/lib -//= require_tree_discourse wizard/mixins //= require_tree_discourse discourse/app/lib //= require_tree_discourse discourse/app/mixins @@ -76,6 +74,7 @@ //= require_tree ./wizard/controllers //= require_tree ./wizard/helpers //= require_tree ./wizard/lib +//= require_tree ./wizard/mixins //= require_tree ./wizard/models //= require_tree ./wizard/routes //= require_tree ./wizard/templates diff --git a/assets/javascripts/wizard-shims.js b/assets/javascripts/wizard-shims.js new file mode 100644 index 00000000..b5063933 --- /dev/null +++ b/assets/javascripts/wizard-shims.js @@ -0,0 +1,47 @@ +define("@popperjs/core", ["exports"], function (__exports__) { + __exports__.default = window.Popper; + __exports__.createPopper = window.Popper.createPopper; + __exports__.defaultModifiers = window.Popper.defaultModifiers; + __exports__.popperGenerator = window.Popper.popperGenerator; +}); + +define("tippy.js", ["exports"], function (__exports__) { + __exports__.default = window.tippy; +}); + +define("@uppy/core", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.Core; + __exports__.BasePlugin = window.Uppy.Core.BasePlugin; +}); + +define("@uppy/aws-s3", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.AwsS3; +}); + +define("@uppy/aws-s3-multipart", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.AwsS3Multipart; +}); + +define("@uppy/xhr-upload", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.XHRUpload; +}); + +define("@uppy/drop-target", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.DropTarget; +}); + +define("@uppy/utils/lib/delay", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.Utils.delay; +}); + +define("@uppy/utils/lib/EventTracker", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.Utils.EventTracker; +}); + +define("@uppy/utils/lib/AbortController", ["exports"], function (__exports__) { + __exports__.AbortController = + window.Uppy.Utils.AbortControllerLib.AbortController; + __exports__.AbortSignal = window.Uppy.Utils.AbortControllerLib.AbortSignal; + __exports__.createAbortError = + window.Uppy.Utils.AbortControllerLib.createAbortError; +}); diff --git a/assets/javascripts/wizard-vendor.js b/assets/javascripts/wizard-vendor.js new file mode 100644 index 00000000..5d175a6b --- /dev/null +++ b/assets/javascripts/wizard-vendor.js @@ -0,0 +1,10 @@ +//= require ember_jquery +//= require template_include.js +//= require uppy.js +//= require bootstrap-modal.js +//= require bootbox.js +//= require virtual-dom +//= require virtual-dom-amd +//= require popper.js +//= require tippy.umd.js +//= require wizard-shims diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index aa68660c..2e8498ef 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -1,7 +1,7 @@ import Component from "@ember/component"; import { equal } from "@ember/object/computed"; import { ajax } from "discourse/lib/ajax"; -import { getToken } from "wizard/lib/ajax"; +import { getToken } from "../lib/ajax"; export default Component.extend({ classNames: ["validator"], diff --git a/assets/javascripts/wizard/lib/ajax.js.es6 b/assets/javascripts/wizard/lib/ajax.js.es6 new file mode 100644 index 00000000..3889069f --- /dev/null +++ b/assets/javascripts/wizard/lib/ajax.js.es6 @@ -0,0 +1,33 @@ +import { Promise } from "rsvp"; +import getUrl from "discourse-common/lib/get-url"; +import jQuery from "jquery"; +import { run } from "@ember/runloop"; + +let token; + +export function getToken() { + if (!token) { + token = document.querySelector('meta[name="csrf-token"]')?.content; + } + + return token; +} + +export function ajax(args) { + let url; + + if (arguments.length === 2) { + url = arguments[0]; + args = arguments[1]; + } else { + url = args.url; + } + + return new Promise((resolve, reject) => { + args.headers = { "X-CSRF-Token": getToken() }; + args.success = (data) => run(null, resolve, data); + args.error = (xhr) => run(null, reject, xhr); + args.url = getUrl(url); + jQuery.ajax(args); + }); +} diff --git a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 index 9d13160e..8cb5e4b3 100644 --- a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 @@ -1,6 +1,8 @@ export default { run(app, container) { - const getToken = requirejs("wizard/lib/ajax").getToken; + const getToken = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/lib/ajax" + ).getToken; const isTesting = requirejs("discourse-common/config/environment") .isTesting; diff --git a/assets/javascripts/wizard/lib/load-script.js.es6 b/assets/javascripts/wizard/lib/load-script.js.es6 index d0b07c26..aeabdf06 100644 --- a/assets/javascripts/wizard/lib/load-script.js.es6 +++ b/assets/javascripts/wizard/lib/load-script.js.es6 @@ -1,4 +1,4 @@ -import { ajax } from "wizard/lib/ajax"; +import { ajax } from "./ajax"; import getURL, { getURLWithCDN } from "discourse-common/lib/get-url"; import { run } from "@ember/runloop"; import { Promise } from "rsvp"; diff --git a/assets/javascripts/wizard/mixins/valid-state.js.es6 b/assets/javascripts/wizard/mixins/valid-state.js.es6 new file mode 100644 index 00000000..ca86d7e4 --- /dev/null +++ b/assets/javascripts/wizard/mixins/valid-state.js.es6 @@ -0,0 +1,36 @@ +import discourseComputed from "discourse-common/utils/decorators"; + +export const States = { + UNCHECKED: 0, + INVALID: 1, + VALID: 2, +}; + +export default { + _validState: null, + errorDescription: null, + + init() { + this._super(...arguments); + this.set("_validState", States.UNCHECKED); + }, + + @discourseComputed("_validState") + valid: (state) => state === States.VALID, + + @discourseComputed("_validState") + invalid: (state) => state === States.INVALID, + + @discourseComputed("_validState") + unchecked: (state) => state === States.UNCHECKED, + + setValid(valid, description) { + this.set("_validState", valid ? States.VALID : States.INVALID); + + if (!valid && description && description.length) { + this.set("errorDescription", description); + } else { + this.set("errorDescription", null); + } + }, +}; diff --git a/assets/javascripts/wizard/models/field.js.es6 b/assets/javascripts/wizard/models/field.js.es6 index 2b88140e..9d0be1ff 100644 --- a/assets/javascripts/wizard/models/field.js.es6 +++ b/assets/javascripts/wizard/models/field.js.es6 @@ -1,5 +1,5 @@ import EmberObject from "@ember/object"; -import ValidState from "wizard/mixins/valid-state"; +import ValidState from "discourse/plugins/discourse-custom-wizard/wizard/mixins/valid-state"; import discourseComputed from "discourse-common/utils/decorators"; import { translatedText } from "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n"; diff --git a/assets/javascripts/wizard/models/step.js.es6 b/assets/javascripts/wizard/models/step.js.es6 index 36503276..87f2d4b6 100644 --- a/assets/javascripts/wizard/models/step.js.es6 +++ b/assets/javascripts/wizard/models/step.js.es6 @@ -1,6 +1,6 @@ import EmberObject from "@ember/object"; -import ValidState from "wizard/mixins/valid-state"; -import { ajax } from "wizard/lib/ajax"; +import ValidState from "discourse/plugins/discourse-custom-wizard/wizard/mixins/valid-state"; +import { ajax } from "../lib/ajax"; import discourseComputed from "discourse-common/utils/decorators"; import { translatedText } from "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n"; import { later } from "@ember/runloop"; diff --git a/assets/javascripts/wizard/models/wizard.js.es6 b/assets/javascripts/wizard/models/wizard.js.es6 index 15bcbb8c..d742c244 100644 --- a/assets/javascripts/wizard/models/wizard.js.es6 +++ b/assets/javascripts/wizard/models/wizard.js.es6 @@ -1,7 +1,7 @@ import { default as computed } from "discourse-common/utils/decorators"; import getUrl from "discourse-common/lib/get-url"; import Field from "./field"; -import { ajax } from "wizard/lib/ajax"; +import { ajax } from "../lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; import Step from "./step"; import EmberObject from "@ember/object"; diff --git a/plugin.rb b/plugin.rb index bdb86b64..a0e6dd7b 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-custom-wizard # about: Create custom wizards -# version: 1.19.0 +# version: 1.20.0 # authors: Angus McLeod # url: https://github.com/paviliondev/discourse-custom-wizard # contact emails: angus@thepavilion.io From ce3d2ced557e60146f82c742030b24f42c4395c6 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 22 Jun 2022 09:14:55 +0200 Subject: [PATCH 16/30] COMPATIBILITY: Various core file changes The completion of the move to ember-cli in core requires a move to an independent asset pipeline. The wizard app itself will be upgraded to ember-cli when I have time. See further: - https://github.com/discourse/discourse/commit/fcb4e5a1a15631cef9c20489866a68600ecf768d - https://github.com/discourse/discourse/commit/1b4692039e89392731ca5f75cfc4c4febaa3fa9a - https://github.com/discourse/discourse/commit/a69b9147107aa7858044f0db666da5b727980638 --- .github/workflows/plugin-tests.yml | 5 - app/views/layouts/wizard.html.erb | 4 +- assets/javascripts/legacy/discourse-loader.js | 432 + assets/javascripts/legacy/discourse-shims.js | 67 + .../javascripts/legacy/ember_include.js.erb | 7 + assets/javascripts/legacy/ember_jquery.js | 5 + assets/javascripts/legacy/env.js | 3 + .../javascripts/legacy/handlebars.runtime.js | 1800 +++ assets/javascripts/legacy/itsatrap.js | 1159 ++ assets/javascripts/legacy/jquery.js | 10872 ++++++++++++++++ assets/javascripts/legacy/popper.js | 1948 +++ .../legacy/set-prototype-polyfill.js | 8 + assets/javascripts/legacy/template_include.js | 2 + assets/javascripts/legacy/tippy.umd.js | 2496 ++++ assets/javascripts/legacy/uppy.js | 7616 +++++++++++ assets/javascripts/legacy/virtual-dom-amd.js | 4 + assets/javascripts/legacy/virtual-dom.js | 1668 +++ assets/javascripts/wizard-custom.js | 12 +- assets/javascripts/wizard-vendor.js | 16 +- assets/stylesheets/wizard/custom/field.scss | 2 +- assets/stylesheets/wizard/custom/mobile.scss | 2 +- assets/stylesheets/wizard/custom/step.scss | 4 +- assets/stylesheets/wizard/custom/wizard.scss | 8 +- assets/stylesheets/wizard/wizard_custom.scss | 5 + 24 files changed, 28114 insertions(+), 31 deletions(-) create mode 100644 assets/javascripts/legacy/discourse-loader.js create mode 100644 assets/javascripts/legacy/discourse-shims.js create mode 100644 assets/javascripts/legacy/ember_include.js.erb create mode 100644 assets/javascripts/legacy/ember_jquery.js create mode 100644 assets/javascripts/legacy/env.js create mode 100644 assets/javascripts/legacy/handlebars.runtime.js create mode 100644 assets/javascripts/legacy/itsatrap.js create mode 100644 assets/javascripts/legacy/jquery.js create mode 100644 assets/javascripts/legacy/popper.js create mode 100644 assets/javascripts/legacy/set-prototype-polyfill.js create mode 100644 assets/javascripts/legacy/template_include.js create mode 100644 assets/javascripts/legacy/tippy.umd.js create mode 100644 assets/javascripts/legacy/uppy.js create mode 100644 assets/javascripts/legacy/virtual-dom-amd.js create mode 100644 assets/javascripts/legacy/virtual-dom.js diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 497486ea..a7951188 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -132,8 +132,3 @@ jobs: export COVERAGE=1 fi bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}] - - - name: Plugin QUnit - if: matrix.build_type == 'frontend' - run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 QUNIT_SKIP_CORE=1 bin/rake qunit:test['600000','/w/qunit'] - timeout-minutes: 10 diff --git a/app/views/layouts/wizard.html.erb b/app/views/layouts/wizard.html.erb index f909ae84..cab255c3 100644 --- a/app/views/layouts/wizard.html.erb +++ b/app/views/layouts/wizard.html.erb @@ -15,7 +15,7 @@ <%= preload_script "wizard-raw-templates" %> <%= preload_script "wizard-plugin" %> <%= preload_script "pretty-text-bundle" %> - + <%= preload_script_url ExtraLocalesController.url("wizard") %> <%= csrf_meta_tags %> <%- unless customization_disabled? %> @@ -34,7 +34,7 @@ <%= wizard_page_title %> - + <%- unless customization_disabled? %> <%= raw wizard_theme_lookup("header") %> <%- end %> diff --git a/assets/javascripts/legacy/discourse-loader.js b/assets/javascripts/legacy/discourse-loader.js new file mode 100644 index 00000000..edcbf27f --- /dev/null +++ b/assets/javascripts/legacy/discourse-loader.js @@ -0,0 +1,432 @@ +var define, requirejs; + +(function () { + let JS_MODULES = {}; + let ALIASES = { + "ember-addons/ember-computed-decorators": + "discourse-common/utils/decorators", + "discourse/lib/raw-templates": "discourse-common/lib/raw-templates", + "preload-store": "discourse/lib/preload-store", + "fixtures/user_fixtures": "discourse/tests/fixtures/user-fixtures", + }; + let ALIAS_PREPEND = { + fixtures: "discourse/tests/", + helpers: "discourse/tests/", + }; + + // In future versions of ember we don't need this + if (typeof Ember !== "undefined") { + JS_MODULES = { + jquery: { default: $ }, + "@ember/array": { + default: Ember.Array, + A: Ember.A, + isArray: Ember.isArray, + }, + "@ember/array/proxy": { + default: Ember.ArrayProxy, + }, + "@ember/component": { + default: Ember.Component, + }, + "@ember/controller": { + default: Ember.Controller, + inject: Ember.inject.controller, + }, + "@ember/debug": { + assert: Ember.assert, + runInDebug: Ember.runInDebug, + warn: Ember.warn, + }, + "@ember/object": { + action: Ember._action, + default: Ember.Object, + get: Ember.get, + getProperties: Ember.getProperties, + set: Ember.set, + setProperties: Ember.setProperties, + computed: Ember.computed, + defineProperty: Ember.defineProperty, + observer: Ember.observer, + }, + "@ember/object/computed": { + alias: Ember.computed.alias, + and: Ember.computed.and, + bool: Ember.computed.bool, + collect: Ember.computed.collect, + deprecatingAlias: Ember.computed.deprecatingAlias, + empty: Ember.computed.empty, + equal: Ember.computed.equal, + filter: Ember.computed.filter, + filterBy: Ember.computed.filterBy, + gt: Ember.computed.gt, + gte: Ember.computed.gte, + intersect: Ember.computed.intersect, + lt: Ember.computed.lt, + lte: Ember.computed.lte, + map: Ember.computed.map, + mapBy: Ember.computed.mapBy, + match: Ember.computed.match, + max: Ember.computed.max, + min: Ember.computed.min, + none: Ember.computed.none, + not: Ember.computed.not, + notEmpty: Ember.computed.notEmpty, + oneWay: Ember.computed.oneWay, + or: Ember.computed.or, + readOnly: Ember.computed.readOnly, + reads: Ember.computed.reads, + setDiff: Ember.computed.setDiff, + sort: Ember.computed.sort, + sum: Ember.computed.sum, + union: Ember.computed.union, + uniq: Ember.computed.uniq, + uniqBy: Ember.computed.uniqBy, + }, + "@ember/object/mixin": { default: Ember.Mixin }, + "@ember/object/proxy": { default: Ember.ObjectProxy }, + "@ember/object/promise-proxy-mixin": { default: Ember.PromiseProxyMixin }, + "@ember/object/evented": { + default: Ember.Evented, + on: Ember.on, + }, + "@ember/routing/route": { default: Ember.Route }, + "@ember/routing/router": { default: Ember.Router }, + "@ember/runloop": { + bind: Ember.run.bind, + cancel: Ember.run.cancel, + debounce: Ember.testing ? Ember.run : Ember.run.debounce, + later: Ember.run.later, + next: Ember.run.next, + once: Ember.run.once, + run: Ember.run, + schedule: Ember.run.schedule, + scheduleOnce: Ember.run.scheduleOnce, + throttle: Ember.run.throttle, + }, + "@ember/service": { + default: Ember.Service, + inject: Ember.inject.service, + }, + "@ember/utils": { + isBlank: Ember.isBlank, + isEmpty: Ember.isEmpty, + isNone: Ember.isNone, + isPresent: Ember.isPresent, + }, + rsvp: { + asap: Ember.RSVP.asap, + all: Ember.RSVP.all, + allSettled: Ember.RSVP.allSettled, + race: Ember.RSVP.race, + hash: Ember.RSVP.hash, + hashSettled: Ember.RSVP.hashSettled, + rethrow: Ember.RSVP.rethrow, + defer: Ember.RSVP.defer, + denodeify: Ember.RSVP.denodeify, + resolve: Ember.RSVP.resolve, + reject: Ember.RSVP.reject, + map: Ember.RSVP.map, + filter: Ember.RSVP.filter, + default: Ember.RSVP, + Promise: Ember.RSVP.Promise, + EventTarget: Ember.RSVP.EventTarget, + }, + "@ember/string": { + w: Ember.String.w, + dasherize: Ember.String.dasherize, + decamelize: Ember.String.decamelize, + camelize: Ember.String.camelize, + classify: Ember.String.classify, + underscore: Ember.String.underscore, + capitalize: Ember.String.capitalize, + }, + "@ember/template": { + htmlSafe: Ember.String.htmlSafe, + }, + "@ember/application": { + default: Ember.Application, + setOwner: Ember.setOwner, + getOwner: Ember.getOwner, + }, + "@ember/component/helper": { + default: Ember.Helper, + }, + "@ember/component/text-field": { + default: Ember.TextField, + }, + "@ember/component/text-area": { + default: Ember.TextArea, + }, + "@ember/error": { + default: Ember.error, + }, + "@ember/object/internals": { + guidFor: Ember.guidFor, + }, + "@ember/test": { + registerWaiter: Ember.Test && Ember.Test.registerWaiter, + unregisterWaiter: Ember.Test && Ember.Test.unregisterWaiter, + }, + I18n: { + // eslint-disable-next-line + default: I18n, + }, + }; + } + + let _isArray; + if (!Array.isArray) { + _isArray = function (x) { + return Object.prototype.toString.call(x) === "[object Array]"; + }; + } else { + _isArray = Array.isArray; + } + + let registry = {}; + let seen = {}; + let FAILED = false; + + let uuid = 0; + + function tryFinally(tryable, finalizer) { + try { + return tryable(); + } finally { + finalizer(); + } + } + + function unsupportedModule(length) { + throw new Error( + "an unsupported module was defined, expected `define(name, deps, module)` instead got: `" + + length + + "` arguments to define`" + ); + } + + function deprecatedModule(depricated, useInstead) { + let warning = "[DEPRECATION] `" + depricated + "` is deprecated."; + if (useInstead) { + warning += " Please use `" + useInstead + "` instead."; + } + // eslint-disable-next-line no-console + console.warn(warning); + } + + let defaultDeps = ["require", "exports", "module"]; + + function Module(name, deps, callback, exports) { + this.id = uuid++; + this.name = name; + this.deps = !deps.length && callback.length ? defaultDeps : deps; + this.exports = exports || {}; + this.callback = callback; + this.state = undefined; + this._require = undefined; + } + + Module.prototype.makeRequire = function () { + let name = transformForAliases(this.name); + + return ( + this._require || + (this._require = function (dep) { + return requirejs(resolve(dep, name)); + }) + ); + }; + + define = function (name, deps, callback) { + if (arguments.length < 2) { + unsupportedModule(arguments.length); + } + + if (!_isArray(deps)) { + callback = deps; + deps = []; + } + + registry[name] = new Module(name, deps, callback); + }; + + // we don't support all of AMD + // define.amd = {}; + // we will support petals... + define.petal = {}; + + function Alias(path) { + this.name = path; + } + + define.alias = function (path) { + return new Alias(path); + }; + + function reify(mod, name, rseen) { + let deps = mod.deps; + let length = deps.length; + let reified = new Array(length); + let dep; + // TODO: new Module + // TODO: seen refactor + let module = {}; + + for (let i = 0, l = length; i < l; i++) { + dep = deps[i]; + if (dep === "exports") { + module.exports = reified[i] = rseen; + } else if (dep === "require") { + reified[i] = mod.makeRequire(); + } else if (dep === "module") { + mod.exports = rseen; + module = reified[i] = mod; + } else { + reified[i] = requireFrom(resolve(dep, name), name); + } + } + + return { + deps: reified, + module, + }; + } + + function requireFrom(name, origin) { + name = transformForAliases(name); + + if (name === "discourse") { + // eslint-disable-next-line no-console + console.log( + "discourse has been moved to `discourse/app` - please update your code" + ); + name = "discourse/app"; + } + + if (name === "discourse/models/input-validation") { + // eslint-disable-next-line no-console + console.log( + "input-validation has been removed and should be replaced with `@ember/object`" + ); + name = "@ember/object"; + } + + let mod = JS_MODULES[name] || registry[name]; + if (!mod) { + throw new Error( + "Could not find module `" + name + "` imported from `" + origin + "`" + ); + } + return requirejs(name); + } + + function missingModule(name) { + throw new Error("Could not find module " + name); + } + + function transformForAliases(name) { + let alias = ALIASES[name]; + if (!alias) { + let segment = name.split("/")[0]; + let prepend = ALIAS_PREPEND[segment]; + if (!prepend) { + return name; + } + alias = prepend + name; + } + deprecatedModule(name, alias); + return alias; + } + + requirejs = require = function (name) { + name = transformForAliases(name); + if (JS_MODULES[name]) { + return JS_MODULES[name]; + } + + let mod = registry[name]; + + if (mod && mod.callback instanceof Alias) { + mod = registry[mod.callback.name]; + } + + if (!mod) { + missingModule(name); + } + + if (mod.state !== FAILED && seen.hasOwnProperty(name)) { + return seen[name]; + } + + let reified; + let module; + let loaded = false; + + seen[name] = {}; // placeholder for run-time cycles + + tryFinally( + function () { + reified = reify(mod, name, seen[name]); + module = mod.callback.apply(this, reified.deps); + loaded = true; + }, + function () { + if (!loaded) { + mod.state = FAILED; + } + } + ); + + let obj; + if (module === undefined && reified.module.exports) { + obj = reified.module.exports; + } else { + obj = seen[name] = module; + } + + if ( + obj !== null && + (typeof obj === "object" || typeof obj === "function") && + obj["default"] === undefined + ) { + obj["default"] = obj; + } + + return (seen[name] = obj); + }; + window.requireModule = requirejs; + + function resolve(child, name) { + if (child.charAt(0) !== ".") { + return child; + } + + let parts = child.split("/"); + let nameParts = name.split("/"); + let parentBase = nameParts.slice(0, -1); + + for (let i = 0, l = parts.length; i < l; i++) { + let part = parts[i]; + + if (part === "..") { + if (parentBase.length === 0) { + throw new Error("Cannot access parent module of root"); + } + parentBase.pop(); + } else if (part === ".") { + continue; + } else { + parentBase.push(part); + } + } + + return parentBase.join("/"); + } + + requirejs.entries = requirejs._eak_seen = registry; + requirejs.clear = function () { + requirejs.entries = requirejs._eak_seen = registry = {}; + seen = {}; + }; +})(); diff --git a/assets/javascripts/legacy/discourse-shims.js b/assets/javascripts/legacy/discourse-shims.js new file mode 100644 index 00000000..7794b099 --- /dev/null +++ b/assets/javascripts/legacy/discourse-shims.js @@ -0,0 +1,67 @@ +define("message-bus-client", ["exports"], function (__exports__) { + __exports__.default = window.MessageBus; +}); + +define("ember-buffered-proxy/proxy", ["exports"], function (__exports__) { + __exports__.default = window.BufferedProxy; +}); + +define("bootbox", ["exports"], function (__exports__) { + __exports__.default = window.bootbox; +}); + +define("xss", ["exports"], function (__exports__) { + __exports__.default = window.filterXSS; +}); + +define("@discourse/itsatrap", ["exports"], function (__exports__) { + __exports__.default = window.ItsATrap; +}); + +define("@popperjs/core", ["exports"], function (__exports__) { + __exports__.default = window.Popper; + __exports__.createPopper = window.Popper.createPopper; + __exports__.defaultModifiers = window.Popper.defaultModifiers; + __exports__.popperGenerator = window.Popper.popperGenerator; +}); + +define("tippy.js", ["exports"], function (__exports__) { + __exports__.default = window.tippy; +}); + +define("@uppy/core", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.Core; + __exports__.BasePlugin = window.Uppy.Core.BasePlugin; +}); + +define("@uppy/aws-s3", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.AwsS3; +}); + +define("@uppy/aws-s3-multipart", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.AwsS3Multipart; +}); + +define("@uppy/xhr-upload", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.XHRUpload; +}); + +define("@uppy/drop-target", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.DropTarget; +}); + +define("@uppy/utils/lib/delay", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.Utils.delay; +}); + +define("@uppy/utils/lib/EventTracker", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.Utils.EventTracker; +}); + +define("@uppy/utils/lib/AbortController", ["exports"], function (__exports__) { + __exports__.AbortController = + window.Uppy.Utils.AbortControllerLib.AbortController; + __exports__.AbortSignal = window.Uppy.Utils.AbortControllerLib.AbortSignal; + __exports__.createAbortError = + window.Uppy.Utils.AbortControllerLib.createAbortError; +}); diff --git a/assets/javascripts/legacy/ember_include.js.erb b/assets/javascripts/legacy/ember_include.js.erb new file mode 100644 index 00000000..56883bef --- /dev/null +++ b/assets/javascripts/legacy/ember_include.js.erb @@ -0,0 +1,7 @@ +<% + if @force_ember_debug || Rails.env.development? || Rails.env.test? + require_asset("ember.debug.js") + else + require_asset("ember.prod.js") + end +%> diff --git a/assets/javascripts/legacy/ember_jquery.js b/assets/javascripts/legacy/ember_jquery.js new file mode 100644 index 00000000..7506aa62 --- /dev/null +++ b/assets/javascripts/legacy/ember_jquery.js @@ -0,0 +1,5 @@ +//= require legacy/set-prototype-polyfill +//= require legacy/env +//= require legacy/jquery +//= require legacy/ember_include +//= require legacy/discourse-loader diff --git a/assets/javascripts/legacy/env.js b/assets/javascripts/legacy/env.js new file mode 100644 index 00000000..8b897bbe --- /dev/null +++ b/assets/javascripts/legacy/env.js @@ -0,0 +1,3 @@ +window.ENV = {}; +window.EmberENV = window.EmberENV || {}; +window.EmberENV.FORCE_JQUERY = true; diff --git a/assets/javascripts/legacy/handlebars.runtime.js b/assets/javascripts/legacy/handlebars.runtime.js new file mode 100644 index 00000000..da377f0f --- /dev/null +++ b/assets/javascripts/legacy/handlebars.runtime.js @@ -0,0 +1,1800 @@ +/**! + + @license + handlebars v4.7.7 + +Copyright (C) 2011-2019 by Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + {module.exports = factory();} + else if(typeof define === 'function' && define.amd) + {define([], factory);} + else if(typeof exports === 'object') + {exports["Handlebars"] = factory();} + else + {root["Handlebars"] = factory();} +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ let installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ {return installedModules[moduleId].exports;} + +/******/ // Create a new module (and put it into the cache) +/******/ let module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _interopRequireWildcard = __webpack_require__(1)['default']; + + let _interopRequireDefault = __webpack_require__(2)['default']; + + exports.__esModule = true; + + let _handlebarsBase = __webpack_require__(3); + + let base = _interopRequireWildcard(_handlebarsBase); + + // Each of these augment the Handlebars object. No need to setup here. + // (This is done to easily share code between commonjs and browse envs) + + let _handlebarsSafeString = __webpack_require__(36); + + let _handlebarsSafeString2 = _interopRequireDefault(_handlebarsSafeString); + + let _handlebarsException = __webpack_require__(5); + + let _handlebarsException2 = _interopRequireDefault(_handlebarsException); + + let _handlebarsUtils = __webpack_require__(4); + + let Utils = _interopRequireWildcard(_handlebarsUtils); + + let _handlebarsRuntime = __webpack_require__(37); + + let runtime = _interopRequireWildcard(_handlebarsRuntime); + + let _handlebarsNoConflict = __webpack_require__(43); + + let _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict); + + // For compatibility and usage outside of module systems, make the Handlebars object a namespace + function create() { + let hb = new base.HandlebarsEnvironment(); + + Utils.extend(hb, base); + hb.SafeString = _handlebarsSafeString2['default']; + hb.Exception = _handlebarsException2['default']; + hb.Utils = Utils; + hb.escapeExpression = Utils.escapeExpression; + + hb.VM = runtime; + hb.template = function (spec) { + return runtime.template(spec, hb); + }; + + return hb; + } + + let inst = create(); + inst.create = create; + + _handlebarsNoConflict2['default'](inst); + + inst['default'] = inst; + + exports['default'] = inst; + module.exports = exports['default']; + +/***/ }), +/* 1 */ +/***/ (function(module, exports) { + + "use strict"; + + exports["default"] = function (obj) { + if (obj && obj.__esModule) { + return obj; + } else { + let newObj = {}; + + if (obj != null) { + for (let key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) {newObj[key] = obj[key];} + } + } + + newObj["default"] = obj; + return newObj; + } + }; + + exports.__esModule = true; + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + + "use strict"; + + exports["default"] = function (obj) { + return obj && obj.__esModule ? obj : { + "default": obj + }; + }; + + exports.__esModule = true; + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _interopRequireDefault = __webpack_require__(2)['default']; + + exports.__esModule = true; + exports.HandlebarsEnvironment = HandlebarsEnvironment; + + let _utils = __webpack_require__(4); + + let _exception = __webpack_require__(5); + + let _exception2 = _interopRequireDefault(_exception); + + let _helpers = __webpack_require__(9); + + let _decorators = __webpack_require__(29); + + let _logger = __webpack_require__(31); + + let _logger2 = _interopRequireDefault(_logger); + + let _internalProtoAccess = __webpack_require__(32); + + let VERSION = '4.7.7'; + exports.VERSION = VERSION; + let COMPILER_REVISION = 8; + exports.COMPILER_REVISION = COMPILER_REVISION; + let LAST_COMPATIBLE_COMPILER_REVISION = 7; + + exports.LAST_COMPATIBLE_COMPILER_REVISION = LAST_COMPATIBLE_COMPILER_REVISION; + let REVISION_CHANGES = { + 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it + 2: '== 1.0.0-rc.3', + 3: '== 1.0.0-rc.4', + 4: '== 1.x.x', + 5: '== 2.0.0-alpha.x', + 6: '>= 2.0.0-beta.1', + 7: '>= 4.0.0 <4.3.0', + 8: '>= 4.3.0' + }; + + exports.REVISION_CHANGES = REVISION_CHANGES; + let objectType = '[object Object]'; + + function HandlebarsEnvironment(helpers, partials, decorators) { + this.helpers = helpers || {}; + this.partials = partials || {}; + this.decorators = decorators || {}; + + _helpers.registerDefaultHelpers(this); + _decorators.registerDefaultDecorators(this); + } + + HandlebarsEnvironment.prototype = { + constructor: HandlebarsEnvironment, + + logger: _logger2['default'], + log: _logger2['default'].log, + + registerHelper: function registerHelper(name, fn) { + if (_utils.toString.call(name) === objectType) { + if (fn) { + throw new _exception2['default']('Arg not supported with multiple helpers'); + } + _utils.extend(this.helpers, name); + } else { + this.helpers[name] = fn; + } + }, + unregisterHelper: function unregisterHelper(name) { + delete this.helpers[name]; + }, + + registerPartial: function registerPartial(name, partial) { + if (_utils.toString.call(name) === objectType) { + _utils.extend(this.partials, name); + } else { + if (typeof partial === 'undefined') { + throw new _exception2['default']('Attempting to register a partial called "' + name + '" as undefined'); + } + this.partials[name] = partial; + } + }, + unregisterPartial: function unregisterPartial(name) { + delete this.partials[name]; + }, + + registerDecorator: function registerDecorator(name, fn) { + if (_utils.toString.call(name) === objectType) { + if (fn) { + throw new _exception2['default']('Arg not supported with multiple decorators'); + } + _utils.extend(this.decorators, name); + } else { + this.decorators[name] = fn; + } + }, + unregisterDecorator: function unregisterDecorator(name) { + delete this.decorators[name]; + }, + /** + * Reset the memory of illegal property accesses that have already been logged. + * @deprecated should only be used in handlebars test-cases + */ + resetLoggedPropertyAccesses: function resetLoggedPropertyAccesses() { + _internalProtoAccess.resetLoggedProperties(); + } + }; + + let log = _logger2['default'].log; + + exports.log = log; + exports.createFrame = _utils.createFrame; + exports.logger = _logger2['default']; + +/***/ }), +/* 4 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + exports.extend = extend; + exports.indexOf = indexOf; + exports.escapeExpression = escapeExpression; + exports.isEmpty = isEmpty; + exports.createFrame = createFrame; + exports.blockParams = blockParams; + exports.appendContextPath = appendContextPath; + let escape = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`', + '=': '=' + }; + + let badChars = /[&<>"'`=]/g, + possible = /[&<>"'`=]/; + + function escapeChar(chr) { + return escape[chr]; + } + + function extend(obj /* , ...source */) { + for (let i = 1; i < arguments.length; i++) { + for (let key in arguments[i]) { + if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { + obj[key] = arguments[i][key]; + } + } + } + + return obj; + } + + let toString = Object.prototype.toString; + + exports.toString = toString; + // Sourced from lodash + // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt + /* eslint-disable func-style */ + let isFunction = function isFunction(value) { + return typeof value === 'function'; + }; + // fallback for older versions of Chrome and Safari + /* istanbul ignore next */ + if (isFunction(/x/)) { + exports.isFunction = isFunction = function (value) { + return typeof value === 'function' && toString.call(value) === '[object Function]'; + }; + } + exports.isFunction = isFunction; + + /* eslint-enable func-style */ + + /* istanbul ignore next */ + let isArray = Array.isArray || function (value) { + return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false; + }; + + exports.isArray = isArray; + // Older IE versions do not directly support indexOf so we must implement our own, sadly. + + function indexOf(array, value) { + for (let i = 0, len = array.length; i < len; i++) { + if (array[i] === value) { + return i; + } + } + return -1; + } + + function escapeExpression(string) { + if (typeof string !== 'string') { + // don't escape SafeStrings, since they're already safe + if (string && string.toHTML) { + return string.toHTML(); + } else if (string == null) { + return ''; + } else if (!string) { + return string + ''; + } + + // Force a string conversion as this will be done by the append regardless and + // the regex test will do this transparently behind the scenes, causing issues if + // an object's to string has escaped characters in it. + string = '' + string; + } + + if (!possible.test(string)) { + return string; + } + return string.replace(badChars, escapeChar); + } + + function isEmpty(value) { + if (!value && value !== 0) { + return true; + } else if (isArray(value) && value.length === 0) { + return true; + } else { + return false; + } + } + + function createFrame(object) { + let frame = extend({}, object); + frame._parent = object; + return frame; + } + + function blockParams(params, ids) { + params.path = ids; + return params; + } + + function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _Object$defineProperty = __webpack_require__(6)['default']; + + exports.__esModule = true; + let errorProps = ['description', 'fileName', 'lineNumber', 'endLineNumber', 'message', 'name', 'number', 'stack']; + + function Exception(message, node) { + let loc = node && node.loc, + line = undefined, + endLineNumber = undefined, + column = undefined, + endColumn = undefined; + + if (loc) { + line = loc.start.line; + endLineNumber = loc.end.line; + column = loc.start.column; + endColumn = loc.end.column; + + message += ' - ' + line + ':' + column; + } + + let tmp = Error.prototype.constructor.call(this, message); + + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (let idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } + + /* istanbul ignore else */ + if (Error.captureStackTrace) { + Error.captureStackTrace(this, Exception); + } + + try { + if (loc) { + this.lineNumber = line; + this.endLineNumber = endLineNumber; + + // Work around issue under safari where we can't directly set the column value + /* istanbul ignore next */ + if (_Object$defineProperty) { + Object.defineProperty(this, 'column', { + value: column, + enumerable: true + }); + Object.defineProperty(this, 'endColumn', { + value: endColumn, + enumerable: true + }); + } else { + this.column = column; + this.endColumn = endColumn; + } + } + } catch (nop) { + /* Ignore if the browser is very particular */ + } + } + + Exception.prototype = new Error(); + + exports['default'] = Exception; + module.exports = exports['default']; + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(7), __esModule: true }; + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + + let $ = __webpack_require__(8); + module.exports = function defineProperty(it, key, desc){ + return $.setDesc(it, key, desc); + }; + +/***/ }), +/* 8 */ +/***/ (function(module, exports) { + + let $Object = Object; + module.exports = { + create: $Object.create, + getProto: $Object.getPrototypeOf, + isEnum: {}.propertyIsEnumerable, + getDesc: $Object.getOwnPropertyDescriptor, + setDesc: $Object.defineProperty, + setDescs: $Object.defineProperties, + getKeys: $Object.keys, + getNames: $Object.getOwnPropertyNames, + getSymbols: $Object.getOwnPropertySymbols, + each: [].forEach + }; + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _interopRequireDefault = __webpack_require__(2)['default']; + + exports.__esModule = true; + exports.registerDefaultHelpers = registerDefaultHelpers; + exports.moveHelperToHooks = moveHelperToHooks; + + let _helpersBlockHelperMissing = __webpack_require__(10); + + let _helpersBlockHelperMissing2 = _interopRequireDefault(_helpersBlockHelperMissing); + + let _helpersEach = __webpack_require__(11); + + let _helpersEach2 = _interopRequireDefault(_helpersEach); + + let _helpersHelperMissing = __webpack_require__(24); + + let _helpersHelperMissing2 = _interopRequireDefault(_helpersHelperMissing); + + let _helpersIf = __webpack_require__(25); + + let _helpersIf2 = _interopRequireDefault(_helpersIf); + + let _helpersLog = __webpack_require__(26); + + let _helpersLog2 = _interopRequireDefault(_helpersLog); + + let _helpersLookup = __webpack_require__(27); + + let _helpersLookup2 = _interopRequireDefault(_helpersLookup); + + let _helpersWith = __webpack_require__(28); + + let _helpersWith2 = _interopRequireDefault(_helpersWith); + + function registerDefaultHelpers(instance) { + _helpersBlockHelperMissing2['default'](instance); + _helpersEach2['default'](instance); + _helpersHelperMissing2['default'](instance); + _helpersIf2['default'](instance); + _helpersLog2['default'](instance); + _helpersLookup2['default'](instance); + _helpersWith2['default'](instance); + } + + function moveHelperToHooks(instance, helperName, keepHelper) { + if (instance.helpers[helperName]) { + instance.hooks[helperName] = instance.helpers[helperName]; + if (!keepHelper) { + delete instance.helpers[helperName]; + } + } + } + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + let _utils = __webpack_require__(4); + + exports['default'] = function (instance) { + instance.registerHelper('blockHelperMissing', function (context, options) { + let inverse = options.inverse, + fn = options.fn; + + if (context === true) { + return fn(this); + } else if (context === false || context == null) { + return inverse(this); + } else if (_utils.isArray(context)) { + if (context.length > 0) { + if (options.ids) { + options.ids = [options.name]; + } + + return instance.helpers.each(context, options); + } else { + return inverse(this); + } + } else { + if (options.data && options.ids) { + let data = _utils.createFrame(options.data); + data.contextPath = _utils.appendContextPath(options.data.contextPath, options.name); + options = { data }; + } + + return fn(context, options); + } + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + let _Object$keys = __webpack_require__(12)['default']; + + let _interopRequireDefault = __webpack_require__(2)['default']; + + exports.__esModule = true; + + let _utils = __webpack_require__(4); + + let _exception = __webpack_require__(5); + + let _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('each', function (context, options) { + if (!options) { + throw new _exception2['default']('Must pass iterator to #each'); + } + + let fn = options.fn, + inverse = options.inverse, + i = 0, + ret = '', + data = undefined, + contextPath = undefined; + + if (options.data && options.ids) { + contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; + } + + if (_utils.isFunction(context)) { + context = context.call(this); + } + + if (options.data) { + data = _utils.createFrame(options.data); + } + + function execIteration(field, index, last) { + if (data) { + data.key = field; + data.index = index; + data.first = index === 0; + data.last = !!last; + + if (contextPath) { + data.contextPath = contextPath + field; + } + } + + ret = ret + fn(context[field], { + data, + blockParams: _utils.blockParams([context[field], field], [contextPath + field, null]) + }); + } + + if (context && typeof context === 'object') { + if (_utils.isArray(context)) { + for (var j = context.length; i < j; i++) { + if (i in context) { + execIteration(i, i, i === context.length - 1); + } + } + } else if (global.Symbol && context[global.Symbol.iterator]) { + let newContext = []; + let iterator = context[global.Symbol.iterator](); + for (let it = iterator.next(); !it.done; it = iterator.next()) { + newContext.push(it.value); + } + context = newContext; + for (var j = context.length; i < j; i++) { + execIteration(i, i, i === context.length - 1); + } + } else { + (function () { + let priorKey = undefined; + + _Object$keys(context).forEach(function (key) { + // We're running the iterations one step out of sync so we can detect + // the last iteration without have to scan the object twice and create + // an itermediate keys array. + if (priorKey !== undefined) { + execIteration(priorKey, i - 1); + } + priorKey = key; + i++; + }); + if (priorKey !== undefined) { + execIteration(priorKey, i - 1, true); + } + })(); + } + } + + if (i === 0) { + ret = inverse(this); + } + + return ret; + }); + }; + + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; })())); + +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(13), __esModule: true }; + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + + __webpack_require__(14); + module.exports = __webpack_require__(20).Object.keys; + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + + // 19.1.2.14 Object.keys(O) + let toObject = __webpack_require__(15); + + __webpack_require__(17)('keys', function($keys){ + return function keys(it){ + return $keys(toObject(it)); + }; + }); + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + + // 7.1.13 ToObject(argument) + let defined = __webpack_require__(16); + module.exports = function(it){ + return Object(defined(it)); + }; + +/***/ }), +/* 16 */ +/***/ (function(module, exports) { + + // 7.2.1 RequireObjectCoercible(argument) + module.exports = function(it){ + if(it == undefined){throw TypeError("Can't call method on " + it);} + return it; + }; + +/***/ }), +/* 17 */ +/***/ (function(module, exports, __webpack_require__) { + + // most Object methods by ES6 should accept primitives + let $export = __webpack_require__(18) + , core = __webpack_require__(20) + , fails = __webpack_require__(23); + module.exports = function(KEY, exec){ + let fn = (core.Object || {})[KEY] || Object[KEY] + , exp = {}; + exp[KEY] = exec(fn); + $export($export.S + $export.F * fails(function(){ fn(1); }), 'Object', exp); + }; + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + + let global = __webpack_require__(19) + , core = __webpack_require__(20) + , ctx = __webpack_require__(21) + , PROTOTYPE = 'prototype'; + + var $export = function(type, name, source){ + let IS_FORCED = type & $export.F + , IS_GLOBAL = type & $export.G + , IS_STATIC = type & $export.S + , IS_PROTO = type & $export.P + , IS_BIND = type & $export.B + , IS_WRAP = type & $export.W + , exports = IS_GLOBAL ? core : core[name] || (core[name] = {}) + , target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE] + , key, own, out; + if(IS_GLOBAL){source = name;} + for(key in source){ + // contains in native + own = !IS_FORCED && target && key in target; + if(own && key in exports){continue;} + // export native or passed + out = own ? target[key] : source[key]; + // prevent global pollution for namespaces + exports[key] = IS_GLOBAL && typeof target[key] !== 'function' ? source[key] + // bind timers to global for call from export context + : IS_BIND && own ? ctx(out, global) + // wrap global constructors for prevent change them in library + : IS_WRAP && target[key] == out ? (function(C){ + let F = function(param){ + return this instanceof C ? new C(param) : C(param); + }; + F[PROTOTYPE] = C[PROTOTYPE]; + return F; + // make static versions for prototype methods + })(out) : IS_PROTO && typeof out === 'function' ? ctx(Function.call, out) : out; + if(IS_PROTO){(exports[PROTOTYPE] || (exports[PROTOTYPE] = {}))[key] = out;} + } + }; + // type bitmap + $export.F = 1; // forced + $export.G = 2; // global + $export.S = 4; // static + $export.P = 8; // proto + $export.B = 16; // bind + $export.W = 32; // wrap + module.exports = $export; + +/***/ }), +/* 19 */ +/***/ (function(module, exports) { + + // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 + let global = module.exports = typeof window !== 'undefined' && window.Math == Math + ? window : typeof self !== 'undefined' && self.Math == Math ? self : Function('return this')(); + if(typeof __g === 'number'){__g = global;} // eslint-disable-line no-undef + +/***/ }), +/* 20 */ +/***/ (function(module, exports) { + + let core = module.exports = {version: '1.2.6'}; + if(typeof __e === 'number'){__e = core;} // eslint-disable-line no-undef + +/***/ }), +/* 21 */ +/***/ (function(module, exports, __webpack_require__) { + + // optional / simple context binding + let aFunction = __webpack_require__(22); + module.exports = function(fn, that, length){ + aFunction(fn); + if(that === undefined){return fn;} + switch(length){ + case 1: return function(a){ + return fn.call(that, a); + }; + case 2: return function(a, b){ + return fn.call(that, a, b); + }; + case 3: return function(a, b, c){ + return fn.call(that, a, b, c); + }; + } + return function(/* ...args */){ + return fn.apply(that, arguments); + }; + }; + +/***/ }), +/* 22 */ +/***/ (function(module, exports) { + + module.exports = function(it){ + if(typeof it !== 'function'){throw TypeError(it + ' is not a function!');} + return it; + }; + +/***/ }), +/* 23 */ +/***/ (function(module, exports) { + + module.exports = function(exec){ + try { + return !!exec(); + } catch(e){ + return true; + } + }; + +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _interopRequireDefault = __webpack_require__(2)['default']; + + exports.__esModule = true; + + let _exception = __webpack_require__(5); + + let _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('helperMissing', function () /* [args, ]options */{ + if (arguments.length === 1) { + // A missing field in a {{foo}} construct. + return undefined; + } else { + // Someone is actually trying to call something, blow up. + throw new _exception2['default']('Missing helper: "' + arguments[arguments.length - 1].name + '"'); + } + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _interopRequireDefault = __webpack_require__(2)['default']; + + exports.__esModule = true; + + let _utils = __webpack_require__(4); + + let _exception = __webpack_require__(5); + + let _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('if', function (conditional, options) { + if (arguments.length != 2) { + throw new _exception2['default']('#if requires exactly one argument'); + } + if (_utils.isFunction(conditional)) { + conditional = conditional.call(this); + } + + // Default behavior is to render the positive path if the value is truthy and not empty. + // The `includeZero` option may be set to treat the condtional as purely not empty based on the + // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative. + if (!options.hash.includeZero && !conditional || _utils.isEmpty(conditional)) { + return options.inverse(this); + } else { + return options.fn(this); + } + }); + + instance.registerHelper('unless', function (conditional, options) { + if (arguments.length != 2) { + throw new _exception2['default']('#unless requires exactly one argument'); + } + return instance.helpers['if'].call(this, conditional, { + fn: options.inverse, + inverse: options.fn, + hash: options.hash + }); + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 26 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (instance) { + instance.registerHelper('log', function () /* message, options */{ + let args = [undefined], + options = arguments[arguments.length - 1]; + for (let i = 0; i < arguments.length - 1; i++) { + args.push(arguments[i]); + } + + let level = 1; + if (options.hash.level != null) { + level = options.hash.level; + } else if (options.data && options.data.level != null) { + level = options.data.level; + } + args[0] = level; + + instance.log.apply(instance, args); + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 27 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (instance) { + instance.registerHelper('lookup', function (obj, field, options) { + if (!obj) { + // Note for 5.0: Change to "obj == null" in 5.0 + return obj; + } + return options.lookupProperty(obj, field); + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 28 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _interopRequireDefault = __webpack_require__(2)['default']; + + exports.__esModule = true; + + let _utils = __webpack_require__(4); + + let _exception = __webpack_require__(5); + + let _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('with', function (context, options) { + if (arguments.length != 2) { + throw new _exception2['default']('#with requires exactly one argument'); + } + if (_utils.isFunction(context)) { + context = context.call(this); + } + + let fn = options.fn; + + if (!_utils.isEmpty(context)) { + let data = options.data; + if (options.data && options.ids) { + data = _utils.createFrame(options.data); + data.contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]); + } + + return fn(context, { + data, + blockParams: _utils.blockParams([context], [data && data.contextPath]) + }); + } else { + return options.inverse(this); + } + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _interopRequireDefault = __webpack_require__(2)['default']; + + exports.__esModule = true; + exports.registerDefaultDecorators = registerDefaultDecorators; + + let _decoratorsInline = __webpack_require__(30); + + let _decoratorsInline2 = _interopRequireDefault(_decoratorsInline); + + function registerDefaultDecorators(instance) { + _decoratorsInline2['default'](instance); + } + +/***/ }), +/* 30 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + let _utils = __webpack_require__(4); + + exports['default'] = function (instance) { + instance.registerDecorator('inline', function (fn, props, container, options) { + let ret = fn; + if (!props.partials) { + props.partials = {}; + ret = function (context, options) { + // Create a new partials stack frame prior to exec. + let original = container.partials; + container.partials = _utils.extend({}, original, props.partials); + let ret = fn(context, options); + container.partials = original; + return ret; + }; + } + + props.partials[options.args[0]] = options.fn; + + return ret; + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 31 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + let _utils = __webpack_require__(4); + + var logger = { + methodMap: ['debug', 'info', 'warn', 'error'], + level: 'info', + + // Maps a given level value to the `methodMap` indexes above. + lookupLevel: function lookupLevel(level) { + if (typeof level === 'string') { + let levelMap = _utils.indexOf(logger.methodMap, level.toLowerCase()); + if (levelMap >= 0) { + level = levelMap; + } else { + level = parseInt(level, 10); + } + } + + return level; + }, + + // Can be overridden in the host environment + log: function log(level) { + level = logger.lookupLevel(level); + + if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) { + let method = logger.methodMap[level]; + // eslint-disable-next-line no-console + if (!console[method]) { + method = 'log'; + } + + for (var _len = arguments.length, message = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + message[_key - 1] = arguments[_key]; + } + + console[method].apply(console, message); // eslint-disable-line no-console + } + } + }; + + exports['default'] = logger; + module.exports = exports['default']; + +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _Object$create = __webpack_require__(33)['default']; + + let _Object$keys = __webpack_require__(12)['default']; + + let _interopRequireWildcard = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.createProtoAccessControl = createProtoAccessControl; + exports.resultIsAllowed = resultIsAllowed; + exports.resetLoggedProperties = resetLoggedProperties; + + let _createNewLookupObject = __webpack_require__(35); + + let _logger = __webpack_require__(31); + + let logger = _interopRequireWildcard(_logger); + + let loggedProperties = _Object$create(null); + + function createProtoAccessControl(runtimeOptions) { + let defaultMethodWhiteList = _Object$create(null); + defaultMethodWhiteList['constructor'] = false; + defaultMethodWhiteList['__defineGetter__'] = false; + defaultMethodWhiteList['__defineSetter__'] = false; + defaultMethodWhiteList['__lookupGetter__'] = false; + + let defaultPropertyWhiteList = _Object$create(null); + // eslint-disable-next-line no-proto + defaultPropertyWhiteList['__proto__'] = false; + + return { + properties: { + whitelist: _createNewLookupObject.createNewLookupObject(defaultPropertyWhiteList, runtimeOptions.allowedProtoProperties), + defaultValue: runtimeOptions.allowProtoPropertiesByDefault + }, + methods: { + whitelist: _createNewLookupObject.createNewLookupObject(defaultMethodWhiteList, runtimeOptions.allowedProtoMethods), + defaultValue: runtimeOptions.allowProtoMethodsByDefault + } + }; + } + + function resultIsAllowed(result, protoAccessControl, propertyName) { + if (typeof result === 'function') { + return checkWhiteList(protoAccessControl.methods, propertyName); + } else { + return checkWhiteList(protoAccessControl.properties, propertyName); + } + } + + function checkWhiteList(protoAccessControlForType, propertyName) { + if (protoAccessControlForType.whitelist[propertyName] !== undefined) { + return protoAccessControlForType.whitelist[propertyName] === true; + } + if (protoAccessControlForType.defaultValue !== undefined) { + return protoAccessControlForType.defaultValue; + } + logUnexpecedPropertyAccessOnce(propertyName); + return false; + } + + function logUnexpecedPropertyAccessOnce(propertyName) { + if (loggedProperties[propertyName] !== true) { + loggedProperties[propertyName] = true; + logger.log('error', 'Handlebars: Access has been denied to resolve the property "' + propertyName + '" because it is not an "own property" of its parent.\n' + 'You can add a runtime option to disable the check or this warning:\n' + 'See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details'); + } + } + + function resetLoggedProperties() { + _Object$keys(loggedProperties).forEach(function (propertyName) { + delete loggedProperties[propertyName]; + }); + } + +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(34), __esModule: true }; + +/***/ }), +/* 34 */ +/***/ (function(module, exports, __webpack_require__) { + + let $ = __webpack_require__(8); + module.exports = function create(P, D){ + return $.create(P, D); + }; + +/***/ }), +/* 35 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _Object$create = __webpack_require__(33)['default']; + + exports.__esModule = true; + exports.createNewLookupObject = createNewLookupObject; + + let _utils = __webpack_require__(4); + + /** + * Create a new object with "null"-prototype to avoid truthy results on prototype properties. + * The resulting object can be used with "object[property]" to check if a property exists + * @param {...object} sources a varargs parameter of source objects that will be merged + * @returns {object} + */ + + function createNewLookupObject() { + for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) { + sources[_key] = arguments[_key]; + } + + return _utils.extend.apply(undefined, [_Object$create(null)].concat(sources)); + } + +/***/ }), +/* 36 */ +/***/ (function(module, exports) { + + // Build out our basic SafeString type + 'use strict'; + + exports.__esModule = true; + function SafeString(string) { + this.string = string; + } + + SafeString.prototype.toString = SafeString.prototype.toHTML = function () { + return '' + this.string; + }; + + exports['default'] = SafeString; + module.exports = exports['default']; + +/***/ }), +/* 37 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + let _Object$seal = __webpack_require__(38)['default']; + + let _Object$keys = __webpack_require__(12)['default']; + + let _interopRequireWildcard = __webpack_require__(1)['default']; + + let _interopRequireDefault = __webpack_require__(2)['default']; + + exports.__esModule = true; + exports.checkRevision = checkRevision; + exports.template = template; + exports.wrapProgram = wrapProgram; + exports.resolvePartial = resolvePartial; + exports.invokePartial = invokePartial; + exports.noop = noop; + + let _utils = __webpack_require__(4); + + let Utils = _interopRequireWildcard(_utils); + + let _exception = __webpack_require__(5); + + let _exception2 = _interopRequireDefault(_exception); + + let _base = __webpack_require__(3); + + let _helpers = __webpack_require__(9); + + let _internalWrapHelper = __webpack_require__(42); + + let _internalProtoAccess = __webpack_require__(32); + + function checkRevision(compilerInfo) { + let compilerRevision = compilerInfo && compilerInfo[0] || 1, + currentRevision = _base.COMPILER_REVISION; + + if (compilerRevision >= _base.LAST_COMPATIBLE_COMPILER_REVISION && compilerRevision <= _base.COMPILER_REVISION) { + return; + } + + if (compilerRevision < _base.LAST_COMPATIBLE_COMPILER_REVISION) { + let runtimeVersions = _base.REVISION_CHANGES[currentRevision], + compilerVersions = _base.REVISION_CHANGES[compilerRevision]; + throw new _exception2['default']('Template was precompiled with an older version of Handlebars than the current runtime. ' + 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').'); + } else { + // Use the embedded version info since the runtime doesn't know about this revision yet + throw new _exception2['default']('Template was precompiled with a newer version of Handlebars than the current runtime. ' + 'Please update your runtime to a newer version (' + compilerInfo[1] + ').'); + } + } + + function template(templateSpec, env) { + /* istanbul ignore next */ + if (!env) { + throw new _exception2['default']('No environment passed to template'); + } + if (!templateSpec || !templateSpec.main) { + throw new _exception2['default']('Unknown template object: ' + typeof templateSpec); + } + + templateSpec.main.decorator = templateSpec.main_d; + + // Note: Using env.VM references rather than local var references throughout this section to allow + // for external users to override these as pseudo-supported APIs. + env.VM.checkRevision(templateSpec.compiler); + + // backwards compatibility for precompiled templates with compiler-version 7 (<4.3.0) + let templateWasPrecompiledWithCompilerV7 = templateSpec.compiler && templateSpec.compiler[0] === 7; + + function invokePartialWrapper(partial, context, options) { + if (options.hash) { + context = Utils.extend({}, context, options.hash); + if (options.ids) { + options.ids[0] = true; + } + } + partial = env.VM.resolvePartial.call(this, partial, context, options); + + let extendedOptions = Utils.extend({}, options, { + hooks: this.hooks, + protoAccessControl: this.protoAccessControl + }); + + let result = env.VM.invokePartial.call(this, partial, context, extendedOptions); + + if (result == null && env.compile) { + options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env); + result = options.partials[options.name](context, extendedOptions); + } + if (result != null) { + if (options.indent) { + let lines = result.split('\n'); + for (let i = 0, l = lines.length; i < l; i++) { + if (!lines[i] && i + 1 === l) { + break; + } + + lines[i] = options.indent + lines[i]; + } + result = lines.join('\n'); + } + return result; + } else { + throw new _exception2['default']('The partial ' + options.name + ' could not be compiled when running in runtime-only mode'); + } + } + + // Just add water + var container = { + strict: function strict(obj, name, loc) { + if (!obj || !(name in obj)) { + throw new _exception2['default']('"' + name + '" not defined in ' + obj, { + loc + }); + } + return container.lookupProperty(obj, name); + }, + lookupProperty: function lookupProperty(parent, propertyName) { + let result = parent[propertyName]; + if (result == null) { + return result; + } + if (Object.prototype.hasOwnProperty.call(parent, propertyName)) { + return result; + } + + if (_internalProtoAccess.resultIsAllowed(result, container.protoAccessControl, propertyName)) { + return result; + } + return undefined; + }, + lookup: function lookup(depths, name) { + let len = depths.length; + for (let i = 0; i < len; i++) { + let result = depths[i] && container.lookupProperty(depths[i], name); + if (result != null) { + return depths[i][name]; + } + } + }, + lambda: function lambda(current, context) { + return typeof current === 'function' ? current.call(context) : current; + }, + + escapeExpression: Utils.escapeExpression, + invokePartial: invokePartialWrapper, + + fn: function fn(i) { + let ret = templateSpec[i]; + ret.decorator = templateSpec[i + '_d']; + return ret; + }, + + programs: [], + program: function program(i, data, declaredBlockParams, blockParams, depths) { + let programWrapper = this.programs[i], + fn = this.fn(i); + if (data || depths || blockParams || declaredBlockParams) { + programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths); + } else if (!programWrapper) { + programWrapper = this.programs[i] = wrapProgram(this, i, fn); + } + return programWrapper; + }, + + data: function data(value, depth) { + while (value && depth--) { + value = value._parent; + } + return value; + }, + mergeIfNeeded: function mergeIfNeeded(param, common) { + let obj = param || common; + + if (param && common && param !== common) { + obj = Utils.extend({}, common, param); + } + + return obj; + }, + // An empty object to use as replacement for null-contexts + nullContext: _Object$seal({}), + + noop: env.VM.noop, + compilerInfo: templateSpec.compiler + }; + + function ret(context) { + let options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + let data = options.data; + + ret._setup(options); + if (!options.partial && templateSpec.useData) { + data = initData(context, data); + } + let depths = undefined, + blockParams = templateSpec.useBlockParams ? [] : undefined; + if (templateSpec.useDepths) { + if (options.depths) { + depths = context != options.depths[0] ? [context].concat(options.depths) : options.depths; + } else { + depths = [context]; + } + } + + function main(context /*, options*/) { + return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths); + } + + main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams); + return main(context, options); + } + + ret.isTop = true; + + ret._setup = function (options) { + if (!options.partial) { + let mergedHelpers = Utils.extend({}, env.helpers, options.helpers); + wrapHelpersToPassLookupProperty(mergedHelpers, container); + container.helpers = mergedHelpers; + + if (templateSpec.usePartial) { + // Use mergeIfNeeded here to prevent compiling global partials multiple times + container.partials = container.mergeIfNeeded(options.partials, env.partials); + } + if (templateSpec.usePartial || templateSpec.useDecorators) { + container.decorators = Utils.extend({}, env.decorators, options.decorators); + } + + container.hooks = {}; + container.protoAccessControl = _internalProtoAccess.createProtoAccessControl(options); + + let keepHelperInHelpers = options.allowCallsToHelperMissing || templateWasPrecompiledWithCompilerV7; + _helpers.moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers); + _helpers.moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers); + } else { + container.protoAccessControl = options.protoAccessControl; // internal option + container.helpers = options.helpers; + container.partials = options.partials; + container.decorators = options.decorators; + container.hooks = options.hooks; + } + }; + + ret._child = function (i, data, blockParams, depths) { + if (templateSpec.useBlockParams && !blockParams) { + throw new _exception2['default']('must pass block params'); + } + if (templateSpec.useDepths && !depths) { + throw new _exception2['default']('must pass parent depths'); + } + + return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths); + }; + return ret; + } + + function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) { + function prog(context) { + let options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + let currentDepths = depths; + if (depths && context != depths[0] && !(context === container.nullContext && depths[0] === null)) { + currentDepths = [context].concat(depths); + } + + return fn(container, context, container.helpers, container.partials, options.data || data, blockParams && [options.blockParams].concat(blockParams), currentDepths); + } + + prog = executeDecorators(fn, prog, container, depths, data, blockParams); + + prog.program = i; + prog.depth = depths ? depths.length : 0; + prog.blockParams = declaredBlockParams || 0; + return prog; + } + + /** + * This is currently part of the official API, therefore implementation details should not be changed. + */ + + function resolvePartial(partial, context, options) { + if (!partial) { + if (options.name === '@partial-block') { + partial = options.data['partial-block']; + } else { + partial = options.partials[options.name]; + } + } else if (!partial.call && !options.name) { + // This is a dynamic partial that returned a string + options.name = partial; + partial = options.partials[partial]; + } + return partial; + } + + function invokePartial(partial, context, options) { + // Use the current closure context to save the partial-block if this partial + let currentPartialBlock = options.data && options.data['partial-block']; + options.partial = true; + if (options.ids) { + options.data.contextPath = options.ids[0] || options.data.contextPath; + } + + let partialBlock = undefined; + if (options.fn && options.fn !== noop) { + (function () { + options.data = _base.createFrame(options.data); + // Wrapper function to get access to currentPartialBlock from the closure + let fn = options.fn; + partialBlock = options.data['partial-block'] = function partialBlockWrapper(context) { + let options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + // Restore the partial-block from the closure for the execution of the block + // i.e. the part inside the block of the partial call. + options.data = _base.createFrame(options.data); + options.data['partial-block'] = currentPartialBlock; + return fn(context, options); + }; + if (fn.partials) { + options.partials = Utils.extend({}, options.partials, fn.partials); + } + })(); + } + + if (partial === undefined && partialBlock) { + partial = partialBlock; + } + + if (partial === undefined) { + throw new _exception2['default']('The partial ' + options.name + ' could not be found'); + } else if (partial instanceof Function) { + return partial(context, options); + } + } + + function noop() { + return ''; + } + + function initData(context, data) { + if (!data || !('root' in data)) { + data = data ? _base.createFrame(data) : {}; + data.root = context; + } + return data; + } + + function executeDecorators(fn, prog, container, depths, data, blockParams) { + if (fn.decorator) { + let props = {}; + prog = fn.decorator(prog, props, container, depths && depths[0], data, blockParams, depths); + Utils.extend(prog, props); + } + return prog; + } + + function wrapHelpersToPassLookupProperty(mergedHelpers, container) { + _Object$keys(mergedHelpers).forEach(function (helperName) { + let helper = mergedHelpers[helperName]; + mergedHelpers[helperName] = passLookupPropertyOption(helper, container); + }); + } + + function passLookupPropertyOption(helper, container) { + let lookupProperty = container.lookupProperty; + return _internalWrapHelper.wrapHelper(helper, function (options) { + return Utils.extend({ lookupProperty }, options); + }); + } + +/***/ }), +/* 38 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(39), __esModule: true }; + +/***/ }), +/* 39 */ +/***/ (function(module, exports, __webpack_require__) { + + __webpack_require__(40); + module.exports = __webpack_require__(20).Object.seal; + +/***/ }), +/* 40 */ +/***/ (function(module, exports, __webpack_require__) { + + // 19.1.2.17 Object.seal(O) + let isObject = __webpack_require__(41); + + __webpack_require__(17)('seal', function($seal){ + return function seal(it){ + return $seal && isObject(it) ? $seal(it) : it; + }; + }); + +/***/ }), +/* 41 */ +/***/ (function(module, exports) { + + module.exports = function(it){ + return typeof it === 'object' ? it !== null : typeof it === 'function'; + }; + +/***/ }), +/* 42 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + exports.wrapHelper = wrapHelper; + + function wrapHelper(helper, transformOptionsFn) { + if (typeof helper !== 'function') { + // This should not happen, but apparently it does in https://github.com/wycats/handlebars.js/issues/1639 + // We try to make the wrapper least-invasive by not wrapping it, if the helper is not a function. + return helper; + } + let wrapper = function wrapper() /* dynamic arguments */{ + let options = arguments[arguments.length - 1]; + arguments[arguments.length - 1] = transformOptionsFn(options); + return helper.apply(this, arguments); + }; + return wrapper; + } + +/***/ }), +/* 43 */ +/***/ (function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + exports.__esModule = true; + + exports['default'] = function (Handlebars) { + /* istanbul ignore next */ + let root = typeof global !== 'undefined' ? global : window, + $Handlebars = root.Handlebars; + /* istanbul ignore next */ + Handlebars.noConflict = function () { + if (root.Handlebars === Handlebars) { + root.Handlebars = $Handlebars; + } + return Handlebars; + }; + }; + + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; })())); + +/***/ }) +/******/ ]); +}); +; diff --git a/assets/javascripts/legacy/itsatrap.js b/assets/javascripts/legacy/itsatrap.js new file mode 100644 index 00000000..e4f847e9 --- /dev/null +++ b/assets/javascripts/legacy/itsatrap.js @@ -0,0 +1,1159 @@ +/*global define:false */ +/** + * Copyright 2012-2017 Craig Campbell + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ItsATrap is a simple keyboard shortcut library for Javascript with + * no external dependencies + * + * @version 2.0.1 + * @url github.com/discourse/itsatrap + */ +(function(window, document, undefined) { + // Check if itsatrap is used inside browser, if not, return + if (!window) { + return; + } + + /** + * mapping of special keycodes to their corresponding keys + * + * everything in this dictionary cannot use keypress events + * so it has to be here to map to the correct keycodes for + * keyup/keydown events + * + * @type {Object} + */ + let _MAP = { + 8: "backspace", + 9: "tab", + 13: "enter", + 16: "shift", + 17: "ctrl", + 18: "alt", + 20: "capslock", + 27: "esc", + 32: "space", + 33: "pageup", + 34: "pagedown", + 35: "end", + 36: "home", + 37: "left", + 38: "up", + 39: "right", + 40: "down", + 45: "ins", + 46: "del", + 91: "meta", + 93: "meta", + 224: "meta" + }; + + /** + * mapping for special characters so they can support + * + * this dictionary is only used incase you want to bind a + * keyup or keydown event to one of these keys + * + * @type {Object} + */ + let _KEYCODE_MAP = { + 106: "*", + 107: "+", + 109: "-", + 110: ".", + 111: "/", + 186: ";", + 187: "=", + 188: ",", + 189: "-", + 190: ".", + 191: "/", + 192: "`", + 219: "[", + 220: "\\", + 221: "]", + 222: "'" + }; + + /** + * this is a mapping of keys that require shift on a US keypad + * back to the non shift equivelents + * + * this is so you can use keyup events with these keys + * + * note that this will only work reliably on US keyboards + * + * @type {Object} + */ + let _SHIFT_MAP = { + "~": "`", + "!": "1", + "@": "2", + "#": "3", + $: "4", + "%": "5", + "^": "6", + "&": "7", + "*": "8", + "(": "9", + ")": "0", + _: "-", + "+": "=", + ":": ";", + '"': "'", + "<": ",", + ">": ".", + "?": "/", + "|": "\\" + }; + + /** + * this is a list of special strings you can use to map + * to modifier keys when you specify your keyboard shortcuts + * + * @type {Object} + */ + let _SPECIAL_ALIASES = { + option: "alt", + command: "meta", + return: "enter", + escape: "esc", + plus: "+", + mod: /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? "meta" : "ctrl" + }; + + /** + * variable to store the flipped version of _MAP from above + * needed to check if we should use keypress or not when no action + * is specified + * + * @type {Object|undefined} + */ + let _REVERSE_MAP; + + /** + * holds a reference to global bindings + * + * @type {Object|undefined} + */ + let _globalCallbacks = {}; + + /** + * loop through the f keys, f1 to f19 and add them to the map + * programatically + */ + for (var i = 1; i < 20; ++i) { + _MAP[111 + i] = "f" + i; + } + + /** + * loop through to map numbers on the numeric keypad + */ + for (i = 0; i <= 9; ++i) { + // This needs to use a string cause otherwise since 0 is falsey + // itsatrap will never fire for numpad 0 pressed as part of a keydown + // event. + // + // @see https://github.com/ccampbell/itsatrap/pull/258 + _MAP[i + 96] = i.toString(); + } + + /** + * cross browser add event method + * + * @param {Element|HTMLDocument} object + * @param {string} type + * @param {EventListenerOrEventListenerObject} callback + * @returns void + */ + function _addEvent(object, type, callback) { + if (object.addEventListener) { + object.addEventListener(type, callback, false); + return; + } + + object.attachEvent("on" + type, callback); + } + + /** + * cross browser remove event method + * + * @param {Element|HTMLDocument} object + * @param {string} type + * @param {EventListenerOrEventListenerObject} callback + * @returns void + */ + function _removeEvent(object, type, callback) { + if (object.removeEventListener) { + object.removeEventListener(type, callback, false); + return; + } + object.detachEvent("on" + type, callback); + } + + /** + * takes the event and returns the key character + * + * @param {Event} e + * @return {string} + */ + function _characterFromEvent(e) { + // for keypress events we should return the character as is + if (e.type == "keypress") { + let character = String.fromCharCode(e.which); + + // if the shift key is not pressed then it is safe to assume + // that we want the character to be lowercase. this means if + // you accidentally have caps lock on then your key bindings + // will continue to work + // + // the only side effect that might not be desired is if you + // bind something like 'A' cause you want to trigger an + // event when capital A is pressed caps lock will no longer + // trigger the event. shift+a will though. + if (!e.shiftKey) { + character = character.toLowerCase(); + } + + return character; + } + + // for non keypress events the special maps are needed + if (_MAP[e.which]) { + return _MAP[e.which]; + } + + if (_KEYCODE_MAP[e.which]) { + return _KEYCODE_MAP[e.which]; + } + + // if it is not in the special map + + // with keydown and keyup events the character seems to always + // come in as an uppercase character whether you are pressing shift + // or not. we should make sure it is always lowercase for comparisons + return String.fromCharCode(e.which).toLowerCase(); + } + + /** + * checks if two arrays are equal + * + * @param {Array} modifiers1 + * @param {Array} modifiers2 + * @returns {boolean} + */ + function _modifiersMatch(modifiers1, modifiers2) { + return modifiers1.sort().join(",") === modifiers2.sort().join(","); + } + + /** + * takes a key event and figures out what the modifiers are + * + * @param {Event} e + * @returns {Array} + */ + function _eventModifiers(e) { + let modifiers = []; + + if (e.shiftKey) { + modifiers.push("shift"); + } + + if (e.altKey) { + modifiers.push("alt"); + } + + if (e.ctrlKey) { + modifiers.push("ctrl"); + } + + if (e.metaKey) { + modifiers.push("meta"); + } + + return modifiers; + } + + /** + * prevents default for this event + * + * @param {Event} e + * @returns void + */ + function _preventDefault(e) { + if (e.preventDefault) { + e.preventDefault(); + return; + } + + e.returnValue = false; + } + + /** + * stops propogation for this event + * + * @param {Event} e + * @returns void + */ + function _stopPropagation(e) { + if (e.stopPropagation) { + e.stopPropagation(); + return; + } + + e.cancelBubble = true; + } + + /** + * determines if the keycode specified is a modifier key or not + * + * @param {string} key + * @returns {boolean} + */ + function _isModifier(key) { + return key == "shift" || key == "ctrl" || key == "alt" || key == "meta"; + } + + /** + * reverses the map lookup so that we can look for specific keys + * to see what can and can't use keypress + * + * @return {Object} + */ + function _getReverseMap() { + if (!_REVERSE_MAP) { + _REVERSE_MAP = {}; + for (let key in _MAP) { + // pull out the numeric keypad from here cause keypress should + // be able to detect the keys from the character + if (key > 95 && key < 112) { + continue; + } + + if (_MAP.hasOwnProperty(key)) { + _REVERSE_MAP[_MAP[key]] = key; + } + } + } + return _REVERSE_MAP; + } + + /** + * picks the best action based on the key combination + * + * @param {string} key - character for key + * @param {Array} modifiers + * @param {string=} action passed in + */ + function _pickBestAction(key, modifiers, action) { + // if no action was picked in we should try to pick the one + // that we think would work best for this key + if (!action) { + action = _getReverseMap()[key] ? "keydown" : "keypress"; + } + + // modifier keys don't work as expected with keypress, + // switch to keydown + if (action == "keypress" && modifiers.length) { + action = "keydown"; + } + + return action; + } + + /** + * Converts from a string key combination to an array + * + * @param {string} combination like "command+shift+l" + * @return {Array} + */ + function _keysFromString(combination) { + if (combination === "+") { + return ["+"]; + } + + combination = combination.replace(/\+{2}/g, "+plus"); + return combination.split("+"); + } + + /** + * Gets info for a specific key combination + * + * @param {string} combination key combination ("command+s" or "a" or "*") + * @param {string=} action + * @returns {Object} + */ + function _getKeyInfo(combination, action) { + let keys; + let key; + let i; + let modifiers = []; + + // take the keys from this pattern and figure out what the actual + // pattern is all about + keys = _keysFromString(combination); + + for (i = 0; i < keys.length; ++i) { + key = keys[i]; + + // normalize key names + if (_SPECIAL_ALIASES[key]) { + key = _SPECIAL_ALIASES[key]; + } + + // if this is not a keypress event then we should + // be smart about using shift keys + // this will only work for US keyboards however + if (action && action != "keypress" && _SHIFT_MAP[key]) { + key = _SHIFT_MAP[key]; + modifiers.push("shift"); + } + + // if this key is a modifier then add it to the list of modifiers + if (_isModifier(key)) { + modifiers.push(key); + } + } + + // depending on what the key combination is + // we will try to pick the best event for it + action = _pickBestAction(key, modifiers, action); + + return { + key, + modifiers, + action + }; + } + + function _belongsTo(element, ancestor) { + if (element === null || element === document) { + return false; + } + + if (element === ancestor) { + return true; + } + + return _belongsTo(element.parentNode, ancestor); + } + + function ItsATrap(targetElement) { + let self = this; + + targetElement = targetElement || document; + + if (!(self instanceof ItsATrap)) { + return new ItsATrap(targetElement); + } + + /** + * element to attach key events to + * + * @type {Element} + */ + self.target = targetElement; + + /** + * a list of all the callbacks setup via ItsATrap.bind() + * + * @type {Object} + */ + self._callbacks = {}; + + /** + * direct map of string combinations to callbacks used for trigger() + * + * @type {Object} + */ + self._directMap = {}; + + /** + * keeps track of what level each sequence is at since multiple + * sequences can start out with the same sequence + * + * @type {Object} + */ + let _sequenceLevels = {}; + + /** + * variable to store the setTimeout call + * + * @type {null|number} + */ + let _resetTimer; + + /** + * temporary state where we will ignore the next keyup + * + * @type {boolean|string} + */ + let _ignoreNextKeyup = false; + + /** + * temporary state where we will ignore the next keypress + * + * @type {boolean} + */ + let _ignoreNextKeypress = false; + + /** + * are we currently inside of a sequence? + * type of action ("keyup" or "keydown" or "keypress") or false + * + * @type {boolean|string} + */ + let _nextExpectedAction = false; + + /** + * resets all sequence counters except for the ones passed in + * + * @param {Object} doNotReset + * @returns void + */ + function _resetSequences(doNotReset) { + doNotReset = doNotReset || {}; + + let activeSequences = false, + key; + + for (key in _sequenceLevels) { + if (doNotReset[key]) { + activeSequences = true; + continue; + } + _sequenceLevels[key] = 0; + } + + if (!activeSequences) { + _nextExpectedAction = false; + } + } + + /** + * finds all callbacks that match based on the keycode, modifiers, + * and action + * + * @param {string} character + * @param {Array} modifiers + * @param {Event|Object} e + * @param {string=} sequenceName - name of the sequence we are looking for + * @param {string=} combination + * @param {number=} level + * @returns {Array} + */ + function _getMatches( + character, + modifiers, + e, + sequenceName, + combination, + level + ) { + let i; + let callback; + let matches = []; + let action = e.type; + + // if there are no events related to this keycode + if (!self._callbacks[character]) { + return []; + } + + // if a modifier key is coming up on its own we should allow it + if (action == "keyup" && _isModifier(character)) { + modifiers = [character]; + } + + // loop through all callbacks for the key that was pressed + // and see if any of them match + for (i = 0; i < self._callbacks[character].length; ++i) { + callback = self._callbacks[character][i]; + + // if a sequence name is not specified, but this is a sequence at + // the wrong level then move onto the next match + if ( + !sequenceName && + callback.seq && + _sequenceLevels[callback.seq] != callback.level + ) { + continue; + } + + // if the action we are looking for doesn't match the action we got + // then we should keep going + if (action != callback.action) { + continue; + } + + // if this is a keypress event and the meta key and control key + // are not pressed that means that we need to only look at the + // character, otherwise check the modifiers as well + // + // chrome will not fire a keypress if meta or control is down + // safari will fire a keypress if meta or meta+shift is down + // firefox will fire a keypress if meta or control is down + if ( + (action == "keypress" && !e.metaKey && !e.ctrlKey) || + _modifiersMatch(modifiers, callback.modifiers) + ) { + // when you bind a combination or sequence a second time it + // should overwrite the first one. if a sequenceName or + // combination is specified in this call it does just that + // + // @todo make deleting its own method? + let deleteCombo = !sequenceName && callback.combo == combination; + let deleteSequence = + sequenceName && + callback.seq == sequenceName && + callback.level == level; + if (deleteCombo || deleteSequence) { + self._callbacks[character].splice(i, 1); + } + + matches.push(callback); + } + } + + return matches; + } + + /** + * actually calls the callback function + * + * if your callback function returns false this will use the jquery + * convention - prevent default and stop propogation on the event + * + * @param {Function} callback + * @param {Event} e + * @returns void + */ + function _fireCallback(callback, e, combo, sequence) { + // if this event should not happen stop here + if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) { + return; + } + + if (callback(e, combo) === false) { + _preventDefault(e); + _stopPropagation(e); + } + } + + /** + * handles a character key event + * + * @param {string} character + * @param {Array} modifiers + * @param {Event} e + * @returns void + */ + self._handleKey = function(character, modifiers, e) { + let callbacks = _getMatches(character, modifiers, e); + let i; + let doNotReset = {}; + let maxLevel = 0; + let processedSequenceCallback = false; + + // Calculate the maxLevel for sequences so we can only execute the longest callback sequence + for (i = 0; i < callbacks.length; ++i) { + if (callbacks[i].seq) { + maxLevel = Math.max(maxLevel, callbacks[i].level); + } + } + + // loop through matching callbacks for this key event + for (i = 0; i < callbacks.length; ++i) { + // fire for all sequence callbacks + // this is because if for example you have multiple sequences + // bound such as "g i" and "g t" they both need to fire the + // callback for matching g cause otherwise you can only ever + // match the first one + if (callbacks[i].seq) { + // only fire callbacks for the maxLevel to prevent + // subsequences from also firing + // + // for example 'a option b' should not cause 'option b' to fire + // even though 'option b' is part of the other sequence + // + // any sequences that do not match here will be discarded + // below by the _resetSequences call + if (callbacks[i].level != maxLevel) { + continue; + } + + processedSequenceCallback = true; + + // keep a list of which sequences were matches for later + doNotReset[callbacks[i].seq] = 1; + _fireCallback( + callbacks[i].callback, + e, + callbacks[i].combo, + callbacks[i].seq + ); + continue; + } + + // if there were no sequence matches but we are still here + // that means this is a regular match so we should fire that + if (!processedSequenceCallback) { + _fireCallback(callbacks[i].callback, e, callbacks[i].combo); + } + } + + // if the key you pressed matches the type of sequence without + // being a modifier (ie "keyup" or "keypress") then we should + // reset all sequences that were not matched by this event + // + // this is so, for example, if you have the sequence "h a t" and you + // type "h e a r t" it does not match. in this case the "e" will + // cause the sequence to reset + // + // modifier keys are ignored because you can have a sequence + // that contains modifiers such as "enter ctrl+space" and in most + // cases the modifier key will be pressed before the next key + // + // also if you have a sequence such as "ctrl+b a" then pressing the + // "b" key will trigger a "keypress" and a "keydown" + // + // the "keydown" is expected when there is a modifier, but the + // "keypress" ends up matching the _nextExpectedAction since it occurs + // after and that causes the sequence to reset + // + // we ignore keypresses in a sequence that directly follow a keydown + // for the same character + let ignoreThisKeypress = e.type == "keypress" && _ignoreNextKeypress; + if ( + e.type == _nextExpectedAction && + !_isModifier(character) && + !ignoreThisKeypress + ) { + _resetSequences(doNotReset); + } + + _ignoreNextKeypress = processedSequenceCallback && e.type == "keydown"; + }; + + /** + * handles a keydown event + * + * @param {Event} e + * @returns void + */ + self._handleKeyEvent = function(e) { + // normalize e.which for key events + // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion + if (typeof e.which !== "number") { + e.which = e.keyCode; + } + + let character = _characterFromEvent(e); + + // no character found then stop + if (!character) { + return; + } + + // need to use === for the character check because the character can be 0 + if (e.type == "keyup" && _ignoreNextKeyup === character) { + _ignoreNextKeyup = false; + return; + } + + self.handleKey(character, _eventModifiers(e), e); + }; + + /** + * called to set a 1 second timeout on the specified sequence + * + * this is so after each key press in the sequence you have 1 second + * to press the next key before you have to start over + * + * @returns void + */ + function _resetSequenceTimer() { + clearTimeout(_resetTimer); + _resetTimer = setTimeout(_resetSequences, 1000); + } + + /** + * binds a key sequence to an event + * + * @param {string} combo - combo specified in bind call + * @param {Array} keys + * @param {Function} callback + * @param {string=} action + * @returns void + */ + function _bindSequence(combo, keys, callback, action) { + // start off by adding a sequence level record for this combination + // and setting the level to 0 + _sequenceLevels[combo] = 0; + + /** + * callback to increase the sequence level for this sequence and reset + * all other sequences that were active + * + * @param {string} nextAction + * @returns {Function} + */ + function _increaseSequence(nextAction) { + return function() { + _nextExpectedAction = nextAction; + ++_sequenceLevels[combo]; + _resetSequenceTimer(); + }; + } + + /** + * wraps the specified callback inside of another function in order + * to reset all sequence counters as soon as this sequence is done + * + * @param {Event} e + * @returns void + */ + function _callbackAndReset(e) { + _fireCallback(callback, e, combo); + + // we should ignore the next key up if the action is key down + // or keypress. this is so if you finish a sequence and + // release the key the final key will not trigger a keyup + if (action !== "keyup") { + _ignoreNextKeyup = _characterFromEvent(e); + } + + // weird race condition if a sequence ends with the key + // another sequence begins with + setTimeout(_resetSequences, 10); + } + + // loop through keys one at a time and bind the appropriate callback + // function. for any key leading up to the final one it should + // increase the sequence. after the final, it should reset all sequences + // + // if an action is specified in the original bind call then that will + // be used throughout. otherwise we will pass the action that the + // next key in the sequence should match. this allows a sequence + // to mix and match keypress and keydown events depending on which + // ones are better suited to the key provided + for (let i = 0; i < keys.length; ++i) { + let isFinal = i + 1 === keys.length; + let wrappedCallback = isFinal + ? _callbackAndReset + : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action); + _bindSingle(keys[i], wrappedCallback, action, combo, i); + } + } + + /** + * binds a single keyboard combination + * + * @param {string} combination + * @param {Function} callback + * @param {string=} action + * @param {string=} sequenceName - name of sequence if part of sequence + * @param {number=} level - what part of the sequence the command is + * @returns void + */ + function _bindSingle(combination, callback, action, sequenceName, level) { + // store a direct mapped reference for use with ItsATrap.trigger + self._directMap[combination + ":" + action] = callback; + + // make sure multiple spaces in a row become a single space + combination = combination.replace(/\s+/g, " "); + + let sequence = combination.split(" "); + let info; + + // if this pattern is a sequence of keys then run through this method + // to reprocess each pattern one key at a time + if (sequence.length > 1) { + _bindSequence(combination, sequence, callback, action); + return; + } + + info = _getKeyInfo(combination, action); + + // make sure to initialize array if this is the first time + // a callback is added for this key + self._callbacks[info.key] = self._callbacks[info.key] || []; + + // remove an existing match if there is one + _getMatches( + info.key, + info.modifiers, + { type: info.action }, + sequenceName, + combination, + level + ); + + // add this call back to the array + // if it is a sequence put it at the beginning + // if not put it at the end + // + // this is important because the way these are processed expects + // the sequence ones to come first + self._callbacks[info.key][sequenceName ? "unshift" : "push"]({ + callback, + modifiers: info.modifiers, + action: info.action, + seq: sequenceName, + level, + combo: combination + }); + } + + /** + * binds multiple combinations to the same callback + * + * @param {Array} combinations + * @param {Function} callback + * @param {string|undefined} action + * @returns void + */ + self._bindMultiple = function(combinations, callback, action) { + for (let i = 0; i < combinations.length; ++i) { + _bindSingle(combinations[i], callback, action); + } + }; + + // start! + _addEvent(targetElement, "keypress", self._handleKeyEvent); + _addEvent(targetElement, "keydown", self._handleKeyEvent); + _addEvent(targetElement, "keyup", self._handleKeyEvent); + } + + /** + * binds an event to itsatrap + * + * can be a single key, a combination of keys separated with +, + * an array of keys, or a sequence of keys separated by spaces + * + * be sure to list the modifier keys first to make sure that the + * correct key ends up getting bound (the last key in the pattern) + * + * @param {string|Array} keys + * @param {Function} callback + * @param {string=} action - 'keypress', 'keydown', or 'keyup' + * @returns void + */ + ItsATrap.prototype.bind = function(keys, callback, action) { + let self = this; + keys = keys instanceof Array ? keys : [keys]; + self._bindMultiple.call(self, keys, callback, action); + return self; + }; + + /** + * unbinds an event to itsatrap + * + * the unbinding sets the callback function of the specified key combo + * to an empty function and deletes the corresponding key in the + * _directMap dict. + * + * TODO: actually remove this from the _callbacks dictionary instead + * of binding an empty function + * + * the keycombo+action has to be exactly the same as + * it was defined in the bind method + * + * @param {string|Array} keys + * @param {string} action + * @returns void + */ + ItsATrap.prototype.unbind = function(keys, action) { + let self = this; + return self.bind.call(self, keys, function() {}, action); + }; + + /** + * triggers an event that has already been bound + * + * @param {string} keys + * @param {string=} action + * @returns void + */ + ItsATrap.prototype.trigger = function(keys, action) { + let self = this; + if (self._directMap[keys + ":" + action]) { + self._directMap[keys + ":" + action]({}, keys); + } + return self; + }; + + /** + * resets the library back to its initial state. this is useful + * if you want to clear out the current keyboard shortcuts and bind + * new ones - for example if you switch to another page + * + * @returns void + */ + ItsATrap.prototype.reset = function() { + let self = this; + self._callbacks = {}; + self._directMap = {}; + return self; + }; + + /** + * destroy itsatrap object + * + * - call reset on the itsatrap object ( removing all binding ) + * - remove all javascript event listener from target element or document + * - remove all reference to target + * + * @return void + */ + + ItsATrap.prototype.destroy = function() { + let self = this; + + self.reset(); + + _removeEvent(self.target, "keypress", self._handleKeyEvent); + _removeEvent(self.target, "keydown", self._handleKeyEvent); + _removeEvent(self.target, "keyup", self._handleKeyEvent); + + self.target = undefined; + self._handleKeyEvent = undefined; + }; + + /** + * should we stop this event before firing off callbacks + * + * @param {Event} e + * @param {Element} element + * @return {boolean} + */ + ItsATrap.prototype.stopCallback = function(e, element, combo, sequence) { + let self = this; + + if (self.paused) { + return true; + } + + if (_globalCallbacks[combo] || _globalCallbacks[sequence]) { + return false; + } + + // if the element has the class "itsatrap" then no need to stop + if ((" " + element.className + " ").indexOf(" itsatrap ") > -1) { + return false; + } + + if (_belongsTo(element, self.target)) { + return false; + } + + // Events originating from a shadow DOM are re-targetted and `e.target` is the shadow host, + // not the initial event target in the shadow tree. Note that not all events cross the + // shadow boundary. + // For shadow trees with `mode: 'open'`, the initial event target is the first element in + // the event’s composed path. For shadow trees with `mode: 'closed'`, the initial event + // target cannot be obtained. + if ("composedPath" in e && typeof e.composedPath === "function") { + // For open shadow trees, update `element` so that the following check works. + let initialEventTarget = e.composedPath()[0]; + if (initialEventTarget !== e.target) { + element = initialEventTarget; + } + } + + // stop for input, select, and textarea + return ( + element.tagName == "INPUT" || + element.tagName == "SELECT" || + element.tagName == "TEXTAREA" || + element.isContentEditable + ); + }; + + /** + * exposes _handleKey publicly so it can be overwritten by extensions + */ + ItsATrap.prototype.handleKey = function() { + let self = this; + return self._handleKey.apply(self, arguments); + }; + + /** + * allow custom key mappings + */ + ItsATrap.addKeycodes = function(object) { + for (let key in object) { + if (object.hasOwnProperty(key)) { + _MAP[key] = object[key]; + } + } + _REVERSE_MAP = null; + }; + + /** + * adds a pause and unpause method to ItsATrap + * this allows you to enable or disable keyboard shortcuts + * without having to reset ItsATrap and rebind everything + */ + ItsATrap.prototype.pause = function() { + let self = this; + self.paused = true; + }; + + ItsATrap.prototype.unpause = function() { + let self = this; + self.paused = false; + }; + + /** + * adds a bindGlobal method to ItsATrap that allows you to + * bind specific keyboard shortcuts that will still work + * inside a text input field + * + * usage: + * ItsATrap.bindGlobal('ctrl+s', _saveChanges); + */ + ItsATrap.prototype.bindGlobal = function(keys, callback, action) { + let self = this; + self.bind(keys, callback, action); + + if (keys instanceof Array) { + for (let i = 0; i < keys.length; i++) { + _globalCallbacks[keys[i]] = true; + } + return; + } + + _globalCallbacks[keys] = true; + }; + + // expose itsatrap to the global object + window.ItsATrap = ItsATrap; + + // expose as a common js module + if (typeof module !== "undefined" && module.exports) { + module.exports = ItsATrap; + } + + // expose itsatrap as an AMD module + if (typeof define === "function" && define.amd) { + define(function() { + return ItsATrap; + }); + } +})( + typeof window !== "undefined" ? window : null, + typeof window !== "undefined" ? document : null +); diff --git a/assets/javascripts/legacy/jquery.js b/assets/javascripts/legacy/jquery.js new file mode 100644 index 00000000..de3b2064 --- /dev/null +++ b/assets/javascripts/legacy/jquery.js @@ -0,0 +1,10872 @@ +/*! + * jQuery JavaScript Library v3.5.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2020-05-04T22:49Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +let arr = []; + +let getProto = Object.getPrototypeOf; + +let slice = arr.slice; + +let flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +let push = arr.push; + +let indexOf = arr.indexOf; + +let class2type = {}; + +let toString = class2type.toString; + +let hasOwn = class2type.hasOwnProperty; + +let fnToString = hasOwn.toString; + +let ObjectFunctionString = fnToString.call( Object ); + +let support = {}; + +let isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +let isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +let document = window.document; + + + + let preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + let i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.5.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + let ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + let len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + let options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + let proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + let name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + let length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + let ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + let len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + let callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + let length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + let length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +let Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.5 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2020-03-14 + */ +( function( window ) { +let i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + let i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + let high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + let j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + let m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + let keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + let el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + let arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + let cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + let name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + let name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + let j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + let namespace = elem.namespaceURI, + docElem = ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + let hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + let attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + let elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + let attrId = id.replace( runescape, funescape ); + return function( elem ) { + let node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + let node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + let elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + let input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + let input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + let adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + let compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + let cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + let ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + let fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + let elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + let node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + let excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + let nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + let pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + let result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + let simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + let cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + let args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + let idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + let input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + let elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + let elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + let hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + let nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + let name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + let attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + let i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + let i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + let i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + let i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + let matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + let i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + let dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + let oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + let i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + let i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + let elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + let temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + let checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + let ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + let bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + let elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + let i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + let i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + let val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +let dir = function( elem, dir, until ) { + let matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +let siblings = function( n, elem ) { + let matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +let rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +let rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + let elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + let i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +let rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + let match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +let rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + let targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + let i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + let cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + let parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + let matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +let rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + let object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + let index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + let method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + let fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + let fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + let returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + let maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + let returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + let list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + let + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +let rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +let readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + let i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +let rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +let acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + let value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + let prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + let i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + let cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +let dataPriv = new Data(); + +let dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +let rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + let name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + let i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + let data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + let queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + let queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + let key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + let setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + let queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + let tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +let pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +let rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +let cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +let documentElement = document.documentElement; + + + + let isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +let isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + let adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +let defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + let temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + let display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +let rcheckableType = ( /^(?:checkbox|radio)$/i ); + +let rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +let rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + let fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +let wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + let ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + let i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +let rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + let elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +let + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + let origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + let handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type, + origType, + data, + handler, + guid: handler.guid, + selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + let j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + let i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + let i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + let el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + let el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + let target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + let notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + let e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + let e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + let e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + let button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + let ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + let handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +let + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + let i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + let nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + let fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + let self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + let node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + let i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + let data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + let target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + let target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + let elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + let elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + let ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + let parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + let elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +let rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +let getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + let view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +let swap = function( elem, options, callback ) { + let ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +let rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + let divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + reliableTrDimensions: function() { + let table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px"; + tr.style.height = "1px"; + trChild.style.height = "9px"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + let width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +let cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + let capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + let final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +let + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + let matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + let i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + let styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + let ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + let ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + let val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + let matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + let i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + let styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + let hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + let eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + let result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +let + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + let which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + let tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + let prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + let index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + let currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + let tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + let index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + let tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + let prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + let opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + let empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + let anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + let stopQueue = function( hooks ) { + let stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + let dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + let index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + let cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + let timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + let timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + let input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +let boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + let ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + let val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + let name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + let getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + let ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +let rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + let ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + let tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + let parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + let parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + let tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + let classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + let classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + let type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + let className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + let className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +let rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + let hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + let val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + let val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + let value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + let optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +let rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + let i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( + dataPriv.get( cur, "events" ) || Object.create( null ) + )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + let e = jQuery.extend( + new jQuery.Event(), + event, + { + type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + let elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + let handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + let doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + let doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +let location = window.location; + +let nonce = { guid: Date.now() }; + +let rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + let xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +let + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + let name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + let prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + let value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + let elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + let type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( _i, elem ) { + let val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +let + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + let dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + let inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + let selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + let dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + let key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + let ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + let conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + let match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + let code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + let finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + let isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script + if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url, + type: method, + dataType: type, + data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + let i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + let wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + let elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + let self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + let htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +let xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + let callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + let i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + let script, callback; + return { + send: function( _, complete ) { + script = jQuery( " diff --git a/assets/javascripts/wizard-qunit.js b/assets/javascripts/wizard-qunit.js index e6692f08..37b63a3d 100644 --- a/assets/javascripts/wizard-qunit.js +++ b/assets/javascripts/wizard-qunit.js @@ -1,5 +1,5 @@ //= require legacy/env -//= require jquery.debug +//= require legacy/jquery //= require ember.debug //= require route-recognizer //= require fake_xml_http_request From 8496e15728a2cb66db96eefde3fd88e8a9156fad Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 22 Jun 2022 10:22:20 +0200 Subject: [PATCH 19/30] Remove qunit entirely for now Re-add when migration to ember-cli is made --- app/views/layouts/qunit.html.erb | 28 ---------------------------- app/views/layouts/wizard.html.erb | 1 - assets/javascripts/wizard-qunit.js | 17 ----------------- 3 files changed, 46 deletions(-) delete mode 100644 app/views/layouts/qunit.html.erb delete mode 100644 assets/javascripts/wizard-qunit.js diff --git a/app/views/layouts/qunit.html.erb b/app/views/layouts/qunit.html.erb deleted file mode 100644 index 780dc7d6..00000000 --- a/app/views/layouts/qunit.html.erb +++ /dev/null @@ -1,28 +0,0 @@ - - - - Custom Wizard QUnit Test Runner - <%= discourse_stylesheet_link_tag(:test_helper, theme_id: nil) %> - <%= discourse_stylesheet_link_tag :wizard, theme_id: nil %> - <%= discourse_stylesheet_link_tag :wizard_custom %> - <%= preload_script "locales/en" %> - <%= preload_script "ember_jquery" %> - <%= preload_script "wizard-vendor" %> - <%= preload_script "wizard-custom" %> - <%= preload_script "wizard-raw-templates" %> - <%= preload_script "wizard-plugin" %> - <%= preload_script "pretty-text-bundle" %> - <%#<%= preload_script "wizard-qunit" %>%> - <%= csrf_meta_tags %> - - - - <%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %> - - - - -
-
- - diff --git a/app/views/layouts/wizard.html.erb b/app/views/layouts/wizard.html.erb index cab255c3..325a94e8 100644 --- a/app/views/layouts/wizard.html.erb +++ b/app/views/layouts/wizard.html.erb @@ -9,7 +9,6 @@ <%- end %> <%= preload_script "locales/#{I18n.locale}" %> - <%= preload_script "ember_jquery" %> <%= preload_script "wizard-vendor" %> <%= preload_script "wizard-custom" %> <%= preload_script "wizard-raw-templates" %> diff --git a/assets/javascripts/wizard-qunit.js b/assets/javascripts/wizard-qunit.js deleted file mode 100644 index 37b63a3d..00000000 --- a/assets/javascripts/wizard-qunit.js +++ /dev/null @@ -1,17 +0,0 @@ -//= require legacy/env -//= require legacy/jquery -//= require ember.debug -//= require route-recognizer -//= require fake_xml_http_request -//= require pretender -//= require qunit -//= require ember-qunit -//= require test-shims -//= require ember-template-compiler - -//= require_tree ./wizard/tests/fixtures -//= require ./wizard/tests/pretender -//= require_tree ./wizard/tests/helpers -//= require_tree ./wizard/tests/acceptance - -//= require ./wizard/tests/bootstrap From b8329f0fa06f47a7a75f0fd0b1cd5e5fc0e3d206 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 22 Jun 2022 10:37:02 +0200 Subject: [PATCH 20/30] wizard-vendor needs to be precompiled --- plugin.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin.rb b/plugin.rb index a0e6dd7b..136056a6 100644 --- a/plugin.rb +++ b/plugin.rb @@ -25,6 +25,7 @@ if Rails.env.production? wizard-custom.js wizard-plugin.js.erb wizard-raw-templates.js.erb + wizard-vendor.js } end From b7d332e0e562f2b5c7a4a51b26fa790ec43b9326 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 22 Jun 2022 12:19:50 +0200 Subject: [PATCH 21/30] COMPATIBILITY: everything is transpiled by default now. https://github.com/discourse/discourse/commit/624c684d51d215a7b0969598d94c6c04f7224e22 --- app/views/layouts/wizard.html.erb | 1 + .../javascripts/{legacy => }/ember_jquery.js | 0 assets/javascripts/legacy/discourse-loader.js | 4 +- assets/javascripts/legacy/discourse-shims.js | 2 + .../javascripts/legacy/ember_include.js.erb | 2 + assets/javascripts/legacy/env.js | 2 + .../javascripts/legacy/handlebars.runtime.js | 2 + assets/javascripts/legacy/jquery.js | 2 + assets/javascripts/legacy/popper.js | 2 + .../legacy/set-prototype-polyfill.js | 2 + assets/javascripts/legacy/template_include.js | 2 + assets/javascripts/legacy/virtual-dom-amd.js | 2 + assets/javascripts/legacy/virtual-dom.js | 2 + assets/javascripts/wizard-custom-guest.js | 2 + assets/javascripts/wizard-custom-start.js | 2 + assets/javascripts/wizard-custom.js | 3 +- assets/javascripts/wizard-plugin.js.erb | 2 + .../javascripts/wizard-raw-templates.js.erb | 14 +++--- assets/javascripts/wizard-shims.js | 47 ------------------- assets/javascripts/wizard-vendor.js | 5 +- 20 files changed, 43 insertions(+), 57 deletions(-) rename assets/javascripts/{legacy => }/ember_jquery.js (100%) delete mode 100644 assets/javascripts/wizard-shims.js diff --git a/app/views/layouts/wizard.html.erb b/app/views/layouts/wizard.html.erb index 325a94e8..cab255c3 100644 --- a/app/views/layouts/wizard.html.erb +++ b/app/views/layouts/wizard.html.erb @@ -9,6 +9,7 @@ <%- end %> <%= preload_script "locales/#{I18n.locale}" %> + <%= preload_script "ember_jquery" %> <%= preload_script "wizard-vendor" %> <%= preload_script "wizard-custom" %> <%= preload_script "wizard-raw-templates" %> diff --git a/assets/javascripts/legacy/ember_jquery.js b/assets/javascripts/ember_jquery.js similarity index 100% rename from assets/javascripts/legacy/ember_jquery.js rename to assets/javascripts/ember_jquery.js diff --git a/assets/javascripts/legacy/discourse-loader.js b/assets/javascripts/legacy/discourse-loader.js index edcbf27f..ec77a6d2 100644 --- a/assets/javascripts/legacy/discourse-loader.js +++ b/assets/javascripts/legacy/discourse-loader.js @@ -1,3 +1,5 @@ +// discourse-skip-module + var define, requirejs; (function () { @@ -171,7 +173,7 @@ var define, requirejs; I18n: { // eslint-disable-next-line default: I18n, - }, + } }; } diff --git a/assets/javascripts/legacy/discourse-shims.js b/assets/javascripts/legacy/discourse-shims.js index 7794b099..a6b25771 100644 --- a/assets/javascripts/legacy/discourse-shims.js +++ b/assets/javascripts/legacy/discourse-shims.js @@ -1,3 +1,5 @@ +// discourse-skip-module + define("message-bus-client", ["exports"], function (__exports__) { __exports__.default = window.MessageBus; }); diff --git a/assets/javascripts/legacy/ember_include.js.erb b/assets/javascripts/legacy/ember_include.js.erb index 56883bef..0d6ddf43 100644 --- a/assets/javascripts/legacy/ember_include.js.erb +++ b/assets/javascripts/legacy/ember_include.js.erb @@ -1,3 +1,5 @@ +// discourse-skip-module + <% if @force_ember_debug || Rails.env.development? || Rails.env.test? require_asset("ember.debug.js") diff --git a/assets/javascripts/legacy/env.js b/assets/javascripts/legacy/env.js index 8b897bbe..0ae0e456 100644 --- a/assets/javascripts/legacy/env.js +++ b/assets/javascripts/legacy/env.js @@ -1,3 +1,5 @@ +// discourse-skip-module + window.ENV = {}; window.EmberENV = window.EmberENV || {}; window.EmberENV.FORCE_JQUERY = true; diff --git a/assets/javascripts/legacy/handlebars.runtime.js b/assets/javascripts/legacy/handlebars.runtime.js index da377f0f..145aa7c1 100644 --- a/assets/javascripts/legacy/handlebars.runtime.js +++ b/assets/javascripts/legacy/handlebars.runtime.js @@ -1,3 +1,5 @@ +// discourse-skip-module + /**! @license diff --git a/assets/javascripts/legacy/jquery.js b/assets/javascripts/legacy/jquery.js index de3b2064..956c6699 100644 --- a/assets/javascripts/legacy/jquery.js +++ b/assets/javascripts/legacy/jquery.js @@ -1,3 +1,5 @@ +// discourse-skip-module + /*! * jQuery JavaScript Library v3.5.1 * https://jquery.com/ diff --git a/assets/javascripts/legacy/popper.js b/assets/javascripts/legacy/popper.js index 95d3428c..25298b6d 100644 --- a/assets/javascripts/legacy/popper.js +++ b/assets/javascripts/legacy/popper.js @@ -1,3 +1,5 @@ +// discourse-skip-module + /** * @popperjs/core v2.10.2 - MIT License */ diff --git a/assets/javascripts/legacy/set-prototype-polyfill.js b/assets/javascripts/legacy/set-prototype-polyfill.js index 355fa28e..669b4458 100644 --- a/assets/javascripts/legacy/set-prototype-polyfill.js +++ b/assets/javascripts/legacy/set-prototype-polyfill.js @@ -1,3 +1,5 @@ +// discourse-skip-module + /* eslint-disable */ Object.setPrototypeOf = Object.setPrototypeOf || diff --git a/assets/javascripts/legacy/template_include.js b/assets/javascripts/legacy/template_include.js index 7188ba8e..273eb7a2 100644 --- a/assets/javascripts/legacy/template_include.js +++ b/assets/javascripts/legacy/template_include.js @@ -1,2 +1,4 @@ +// discourse-skip-module + //= require legacy/handlebars.runtime //= require handlebars-shim diff --git a/assets/javascripts/legacy/virtual-dom-amd.js b/assets/javascripts/legacy/virtual-dom-amd.js index 75ee9dcc..d9047402 100644 --- a/assets/javascripts/legacy/virtual-dom-amd.js +++ b/assets/javascripts/legacy/virtual-dom-amd.js @@ -1,3 +1,5 @@ +// discourse-skip-module + // Just a wrapper from the standalone browserified virtual-dom define("virtual-dom", [], function() { return virtualDom; diff --git a/assets/javascripts/legacy/virtual-dom.js b/assets/javascripts/legacy/virtual-dom.js index bece554b..231a7622 100644 --- a/assets/javascripts/legacy/virtual-dom.js +++ b/assets/javascripts/legacy/virtual-dom.js @@ -1,3 +1,5 @@ +// discourse-skip-module + !(function(e){if("object"===typeof exports&&"undefined"!==typeof module){module.exports=e();}else if("function"===typeof define&&define.amd){define([],e);}else{let f;"undefined"!==typeof window?f=window:"undefined"!==typeof global?f=global:"undefined"!==typeof self&&(f=self),f.virtualDom=e();}})(function(){let define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){let a=typeof require==="function"&&require;if(!u&&a){return a(o,!0);}if(i){return i(o,!0);}let f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f;}let l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){let n=t[o][1][e];return s(n?n:e);},l,l.exports,e,t,n,r);}return n[o].exports;}var i=typeof require==="function"&&require;for(let o=0;o Date: Wed, 22 Jun 2022 12:29:54 +0200 Subject: [PATCH 22/30] Add ember_jquery to precompilation pipeline --- plugin.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin.rb b/plugin.rb index 136056a6..a5ffc914 100644 --- a/plugin.rb +++ b/plugin.rb @@ -20,6 +20,7 @@ config.assets.paths << "#{plugin_asset_path}/stylesheets/wizard" if Rails.env.production? config.assets.precompile += %w{ + ember_jquery.js wizard-custom-guest.js wizard-custom-start.js wizard-custom.js From 44f5d39e0a96497df9504c535e4461de760afe8d Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 22 Jun 2022 12:45:35 +0200 Subject: [PATCH 23/30] Skip more vendor modules and tweak uppy --- assets/javascripts/legacy/itsatrap.js | 2 ++ assets/javascripts/legacy/tippy.umd.js | 2 ++ assets/javascripts/legacy/uppy.js | 6 ++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/legacy/itsatrap.js b/assets/javascripts/legacy/itsatrap.js index e4f847e9..ae5ad391 100644 --- a/assets/javascripts/legacy/itsatrap.js +++ b/assets/javascripts/legacy/itsatrap.js @@ -1,3 +1,5 @@ +// discourse-skip-module + /*global define:false */ /** * Copyright 2012-2017 Craig Campbell diff --git a/assets/javascripts/legacy/tippy.umd.js b/assets/javascripts/legacy/tippy.umd.js index 90f09c38..4d5b9276 100644 --- a/assets/javascripts/legacy/tippy.umd.js +++ b/assets/javascripts/legacy/tippy.umd.js @@ -1,3 +1,5 @@ +// discourse-skip-module + /**! * tippy.js v6.3.7 * (c) 2017-2021 atomiks diff --git a/assets/javascripts/legacy/uppy.js b/assets/javascripts/legacy/uppy.js index accccdf2..d037c696 100644 --- a/assets/javascripts/legacy/uppy.js +++ b/assets/javascripts/legacy/uppy.js @@ -1,3 +1,5 @@ +// discourse-skip-module + (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){let c="function"===typeof require&&require;if(!f&&c){return c(i,!0);}if(u){return u(i,!0);}let a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a;}let p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){let n=e[i][1][r];return o(n||r);},p,p.exports,r,e,n,t);}return n[i].exports;}for(var u="function"===typeof require&&require,i=0;i Date: Wed, 22 Jun 2022 12:56:41 +0200 Subject: [PATCH 24/30] Bump version Plugin supports all recent asset pipeline changes in Discourse. --- plugin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.rb b/plugin.rb index a5ffc914..ef227f12 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-custom-wizard # about: Create custom wizards -# version: 1.20.0 +# version: 1.21.0 # authors: Angus McLeod # url: https://github.com/paviliondev/discourse-custom-wizard # contact emails: angus@thepavilion.io From 85c9629da680bedcb3347dc452efd790a69df774 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 27 Jun 2022 11:36:00 +0200 Subject: [PATCH 25/30] COMPATIBILITY: XSS lib has also been moved to a node module https://github.com/discourse/discourse/commit/d1d6868325bdc98245a6e8aa67a3bf0d3f4b00ff --- assets/javascripts/legacy/xss.min.js | 3 +++ assets/javascripts/wizard-vendor.js | 1 + 2 files changed, 4 insertions(+) create mode 100644 assets/javascripts/legacy/xss.min.js diff --git a/assets/javascripts/legacy/xss.min.js b/assets/javascripts/legacy/xss.min.js new file mode 100644 index 00000000..d84aedcb --- /dev/null +++ b/assets/javascripts/legacy/xss.min.js @@ -0,0 +1,3 @@ +// discourse-skip-module + +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i/g;var REGEXP_QUOTE=/"/g;var REGEXP_QUOTE_2=/"/g;var REGEXP_ATTR_VALUE_1=/&#([a-zA-Z0-9]*);?/gim;var REGEXP_ATTR_VALUE_COLON=/:?/gim;var REGEXP_ATTR_VALUE_NEWLINE=/&newline;?/gim;var REGEXP_DEFAULT_ON_TAG_ATTR_3=/\/\*|\*\//gm;var REGEXP_DEFAULT_ON_TAG_ATTR_4=/((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a)\:/gi;var REGEXP_DEFAULT_ON_TAG_ATTR_5=/^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/gi;var REGEXP_DEFAULT_ON_TAG_ATTR_6=/^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//gi;var REGEXP_DEFAULT_ON_TAG_ATTR_7=/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/gi;var REGEXP_DEFAULT_ON_TAG_ATTR_8=/u\s*r\s*l\s*\(.*/gi;function escapeQuote(str){return str.replace(REGEXP_QUOTE,""")}function unescapeQuote(str){return str.replace(REGEXP_QUOTE_2,'"')}function escapeHtmlEntities(str){return str.replace(REGEXP_ATTR_VALUE_1,function replaceUnicode(str,code){return code[0]==="x"||code[0]==="X"?String.fromCharCode(parseInt(code.substr(1),16)):String.fromCharCode(parseInt(code,10))})}function escapeDangerHtml5Entities(str){return str.replace(REGEXP_ATTR_VALUE_COLON,":").replace(REGEXP_ATTR_VALUE_NEWLINE," ")}function clearNonPrintableCharacter(str){var str2="";for(var i=0,len=str.length;i"){rethtml+=escapeHtml(html.slice(lastPos,tagStart));currentHtml=html.slice(tagStart,currentPos+1);currentTagName=getTagName(currentHtml);rethtml+=onTag(tagStart,rethtml.length,currentTagName,currentHtml,isClosing(currentHtml));lastPos=currentPos+1;tagStart=false;continue}if(c==='"'||c==="'"){var i=1;var ic=html.charAt(currentPos-i);while(ic.trim()===""||ic==="="){if(ic==="="){quoteStart=c;continue chariterator}ic=html.charAt(currentPos-++i)}}}else{if(c===quoteStart){quoteStart=false;continue}}}}if(lastPos0;i--){var c=str[i];if(c===" ")continue;if(c==="=")return i;return-1}}function isQuoteWrapString(text){if(text[0]==='"'&&text[text.length-1]==='"'||text[0]==="'"&&text[text.length-1]==="'"){return true}else{return false}}function stripQuoteWrap(text){if(isQuoteWrapString(text)){return text.substr(1,text.length-2)}else{return text}}exports.parseTag=parseTag;exports.parseAttr=parseAttr},{"./util":4}],4:[function(require,module,exports){module.exports={indexOf:function(arr,item){var i,j;if(Array.prototype.indexOf){return arr.indexOf(item)}for(i=0,j=arr.length;i"}var attrs=getAttrs(html);var whiteAttrList=whiteList[tag];var attrsHtml=parseAttr(attrs.html,function(name,value){var isWhiteAttr=_.indexOf(whiteAttrList,name)!==-1;var ret=onTagAttr(tag,name,value,isWhiteAttr);if(!isNull(ret))return ret;if(isWhiteAttr){value=safeAttrValue(tag,name,value,cssFilter);if(value){return name+'="'+value+'"'}else{return name}}else{var ret=onIgnoreTagAttr(tag,name,value,isWhiteAttr);if(!isNull(ret))return ret;return}});var html="<"+tag;if(attrsHtml)html+=" "+attrsHtml;if(attrs.closing)html+=" /";html+=">";return html}else{var ret=onIgnoreTag(tag,html,info);if(!isNull(ret))return ret;return escapeHtml(html)}},escapeHtml);if(stripIgnoreTagBody){retHtml=stripIgnoreTagBody.remove(retHtml)}return retHtml};module.exports=FilterXSS},{"./default":1,"./parser":3,"./util":4,cssfilter:8}],6:[function(require,module,exports){var DEFAULT=require("./default");var parseStyle=require("./parser");var _=require("./util");function isNull(obj){return obj===undefined||obj===null}function shallowCopyObject(obj){var ret={};for(var i in obj){ret[i]=obj[i]}return ret}function FilterCSS(options){options=shallowCopyObject(options||{});options.whiteList=options.whiteList||DEFAULT.whiteList;options.onAttr=options.onAttr||DEFAULT.onAttr;options.onIgnoreAttr=options.onIgnoreAttr||DEFAULT.onIgnoreAttr;options.safeAttrValue=options.safeAttrValue||DEFAULT.safeAttrValue;this.options=options}FilterCSS.prototype.process=function(css){css=css||"";css=css.toString();if(!css)return"";var me=this;var options=me.options;var whiteList=options.whiteList;var onAttr=options.onAttr;var onIgnoreAttr=options.onIgnoreAttr;var safeAttrValue=options.safeAttrValue;var retCSS=parseStyle(css,function(sourcePosition,position,name,value,source){var check=whiteList[name];var isWhite=false;if(check===true)isWhite=check;else if(typeof check==="function")isWhite=check(value);else if(check instanceof RegExp)isWhite=check.test(value);if(isWhite!==true)isWhite=false;value=safeAttrValue(name,value);if(!value)return;var opts={position:position,sourcePosition:sourcePosition,source:source,isWhite:isWhite};if(isWhite){var ret=onAttr(name,value,opts);if(isNull(ret)){return name+":"+value}else{return ret}}else{var ret=onIgnoreAttr(name,value,opts);if(!isNull(ret)){return ret}}});return retCSS};module.exports=FilterCSS},{"./default":7,"./parser":9,"./util":10}],7:[function(require,module,exports){function getDefaultWhiteList(){var whiteList={};whiteList["align-content"]=false;whiteList["align-items"]=false;whiteList["align-self"]=false;whiteList["alignment-adjust"]=false;whiteList["alignment-baseline"]=false;whiteList["all"]=false;whiteList["anchor-point"]=false;whiteList["animation"]=false;whiteList["animation-delay"]=false;whiteList["animation-direction"]=false;whiteList["animation-duration"]=false;whiteList["animation-fill-mode"]=false;whiteList["animation-iteration-count"]=false;whiteList["animation-name"]=false;whiteList["animation-play-state"]=false;whiteList["animation-timing-function"]=false;whiteList["azimuth"]=false;whiteList["backface-visibility"]=false;whiteList["background"]=true;whiteList["background-attachment"]=true;whiteList["background-clip"]=true;whiteList["background-color"]=true;whiteList["background-image"]=true;whiteList["background-origin"]=true;whiteList["background-position"]=true;whiteList["background-repeat"]=true;whiteList["background-size"]=true;whiteList["baseline-shift"]=false;whiteList["binding"]=false;whiteList["bleed"]=false;whiteList["bookmark-label"]=false;whiteList["bookmark-level"]=false;whiteList["bookmark-state"]=false;whiteList["border"]=true;whiteList["border-bottom"]=true;whiteList["border-bottom-color"]=true;whiteList["border-bottom-left-radius"]=true;whiteList["border-bottom-right-radius"]=true;whiteList["border-bottom-style"]=true;whiteList["border-bottom-width"]=true;whiteList["border-collapse"]=true;whiteList["border-color"]=true;whiteList["border-image"]=true;whiteList["border-image-outset"]=true;whiteList["border-image-repeat"]=true;whiteList["border-image-slice"]=true;whiteList["border-image-source"]=true;whiteList["border-image-width"]=true;whiteList["border-left"]=true;whiteList["border-left-color"]=true;whiteList["border-left-style"]=true;whiteList["border-left-width"]=true;whiteList["border-radius"]=true;whiteList["border-right"]=true;whiteList["border-right-color"]=true;whiteList["border-right-style"]=true;whiteList["border-right-width"]=true;whiteList["border-spacing"]=true;whiteList["border-style"]=true;whiteList["border-top"]=true;whiteList["border-top-color"]=true;whiteList["border-top-left-radius"]=true;whiteList["border-top-right-radius"]=true;whiteList["border-top-style"]=true;whiteList["border-top-width"]=true;whiteList["border-width"]=true;whiteList["bottom"]=false;whiteList["box-decoration-break"]=true;whiteList["box-shadow"]=true;whiteList["box-sizing"]=true;whiteList["box-snap"]=true;whiteList["box-suppress"]=true;whiteList["break-after"]=true;whiteList["break-before"]=true;whiteList["break-inside"]=true;whiteList["caption-side"]=false;whiteList["chains"]=false;whiteList["clear"]=true;whiteList["clip"]=false;whiteList["clip-path"]=false;whiteList["clip-rule"]=false;whiteList["color"]=true;whiteList["color-interpolation-filters"]=true;whiteList["column-count"]=false;whiteList["column-fill"]=false;whiteList["column-gap"]=false;whiteList["column-rule"]=false;whiteList["column-rule-color"]=false;whiteList["column-rule-style"]=false;whiteList["column-rule-width"]=false;whiteList["column-span"]=false;whiteList["column-width"]=false;whiteList["columns"]=false;whiteList["contain"]=false;whiteList["content"]=false;whiteList["counter-increment"]=false;whiteList["counter-reset"]=false;whiteList["counter-set"]=false;whiteList["crop"]=false;whiteList["cue"]=false;whiteList["cue-after"]=false;whiteList["cue-before"]=false;whiteList["cursor"]=false;whiteList["direction"]=false;whiteList["display"]=true;whiteList["display-inside"]=true;whiteList["display-list"]=true;whiteList["display-outside"]=true;whiteList["dominant-baseline"]=false;whiteList["elevation"]=false;whiteList["empty-cells"]=false;whiteList["filter"]=false;whiteList["flex"]=false;whiteList["flex-basis"]=false;whiteList["flex-direction"]=false;whiteList["flex-flow"]=false;whiteList["flex-grow"]=false;whiteList["flex-shrink"]=false;whiteList["flex-wrap"]=false;whiteList["float"]=false;whiteList["float-offset"]=false;whiteList["flood-color"]=false;whiteList["flood-opacity"]=false;whiteList["flow-from"]=false;whiteList["flow-into"]=false;whiteList["font"]=true;whiteList["font-family"]=true;whiteList["font-feature-settings"]=true;whiteList["font-kerning"]=true;whiteList["font-language-override"]=true;whiteList["font-size"]=true;whiteList["font-size-adjust"]=true;whiteList["font-stretch"]=true;whiteList["font-style"]=true;whiteList["font-synthesis"]=true;whiteList["font-variant"]=true;whiteList["font-variant-alternates"]=true;whiteList["font-variant-caps"]=true;whiteList["font-variant-east-asian"]=true;whiteList["font-variant-ligatures"]=true;whiteList["font-variant-numeric"]=true;whiteList["font-variant-position"]=true;whiteList["font-weight"]=true;whiteList["grid"]=false;whiteList["grid-area"]=false;whiteList["grid-auto-columns"]=false;whiteList["grid-auto-flow"]=false;whiteList["grid-auto-rows"]=false;whiteList["grid-column"]=false;whiteList["grid-column-end"]=false;whiteList["grid-column-start"]=false;whiteList["grid-row"]=false;whiteList["grid-row-end"]=false;whiteList["grid-row-start"]=false;whiteList["grid-template"]=false;whiteList["grid-template-areas"]=false;whiteList["grid-template-columns"]=false;whiteList["grid-template-rows"]=false;whiteList["hanging-punctuation"]=false;whiteList["height"]=true;whiteList["hyphens"]=false;whiteList["icon"]=false;whiteList["image-orientation"]=false;whiteList["image-resolution"]=false;whiteList["ime-mode"]=false;whiteList["initial-letters"]=false;whiteList["inline-box-align"]=false;whiteList["justify-content"]=false;whiteList["justify-items"]=false;whiteList["justify-self"]=false;whiteList["left"]=false;whiteList["letter-spacing"]=true;whiteList["lighting-color"]=true;whiteList["line-box-contain"]=false;whiteList["line-break"]=false;whiteList["line-grid"]=false;whiteList["line-height"]=false;whiteList["line-snap"]=false;whiteList["line-stacking"]=false;whiteList["line-stacking-ruby"]=false;whiteList["line-stacking-shift"]=false;whiteList["line-stacking-strategy"]=false;whiteList["list-style"]=true;whiteList["list-style-image"]=true;whiteList["list-style-position"]=true;whiteList["list-style-type"]=true;whiteList["margin"]=true;whiteList["margin-bottom"]=true;whiteList["margin-left"]=true;whiteList["margin-right"]=true;whiteList["margin-top"]=true;whiteList["marker-offset"]=false;whiteList["marker-side"]=false;whiteList["marks"]=false;whiteList["mask"]=false;whiteList["mask-box"]=false;whiteList["mask-box-outset"]=false;whiteList["mask-box-repeat"]=false;whiteList["mask-box-slice"]=false;whiteList["mask-box-source"]=false;whiteList["mask-box-width"]=false;whiteList["mask-clip"]=false;whiteList["mask-image"]=false;whiteList["mask-origin"]=false;whiteList["mask-position"]=false;whiteList["mask-repeat"]=false;whiteList["mask-size"]=false;whiteList["mask-source-type"]=false;whiteList["mask-type"]=false;whiteList["max-height"]=true;whiteList["max-lines"]=false;whiteList["max-width"]=true;whiteList["min-height"]=true;whiteList["min-width"]=true;whiteList["move-to"]=false;whiteList["nav-down"]=false;whiteList["nav-index"]=false;whiteList["nav-left"]=false;whiteList["nav-right"]=false;whiteList["nav-up"]=false;whiteList["object-fit"]=false;whiteList["object-position"]=false;whiteList["opacity"]=false;whiteList["order"]=false;whiteList["orphans"]=false;whiteList["outline"]=false;whiteList["outline-color"]=false;whiteList["outline-offset"]=false;whiteList["outline-style"]=false;whiteList["outline-width"]=false;whiteList["overflow"]=false;whiteList["overflow-wrap"]=false;whiteList["overflow-x"]=false;whiteList["overflow-y"]=false;whiteList["padding"]=true;whiteList["padding-bottom"]=true;whiteList["padding-left"]=true;whiteList["padding-right"]=true;whiteList["padding-top"]=true;whiteList["page"]=false;whiteList["page-break-after"]=false;whiteList["page-break-before"]=false;whiteList["page-break-inside"]=false;whiteList["page-policy"]=false;whiteList["pause"]=false;whiteList["pause-after"]=false;whiteList["pause-before"]=false;whiteList["perspective"]=false;whiteList["perspective-origin"]=false;whiteList["pitch"]=false;whiteList["pitch-range"]=false;whiteList["play-during"]=false;whiteList["position"]=false;whiteList["presentation-level"]=false;whiteList["quotes"]=false;whiteList["region-fragment"]=false;whiteList["resize"]=false;whiteList["rest"]=false;whiteList["rest-after"]=false;whiteList["rest-before"]=false;whiteList["richness"]=false;whiteList["right"]=false;whiteList["rotation"]=false;whiteList["rotation-point"]=false;whiteList["ruby-align"]=false;whiteList["ruby-merge"]=false;whiteList["ruby-position"]=false;whiteList["shape-image-threshold"]=false;whiteList["shape-outside"]=false;whiteList["shape-margin"]=false;whiteList["size"]=false;whiteList["speak"]=false;whiteList["speak-as"]=false;whiteList["speak-header"]=false;whiteList["speak-numeral"]=false;whiteList["speak-punctuation"]=false;whiteList["speech-rate"]=false;whiteList["stress"]=false;whiteList["string-set"]=false;whiteList["tab-size"]=false;whiteList["table-layout"]=false;whiteList["text-align"]=true;whiteList["text-align-last"]=true;whiteList["text-combine-upright"]=true;whiteList["text-decoration"]=true;whiteList["text-decoration-color"]=true;whiteList["text-decoration-line"]=true;whiteList["text-decoration-skip"]=true;whiteList["text-decoration-style"]=true;whiteList["text-emphasis"]=true;whiteList["text-emphasis-color"]=true;whiteList["text-emphasis-position"]=true;whiteList["text-emphasis-style"]=true;whiteList["text-height"]=true;whiteList["text-indent"]=true;whiteList["text-justify"]=true;whiteList["text-orientation"]=true;whiteList["text-overflow"]=true;whiteList["text-shadow"]=true;whiteList["text-space-collapse"]=true;whiteList["text-transform"]=true;whiteList["text-underline-position"]=true;whiteList["text-wrap"]=true;whiteList["top"]=false;whiteList["transform"]=false;whiteList["transform-origin"]=false;whiteList["transform-style"]=false;whiteList["transition"]=false;whiteList["transition-delay"]=false;whiteList["transition-duration"]=false;whiteList["transition-property"]=false;whiteList["transition-timing-function"]=false;whiteList["unicode-bidi"]=false;whiteList["vertical-align"]=false;whiteList["visibility"]=false;whiteList["voice-balance"]=false;whiteList["voice-duration"]=false;whiteList["voice-family"]=false;whiteList["voice-pitch"]=false;whiteList["voice-range"]=false;whiteList["voice-rate"]=false;whiteList["voice-stress"]=false;whiteList["voice-volume"]=false;whiteList["volume"]=false;whiteList["white-space"]=false;whiteList["widows"]=false;whiteList["width"]=true;whiteList["will-change"]=false;whiteList["word-break"]=true;whiteList["word-spacing"]=true;whiteList["word-wrap"]=true;whiteList["wrap-flow"]=false;whiteList["wrap-through"]=false;whiteList["writing-mode"]=false;whiteList["z-index"]=false;return whiteList}function onAttr(name,value,options){}function onIgnoreAttr(name,value,options){}var REGEXP_URL_JAVASCRIPT=/javascript\s*\:/gim;function safeAttrValue(name,value){if(REGEXP_URL_JAVASCRIPT.test(value))return"";return value}exports.whiteList=getDefaultWhiteList();exports.getDefaultWhiteList=getDefaultWhiteList;exports.onAttr=onAttr;exports.onIgnoreAttr=onIgnoreAttr;exports.safeAttrValue=safeAttrValue},{}],8:[function(require,module,exports){var DEFAULT=require("./default");var FilterCSS=require("./css");function filterCSS(html,options){var xss=new FilterCSS(options);return xss.process(html)}exports=module.exports=filterCSS;exports.FilterCSS=FilterCSS;for(var i in DEFAULT)exports[i]=DEFAULT[i];if(typeof window!=="undefined"){window.filterCSS=module.exports}},{"./css":6,"./default":7}],9:[function(require,module,exports){var _=require("./util");function parseStyle(css,onAttr){css=_.trimRight(css);if(css[css.length-1]!==";")css+=";";var cssLength=css.length;var isParenthesisOpen=false;var lastPos=0;var i=0;var retCSS="";function addNewAttr(){if(!isParenthesisOpen){var source=_.trim(css.slice(lastPos,i));var j=source.indexOf(":");if(j!==-1){var name=_.trim(source.slice(0,j));var value=_.trim(source.slice(j+1));if(name){var ret=onAttr(lastPos,retCSS.length,name,value,source);if(ret)retCSS+=ret+"; "}}}lastPos=i+1}for(;i Date: Wed, 29 Jun 2022 09:55:21 +0200 Subject: [PATCH 26/30] Update linting workflow --- .github/workflows/plugin-linting.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/plugin-linting.yml b/.github/workflows/plugin-linting.yml index a915ea7f..a7cbe811 100644 --- a/.github/workflows/plugin-linting.yml +++ b/.github/workflows/plugin-linting.yml @@ -37,20 +37,17 @@ jobs: run: yarn install --dev - name: ESLint - run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets}/javascripts + run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets}/javascripts/{discourse,wizard} - name: Prettier run: | yarn prettier -v if [ -d "assets" ]; then \ - yarn prettier --list-different "assets/**/*.{scss,js,es6}" ; \ - fi - if [ -d "test" ]; then \ - yarn prettier --list-different "test/**/*.{js,es6}" ; \ + yarn prettier --list-different "assets/javascripts/{discourse,wizard}/**/*.{scss,js,es6}" ; \ fi - name: Ember template lint run: yarn ember-template-lint assets/javascripts - name: Rubocop - run: bundle exec rubocop . \ No newline at end of file + run: bundle exec rubocop . From 180fc0b3ea9592abc2958b417ec98f16cbf9967d Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 5 Jul 2022 08:40:55 +0200 Subject: [PATCH 27/30] Create bootstrap-modal.js --- assets/javascripts/legacy/bootstrap-modal.js | 360 +++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 assets/javascripts/legacy/bootstrap-modal.js diff --git a/assets/javascripts/legacy/bootstrap-modal.js b/assets/javascripts/legacy/bootstrap-modal.js new file mode 100644 index 00000000..61c76fbd --- /dev/null +++ b/assets/javascripts/legacy/bootstrap-modal.js @@ -0,0 +1,360 @@ +// discourse-skip-module + +/* ======================================================================== + * Bootstrap: modal.js v3.4.1 + * https://getbootstrap.com/docs/3.4/javascript/#modals + * ======================================================================== + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // MODAL CLASS DEFINITION + // ====================== + + var Modal = function (element, options) { + this.options = options + this.$body = $(document.body) + this.$element = $(element) + this.$dialog = this.$element.find('.modal-dialog') + this.$backdrop = null + this.isShown = null + this.originalBodyPad = null + this.scrollbarWidth = 0 + this.ignoreBackdropClick = false + this.fixedContent = '.navbar-fixed-top, .navbar-fixed-bottom' + + if (this.options.remote) { + this.$element + .find('.modal-content') + .load(this.options.remote, $.proxy(function () { + this.$element.trigger('loaded.bs.modal') + }, this)) + } + } + + Modal.VERSION = '3.4.1' + + Modal.TRANSITION_DURATION = 300 + Modal.BACKDROP_TRANSITION_DURATION = 150 + + Modal.DEFAULTS = { + backdrop: true, + keyboard: true, + show: true + } + + Modal.prototype.toggle = function (_relatedTarget) { + return this.isShown ? this.hide() : this.show(_relatedTarget) + } + + Modal.prototype.show = function (_relatedTarget) { + var that = this + var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) + + this.$element.trigger(e) + + if (this.isShown || e.isDefaultPrevented()) return + + this.isShown = true + + this.checkScrollbar() + this.setScrollbar() + this.$body.addClass('modal-open') + + this.escape() + this.resize() + + this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) + + this.$dialog.on('mousedown.dismiss.bs.modal', function () { + that.$element.one('mouseup.dismiss.bs.modal', function (e) { + if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true + }) + }) + + this.backdrop(function () { + var transition = $.support.transition && that.$element.hasClass('fade') + + if (!that.$element.parent().length) { + that.$element.appendTo(that.$body) // don't move modals dom position + } + + that.$element + .show() + .scrollTop(0) + + that.adjustDialog() + + if (transition) { + that.$element[0].offsetWidth // force reflow + } + + that.$element.addClass('in') + + that.enforceFocus() + + var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) + + transition ? + that.$dialog // wait for modal to slide in + .one('bsTransitionEnd', function () { + that.$element.trigger('focus').trigger(e) + }) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + that.$element.trigger('focus').trigger(e) + }) + } + + Modal.prototype.hide = function (e) { + if (e) e.preventDefault() + + e = $.Event('hide.bs.modal') + + this.$element.trigger(e) + + if (!this.isShown || e.isDefaultPrevented()) return + + this.isShown = false + + this.escape() + this.resize() + + $(document).off('focusin.bs.modal') + + this.$element + .removeClass('in') + .off('click.dismiss.bs.modal') + .off('mouseup.dismiss.bs.modal') + + this.$dialog.off('mousedown.dismiss.bs.modal') + + $.support.transition && this.$element.hasClass('fade') ? + this.$element + .one('bsTransitionEnd', $.proxy(this.hideModal, this)) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + this.hideModal() + } + + Modal.prototype.enforceFocus = function () { + $(document) + .off('focusin.bs.modal') // guard against infinite focus loop + .on('focusin.bs.modal', $.proxy(function (e) { + if (document !== e.target && + this.$element[0] !== e.target && + !this.$element.has(e.target).length) { + this.$element.trigger('focus') + } + }, this)) + } + + Modal.prototype.escape = function () { + if (this.isShown && this.options.keyboard) { + this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { + e.which == 27 && this.hide() + }, this)) + } else if (!this.isShown) { + this.$element.off('keydown.dismiss.bs.modal') + } + } + + Modal.prototype.resize = function () { + if (this.isShown) { + $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) + } else { + $(window).off('resize.bs.modal') + } + } + + Modal.prototype.hideModal = function () { + var that = this + this.$element.hide() + this.backdrop(function () { + that.$body.removeClass('modal-open') + that.resetAdjustments() + that.resetScrollbar() + that.$element.trigger('hidden.bs.modal') + }) + } + + Modal.prototype.removeBackdrop = function () { + this.$backdrop && this.$backdrop.remove() + this.$backdrop = null + } + + Modal.prototype.backdrop = function (callback) { + var that = this + var animate = this.$element.hasClass('fade') ? 'fade' : '' + + if (this.isShown && this.options.backdrop) { + var doAnimate = $.support.transition && animate + + this.$backdrop = $(document.createElement('div')) + .addClass('modal-backdrop ' + animate) + .appendTo(this.$body) + + this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { + if (this.ignoreBackdropClick) { + this.ignoreBackdropClick = false + return + } + if (e.target !== e.currentTarget) return + this.options.backdrop == 'static' + ? this.$element[0].focus() + : this.hide() + }, this)) + + if (doAnimate) this.$backdrop[0].offsetWidth // force reflow + + this.$backdrop.addClass('in') + + if (!callback) return + + doAnimate ? + this.$backdrop + .one('bsTransitionEnd', callback) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callback() + + } else if (!this.isShown && this.$backdrop) { + this.$backdrop.removeClass('in') + + var callbackRemove = function () { + that.removeBackdrop() + callback && callback() + } + $.support.transition && this.$element.hasClass('fade') ? + this.$backdrop + .one('bsTransitionEnd', callbackRemove) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callbackRemove() + + } else if (callback) { + callback() + } + } + + // these following methods are used to handle overflowing modals + + Modal.prototype.handleUpdate = function () { + this.adjustDialog() + } + + Modal.prototype.adjustDialog = function () { + var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight + + this.$element.css({ + paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', + paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' + }) + } + + Modal.prototype.resetAdjustments = function () { + this.$element.css({ + paddingLeft: '', + paddingRight: '' + }) + } + + Modal.prototype.checkScrollbar = function () { + var fullWindowWidth = window.innerWidth + if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 + var documentElementRect = document.documentElement.getBoundingClientRect() + fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) + } + this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth + this.scrollbarWidth = this.measureScrollbar() + } + + Modal.prototype.setScrollbar = function () { + var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) + this.originalBodyPad = document.body.style.paddingRight || '' + var scrollbarWidth = this.scrollbarWidth + if (this.bodyIsOverflowing) { + this.$body.css('padding-right', bodyPad + scrollbarWidth) + $(this.fixedContent).each(function (index, element) { + var actualPadding = element.style.paddingRight + var calculatedPadding = $(element).css('padding-right') + $(element) + .data('padding-right', actualPadding) + .css('padding-right', parseFloat(calculatedPadding) + scrollbarWidth + 'px') + }) + } + } + + Modal.prototype.resetScrollbar = function () { + this.$body.css('padding-right', this.originalBodyPad) + $(this.fixedContent).each(function (index, element) { + var padding = $(element).data('padding-right') + $(element).removeData('padding-right') + element.style.paddingRight = padding ? padding : '' + }) + } + + Modal.prototype.measureScrollbar = function () { // thx walsh + var scrollDiv = document.createElement('div') + scrollDiv.className = 'modal-scrollbar-measure' + this.$body.append(scrollDiv) + var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth + this.$body[0].removeChild(scrollDiv) + return scrollbarWidth + } + + + // MODAL PLUGIN DEFINITION + // ======================= + + function Plugin(option, _relatedTarget) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.modal') + var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('bs.modal', (data = new Modal(this, options))) + if (typeof option == 'string') data[option](_relatedTarget) + else if (options.show) data.show(_relatedTarget) + }) + } + + var old = $.fn.modal + + $.fn.modal = Plugin + $.fn.modal.Constructor = Modal + + + // MODAL NO CONFLICT + // ================= + + $.fn.modal.noConflict = function () { + $.fn.modal = old + return this + } + + + // MODAL DATA-API + // ============== + + $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { + var $this = $(this) + var href = $this.attr('href') + var target = $this.attr('data-target') || + (href && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + + var $target = $(document).find(target) + var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) + + if ($this.is('a')) e.preventDefault() + + $target.one('show.bs.modal', function (showEvent) { + if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown + $target.one('hidden.bs.modal', function () { + $this.is(':visible') && $this.trigger('focus') + }) + }) + Plugin.call($target, option, this) + }) + +}(jQuery); From 5d445ecd8640cd19096432efe8bf4f4b6bd2f6d2 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 5 Jul 2022 08:45:31 +0200 Subject: [PATCH 28/30] COMPATIBILITY: Move all remaining vendor assets into legacy support --- assets/javascripts/legacy/bootbox.js | 717 ++++++++++++++++++++ assets/javascripts/legacy/caret_position.js | 164 +++++ assets/javascripts/wizard-vendor.js | 6 +- 3 files changed, 884 insertions(+), 3 deletions(-) create mode 100644 assets/javascripts/legacy/bootbox.js create mode 100644 assets/javascripts/legacy/caret_position.js diff --git a/assets/javascripts/legacy/bootbox.js b/assets/javascripts/legacy/bootbox.js new file mode 100644 index 00000000..c2520af8 --- /dev/null +++ b/assets/javascripts/legacy/bootbox.js @@ -0,0 +1,717 @@ +// discourse-skip-module + +/** + * bootbox.js v3.2.0 + * + * http://bootboxjs.com/license.txt + */ +var bootbox = + window.bootbox || + (function (document, $) { + /*jshint scripturl:true sub:true */ + + var _locale = "en", + _defaultLocale = "en", + _animate = true, + _backdrop = "static", + _defaultHref = "javascript:;", + _classes = "", + _btnClasses = {}, + _icons = {}, + /* last var should always be the public object we'll return */ + that = {}; + + /** + * public API + */ + that.setLocale = function (locale) { + for (var i in _locales) { + if (i == locale) { + _locale = locale; + return; + } + } + throw new Error("Invalid locale: " + locale); + }; + + that.addLocale = function (locale, translations) { + if (typeof _locales[locale] === "undefined") { + _locales[locale] = {}; + } + for (var str in translations) { + _locales[locale][str] = translations[str]; + } + }; + + that.setIcons = function (icons) { + _icons = icons; + if (typeof _icons !== "object" || _icons === null) { + _icons = {}; + } + }; + + that.setBtnClasses = function (btnClasses) { + _btnClasses = btnClasses; + if (typeof _btnClasses !== "object" || _btnClasses === null) { + _btnClasses = {}; + } + }; + + that.alert = function (/*str, label, cb*/) { + var str = "", + label = _translate("OK"), + cb = null; + + switch (arguments.length) { + case 1: + // no callback, default button label + str = arguments[0]; + break; + case 2: + // callback *or* custom button label dependent on type + str = arguments[0]; + if (typeof arguments[1] == "function") { + cb = arguments[1]; + } else { + label = arguments[1]; + } + break; + case 3: + // callback and custom button label + str = arguments[0]; + label = arguments[1]; + cb = arguments[2]; + break; + default: + throw new Error("Incorrect number of arguments: expected 1-3"); + } + + return that.dialog( + str, + { + // only button (ok) + label: label, + icon: _icons.OK, + class: _btnClasses.OK, + callback: cb, + }, + { + // ensure that the escape key works; either invoking the user's + // callback or true to just close the dialog + onEscape: cb || true, + } + ); + }; + + that.confirm = function (/*str, labelCancel, labelOk, cb*/) { + var str = "", + labelCancel = _translate("CANCEL"), + labelOk = _translate("CONFIRM"), + cb = null; + + switch (arguments.length) { + case 1: + str = arguments[0]; + break; + case 2: + str = arguments[0]; + if (typeof arguments[1] == "function") { + cb = arguments[1]; + } else { + labelCancel = arguments[1]; + } + break; + case 3: + str = arguments[0]; + labelCancel = arguments[1]; + if (typeof arguments[2] == "function") { + cb = arguments[2]; + } else { + labelOk = arguments[2]; + } + break; + case 4: + str = arguments[0]; + labelCancel = arguments[1]; + labelOk = arguments[2]; + cb = arguments[3]; + break; + default: + throw new Error("Incorrect number of arguments: expected 1-4"); + } + + var cancelCallback = function () { + if (typeof cb === "function") { + return cb(false); + } + }; + + var confirmCallback = function () { + if (typeof cb === "function") { + return cb(true); + } + }; + + return that.dialog( + str, + [ + { + // first button (cancel) + label: labelCancel, + icon: _icons.CANCEL, + class: _btnClasses.CANCEL, + callback: cancelCallback, + }, + { + // second button (confirm) + label: labelOk, + icon: _icons.CONFIRM, + class: _btnClasses.CONFIRM, + callback: confirmCallback, + }, + ], + { + // escape key bindings + onEscape: cancelCallback, + } + ); + }; + + that.prompt = function (/*str, labelCancel, labelOk, cb, defaultVal*/) { + var str = "", + labelCancel = _translate("CANCEL"), + labelOk = _translate("CONFIRM"), + cb = null, + defaultVal = ""; + + switch (arguments.length) { + case 1: + str = arguments[0]; + break; + case 2: + str = arguments[0]; + if (typeof arguments[1] == "function") { + cb = arguments[1]; + } else { + labelCancel = arguments[1]; + } + break; + case 3: + str = arguments[0]; + labelCancel = arguments[1]; + if (typeof arguments[2] == "function") { + cb = arguments[2]; + } else { + labelOk = arguments[2]; + } + break; + case 4: + str = arguments[0]; + labelCancel = arguments[1]; + labelOk = arguments[2]; + cb = arguments[3]; + break; + case 5: + str = arguments[0]; + labelCancel = arguments[1]; + labelOk = arguments[2]; + cb = arguments[3]; + defaultVal = arguments[4]; + break; + default: + throw new Error("Incorrect number of arguments: expected 1-5"); + } + + var header = str; + + // let's keep a reference to the form object for later + var form = $("
"); + form.append( + "" + ); + + var cancelCallback = function () { + if (typeof cb === "function") { + // yep, native prompts dismiss with null, whereas native + // confirms dismiss with false... + return cb(null); + } + }; + + var confirmCallback = function () { + if (typeof cb === "function") { + return cb(form.find("input[type=text]").val()); + } + }; + + var div = that.dialog( + form, + [ + { + // first button (cancel) + label: labelCancel, + icon: _icons.CANCEL, + class: _btnClasses.CANCEL, + callback: cancelCallback, + }, + { + // second button (confirm) + label: labelOk, + icon: _icons.CONFIRM, + class: _btnClasses.CONFIRM, + callback: confirmCallback, + }, + ], + { + // prompts need a few extra options + header: header, + // explicitly tell dialog NOT to show the dialog... + show: false, + onEscape: cancelCallback, + } + ); + + // ... the reason the prompt needs to be hidden is because we need + // to bind our own "shown" handler, after creating the modal but + // before any show(n) events are triggered + // @see https://github.com/makeusabrew/bootbox/issues/69 + + div.on("shown", function () { + form.find("input[type=text]").focus(); + + // ensure that submitting the form (e.g. with the enter key) + // replicates the behaviour of a normal prompt() + form.on("submit", function (e) { + e.preventDefault(); + div.find(".btn-primary").click(); + }); + }); + + div.modal("show"); + + return div; + }; + + that.dialog = function (str, handlers, options) { + var buttons = "", + callbacks = []; + + if (!options) { + options = {}; + } + + // check for single object and convert to array if necessary + if (typeof handlers === "undefined") { + handlers = []; + } else if (typeof handlers.length == "undefined") { + handlers = [handlers]; + } + + var i = handlers.length; + while (i--) { + var label = null, + href = null, + _class = "btn-default", + icon = "", + callback = null; + + if ( + typeof handlers[i]["label"] == "undefined" && + typeof handlers[i]["class"] == "undefined" && + typeof handlers[i]["callback"] == "undefined" + ) { + // if we've got nothing we expect, check for condensed format + + var propCount = 0, // condensed will only match if this == 1 + property = null; // save the last property we found + + // be nicer to count the properties without this, but don't think it's possible... + for (var j in handlers[i]) { + property = j; + if (++propCount > 1) { + // forget it, too many properties + break; + } + } + + if (propCount == 1 && typeof handlers[i][j] == "function") { + // matches condensed format of label -> function + handlers[i]["label"] = property; + handlers[i]["callback"] = handlers[i][j]; + } + } + + if (typeof handlers[i]["callback"] == "function") { + callback = handlers[i]["callback"]; + } + + if (handlers[i]["class"]) { + _class = handlers[i]["class"]; + } else if (i == handlers.length - 1 && handlers.length <= 2) { + // always add a primary to the main option in a two-button dialog + _class = "btn-primary"; + } + + // See: https://github.com/makeusabrew/bootbox/pull/114 + // Upgrade to official bootbox release when it gets merged. + if (handlers[i]["link"] !== true) { + _class = "btn " + _class; + } + + if (handlers[i]["label"]) { + label = handlers[i]["label"]; + } else { + label = "Option " + (i + 1); + } + + if (handlers[i]["icon"]) { + icon = handlers[i]["icon"]; + } + + if (handlers[i]["href"]) { + href = handlers[i]["href"]; + } else { + href = _defaultHref; + } + + buttons = + buttons + + "" + + icon + + "" + + label + + ""; + + callbacks[i] = callback; + } + + // @see https://github.com/makeusabrew/bootbox/issues/46#issuecomment-8235302 + // and https://github.com/twitter/bootstrap/issues/4474 + // for an explanation of the inline overflow: hidden + // @see https://github.com/twitter/bootstrap/issues/4854 + // for an explanation of tabIndex=-1 + + var parts = [ + ""); + + var div = $(parts.join("\n")); + + // check whether we should fade in/out + var shouldFade = + typeof options.animate === "undefined" ? _animate : options.animate; + + if (shouldFade) { + div.addClass("fade"); + } + + var optionalClasses = + typeof options.classes === "undefined" ? _classes : options.classes; + if (optionalClasses) { + div.addClass(optionalClasses); + } + + // now we've built up the div properly we can inject the content whether it was a string or a jQuery object + div.find(".modal-body").html(str); + + function onCancel(source) { + // for now source is unused, but it will be in future + var hideModal = null; + if (typeof options.onEscape === "function") { + // @see https://github.com/makeusabrew/bootbox/issues/91 + hideModal = options.onEscape(); + } + + if (hideModal !== false) { + div.modal("hide"); + } + } + + // hook into the modal's keyup trigger to check for the escape key + div.on("keyup.dismiss.modal", function (e) { + // any truthy value passed to onEscape will dismiss the dialog + // as long as the onEscape function (if defined) doesn't prevent it + if (e.which === 27 && options.onEscape !== false) { + onCancel("escape"); + } + }); + + // handle close buttons too + div.on("click", "a.close", function (e) { + e.preventDefault(); + onCancel("close"); + }); + + // well, *if* we have a primary - give the first dom element focus + div.on("shown.bs.modal", function () { + div.find("a.btn-primary:first").focus(); + }); + + div.on("hidden.bs.modal", function () { + div.remove(); + }); + + // wire up button handlers + div.on("click", ".modal-footer a", function (e) { + var self = this; + Ember.run(function () { + var handler = $(self).data("handler"), + cb = callbacks[handler], + hideModal = null; + + // sort of @see https://github.com/makeusabrew/bootbox/pull/68 - heavily adapted + // if we've got a custom href attribute, all bets are off + if ( + typeof handler !== "undefined" && + typeof handlers[handler]["href"] !== "undefined" + ) { + return; + } + + e.preventDefault(); + + if (typeof cb === "function") { + hideModal = cb(e); + } + + // the only way hideModal *will* be false is if a callback exists and + // returns it as a value. in those situations, don't hide the dialog + // @see https://github.com/makeusabrew/bootbox/pull/25 + if (hideModal !== false) { + div.modal("hide"); + } + }); + }); + + // stick the modal right at the bottom of the main body out of the way + (that.$body || $("body")).append(div); + + div.modal({ + // unless explicitly overridden take whatever our default backdrop value is + backdrop: + typeof options.backdrop === "undefined" + ? _backdrop + : options.backdrop, + // ignore bootstrap's keyboard options; we'll handle this ourselves (more fine-grained control) + keyboard: false, + // @ see https://github.com/makeusabrew/bootbox/issues/69 + // we *never* want the modal to be shown before we can bind stuff to it + // this method can also take a 'show' option, but we'll only use that + // later if we need to + show: false, + }); + + // @see https://github.com/makeusabrew/bootbox/issues/64 + // @see https://github.com/makeusabrew/bootbox/issues/60 + // ...caused by... + // @see https://github.com/twitter/bootstrap/issues/4781 + div.on("show", function (e) { + $(document).off("focusin.modal"); + }); + + if (typeof options.show === "undefined" || options.show === true) { + div.modal("show"); + } + + return div; + }; + + /** + * #modal is deprecated in v3; it can still be used but no guarantees are + * made - have never been truly convinced of its merit but perhaps just + * needs a tidyup and some TLC + */ + that.modal = function (/*str, label, options*/) { + var str; + var label; + var options; + + var defaultOptions = { + onEscape: null, + keyboard: true, + backdrop: _backdrop, + }; + + switch (arguments.length) { + case 1: + str = arguments[0]; + break; + case 2: + str = arguments[0]; + if (typeof arguments[1] == "object") { + options = arguments[1]; + } else { + label = arguments[1]; + } + break; + case 3: + str = arguments[0]; + label = arguments[1]; + options = arguments[2]; + break; + default: + throw new Error("Incorrect number of arguments: expected 1-3"); + } + + defaultOptions["header"] = label; + + if (typeof options == "object") { + options = $.extend(defaultOptions, options); + } else { + options = defaultOptions; + } + + return that.dialog(str, [], options); + }; + + that.hideAll = function () { + $(".bootbox").modal("hide"); + }; + + that.animate = function (animate) { + _animate = animate; + }; + + that.backdrop = function (backdrop) { + _backdrop = backdrop; + }; + + that.classes = function (classes) { + _classes = classes; + }; + + /** + * private API + */ + + /** + * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are + * unlikely to be required. If this gets too large it can be split out into separate JS files. + */ + var _locales = { + br: { + OK: "OK", + CANCEL: "Cancelar", + CONFIRM: "Sim", + }, + da: { + OK: "OK", + CANCEL: "Annuller", + CONFIRM: "Accepter", + }, + de: { + OK: "OK", + CANCEL: "Abbrechen", + CONFIRM: "Akzeptieren", + }, + en: { + OK: "OK", + CANCEL: "Cancel", + CONFIRM: "OK", + }, + es: { + OK: "OK", + CANCEL: "Cancelar", + CONFIRM: "Aceptar", + }, + fr: { + OK: "OK", + CANCEL: "Annuler", + CONFIRM: "D'accord", + }, + it: { + OK: "OK", + CANCEL: "Annulla", + CONFIRM: "Conferma", + }, + nl: { + OK: "OK", + CANCEL: "Annuleren", + CONFIRM: "Accepteren", + }, + pl: { + OK: "OK", + CANCEL: "Anuluj", + CONFIRM: "Potwierdź", + }, + ru: { + OK: "OK", + CANCEL: "Отмена", + CONFIRM: "Применить", + }, + zh_CN: { + OK: "OK", + CANCEL: "取消", + CONFIRM: "确认", + }, + zh_TW: { + OK: "OK", + CANCEL: "取消", + CONFIRM: "確認", + }, + }; + + function _translate(str, locale) { + // we assume if no target locale is probided then we should take it from current setting + if (typeof locale === "undefined") { + locale = _locale; + } + if (typeof _locales[locale][str] === "string") { + return _locales[locale][str]; + } + + // if we couldn't find a lookup then try and fallback to a default translation + + if (locale != _defaultLocale) { + return _translate(str, _defaultLocale); + } + + // if we can't do anything then bail out with whatever string was passed in - last resort + return str; + } + + return that; + })(document, window.jQuery); + +// @see https://github.com/makeusabrew/bootbox/issues/71 +window.bootbox = bootbox; + +define("bootbox", ["exports"], function (__exports__) { + __exports__.default = window.bootbox; +}); diff --git a/assets/javascripts/legacy/caret_position.js b/assets/javascripts/legacy/caret_position.js new file mode 100644 index 00000000..2ae02794 --- /dev/null +++ b/assets/javascripts/legacy/caret_position.js @@ -0,0 +1,164 @@ +// discourse-skip-module + +// TODO: This code should be moved to lib, it was heavily modified by us over the years, and mostly written by us +// except for the little snippet from StackOverflow +// +// http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea +var clone = null; + +$.fn.caret = function(elem) { + var getCaret = function(el) { + if (el.selectionStart) { + return el.selectionStart; + } + return 0; + }; + return getCaret(elem || this[0]); +}; + +/** + This is a jQuery plugin to retrieve the caret position in a textarea + + @module $.fn.caretPosition +**/ +$.fn.caretPosition = function(options) { + var after, + before, + getStyles, + guard, + html, + important, + insertSpaceAfterBefore, + letter, + makeCursor, + p, + pPos, + pos, + span, + styles, + textarea, + val; + if (clone) { + clone.remove(); + } + span = $("#pos span"); + textarea = $(this); + + getStyles = function(el) { + if (el.currentStyle) { + return el.currentStyle; + } else { + return document.defaultView.getComputedStyle(el, ""); + } + }; + + important = function(prop) { + return styles.getPropertyValue(prop); + }; + + styles = getStyles(textarea[0]); + clone = $("

").appendTo("body"); + p = clone.find("p"); + + var isRTL = $("html").hasClass("rtl"); + clone.css({ + border: "1px solid black", + padding: important("padding"), + resize: important("resize"), + "max-height": textarea.height() + "px", + "overflow-y": "auto", + "word-wrap": "break-word", + position: "absolute", + left: isRTL ? "auto" : "-7000px", + right: isRTL ? "-7000px" : "auto" + }); + + p.css({ + margin: 0, + padding: 0, + "word-wrap": "break-word", + "letter-spacing": important("letter-spacing"), + "font-family": important("font-family"), + "font-size": important("font-size"), + "line-height": important("line-height") + }); + + clone.width(textarea.width()); + clone.height(textarea.height()); + + pos = + options && (options.pos || options.pos === 0) + ? options.pos + : $.caret(textarea[0]); + + val = textarea.val().replace("\r", ""); + if (options && options.key) { + val = val.substring(0, pos) + options.key + val.substring(pos); + } + before = pos - 1; + after = pos; + insertSpaceAfterBefore = false; + + // if before and after are \n insert a space + if (val[before] === "\n" && val[after] === "\n") { + insertSpaceAfterBefore = true; + } + + guard = function(v) { + var buf; + buf = v.replace(//g, ">"); + buf = buf.replace(/[ ]/g, "​ ​"); + return buf.replace(/\n/g, "
"); + }; + + makeCursor = function(pos, klass, color) { + var l; + l = val.substring(pos, pos + 1); + if (l === "\n") return "
"; + return ( + "" + + guard(l) + + "" + ); + }; + + html = ""; + + if (before >= 0) { + html += + guard(val.substring(0, pos - 1)) + + makeCursor(before, "before", "#d0ffff"); + if (insertSpaceAfterBefore) { + html += makeCursor(0, "post-before", "#d0ffff"); + } + } + + if (after >= 0) { + html += makeCursor(after, "after", "#ffd0ff"); + if (after - 1 < val.length) { + html += guard(val.substring(after + 1)); + } + } + + p.html(html); + clone.scrollTop(textarea.scrollTop()); + letter = p.find("span:first"); + pos = letter.offset(); + if (letter.hasClass("before")) { + pos.left = pos.left + letter.width(); + } + + pPos = p.offset(); + var position = { + left: pos.left - pPos.left, + top: pos.top - pPos.top - clone.scrollTop() + }; + + clone.remove(); + return position; +}; diff --git a/assets/javascripts/wizard-vendor.js b/assets/javascripts/wizard-vendor.js index 8b1cf56f..a0f25d0c 100644 --- a/assets/javascripts/wizard-vendor.js +++ b/assets/javascripts/wizard-vendor.js @@ -2,12 +2,12 @@ //= require legacy/template_include.js //= require legacy/uppy.js -//= require bootstrap-modal.js -//= require bootbox.js +//= require legacy/bootstrap-modal.js +//= require legacy/bootbox.js //= require legacy/virtual-dom //= require legacy/virtual-dom-amd //= require legacy/itsatrap.js -//= require caret_position.js +//= require legacy/caret_position.js //= require legacy/popper.js //= require legacy/tippy.umd.js //= require legacy/discourse-shims.js From e761276ed57acfcb17476dcb73f8b3badbe33cd6 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 5 Jul 2022 09:06:09 +0200 Subject: [PATCH 29/30] COMPATIBILITY: plugin-outlet no longer works as a helper. --- assets/javascripts/wizard/helpers/plugin-outlet.js.es6 | 6 ------ .../wizard/templates/components/plugin-outlet.hbs | 0 2 files changed, 6 deletions(-) delete mode 100644 assets/javascripts/wizard/helpers/plugin-outlet.js.es6 create mode 100644 assets/javascripts/wizard/templates/components/plugin-outlet.hbs diff --git a/assets/javascripts/wizard/helpers/plugin-outlet.js.es6 b/assets/javascripts/wizard/helpers/plugin-outlet.js.es6 deleted file mode 100644 index e6ff81a6..00000000 --- a/assets/javascripts/wizard/helpers/plugin-outlet.js.es6 +++ /dev/null @@ -1,6 +0,0 @@ -import { registerUnbound } from "discourse-common/lib/helpers"; -import Handlebars from "handlebars"; - -export default registerUnbound("plugin-outlet", function () { - return new Handlebars.SafeString(""); -}); diff --git a/assets/javascripts/wizard/templates/components/plugin-outlet.hbs b/assets/javascripts/wizard/templates/components/plugin-outlet.hbs new file mode 100644 index 00000000..e69de29b From d64b6b50dd5a16c4737e9e28a41c3653019784d8 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Sat, 9 Jul 2022 10:24:27 +0200 Subject: [PATCH 30/30] COMPATIBILITY: The ember resolver has been "modernized".. https://github.com/discourse/discourse/commit/fc36ac6cde2afaf8c5e9e6df22e71a7b3044c0a2 --- assets/javascripts/legacy/discourse-loader.js | 3 +- assets/javascripts/legacy/raw-templates.js | 42 +++ assets/javascripts/legacy/resolver.js | 337 ++++++++++++++++++ assets/javascripts/wizard-custom.js | 22 +- .../javascripts/wizard-raw-templates.js.erb | 4 +- assets/javascripts/wizard/application.js.es6 | 2 +- .../components/wizard-composer-editor.js.es6 | 2 +- 7 files changed, 406 insertions(+), 6 deletions(-) create mode 100644 assets/javascripts/legacy/raw-templates.js create mode 100644 assets/javascripts/legacy/resolver.js diff --git a/assets/javascripts/legacy/discourse-loader.js b/assets/javascripts/legacy/discourse-loader.js index ec77a6d2..ee9a2a07 100644 --- a/assets/javascripts/legacy/discourse-loader.js +++ b/assets/javascripts/legacy/discourse-loader.js @@ -7,7 +7,8 @@ var define, requirejs; let ALIASES = { "ember-addons/ember-computed-decorators": "discourse-common/utils/decorators", - "discourse/lib/raw-templates": "discourse-common/lib/raw-templates", + "discourse/lib/raw-templates": "discourse/plugins/discourse-custom-wizard/legacy/raw-templates", + "discourse-common/lib/raw-templates": "discourse/plugins/discourse-custom-wizard/legacy/raw-templates", "preload-store": "discourse/lib/preload-store", "fixtures/user_fixtures": "discourse/tests/fixtures/user-fixtures", }; diff --git a/assets/javascripts/legacy/raw-templates.js b/assets/javascripts/legacy/raw-templates.js new file mode 100644 index 00000000..5e67bbd8 --- /dev/null +++ b/assets/javascripts/legacy/raw-templates.js @@ -0,0 +1,42 @@ +import { getResolverOption } from "./resolver"; + +export const __DISCOURSE_RAW_TEMPLATES = {}; + +export function addRawTemplate(name, template, opts = {}) { + // Core templates should never overwrite themes / plugins + if (opts.core && __DISCOURSE_RAW_TEMPLATES[name]) { + return; + } + __DISCOURSE_RAW_TEMPLATES[name] = template; +} + +export function removeRawTemplate(name) { + delete __DISCOURSE_RAW_TEMPLATES[name]; +} + +export function findRawTemplate(name) { + if (getResolverOption("mobileView")) { + return ( + __DISCOURSE_RAW_TEMPLATES[`javascripts/mobile/${name}`] || + __DISCOURSE_RAW_TEMPLATES[`javascripts/${name}`] || + __DISCOURSE_RAW_TEMPLATES[`mobile/${name}`] || + __DISCOURSE_RAW_TEMPLATES[name] + ); + } + + return ( + __DISCOURSE_RAW_TEMPLATES[`javascripts/${name}`] || + __DISCOURSE_RAW_TEMPLATES[name] + ); +} + +export function buildRawConnectorCache(findOutlets) { + let result = {}; + findOutlets(__DISCOURSE_RAW_TEMPLATES, (outletName, resource) => { + result[outletName] = result[outletName] || []; + result[outletName].push({ + template: __DISCOURSE_RAW_TEMPLATES[resource], + }); + }); + return result; +} diff --git a/assets/javascripts/legacy/resolver.js b/assets/javascripts/legacy/resolver.js new file mode 100644 index 00000000..5980b39b --- /dev/null +++ b/assets/javascripts/legacy/resolver.js @@ -0,0 +1,337 @@ + +/* global Ember */ +import { classify, dasherize, decamelize } from "@ember/string"; +import deprecated from "discourse-common/lib/deprecated"; +import { findHelper } from "discourse-common/lib/helpers"; +import { get } from "@ember/object"; +import SuffixTrie from "discourse-common/lib/suffix-trie"; + +let _options = {}; +let moduleSuffixTrie = null; + +export function setResolverOption(name, value) { + _options[name] = value; +} + +export function getResolverOption(name) { + return _options[name]; +} + +export function clearResolverOptions() { + _options = {}; +} + +function parseName(fullName) { + const nameParts = fullName.split(":"); + const type = nameParts[0]; + let fullNameWithoutType = nameParts[1]; + const namespace = get(this, "namespace"); + const root = namespace; + + return { + fullName, + type, + fullNameWithoutType, + name: fullNameWithoutType, + root, + resolveMethodName: "resolve" + classify(type), + }; +} + +function lookupModuleBySuffix(suffix) { + if (!moduleSuffixTrie) { + moduleSuffixTrie = new SuffixTrie("/"); + Object.keys(requirejs.entries).forEach((name) => { + if (!name.includes("/templates/")) { + moduleSuffixTrie.add(name); + } + }); + } + return moduleSuffixTrie.withSuffix(suffix, 1)[0]; +} + +export function buildResolver(baseName) { + return Ember.DefaultResolver.extend({ + parseName, + + resolveRouter(parsedName) { + const routerPath = `${baseName}/router`; + if (requirejs.entries[routerPath]) { + const module = requirejs(routerPath, null, null, true); + return module.default; + } + return this._super(parsedName); + }, + + normalize(fullName) { + if (fullName === "app-events:main") { + deprecated( + "`app-events:main` has been replaced with `service:app-events`", + { since: "2.4.0", dropFrom: "2.9.0.beta1" } + ); + return "service:app-events"; + } + + for (const [key, value] of Object.entries({ + "controller:discovery.categoryWithID": "controller:discovery.category", + "controller:discovery.parentCategory": "controller:discovery.category", + "controller:tags-show": "controller:tag-show", + "controller:tags.show": "controller:tag.show", + "controller:tagsShow": "controller:tagShow", + "route:discovery.categoryWithID": "route:discovery.category", + "route:discovery.parentCategory": "route:discovery.category", + "route:tags-show": "route:tag-show", + "route:tags.show": "route:tag.show", + "route:tagsShow": "route:tagShow", + })) { + if (fullName === key) { + deprecated(`${key} was replaced with ${value}`, { since: "2.6.0" }); + return value; + } + } + + const split = fullName.split(":"); + if (split.length > 1) { + const appBase = `${baseName}/${split[0]}s/`; + const adminBase = "admin/" + split[0] + "s/"; + const wizardBase = "wizard/" + split[0] + "s/"; + + // Allow render 'admin/templates/xyz' too + split[1] = split[1].replace(".templates", "").replace("/templates", ""); + + // Try slashes + let dashed = dasherize(split[1].replace(/\./g, "/")); + if ( + requirejs.entries[appBase + dashed] || + requirejs.entries[adminBase + dashed] || + requirejs.entries[wizardBase + dashed] + ) { + return split[0] + ":" + dashed; + } + + // Try with dashes instead of slashes + dashed = dasherize(split[1].replace(/\./g, "-")); + if ( + requirejs.entries[appBase + dashed] || + requirejs.entries[adminBase + dashed] || + requirejs.entries[wizardBase + dashed] + ) { + return split[0] + ":" + dashed; + } + } + return this._super(fullName); + }, + + customResolve(parsedName) { + // If we end with the name we want, use it. This allows us to define components within plugins. + const suffix = parsedName.type + "s/" + parsedName.fullNameWithoutType, + dashed = dasherize(suffix), + moduleName = lookupModuleBySuffix(dashed); + + let module; + if (moduleName) { + module = requirejs(moduleName, null, null, true /* force sync */); + if (module && module["default"]) { + module = module["default"]; + } + } + return module; + }, + + resolveWidget(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveAdapter(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveModel(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveView(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveHelper(parsedName) { + return ( + findHelper(parsedName.fullNameWithoutType) || + this.customResolve(parsedName) || + this._super(parsedName) + ); + }, + + resolveController(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveComponent(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveService(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveRawView(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveRoute(parsedName) { + if (parsedName.fullNameWithoutType === "basic") { + return requirejs("discourse/routes/discourse", null, null, true) + .default; + } + + return this.customResolve(parsedName) || this._super(parsedName); + }, + + findLoadingTemplate(parsedName) { + if (parsedName.fullNameWithoutType.match(/loading$/)) { + return Ember.TEMPLATES.loading; + } + }, + + findConnectorTemplate(parsedName) { + const full = parsedName.fullNameWithoutType.replace("components/", ""); + if (full.indexOf("connectors") === 0) { + return Ember.TEMPLATES[`javascripts/${full}`]; + } + }, + + resolveTemplate(parsedName) { + return ( + this.findPluginMobileTemplate(parsedName) || + this.findPluginTemplate(parsedName) || + this.findMobileTemplate(parsedName) || + this.findTemplate(parsedName) || + this.findLoadingTemplate(parsedName) || + this.findConnectorTemplate(parsedName) || + Ember.TEMPLATES.not_found + ); + }, + + findPluginTemplate(parsedName) { + const pluginParsedName = this.parseName( + parsedName.fullName.replace("template:", "template:javascripts/") + ); + return this.findTemplate(pluginParsedName); + }, + + findPluginMobileTemplate(parsedName) { + if (_options.mobileView) { + let pluginParsedName = this.parseName( + parsedName.fullName.replace( + "template:", + "template:javascripts/mobile/" + ) + ); + return this.findTemplate(pluginParsedName); + } + }, + + findMobileTemplate(parsedName) { + if (_options.mobileView) { + let mobileParsedName = this.parseName( + parsedName.fullName.replace("template:", "template:mobile/") + ); + return this.findTemplate(mobileParsedName); + } + }, + + findTemplate(parsedName) { + const withoutType = parsedName.fullNameWithoutType, + slashedType = withoutType.replace(/\./g, "/"), + decamelized = decamelize(withoutType), + dashed = decamelized.replace(/\./g, "-").replace(/\_/g, "-"), + templates = Ember.TEMPLATES; + + return ( + this._super(parsedName) || + templates[slashedType] || + templates[withoutType] || + templates[withoutType.replace(/\.raw$/, "")] || + templates[dashed] || + templates[decamelized.replace(/\./, "/")] || + templates[decamelized.replace(/\_/, "/")] || + templates[`${baseName}/templates/${withoutType}`] || + this.findAdminTemplate(parsedName) || + this.findWizardTemplate(parsedName) || + this.findUnderscoredTemplate(parsedName) + ); + }, + + findUnderscoredTemplate(parsedName) { + let decamelized = decamelize(parsedName.fullNameWithoutType); + let underscored = decamelized.replace(/\-/g, "_"); + return Ember.TEMPLATES[underscored]; + }, + + // Try to find a template within a special admin namespace, e.g. adminEmail => admin/templates/email + // (similar to how discourse lays out templates) + findAdminTemplate(parsedName) { + let decamelized = decamelize(parsedName.fullNameWithoutType); + if (decamelized.indexOf("components") === 0) { + let comPath = `admin/templates/${decamelized}`; + const compTemplate = + Ember.TEMPLATES[`javascripts/${comPath}`] || Ember.TEMPLATES[comPath]; + if (compTemplate) { + return compTemplate; + } + } + + if (decamelized === "javascripts/admin") { + return Ember.TEMPLATES["admin/templates/admin"]; + } + + if ( + decamelized.indexOf("admin") === 0 || + decamelized.indexOf("javascripts/admin") === 0 + ) { + decamelized = decamelized.replace(/^admin\_/, "admin/templates/"); + decamelized = decamelized.replace(/^admin\./, "admin/templates/"); + decamelized = decamelized.replace(/\./g, "_"); + + const dashed = decamelized.replace(/_/g, "-"); + return ( + Ember.TEMPLATES[decamelized] || + Ember.TEMPLATES[dashed] || + Ember.TEMPLATES[dashed.replace("admin-", "admin/")] + ); + } + }, + + findWizardTemplate(parsedName) { + let decamelized = decamelize(parsedName.fullNameWithoutType); + if (decamelized.startsWith("components")) { + let comPath = `wizard/templates/${decamelized}`; + const compTemplate = + Ember.TEMPLATES[`javascripts/${comPath}`] || Ember.TEMPLATES[comPath]; + if (compTemplate) { + return compTemplate; + } + } + + if (decamelized === "javascripts/wizard") { + return Ember.TEMPLATES["wizard/templates/wizard"]; + } + + if ( + decamelized.startsWith("wizard") || + decamelized.startsWith("javascripts/wizard") + ) { + decamelized = decamelized.replace(/^wizard\_/, "wizard/templates/"); + decamelized = decamelized.replace(/^wizard\./, "wizard/templates/"); + decamelized = decamelized.replace(/\./g, "_"); + + const dashed = decamelized.replace(/_/g, "-"); + return ( + Ember.TEMPLATES[decamelized] || + Ember.TEMPLATES[dashed] || + Ember.TEMPLATES[dashed.replace("wizard-", "wizard/")] + ); + } + }, + }); +} diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js index 05a6fcb3..0dd67833 100644 --- a/assets/javascripts/wizard-custom.js +++ b/assets/javascripts/wizard-custom.js @@ -1,7 +1,27 @@ // discourse-skip-module //= require_tree_discourse truth-helpers/addon -//= require_tree_discourse discourse-common/addon + +//= require legacy/resolver +//= require legacy/raw-templates + +//= require_tree_discourse discourse-common/addon/config +//= require_tree_discourse discourse-common/addon/helpers +//= require discourse-common/addon/lib/get-owner +//= require discourse-common/addon/lib/object +//= require discourse-common/addon/lib/helpers +//= require discourse-common/addon/lib/get-url +//= require discourse-common/addon/lib/deprecated +//= require discourse-common/addon/lib/suffix-trie +//= require discourse-common/addon/lib/debounce +//= require discourse-common/addon/lib/raw-handlebars +//= require discourse-common/addon/lib/raw-handlebars-helpers +//= require discourse-common/addon/lib/escape +//= require discourse-common/addon/lib/icon-library +//= require discourse-common/addon/lib/attribute-hook +//= require discourse-common/addon/lib/dom-from-string +//= require_tree_discourse discourse-common/addon/utils + //= require_tree_discourse select-kit/addon //= require_tree_discourse discourse/app/lib //= require_tree_discourse discourse/app/mixins diff --git a/assets/javascripts/wizard-raw-templates.js.erb b/assets/javascripts/wizard-raw-templates.js.erb index 55b255e0..ff216180 100644 --- a/assets/javascripts/wizard-raw-templates.js.erb +++ b/assets/javascripts/wizard-raw-templates.js.erb @@ -22,14 +22,14 @@ Discourse.unofficial_plugins.each do |plugin| compiled = Barber::Precompiler.new().compile(File.read(f)) result << " (function() { - requirejs('discourse-common/lib/raw-templates').addRawTemplate(#{compiled}); + requirejs('discourse/plugins/discourse-custom-wizard/legacy/raw-templates').addRawTemplate(#{compiled}); })(); " end result << " (function() { - window.__DISCOURSE_RAW_TEMPLATES = requirejs('discourse-common/lib/raw-templates').__DISCOURSE_RAW_TEMPLATES; + window.__DISCOURSE_RAW_TEMPLATES = requirejs('discourse/plugins/discourse-custom-wizard/legacy/raw-templates').__DISCOURSE_RAW_TEMPLATES; })(); " end diff --git a/assets/javascripts/wizard/application.js.es6 b/assets/javascripts/wizard/application.js.es6 index 012949b3..dd2d09f8 100644 --- a/assets/javascripts/wizard/application.js.es6 +++ b/assets/javascripts/wizard/application.js.es6 @@ -1,4 +1,4 @@ -import { buildResolver } from "discourse-common/resolver"; +import { buildResolver } from "discourse/plugins/discourse-custom-wizard/legacy/resolver"; import Application from "@ember/application"; import WizardInitializer from "./lib/initialize/wizard"; import { isTesting } from "discourse-common/config/environment"; diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 index 4f44d439..de70986b 100644 --- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 @@ -3,7 +3,7 @@ import { default as discourseComputed, on, } from "discourse-common/utils/decorators"; -import { findRawTemplate } from "discourse-common/lib/raw-templates"; +import { findRawTemplate } from "discourse/plugins/discourse-custom-wizard/legacy/raw-templates"; import { scheduleOnce } from "@ember/runloop"; import { caretPosition, inCodeBlock } from "discourse/lib/utilities"; import highlightSyntax from "discourse/lib/highlight-syntax";