diff --git a/app/assets/javascripts/trln_argon/advanced_search_scope.js b/app/assets/javascripts/trln_argon/advanced_search_scope.js
index 094369cf..b5c2f53a 100644
--- a/app/assets/javascripts/trln_argon/advanced_search_scope.js
+++ b/app/assets/javascripts/trln_argon/advanced_search_scope.js
@@ -16,13 +16,5 @@ Blacklight.onLoad(function() {
var action = $(this).val();
$(".advanced").attr("action", "/" + action);
});
-
- // fix labeling for fields generated by Chosen jquery library (https://harvesthq.github.io/chosen/)
- $("[id$='_chosen']").each(function() {
- $the_id = $(this).attr('id');
- $aria_label = $(this).attr('id').replace('_chosen', '_label');
- $('#' + $the_id + ' .chosen-search-input').attr('aria-labelledby', $aria_label);
- });
-
});
});
\ No newline at end of file
diff --git a/app/assets/javascripts/trln_argon/advanced_search_select.js b/app/assets/javascripts/trln_argon/advanced_search_select.js
deleted file mode 100644
index 3c61408a..00000000
--- a/app/assets/javascripts/trln_argon/advanced_search_select.js
+++ /dev/null
@@ -1,7 +0,0 @@
-Blacklight.onLoad(function() {
- $('.advanced-search-facet-select').chosen({
- allow_single_deselect: true,
- no_results_text: 'No results matched',
- width: '100%'
- });
-});
diff --git a/app/assets/javascripts/trln_argon/controllers/multi_select_controller.js b/app/assets/javascripts/trln_argon/controllers/multi_select_controller.js
new file mode 100644
index 00000000..2b256104
--- /dev/null
+++ b/app/assets/javascripts/trln_argon/controllers/multi_select_controller.js
@@ -0,0 +1,27 @@
+document.addEventListener("DOMContentLoaded", function () {
+ if (window.Stimulus) {
+ // Only register the controller if Stimulus is loaded
+ const application = window.Stimulus.Application.start();
+
+ application.register("multi-select", class extends Stimulus.Controller {
+ static values = { plugins: Array };
+
+ connect() {
+ this.select = new TomSelect(this.element, {
+ plugins: this.pluginsValue,
+ render: {
+ item: function (data, escape) {
+ return `
${escape(data.text)}
`;
+ },
+ },
+ });
+ }
+
+ disconnect() {
+ this.select?.destroy();
+ }
+ });
+ } else {
+ console.error("Stimulus is not loaded yet.");
+ }
+ });
diff --git a/app/assets/javascripts/trln_argon/trln_argon.js b/app/assets/javascripts/trln_argon/trln_argon.js
index d48ac739..c7572493 100644
--- a/app/assets/javascripts/trln_argon/trln_argon.js
+++ b/app/assets/javascripts/trln_argon/trln_argon.js
@@ -4,10 +4,8 @@
//= require trln_argon/progressive_links.js
//= require trln_argon/physical_media_facet.js
//= require bootstrap
-//= require chosen.jquery.js
//= require blacklight/hierarchy/hierarchy.js
//= require trln_argon/location_facet.js
-//= require trln_argon/advanced_search_select.js
//= require trln_argon/results_count_for_toggle.js
//= require trln_argon/google_books_preview.js
//= require trln_argon/fulltext_links.js
@@ -16,3 +14,6 @@
//= require trln_argon/typeahead.bundle.js
//= require trln_argon/advanced_search_tt_menu.js
//= require trln_argon/autocomplete.js
+//= require tom-select/tom-select.complete.js
+//= require trln_argon/controllers/multi_select_controller.js
+//= require stimulus/stimulus.umd.js
diff --git a/app/assets/stylesheets/trln_argon/blacklight_advanced_search.scss b/app/assets/stylesheets/trln_argon/blacklight_advanced_search.scss
index bdc70df4..b99fae12 100644
--- a/app/assets/stylesheets/trln_argon/blacklight_advanced_search.scss
+++ b/app/assets/stylesheets/trln_argon/blacklight_advanced_search.scss
@@ -11,10 +11,6 @@ h2.query-criteria-heading label {
.col-form-label {
text-align: left;
}
-
- .chosen-search-input {
- color: #767676;
- }
}
#advanced_search {
@@ -58,3 +54,14 @@ h2.query-criteria-heading label {
font-weight: 700;
}
}
+
+.card {
+ margin-top: 1rem !important; /* Override with higher value */
+ margin-bottom: 1rem !important; /* Override with higher value */
+}
+
+.ts-wrapper.multi .ts-control [data-value] {
+ background-color: #eeeeee !important;;
+ background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%) !important;
+ text-shadow: none !important; /* Remove text-shadow */
+}
diff --git a/app/assets/stylesheets/trln_argon/trln_argon_dependencies.scss b/app/assets/stylesheets/trln_argon/trln_argon_dependencies.scss
index 3c361d47..86f87fbd 100644
--- a/app/assets/stylesheets/trln_argon/trln_argon_dependencies.scss
+++ b/app/assets/stylesheets/trln_argon/trln_argon_dependencies.scss
@@ -10,4 +10,7 @@
@import 'font-awesome';
-@import 'chosen';
+// @import 'chosen';
+
+@import 'tom-select/tom-select.default.min.css';
+@import 'tom-select/tom-select.bootstrap5.min.css';
diff --git a/app/components/trln_argon/multi_select_facet_component.html.erb b/app/components/trln_argon/multi_select_facet_component.html.erb
new file mode 100644
index 00000000..6b1e6c12
--- /dev/null
+++ b/app/components/trln_argon/multi_select_facet_component.html.erb
@@ -0,0 +1,17 @@
+<%= render(@layout.new(facet_field: @facet_field)) do |component| %>
+ <% component.with_label do %>
+ <%= @facet_field.label %>
+ <% end %>
+ <% component.with_body do %>
+
+
+
+ <% end %>
+ <% end %>
+
\ No newline at end of file
diff --git a/app/components/trln_argon/multi_select_facet_component.rb b/app/components/trln_argon/multi_select_facet_component.rb
new file mode 100644
index 00000000..6d5d3276
--- /dev/null
+++ b/app/components/trln_argon/multi_select_facet_component.rb
@@ -0,0 +1,59 @@
+module TrlnArgon
+ # Multi select facet component using TomSelect
+ class MultiSelectFacetComponent < Blacklight::Component
+ def initialize(facet_field:, layout: nil)
+ super()
+ @facet_field = facet_field
+ @layout = layout == false ? FacetFieldNoLayoutComponent : Blacklight::FacetFieldComponent
+ end
+
+ # @return [Boolean] whether to render the component
+ def render?
+ presenters.any?
+ end
+
+ # @return [Array] array of facet field presenters
+ def presenters
+ return [] unless @facet_field.paginator
+
+ return to_enum(:presenters) unless block_given?
+
+ @facet_field.paginator.items.each do |item|
+ yield @facet_field.facet_field
+ .item_presenter
+ .new(item, @facet_field.facet_field, helpers, @facet_field.key, @facet_field.search_state)
+ end
+ end
+
+ # @return [Hash] HTML attributes for the select element
+ def select_attributes
+ {
+ class: "#{@facet_field.key}-select",
+ name: "f_inclusive[#{@facet_field.key}][]",
+ placeholder: I18n.t('facets.advanced_search.placeholder'),
+ multiple: true,
+ data: {
+ controller: 'multi-select',
+ multi_select_plugins_value: select_plugins.to_json
+ }
+ }
+ end
+
+ # @return [Hash] HTML attributes for the option elements within the select element
+ def option_attributes(presenter:)
+ {
+ value: presenter.value,
+ selected: presenter.selected? ? 'selected' : nil
+ }
+ end
+
+ # TomSelect functionality can be expanded with plugins. `checkbox_options`
+ # allow us to use the existing advanced search facet logic by using checkboxes.
+ # More plugins can be found here: https://tom-select.js.org/plugins/
+ #
+ # @return [Array] array of TomSelect plugins
+ def select_plugins
+ %w[checkbox_options caret_position input_autogrow clear_button]
+ end
+ end
+end
diff --git a/config/locales/trln_argon.en.yml b/config/locales/trln_argon.en.yml
index 0179e33e..4a759cd2 100644
--- a/config/locales/trln_argon.en.yml
+++ b/config/locales/trln_argon.en.yml
@@ -66,6 +66,7 @@ en:
sort_label: "Sort results by"
start_over: "Start over"
search_btn: 'Search'
+ placeholder: 'Select facets...'
trln_argon:
diff --git a/lib/trln_argon/controller_override.rb b/lib/trln_argon/controller_override.rb
index 08ca1a55..3c9a7d56 100644
--- a/lib/trln_argon/controller_override.rb
+++ b/lib/trln_argon/controller_override.rb
@@ -205,13 +205,13 @@ module ControllerOverride
collapse: false,
show: true,
component: TrlnArgon::FacetFieldCheckboxesComponent,
- advanced_search_component: TrlnArgon::AdvancedSearchFacetFieldComponent
+ advanced_search_component: TrlnArgon::MultiSelectFacetComponent
config.add_facet_field TrlnArgon::Fields::AVAILABLE_FACET.to_s,
label: TrlnArgon::Fields::AVAILABLE_FACET.label,
limit: true,
collapse: false,
show: true,
- advanced_search_component: TrlnArgon::AdvancedSearchFacetFieldComponent
+ advanced_search_component: TrlnArgon::MultiSelectFacetComponent
config.add_facet_field TrlnArgon::Fields::LOCATION_HIERARCHY_FACET.to_s,
label: TrlnArgon::Fields::LOCATION_HIERARCHY_FACET.label,
limit: -1,
@@ -221,17 +221,17 @@ module ControllerOverride
# This helper is still needed for the label in constraints
helper_method: :location_filter_display,
component: Blacklight::Hierarchy::FacetFieldListComponent,
- advanced_search_component: TrlnArgon::AdvancedSearchFacetFieldComponent
+ advanced_search_component: TrlnArgon::MultiSelectFacetComponent
config.add_facet_field TrlnArgon::Fields::RESOURCE_TYPE_FACET.to_s,
label: TrlnArgon::Fields::RESOURCE_TYPE_FACET.label,
limit: true,
collapse: false,
- advanced_search_component: TrlnArgon::AdvancedSearchFacetFieldComponent
+ advanced_search_component: TrlnArgon::MultiSelectFacetComponent
config.add_facet_field TrlnArgon::Fields::PHYSICAL_MEDIA_FACET.to_s,
label: TrlnArgon::Fields::PHYSICAL_MEDIA_FACET.label,
limit: true,
collapse: false,
- advanced_search_component: TrlnArgon::AdvancedSearchFacetFieldComponent
+ advanced_search_component: TrlnArgon::MultiSelectFacetComponent
config.add_facet_field TrlnArgon::Fields::SUBJECT_TOPICAL_FACET.to_s,
label: TrlnArgon::Fields::SUBJECT_TOPICAL_FACET.label,
limit: true,
@@ -244,11 +244,11 @@ module ControllerOverride
# This helper is still needed for the label in constraints
helper_method: :call_number_filter_display,
component: Blacklight::Hierarchy::FacetFieldListComponent,
- advanced_search_component: TrlnArgon::AdvancedSearchFacetFieldComponent
+ advanced_search_component: TrlnArgon::MultiSelectFacetComponent
config.add_facet_field TrlnArgon::Fields::LANGUAGE_FACET.to_s,
label: TrlnArgon::Fields::LANGUAGE_FACET.label,
limit: true,
- advanced_search_component: TrlnArgon::AdvancedSearchFacetFieldComponent
+ advanced_search_component: TrlnArgon::MultiSelectFacetComponent
# See Range Facet Configuration options:
# https://github.com/projectblacklight/blacklight_range_limit?tab=readme-ov-file#range-facet-configuration
@@ -284,7 +284,7 @@ module ControllerOverride
fq: "#{TrlnArgon::Fields::DATE_CATALOGED_FACET}:[NOW-3MONTH/DAY TO NOW]" } },
label: TrlnArgon::Fields::DATE_CATALOGED_FACET.label,
limit: true,
- advanced_search_component: TrlnArgon::AdvancedSearchFacetFieldComponent
+ advanced_search_component: TrlnArgon::MultiSelectFacetComponent
# Hierarchical facet configuration
# See: https://github.com/sul-dlss/blacklight-hierarchy/blob/main/README.md
diff --git a/vendor/assets/javascripts/chosen.jquery.js b/vendor/assets/javascripts/chosen.jquery.js
deleted file mode 100644
index 5f2df671..00000000
--- a/vendor/assets/javascripts/chosen.jquery.js
+++ /dev/null
@@ -1,1359 +0,0 @@
-/*!
-Chosen, a Select Box Enhancer for jQuery and Prototype
-by Patrick Filler for Harvest, http://getharvest.com
-
-Version 1.8.7
-Full source at https://github.com/harvesthq/chosen
-Copyright (c) 2011-2018 Harvest http://getharvest.com
-
-MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
-This file is generated by `grunt build`, do not edit it by hand.
-*/
-
-(function() {
- var $, AbstractChosen, Chosen, SelectParser,
- bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
- extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- hasProp = {}.hasOwnProperty;
-
- SelectParser = (function() {
- function SelectParser() {
- this.options_index = 0;
- this.parsed = [];
- }
-
- SelectParser.prototype.add_node = function(child) {
- if (child.nodeName.toUpperCase() === "OPTGROUP") {
- return this.add_group(child);
- } else {
- return this.add_option(child);
- }
- };
-
- SelectParser.prototype.add_group = function(group) {
- var group_position, i, len, option, ref, results1;
- group_position = this.parsed.length;
- this.parsed.push({
- array_index: group_position,
- group: true,
- label: group.label,
- title: group.title ? group.title : void 0,
- children: 0,
- disabled: group.disabled,
- classes: group.className
- });
- ref = group.childNodes;
- results1 = [];
- for (i = 0, len = ref.length; i < len; i++) {
- option = ref[i];
- results1.push(this.add_option(option, group_position, group.disabled));
- }
- return results1;
- };
-
- SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
- if (option.nodeName.toUpperCase() === "OPTION") {
- if (option.text !== "") {
- if (group_position != null) {
- this.parsed[group_position].children += 1;
- }
- this.parsed.push({
- array_index: this.parsed.length,
- options_index: this.options_index,
- value: option.value,
- text: option.text,
- html: option.innerHTML,
- title: option.title ? option.title : void 0,
- selected: option.selected,
- disabled: group_disabled === true ? group_disabled : option.disabled,
- group_array_index: group_position,
- group_label: group_position != null ? this.parsed[group_position].label : null,
- classes: option.className,
- style: option.style.cssText
- });
- } else {
- this.parsed.push({
- array_index: this.parsed.length,
- options_index: this.options_index,
- empty: true
- });
- }
- return this.options_index += 1;
- }
- };
-
- return SelectParser;
-
- })();
-
- SelectParser.select_to_array = function(select) {
- var child, i, len, parser, ref;
- parser = new SelectParser();
- ref = select.childNodes;
- for (i = 0, len = ref.length; i < len; i++) {
- child = ref[i];
- parser.add_node(child);
- }
- return parser.parsed;
- };
-
- AbstractChosen = (function() {
- function AbstractChosen(form_field, options1) {
- this.form_field = form_field;
- this.options = options1 != null ? options1 : {};
- this.label_click_handler = bind(this.label_click_handler, this);
- if (!AbstractChosen.browser_is_supported()) {
- return;
- }
- this.is_multiple = this.form_field.multiple;
- this.set_default_text();
- this.set_default_values();
- this.setup();
- this.set_up_html();
- this.register_observers();
- this.on_ready();
- }
-
- AbstractChosen.prototype.set_default_values = function() {
- this.click_test_action = (function(_this) {
- return function(evt) {
- return _this.test_active_click(evt);
- };
- })(this);
- this.activate_action = (function(_this) {
- return function(evt) {
- return _this.activate_field(evt);
- };
- })(this);
- this.active_field = false;
- this.mouse_on_container = false;
- this.results_showing = false;
- this.result_highlighted = null;
- this.is_rtl = this.options.rtl || /\bchosen-rtl\b/.test(this.form_field.className);
- this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false;
- this.disable_search_threshold = this.options.disable_search_threshold || 0;
- this.disable_search = this.options.disable_search || false;
- this.enable_split_word_search = this.options.enable_split_word_search != null ? this.options.enable_split_word_search : true;
- this.group_search = this.options.group_search != null ? this.options.group_search : true;
- this.search_contains = this.options.search_contains || false;
- this.single_backstroke_delete = this.options.single_backstroke_delete != null ? this.options.single_backstroke_delete : true;
- this.max_selected_options = this.options.max_selected_options || Infinity;
- this.inherit_select_classes = this.options.inherit_select_classes || false;
- this.display_selected_options = this.options.display_selected_options != null ? this.options.display_selected_options : true;
- this.display_disabled_options = this.options.display_disabled_options != null ? this.options.display_disabled_options : true;
- this.include_group_label_in_selected = this.options.include_group_label_in_selected || false;
- this.max_shown_results = this.options.max_shown_results || Number.POSITIVE_INFINITY;
- this.case_sensitive_search = this.options.case_sensitive_search || false;
- return this.hide_results_on_select = this.options.hide_results_on_select != null ? this.options.hide_results_on_select : true;
- };
-
- AbstractChosen.prototype.set_default_text = function() {
- if (this.form_field.getAttribute("data-placeholder")) {
- this.default_text = this.form_field.getAttribute("data-placeholder");
- } else if (this.is_multiple) {
- this.default_text = this.options.placeholder_text_multiple || this.options.placeholder_text || AbstractChosen.default_multiple_text;
- } else {
- this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || AbstractChosen.default_single_text;
- }
- this.default_text = this.escape_html(this.default_text);
- return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text;
- };
-
- AbstractChosen.prototype.choice_label = function(item) {
- if (this.include_group_label_in_selected && (item.group_label != null)) {
- return "" + (this.escape_html(item.group_label)) + "" + item.html;
- } else {
- return item.html;
- }
- };
-
- AbstractChosen.prototype.mouse_enter = function() {
- return this.mouse_on_container = true;
- };
-
- AbstractChosen.prototype.mouse_leave = function() {
- return this.mouse_on_container = false;
- };
-
- AbstractChosen.prototype.input_focus = function(evt) {
- if (this.is_multiple) {
- if (!this.active_field) {
- return setTimeout(((function(_this) {
- return function() {
- return _this.container_mousedown();
- };
- })(this)), 50);
- }
- } else {
- if (!this.active_field) {
- return this.activate_field();
- }
- }
- };
-
- AbstractChosen.prototype.input_blur = function(evt) {
- if (!this.mouse_on_container) {
- this.active_field = false;
- return setTimeout(((function(_this) {
- return function() {
- return _this.blur_test();
- };
- })(this)), 100);
- }
- };
-
- AbstractChosen.prototype.label_click_handler = function(evt) {
- if (this.is_multiple) {
- return this.container_mousedown(evt);
- } else {
- return this.activate_field();
- }
- };
-
- AbstractChosen.prototype.results_option_build = function(options) {
- var content, data, data_content, i, len, ref, shown_results;
- content = '';
- shown_results = 0;
- ref = this.results_data;
- for (i = 0, len = ref.length; i < len; i++) {
- data = ref[i];
- data_content = '';
- if (data.group) {
- data_content = this.result_add_group(data);
- } else {
- data_content = this.result_add_option(data);
- }
- if (data_content !== '') {
- shown_results++;
- content += data_content;
- }
- if (options != null ? options.first : void 0) {
- if (data.selected && this.is_multiple) {
- this.choice_build(data);
- } else if (data.selected && !this.is_multiple) {
- this.single_set_selected_text(this.choice_label(data));
- }
- }
- if (shown_results >= this.max_shown_results) {
- break;
- }
- }
- return content;
- };
-
- AbstractChosen.prototype.result_add_option = function(option) {
- var classes, option_el;
- if (!option.search_match) {
- return '';
- }
- if (!this.include_option_in_results(option)) {
- return '';
- }
- classes = [];
- if (!option.disabled && !(option.selected && this.is_multiple)) {
- classes.push("active-result");
- }
- if (option.disabled && !(option.selected && this.is_multiple)) {
- classes.push("disabled-result");
- }
- if (option.selected) {
- classes.push("result-selected");
- }
- if (option.group_array_index != null) {
- classes.push("group-option");
- }
- if (option.classes !== "") {
- classes.push(option.classes);
- }
- option_el = document.createElement("li");
- option_el.className = classes.join(" ");
- if (option.style) {
- option_el.style.cssText = option.style;
- }
- option_el.setAttribute("data-option-array-index", option.array_index);
- option_el.innerHTML = option.highlighted_html || option.html;
- if (option.title) {
- option_el.title = option.title;
- }
- return this.outerHTML(option_el);
- };
-
- AbstractChosen.prototype.result_add_group = function(group) {
- var classes, group_el;
- if (!(group.search_match || group.group_match)) {
- return '';
- }
- if (!(group.active_options > 0)) {
- return '';
- }
- classes = [];
- classes.push("group-result");
- if (group.classes) {
- classes.push(group.classes);
- }
- group_el = document.createElement("li");
- group_el.className = classes.join(" ");
- group_el.innerHTML = group.highlighted_html || this.escape_html(group.label);
- if (group.title) {
- group_el.title = group.title;
- }
- return this.outerHTML(group_el);
- };
-
- AbstractChosen.prototype.results_update_field = function() {
- this.set_default_text();
- if (!this.is_multiple) {
- this.results_reset_cleanup();
- }
- this.result_clear_highlight();
- this.results_build();
- if (this.results_showing) {
- return this.winnow_results();
- }
- };
-
- AbstractChosen.prototype.reset_single_select_options = function() {
- var i, len, ref, result, results1;
- ref = this.results_data;
- results1 = [];
- for (i = 0, len = ref.length; i < len; i++) {
- result = ref[i];
- if (result.selected) {
- results1.push(result.selected = false);
- } else {
- results1.push(void 0);
- }
- }
- return results1;
- };
-
- AbstractChosen.prototype.results_toggle = function() {
- if (this.results_showing) {
- return this.results_hide();
- } else {
- return this.results_show();
- }
- };
-
- AbstractChosen.prototype.results_search = function(evt) {
- if (this.results_showing) {
- return this.winnow_results();
- } else {
- return this.results_show();
- }
- };
-
- AbstractChosen.prototype.winnow_results = function(options) {
- var escapedQuery, fix, i, len, option, prefix, query, ref, regex, results, results_group, search_match, startpos, suffix, text;
- this.no_results_clear();
- results = 0;
- query = this.get_search_text();
- escapedQuery = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
- regex = this.get_search_regex(escapedQuery);
- ref = this.results_data;
- for (i = 0, len = ref.length; i < len; i++) {
- option = ref[i];
- option.search_match = false;
- results_group = null;
- search_match = null;
- option.highlighted_html = '';
- if (this.include_option_in_results(option)) {
- if (option.group) {
- option.group_match = false;
- option.active_options = 0;
- }
- if ((option.group_array_index != null) && this.results_data[option.group_array_index]) {
- results_group = this.results_data[option.group_array_index];
- if (results_group.active_options === 0 && results_group.search_match) {
- results += 1;
- }
- results_group.active_options += 1;
- }
- text = option.group ? option.label : option.text;
- if (!(option.group && !this.group_search)) {
- search_match = this.search_string_match(text, regex);
- option.search_match = search_match != null;
- if (option.search_match && !option.group) {
- results += 1;
- }
- if (option.search_match) {
- if (query.length) {
- startpos = search_match.index;
- prefix = text.slice(0, startpos);
- fix = text.slice(startpos, startpos + query.length);
- suffix = text.slice(startpos + query.length);
- option.highlighted_html = (this.escape_html(prefix)) + "" + (this.escape_html(fix)) + "" + (this.escape_html(suffix));
- }
- if (results_group != null) {
- results_group.group_match = true;
- }
- } else if ((option.group_array_index != null) && this.results_data[option.group_array_index].search_match) {
- option.search_match = true;
- }
- }
- }
- }
- this.result_clear_highlight();
- if (results < 1 && query.length) {
- this.update_results_content("");
- return this.no_results(query);
- } else {
- this.update_results_content(this.results_option_build());
- if (!(options != null ? options.skip_highlight : void 0)) {
- return this.winnow_results_set_highlight();
- }
- }
- };
-
- AbstractChosen.prototype.get_search_regex = function(escaped_search_string) {
- var regex_flag, regex_string;
- regex_string = this.search_contains ? escaped_search_string : "(^|\\s|\\b)" + escaped_search_string + "[^\\s]*";
- if (!(this.enable_split_word_search || this.search_contains)) {
- regex_string = "^" + regex_string;
- }
- regex_flag = this.case_sensitive_search ? "" : "i";
- return new RegExp(regex_string, regex_flag);
- };
-
- AbstractChosen.prototype.search_string_match = function(search_string, regex) {
- var match;
- match = regex.exec(search_string);
- if (!this.search_contains && (match != null ? match[1] : void 0)) {
- match.index += 1;
- }
- return match;
- };
-
- AbstractChosen.prototype.choices_count = function() {
- var i, len, option, ref;
- if (this.selected_option_count != null) {
- return this.selected_option_count;
- }
- this.selected_option_count = 0;
- ref = this.form_field.options;
- for (i = 0, len = ref.length; i < len; i++) {
- option = ref[i];
- if (option.selected) {
- this.selected_option_count += 1;
- }
- }
- return this.selected_option_count;
- };
-
- AbstractChosen.prototype.choices_click = function(evt) {
- evt.preventDefault();
- this.activate_field();
- if (!(this.results_showing || this.is_disabled)) {
- return this.results_show();
- }
- };
-
- AbstractChosen.prototype.keydown_checker = function(evt) {
- var ref, stroke;
- stroke = (ref = evt.which) != null ? ref : evt.keyCode;
- this.search_field_scale();
- if (stroke !== 8 && this.pending_backstroke) {
- this.clear_backstroke();
- }
- switch (stroke) {
- case 8:
- this.backstroke_length = this.get_search_field_value().length;
- break;
- case 9:
- if (this.results_showing && !this.is_multiple) {
- this.result_select(evt);
- }
- this.mouse_on_container = false;
- break;
- case 13:
- if (this.results_showing) {
- evt.preventDefault();
- }
- break;
- case 27:
- if (this.results_showing) {
- evt.preventDefault();
- }
- break;
- case 32:
- if (this.disable_search) {
- evt.preventDefault();
- }
- break;
- case 38:
- evt.preventDefault();
- this.keyup_arrow();
- break;
- case 40:
- evt.preventDefault();
- this.keydown_arrow();
- break;
- }
- };
-
- AbstractChosen.prototype.keyup_checker = function(evt) {
- var ref, stroke;
- stroke = (ref = evt.which) != null ? ref : evt.keyCode;
- this.search_field_scale();
- switch (stroke) {
- case 8:
- if (this.is_multiple && this.backstroke_length < 1 && this.choices_count() > 0) {
- this.keydown_backstroke();
- } else if (!this.pending_backstroke) {
- this.result_clear_highlight();
- this.results_search();
- }
- break;
- case 13:
- evt.preventDefault();
- if (this.results_showing) {
- this.result_select(evt);
- }
- break;
- case 27:
- if (this.results_showing) {
- this.results_hide();
- }
- break;
- case 9:
- case 16:
- case 17:
- case 18:
- case 38:
- case 40:
- case 91:
- break;
- default:
- this.results_search();
- break;
- }
- };
-
- AbstractChosen.prototype.clipboard_event_checker = function(evt) {
- if (this.is_disabled) {
- return;
- }
- return setTimeout(((function(_this) {
- return function() {
- return _this.results_search();
- };
- })(this)), 50);
- };
-
- AbstractChosen.prototype.container_width = function() {
- if (this.options.width != null) {
- return this.options.width;
- } else {
- return this.form_field.offsetWidth + "px";
- }
- };
-
- AbstractChosen.prototype.include_option_in_results = function(option) {
- if (this.is_multiple && (!this.display_selected_options && option.selected)) {
- return false;
- }
- if (!this.display_disabled_options && option.disabled) {
- return false;
- }
- if (option.empty) {
- return false;
- }
- return true;
- };
-
- AbstractChosen.prototype.search_results_touchstart = function(evt) {
- this.touch_started = true;
- return this.search_results_mouseover(evt);
- };
-
- AbstractChosen.prototype.search_results_touchmove = function(evt) {
- this.touch_started = false;
- return this.search_results_mouseout(evt);
- };
-
- AbstractChosen.prototype.search_results_touchend = function(evt) {
- if (this.touch_started) {
- return this.search_results_mouseup(evt);
- }
- };
-
- AbstractChosen.prototype.outerHTML = function(element) {
- var tmp;
- if (element.outerHTML) {
- return element.outerHTML;
- }
- tmp = document.createElement("div");
- tmp.appendChild(element);
- return tmp.innerHTML;
- };
-
- AbstractChosen.prototype.get_single_html = function() {
- return "\n " + this.default_text + "\n
\n\n";
- };
-
- AbstractChosen.prototype.get_multi_html = function() {
- return "\n";
- };
-
- AbstractChosen.prototype.get_no_results_html = function(terms) {
- return "\n " + this.results_none_found + " " + (this.escape_html(terms)) + "\n";
- };
-
- AbstractChosen.browser_is_supported = function() {
- if ("Microsoft Internet Explorer" === window.navigator.appName) {
- return document.documentMode >= 8;
- }
- if (/iP(od|hone)/i.test(window.navigator.userAgent) || /IEMobile/i.test(window.navigator.userAgent) || /Windows Phone/i.test(window.navigator.userAgent) || /BlackBerry/i.test(window.navigator.userAgent) || /BB10/i.test(window.navigator.userAgent) || /Android.*Mobile/i.test(window.navigator.userAgent)) {
- return false;
- }
- return true;
- };
-
- AbstractChosen.default_multiple_text = "Select Some Options";
-
- AbstractChosen.default_single_text = "Select an Option";
-
- AbstractChosen.default_no_result_text = "No results match";
-
- return AbstractChosen;
-
- })();
-
- $ = jQuery;
-
- $.fn.extend({
- chosen: function(options) {
- if (!AbstractChosen.browser_is_supported()) {
- return this;
- }
- return this.each(function(input_field) {
- var $this, chosen;
- $this = $(this);
- chosen = $this.data('chosen');
- if (options === 'destroy') {
- if (chosen instanceof Chosen) {
- chosen.destroy();
- }
- return;
- }
- if (!(chosen instanceof Chosen)) {
- $this.data('chosen', new Chosen(this, options));
- }
- });
- }
- });
-
- Chosen = (function(superClass) {
- extend(Chosen, superClass);
-
- function Chosen() {
- return Chosen.__super__.constructor.apply(this, arguments);
- }
-
- Chosen.prototype.setup = function() {
- this.form_field_jq = $(this.form_field);
- return this.current_selectedIndex = this.form_field.selectedIndex;
- };
-
- Chosen.prototype.set_up_html = function() {
- var container_classes, container_props;
- container_classes = ["chosen-container"];
- container_classes.push("chosen-container-" + (this.is_multiple ? "multi" : "single"));
- if (this.inherit_select_classes && this.form_field.className) {
- container_classes.push(this.form_field.className);
- }
- if (this.is_rtl) {
- container_classes.push("chosen-rtl");
- }
- container_props = {
- 'class': container_classes.join(' '),
- 'title': this.form_field.title
- };
- if (this.form_field.id.length) {
- container_props.id = this.form_field.id.replace(/[^\w]/g, '_') + "_chosen";
- }
- this.container = $("", container_props);
- this.container.width(this.container_width());
- if (this.is_multiple) {
- this.container.html(this.get_multi_html());
- } else {
- this.container.html(this.get_single_html());
- }
- this.form_field_jq.hide().after(this.container);
- this.dropdown = this.container.find('div.chosen-drop').first();
- this.search_field = this.container.find('input').first();
- this.search_results = this.container.find('ul.chosen-results').first();
- this.search_field_scale();
- this.search_no_results = this.container.find('li.no-results').first();
- if (this.is_multiple) {
- this.search_choices = this.container.find('ul.chosen-choices').first();
- this.search_container = this.container.find('li.search-field').first();
- } else {
- this.search_container = this.container.find('div.chosen-search').first();
- this.selected_item = this.container.find('.chosen-single').first();
- }
- this.results_build();
- this.set_tab_index();
- return this.set_label_behavior();
- };
-
- Chosen.prototype.on_ready = function() {
- return this.form_field_jq.trigger("chosen:ready", {
- chosen: this
- });
- };
-
- Chosen.prototype.register_observers = function() {
- this.container.on('touchstart.chosen', (function(_this) {
- return function(evt) {
- _this.container_mousedown(evt);
- };
- })(this));
- this.container.on('touchend.chosen', (function(_this) {
- return function(evt) {
- _this.container_mouseup(evt);
- };
- })(this));
- this.container.on('mousedown.chosen', (function(_this) {
- return function(evt) {
- _this.container_mousedown(evt);
- };
- })(this));
- this.container.on('mouseup.chosen', (function(_this) {
- return function(evt) {
- _this.container_mouseup(evt);
- };
- })(this));
- this.container.on('mouseenter.chosen', (function(_this) {
- return function(evt) {
- _this.mouse_enter(evt);
- };
- })(this));
- this.container.on('mouseleave.chosen', (function(_this) {
- return function(evt) {
- _this.mouse_leave(evt);
- };
- })(this));
- this.search_results.on('mouseup.chosen', (function(_this) {
- return function(evt) {
- _this.search_results_mouseup(evt);
- };
- })(this));
- this.search_results.on('mouseover.chosen', (function(_this) {
- return function(evt) {
- _this.search_results_mouseover(evt);
- };
- })(this));
- this.search_results.on('mouseout.chosen', (function(_this) {
- return function(evt) {
- _this.search_results_mouseout(evt);
- };
- })(this));
- this.search_results.on('mousewheel.chosen DOMMouseScroll.chosen', (function(_this) {
- return function(evt) {
- _this.search_results_mousewheel(evt);
- };
- })(this));
- this.search_results.on('touchstart.chosen', (function(_this) {
- return function(evt) {
- _this.search_results_touchstart(evt);
- };
- })(this));
- this.search_results.on('touchmove.chosen', (function(_this) {
- return function(evt) {
- _this.search_results_touchmove(evt);
- };
- })(this));
- this.search_results.on('touchend.chosen', (function(_this) {
- return function(evt) {
- _this.search_results_touchend(evt);
- };
- })(this));
- this.form_field_jq.on("chosen:updated.chosen", (function(_this) {
- return function(evt) {
- _this.results_update_field(evt);
- };
- })(this));
- this.form_field_jq.on("chosen:activate.chosen", (function(_this) {
- return function(evt) {
- _this.activate_field(evt);
- };
- })(this));
- this.form_field_jq.on("chosen:open.chosen", (function(_this) {
- return function(evt) {
- _this.container_mousedown(evt);
- };
- })(this));
- this.form_field_jq.on("chosen:close.chosen", (function(_this) {
- return function(evt) {
- _this.close_field(evt);
- };
- })(this));
- this.search_field.on('blur.chosen', (function(_this) {
- return function(evt) {
- _this.input_blur(evt);
- };
- })(this));
- this.search_field.on('keyup.chosen', (function(_this) {
- return function(evt) {
- _this.keyup_checker(evt);
- };
- })(this));
- this.search_field.on('keydown.chosen', (function(_this) {
- return function(evt) {
- _this.keydown_checker(evt);
- };
- })(this));
- this.search_field.on('focus.chosen', (function(_this) {
- return function(evt) {
- _this.input_focus(evt);
- };
- })(this));
- this.search_field.on('cut.chosen', (function(_this) {
- return function(evt) {
- _this.clipboard_event_checker(evt);
- };
- })(this));
- this.search_field.on('paste.chosen', (function(_this) {
- return function(evt) {
- _this.clipboard_event_checker(evt);
- };
- })(this));
- if (this.is_multiple) {
- return this.search_choices.on('click.chosen', (function(_this) {
- return function(evt) {
- _this.choices_click(evt);
- };
- })(this));
- } else {
- return this.container.on('click.chosen', function(evt) {
- evt.preventDefault();
- });
- }
- };
-
- Chosen.prototype.destroy = function() {
- $(this.container[0].ownerDocument).off('click.chosen', this.click_test_action);
- if (this.form_field_label.length > 0) {
- this.form_field_label.off('click.chosen');
- }
- if (this.search_field[0].tabIndex) {
- this.form_field_jq[0].tabIndex = this.search_field[0].tabIndex;
- }
- this.container.remove();
- this.form_field_jq.removeData('chosen');
- return this.form_field_jq.show();
- };
-
- Chosen.prototype.search_field_disabled = function() {
- this.is_disabled = this.form_field.disabled || this.form_field_jq.parents('fieldset').is(':disabled');
- this.container.toggleClass('chosen-disabled', this.is_disabled);
- this.search_field[0].disabled = this.is_disabled;
- if (!this.is_multiple) {
- this.selected_item.off('focus.chosen', this.activate_field);
- }
- if (this.is_disabled) {
- return this.close_field();
- } else if (!this.is_multiple) {
- return this.selected_item.on('focus.chosen', this.activate_field);
- }
- };
-
- Chosen.prototype.container_mousedown = function(evt) {
- var ref;
- if (this.is_disabled) {
- return;
- }
- if (evt && ((ref = evt.type) === 'mousedown' || ref === 'touchstart') && !this.results_showing) {
- evt.preventDefault();
- }
- if (!((evt != null) && ($(evt.target)).hasClass("search-choice-close"))) {
- if (!this.active_field) {
- if (this.is_multiple) {
- this.search_field.val("");
- }
- $(this.container[0].ownerDocument).on('click.chosen', this.click_test_action);
- this.results_show();
- } else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chosen-single").length)) {
- evt.preventDefault();
- this.results_toggle();
- }
- return this.activate_field();
- }
- };
-
- Chosen.prototype.container_mouseup = function(evt) {
- if (evt.target.nodeName === "ABBR" && !this.is_disabled) {
- return this.results_reset(evt);
- }
- };
-
- Chosen.prototype.search_results_mousewheel = function(evt) {
- var delta;
- if (evt.originalEvent) {
- delta = evt.originalEvent.deltaY || -evt.originalEvent.wheelDelta || evt.originalEvent.detail;
- }
- if (delta != null) {
- evt.preventDefault();
- if (evt.type === 'DOMMouseScroll') {
- delta = delta * 40;
- }
- return this.search_results.scrollTop(delta + this.search_results.scrollTop());
- }
- };
-
- Chosen.prototype.blur_test = function(evt) {
- if (!this.active_field && this.container.hasClass("chosen-container-active")) {
- return this.close_field();
- }
- };
-
- Chosen.prototype.close_field = function() {
- $(this.container[0].ownerDocument).off("click.chosen", this.click_test_action);
- this.active_field = false;
- this.results_hide();
- this.container.removeClass("chosen-container-active");
- this.clear_backstroke();
- this.show_search_field_default();
- this.search_field_scale();
- return this.search_field.blur();
- };
-
- Chosen.prototype.activate_field = function() {
- if (this.is_disabled) {
- return;
- }
- this.container.addClass("chosen-container-active");
- this.active_field = true;
- this.search_field.val(this.search_field.val());
- return this.search_field.focus();
- };
-
- Chosen.prototype.test_active_click = function(evt) {
- var active_container;
- active_container = $(evt.target).closest('.chosen-container');
- if (active_container.length && this.container[0] === active_container[0]) {
- return this.active_field = true;
- } else {
- return this.close_field();
- }
- };
-
- Chosen.prototype.results_build = function() {
- this.parsing = true;
- this.selected_option_count = null;
- this.results_data = SelectParser.select_to_array(this.form_field);
- if (this.is_multiple) {
- this.search_choices.find("li.search-choice").remove();
- } else {
- this.single_set_selected_text();
- if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) {
- this.search_field[0].readOnly = true;
- this.container.addClass("chosen-container-single-nosearch");
- } else {
- this.search_field[0].readOnly = false;
- this.container.removeClass("chosen-container-single-nosearch");
- }
- }
- this.update_results_content(this.results_option_build({
- first: true
- }));
- this.search_field_disabled();
- this.show_search_field_default();
- this.search_field_scale();
- return this.parsing = false;
- };
-
- Chosen.prototype.result_do_highlight = function(el) {
- var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
- if (el.length) {
- this.result_clear_highlight();
- this.result_highlight = el;
- this.result_highlight.addClass("highlighted");
- maxHeight = parseInt(this.search_results.css("maxHeight"), 10);
- visible_top = this.search_results.scrollTop();
- visible_bottom = maxHeight + visible_top;
- high_top = this.result_highlight.position().top + this.search_results.scrollTop();
- high_bottom = high_top + this.result_highlight.outerHeight();
- if (high_bottom >= visible_bottom) {
- return this.search_results.scrollTop((high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0);
- } else if (high_top < visible_top) {
- return this.search_results.scrollTop(high_top);
- }
- }
- };
-
- Chosen.prototype.result_clear_highlight = function() {
- if (this.result_highlight) {
- this.result_highlight.removeClass("highlighted");
- }
- return this.result_highlight = null;
- };
-
- Chosen.prototype.results_show = function() {
- if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
- this.form_field_jq.trigger("chosen:maxselected", {
- chosen: this
- });
- return false;
- }
- this.container.addClass("chosen-with-drop");
- this.results_showing = true;
- this.search_field.focus();
- this.search_field.val(this.get_search_field_value());
- this.winnow_results();
- return this.form_field_jq.trigger("chosen:showing_dropdown", {
- chosen: this
- });
- };
-
- Chosen.prototype.update_results_content = function(content) {
- return this.search_results.html(content);
- };
-
- Chosen.prototype.results_hide = function() {
- if (this.results_showing) {
- this.result_clear_highlight();
- this.container.removeClass("chosen-with-drop");
- this.form_field_jq.trigger("chosen:hiding_dropdown", {
- chosen: this
- });
- }
- return this.results_showing = false;
- };
-
- Chosen.prototype.set_tab_index = function(el) {
- var ti;
- if (this.form_field.tabIndex) {
- ti = this.form_field.tabIndex;
- this.form_field.tabIndex = -1;
- return this.search_field[0].tabIndex = ti;
- }
- };
-
- Chosen.prototype.set_label_behavior = function() {
- this.form_field_label = this.form_field_jq.parents("label");
- if (!this.form_field_label.length && this.form_field.id.length) {
- this.form_field_label = $("label[for='" + this.form_field.id + "']");
- }
- if (this.form_field_label.length > 0) {
- return this.form_field_label.on('click.chosen', this.label_click_handler);
- }
- };
-
- Chosen.prototype.show_search_field_default = function() {
- if (this.is_multiple && this.choices_count() < 1 && !this.active_field) {
- this.search_field.val(this.default_text);
- return this.search_field.addClass("default");
- } else {
- this.search_field.val("");
- return this.search_field.removeClass("default");
- }
- };
-
- Chosen.prototype.search_results_mouseup = function(evt) {
- var target;
- target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
- if (target.length) {
- this.result_highlight = target;
- this.result_select(evt);
- return this.search_field.focus();
- }
- };
-
- Chosen.prototype.search_results_mouseover = function(evt) {
- var target;
- target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
- if (target) {
- return this.result_do_highlight(target);
- }
- };
-
- Chosen.prototype.search_results_mouseout = function(evt) {
- if ($(evt.target).hasClass("active-result") || $(evt.target).parents('.active-result').first()) {
- return this.result_clear_highlight();
- }
- };
-
- Chosen.prototype.choice_build = function(item) {
- var choice, close_link;
- choice = $('', {
- "class": "search-choice"
- }).html("" + (this.choice_label(item)) + "");
- if (item.disabled) {
- choice.addClass('search-choice-disabled');
- } else {
- close_link = $('', {
- "class": 'search-choice-close',
- 'data-option-array-index': item.array_index
- });
- close_link.on('click.chosen', (function(_this) {
- return function(evt) {
- return _this.choice_destroy_link_click(evt);
- };
- })(this));
- choice.append(close_link);
- }
- return this.search_container.before(choice);
- };
-
- Chosen.prototype.choice_destroy_link_click = function(evt) {
- evt.preventDefault();
- evt.stopPropagation();
- if (!this.is_disabled) {
- return this.choice_destroy($(evt.target));
- }
- };
-
- Chosen.prototype.choice_destroy = function(link) {
- if (this.result_deselect(link[0].getAttribute("data-option-array-index"))) {
- if (this.active_field) {
- this.search_field.focus();
- } else {
- this.show_search_field_default();
- }
- if (this.is_multiple && this.choices_count() > 0 && this.get_search_field_value().length < 1) {
- this.results_hide();
- }
- link.parents('li').first().remove();
- return this.search_field_scale();
- }
- };
-
- Chosen.prototype.results_reset = function() {
- this.reset_single_select_options();
- this.form_field.options[0].selected = true;
- this.single_set_selected_text();
- this.show_search_field_default();
- this.results_reset_cleanup();
- this.trigger_form_field_change();
- if (this.active_field) {
- return this.results_hide();
- }
- };
-
- Chosen.prototype.results_reset_cleanup = function() {
- this.current_selectedIndex = this.form_field.selectedIndex;
- return this.selected_item.find("abbr").remove();
- };
-
- Chosen.prototype.result_select = function(evt) {
- var high, item;
- if (this.result_highlight) {
- high = this.result_highlight;
- this.result_clear_highlight();
- if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
- this.form_field_jq.trigger("chosen:maxselected", {
- chosen: this
- });
- return false;
- }
- if (this.is_multiple) {
- high.removeClass("active-result");
- } else {
- this.reset_single_select_options();
- }
- high.addClass("result-selected");
- item = this.results_data[high[0].getAttribute("data-option-array-index")];
- item.selected = true;
- this.form_field.options[item.options_index].selected = true;
- this.selected_option_count = null;
- if (this.is_multiple) {
- this.choice_build(item);
- } else {
- this.single_set_selected_text(this.choice_label(item));
- }
- if (this.is_multiple && (!this.hide_results_on_select || (evt.metaKey || evt.ctrlKey))) {
- if (evt.metaKey || evt.ctrlKey) {
- this.winnow_results({
- skip_highlight: true
- });
- } else {
- this.search_field.val("");
- this.winnow_results();
- }
- } else {
- this.results_hide();
- this.show_search_field_default();
- }
- if (this.is_multiple || this.form_field.selectedIndex !== this.current_selectedIndex) {
- this.trigger_form_field_change({
- selected: this.form_field.options[item.options_index].value
- });
- }
- this.current_selectedIndex = this.form_field.selectedIndex;
- evt.preventDefault();
- return this.search_field_scale();
- }
- };
-
- Chosen.prototype.single_set_selected_text = function(text) {
- if (text == null) {
- text = this.default_text;
- }
- if (text === this.default_text) {
- this.selected_item.addClass("chosen-default");
- } else {
- this.single_deselect_control_build();
- this.selected_item.removeClass("chosen-default");
- }
- return this.selected_item.find("span").html(text);
- };
-
- Chosen.prototype.result_deselect = function(pos) {
- var result_data;
- result_data = this.results_data[pos];
- if (!this.form_field.options[result_data.options_index].disabled) {
- result_data.selected = false;
- this.form_field.options[result_data.options_index].selected = false;
- this.selected_option_count = null;
- this.result_clear_highlight();
- if (this.results_showing) {
- this.winnow_results();
- }
- this.trigger_form_field_change({
- deselected: this.form_field.options[result_data.options_index].value
- });
- this.search_field_scale();
- return true;
- } else {
- return false;
- }
- };
-
- Chosen.prototype.single_deselect_control_build = function() {
- if (!this.allow_single_deselect) {
- return;
- }
- if (!this.selected_item.find("abbr").length) {
- this.selected_item.find("span").first().after("");
- }
- return this.selected_item.addClass("chosen-single-with-deselect");
- };
-
- Chosen.prototype.get_search_field_value = function() {
- return this.search_field.val();
- };
-
- Chosen.prototype.get_search_text = function() {
- return $.trim(this.get_search_field_value());
- };
-
- Chosen.prototype.escape_html = function(text) {
- return $('').text(text).html();
- };
-
- Chosen.prototype.winnow_results_set_highlight = function() {
- var do_high, selected_results;
- selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : [];
- do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first();
- if (do_high != null) {
- return this.result_do_highlight(do_high);
- }
- };
-
- Chosen.prototype.no_results = function(terms) {
- var no_results_html;
- no_results_html = this.get_no_results_html(terms);
- this.search_results.append(no_results_html);
- return this.form_field_jq.trigger("chosen:no_results", {
- chosen: this
- });
- };
-
- Chosen.prototype.no_results_clear = function() {
- return this.search_results.find(".no-results").remove();
- };
-
- Chosen.prototype.keydown_arrow = function() {
- var next_sib;
- if (this.results_showing && this.result_highlight) {
- next_sib = this.result_highlight.nextAll("li.active-result").first();
- if (next_sib) {
- return this.result_do_highlight(next_sib);
- }
- } else {
- return this.results_show();
- }
- };
-
- Chosen.prototype.keyup_arrow = function() {
- var prev_sibs;
- if (!this.results_showing && !this.is_multiple) {
- return this.results_show();
- } else if (this.result_highlight) {
- prev_sibs = this.result_highlight.prevAll("li.active-result");
- if (prev_sibs.length) {
- return this.result_do_highlight(prev_sibs.first());
- } else {
- if (this.choices_count() > 0) {
- this.results_hide();
- }
- return this.result_clear_highlight();
- }
- }
- };
-
- Chosen.prototype.keydown_backstroke = function() {
- var next_available_destroy;
- if (this.pending_backstroke) {
- this.choice_destroy(this.pending_backstroke.find("a").first());
- return this.clear_backstroke();
- } else {
- next_available_destroy = this.search_container.siblings("li.search-choice").last();
- if (next_available_destroy.length && !next_available_destroy.hasClass("search-choice-disabled")) {
- this.pending_backstroke = next_available_destroy;
- if (this.single_backstroke_delete) {
- return this.keydown_backstroke();
- } else {
- return this.pending_backstroke.addClass("search-choice-focus");
- }
- }
- }
- };
-
- Chosen.prototype.clear_backstroke = function() {
- if (this.pending_backstroke) {
- this.pending_backstroke.removeClass("search-choice-focus");
- }
- return this.pending_backstroke = null;
- };
-
- Chosen.prototype.search_field_scale = function() {
- var div, i, len, style, style_block, styles, width;
- if (!this.is_multiple) {
- return;
- }
- style_block = {
- position: 'absolute',
- left: '-1000px',
- top: '-1000px',
- display: 'none',
- whiteSpace: 'pre'
- };
- styles = ['fontSize', 'fontStyle', 'fontWeight', 'fontFamily', 'lineHeight', 'textTransform', 'letterSpacing'];
- for (i = 0, len = styles.length; i < len; i++) {
- style = styles[i];
- style_block[style] = this.search_field.css(style);
- }
- div = $('').css(style_block);
- div.text(this.get_search_field_value());
- $('body').append(div);
- width = div.width() + 25;
- div.remove();
- if (this.container.is(':visible')) {
- width = Math.min(this.container.outerWidth() - 10, width);
- }
- return this.search_field.width(width);
- };
-
- Chosen.prototype.trigger_form_field_change = function(extra) {
- this.form_field_jq.trigger("input", extra);
- return this.form_field_jq.trigger("change", extra);
- };
-
- return Chosen;
-
- })(AbstractChosen);
-
-}).call(this);
diff --git a/vendor/assets/javascripts/stimulus/stimulus.umd.js b/vendor/assets/javascripts/stimulus/stimulus.umd.js
new file mode 100644
index 00000000..b066e587
--- /dev/null
+++ b/vendor/assets/javascripts/stimulus/stimulus.umd.js
@@ -0,0 +1,2588 @@
+/*
+Stimulus 3.2.1
+Copyright © 2023 Basecamp, LLC
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Stimulus = {}));
+}(this, (function (exports) { 'use strict';
+
+ class EventListener {
+ constructor(eventTarget, eventName, eventOptions) {
+ this.eventTarget = eventTarget;
+ this.eventName = eventName;
+ this.eventOptions = eventOptions;
+ this.unorderedBindings = new Set();
+ }
+ connect() {
+ this.eventTarget.addEventListener(this.eventName, this, this.eventOptions);
+ }
+ disconnect() {
+ this.eventTarget.removeEventListener(this.eventName, this, this.eventOptions);
+ }
+ bindingConnected(binding) {
+ this.unorderedBindings.add(binding);
+ }
+ bindingDisconnected(binding) {
+ this.unorderedBindings.delete(binding);
+ }
+ handleEvent(event) {
+ const extendedEvent = extendEvent(event);
+ for (const binding of this.bindings) {
+ if (extendedEvent.immediatePropagationStopped) {
+ break;
+ }
+ else {
+ binding.handleEvent(extendedEvent);
+ }
+ }
+ }
+ hasBindings() {
+ return this.unorderedBindings.size > 0;
+ }
+ get bindings() {
+ return Array.from(this.unorderedBindings).sort((left, right) => {
+ const leftIndex = left.index, rightIndex = right.index;
+ return leftIndex < rightIndex ? -1 : leftIndex > rightIndex ? 1 : 0;
+ });
+ }
+ }
+ function extendEvent(event) {
+ if ("immediatePropagationStopped" in event) {
+ return event;
+ }
+ else {
+ const { stopImmediatePropagation } = event;
+ return Object.assign(event, {
+ immediatePropagationStopped: false,
+ stopImmediatePropagation() {
+ this.immediatePropagationStopped = true;
+ stopImmediatePropagation.call(this);
+ },
+ });
+ }
+ }
+
+ class Dispatcher {
+ constructor(application) {
+ this.application = application;
+ this.eventListenerMaps = new Map();
+ this.started = false;
+ }
+ start() {
+ if (!this.started) {
+ this.started = true;
+ this.eventListeners.forEach((eventListener) => eventListener.connect());
+ }
+ }
+ stop() {
+ if (this.started) {
+ this.started = false;
+ this.eventListeners.forEach((eventListener) => eventListener.disconnect());
+ }
+ }
+ get eventListeners() {
+ return Array.from(this.eventListenerMaps.values()).reduce((listeners, map) => listeners.concat(Array.from(map.values())), []);
+ }
+ bindingConnected(binding) {
+ this.fetchEventListenerForBinding(binding).bindingConnected(binding);
+ }
+ bindingDisconnected(binding, clearEventListeners = false) {
+ this.fetchEventListenerForBinding(binding).bindingDisconnected(binding);
+ if (clearEventListeners)
+ this.clearEventListenersForBinding(binding);
+ }
+ handleError(error, message, detail = {}) {
+ this.application.handleError(error, `Error ${message}`, detail);
+ }
+ clearEventListenersForBinding(binding) {
+ const eventListener = this.fetchEventListenerForBinding(binding);
+ if (!eventListener.hasBindings()) {
+ eventListener.disconnect();
+ this.removeMappedEventListenerFor(binding);
+ }
+ }
+ removeMappedEventListenerFor(binding) {
+ const { eventTarget, eventName, eventOptions } = binding;
+ const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget);
+ const cacheKey = this.cacheKey(eventName, eventOptions);
+ eventListenerMap.delete(cacheKey);
+ if (eventListenerMap.size == 0)
+ this.eventListenerMaps.delete(eventTarget);
+ }
+ fetchEventListenerForBinding(binding) {
+ const { eventTarget, eventName, eventOptions } = binding;
+ return this.fetchEventListener(eventTarget, eventName, eventOptions);
+ }
+ fetchEventListener(eventTarget, eventName, eventOptions) {
+ const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget);
+ const cacheKey = this.cacheKey(eventName, eventOptions);
+ let eventListener = eventListenerMap.get(cacheKey);
+ if (!eventListener) {
+ eventListener = this.createEventListener(eventTarget, eventName, eventOptions);
+ eventListenerMap.set(cacheKey, eventListener);
+ }
+ return eventListener;
+ }
+ createEventListener(eventTarget, eventName, eventOptions) {
+ const eventListener = new EventListener(eventTarget, eventName, eventOptions);
+ if (this.started) {
+ eventListener.connect();
+ }
+ return eventListener;
+ }
+ fetchEventListenerMapForEventTarget(eventTarget) {
+ let eventListenerMap = this.eventListenerMaps.get(eventTarget);
+ if (!eventListenerMap) {
+ eventListenerMap = new Map();
+ this.eventListenerMaps.set(eventTarget, eventListenerMap);
+ }
+ return eventListenerMap;
+ }
+ cacheKey(eventName, eventOptions) {
+ const parts = [eventName];
+ Object.keys(eventOptions)
+ .sort()
+ .forEach((key) => {
+ parts.push(`${eventOptions[key] ? "" : "!"}${key}`);
+ });
+ return parts.join(":");
+ }
+ }
+
+ const defaultActionDescriptorFilters = {
+ stop({ event, value }) {
+ if (value)
+ event.stopPropagation();
+ return true;
+ },
+ prevent({ event, value }) {
+ if (value)
+ event.preventDefault();
+ return true;
+ },
+ self({ event, value, element }) {
+ if (value) {
+ return element === event.target;
+ }
+ else {
+ return true;
+ }
+ },
+ };
+ const descriptorPattern = /^(?:(?:([^.]+?)\+)?(.+?)(?:\.(.+?))?(?:@(window|document))?->)?(.+?)(?:#([^:]+?))(?::(.+))?$/;
+ function parseActionDescriptorString(descriptorString) {
+ const source = descriptorString.trim();
+ const matches = source.match(descriptorPattern) || [];
+ let eventName = matches[2];
+ let keyFilter = matches[3];
+ if (keyFilter && !["keydown", "keyup", "keypress"].includes(eventName)) {
+ eventName += `.${keyFilter}`;
+ keyFilter = "";
+ }
+ return {
+ eventTarget: parseEventTarget(matches[4]),
+ eventName,
+ eventOptions: matches[7] ? parseEventOptions(matches[7]) : {},
+ identifier: matches[5],
+ methodName: matches[6],
+ keyFilter: matches[1] || keyFilter,
+ };
+ }
+ function parseEventTarget(eventTargetName) {
+ if (eventTargetName == "window") {
+ return window;
+ }
+ else if (eventTargetName == "document") {
+ return document;
+ }
+ }
+ function parseEventOptions(eventOptions) {
+ return eventOptions
+ .split(":")
+ .reduce((options, token) => Object.assign(options, { [token.replace(/^!/, "")]: !/^!/.test(token) }), {});
+ }
+ function stringifyEventTarget(eventTarget) {
+ if (eventTarget == window) {
+ return "window";
+ }
+ else if (eventTarget == document) {
+ return "document";
+ }
+ }
+
+ function camelize(value) {
+ return value.replace(/(?:[_-])([a-z0-9])/g, (_, char) => char.toUpperCase());
+ }
+ function namespaceCamelize(value) {
+ return camelize(value.replace(/--/g, "-").replace(/__/g, "_"));
+ }
+ function capitalize(value) {
+ return value.charAt(0).toUpperCase() + value.slice(1);
+ }
+ function dasherize(value) {
+ return value.replace(/([A-Z])/g, (_, char) => `-${char.toLowerCase()}`);
+ }
+ function tokenize(value) {
+ return value.match(/[^\s]+/g) || [];
+ }
+
+ function isSomething(object) {
+ return object !== null && object !== undefined;
+ }
+ function hasProperty(object, property) {
+ return Object.prototype.hasOwnProperty.call(object, property);
+ }
+
+ const allModifiers = ["meta", "ctrl", "alt", "shift"];
+ class Action {
+ constructor(element, index, descriptor, schema) {
+ this.element = element;
+ this.index = index;
+ this.eventTarget = descriptor.eventTarget || element;
+ this.eventName = descriptor.eventName || getDefaultEventNameForElement(element) || error("missing event name");
+ this.eventOptions = descriptor.eventOptions || {};
+ this.identifier = descriptor.identifier || error("missing identifier");
+ this.methodName = descriptor.methodName || error("missing method name");
+ this.keyFilter = descriptor.keyFilter || "";
+ this.schema = schema;
+ }
+ static forToken(token, schema) {
+ return new this(token.element, token.index, parseActionDescriptorString(token.content), schema);
+ }
+ toString() {
+ const eventFilter = this.keyFilter ? `.${this.keyFilter}` : "";
+ const eventTarget = this.eventTargetName ? `@${this.eventTargetName}` : "";
+ return `${this.eventName}${eventFilter}${eventTarget}->${this.identifier}#${this.methodName}`;
+ }
+ shouldIgnoreKeyboardEvent(event) {
+ if (!this.keyFilter) {
+ return false;
+ }
+ const filters = this.keyFilter.split("+");
+ if (this.keyFilterDissatisfied(event, filters)) {
+ return true;
+ }
+ const standardFilter = filters.filter((key) => !allModifiers.includes(key))[0];
+ if (!standardFilter) {
+ return false;
+ }
+ if (!hasProperty(this.keyMappings, standardFilter)) {
+ error(`contains unknown key filter: ${this.keyFilter}`);
+ }
+ return this.keyMappings[standardFilter].toLowerCase() !== event.key.toLowerCase();
+ }
+ shouldIgnoreMouseEvent(event) {
+ if (!this.keyFilter) {
+ return false;
+ }
+ const filters = [this.keyFilter];
+ if (this.keyFilterDissatisfied(event, filters)) {
+ return true;
+ }
+ return false;
+ }
+ get params() {
+ const params = {};
+ const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`, "i");
+ for (const { name, value } of Array.from(this.element.attributes)) {
+ const match = name.match(pattern);
+ const key = match && match[1];
+ if (key) {
+ params[camelize(key)] = typecast(value);
+ }
+ }
+ return params;
+ }
+ get eventTargetName() {
+ return stringifyEventTarget(this.eventTarget);
+ }
+ get keyMappings() {
+ return this.schema.keyMappings;
+ }
+ keyFilterDissatisfied(event, filters) {
+ const [meta, ctrl, alt, shift] = allModifiers.map((modifier) => filters.includes(modifier));
+ return event.metaKey !== meta || event.ctrlKey !== ctrl || event.altKey !== alt || event.shiftKey !== shift;
+ }
+ }
+ const defaultEventNames = {
+ a: () => "click",
+ button: () => "click",
+ form: () => "submit",
+ details: () => "toggle",
+ input: (e) => (e.getAttribute("type") == "submit" ? "click" : "input"),
+ select: () => "change",
+ textarea: () => "input",
+ };
+ function getDefaultEventNameForElement(element) {
+ const tagName = element.tagName.toLowerCase();
+ if (tagName in defaultEventNames) {
+ return defaultEventNames[tagName](element);
+ }
+ }
+ function error(message) {
+ throw new Error(message);
+ }
+ function typecast(value) {
+ try {
+ return JSON.parse(value);
+ }
+ catch (o_O) {
+ return value;
+ }
+ }
+
+ class Binding {
+ constructor(context, action) {
+ this.context = context;
+ this.action = action;
+ }
+ get index() {
+ return this.action.index;
+ }
+ get eventTarget() {
+ return this.action.eventTarget;
+ }
+ get eventOptions() {
+ return this.action.eventOptions;
+ }
+ get identifier() {
+ return this.context.identifier;
+ }
+ handleEvent(event) {
+ const actionEvent = this.prepareActionEvent(event);
+ if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(actionEvent)) {
+ this.invokeWithEvent(actionEvent);
+ }
+ }
+ get eventName() {
+ return this.action.eventName;
+ }
+ get method() {
+ const method = this.controller[this.methodName];
+ if (typeof method == "function") {
+ return method;
+ }
+ throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`);
+ }
+ applyEventModifiers(event) {
+ const { element } = this.action;
+ const { actionDescriptorFilters } = this.context.application;
+ const { controller } = this.context;
+ let passes = true;
+ for (const [name, value] of Object.entries(this.eventOptions)) {
+ if (name in actionDescriptorFilters) {
+ const filter = actionDescriptorFilters[name];
+ passes = passes && filter({ name, value, event, element, controller });
+ }
+ else {
+ continue;
+ }
+ }
+ return passes;
+ }
+ prepareActionEvent(event) {
+ return Object.assign(event, { params: this.action.params });
+ }
+ invokeWithEvent(event) {
+ const { target, currentTarget } = event;
+ try {
+ this.method.call(this.controller, event);
+ this.context.logDebugActivity(this.methodName, { event, target, currentTarget, action: this.methodName });
+ }
+ catch (error) {
+ const { identifier, controller, element, index } = this;
+ const detail = { identifier, controller, element, index, event };
+ this.context.handleError(error, `invoking action "${this.action}"`, detail);
+ }
+ }
+ willBeInvokedByEvent(event) {
+ const eventTarget = event.target;
+ if (event instanceof KeyboardEvent && this.action.shouldIgnoreKeyboardEvent(event)) {
+ return false;
+ }
+ if (event instanceof MouseEvent && this.action.shouldIgnoreMouseEvent(event)) {
+ return false;
+ }
+ if (this.element === eventTarget) {
+ return true;
+ }
+ else if (eventTarget instanceof Element && this.element.contains(eventTarget)) {
+ return this.scope.containsElement(eventTarget);
+ }
+ else {
+ return this.scope.containsElement(this.action.element);
+ }
+ }
+ get controller() {
+ return this.context.controller;
+ }
+ get methodName() {
+ return this.action.methodName;
+ }
+ get element() {
+ return this.scope.element;
+ }
+ get scope() {
+ return this.context.scope;
+ }
+ }
+
+ class ElementObserver {
+ constructor(element, delegate) {
+ this.mutationObserverInit = { attributes: true, childList: true, subtree: true };
+ this.element = element;
+ this.started = false;
+ this.delegate = delegate;
+ this.elements = new Set();
+ this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
+ }
+ start() {
+ if (!this.started) {
+ this.started = true;
+ this.mutationObserver.observe(this.element, this.mutationObserverInit);
+ this.refresh();
+ }
+ }
+ pause(callback) {
+ if (this.started) {
+ this.mutationObserver.disconnect();
+ this.started = false;
+ }
+ callback();
+ if (!this.started) {
+ this.mutationObserver.observe(this.element, this.mutationObserverInit);
+ this.started = true;
+ }
+ }
+ stop() {
+ if (this.started) {
+ this.mutationObserver.takeRecords();
+ this.mutationObserver.disconnect();
+ this.started = false;
+ }
+ }
+ refresh() {
+ if (this.started) {
+ const matches = new Set(this.matchElementsInTree());
+ for (const element of Array.from(this.elements)) {
+ if (!matches.has(element)) {
+ this.removeElement(element);
+ }
+ }
+ for (const element of Array.from(matches)) {
+ this.addElement(element);
+ }
+ }
+ }
+ processMutations(mutations) {
+ if (this.started) {
+ for (const mutation of mutations) {
+ this.processMutation(mutation);
+ }
+ }
+ }
+ processMutation(mutation) {
+ if (mutation.type == "attributes") {
+ this.processAttributeChange(mutation.target, mutation.attributeName);
+ }
+ else if (mutation.type == "childList") {
+ this.processRemovedNodes(mutation.removedNodes);
+ this.processAddedNodes(mutation.addedNodes);
+ }
+ }
+ processAttributeChange(element, attributeName) {
+ if (this.elements.has(element)) {
+ if (this.delegate.elementAttributeChanged && this.matchElement(element)) {
+ this.delegate.elementAttributeChanged(element, attributeName);
+ }
+ else {
+ this.removeElement(element);
+ }
+ }
+ else if (this.matchElement(element)) {
+ this.addElement(element);
+ }
+ }
+ processRemovedNodes(nodes) {
+ for (const node of Array.from(nodes)) {
+ const element = this.elementFromNode(node);
+ if (element) {
+ this.processTree(element, this.removeElement);
+ }
+ }
+ }
+ processAddedNodes(nodes) {
+ for (const node of Array.from(nodes)) {
+ const element = this.elementFromNode(node);
+ if (element && this.elementIsActive(element)) {
+ this.processTree(element, this.addElement);
+ }
+ }
+ }
+ matchElement(element) {
+ return this.delegate.matchElement(element);
+ }
+ matchElementsInTree(tree = this.element) {
+ return this.delegate.matchElementsInTree(tree);
+ }
+ processTree(tree, processor) {
+ for (const element of this.matchElementsInTree(tree)) {
+ processor.call(this, element);
+ }
+ }
+ elementFromNode(node) {
+ if (node.nodeType == Node.ELEMENT_NODE) {
+ return node;
+ }
+ }
+ elementIsActive(element) {
+ if (element.isConnected != this.element.isConnected) {
+ return false;
+ }
+ else {
+ return this.element.contains(element);
+ }
+ }
+ addElement(element) {
+ if (!this.elements.has(element)) {
+ if (this.elementIsActive(element)) {
+ this.elements.add(element);
+ if (this.delegate.elementMatched) {
+ this.delegate.elementMatched(element);
+ }
+ }
+ }
+ }
+ removeElement(element) {
+ if (this.elements.has(element)) {
+ this.elements.delete(element);
+ if (this.delegate.elementUnmatched) {
+ this.delegate.elementUnmatched(element);
+ }
+ }
+ }
+ }
+
+ class AttributeObserver {
+ constructor(element, attributeName, delegate) {
+ this.attributeName = attributeName;
+ this.delegate = delegate;
+ this.elementObserver = new ElementObserver(element, this);
+ }
+ get element() {
+ return this.elementObserver.element;
+ }
+ get selector() {
+ return `[${this.attributeName}]`;
+ }
+ start() {
+ this.elementObserver.start();
+ }
+ pause(callback) {
+ this.elementObserver.pause(callback);
+ }
+ stop() {
+ this.elementObserver.stop();
+ }
+ refresh() {
+ this.elementObserver.refresh();
+ }
+ get started() {
+ return this.elementObserver.started;
+ }
+ matchElement(element) {
+ return element.hasAttribute(this.attributeName);
+ }
+ matchElementsInTree(tree) {
+ const match = this.matchElement(tree) ? [tree] : [];
+ const matches = Array.from(tree.querySelectorAll(this.selector));
+ return match.concat(matches);
+ }
+ elementMatched(element) {
+ if (this.delegate.elementMatchedAttribute) {
+ this.delegate.elementMatchedAttribute(element, this.attributeName);
+ }
+ }
+ elementUnmatched(element) {
+ if (this.delegate.elementUnmatchedAttribute) {
+ this.delegate.elementUnmatchedAttribute(element, this.attributeName);
+ }
+ }
+ elementAttributeChanged(element, attributeName) {
+ if (this.delegate.elementAttributeValueChanged && this.attributeName == attributeName) {
+ this.delegate.elementAttributeValueChanged(element, attributeName);
+ }
+ }
+ }
+
+ function add(map, key, value) {
+ fetch(map, key).add(value);
+ }
+ function del(map, key, value) {
+ fetch(map, key).delete(value);
+ prune(map, key);
+ }
+ function fetch(map, key) {
+ let values = map.get(key);
+ if (!values) {
+ values = new Set();
+ map.set(key, values);
+ }
+ return values;
+ }
+ function prune(map, key) {
+ const values = map.get(key);
+ if (values != null && values.size == 0) {
+ map.delete(key);
+ }
+ }
+
+ class Multimap {
+ constructor() {
+ this.valuesByKey = new Map();
+ }
+ get keys() {
+ return Array.from(this.valuesByKey.keys());
+ }
+ get values() {
+ const sets = Array.from(this.valuesByKey.values());
+ return sets.reduce((values, set) => values.concat(Array.from(set)), []);
+ }
+ get size() {
+ const sets = Array.from(this.valuesByKey.values());
+ return sets.reduce((size, set) => size + set.size, 0);
+ }
+ add(key, value) {
+ add(this.valuesByKey, key, value);
+ }
+ delete(key, value) {
+ del(this.valuesByKey, key, value);
+ }
+ has(key, value) {
+ const values = this.valuesByKey.get(key);
+ return values != null && values.has(value);
+ }
+ hasKey(key) {
+ return this.valuesByKey.has(key);
+ }
+ hasValue(value) {
+ const sets = Array.from(this.valuesByKey.values());
+ return sets.some((set) => set.has(value));
+ }
+ getValuesForKey(key) {
+ const values = this.valuesByKey.get(key);
+ return values ? Array.from(values) : [];
+ }
+ getKeysForValue(value) {
+ return Array.from(this.valuesByKey)
+ .filter(([_key, values]) => values.has(value))
+ .map(([key, _values]) => key);
+ }
+ }
+
+ class IndexedMultimap extends Multimap {
+ constructor() {
+ super();
+ this.keysByValue = new Map();
+ }
+ get values() {
+ return Array.from(this.keysByValue.keys());
+ }
+ add(key, value) {
+ super.add(key, value);
+ add(this.keysByValue, value, key);
+ }
+ delete(key, value) {
+ super.delete(key, value);
+ del(this.keysByValue, value, key);
+ }
+ hasValue(value) {
+ return this.keysByValue.has(value);
+ }
+ getKeysForValue(value) {
+ const set = this.keysByValue.get(value);
+ return set ? Array.from(set) : [];
+ }
+ }
+
+ class SelectorObserver {
+ constructor(element, selector, delegate, details) {
+ this._selector = selector;
+ this.details = details;
+ this.elementObserver = new ElementObserver(element, this);
+ this.delegate = delegate;
+ this.matchesByElement = new Multimap();
+ }
+ get started() {
+ return this.elementObserver.started;
+ }
+ get selector() {
+ return this._selector;
+ }
+ set selector(selector) {
+ this._selector = selector;
+ this.refresh();
+ }
+ start() {
+ this.elementObserver.start();
+ }
+ pause(callback) {
+ this.elementObserver.pause(callback);
+ }
+ stop() {
+ this.elementObserver.stop();
+ }
+ refresh() {
+ this.elementObserver.refresh();
+ }
+ get element() {
+ return this.elementObserver.element;
+ }
+ matchElement(element) {
+ const { selector } = this;
+ if (selector) {
+ const matches = element.matches(selector);
+ if (this.delegate.selectorMatchElement) {
+ return matches && this.delegate.selectorMatchElement(element, this.details);
+ }
+ return matches;
+ }
+ else {
+ return false;
+ }
+ }
+ matchElementsInTree(tree) {
+ const { selector } = this;
+ if (selector) {
+ const match = this.matchElement(tree) ? [tree] : [];
+ const matches = Array.from(tree.querySelectorAll(selector)).filter((match) => this.matchElement(match));
+ return match.concat(matches);
+ }
+ else {
+ return [];
+ }
+ }
+ elementMatched(element) {
+ const { selector } = this;
+ if (selector) {
+ this.selectorMatched(element, selector);
+ }
+ }
+ elementUnmatched(element) {
+ const selectors = this.matchesByElement.getKeysForValue(element);
+ for (const selector of selectors) {
+ this.selectorUnmatched(element, selector);
+ }
+ }
+ elementAttributeChanged(element, _attributeName) {
+ const { selector } = this;
+ if (selector) {
+ const matches = this.matchElement(element);
+ const matchedBefore = this.matchesByElement.has(selector, element);
+ if (matches && !matchedBefore) {
+ this.selectorMatched(element, selector);
+ }
+ else if (!matches && matchedBefore) {
+ this.selectorUnmatched(element, selector);
+ }
+ }
+ }
+ selectorMatched(element, selector) {
+ this.delegate.selectorMatched(element, selector, this.details);
+ this.matchesByElement.add(selector, element);
+ }
+ selectorUnmatched(element, selector) {
+ this.delegate.selectorUnmatched(element, selector, this.details);
+ this.matchesByElement.delete(selector, element);
+ }
+ }
+
+ class StringMapObserver {
+ constructor(element, delegate) {
+ this.element = element;
+ this.delegate = delegate;
+ this.started = false;
+ this.stringMap = new Map();
+ this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
+ }
+ start() {
+ if (!this.started) {
+ this.started = true;
+ this.mutationObserver.observe(this.element, { attributes: true, attributeOldValue: true });
+ this.refresh();
+ }
+ }
+ stop() {
+ if (this.started) {
+ this.mutationObserver.takeRecords();
+ this.mutationObserver.disconnect();
+ this.started = false;
+ }
+ }
+ refresh() {
+ if (this.started) {
+ for (const attributeName of this.knownAttributeNames) {
+ this.refreshAttribute(attributeName, null);
+ }
+ }
+ }
+ processMutations(mutations) {
+ if (this.started) {
+ for (const mutation of mutations) {
+ this.processMutation(mutation);
+ }
+ }
+ }
+ processMutation(mutation) {
+ const attributeName = mutation.attributeName;
+ if (attributeName) {
+ this.refreshAttribute(attributeName, mutation.oldValue);
+ }
+ }
+ refreshAttribute(attributeName, oldValue) {
+ const key = this.delegate.getStringMapKeyForAttribute(attributeName);
+ if (key != null) {
+ if (!this.stringMap.has(attributeName)) {
+ this.stringMapKeyAdded(key, attributeName);
+ }
+ const value = this.element.getAttribute(attributeName);
+ if (this.stringMap.get(attributeName) != value) {
+ this.stringMapValueChanged(value, key, oldValue);
+ }
+ if (value == null) {
+ const oldValue = this.stringMap.get(attributeName);
+ this.stringMap.delete(attributeName);
+ if (oldValue)
+ this.stringMapKeyRemoved(key, attributeName, oldValue);
+ }
+ else {
+ this.stringMap.set(attributeName, value);
+ }
+ }
+ }
+ stringMapKeyAdded(key, attributeName) {
+ if (this.delegate.stringMapKeyAdded) {
+ this.delegate.stringMapKeyAdded(key, attributeName);
+ }
+ }
+ stringMapValueChanged(value, key, oldValue) {
+ if (this.delegate.stringMapValueChanged) {
+ this.delegate.stringMapValueChanged(value, key, oldValue);
+ }
+ }
+ stringMapKeyRemoved(key, attributeName, oldValue) {
+ if (this.delegate.stringMapKeyRemoved) {
+ this.delegate.stringMapKeyRemoved(key, attributeName, oldValue);
+ }
+ }
+ get knownAttributeNames() {
+ return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)));
+ }
+ get currentAttributeNames() {
+ return Array.from(this.element.attributes).map((attribute) => attribute.name);
+ }
+ get recordedAttributeNames() {
+ return Array.from(this.stringMap.keys());
+ }
+ }
+
+ class TokenListObserver {
+ constructor(element, attributeName, delegate) {
+ this.attributeObserver = new AttributeObserver(element, attributeName, this);
+ this.delegate = delegate;
+ this.tokensByElement = new Multimap();
+ }
+ get started() {
+ return this.attributeObserver.started;
+ }
+ start() {
+ this.attributeObserver.start();
+ }
+ pause(callback) {
+ this.attributeObserver.pause(callback);
+ }
+ stop() {
+ this.attributeObserver.stop();
+ }
+ refresh() {
+ this.attributeObserver.refresh();
+ }
+ get element() {
+ return this.attributeObserver.element;
+ }
+ get attributeName() {
+ return this.attributeObserver.attributeName;
+ }
+ elementMatchedAttribute(element) {
+ this.tokensMatched(this.readTokensForElement(element));
+ }
+ elementAttributeValueChanged(element) {
+ const [unmatchedTokens, matchedTokens] = this.refreshTokensForElement(element);
+ this.tokensUnmatched(unmatchedTokens);
+ this.tokensMatched(matchedTokens);
+ }
+ elementUnmatchedAttribute(element) {
+ this.tokensUnmatched(this.tokensByElement.getValuesForKey(element));
+ }
+ tokensMatched(tokens) {
+ tokens.forEach((token) => this.tokenMatched(token));
+ }
+ tokensUnmatched(tokens) {
+ tokens.forEach((token) => this.tokenUnmatched(token));
+ }
+ tokenMatched(token) {
+ this.delegate.tokenMatched(token);
+ this.tokensByElement.add(token.element, token);
+ }
+ tokenUnmatched(token) {
+ this.delegate.tokenUnmatched(token);
+ this.tokensByElement.delete(token.element, token);
+ }
+ refreshTokensForElement(element) {
+ const previousTokens = this.tokensByElement.getValuesForKey(element);
+ const currentTokens = this.readTokensForElement(element);
+ const firstDifferingIndex = zip(previousTokens, currentTokens).findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken));
+ if (firstDifferingIndex == -1) {
+ return [[], []];
+ }
+ else {
+ return [previousTokens.slice(firstDifferingIndex), currentTokens.slice(firstDifferingIndex)];
+ }
+ }
+ readTokensForElement(element) {
+ const attributeName = this.attributeName;
+ const tokenString = element.getAttribute(attributeName) || "";
+ return parseTokenString(tokenString, element, attributeName);
+ }
+ }
+ function parseTokenString(tokenString, element, attributeName) {
+ return tokenString
+ .trim()
+ .split(/\s+/)
+ .filter((content) => content.length)
+ .map((content, index) => ({ element, attributeName, content, index }));
+ }
+ function zip(left, right) {
+ const length = Math.max(left.length, right.length);
+ return Array.from({ length }, (_, index) => [left[index], right[index]]);
+ }
+ function tokensAreEqual(left, right) {
+ return left && right && left.index == right.index && left.content == right.content;
+ }
+
+ class ValueListObserver {
+ constructor(element, attributeName, delegate) {
+ this.tokenListObserver = new TokenListObserver(element, attributeName, this);
+ this.delegate = delegate;
+ this.parseResultsByToken = new WeakMap();
+ this.valuesByTokenByElement = new WeakMap();
+ }
+ get started() {
+ return this.tokenListObserver.started;
+ }
+ start() {
+ this.tokenListObserver.start();
+ }
+ stop() {
+ this.tokenListObserver.stop();
+ }
+ refresh() {
+ this.tokenListObserver.refresh();
+ }
+ get element() {
+ return this.tokenListObserver.element;
+ }
+ get attributeName() {
+ return this.tokenListObserver.attributeName;
+ }
+ tokenMatched(token) {
+ const { element } = token;
+ const { value } = this.fetchParseResultForToken(token);
+ if (value) {
+ this.fetchValuesByTokenForElement(element).set(token, value);
+ this.delegate.elementMatchedValue(element, value);
+ }
+ }
+ tokenUnmatched(token) {
+ const { element } = token;
+ const { value } = this.fetchParseResultForToken(token);
+ if (value) {
+ this.fetchValuesByTokenForElement(element).delete(token);
+ this.delegate.elementUnmatchedValue(element, value);
+ }
+ }
+ fetchParseResultForToken(token) {
+ let parseResult = this.parseResultsByToken.get(token);
+ if (!parseResult) {
+ parseResult = this.parseToken(token);
+ this.parseResultsByToken.set(token, parseResult);
+ }
+ return parseResult;
+ }
+ fetchValuesByTokenForElement(element) {
+ let valuesByToken = this.valuesByTokenByElement.get(element);
+ if (!valuesByToken) {
+ valuesByToken = new Map();
+ this.valuesByTokenByElement.set(element, valuesByToken);
+ }
+ return valuesByToken;
+ }
+ parseToken(token) {
+ try {
+ const value = this.delegate.parseValueForToken(token);
+ return { value };
+ }
+ catch (error) {
+ return { error };
+ }
+ }
+ }
+
+ class BindingObserver {
+ constructor(context, delegate) {
+ this.context = context;
+ this.delegate = delegate;
+ this.bindingsByAction = new Map();
+ }
+ start() {
+ if (!this.valueListObserver) {
+ this.valueListObserver = new ValueListObserver(this.element, this.actionAttribute, this);
+ this.valueListObserver.start();
+ }
+ }
+ stop() {
+ if (this.valueListObserver) {
+ this.valueListObserver.stop();
+ delete this.valueListObserver;
+ this.disconnectAllActions();
+ }
+ }
+ get element() {
+ return this.context.element;
+ }
+ get identifier() {
+ return this.context.identifier;
+ }
+ get actionAttribute() {
+ return this.schema.actionAttribute;
+ }
+ get schema() {
+ return this.context.schema;
+ }
+ get bindings() {
+ return Array.from(this.bindingsByAction.values());
+ }
+ connectAction(action) {
+ const binding = new Binding(this.context, action);
+ this.bindingsByAction.set(action, binding);
+ this.delegate.bindingConnected(binding);
+ }
+ disconnectAction(action) {
+ const binding = this.bindingsByAction.get(action);
+ if (binding) {
+ this.bindingsByAction.delete(action);
+ this.delegate.bindingDisconnected(binding);
+ }
+ }
+ disconnectAllActions() {
+ this.bindings.forEach((binding) => this.delegate.bindingDisconnected(binding, true));
+ this.bindingsByAction.clear();
+ }
+ parseValueForToken(token) {
+ const action = Action.forToken(token, this.schema);
+ if (action.identifier == this.identifier) {
+ return action;
+ }
+ }
+ elementMatchedValue(element, action) {
+ this.connectAction(action);
+ }
+ elementUnmatchedValue(element, action) {
+ this.disconnectAction(action);
+ }
+ }
+
+ class ValueObserver {
+ constructor(context, receiver) {
+ this.context = context;
+ this.receiver = receiver;
+ this.stringMapObserver = new StringMapObserver(this.element, this);
+ this.valueDescriptorMap = this.controller.valueDescriptorMap;
+ }
+ start() {
+ this.stringMapObserver.start();
+ this.invokeChangedCallbacksForDefaultValues();
+ }
+ stop() {
+ this.stringMapObserver.stop();
+ }
+ get element() {
+ return this.context.element;
+ }
+ get controller() {
+ return this.context.controller;
+ }
+ getStringMapKeyForAttribute(attributeName) {
+ if (attributeName in this.valueDescriptorMap) {
+ return this.valueDescriptorMap[attributeName].name;
+ }
+ }
+ stringMapKeyAdded(key, attributeName) {
+ const descriptor = this.valueDescriptorMap[attributeName];
+ if (!this.hasValue(key)) {
+ this.invokeChangedCallback(key, descriptor.writer(this.receiver[key]), descriptor.writer(descriptor.defaultValue));
+ }
+ }
+ stringMapValueChanged(value, name, oldValue) {
+ const descriptor = this.valueDescriptorNameMap[name];
+ if (value === null)
+ return;
+ if (oldValue === null) {
+ oldValue = descriptor.writer(descriptor.defaultValue);
+ }
+ this.invokeChangedCallback(name, value, oldValue);
+ }
+ stringMapKeyRemoved(key, attributeName, oldValue) {
+ const descriptor = this.valueDescriptorNameMap[key];
+ if (this.hasValue(key)) {
+ this.invokeChangedCallback(key, descriptor.writer(this.receiver[key]), oldValue);
+ }
+ else {
+ this.invokeChangedCallback(key, descriptor.writer(descriptor.defaultValue), oldValue);
+ }
+ }
+ invokeChangedCallbacksForDefaultValues() {
+ for (const { key, name, defaultValue, writer } of this.valueDescriptors) {
+ if (defaultValue != undefined && !this.controller.data.has(key)) {
+ this.invokeChangedCallback(name, writer(defaultValue), undefined);
+ }
+ }
+ }
+ invokeChangedCallback(name, rawValue, rawOldValue) {
+ const changedMethodName = `${name}Changed`;
+ const changedMethod = this.receiver[changedMethodName];
+ if (typeof changedMethod == "function") {
+ const descriptor = this.valueDescriptorNameMap[name];
+ try {
+ const value = descriptor.reader(rawValue);
+ let oldValue = rawOldValue;
+ if (rawOldValue) {
+ oldValue = descriptor.reader(rawOldValue);
+ }
+ changedMethod.call(this.receiver, value, oldValue);
+ }
+ catch (error) {
+ if (error instanceof TypeError) {
+ error.message = `Stimulus Value "${this.context.identifier}.${descriptor.name}" - ${error.message}`;
+ }
+ throw error;
+ }
+ }
+ }
+ get valueDescriptors() {
+ const { valueDescriptorMap } = this;
+ return Object.keys(valueDescriptorMap).map((key) => valueDescriptorMap[key]);
+ }
+ get valueDescriptorNameMap() {
+ const descriptors = {};
+ Object.keys(this.valueDescriptorMap).forEach((key) => {
+ const descriptor = this.valueDescriptorMap[key];
+ descriptors[descriptor.name] = descriptor;
+ });
+ return descriptors;
+ }
+ hasValue(attributeName) {
+ const descriptor = this.valueDescriptorNameMap[attributeName];
+ const hasMethodName = `has${capitalize(descriptor.name)}`;
+ return this.receiver[hasMethodName];
+ }
+ }
+
+ class TargetObserver {
+ constructor(context, delegate) {
+ this.context = context;
+ this.delegate = delegate;
+ this.targetsByName = new Multimap();
+ }
+ start() {
+ if (!this.tokenListObserver) {
+ this.tokenListObserver = new TokenListObserver(this.element, this.attributeName, this);
+ this.tokenListObserver.start();
+ }
+ }
+ stop() {
+ if (this.tokenListObserver) {
+ this.disconnectAllTargets();
+ this.tokenListObserver.stop();
+ delete this.tokenListObserver;
+ }
+ }
+ tokenMatched({ element, content: name }) {
+ if (this.scope.containsElement(element)) {
+ this.connectTarget(element, name);
+ }
+ }
+ tokenUnmatched({ element, content: name }) {
+ this.disconnectTarget(element, name);
+ }
+ connectTarget(element, name) {
+ var _a;
+ if (!this.targetsByName.has(name, element)) {
+ this.targetsByName.add(name, element);
+ (_a = this.tokenListObserver) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.targetConnected(element, name));
+ }
+ }
+ disconnectTarget(element, name) {
+ var _a;
+ if (this.targetsByName.has(name, element)) {
+ this.targetsByName.delete(name, element);
+ (_a = this.tokenListObserver) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.targetDisconnected(element, name));
+ }
+ }
+ disconnectAllTargets() {
+ for (const name of this.targetsByName.keys) {
+ for (const element of this.targetsByName.getValuesForKey(name)) {
+ this.disconnectTarget(element, name);
+ }
+ }
+ }
+ get attributeName() {
+ return `data-${this.context.identifier}-target`;
+ }
+ get element() {
+ return this.context.element;
+ }
+ get scope() {
+ return this.context.scope;
+ }
+ }
+
+ function readInheritableStaticArrayValues(constructor, propertyName) {
+ const ancestors = getAncestorsForConstructor(constructor);
+ return Array.from(ancestors.reduce((values, constructor) => {
+ getOwnStaticArrayValues(constructor, propertyName).forEach((name) => values.add(name));
+ return values;
+ }, new Set()));
+ }
+ function readInheritableStaticObjectPairs(constructor, propertyName) {
+ const ancestors = getAncestorsForConstructor(constructor);
+ return ancestors.reduce((pairs, constructor) => {
+ pairs.push(...getOwnStaticObjectPairs(constructor, propertyName));
+ return pairs;
+ }, []);
+ }
+ function getAncestorsForConstructor(constructor) {
+ const ancestors = [];
+ while (constructor) {
+ ancestors.push(constructor);
+ constructor = Object.getPrototypeOf(constructor);
+ }
+ return ancestors.reverse();
+ }
+ function getOwnStaticArrayValues(constructor, propertyName) {
+ const definition = constructor[propertyName];
+ return Array.isArray(definition) ? definition : [];
+ }
+ function getOwnStaticObjectPairs(constructor, propertyName) {
+ const definition = constructor[propertyName];
+ return definition ? Object.keys(definition).map((key) => [key, definition[key]]) : [];
+ }
+
+ class OutletObserver {
+ constructor(context, delegate) {
+ this.started = false;
+ this.context = context;
+ this.delegate = delegate;
+ this.outletsByName = new Multimap();
+ this.outletElementsByName = new Multimap();
+ this.selectorObserverMap = new Map();
+ this.attributeObserverMap = new Map();
+ }
+ start() {
+ if (!this.started) {
+ this.outletDefinitions.forEach((outletName) => {
+ this.setupSelectorObserverForOutlet(outletName);
+ this.setupAttributeObserverForOutlet(outletName);
+ });
+ this.started = true;
+ this.dependentContexts.forEach((context) => context.refresh());
+ }
+ }
+ refresh() {
+ this.selectorObserverMap.forEach((observer) => observer.refresh());
+ this.attributeObserverMap.forEach((observer) => observer.refresh());
+ }
+ stop() {
+ if (this.started) {
+ this.started = false;
+ this.disconnectAllOutlets();
+ this.stopSelectorObservers();
+ this.stopAttributeObservers();
+ }
+ }
+ stopSelectorObservers() {
+ if (this.selectorObserverMap.size > 0) {
+ this.selectorObserverMap.forEach((observer) => observer.stop());
+ this.selectorObserverMap.clear();
+ }
+ }
+ stopAttributeObservers() {
+ if (this.attributeObserverMap.size > 0) {
+ this.attributeObserverMap.forEach((observer) => observer.stop());
+ this.attributeObserverMap.clear();
+ }
+ }
+ selectorMatched(element, _selector, { outletName }) {
+ const outlet = this.getOutlet(element, outletName);
+ if (outlet) {
+ this.connectOutlet(outlet, element, outletName);
+ }
+ }
+ selectorUnmatched(element, _selector, { outletName }) {
+ const outlet = this.getOutletFromMap(element, outletName);
+ if (outlet) {
+ this.disconnectOutlet(outlet, element, outletName);
+ }
+ }
+ selectorMatchElement(element, { outletName }) {
+ const selector = this.selector(outletName);
+ const hasOutlet = this.hasOutlet(element, outletName);
+ const hasOutletController = element.matches(`[${this.schema.controllerAttribute}~=${outletName}]`);
+ if (selector) {
+ return hasOutlet && hasOutletController && element.matches(selector);
+ }
+ else {
+ return false;
+ }
+ }
+ elementMatchedAttribute(_element, attributeName) {
+ const outletName = this.getOutletNameFromOutletAttributeName(attributeName);
+ if (outletName) {
+ this.updateSelectorObserverForOutlet(outletName);
+ }
+ }
+ elementAttributeValueChanged(_element, attributeName) {
+ const outletName = this.getOutletNameFromOutletAttributeName(attributeName);
+ if (outletName) {
+ this.updateSelectorObserverForOutlet(outletName);
+ }
+ }
+ elementUnmatchedAttribute(_element, attributeName) {
+ const outletName = this.getOutletNameFromOutletAttributeName(attributeName);
+ if (outletName) {
+ this.updateSelectorObserverForOutlet(outletName);
+ }
+ }
+ connectOutlet(outlet, element, outletName) {
+ var _a;
+ if (!this.outletElementsByName.has(outletName, element)) {
+ this.outletsByName.add(outletName, outlet);
+ this.outletElementsByName.add(outletName, element);
+ (_a = this.selectorObserverMap.get(outletName)) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.outletConnected(outlet, element, outletName));
+ }
+ }
+ disconnectOutlet(outlet, element, outletName) {
+ var _a;
+ if (this.outletElementsByName.has(outletName, element)) {
+ this.outletsByName.delete(outletName, outlet);
+ this.outletElementsByName.delete(outletName, element);
+ (_a = this.selectorObserverMap
+ .get(outletName)) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.outletDisconnected(outlet, element, outletName));
+ }
+ }
+ disconnectAllOutlets() {
+ for (const outletName of this.outletElementsByName.keys) {
+ for (const element of this.outletElementsByName.getValuesForKey(outletName)) {
+ for (const outlet of this.outletsByName.getValuesForKey(outletName)) {
+ this.disconnectOutlet(outlet, element, outletName);
+ }
+ }
+ }
+ }
+ updateSelectorObserverForOutlet(outletName) {
+ const observer = this.selectorObserverMap.get(outletName);
+ if (observer) {
+ observer.selector = this.selector(outletName);
+ }
+ }
+ setupSelectorObserverForOutlet(outletName) {
+ const selector = this.selector(outletName);
+ const selectorObserver = new SelectorObserver(document.body, selector, this, { outletName });
+ this.selectorObserverMap.set(outletName, selectorObserver);
+ selectorObserver.start();
+ }
+ setupAttributeObserverForOutlet(outletName) {
+ const attributeName = this.attributeNameForOutletName(outletName);
+ const attributeObserver = new AttributeObserver(this.scope.element, attributeName, this);
+ this.attributeObserverMap.set(outletName, attributeObserver);
+ attributeObserver.start();
+ }
+ selector(outletName) {
+ return this.scope.outlets.getSelectorForOutletName(outletName);
+ }
+ attributeNameForOutletName(outletName) {
+ return this.scope.schema.outletAttributeForScope(this.identifier, outletName);
+ }
+ getOutletNameFromOutletAttributeName(attributeName) {
+ return this.outletDefinitions.find((outletName) => this.attributeNameForOutletName(outletName) === attributeName);
+ }
+ get outletDependencies() {
+ const dependencies = new Multimap();
+ this.router.modules.forEach((module) => {
+ const constructor = module.definition.controllerConstructor;
+ const outlets = readInheritableStaticArrayValues(constructor, "outlets");
+ outlets.forEach((outlet) => dependencies.add(outlet, module.identifier));
+ });
+ return dependencies;
+ }
+ get outletDefinitions() {
+ return this.outletDependencies.getKeysForValue(this.identifier);
+ }
+ get dependentControllerIdentifiers() {
+ return this.outletDependencies.getValuesForKey(this.identifier);
+ }
+ get dependentContexts() {
+ const identifiers = this.dependentControllerIdentifiers;
+ return this.router.contexts.filter((context) => identifiers.includes(context.identifier));
+ }
+ hasOutlet(element, outletName) {
+ return !!this.getOutlet(element, outletName) || !!this.getOutletFromMap(element, outletName);
+ }
+ getOutlet(element, outletName) {
+ return this.application.getControllerForElementAndIdentifier(element, outletName);
+ }
+ getOutletFromMap(element, outletName) {
+ return this.outletsByName.getValuesForKey(outletName).find((outlet) => outlet.element === element);
+ }
+ get scope() {
+ return this.context.scope;
+ }
+ get schema() {
+ return this.context.schema;
+ }
+ get identifier() {
+ return this.context.identifier;
+ }
+ get application() {
+ return this.context.application;
+ }
+ get router() {
+ return this.application.router;
+ }
+ }
+
+ class Context {
+ constructor(module, scope) {
+ this.logDebugActivity = (functionName, detail = {}) => {
+ const { identifier, controller, element } = this;
+ detail = Object.assign({ identifier, controller, element }, detail);
+ this.application.logDebugActivity(this.identifier, functionName, detail);
+ };
+ this.module = module;
+ this.scope = scope;
+ this.controller = new module.controllerConstructor(this);
+ this.bindingObserver = new BindingObserver(this, this.dispatcher);
+ this.valueObserver = new ValueObserver(this, this.controller);
+ this.targetObserver = new TargetObserver(this, this);
+ this.outletObserver = new OutletObserver(this, this);
+ try {
+ this.controller.initialize();
+ this.logDebugActivity("initialize");
+ }
+ catch (error) {
+ this.handleError(error, "initializing controller");
+ }
+ }
+ connect() {
+ this.bindingObserver.start();
+ this.valueObserver.start();
+ this.targetObserver.start();
+ this.outletObserver.start();
+ try {
+ this.controller.connect();
+ this.logDebugActivity("connect");
+ }
+ catch (error) {
+ this.handleError(error, "connecting controller");
+ }
+ }
+ refresh() {
+ this.outletObserver.refresh();
+ }
+ disconnect() {
+ try {
+ this.controller.disconnect();
+ this.logDebugActivity("disconnect");
+ }
+ catch (error) {
+ this.handleError(error, "disconnecting controller");
+ }
+ this.outletObserver.stop();
+ this.targetObserver.stop();
+ this.valueObserver.stop();
+ this.bindingObserver.stop();
+ }
+ get application() {
+ return this.module.application;
+ }
+ get identifier() {
+ return this.module.identifier;
+ }
+ get schema() {
+ return this.application.schema;
+ }
+ get dispatcher() {
+ return this.application.dispatcher;
+ }
+ get element() {
+ return this.scope.element;
+ }
+ get parentElement() {
+ return this.element.parentElement;
+ }
+ handleError(error, message, detail = {}) {
+ const { identifier, controller, element } = this;
+ detail = Object.assign({ identifier, controller, element }, detail);
+ this.application.handleError(error, `Error ${message}`, detail);
+ }
+ targetConnected(element, name) {
+ this.invokeControllerMethod(`${name}TargetConnected`, element);
+ }
+ targetDisconnected(element, name) {
+ this.invokeControllerMethod(`${name}TargetDisconnected`, element);
+ }
+ outletConnected(outlet, element, name) {
+ this.invokeControllerMethod(`${namespaceCamelize(name)}OutletConnected`, outlet, element);
+ }
+ outletDisconnected(outlet, element, name) {
+ this.invokeControllerMethod(`${namespaceCamelize(name)}OutletDisconnected`, outlet, element);
+ }
+ invokeControllerMethod(methodName, ...args) {
+ const controller = this.controller;
+ if (typeof controller[methodName] == "function") {
+ controller[methodName](...args);
+ }
+ }
+ }
+
+ function bless(constructor) {
+ return shadow(constructor, getBlessedProperties(constructor));
+ }
+ function shadow(constructor, properties) {
+ const shadowConstructor = extend(constructor);
+ const shadowProperties = getShadowProperties(constructor.prototype, properties);
+ Object.defineProperties(shadowConstructor.prototype, shadowProperties);
+ return shadowConstructor;
+ }
+ function getBlessedProperties(constructor) {
+ const blessings = readInheritableStaticArrayValues(constructor, "blessings");
+ return blessings.reduce((blessedProperties, blessing) => {
+ const properties = blessing(constructor);
+ for (const key in properties) {
+ const descriptor = blessedProperties[key] || {};
+ blessedProperties[key] = Object.assign(descriptor, properties[key]);
+ }
+ return blessedProperties;
+ }, {});
+ }
+ function getShadowProperties(prototype, properties) {
+ return getOwnKeys(properties).reduce((shadowProperties, key) => {
+ const descriptor = getShadowedDescriptor(prototype, properties, key);
+ if (descriptor) {
+ Object.assign(shadowProperties, { [key]: descriptor });
+ }
+ return shadowProperties;
+ }, {});
+ }
+ function getShadowedDescriptor(prototype, properties, key) {
+ const shadowingDescriptor = Object.getOwnPropertyDescriptor(prototype, key);
+ const shadowedByValue = shadowingDescriptor && "value" in shadowingDescriptor;
+ if (!shadowedByValue) {
+ const descriptor = Object.getOwnPropertyDescriptor(properties, key).value;
+ if (shadowingDescriptor) {
+ descriptor.get = shadowingDescriptor.get || descriptor.get;
+ descriptor.set = shadowingDescriptor.set || descriptor.set;
+ }
+ return descriptor;
+ }
+ }
+ const getOwnKeys = (() => {
+ if (typeof Object.getOwnPropertySymbols == "function") {
+ return (object) => [...Object.getOwnPropertyNames(object), ...Object.getOwnPropertySymbols(object)];
+ }
+ else {
+ return Object.getOwnPropertyNames;
+ }
+ })();
+ const extend = (() => {
+ function extendWithReflect(constructor) {
+ function extended() {
+ return Reflect.construct(constructor, arguments, new.target);
+ }
+ extended.prototype = Object.create(constructor.prototype, {
+ constructor: { value: extended },
+ });
+ Reflect.setPrototypeOf(extended, constructor);
+ return extended;
+ }
+ function testReflectExtension() {
+ const a = function () {
+ this.a.call(this);
+ };
+ const b = extendWithReflect(a);
+ b.prototype.a = function () { };
+ return new b();
+ }
+ try {
+ testReflectExtension();
+ return extendWithReflect;
+ }
+ catch (error) {
+ return (constructor) => class extended extends constructor {
+ };
+ }
+ })();
+
+ function blessDefinition(definition) {
+ return {
+ identifier: definition.identifier,
+ controllerConstructor: bless(definition.controllerConstructor),
+ };
+ }
+
+ class Module {
+ constructor(application, definition) {
+ this.application = application;
+ this.definition = blessDefinition(definition);
+ this.contextsByScope = new WeakMap();
+ this.connectedContexts = new Set();
+ }
+ get identifier() {
+ return this.definition.identifier;
+ }
+ get controllerConstructor() {
+ return this.definition.controllerConstructor;
+ }
+ get contexts() {
+ return Array.from(this.connectedContexts);
+ }
+ connectContextForScope(scope) {
+ const context = this.fetchContextForScope(scope);
+ this.connectedContexts.add(context);
+ context.connect();
+ }
+ disconnectContextForScope(scope) {
+ const context = this.contextsByScope.get(scope);
+ if (context) {
+ this.connectedContexts.delete(context);
+ context.disconnect();
+ }
+ }
+ fetchContextForScope(scope) {
+ let context = this.contextsByScope.get(scope);
+ if (!context) {
+ context = new Context(this, scope);
+ this.contextsByScope.set(scope, context);
+ }
+ return context;
+ }
+ }
+
+ class ClassMap {
+ constructor(scope) {
+ this.scope = scope;
+ }
+ has(name) {
+ return this.data.has(this.getDataKey(name));
+ }
+ get(name) {
+ return this.getAll(name)[0];
+ }
+ getAll(name) {
+ const tokenString = this.data.get(this.getDataKey(name)) || "";
+ return tokenize(tokenString);
+ }
+ getAttributeName(name) {
+ return this.data.getAttributeNameForKey(this.getDataKey(name));
+ }
+ getDataKey(name) {
+ return `${name}-class`;
+ }
+ get data() {
+ return this.scope.data;
+ }
+ }
+
+ class DataMap {
+ constructor(scope) {
+ this.scope = scope;
+ }
+ get element() {
+ return this.scope.element;
+ }
+ get identifier() {
+ return this.scope.identifier;
+ }
+ get(key) {
+ const name = this.getAttributeNameForKey(key);
+ return this.element.getAttribute(name);
+ }
+ set(key, value) {
+ const name = this.getAttributeNameForKey(key);
+ this.element.setAttribute(name, value);
+ return this.get(key);
+ }
+ has(key) {
+ const name = this.getAttributeNameForKey(key);
+ return this.element.hasAttribute(name);
+ }
+ delete(key) {
+ if (this.has(key)) {
+ const name = this.getAttributeNameForKey(key);
+ this.element.removeAttribute(name);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ getAttributeNameForKey(key) {
+ return `data-${this.identifier}-${dasherize(key)}`;
+ }
+ }
+
+ class Guide {
+ constructor(logger) {
+ this.warnedKeysByObject = new WeakMap();
+ this.logger = logger;
+ }
+ warn(object, key, message) {
+ let warnedKeys = this.warnedKeysByObject.get(object);
+ if (!warnedKeys) {
+ warnedKeys = new Set();
+ this.warnedKeysByObject.set(object, warnedKeys);
+ }
+ if (!warnedKeys.has(key)) {
+ warnedKeys.add(key);
+ this.logger.warn(message, object);
+ }
+ }
+ }
+
+ function attributeValueContainsToken(attributeName, token) {
+ return `[${attributeName}~="${token}"]`;
+ }
+
+ class TargetSet {
+ constructor(scope) {
+ this.scope = scope;
+ }
+ get element() {
+ return this.scope.element;
+ }
+ get identifier() {
+ return this.scope.identifier;
+ }
+ get schema() {
+ return this.scope.schema;
+ }
+ has(targetName) {
+ return this.find(targetName) != null;
+ }
+ find(...targetNames) {
+ return targetNames.reduce((target, targetName) => target || this.findTarget(targetName) || this.findLegacyTarget(targetName), undefined);
+ }
+ findAll(...targetNames) {
+ return targetNames.reduce((targets, targetName) => [
+ ...targets,
+ ...this.findAllTargets(targetName),
+ ...this.findAllLegacyTargets(targetName),
+ ], []);
+ }
+ findTarget(targetName) {
+ const selector = this.getSelectorForTargetName(targetName);
+ return this.scope.findElement(selector);
+ }
+ findAllTargets(targetName) {
+ const selector = this.getSelectorForTargetName(targetName);
+ return this.scope.findAllElements(selector);
+ }
+ getSelectorForTargetName(targetName) {
+ const attributeName = this.schema.targetAttributeForScope(this.identifier);
+ return attributeValueContainsToken(attributeName, targetName);
+ }
+ findLegacyTarget(targetName) {
+ const selector = this.getLegacySelectorForTargetName(targetName);
+ return this.deprecate(this.scope.findElement(selector), targetName);
+ }
+ findAllLegacyTargets(targetName) {
+ const selector = this.getLegacySelectorForTargetName(targetName);
+ return this.scope.findAllElements(selector).map((element) => this.deprecate(element, targetName));
+ }
+ getLegacySelectorForTargetName(targetName) {
+ const targetDescriptor = `${this.identifier}.${targetName}`;
+ return attributeValueContainsToken(this.schema.targetAttribute, targetDescriptor);
+ }
+ deprecate(element, targetName) {
+ if (element) {
+ const { identifier } = this;
+ const attributeName = this.schema.targetAttribute;
+ const revisedAttributeName = this.schema.targetAttributeForScope(identifier);
+ this.guide.warn(element, `target:${targetName}`, `Please replace ${attributeName}="${identifier}.${targetName}" with ${revisedAttributeName}="${targetName}". ` +
+ `The ${attributeName} attribute is deprecated and will be removed in a future version of Stimulus.`);
+ }
+ return element;
+ }
+ get guide() {
+ return this.scope.guide;
+ }
+ }
+
+ class OutletSet {
+ constructor(scope, controllerElement) {
+ this.scope = scope;
+ this.controllerElement = controllerElement;
+ }
+ get element() {
+ return this.scope.element;
+ }
+ get identifier() {
+ return this.scope.identifier;
+ }
+ get schema() {
+ return this.scope.schema;
+ }
+ has(outletName) {
+ return this.find(outletName) != null;
+ }
+ find(...outletNames) {
+ return outletNames.reduce((outlet, outletName) => outlet || this.findOutlet(outletName), undefined);
+ }
+ findAll(...outletNames) {
+ return outletNames.reduce((outlets, outletName) => [...outlets, ...this.findAllOutlets(outletName)], []);
+ }
+ getSelectorForOutletName(outletName) {
+ const attributeName = this.schema.outletAttributeForScope(this.identifier, outletName);
+ return this.controllerElement.getAttribute(attributeName);
+ }
+ findOutlet(outletName) {
+ const selector = this.getSelectorForOutletName(outletName);
+ if (selector)
+ return this.findElement(selector, outletName);
+ }
+ findAllOutlets(outletName) {
+ const selector = this.getSelectorForOutletName(outletName);
+ return selector ? this.findAllElements(selector, outletName) : [];
+ }
+ findElement(selector, outletName) {
+ const elements = this.scope.queryElements(selector);
+ return elements.filter((element) => this.matchesElement(element, selector, outletName))[0];
+ }
+ findAllElements(selector, outletName) {
+ const elements = this.scope.queryElements(selector);
+ return elements.filter((element) => this.matchesElement(element, selector, outletName));
+ }
+ matchesElement(element, selector, outletName) {
+ const controllerAttribute = element.getAttribute(this.scope.schema.controllerAttribute) || "";
+ return element.matches(selector) && controllerAttribute.split(" ").includes(outletName);
+ }
+ }
+
+ class Scope {
+ constructor(schema, element, identifier, logger) {
+ this.targets = new TargetSet(this);
+ this.classes = new ClassMap(this);
+ this.data = new DataMap(this);
+ this.containsElement = (element) => {
+ return element.closest(this.controllerSelector) === this.element;
+ };
+ this.schema = schema;
+ this.element = element;
+ this.identifier = identifier;
+ this.guide = new Guide(logger);
+ this.outlets = new OutletSet(this.documentScope, element);
+ }
+ findElement(selector) {
+ return this.element.matches(selector) ? this.element : this.queryElements(selector).find(this.containsElement);
+ }
+ findAllElements(selector) {
+ return [
+ ...(this.element.matches(selector) ? [this.element] : []),
+ ...this.queryElements(selector).filter(this.containsElement),
+ ];
+ }
+ queryElements(selector) {
+ return Array.from(this.element.querySelectorAll(selector));
+ }
+ get controllerSelector() {
+ return attributeValueContainsToken(this.schema.controllerAttribute, this.identifier);
+ }
+ get isDocumentScope() {
+ return this.element === document.documentElement;
+ }
+ get documentScope() {
+ return this.isDocumentScope
+ ? this
+ : new Scope(this.schema, document.documentElement, this.identifier, this.guide.logger);
+ }
+ }
+
+ class ScopeObserver {
+ constructor(element, schema, delegate) {
+ this.element = element;
+ this.schema = schema;
+ this.delegate = delegate;
+ this.valueListObserver = new ValueListObserver(this.element, this.controllerAttribute, this);
+ this.scopesByIdentifierByElement = new WeakMap();
+ this.scopeReferenceCounts = new WeakMap();
+ }
+ start() {
+ this.valueListObserver.start();
+ }
+ stop() {
+ this.valueListObserver.stop();
+ }
+ get controllerAttribute() {
+ return this.schema.controllerAttribute;
+ }
+ parseValueForToken(token) {
+ const { element, content: identifier } = token;
+ return this.parseValueForElementAndIdentifier(element, identifier);
+ }
+ parseValueForElementAndIdentifier(element, identifier) {
+ const scopesByIdentifier = this.fetchScopesByIdentifierForElement(element);
+ let scope = scopesByIdentifier.get(identifier);
+ if (!scope) {
+ scope = this.delegate.createScopeForElementAndIdentifier(element, identifier);
+ scopesByIdentifier.set(identifier, scope);
+ }
+ return scope;
+ }
+ elementMatchedValue(element, value) {
+ const referenceCount = (this.scopeReferenceCounts.get(value) || 0) + 1;
+ this.scopeReferenceCounts.set(value, referenceCount);
+ if (referenceCount == 1) {
+ this.delegate.scopeConnected(value);
+ }
+ }
+ elementUnmatchedValue(element, value) {
+ const referenceCount = this.scopeReferenceCounts.get(value);
+ if (referenceCount) {
+ this.scopeReferenceCounts.set(value, referenceCount - 1);
+ if (referenceCount == 1) {
+ this.delegate.scopeDisconnected(value);
+ }
+ }
+ }
+ fetchScopesByIdentifierForElement(element) {
+ let scopesByIdentifier = this.scopesByIdentifierByElement.get(element);
+ if (!scopesByIdentifier) {
+ scopesByIdentifier = new Map();
+ this.scopesByIdentifierByElement.set(element, scopesByIdentifier);
+ }
+ return scopesByIdentifier;
+ }
+ }
+
+ class Router {
+ constructor(application) {
+ this.application = application;
+ this.scopeObserver = new ScopeObserver(this.element, this.schema, this);
+ this.scopesByIdentifier = new Multimap();
+ this.modulesByIdentifier = new Map();
+ }
+ get element() {
+ return this.application.element;
+ }
+ get schema() {
+ return this.application.schema;
+ }
+ get logger() {
+ return this.application.logger;
+ }
+ get controllerAttribute() {
+ return this.schema.controllerAttribute;
+ }
+ get modules() {
+ return Array.from(this.modulesByIdentifier.values());
+ }
+ get contexts() {
+ return this.modules.reduce((contexts, module) => contexts.concat(module.contexts), []);
+ }
+ start() {
+ this.scopeObserver.start();
+ }
+ stop() {
+ this.scopeObserver.stop();
+ }
+ loadDefinition(definition) {
+ this.unloadIdentifier(definition.identifier);
+ const module = new Module(this.application, definition);
+ this.connectModule(module);
+ const afterLoad = definition.controllerConstructor.afterLoad;
+ if (afterLoad) {
+ afterLoad.call(definition.controllerConstructor, definition.identifier, this.application);
+ }
+ }
+ unloadIdentifier(identifier) {
+ const module = this.modulesByIdentifier.get(identifier);
+ if (module) {
+ this.disconnectModule(module);
+ }
+ }
+ getContextForElementAndIdentifier(element, identifier) {
+ const module = this.modulesByIdentifier.get(identifier);
+ if (module) {
+ return module.contexts.find((context) => context.element == element);
+ }
+ }
+ proposeToConnectScopeForElementAndIdentifier(element, identifier) {
+ const scope = this.scopeObserver.parseValueForElementAndIdentifier(element, identifier);
+ if (scope) {
+ this.scopeObserver.elementMatchedValue(scope.element, scope);
+ }
+ else {
+ console.error(`Couldn't find or create scope for identifier: "${identifier}" and element:`, element);
+ }
+ }
+ handleError(error, message, detail) {
+ this.application.handleError(error, message, detail);
+ }
+ createScopeForElementAndIdentifier(element, identifier) {
+ return new Scope(this.schema, element, identifier, this.logger);
+ }
+ scopeConnected(scope) {
+ this.scopesByIdentifier.add(scope.identifier, scope);
+ const module = this.modulesByIdentifier.get(scope.identifier);
+ if (module) {
+ module.connectContextForScope(scope);
+ }
+ }
+ scopeDisconnected(scope) {
+ this.scopesByIdentifier.delete(scope.identifier, scope);
+ const module = this.modulesByIdentifier.get(scope.identifier);
+ if (module) {
+ module.disconnectContextForScope(scope);
+ }
+ }
+ connectModule(module) {
+ this.modulesByIdentifier.set(module.identifier, module);
+ const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
+ scopes.forEach((scope) => module.connectContextForScope(scope));
+ }
+ disconnectModule(module) {
+ this.modulesByIdentifier.delete(module.identifier);
+ const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
+ scopes.forEach((scope) => module.disconnectContextForScope(scope));
+ }
+ }
+
+ const defaultSchema = {
+ controllerAttribute: "data-controller",
+ actionAttribute: "data-action",
+ targetAttribute: "data-target",
+ targetAttributeForScope: (identifier) => `data-${identifier}-target`,
+ outletAttributeForScope: (identifier, outlet) => `data-${identifier}-${outlet}-outlet`,
+ keyMappings: Object.assign(Object.assign({ enter: "Enter", tab: "Tab", esc: "Escape", space: " ", up: "ArrowUp", down: "ArrowDown", left: "ArrowLeft", right: "ArrowRight", home: "Home", end: "End", page_up: "PageUp", page_down: "PageDown" }, objectFromEntries("abcdefghijklmnopqrstuvwxyz".split("").map((c) => [c, c]))), objectFromEntries("0123456789".split("").map((n) => [n, n]))),
+ };
+ function objectFromEntries(array) {
+ return array.reduce((memo, [k, v]) => (Object.assign(Object.assign({}, memo), { [k]: v })), {});
+ }
+
+ class Application {
+ constructor(element = document.documentElement, schema = defaultSchema) {
+ this.logger = console;
+ this.debug = false;
+ this.logDebugActivity = (identifier, functionName, detail = {}) => {
+ if (this.debug) {
+ this.logFormattedMessage(identifier, functionName, detail);
+ }
+ };
+ this.element = element;
+ this.schema = schema;
+ this.dispatcher = new Dispatcher(this);
+ this.router = new Router(this);
+ this.actionDescriptorFilters = Object.assign({}, defaultActionDescriptorFilters);
+ }
+ static start(element, schema) {
+ const application = new this(element, schema);
+ application.start();
+ return application;
+ }
+ async start() {
+ await domReady();
+ this.logDebugActivity("application", "starting");
+ this.dispatcher.start();
+ this.router.start();
+ this.logDebugActivity("application", "start");
+ }
+ stop() {
+ this.logDebugActivity("application", "stopping");
+ this.dispatcher.stop();
+ this.router.stop();
+ this.logDebugActivity("application", "stop");
+ }
+ register(identifier, controllerConstructor) {
+ this.load({ identifier, controllerConstructor });
+ }
+ registerActionOption(name, filter) {
+ this.actionDescriptorFilters[name] = filter;
+ }
+ load(head, ...rest) {
+ const definitions = Array.isArray(head) ? head : [head, ...rest];
+ definitions.forEach((definition) => {
+ if (definition.controllerConstructor.shouldLoad) {
+ this.router.loadDefinition(definition);
+ }
+ });
+ }
+ unload(head, ...rest) {
+ const identifiers = Array.isArray(head) ? head : [head, ...rest];
+ identifiers.forEach((identifier) => this.router.unloadIdentifier(identifier));
+ }
+ get controllers() {
+ return this.router.contexts.map((context) => context.controller);
+ }
+ getControllerForElementAndIdentifier(element, identifier) {
+ const context = this.router.getContextForElementAndIdentifier(element, identifier);
+ return context ? context.controller : null;
+ }
+ handleError(error, message, detail) {
+ var _a;
+ this.logger.error(`%s\n\n%o\n\n%o`, message, error, detail);
+ (_a = window.onerror) === null || _a === void 0 ? void 0 : _a.call(window, message, "", 0, 0, error);
+ }
+ logFormattedMessage(identifier, functionName, detail = {}) {
+ detail = Object.assign({ application: this }, detail);
+ this.logger.groupCollapsed(`${identifier} #${functionName}`);
+ this.logger.log("details:", Object.assign({}, detail));
+ this.logger.groupEnd();
+ }
+ }
+ function domReady() {
+ return new Promise((resolve) => {
+ if (document.readyState == "loading") {
+ document.addEventListener("DOMContentLoaded", () => resolve());
+ }
+ else {
+ resolve();
+ }
+ });
+ }
+
+ function ClassPropertiesBlessing(constructor) {
+ const classes = readInheritableStaticArrayValues(constructor, "classes");
+ return classes.reduce((properties, classDefinition) => {
+ return Object.assign(properties, propertiesForClassDefinition(classDefinition));
+ }, {});
+ }
+ function propertiesForClassDefinition(key) {
+ return {
+ [`${key}Class`]: {
+ get() {
+ const { classes } = this;
+ if (classes.has(key)) {
+ return classes.get(key);
+ }
+ else {
+ const attribute = classes.getAttributeName(key);
+ throw new Error(`Missing attribute "${attribute}"`);
+ }
+ },
+ },
+ [`${key}Classes`]: {
+ get() {
+ return this.classes.getAll(key);
+ },
+ },
+ [`has${capitalize(key)}Class`]: {
+ get() {
+ return this.classes.has(key);
+ },
+ },
+ };
+ }
+
+ function OutletPropertiesBlessing(constructor) {
+ const outlets = readInheritableStaticArrayValues(constructor, "outlets");
+ return outlets.reduce((properties, outletDefinition) => {
+ return Object.assign(properties, propertiesForOutletDefinition(outletDefinition));
+ }, {});
+ }
+ function getOutletController(controller, element, identifier) {
+ return controller.application.getControllerForElementAndIdentifier(element, identifier);
+ }
+ function getControllerAndEnsureConnectedScope(controller, element, outletName) {
+ let outletController = getOutletController(controller, element, outletName);
+ if (outletController)
+ return outletController;
+ controller.application.router.proposeToConnectScopeForElementAndIdentifier(element, outletName);
+ outletController = getOutletController(controller, element, outletName);
+ if (outletController)
+ return outletController;
+ }
+ function propertiesForOutletDefinition(name) {
+ const camelizedName = namespaceCamelize(name);
+ return {
+ [`${camelizedName}Outlet`]: {
+ get() {
+ const outletElement = this.outlets.find(name);
+ const selector = this.outlets.getSelectorForOutletName(name);
+ if (outletElement) {
+ const outletController = getControllerAndEnsureConnectedScope(this, outletElement, name);
+ if (outletController)
+ return outletController;
+ throw new Error(`The provided outlet element is missing an outlet controller "${name}" instance for host controller "${this.identifier}"`);
+ }
+ throw new Error(`Missing outlet element "${name}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${selector}".`);
+ },
+ },
+ [`${camelizedName}Outlets`]: {
+ get() {
+ const outlets = this.outlets.findAll(name);
+ if (outlets.length > 0) {
+ return outlets
+ .map((outletElement) => {
+ const outletController = getControllerAndEnsureConnectedScope(this, outletElement, name);
+ if (outletController)
+ return outletController;
+ console.warn(`The provided outlet element is missing an outlet controller "${name}" instance for host controller "${this.identifier}"`, outletElement);
+ })
+ .filter((controller) => controller);
+ }
+ return [];
+ },
+ },
+ [`${camelizedName}OutletElement`]: {
+ get() {
+ const outletElement = this.outlets.find(name);
+ const selector = this.outlets.getSelectorForOutletName(name);
+ if (outletElement) {
+ return outletElement;
+ }
+ else {
+ throw new Error(`Missing outlet element "${name}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${selector}".`);
+ }
+ },
+ },
+ [`${camelizedName}OutletElements`]: {
+ get() {
+ return this.outlets.findAll(name);
+ },
+ },
+ [`has${capitalize(camelizedName)}Outlet`]: {
+ get() {
+ return this.outlets.has(name);
+ },
+ },
+ };
+ }
+
+ function TargetPropertiesBlessing(constructor) {
+ const targets = readInheritableStaticArrayValues(constructor, "targets");
+ return targets.reduce((properties, targetDefinition) => {
+ return Object.assign(properties, propertiesForTargetDefinition(targetDefinition));
+ }, {});
+ }
+ function propertiesForTargetDefinition(name) {
+ return {
+ [`${name}Target`]: {
+ get() {
+ const target = this.targets.find(name);
+ if (target) {
+ return target;
+ }
+ else {
+ throw new Error(`Missing target element "${name}" for "${this.identifier}" controller`);
+ }
+ },
+ },
+ [`${name}Targets`]: {
+ get() {
+ return this.targets.findAll(name);
+ },
+ },
+ [`has${capitalize(name)}Target`]: {
+ get() {
+ return this.targets.has(name);
+ },
+ },
+ };
+ }
+
+ function ValuePropertiesBlessing(constructor) {
+ const valueDefinitionPairs = readInheritableStaticObjectPairs(constructor, "values");
+ const propertyDescriptorMap = {
+ valueDescriptorMap: {
+ get() {
+ return valueDefinitionPairs.reduce((result, valueDefinitionPair) => {
+ const valueDescriptor = parseValueDefinitionPair(valueDefinitionPair, this.identifier);
+ const attributeName = this.data.getAttributeNameForKey(valueDescriptor.key);
+ return Object.assign(result, { [attributeName]: valueDescriptor });
+ }, {});
+ },
+ },
+ };
+ return valueDefinitionPairs.reduce((properties, valueDefinitionPair) => {
+ return Object.assign(properties, propertiesForValueDefinitionPair(valueDefinitionPair));
+ }, propertyDescriptorMap);
+ }
+ function propertiesForValueDefinitionPair(valueDefinitionPair, controller) {
+ const definition = parseValueDefinitionPair(valueDefinitionPair, controller);
+ const { key, name, reader: read, writer: write } = definition;
+ return {
+ [name]: {
+ get() {
+ const value = this.data.get(key);
+ if (value !== null) {
+ return read(value);
+ }
+ else {
+ return definition.defaultValue;
+ }
+ },
+ set(value) {
+ if (value === undefined) {
+ this.data.delete(key);
+ }
+ else {
+ this.data.set(key, write(value));
+ }
+ },
+ },
+ [`has${capitalize(name)}`]: {
+ get() {
+ return this.data.has(key) || definition.hasCustomDefaultValue;
+ },
+ },
+ };
+ }
+ function parseValueDefinitionPair([token, typeDefinition], controller) {
+ return valueDescriptorForTokenAndTypeDefinition({
+ controller,
+ token,
+ typeDefinition,
+ });
+ }
+ function parseValueTypeConstant(constant) {
+ switch (constant) {
+ case Array:
+ return "array";
+ case Boolean:
+ return "boolean";
+ case Number:
+ return "number";
+ case Object:
+ return "object";
+ case String:
+ return "string";
+ }
+ }
+ function parseValueTypeDefault(defaultValue) {
+ switch (typeof defaultValue) {
+ case "boolean":
+ return "boolean";
+ case "number":
+ return "number";
+ case "string":
+ return "string";
+ }
+ if (Array.isArray(defaultValue))
+ return "array";
+ if (Object.prototype.toString.call(defaultValue) === "[object Object]")
+ return "object";
+ }
+ function parseValueTypeObject(payload) {
+ const { controller, token, typeObject } = payload;
+ const hasType = isSomething(typeObject.type);
+ const hasDefault = isSomething(typeObject.default);
+ const fullObject = hasType && hasDefault;
+ const onlyType = hasType && !hasDefault;
+ const onlyDefault = !hasType && hasDefault;
+ const typeFromObject = parseValueTypeConstant(typeObject.type);
+ const typeFromDefaultValue = parseValueTypeDefault(payload.typeObject.default);
+ if (onlyType)
+ return typeFromObject;
+ if (onlyDefault)
+ return typeFromDefaultValue;
+ if (typeFromObject !== typeFromDefaultValue) {
+ const propertyPath = controller ? `${controller}.${token}` : token;
+ throw new Error(`The specified default value for the Stimulus Value "${propertyPath}" must match the defined type "${typeFromObject}". The provided default value of "${typeObject.default}" is of type "${typeFromDefaultValue}".`);
+ }
+ if (fullObject)
+ return typeFromObject;
+ }
+ function parseValueTypeDefinition(payload) {
+ const { controller, token, typeDefinition } = payload;
+ const typeObject = { controller, token, typeObject: typeDefinition };
+ const typeFromObject = parseValueTypeObject(typeObject);
+ const typeFromDefaultValue = parseValueTypeDefault(typeDefinition);
+ const typeFromConstant = parseValueTypeConstant(typeDefinition);
+ const type = typeFromObject || typeFromDefaultValue || typeFromConstant;
+ if (type)
+ return type;
+ const propertyPath = controller ? `${controller}.${typeDefinition}` : token;
+ throw new Error(`Unknown value type "${propertyPath}" for "${token}" value`);
+ }
+ function defaultValueForDefinition(typeDefinition) {
+ const constant = parseValueTypeConstant(typeDefinition);
+ if (constant)
+ return defaultValuesByType[constant];
+ const hasDefault = hasProperty(typeDefinition, "default");
+ const hasType = hasProperty(typeDefinition, "type");
+ const typeObject = typeDefinition;
+ if (hasDefault)
+ return typeObject.default;
+ if (hasType) {
+ const { type } = typeObject;
+ const constantFromType = parseValueTypeConstant(type);
+ if (constantFromType)
+ return defaultValuesByType[constantFromType];
+ }
+ return typeDefinition;
+ }
+ function valueDescriptorForTokenAndTypeDefinition(payload) {
+ const { token, typeDefinition } = payload;
+ const key = `${dasherize(token)}-value`;
+ const type = parseValueTypeDefinition(payload);
+ return {
+ type,
+ key,
+ name: camelize(key),
+ get defaultValue() {
+ return defaultValueForDefinition(typeDefinition);
+ },
+ get hasCustomDefaultValue() {
+ return parseValueTypeDefault(typeDefinition) !== undefined;
+ },
+ reader: readers[type],
+ writer: writers[type] || writers.default,
+ };
+ }
+ const defaultValuesByType = {
+ get array() {
+ return [];
+ },
+ boolean: false,
+ number: 0,
+ get object() {
+ return {};
+ },
+ string: "",
+ };
+ const readers = {
+ array(value) {
+ const array = JSON.parse(value);
+ if (!Array.isArray(array)) {
+ throw new TypeError(`expected value of type "array" but instead got value "${value}" of type "${parseValueTypeDefault(array)}"`);
+ }
+ return array;
+ },
+ boolean(value) {
+ return !(value == "0" || String(value).toLowerCase() == "false");
+ },
+ number(value) {
+ return Number(value.replace(/_/g, ""));
+ },
+ object(value) {
+ const object = JSON.parse(value);
+ if (object === null || typeof object != "object" || Array.isArray(object)) {
+ throw new TypeError(`expected value of type "object" but instead got value "${value}" of type "${parseValueTypeDefault(object)}"`);
+ }
+ return object;
+ },
+ string(value) {
+ return value;
+ },
+ };
+ const writers = {
+ default: writeString,
+ array: writeJSON,
+ object: writeJSON,
+ };
+ function writeJSON(value) {
+ return JSON.stringify(value);
+ }
+ function writeString(value) {
+ return `${value}`;
+ }
+
+ class Controller {
+ constructor(context) {
+ this.context = context;
+ }
+ static get shouldLoad() {
+ return true;
+ }
+ static afterLoad(_identifier, _application) {
+ return;
+ }
+ get application() {
+ return this.context.application;
+ }
+ get scope() {
+ return this.context.scope;
+ }
+ get element() {
+ return this.scope.element;
+ }
+ get identifier() {
+ return this.scope.identifier;
+ }
+ get targets() {
+ return this.scope.targets;
+ }
+ get outlets() {
+ return this.scope.outlets;
+ }
+ get classes() {
+ return this.scope.classes;
+ }
+ get data() {
+ return this.scope.data;
+ }
+ initialize() {
+ }
+ connect() {
+ }
+ disconnect() {
+ }
+ dispatch(eventName, { target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true, } = {}) {
+ const type = prefix ? `${prefix}:${eventName}` : eventName;
+ const event = new CustomEvent(type, { detail, bubbles, cancelable });
+ target.dispatchEvent(event);
+ return event;
+ }
+ }
+ Controller.blessings = [
+ ClassPropertiesBlessing,
+ TargetPropertiesBlessing,
+ ValuePropertiesBlessing,
+ OutletPropertiesBlessing,
+ ];
+ Controller.targets = [];
+ Controller.outlets = [];
+ Controller.values = {};
+
+ exports.Application = Application;
+ exports.AttributeObserver = AttributeObserver;
+ exports.Context = Context;
+ exports.Controller = Controller;
+ exports.ElementObserver = ElementObserver;
+ exports.IndexedMultimap = IndexedMultimap;
+ exports.Multimap = Multimap;
+ exports.SelectorObserver = SelectorObserver;
+ exports.StringMapObserver = StringMapObserver;
+ exports.TokenListObserver = TokenListObserver;
+ exports.ValueListObserver = ValueListObserver;
+ exports.add = add;
+ exports.defaultSchema = defaultSchema;
+ exports.del = del;
+ exports.fetch = fetch;
+ exports.prune = prune;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
diff --git a/vendor/assets/javascripts/tom-select/tom-select.complete.js b/vendor/assets/javascripts/tom-select/tom-select.complete.js
new file mode 100644
index 00000000..a1dc48f0
--- /dev/null
+++ b/vendor/assets/javascripts/tom-select/tom-select.complete.js
@@ -0,0 +1,5021 @@
+/**
+* Tom Select v2.4.2
+* Licensed under the Apache License, Version 2.0 (the "License");
+*/
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.TomSelect = factory());
+})(this, (function () { 'use strict';
+
+ /**
+ * MicroEvent - to make any js object an event emitter
+ *
+ * - pure javascript - server compatible, browser compatible
+ * - dont rely on the browser doms
+ * - super simple - you get it immediatly, no mistery, no magic involved
+ *
+ * @author Jerome Etienne (https://github.com/jeromeetienne)
+ */
+
+ /**
+ * Execute callback for each event in space separated list of event names
+ *
+ */
+ function forEvents(events, callback) {
+ events.split(/\s+/).forEach(event => {
+ callback(event);
+ });
+ }
+ class MicroEvent {
+ constructor() {
+ this._events = {};
+ }
+ on(events, fct) {
+ forEvents(events, event => {
+ const event_array = this._events[event] || [];
+ event_array.push(fct);
+ this._events[event] = event_array;
+ });
+ }
+ off(events, fct) {
+ var n = arguments.length;
+ if (n === 0) {
+ this._events = {};
+ return;
+ }
+ forEvents(events, event => {
+ if (n === 1) {
+ delete this._events[event];
+ return;
+ }
+ const event_array = this._events[event];
+ if (event_array === undefined) return;
+ event_array.splice(event_array.indexOf(fct), 1);
+ this._events[event] = event_array;
+ });
+ }
+ trigger(events, ...args) {
+ var self = this;
+ forEvents(events, event => {
+ const event_array = self._events[event];
+ if (event_array === undefined) return;
+ event_array.forEach(fct => {
+ fct.apply(self, args);
+ });
+ });
+ }
+ }
+
+ /**
+ * microplugin.js
+ * Copyright (c) 2013 Brian Reavis & contributors
+ *
+ * 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.
+ *
+ * @author Brian Reavis
+ */
+
+ function MicroPlugin(Interface) {
+ Interface.plugins = {};
+ return class extends Interface {
+ constructor(...args) {
+ super(...args);
+ this.plugins = {
+ names: [],
+ settings: {},
+ requested: {},
+ loaded: {}
+ };
+ }
+ /**
+ * Registers a plugin.
+ *
+ * @param {function} fn
+ */
+ static define(name, fn) {
+ Interface.plugins[name] = {
+ 'name': name,
+ 'fn': fn
+ };
+ }
+
+ /**
+ * Initializes the listed plugins (with options).
+ * Acceptable formats:
+ *
+ * List (without options):
+ * ['a', 'b', 'c']
+ *
+ * List (with options):
+ * [{'name': 'a', options: {}}, {'name': 'b', options: {}}]
+ *
+ * Hash (with options):
+ * {'a': { ... }, 'b': { ... }, 'c': { ... }}
+ *
+ * @param {array|object} plugins
+ */
+ initializePlugins(plugins) {
+ var key, name;
+ const self = this;
+ const queue = [];
+ if (Array.isArray(plugins)) {
+ plugins.forEach(plugin => {
+ if (typeof plugin === 'string') {
+ queue.push(plugin);
+ } else {
+ self.plugins.settings[plugin.name] = plugin.options;
+ queue.push(plugin.name);
+ }
+ });
+ } else if (plugins) {
+ for (key in plugins) {
+ if (plugins.hasOwnProperty(key)) {
+ self.plugins.settings[key] = plugins[key];
+ queue.push(key);
+ }
+ }
+ }
+ while (name = queue.shift()) {
+ self.require(name);
+ }
+ }
+ loadPlugin(name) {
+ var self = this;
+ var plugins = self.plugins;
+ var plugin = Interface.plugins[name];
+ if (!Interface.plugins.hasOwnProperty(name)) {
+ throw new Error('Unable to find "' + name + '" plugin');
+ }
+ plugins.requested[name] = true;
+ plugins.loaded[name] = plugin.fn.apply(self, [self.plugins.settings[name] || {}]);
+ plugins.names.push(name);
+ }
+
+ /**
+ * Initializes a plugin.
+ *
+ */
+ require(name) {
+ var self = this;
+ var plugins = self.plugins;
+ if (!self.plugins.loaded.hasOwnProperty(name)) {
+ if (plugins.requested[name]) {
+ throw new Error('Plugin has circular dependency ("' + name + '")');
+ }
+ self.loadPlugin(name);
+ }
+ return plugins.loaded[name];
+ }
+ };
+ }
+
+ /**
+ * Convert array of strings to a regular expression
+ * ex ['ab','a'] => (?:ab|a)
+ * ex ['a','b'] => [ab]
+ */
+ const arrayToPattern = (chars) => {
+ chars = chars.filter(Boolean);
+ if (chars.length < 2) {
+ return chars[0] || '';
+ }
+ return (maxValueLength(chars) == 1) ? '[' + chars.join('') + ']' : '(?:' + chars.join('|') + ')';
+ };
+ const sequencePattern = (array) => {
+ if (!hasDuplicates(array)) {
+ return array.join('');
+ }
+ let pattern = '';
+ let prev_char_count = 0;
+ const prev_pattern = () => {
+ if (prev_char_count > 1) {
+ pattern += '{' + prev_char_count + '}';
+ }
+ };
+ array.forEach((char, i) => {
+ if (char === array[i - 1]) {
+ prev_char_count++;
+ return;
+ }
+ prev_pattern();
+ pattern += char;
+ prev_char_count = 1;
+ });
+ prev_pattern();
+ return pattern;
+ };
+ /**
+ * Convert array of strings to a regular expression
+ * ex ['ab','a'] => (?:ab|a)
+ * ex ['a','b'] => [ab]
+ */
+ const setToPattern = (chars) => {
+ let array = Array.from(chars);
+ return arrayToPattern(array);
+ };
+ /**
+ * https://stackoverflow.com/questions/7376598/in-javascript-how-do-i-check-if-an-array-has-duplicate-values
+ */
+ const hasDuplicates = (array) => {
+ return (new Set(array)).size !== array.length;
+ };
+ /**
+ * https://stackoverflow.com/questions/63006601/why-does-u-throw-an-invalid-escape-error
+ */
+ const escape_regex = (str) => {
+ return (str + '').replace(/([\$\(\)\*\+\.\?\[\]\^\{\|\}\\])/gu, '\\$1');
+ };
+ /**
+ * Return the max length of array values
+ */
+ const maxValueLength = (array) => {
+ return array.reduce((longest, value) => Math.max(longest, unicodeLength(value)), 0);
+ };
+ const unicodeLength = (str) => {
+ return Array.from(str).length;
+ };
+
+ /**
+ * Get all possible combinations of substrings that add up to the given string
+ * https://stackoverflow.com/questions/30169587/find-all-the-combination-of-substrings-that-add-up-to-the-given-string
+ */
+ const allSubstrings = (input) => {
+ if (input.length === 1)
+ return [[input]];
+ let result = [];
+ const start = input.substring(1);
+ const suba = allSubstrings(start);
+ suba.forEach(function (subresult) {
+ let tmp = subresult.slice(0);
+ tmp[0] = input.charAt(0) + tmp[0];
+ result.push(tmp);
+ tmp = subresult.slice(0);
+ tmp.unshift(input.charAt(0));
+ result.push(tmp);
+ });
+ return result;
+ };
+
+ const code_points = [[0, 65535]];
+ const accent_pat = '[\u0300-\u036F\u{b7}\u{2be}\u{2bc}]';
+ let unicode_map;
+ let multi_char_reg;
+ const max_char_length = 3;
+ const latin_convert = {};
+ const latin_condensed = {
+ '/': '⁄∕',
+ '0': '߀',
+ "a": "ⱥɐɑ",
+ "aa": "ꜳ",
+ "ae": "æǽǣ",
+ "ao": "ꜵ",
+ "au": "ꜷ",
+ "av": "ꜹꜻ",
+ "ay": "ꜽ",
+ "b": "ƀɓƃ",
+ "c": "ꜿƈȼↄ",
+ "d": "đɗɖᴅƌꮷԁɦ",
+ "e": "ɛǝᴇɇ",
+ "f": "ꝼƒ",
+ "g": "ǥɠꞡᵹꝿɢ",
+ "h": "ħⱨⱶɥ",
+ "i": "ɨı",
+ "j": "ɉȷ",
+ "k": "ƙⱪꝁꝃꝅꞣ",
+ "l": "łƚɫⱡꝉꝇꞁɭ",
+ "m": "ɱɯϻ",
+ "n": "ꞥƞɲꞑᴎлԉ",
+ "o": "øǿɔɵꝋꝍᴑ",
+ "oe": "œ",
+ "oi": "ƣ",
+ "oo": "ꝏ",
+ "ou": "ȣ",
+ "p": "ƥᵽꝑꝓꝕρ",
+ "q": "ꝗꝙɋ",
+ "r": "ɍɽꝛꞧꞃ",
+ "s": "ßȿꞩꞅʂ",
+ "t": "ŧƭʈⱦꞇ",
+ "th": "þ",
+ "tz": "ꜩ",
+ "u": "ʉ",
+ "v": "ʋꝟʌ",
+ "vy": "ꝡ",
+ "w": "ⱳ",
+ "y": "ƴɏỿ",
+ "z": "ƶȥɀⱬꝣ",
+ "hv": "ƕ"
+ };
+ for (let latin in latin_condensed) {
+ let unicode = latin_condensed[latin] || '';
+ for (let i = 0; i < unicode.length; i++) {
+ let char = unicode.substring(i, i + 1);
+ latin_convert[char] = latin;
+ }
+ }
+ const convert_pat = new RegExp(Object.keys(latin_convert).join('|') + '|' + accent_pat, 'gu');
+ /**
+ * Initialize the unicode_map from the give code point ranges
+ */
+ const initialize = (_code_points) => {
+ if (unicode_map !== undefined)
+ return;
+ unicode_map = generateMap(code_points);
+ };
+ /**
+ * Helper method for normalize a string
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
+ */
+ const normalize = (str, form = 'NFKD') => str.normalize(form);
+ /**
+ * Remove accents without reordering string
+ * calling str.normalize('NFKD') on \u{594}\u{595}\u{596} becomes \u{596}\u{594}\u{595}
+ * via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703
+ */
+ const asciifold = (str) => {
+ return Array.from(str).reduce(
+ /**
+ * @param {string} result
+ * @param {string} char
+ */
+ (result, char) => {
+ return result + _asciifold(char);
+ }, '');
+ };
+ const _asciifold = (str) => {
+ str = normalize(str)
+ .toLowerCase()
+ .replace(convert_pat, (/** @type {string} */ char) => {
+ return latin_convert[char] || '';
+ });
+ //return str;
+ return normalize(str, 'NFC');
+ };
+ /**
+ * Generate a list of unicode variants from the list of code points
+ */
+ function* generator(code_points) {
+ for (const [code_point_min, code_point_max] of code_points) {
+ for (let i = code_point_min; i <= code_point_max; i++) {
+ let composed = String.fromCharCode(i);
+ let folded = asciifold(composed);
+ if (folded == composed.toLowerCase()) {
+ continue;
+ }
+ // skip when folded is a string longer than 3 characters long
+ // bc the resulting regex patterns will be long
+ // eg:
+ // folded صلى الله عليه وسلم length 18 code point 65018
+ // folded جل جلاله length 8 code point 65019
+ if (folded.length > max_char_length) {
+ continue;
+ }
+ if (folded.length == 0) {
+ continue;
+ }
+ yield { folded: folded, composed: composed, code_point: i };
+ }
+ }
+ }
+ /**
+ * Generate a unicode map from the list of code points
+ */
+ const generateSets = (code_points) => {
+ const unicode_sets = {};
+ const addMatching = (folded, to_add) => {
+ /** @type {Set} */
+ const folded_set = unicode_sets[folded] || new Set();
+ const patt = new RegExp('^' + setToPattern(folded_set) + '$', 'iu');
+ if (to_add.match(patt)) {
+ return;
+ }
+ folded_set.add(escape_regex(to_add));
+ unicode_sets[folded] = folded_set;
+ };
+ for (let value of generator(code_points)) {
+ addMatching(value.folded, value.folded);
+ addMatching(value.folded, value.composed);
+ }
+ return unicode_sets;
+ };
+ /**
+ * Generate a unicode map from the list of code points
+ * ae => (?:(?:ae|Æ|Ǽ|Ǣ)|(?:A|Ⓐ|A...)(?:E|ɛ|Ⓔ...))
+ */
+ const generateMap = (code_points) => {
+ const unicode_sets = generateSets(code_points);
+ const unicode_map = {};
+ let multi_char = [];
+ for (let folded in unicode_sets) {
+ let set = unicode_sets[folded];
+ if (set) {
+ unicode_map[folded] = setToPattern(set);
+ }
+ if (folded.length > 1) {
+ multi_char.push(escape_regex(folded));
+ }
+ }
+ multi_char.sort((a, b) => b.length - a.length);
+ const multi_char_patt = arrayToPattern(multi_char);
+ multi_char_reg = new RegExp('^' + multi_char_patt, 'u');
+ return unicode_map;
+ };
+ /**
+ * Map each element of an array from its folded value to all possible unicode matches
+ */
+ const mapSequence = (strings, min_replacement = 1) => {
+ let chars_replaced = 0;
+ strings = strings.map((str) => {
+ if (unicode_map[str]) {
+ chars_replaced += str.length;
+ }
+ return unicode_map[str] || str;
+ });
+ if (chars_replaced >= min_replacement) {
+ return sequencePattern(strings);
+ }
+ return '';
+ };
+ /**
+ * Convert a short string and split it into all possible patterns
+ * Keep a pattern only if min_replacement is met
+ *
+ * 'abc'
+ * => [['abc'],['ab','c'],['a','bc'],['a','b','c']]
+ * => ['abc-pattern','ab-c-pattern'...]
+ */
+ const substringsToPattern = (str, min_replacement = 1) => {
+ min_replacement = Math.max(min_replacement, str.length - 1);
+ return arrayToPattern(allSubstrings(str).map((sub_pat) => {
+ return mapSequence(sub_pat, min_replacement);
+ }));
+ };
+ /**
+ * Convert an array of sequences into a pattern
+ * [{start:0,end:3,length:3,substr:'iii'}...] => (?:iii...)
+ */
+ const sequencesToPattern = (sequences, all = true) => {
+ let min_replacement = sequences.length > 1 ? 1 : 0;
+ return arrayToPattern(sequences.map((sequence) => {
+ let seq = [];
+ const len = all ? sequence.length() : sequence.length() - 1;
+ for (let j = 0; j < len; j++) {
+ seq.push(substringsToPattern(sequence.substrs[j] || '', min_replacement));
+ }
+ return sequencePattern(seq);
+ }));
+ };
+ /**
+ * Return true if the sequence is already in the sequences
+ */
+ const inSequences = (needle_seq, sequences) => {
+ for (const seq of sequences) {
+ if (seq.start != needle_seq.start || seq.end != needle_seq.end) {
+ continue;
+ }
+ if (seq.substrs.join('') !== needle_seq.substrs.join('')) {
+ continue;
+ }
+ let needle_parts = needle_seq.parts;
+ const filter = (part) => {
+ for (const needle_part of needle_parts) {
+ if (needle_part.start === part.start && needle_part.substr === part.substr) {
+ return false;
+ }
+ if (part.length == 1 || needle_part.length == 1) {
+ continue;
+ }
+ // check for overlapping parts
+ // a = ['::=','==']
+ // b = ['::','===']
+ // a = ['r','sm']
+ // b = ['rs','m']
+ if (part.start < needle_part.start && part.end > needle_part.start) {
+ return true;
+ }
+ if (needle_part.start < part.start && needle_part.end > part.start) {
+ return true;
+ }
+ }
+ return false;
+ };
+ let filtered = seq.parts.filter(filter);
+ if (filtered.length > 0) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ };
+ class Sequence {
+ parts;
+ substrs;
+ start;
+ end;
+ constructor() {
+ this.parts = [];
+ this.substrs = [];
+ this.start = 0;
+ this.end = 0;
+ }
+ add(part) {
+ if (part) {
+ this.parts.push(part);
+ this.substrs.push(part.substr);
+ this.start = Math.min(part.start, this.start);
+ this.end = Math.max(part.end, this.end);
+ }
+ }
+ last() {
+ return this.parts[this.parts.length - 1];
+ }
+ length() {
+ return this.parts.length;
+ }
+ clone(position, last_piece) {
+ let clone = new Sequence();
+ let parts = JSON.parse(JSON.stringify(this.parts));
+ let last_part = parts.pop();
+ for (const part of parts) {
+ clone.add(part);
+ }
+ let last_substr = last_piece.substr.substring(0, position - last_part.start);
+ let clone_last_len = last_substr.length;
+ clone.add({ start: last_part.start, end: last_part.start + clone_last_len, length: clone_last_len, substr: last_substr });
+ return clone;
+ }
+ }
+ /**
+ * Expand a regular expression pattern to include unicode variants
+ * eg /a/ becomes /aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐɑAⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ/
+ *
+ * Issue:
+ * ﺊﺋ [ 'ﺊ = \\u{fe8a}', 'ﺋ = \\u{fe8b}' ]
+ * becomes: ئئ [ 'ي = \\u{64a}', 'ٔ = \\u{654}', 'ي = \\u{64a}', 'ٔ = \\u{654}' ]
+ *
+ * İIJ = IIJ = ⅡJ
+ *
+ * 1/2/4
+ */
+ const getPattern = (str) => {
+ initialize();
+ str = asciifold(str);
+ let pattern = '';
+ let sequences = [new Sequence()];
+ for (let i = 0; i < str.length; i++) {
+ let substr = str.substring(i);
+ let match = substr.match(multi_char_reg);
+ const char = str.substring(i, i + 1);
+ const match_str = match ? match[0] : null;
+ // loop through sequences
+ // add either the char or multi_match
+ let overlapping = [];
+ let added_types = new Set();
+ for (const sequence of sequences) {
+ const last_piece = sequence.last();
+ if (!last_piece || last_piece.length == 1 || last_piece.end <= i) {
+ // if we have a multi match
+ if (match_str) {
+ const len = match_str.length;
+ sequence.add({ start: i, end: i + len, length: len, substr: match_str });
+ added_types.add('1');
+ }
+ else {
+ sequence.add({ start: i, end: i + 1, length: 1, substr: char });
+ added_types.add('2');
+ }
+ }
+ else if (match_str) {
+ let clone = sequence.clone(i, last_piece);
+ const len = match_str.length;
+ clone.add({ start: i, end: i + len, length: len, substr: match_str });
+ overlapping.push(clone);
+ }
+ else {
+ // don't add char
+ // adding would create invalid patterns: 234 => [2,34,4]
+ added_types.add('3');
+ }
+ }
+ // if we have overlapping
+ if (overlapping.length > 0) {
+ // ['ii','iii'] before ['i','i','iii']
+ overlapping = overlapping.sort((a, b) => {
+ return a.length() - b.length();
+ });
+ for (let clone of overlapping) {
+ // don't add if we already have an equivalent sequence
+ if (inSequences(clone, sequences)) {
+ continue;
+ }
+ sequences.push(clone);
+ }
+ continue;
+ }
+ // if we haven't done anything unique
+ // clean up the patterns
+ // helps keep patterns smaller
+ // if str = 'r₨㎧aarss', pattern will be 446 instead of 655
+ if (i > 0 && added_types.size == 1 && !added_types.has('3')) {
+ pattern += sequencesToPattern(sequences, false);
+ let new_seq = new Sequence();
+ const old_seq = sequences[0];
+ if (old_seq) {
+ new_seq.add(old_seq.last());
+ }
+ sequences = [new_seq];
+ }
+ }
+ pattern += sequencesToPattern(sequences, true);
+ return pattern;
+ };
+
+ /**
+ * A property getter resolving dot-notation
+ * @param {Object} obj The root object to fetch property on
+ * @param {String} name The optionally dotted property name to fetch
+ * @return {Object} The resolved property value
+ */
+ const getAttr = (obj, name) => {
+ if (!obj)
+ return;
+ return obj[name];
+ };
+ /**
+ * A property getter resolving dot-notation
+ * @param {Object} obj The root object to fetch property on
+ * @param {String} name The optionally dotted property name to fetch
+ * @return {Object} The resolved property value
+ */
+ const getAttrNesting = (obj, name) => {
+ if (!obj)
+ return;
+ var part, names = name.split(".");
+ while ((part = names.shift()) && (obj = obj[part]))
+ ;
+ return obj;
+ };
+ /**
+ * Calculates how close of a match the
+ * given value is against a search token.
+ *
+ */
+ const scoreValue = (value, token, weight) => {
+ var score, pos;
+ if (!value)
+ return 0;
+ value = value + '';
+ if (token.regex == null)
+ return 0;
+ pos = value.search(token.regex);
+ if (pos === -1)
+ return 0;
+ score = token.string.length / value.length;
+ if (pos === 0)
+ score += 0.5;
+ return score * weight;
+ };
+ /**
+ * Cast object property to an array if it exists and has a value
+ *
+ */
+ const propToArray = (obj, key) => {
+ var value = obj[key];
+ if (typeof value == 'function')
+ return value;
+ if (value && !Array.isArray(value)) {
+ obj[key] = [value];
+ }
+ };
+ /**
+ * Iterates over arrays and hashes.
+ *
+ * ```
+ * iterate(this.items, function(item, id) {
+ * // invoked for each item
+ * });
+ * ```
+ *
+ */
+ const iterate$1 = (object, callback) => {
+ if (Array.isArray(object)) {
+ object.forEach(callback);
+ }
+ else {
+ for (var key in object) {
+ if (object.hasOwnProperty(key)) {
+ callback(object[key], key);
+ }
+ }
+ }
+ };
+ const cmp = (a, b) => {
+ if (typeof a === 'number' && typeof b === 'number') {
+ return a > b ? 1 : (a < b ? -1 : 0);
+ }
+ a = asciifold(a + '').toLowerCase();
+ b = asciifold(b + '').toLowerCase();
+ if (a > b)
+ return 1;
+ if (b > a)
+ return -1;
+ return 0;
+ };
+
+ /**
+ * sifter.js
+ * Copyright (c) 2013–2020 Brian Reavis & contributors
+ *
+ * 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.
+ *
+ * @author Brian Reavis
+ */
+ class Sifter {
+ items; // []|{};
+ settings;
+ /**
+ * Textually searches arrays and hashes of objects
+ * by property (or multiple properties). Designed
+ * specifically for autocomplete.
+ *
+ */
+ constructor(items, settings) {
+ this.items = items;
+ this.settings = settings || { diacritics: true };
+ }
+ ;
+ /**
+ * Splits a search string into an array of individual
+ * regexps to be used to match results.
+ *
+ */
+ tokenize(query, respect_word_boundaries, weights) {
+ if (!query || !query.length)
+ return [];
+ const tokens = [];
+ const words = query.split(/\s+/);
+ var field_regex;
+ if (weights) {
+ field_regex = new RegExp('^(' + Object.keys(weights).map(escape_regex).join('|') + ')\:(.*)$');
+ }
+ words.forEach((word) => {
+ let field_match;
+ let field = null;
+ let regex = null;
+ // look for "field:query" tokens
+ if (field_regex && (field_match = word.match(field_regex))) {
+ field = field_match[1];
+ word = field_match[2];
+ }
+ if (word.length > 0) {
+ if (this.settings.diacritics) {
+ regex = getPattern(word) || null;
+ }
+ else {
+ regex = escape_regex(word);
+ }
+ if (regex && respect_word_boundaries)
+ regex = "\\b" + regex;
+ }
+ tokens.push({
+ string: word,
+ regex: regex ? new RegExp(regex, 'iu') : null,
+ field: field,
+ });
+ });
+ return tokens;
+ }
+ ;
+ /**
+ * Returns a function to be used to score individual results.
+ *
+ * Good matches will have a higher score than poor matches.
+ * If an item is not a match, 0 will be returned by the function.
+ *
+ * @returns {T.ScoreFn}
+ */
+ getScoreFunction(query, options) {
+ var search = this.prepareSearch(query, options);
+ return this._getScoreFunction(search);
+ }
+ /**
+ * @returns {T.ScoreFn}
+ *
+ */
+ _getScoreFunction(search) {
+ const tokens = search.tokens, token_count = tokens.length;
+ if (!token_count) {
+ return function () { return 0; };
+ }
+ const fields = search.options.fields, weights = search.weights, field_count = fields.length, getAttrFn = search.getAttrFn;
+ if (!field_count) {
+ return function () { return 1; };
+ }
+ /**
+ * Calculates the score of an object
+ * against the search query.
+ *
+ */
+ const scoreObject = (function () {
+ if (field_count === 1) {
+ return function (token, data) {
+ const field = fields[0].field;
+ return scoreValue(getAttrFn(data, field), token, weights[field] || 1);
+ };
+ }
+ return function (token, data) {
+ var sum = 0;
+ // is the token specific to a field?
+ if (token.field) {
+ const value = getAttrFn(data, token.field);
+ if (!token.regex && value) {
+ sum += (1 / field_count);
+ }
+ else {
+ sum += scoreValue(value, token, 1);
+ }
+ }
+ else {
+ iterate$1(weights, (weight, field) => {
+ sum += scoreValue(getAttrFn(data, field), token, weight);
+ });
+ }
+ return sum / field_count;
+ };
+ })();
+ if (token_count === 1) {
+ return function (data) {
+ return scoreObject(tokens[0], data);
+ };
+ }
+ if (search.options.conjunction === 'and') {
+ return function (data) {
+ var score, sum = 0;
+ for (let token of tokens) {
+ score = scoreObject(token, data);
+ if (score <= 0)
+ return 0;
+ sum += score;
+ }
+ return sum / token_count;
+ };
+ }
+ else {
+ return function (data) {
+ var sum = 0;
+ iterate$1(tokens, (token) => {
+ sum += scoreObject(token, data);
+ });
+ return sum / token_count;
+ };
+ }
+ }
+ ;
+ /**
+ * Returns a function that can be used to compare two
+ * results, for sorting purposes. If no sorting should
+ * be performed, `null` will be returned.
+ *
+ * @return function(a,b)
+ */
+ getSortFunction(query, options) {
+ var search = this.prepareSearch(query, options);
+ return this._getSortFunction(search);
+ }
+ _getSortFunction(search) {
+ var implicit_score, sort_flds = [];
+ const self = this, options = search.options, sort = (!search.query && options.sort_empty) ? options.sort_empty : options.sort;
+ if (typeof sort == 'function') {
+ return sort.bind(this);
+ }
+ /**
+ * Fetches the specified sort field value
+ * from a search result item.
+ *
+ */
+ const get_field = function (name, result) {
+ if (name === '$score')
+ return result.score;
+ return search.getAttrFn(self.items[result.id], name);
+ };
+ // parse options
+ if (sort) {
+ for (let s of sort) {
+ if (search.query || s.field !== '$score') {
+ sort_flds.push(s);
+ }
+ }
+ }
+ // the "$score" field is implied to be the primary
+ // sort field, unless it's manually specified
+ if (search.query) {
+ implicit_score = true;
+ for (let fld of sort_flds) {
+ if (fld.field === '$score') {
+ implicit_score = false;
+ break;
+ }
+ }
+ if (implicit_score) {
+ sort_flds.unshift({ field: '$score', direction: 'desc' });
+ }
+ // without a search.query, all items will have the same score
+ }
+ else {
+ sort_flds = sort_flds.filter((fld) => fld.field !== '$score');
+ }
+ // build function
+ const sort_flds_count = sort_flds.length;
+ if (!sort_flds_count) {
+ return null;
+ }
+ return function (a, b) {
+ var result, field;
+ for (let sort_fld of sort_flds) {
+ field = sort_fld.field;
+ let multiplier = sort_fld.direction === 'desc' ? -1 : 1;
+ result = multiplier * cmp(get_field(field, a), get_field(field, b));
+ if (result)
+ return result;
+ }
+ return 0;
+ };
+ }
+ ;
+ /**
+ * Parses a search query and returns an object
+ * with tokens and fields ready to be populated
+ * with results.
+ *
+ */
+ prepareSearch(query, optsUser) {
+ const weights = {};
+ var options = Object.assign({}, optsUser);
+ propToArray(options, 'sort');
+ propToArray(options, 'sort_empty');
+ // convert fields to new format
+ if (options.fields) {
+ propToArray(options, 'fields');
+ const fields = [];
+ options.fields.forEach((field) => {
+ if (typeof field == 'string') {
+ field = { field: field, weight: 1 };
+ }
+ fields.push(field);
+ weights[field.field] = ('weight' in field) ? field.weight : 1;
+ });
+ options.fields = fields;
+ }
+ return {
+ options: options,
+ query: query.toLowerCase().trim(),
+ tokens: this.tokenize(query, options.respect_word_boundaries, weights),
+ total: 0,
+ items: [],
+ weights: weights,
+ getAttrFn: (options.nesting) ? getAttrNesting : getAttr,
+ };
+ }
+ ;
+ /**
+ * Searches through all items and returns a sorted array of matches.
+ *
+ */
+ search(query, options) {
+ var self = this, score, search;
+ search = this.prepareSearch(query, options);
+ options = search.options;
+ query = search.query;
+ // generate result scoring function
+ const fn_score = options.score || self._getScoreFunction(search);
+ // perform search and sort
+ if (query.length) {
+ iterate$1(self.items, (item, id) => {
+ score = fn_score(item);
+ if (options.filter === false || score > 0) {
+ search.items.push({ 'score': score, 'id': id });
+ }
+ });
+ }
+ else {
+ iterate$1(self.items, (_, id) => {
+ search.items.push({ 'score': 1, 'id': id });
+ });
+ }
+ const fn_sort = self._getSortFunction(search);
+ if (fn_sort)
+ search.items.sort(fn_sort);
+ // apply limits
+ search.total = search.items.length;
+ if (typeof options.limit === 'number') {
+ search.items = search.items.slice(0, options.limit);
+ }
+ return search;
+ }
+ ;
+ }
+
+ /**
+ * Converts a scalar to its best string representation
+ * for hash keys and HTML attribute values.
+ *
+ * Transformations:
+ * 'str' -> 'str'
+ * null -> ''
+ * undefined -> ''
+ * true -> '1'
+ * false -> '0'
+ * 0 -> '0'
+ * 1 -> '1'
+ *
+ */
+ const hash_key = value => {
+ if (typeof value === 'undefined' || value === null) return null;
+ return get_hash(value);
+ };
+ const get_hash = value => {
+ if (typeof value === 'boolean') return value ? '1' : '0';
+ return value + '';
+ };
+
+ /**
+ * Escapes a string for use within HTML.
+ *
+ */
+ const escape_html = str => {
+ return (str + '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
+ };
+
+ /**
+ * use setTimeout if timeout > 0
+ */
+ const timeout = (fn, timeout) => {
+ if (timeout > 0) {
+ return window.setTimeout(fn, timeout);
+ }
+ fn.call(null);
+ return null;
+ };
+
+ /**
+ * Debounce the user provided load function
+ *
+ */
+ const loadDebounce = (fn, delay) => {
+ var timeout;
+ return function (value, callback) {
+ var self = this;
+ if (timeout) {
+ self.loading = Math.max(self.loading - 1, 0);
+ clearTimeout(timeout);
+ }
+ timeout = setTimeout(function () {
+ timeout = null;
+ self.loadedSearches[value] = true;
+ fn.call(self, value, callback);
+ }, delay);
+ };
+ };
+
+ /**
+ * Debounce all fired events types listed in `types`
+ * while executing the provided `fn`.
+ *
+ */
+ const debounce_events = (self, types, fn) => {
+ var type;
+ var trigger = self.trigger;
+ var event_args = {};
+
+ // override trigger method
+ self.trigger = function () {
+ var type = arguments[0];
+ if (types.indexOf(type) !== -1) {
+ event_args[type] = arguments;
+ } else {
+ return trigger.apply(self, arguments);
+ }
+ };
+
+ // invoke provided function
+ fn.apply(self, []);
+ self.trigger = trigger;
+
+ // trigger queued events
+ for (type of types) {
+ if (type in event_args) {
+ trigger.apply(self, event_args[type]);
+ }
+ }
+ };
+
+ /**
+ * Determines the current selection within a text input control.
+ * Returns an object containing:
+ * - start
+ * - length
+ *
+ * Note: "selectionStart, selectionEnd ... apply only to inputs of types text, search, URL, tel and password"
+ * - https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange
+ */
+ const getSelection = input => {
+ return {
+ start: input.selectionStart || 0,
+ length: (input.selectionEnd || 0) - (input.selectionStart || 0)
+ };
+ };
+
+ /**
+ * Prevent default
+ *
+ */
+ const preventDefault = (evt, stop = false) => {
+ if (evt) {
+ evt.preventDefault();
+ if (stop) {
+ evt.stopPropagation();
+ }
+ }
+ };
+
+ /**
+ * Add event helper
+ *
+ */
+ const addEvent = (target, type, callback, options) => {
+ target.addEventListener(type, callback, options);
+ };
+
+ /**
+ * Return true if the requested key is down
+ * Will return false if more than one control character is pressed ( when [ctrl+shift+a] != [ctrl+a] )
+ * The current evt may not always set ( eg calling advanceSelection() )
+ *
+ */
+ const isKeyDown = (key_name, evt) => {
+ if (!evt) {
+ return false;
+ }
+ if (!evt[key_name]) {
+ return false;
+ }
+ var count = (evt.altKey ? 1 : 0) + (evt.ctrlKey ? 1 : 0) + (evt.shiftKey ? 1 : 0) + (evt.metaKey ? 1 : 0);
+ if (count === 1) {
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * Get the id of an element
+ * If the id attribute is not set, set the attribute with the given id
+ *
+ */
+ const getId = (el, id) => {
+ const existing_id = el.getAttribute('id');
+ if (existing_id) {
+ return existing_id;
+ }
+ el.setAttribute('id', id);
+ return id;
+ };
+
+ /**
+ * Returns a string with backslashes added before characters that need to be escaped.
+ */
+ const addSlashes = str => {
+ return str.replace(/[\\"']/g, '\\$&');
+ };
+
+ /**
+ *
+ */
+ const append = (parent, node) => {
+ if (node) parent.append(node);
+ };
+
+ /**
+ * Iterates over arrays and hashes.
+ *
+ * ```
+ * iterate(this.items, function(item, id) {
+ * // invoked for each item
+ * });
+ * ```
+ *
+ */
+ const iterate = (object, callback) => {
+ if (Array.isArray(object)) {
+ object.forEach(callback);
+ } else {
+ for (var key in object) {
+ if (object.hasOwnProperty(key)) {
+ callback(object[key], key);
+ }
+ }
+ }
+ };
+
+ /**
+ * Return a dom element from either a dom query string, jQuery object, a dom element or html string
+ * https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518
+ *
+ * param query should be {}
+ */
+ const getDom = query => {
+ if (query.jquery) {
+ return query[0];
+ }
+ if (query instanceof HTMLElement) {
+ return query;
+ }
+ if (isHtmlString(query)) {
+ var tpl = document.createElement('template');
+ tpl.innerHTML = query.trim(); // Never return a text node of whitespace as the result
+ return tpl.content.firstChild;
+ }
+ return document.querySelector(query);
+ };
+ const isHtmlString = arg => {
+ if (typeof arg === 'string' && arg.indexOf('<') > -1) {
+ return true;
+ }
+ return false;
+ };
+ const escapeQuery = query => {
+ return query.replace(/['"\\]/g, '\\$&');
+ };
+
+ /**
+ * Dispatch an event
+ *
+ */
+ const triggerEvent = (dom_el, event_name) => {
+ var event = document.createEvent('HTMLEvents');
+ event.initEvent(event_name, true, false);
+ dom_el.dispatchEvent(event);
+ };
+
+ /**
+ * Apply CSS rules to a dom element
+ *
+ */
+ const applyCSS = (dom_el, css) => {
+ Object.assign(dom_el.style, css);
+ };
+
+ /**
+ * Add css classes
+ *
+ */
+ const addClasses = (elmts, ...classes) => {
+ var norm_classes = classesArray(classes);
+ elmts = castAsArray(elmts);
+ elmts.map(el => {
+ norm_classes.map(cls => {
+ el.classList.add(cls);
+ });
+ });
+ };
+
+ /**
+ * Remove css classes
+ *
+ */
+ const removeClasses = (elmts, ...classes) => {
+ var norm_classes = classesArray(classes);
+ elmts = castAsArray(elmts);
+ elmts.map(el => {
+ norm_classes.map(cls => {
+ el.classList.remove(cls);
+ });
+ });
+ };
+
+ /**
+ * Return arguments
+ *
+ */
+ const classesArray = args => {
+ var classes = [];
+ iterate(args, _classes => {
+ if (typeof _classes === 'string') {
+ _classes = _classes.trim().split(/[\t\n\f\r\s]/);
+ }
+ if (Array.isArray(_classes)) {
+ classes = classes.concat(_classes);
+ }
+ });
+ return classes.filter(Boolean);
+ };
+
+ /**
+ * Create an array from arg if it's not already an array
+ *
+ */
+ const castAsArray = arg => {
+ if (!Array.isArray(arg)) {
+ arg = [arg];
+ }
+ return arg;
+ };
+
+ /**
+ * Get the closest node to the evt.target matching the selector
+ * Stops at wrapper
+ *
+ */
+ const parentMatch = (target, selector, wrapper) => {
+ if (wrapper && !wrapper.contains(target)) {
+ return;
+ }
+ while (target && target.matches) {
+ if (target.matches(selector)) {
+ return target;
+ }
+ target = target.parentNode;
+ }
+ };
+
+ /**
+ * Get the first or last item from an array
+ *
+ * > 0 - right (last)
+ * <= 0 - left (first)
+ *
+ */
+ const getTail = (list, direction = 0) => {
+ if (direction > 0) {
+ return list[list.length - 1];
+ }
+ return list[0];
+ };
+
+ /**
+ * Return true if an object is empty
+ *
+ */
+ const isEmptyObject = obj => {
+ return Object.keys(obj).length === 0;
+ };
+
+ /**
+ * Get the index of an element amongst sibling nodes of the same type
+ *
+ */
+ const nodeIndex = (el, amongst) => {
+ if (!el) return -1;
+ amongst = amongst || el.nodeName;
+ var i = 0;
+ while (el = el.previousElementSibling) {
+ if (el.matches(amongst)) {
+ i++;
+ }
+ }
+ return i;
+ };
+
+ /**
+ * Set attributes of an element
+ *
+ */
+ const setAttr = (el, attrs) => {
+ iterate(attrs, (val, attr) => {
+ if (val == null) {
+ el.removeAttribute(attr);
+ } else {
+ el.setAttribute(attr, '' + val);
+ }
+ });
+ };
+
+ /**
+ * Replace a node
+ */
+ const replaceNode = (existing, replacement) => {
+ if (existing.parentNode) existing.parentNode.replaceChild(replacement, existing);
+ };
+
+ /**
+ * highlight v3 | MIT license | Johann Burkard
+ * Highlights arbitrary terms in a node.
+ *
+ * - Modified by Marshal 2011-6-24 (added regex)
+ * - Modified by Brian Reavis 2012-8-27 (cleanup)
+ */
+
+ const highlight = (element, regex) => {
+ if (regex === null) return;
+
+ // convet string to regex
+ if (typeof regex === 'string') {
+ if (!regex.length) return;
+ regex = new RegExp(regex, 'i');
+ }
+
+ // Wrap matching part of text node with highlighting , e.g.
+ // Soccer -> Soccer for regex = /soc/i
+ const highlightText = node => {
+ var match = node.data.match(regex);
+ if (match && node.data.length > 0) {
+ var spannode = document.createElement('span');
+ spannode.className = 'highlight';
+ var middlebit = node.splitText(match.index);
+ middlebit.splitText(match[0].length);
+ var middleclone = middlebit.cloneNode(true);
+ spannode.appendChild(middleclone);
+ replaceNode(middlebit, spannode);
+ return 1;
+ }
+ return 0;
+ };
+
+ // Recurse element node, looking for child text nodes to highlight, unless element
+ // is childless,