PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.19.4
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.19.4
1.19.8 1.19.7 1.19.6 1.19.5 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.12.0 1.13.0 1.14.0 1.15.0 1.15.1 1.15.2 1.15.3 1.16.0 1.16.1 1.16.2 1.16.3 1.16.4 1.16.5 1.16.6 1.16.7 1.16.8 1.17.0 1.17.6 1.17.7 1.17.8 1.17.9 1.18.0 1.18.1 1.18.2 1.18.3 1.18.4 1.18.5 1.18.6 1.18.7 1.18.8 1.18.9 1.19.0 1.19.1 1.19.2 1.19.3 1.19.4 1.3.19 1.3.20 1.4.0 1.4.1 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.16 1.5.17 1.5.18 1.5.19 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.8.0 1.8.1 1.8.3 1.9.0 1.9.1 1.9.2
nitropack / view / javascript / np_select2.js
nitropack / view / javascript Last commit date
admin_bar_menu.js 4 months ago elementor_cache_integration.js 5 months ago gravity_forms.js 2 months ago math_captcha.js 1 year ago nitropackUI.js 2 months ago np_notices.js 3 months ago np_safemode.js 1 year ago np_select2.js 2 months ago np_select2.min.js 2 months ago np_settings.js 2 months ago popper.min.js 1 year ago post_clear_cache.js 4 months ago preview_site.js 3 months ago system_report.js 4 months ago widgets_ajax.js 2 months ago
np_select2.js
362 lines
1 (function ($) {
2 "use strict";
3
4 if (typeof $.fn.npSelect === "function") {
5 return;
6 }
7
8 let instanceCount = 0;
9
10 function normalize(value) {
11 if (typeof value === "undefined" || value === null) {
12 return "";
13 }
14 return String(value);
15 }
16
17 function matchesTerm(text, value, term) {
18 const needle = term.toLowerCase();
19 return text.toLowerCase().includes(needle) || value.toLowerCase().includes(needle);
20 }
21
22 class NPSelect {
23 constructor($select) {
24 this.$select = $select;
25 this.instanceId = ++instanceCount;
26 this.isOpen = false;
27 this.init();
28 }
29
30 init() {
31 if (!this.$select.length) {
32 return;
33 }
34
35 this.$select.addClass("np-select-hidden-accessible");
36 this.build();
37 this.bind();
38 this.render();
39 }
40
41 build() {
42 this.$container = $('<span class="np-select"></span>');
43 this.$control = $('<span class="np-select-control" role="combobox" aria-expanded="false"></span>');
44 this.$values = $('<ul class="np-select-values"></ul>');
45 this.$inputItem = $('<li class="np-select-input-item"></li>');
46 this.$input = $('<input class="np-select-input" type="search" autocomplete="off" spellcheck="false" />');
47 this.$dropdown = $('<span class="np-select-dropdown"></span>');
48 this.$options = $('<ul class="np-select-options" role="listbox"></ul>');
49
50 this.$inputItem.append(this.$input);
51 this.$values.append(this.$inputItem);
52 this.$control.append(this.$values);
53 this.$dropdown.append(this.$options);
54 this.$container.append(this.$control, this.$dropdown);
55 this.$select.after(this.$container);
56 this.$dropdown.hide();
57 }
58
59 bind() {
60 this.$control.on("click", (event) => {
61 event.preventDefault();
62 this.open();
63 this.$input.trigger("focus");
64 });
65
66 this.$control.on("click", ".np-select-remove", (event) => {
67 event.preventDefault();
68 event.stopPropagation();
69
70 const value = normalize($(event.currentTarget).closest("li.np-select-value").attr("data-value"));
71 if (!value) {
72 return;
73 }
74
75 this.unselect(value);
76 this.open();
77 this.$input.trigger("focus");
78 });
79
80 this.$input.on("input", () => this.renderOptions());
81
82 this.$input.on("keydown", (event) => {
83 if (event.key === "Enter" || event.key === ",") {
84 event.preventDefault();
85
86 const $first = this.$options.find(".np-select-option:not(.np-select-option--disabled)").first();
87 if ($first.length) {
88 this.pickOption($first);
89 } else {
90 this.addFromInput();
91 }
92 return;
93 }
94
95 if (event.key === "Escape") {
96 event.preventDefault();
97 this.close();
98 return;
99 }
100
101 if (event.key === "Backspace" && !normalize(this.$input.val()).trim()) {
102 this.removeLast();
103 }
104 });
105
106 this.$options.on("mousedown", ".np-select-option", (event) => {
107 event.preventDefault();
108 });
109
110 this.$options.on("click", ".np-select-option", (event) => {
111 this.pickOption($(event.currentTarget));
112 });
113
114 this.$select.on("change.npSelect", () => {
115 this.render();
116 });
117
118 $(document).on("mousedown.npSelect-" + this.instanceId, (event) => {
119 if (!this.$container.is(event.target) && this.$container.has(event.target).length === 0) {
120 this.close();
121 }
122 });
123 }
124
125 pickOption($option) {
126 if ($option.hasClass("np-select-option--disabled")) {
127 return;
128 }
129
130 const value = normalize($option.attr("data-value"));
131 const label = normalize($option.text()).trim();
132 this.select(value, label, $option.hasClass("np-select-option--new"));
133 this.$input.val("");
134 this.renderOptions();
135 this.$input.trigger("focus");
136 }
137
138 render() {
139 this.renderSelected();
140 this.renderOptions();
141
142 const isDisabled = this.$select.prop("disabled");
143 this.$input.prop("disabled", isDisabled);
144 this.$container.toggleClass("np-select--disabled", isDisabled);
145 }
146
147 renderSelected() {
148 this.$values.find("li.np-select-value").remove();
149
150 this.$select.find("option:selected").each((_, optionEl) => {
151 const value = normalize(optionEl.value);
152 const text = normalize($(optionEl).text()).trim();
153
154 const $value = $('<li class="np-select-value" role="option"></li>').attr("data-value", value);
155 const $inner = $('<span class="np-select-value-inner"></span>');
156 $inner.append(document.createTextNode(text));
157 $inner.append('<span class="np-select-remove"></span>');
158 $value.append($inner);
159 this.$inputItem.before($value);
160 });
161 }
162
163 renderOptions() {
164 const term = normalize(this.$input.val()).trim();
165 const termLower = term.toLowerCase();
166
167 const rawSelected = this.$select.val();
168 const selectedValues = Array.isArray(rawSelected)
169 ? rawSelected.map(normalize)
170 : rawSelected
171 ? [normalize(rawSelected)]
172 : [];
173 const selectedSet = new Set(selectedValues);
174
175 let hasOptions = false;
176 let hasExactMatch = false;
177
178 this.$options.empty();
179
180 this.$select.find("option").each((_, optionEl) => {
181 const value = normalize(optionEl.value);
182 const text = normalize($(optionEl).text()).trim();
183 const isDisabled = $(optionEl).prop("disabled");
184
185 if (selectedSet.has(value)) {
186 return;
187 }
188
189 if (term && !matchesTerm(text, value, term)) {
190 return;
191 }
192
193 if (termLower && (text.toLowerCase() === termLower || value.toLowerCase() === termLower)) {
194 hasExactMatch = true;
195 }
196
197 const $option = $('<li class="np-select-option" role="option"></li>')
198 .attr("data-value", value)
199 .attr("aria-selected", "false")
200 .text(text);
201
202 if (isDisabled) {
203 $option.addClass("np-select-option--disabled").attr("aria-disabled", "true");
204 }
205
206 this.$options.append($option);
207 hasOptions = true;
208 });
209
210 if (term && !hasExactMatch) {
211 const $newOption = $('<li class="np-select-option np-select-option--new" role="option"></li>')
212 .attr("data-value", term)
213 .attr("aria-selected", "false")
214 .text(term);
215 this.$options.prepend($newOption);
216 hasOptions = true;
217 }
218
219 if (!hasOptions) {
220 this.$options.append('<li class="np-select-option np-select-option--disabled" aria-disabled="true">No results found</li>');
221 }
222 }
223
224 select(value, label, allowCreate) {
225 const normalizedValue = normalize(value);
226 let $option = this.$select
227 .find("option")
228 .filter(function () {
229 return normalize(this.value) === normalizedValue;
230 })
231 .first();
232
233 if (!$option.length && allowCreate) {
234 $option = $("<option></option>").val(normalizedValue).text(label || normalizedValue);
235 this.$select.append($option);
236 }
237
238 if (!$option.length) {
239 return;
240 }
241
242 if (!this.$select.prop("multiple")) {
243 this.$select.find("option").prop("selected", false);
244 }
245
246 $option.prop("selected", true);
247 this.$select.trigger("change");
248
249 if (!this.$select.prop("multiple")) {
250 this.close();
251 }
252 }
253
254 unselect(value) {
255 const normalizedValue = normalize(value);
256 this.$select
257 .find("option")
258 .filter(function () {
259 return normalize(this.value) === normalizedValue;
260 })
261 .prop("selected", false);
262
263 this.$select.trigger("change");
264 }
265
266 addFromInput() {
267 const term = normalize(this.$input.val()).trim();
268 if (!term) {
269 return;
270 }
271
272 const termLower = term.toLowerCase();
273 const $existing = this.$select
274 .find("option")
275 .filter(function () {
276 const value = normalize(this.value).toLowerCase();
277 const text = normalize($(this).text()).trim().toLowerCase();
278 return value === termLower || text === termLower;
279 })
280 .first();
281
282 if ($existing.length) {
283 this.select(normalize($existing.val()), normalize($existing.text()).trim(), false);
284 return;
285 }
286
287 this.select(term, term, true);
288 }
289
290 removeLast() {
291 if (!this.$select.prop("multiple")) {
292 return;
293 }
294
295 const values = this.$select.val() || [];
296 if (!Array.isArray(values) || values.length === 0) {
297 return;
298 }
299
300 values.pop();
301 this.$select.val(values).trigger("change");
302 }
303
304 open() {
305 if (this.isOpen || this.$select.prop("disabled")) {
306 return;
307 }
308
309 this.isOpen = true;
310 this.$container.addClass("np-select--open");
311 this.$control.attr("aria-expanded", "true");
312 this.$dropdown.show();
313 this.renderOptions();
314 }
315
316 close() {
317 if (!this.isOpen) {
318 return;
319 }
320
321 this.isOpen = false;
322 this.$container.removeClass("np-select--open");
323 this.$control.attr("aria-expanded", "false");
324 this.$dropdown.hide();
325 this.$input.val("");
326 this.renderOptions();
327 }
328
329 destroy() {
330 $(document).off("mousedown.npSelect-" + this.instanceId);
331 this.$select.off("change.npSelect");
332 this.$select.removeClass("np-select-hidden-accessible");
333 if (this.$container) {
334 this.$container.remove();
335 }
336 this.$select.removeData("npSelect");
337 }
338 }
339
340 $.fn.npSelect = function (method) {
341 if (typeof method === "string") {
342 if (method === "destroy") {
343 return this.each(function () {
344 const instance = $(this).data("npSelect");
345 if (instance) {
346 instance.destroy();
347 }
348 });
349 }
350
351 return this;
352 }
353
354 return this.each(function () {
355 const $element = $(this);
356 if (!$element.data("npSelect")) {
357 $element.data("npSelect", new NPSelect($element));
358 }
359 });
360 };
361 })(jQuery);
362