PageSourceSearch

https://www.de-ijssel-coatings.se/wp-content/plugins/color-picker/js/color-picker.js?ver=1.0.8

js de-ijssel-coatings.se collected 2026-10-06 18:11:40 UTC 16,016 bytes, 535 lines download raw bytes

1class UTColorPicker {
2  constructor() {
3    // Data from PHP
4    this.colorPickerData = window.colorPickerData;
5
6    // Colors
7    this.colorSystem = null;
8    this.colors = [];
9    this.selectedColor = null;
10
11    // Fetch options
12    this.search = "";
13    this.pagination = {
14      currentPage: 1,
15      nextPage: 2,
16      previousPage: 0,
17      colorsPerPage: 90,
18      totalColors: 0,
19      totalPages: 0,
20    };
21
22    // Initialize elements as null
23    this.elements = {
24      dialog: null,
25      colorGrid: null,
26      pagination: null,
27      searchInput: null,
28      selectedColor: null,
29    };
30
31    // Events
32    this.handleEvents();
33  }
34
35  setColorSystem = (colorSystem) => {
36    this.colorSystem = colorSystem;
37    this.fetchColorSystems();
38  };
39
40  // Add method to initialize elements
41  initializeElements = () => {
42    this.elements = {
43      dialog: document.getElementById("utcp-dialog"),
44      colorGrid: document.getElementById("utcp-color-grid"),
45      pagination: document.getElementById("utcp-color-pagination"),
46      searchInput: document.getElementById("utcp-color-search-input"),
47      selectedColor: document.getElementById("utcp-selected-color"),
48    };
49  };
50
51  handleEvents = () => {
52    // Use event delegation instead of multiple listeners
53    document.addEventListener("click", this.handleGlobalClick);
54    document.addEventListener("change", this.handleGlobalChange);
55    document.addEventListener("keypress", this.handleSearchKeypress);
56  };
57
58  handleGlobalClick = (event) => {
59    const target = event.target;
60
61    // Use object literal for cleaner event routing
62    const clickHandlers = {
63      "#utcp-close-dialog-btn": () => this.elements.dialog.close(),
64      "#utcp-open-dialog-btn": () => this.handleOpenDialog(target),
65      ".utcp-color-box": () => this.handleColorBoxClick(target),
66      ".utcp-pagination-btn": () => this.handlePaginationClick(target),
67      ".utcp-thumbnail": () => this.handleThumbnailClick(target),
68      "#utcp-color-search-btn": () => this.performSearch(),
69      "#utcp-submit-btn": () => this.elements.dialog.close(),
70    };
71
72    for (const [selector, handler] of Object.entries(clickHandlers)) {
73      if (target.closest(selector)) {
74        handler(event);
75        break;
76      }
77    }
78  };
79
80  handleGlobalChange = (event) => {
81    const target = event.target;
82
83    if (target.matches("#utcp-main-color-system-select")) {
84      this.handleColorSystemChange({ target, isMain: true });
85    } else if (target.matches(".utcp-sub-color-system-select")) {
86      this.handleColorSystemChange({ target, isMain: false });
87    }
88  };
89
90  handleColorSystemChange = ({ target, isMain }) => {
91    const colorSystem = target.value;
92
93    if (isMain) {
94      document
95        .querySelectorAll(".utcp-sub-color-system-select")
96        .forEach((select) => {
97          const isMatching = select.dataset.subColorSystem === colorSystem;
98          select.classList.toggle("utcp-hidden", !isMatching);
99          this.colorSystem = isMatching ? select.value : colorSystem;
100        });
101    } else {
102      this.colorSystem = colorSystem;
103    }
104
105    this.resetSearchAndPagination();
106    this.fetchColorData();
107  };
108
109  handleColorBoxClick = (target) => {
110    const element = target.closest(".utcp-color-box");
111    if (!element) return;
112
113    // Remove selected class from previously selected color
114    const previousSelected = document.querySelector(
115      ".utcp-color-box.utcp-selected"
116    );
117
118    if (previousSelected) {
119      previousSelected.classList.remove("utcp-selected");
120    }
121
122    // Add selected class to clicked color
123    element.classList.add("utcp-selected");
124
125    // Get the color code
126    const colorCode = element.dataset.colorCode;
127
128    const headerSelection = document.getElementById(
129      "utcp-dialog-header-selection"
130    );
131
132    if (headerSelection) {
133      headerSelection.textContent = `${colorCode}`;
134    }
135
136    // Update hidden input and color code display
137    const hex = element.dataset.hex;
138    const input = document.getElementById("utcp-selected-color");
139    const colorCodeDisplay = document.querySelector(".utcp-color-code");
140    const colorSwatch = document.querySelector(".utcp-color-swatch");
141
142    input.value = colorCode;
143    if (colorCodeDisplay) {
144      colorCodeDisplay.textContent = colorCode;
145      colorCodeDisplay.classList.remove("utcp-color-code-placeholder");
146    }
147
148    // Update color swatch
149    if (colorSwatch) {
150      colorSwatch.style.backgroundColor = hex;
151      colorSwatch.classList.add("utcp-color-swatch-has-color");
152    }
153
154    // Update preview colors
155    const previewImgs = document.querySelectorAll(".utcp-preview-image");
156    const thumbnails = document.querySelectorAll(".utcp-thumbnail");
157
158    for (const img of previewImgs) {
159      img.style.backgroundColor = hex;
160    }
161
162    for (const thumb of thumbnails) {
163      thumb.style.backgroundColor = hex;
164    }
165  };
166
167  handlePaginationClick = async (target) => {
168    const element = target.closest(".utcp-pagination-btn");
169    if (!element || !element.dataset.page) return;
170
171    this.pagination.currentPage = parseInt(element.dataset.page);
172    await this.fetchColorData();
173  };
174
175  handleThumbnailClick = (target) => {
176    const element = target.closest(".utcp-thumbnail");
177    if (!element) return;
178
179    const container = element.closest("#utcp-dialog-content-right");
180    const index = parseInt(element.dataset.index);
181
182    container
183      .querySelectorAll(".utcp-thumbnail")
184      .forEach((thumb) => (thumb.dataset.active = "false"));
185    element.dataset.active = "true";
186
187    container
188      .querySelectorAll(".utcp-preview-image")
189      .forEach((img) => (img.dataset.active = "false"));
190    container.querySelectorAll(".utcp-preview-image")[index].dataset.active =
191      "true";
192  };
193
194  resetSearchAndPagination = () => {
195    this.search = "";
196    this.pagination.currentPage = 1;
197
198    // Reset search input field value
199    const searchInput = document.getElementById("utcp-color-search-input");
200    if (searchInput) {
201      searchInput.value = "";
202    }
203  };
204
205  handleOpenDialog = async (target) => {
206    const element = target.closest("#utcp-open-dialog-btn");
207    if (!element) return;
208
209    // Don't open dialog if there's only one color (already auto-selected)
210    if (this.pagination.totalColors === 1) return;
211
212    // Initialize elements when dialog is opened
213    this.initializeElements();
214
215    this.elements.dialog.showModal();
216    await this.fetchColorData();
217
218    // Restore selected color after colors are loaded
219    const selectedColorInput = document.getElementById("utcp-selected-color");
220    if (selectedColorInput && selectedColorInput.value) {
221      const selectedColorBox = document.querySelector(
222        `.utcp-color-box[data-color-code="${selectedColorInput.value}"]`
223      );
224
225      if (selectedColorBox) {
226        selectedColorBox.classList.add("utcp-selected");
227      }
228    }
229  };
230
231  fetchColorSystems = async () => {
232    try {
233      const formData = new FormData();
234      formData.append("action", "get_color_systems");
235      formData.append("color_system", this.colorSystem);
236      formData.append("nonce", this.colorPickerData.nonce);
237
238      const response = await fetch(this.colorPickerData.ajaxUrl, {
239        method: "POST",
240        body: formData,
241      });
242
243      if (!response.ok) {
244        throw new Error(`HTTP error! status: ${response.status}`);
245      }
246
247      const { data, success } = await response.json();
248
249      if (!success) {
250        throw new Error("API returned unsuccessful response");
251      }
252
253      const container = document.getElementById("utcp-color-systems-container");
254      container.innerHTML = data.html;
255
256      this.colorSystem = data.initial_color_system;
257
258      // Fetch colors early to enable auto-selection before dialog opens
259      await this.prefetchColors();
260    } catch (error) {
261      console.error("Error fetching color data:", error);
262      // Optionally show user-friendly error message
263    }
264  };
265
266  prefetchColors = async () => {
267    try {
268      const formData = new FormData();
269      formData.append("action", "get_colors");
270      formData.append("color_system", this.colorSystem);
271      formData.append("nonce", this.colorPickerData.nonce);
272      formData.append("color_page", 1);
273      formData.append("colors_per_page", this.pagination.colorsPerPage);
274      formData.append("color_search", "");
275
276      const response = await fetch(this.colorPickerData.ajaxUrl, {
277        method: "POST",
278        body: formData,
279      });
280
281      if (!response.ok) {
282        throw new Error(`HTTP error! status: ${response.status}`);
283      }
284
285      const { data, success } = await response.json();
286      if (!success) {
287        throw new Error("API returned unsuccessful response");
288      }
289
290      this.colors = data.colors;
291      this.pagination = data.pagination;
292
293      // Auto-select if only one color in the system (before dialog opens)
294      if (this.colors.length === 1 && this.pagination.totalColors === 1) {
295        this.autoSelectColor(this.colors[0]);
296      }
297    } catch (error) {
298      console.error("Error prefetching colors:", error);
299    }
300  };
301
302  autoSelectColor = ({ color, hex }) => {
303    // Update hidden input
304    const input = document.getElementById("utcp-selected-color");
305    if (input) {
306      input.value = color;
307    }
308
309    // Update color code display
310    const colorCodeDisplay = document.querySelector(".utcp-color-code");
311    if (colorCodeDisplay) {
312      colorCodeDisplay.textContent = color;
313      colorCodeDisplay.classList.remove("utcp-color-code-placeholder");
314    }
315
316    // Update color swatch
317    const colorSwatch = document.querySelector(".utcp-color-swatch");
318    if (colorSwatch) {
319      colorSwatch.style.backgroundColor = hex;
320      colorSwatch.classList.add("utcp-color-swatch-has-color");
321    }
322
323    // Hide helper text since there's only one color
324    const helperText = document.querySelector(".utcp-color-code-helper");
325    if (helperText) {
326      helperText.style.display = "none";
327    }
328
329    // Store selection for when dialog opens
330    this.selectedColor = { color, hex };
331  };
332
333  fetchColorData = async () => {
334    try {
335      const formData = new FormData();
336      formData.append("action", "get_colors");
337      formData.append("color_system", this.colorSystem);
338      formData.append("nonce", this.colorPickerData.nonce);
339      formData.append("color_page", this.pagination.currentPage);
340      formData.append("colors_per_page", this.pagination.colorsPerPage);
341      formData.append("color_search", this.search);
342
343      const response = await fetch(this.colorPickerData.ajaxUrl, {
344        method: "POST",
345        body: formData,
346      });
347
348      if (!response.ok) {
349        throw new Error(`HTTP error! status: ${response.status}`);
350      }
351
352      const { data, success } = await response.json();
353      if (!success) {
354        throw new Error("API returned unsuccessful response");
355      }
356
357      this.colors = data.colors;
358      this.pagination = data.pagination;
359
360      this.renderColorGrid();
361      this.renderPagination();
362
363      // Auto-select if only one color in the system
364      if (this.colors.length === 1 && this.pagination.totalColors === 1) {
365        const singleColorBox = document.querySelector(".utcp-color-box");
366        if (singleColorBox) {
367          this.handleColorBoxClick(singleColorBox);
368        }
369      }
370    } catch (error) {
371      console.error("Error fetching color data:", error);
372      // Optionally show user-friendly error message
373    }
374  };
375
376  renderColorGrid = () => {
377    const container = document.getElementById("utcp-color-grid");
378    container.innerHTML = "";
379    container.scrollTop = 0;
380
381    if (this.colors.length === 0) {
382      const noResults = document.createElement("div");
383      noResults.classList.add("utcp-color-grid-msg");
384      noResults.textContent =
385        "Inga kulörkoder matchade din sökning.\nFörsök igen!";
386      container.appendChild(noResults);
387      return;
388    }
389
390    for (const { color, hex, text } of this.colors) {
391      const colorBox = document.createElement("div");
392
393      // Style
394      colorBox.classList.add("utcp-color-box");
395      colorBox.style.backgroundColor = hex;
396
397      // Data
398      colorBox.dataset.colorCode = color;
399      colorBox.dataset.hex = hex;
400      colorBox.dataset.text = text;
401
402      // Content
403      const span = document.createElement("span");
404      span.style.color = text;
405      span.textContent = color;
406      colorBox.appendChild(span);
407
408      // Append
409      container.appendChild(colorBox);
410    }
411  };
412
413  renderPagination = () => {
414    const container = document.getElementById("utcp-color-pagination");
415    container.innerHTML = "";
416
417    if (this.pagination.totalPages <= 1) {
418      container.classList.add("utcp-hidden");
419      return;
420    } else {
421      container.classList.remove("utcp-hidden");
422    }
423
424    // Create previous button
425    const prevButton = document.createElement("button");
426    prevButton.type = "button";
427    prevButton.classList.add("utcp-pagination-btn", "utcp-btn");
428    prevButton.innerHTML = "←";
429    prevButton.dataset.page = this.pagination.previousPage || "";
430    prevButton.disabled = !this.pagination.previousPage;
431    container.appendChild(prevButton);
432
433    // Calculate which page numbers to show
434    const currentPage = this.pagination.currentPage;
435    const totalPages = this.pagination.totalPages;
436    const range = [];
437    const rangeWithDots = [];
438    let l;
439
440    // Show all pages if total pages is 7 or less
441    if (totalPages <= 7) {
442      for (let i = 1; i <= totalPages; i++) {
443        range.push(i);
444      }
445    } else {
446      // Always add page 1
447      range.push(1);
448
449      // Calculate the range around current page
450      if (currentPage <= 4) {
451        // Near the start
452        for (let i = 2; i <= 5; i++) {
453          range.push(i);
454        }
455      } else if (currentPage >= totalPages - 3) {
456        // Near the end
457        for (let i = totalPages - 4; i < totalPages; i++) {
458          range.push(i);
459        }
460      } else {
461        // In the middle
462        for (let i = currentPage - 1; i <= currentPage + 1; i++) {
463          range.push(i);
464        }
465      }
466
467      // Always add last page
468      if (!range.includes(totalPages)) {
469        range.push(totalPages);
470      }
471    }
472
473    // Add dots where needed
474    for (let i of range) {
475      if (l) {
476        if (i - l === 2) {
477          rangeWithDots.push(l + 1);
478        } else if (i - l !== 1) {
479          rangeWithDots.push("...");
480        }
481      }
482      rangeWithDots.push(i);
483      l = i;
484    }
485
486    // Create page number buttons
487    for (const i of rangeWithDots) {
488      const button = document.createElement("button");
489      button.type = "button";
490      button.classList.add("utcp-pagination-btn", "utcp-btn");
491
492      if (i === "...") {
493        button.textContent = i;
494        button.disabled = true;
495        button.classList.add("utcp-pagination-dots");
496      } else {
497        button.textContent = i;
498        button.dataset.page = i;
499        if (i === currentPage) {
500          button.classList.add("utcp-pagination-current");
501        }
502      }
503
504      container.appendChild(button);
505    }
506
507    // Create next button
508    const nextButton = document.createElement("button");
509    nextButton.type = "button";
510    nextButton.classList.add("utcp-pagination-btn", "utcp-btn");
511    nextButton.innerHTML = "→";
512    nextButton.dataset.page = this.pagination.nextPage || "";
513    nextButton.disabled = !this.pagination.nextPage;
514    container.appendChild(nextButton);
515  };
516
517  handleSearchKeypress = async (event) => {
518    const element = event.target.closest("#utcp-color-search-input");
519    if (!element) return;
520
521    if (event.key === "Enter") {
522      event.preventDefault();
523      await this.performSearch();
524    }
525  };
526
527  performSearch = async () => {
528    const searchInput = document.getElementById("utcp-color-search-input");
529    this.search = searchInput.value.trim();
530    this.pagination.currentPage = 1; // Reset to first page when searching
531    await this.fetchColorData();
532  };
533}
534
535window.UTColorPicker = UTColorPicker;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.