PageSourceSearch

https://dgdiffusion.com/static/assets/js/weblinks/auto_complete20260622.js

js dgdiffusion.com collected 2026-10-04 01:12:35 UTC 17,489 bytes, 421 lines download raw bytes

1function autoCompleteJsOLD(language) {
2    let dataset = [];
3
4    const compressedData = localStorage.getItem(`products_${language}`);
5
6    const cartDataElement = document.getElementById("cart-data");
7    const defaultTacCode = !cartDataElement ? "" : cartDataElement.getAttribute("data-default-tac-code") || "";
8
9// Vérifie si des données sont présentes dans localStorage
10    if (compressedData) {
11        // Décompresser les données et les parser en JSON
12        const decompressedData = LZString.decompressFromUTF16(compressedData);
13
14        if (decompressedData) {
15            dataset = JSON.parse(decompressedData);
16        }
17    }
18    let base_url = "product";
19
20    let searchingEngine = [];
21    try {
22        let searchingEngineParam = $('#searching-engine').text();
23        if (searchingEngineParam) {
24            searchingEngine = JSON.parse(searchingEngineParam);
25        }
26    } catch (e) {
27        console.warn("searching_engine non défini ou invalide, fallback");
28        searchingEngine = [
29            {key: "code_article"},
30            {key: "libelle_court"},
31            {key: "libelle_long"}
32        ];
33    }
34    const keysToDisplay = searchingEngine.map(cfg => cfg.key);
35
36    const autoCompleteJS = new autoComplete({
37        selector: ".autoComplete",
38        threshold: 3,
39        searchEngine: (query, record) => {
40            return searchingEngineFullRegex(query, record);
41        },
42        render: false,
43        data: {
44            src: dataset,
45            cache: true,
46            keys: ["search_text"],
47        },
48        resultsList: {
49            element: (list, data) => {
50                if (!data.results.length) {
51                    const message = document.createElement("div");
52                    message.setAttribute("class", "no_result");
53                    message.innerHTML = `<span>Aucun résultat pour "${data.query}"</span>`;
54                    list.prepend(message);
55                }
56            },
57            id: "autoComplete_list",
58            class: "results_list",
59            destination: ".autoComplete",
60            position: "afterend",
61            maxResults: 1000,
62            overflow: true,
63            height: 1000,
64            noResults: false,
65        },
66        resultItem: {
67            class: "autoComplete_result",
68            element: (item, data) => {
69                const ard_guid = data.value.ard_guid;
70                // const image_thumb = data.value.image_thumb && data.value.image_thumb !== 'null' ? `${data.value.image_thumb}` : '';
71                const fieldsHtml = keysToDisplay.map(key => data.value[key] || '').join(' - ');
72
73                item.innerHTML = `
74                    <a href="/${base_url}/${ard_guid}/" style="display: flex; justify-content: space-between; align-items: center; color: #29244a !important; padding: 10px; text-decoration: none;">
75                        <div style="display: flex; align-items: center;">
76                            <div>
77                                <span style="text-overflow: ellipsis; white-space: nowrap; overflow: hidden; font-size: 13px; display: block;">
78                                    ${data.match || data.value[keysToDisplay[1]]}
79                                </span>
80                                <span style="font-size: 12px; font-weight: 200; color: #000;">
81                                    ${fieldsHtml}
82                                </span>
83                            </div>
84                        </div>
85                        <span class="hidden-xs" style="font-size: 12px; font-weight: 200; color: #000;">
86                            ${data.value.code_article}
87                        </span>
88                    </a>
89                    <hr> <!-- Ajoute un séparateur entre chaque item -->
90                `;
91            },
92            highlight: "autoComplete_highlight",
93            selected: "autoComplete_selected"
94        },
95        events: {
96            input: {
97                selection: (event) => {
98                    const selection = event.detail.selection.value;
99                    autoCompleteJS.input.value = selection.code_article;
100                }
101            }
102        }
103    });
104
105    const autoCompleteJSCART = new autoComplete({
106        selector: ".autoComplete-cart",
107        threshold: 3,
108        searchEngine: (query, record) => {
109           return searchingEngineFullRegex(query, record);
110        },
111        render: false,
112        data: {
113            src: dataset,
114            cache: true,
115            keys: ["search_text"],
116        },
117        resultsList: {
118            element: (list, data) => {
119                if (!data.results.length) {
120                    const message = document.createElement("div");
121                    message.setAttribute("class", "no_result");
122                    message.innerHTML = `<span>Aucun résultat pour "${data.query}"</span>`;
123                    list.prepend(message);
124                }
125            },
126            id: "autoComplete_list",
127            class: "results_list",
128            destination: ".autoComplete-cart",
129            position: "afterend",
130            maxResults: 1000,
131            overflow: true,
132            height: 1000,
133            noResults: false,
134        },
135        resultItem: {
136            class: "autoComplete_result",
137            element: (item, data) => {
138                const ard_guid = data.value.ard_guid;
139                // const image_thumb = data.value.image_thumb && data.value.image_thumb !== 'null' ? `${data.value.image_thumb}` : '/static/assets/images/webLinks/default.jpg';
140                const fieldsHtml = keysToDisplay.map(key => data.value[key] || '').join(' - ');
141
142                item.innerHTML = `
143                    <a onclick="quickAddToCartOnClick('${data.value.code_article}', '${language}', '${defaultTacCode}')"
144                     style="display: flex; justify-content: space-between; align-items: center; color: #29244a !important; padding: 10px; text-decoration: none;">
145                        <div style="display: flex; align-items: center;">
146                            <div>
147                                <span style="text-overflow: ellipsis; white-space: nowrap; overflow: hidden; font-size: 13px; display: block;">
148                                    ${data.match || data.value[keysToDisplay[1]]}
149                                </span>
150                                <span style="font-size: 12px; font-weight: 200; color: #000;">
151                                    ${fieldsHtml}
152                                </span>
153                            </div>
154                        </div>
155                        <span class="hidden-xs" style="font-size: 12px; font-weight: 200; color: #000;">
156                            ${data.value.code_article}
157                        </span>
158                    </a>
159                    <hr> <!-- Ajoute un séparateur entre chaque item -->
160                `;
161            },
162            highlight: "autoComplete_highlight",
163            selected: "autoComplete_selected"
164        },
165        events: {
166            input: {
167                selection: (event) => {
168                    const selection = event.detail.selection.value;
169                    autoCompleteJSCART.input.value = selection.code_article;
170                }
171            }
172        }
173    });
174
175    // Style custom pour la liste des suggestions
176    $("#autoComplete_list").css({
177        "max-height": "400px",
178        "overflow-y": "auto",
179        "border": "1px solid #ccc",
180    });
181
182}
183
184function searchingEngineFullRegexOL(query, record){
185    const keywords = query.toLowerCase().split(" "); // Divise la requête en mots-clés
186
187    if (typeof record === "string") {
188        // Vérifie si tous les mots-clés sont présents dans le texte
189        const allKeywordsMatch = keywords.every(keyword => record.toLowerCase().includes(keyword));
190
191        if (allKeywordsMatch) {
192            // Applique la surbrillance sur le texte complet
193            let highlightedText = record;
194
195            keywords.forEach(keyword => {
196                // Crée une regex pour surligner le mot-clé dans le texte
197                const regex = new RegExp(`(${keyword})(?![^<]*>)`, "gi");
198                highlightedText = highlightedText.replace(regex, (match) => {
199                    return `<span style="color: red;">${match}</span>`; // Applique la surbrillance
200                });
201            });
202
203            return highlightedText; // Retourne le texte avec les mots-clés en surbrillance
204        }
205    }
206
207    return null; // Retourne null si tous les mots-clés ne sont pas présents
208}
209
210function searchingEngineOnCurrentIndexRegex(query, record){
211    const keywords = query.toLowerCase().split(" "); // Divise la requête en mots-clés
212
213    if (typeof record === "string") {
214        const recordText = record.toLowerCase();
215
216        // Vérifie si les mots-clés apparaissent dans le bon ordre
217        let index = 0;
218        const allKeywordsInOrder = keywords.every(keyword => {
219            const keywordIndex = recordText.indexOf(keyword, index); // Cherche le mot-clé à partir de la position actuelle
220            if (keywordIndex !== -1) {
221                index = keywordIndex + keyword.length; // Met à jour l'index pour vérifier les prochains mots après le mot trouvé
222                return true;
223            }
224            return false;
225        });
226
227        if (allKeywordsInOrder) {
228            // Applique la surbrillance sur le texte complet
229            let highlightedText = record;
230
231            keywords.forEach(keyword => {
232                // Crée une regex pour surligner le mot-clé dans le texte
233                const regex = new RegExp(`(${keyword})(?![^<]*>)`, "gi");
234                highlightedText = highlightedText.replace(regex, (match) => {
235                    return `<span style="color: red;">${match}</span>`; // Applique la surbrillance
236                });
237            });
238
239            return highlightedText; // Retourne le texte avec les mots-clés en surbrillance
240        }
241    }
242
243    return null; // Retourne null si tous les mots-clés ne sont pas présents dans l'ordre
244
245}
246
247//*******************************************************************************************
248// --- ÉTAT GLOBAL PRIVÉ (Mémoire persistante pour éviter les re-calculs) ---
249let isSearchInitialized = false;
250let globalDataset = [];
251let globalKeysToDisplay = [];
252let defaultTacCodeSearchingEngine = "";
253const BASE_URL = "product";
254
255/**
256 * 1. INITIALISATION UNIQUE (L'optimisation clé)
257 * Charge les données une seule fois au premier besoin.
258 */
259function initSearchContext(language) {
260    if (isSearchInitialized) return; // Déjà initialisé, on ne fait rien.
261
262    // 1. Récupération et parsing unique du dataset
263    try {
264        const compressed = localStorage.getItem(`products_${language}`);
265        if (compressed) {
266            const decompressed = LZString.decompressFromUTF16(compressed);
267            globalDataset = decompressed ? JSON.parse(decompressed) : [];
268        }
269    } catch (e) {
270        console.error("Échec de la préparation du dataset", e);
271        globalDataset = [];
272    }
273
274    // 2. Extraction unique des clés de recherche
275    const defaultEngine = [{ key: "code_article" }, { key: "libelle_court" }, { key: "libelle_long" }];
276    const engineElement = document.getElementById('searching-engine');
277    let engineConfig = defaultEngine;
278
279    if (engineElement?.textContent) {
280        try {
281            engineConfig = JSON.parse(engineElement.textContent);
282        } catch (e) {
283            console.warn("Fallback sur searching_engine par défaut.");
284        }
285    }
286    globalKeysToDisplay = engineConfig.map(cfg => cfg.key);
287
288    // 3. Cache des données du panier
289    const cartDataElement = document.getElementById("cart-data");
290    defaultTacCodeSearchingEngine = cartDataElement?.getAttribute("data-default-tac-code") || "";
291
292    isSearchInitialized = true;
293}
294
295/**
296 * 2. MOTEUR DE RECHERCHE OPTIMISÉ
297 * Utilise des méthodes de chaînes natives (indexOf/includes) ultra-rapides pour filtrer,
298 * et ne génère la Regex de surbrillance QUE pour les résultats finaux retenus (et pas sur tout le dataset).
299 */
300function searchingEngineFullRegex(query, record) {
301    if (typeof record !== "string") return null;
302
303    // Nettoyage de la requête (micro-optimisation : on évite le split/loop si vide)
304    const cleanedQuery = query.trim().toLowerCase();
305    if (!cleanedQuery) return null;
306
307    const keywords = cleanedQuery.split(" ").filter(Boolean);
308    const recordLower = record.toLowerCase();
309
310    // PERFORMANCE CRITIQUE : "Early Exit" avec .includes().
311    // C'est 10x à 50x plus rapide qu'une Regex sur de gros volumes.
312    for (let i = 0; i < keywords.length; i++) {
313        if (!recordLower.includes(keywords[i])) {
314            return null; // On élimine le produit instantanément sans perdre de CPU
315        }
316    }
317
318    // Si on arrive ici, le produit correspond. On applique la surbrillance.
319    let highlightedText = record;
320    for (let i = 0; i < keywords.length; i++) {
321        const regex = new RegExp(`(${keywords[i]}
321)(?![^<]*>)`, "gi");
322        highlightedText = highlightedText.replace(regex, '<span style="color: red;">$1</span>');
323    }
324
325    return highlightedText;
326}
327
328/**
329 * 3. FACTORY DE CONFIGURATION SANS EFFETS DE BORD
330 */
331function createAutoCompleteConfig({ selector, onRenderItem, onSelection }) {
332    return {
333        selector,
334        threshold: 3,
335        debounce: 150, // Micro-optimisation : Attend 150ms de pause dans la frappe avant de chercher
336        searchEngine: (query, record) => searchingEngineFullRegex(query, record),
337        resultsList: {
338            id: "autoComplete_list",
339            class: "results_list",
340            destination: selector,
341            position: "afterend",
342            maxResults: 50, // UI/UX Optimisation : Afficher 1000 items bloque le thread principal du navigateur. 50 suffisent largement.
343            element: (list, data) => {
344                if (!data.results.length) {
345                    const message = document.createElement("div");
346                    message.className = "no_result";
347                    message.innerHTML = `<span>Aucun résultat pour "${data.query}"</span>`;
348                    list.prepend(message);
349                }
350            },
351        },
352        data: {
353            src: globalDataset,
354            cache: true,
355            keys: ["search_text"],
356        },
357        resultItem: {
358            class: "autoComplete_result",
359            highlight: "autoComplete_highlight",
360            selected: "autoComplete_selected",
361            element: onRenderItem
362        },
363        events: {
364            input: { selection: onSelection }
365        }
366    };
367}
368
369/**
370 * 4. POINT D'ENTRÉE DE L'INITIALISATION DES INPUTS
371 */
372function setupAutoCompleteInputs(language) {
373    // Initialise le contexte (si ce n'est pas déjà fait)
374    initSearchContext(language);
375
376    // Instance Globale
377    const instanceStandard = new autoComplete(createAutoCompleteConfig({
378        selector: ".autoComplete",
379        onSelection: (event) => {
380            instanceStandard.input.value = event.detail.selection.value.code_article;
381        },
382        onRenderItem: (item, data) => {
383            const fieldsHtml = globalKeysToDisplay.map(key => data.value[key] || '').join(' - ');
384            const mainLabel = data.match || data.value[globalKeysToDisplay[1]] || '';
385
386            item.innerHTML = `
387                <a href="/${BASE_URL}/${data.value.ard_guid}/" style="display: flex; justify-content: space-between; align-items: center; color: #29244a !important; padding: 10px; text-decoration: none;">
388                    <div>
389                        <span style="text-overflow: ellipsis; white-space: nowrap; overflow: hidden; font-size: 13px; display: block;">${mainLabel}</span>
390                        <span style="font-size: 12px; font-weight: 200; color: #000;">${fieldsHtml}</span>
391                    </div>
392                    <span class="hidden-xs" style="font-size: 12px; font-weight: 200; color: #000;">${data.value.code_article}</span>
393                </a>
394                <hr>
395            `;
396        }
397    }));
398
399    // Instance Panier
400    const instanceCart = new autoComplete(createAutoCompleteConfig({
401        selector: ".autoComplete-cart",
402        onSelection: (event) => {
403            instanceCart.input.value = event.detail.selection.value.code_article;
404        },
405        onRenderItem: (item, data) => {
406            const fieldsHtml = globalKeysToDisplay.map(key => data.value[key] || '').join(' - ');
407            const mainLabel = data.match || data.value[globalKeysToDisplay[1]] || '';
408
409            item.innerHTML = `
410                <a onclick="quickAddToCartOnClick('${data.value.code_article}', '${language}', '${defaultTacCodeSearchingEngine}')" style="cursor: pointer; display: flex; justify-content: space-between; align-items: center; color: #29244a !important; padding: 10px; text-decoration: none;">
411                    <div>
412                        <span style="text-overflow: ellipsis; white-space: nowrap; overflow: hidden; font-size: 13px; display: block;">${mainLabel}</span>
413                        <span style="font-size: 12px; font-weight: 200; color: #000;">${fieldsHtml}</span>
414                    </div>
415                    <span class="hidden-xs" style="font-size: 12px; font-weight: 200; color: #000;">${data.value.code_article}</span>
416                </a>
417                <hr>
418            `;
419        }
420    }));
421}

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.