PageSourceSearch

https://www.analytikashop.gr/_content/Syncfusion.Blazor.Core/scripts/sf-utils.l13mrfrp9g.js

js analytikashop.gr collected 2026-10-10 00:36:48 UTC 17,202 bytes, 390 lines download raw bytes

1(function () {
2    // Prevent multiple initializations if the script is accidentally loaded more than once
3    if (window.SfStyleWatcher) return;
4
5    const DATA_STYLE_ATTR = 'data-sf-style';
6    const LAYOUT_CRITICAL_PROPS = {
7        width: true,
8        height: true,
9        'min-width': true,
10        'max-width': true,
11        'min-height': true,
12        'max-height': true,
13        top: true,
14        left: true,
15        right: true,
16        bottom: true,
17        display: true,
18        position: true,
19        transform: true
20    };
21
22    /**
23     * Minimal microtask scheduler to batch multiple style writes into a single batch.
24     * queueMicrotask finishes before the browser does things like rendering the page Ensures no flicker.
25     */
26    const queue = [];
27    let queued = false; // flag
28    /**
29     * Schedule a function to run in the next microtask batch.
30     * Multiple calls before the batch executes will queue multiple functions.
31     */
32    function schedule(fn) {
33        queue.push(fn);
34        if (!queued) {
35            queued = true;
36            queueMicrotask(() => {
37                try {
38                    // Execute all queued style updates in order
39                    for (let i = 0; i < queue.length; i++) {
40                        queue[i]()
41                    }
42                } finally {
43                    // Always reset the queue and flag
44                    queue.length = 0;
45                    queued = false;
46                }
47            });
48        }
49    }
50
51    /**
52     * Flush queued style operations immediately.
53     * Useful when deterministic style reads are required.
54     */
55    function flushScheduled() {
56        if (!queue.length) {
57            queued = false;
58            return;
59        }
60
61        const pending = queue.splice(0, queue.length);
62        queued = false;
63        for (let i = 0; i < pending.length; i++) {
64            pending[i]();
65        }
66    }
67
68    /**
69     * Parse a CSS style text into a property map.
70     * Handles !important priority and trims whitespace.
71     *
72     * @param {string} styleText - Raw style text
73     * @returns {Object<string, { value: string, priority: string }>} - Parsed styles
74     */
75    function parseStyleText(styleText) {
76        const map = {};
77        if (!styleText) return map;
78        const parts = styleText.split(';');
79        for (let i = 0; i < parts.length; i++) {
80            const part = parts[i].trim();
81            if (!part) continue;
82            const colonIndex = part.indexOf(':');
83            if (colonIndex === -1) continue;
84            const prop = part.slice(0, colonIndex).trim();
85            let value = part.slice(colonIndex + 1).trim();
86            let priority = '';
87            if (/!important$/i.test(value)) {
88                value = value.replace(/!important$/i, '').trim();
89                priority = 'important';
90            }
91            if (prop) {
92                map[prop] = { value, priority };
93            }
94        }
95        return map;
96    }
97
98    /**
99     * Returns true when style map includes layout-critical properties.
100     * @param {Object<string, { value: string, priority: string }>} map - Parsed style map.
101     * @returns {boolean} - Whether immediate application is required.
102     */
103    function hasLayoutCriticalProps(map) {
104        const keys = Object.keys(map);
105        for (let i = 0; i < keys.length; i++) {
106            if (LAYOUT_CRITICAL_PROPS[keys[i].toLowerCase()]) {
107                return true;
108            }
109        }
110        return false;
111    }
112
113    /**
114     * Apply inline styles from the data-sf-style attribute without overriding
115     * styles set by component scripts. Only properties present in data-sf-style
116     * are updated, and previously applied properties are removed when absent.
117     *
118     * Uses cached values (sfAppliedStyleText/sfAppliedStyleMap) to skip redundant writes.
119     * All actual DOM writes are deferred to the microtask scheduler to avoid flicker.
120     *
121     * JS-override detection: if component JS has changed a property's value since
122     * sf-utils last applied it (i.e. current inline value differs from cached value),
123     * that property is considered "JS-owned" and will not be overwritten or removed.
124     * This prevents sf-utils from fighting against component scripts (e.g. sf-dialog.js
125     * setting display:block on the overlay after sf-utils set display:none from
126     * data-sf-style).
127     *
128     * @param {Element} element - The DOM element to style
129     * @param {string|null} styleValue - The raw value of the data-sf-style attribute
130     */
131    function applySfStyle(element, styleValue, forceSync) {
132        const newCss = styleValue || '';
133        const prevCss = element.sfAppliedStyleText || '';
134
135        // Skip if the style string hasn't changed
136        if (newCss === prevCss) return;
137
138        const newMap = parseStyleText(newCss);
139        const prevMap = element.sfAppliedStyleMap || {};
140        const shouldApplySync = !!forceSync || hasLayoutCriticalProps(newMap) || hasLayoutCriticalProps(prevMap);
141
142        // Snapshot the current inline style map at the moment data-sf-style is processed
143        // (synchronous, before the microtask runs). This is the baseline for first-time
144        // properties (prevEntry === undefined): if a property's value changes between
145        // this snapshot and when the microtask executes, component JS set it in the
146        // interim and must not be overwritten.
147        const scheduleTimeMap = parseStyleText(element.style.cssText);
148
149        const applyStyles = () => {
150            // Remove properties that were previously applied but no longer present,
151            // but only if component JS has not overridden the value since we last set it.
152            Object.keys(prevMap).forEach(prop => {
153                if (!newMap.hasOwnProperty(prop)) {
154                    const currentValue = element.style.getPropertyValue(prop);
155                    // Safe to remove only when the value still matches what sf-utils last wrote.
156                    // If it differs, JS has changed it — leave it alone.
157                    if (currentValue === prevMap[prop].value) {
158                        element.style.removeProperty(prop);
159                    }
160                }
161            });
162
163            // Set/update current properties from data-sf-style,
164            // but only if component JS has not overridden the value since data-sf-style was stamped.
165            Object.keys(newMap).forEach(prop => {
166                const entry = newMap[prop];
167                const currentValue = element.style.getPropertyValue(prop);
168                const prevEntry = prevMap[prop];
169
170                let jsOverrode;
171                if (prevEntry !== undefined) {
172                    // sf-utils previously managed this property.
173                    // JS-overridden if the current value is non-empty and differs from what sf-utils last wrote.
174                    jsOverrode = currentValue !== '' && currentValue !== prevEntry.value;
175                } else if (element.sfAppliedStyleMap !== undefined) {
176                    // sf-utils manages this element but has never seen this specific property before.
177                    // Use the schedule-time snapshot as the baseline: if the value changed between
178                    // snapshot time and microtask execution, component JS set it — respect it.
179                    const scheduleTimeValue = scheduleTimeMap[prop] ? scheduleTimeMap[prop].value : '';
180                    jsOverrode = currentValue !== '' && currentValue !== scheduleTimeValue;
181                } else {
182                    // sf-utils has NEVER managed this element at all (sfAppliedStyleMap is absent).
183                    // Any pre-existing inline value must be treated as JS-owned unconditionally.
184                    // The schedule-time snapshot cannot be used here: if JS set the value before
185                    // applySfStyle was ever called (e.g. Gantt JS sets splitter height before
186                    // Blazor re-stamps data-sf-style after a cache-clearing re-mount in WASM),
187                    // scheduleTimeValue === currentValue, so the snapshot check would incorrectly
188                    // report jsOverrode=false and overwrite the JS value with the Blazor default.
189                    jsOverrode = currentValue !== '';
190                }
191
192                if (!jsOverrode) {
193                    element.style.setProperty(prop, entry.value, entry.priority);
194                }
195            });
196
197            // Update the cache to reflect what is actually in the element now.
198            // For JS-overridden properties, record the live JS value so that future
199            // data-sf-style re-stamps can correctly detect further JS changes.
200            const updatedMap = {};
201            Object.keys(newMap).forEach(prop => {
202                const currentValue = element.style.getPropertyValue(prop);
203                updatedMap[prop] = {
204                    value: currentValue,
205                    priority: element.style.getPropertyPriority(prop)
206                };
207            });
208            element.sfAppliedStyleText = newCss;
209            element.sfAppliedStyleMap = updatedMap;
210
211            if (element.hasAttribute('data-sf-style')) {
212                // Mark intentional removal so the observer does not call clearSfStyle
213                element.isDataAttrRemoved = true;
214                element.removeAttribute(DATA_STYLE_ATTR);
215                queueMicrotask(() => { delete element.isDataAttrRemoved; });
216            }
217        };
218
219        // Apply immediately for layout-critical updates to avoid stale reads (WASM/server parity).
220        // Keep batching for non-critical properties to reduce style churn and flicker.
221        if (shouldApplySync) {
222            applyStyles();
223        } else {
224            schedule(applyStyles);
225        }
226    }
227
228    /**
229       * Clear inline styles that were previously applied by this script.
230       * Important: Only clears if we previously set something (checks cache).
231       * This prevents accidentally wiping user- or framework-defined inline styles.
232       * 
233       * @param {Element} element - The DOM element to potentially clear
234       */
235    function clearSfStyle(element, forceSync) {
236        // Only act if this script previously applied a style to this element
237        if (element.sfAppliedStyleMap !== undefined || element.sfAppliedStyleText !== undefined) {
238            const clearStyles = () => {
239                const prevMap = element.sfAppliedStyleMap || {};
240                Object.keys(prevMap).forEach(prop => {
241                    const currentValue = element.style.getPropertyValue(prop);
242                    // Only remove if the value still matches what sf-utils last wrote.
243                    // If JS changed it, leave it alone.
244                    if (currentValue === prevMap[prop].value) {
245                        element.style.removeProperty(prop);
246                    }
247                });
248                // Remove cache entries to indicate we no longer manage this element's styles
249                delete element.sfAppliedStyleText;
250                delete element.sfAppliedStyleMap;
251            };
252
253            if (forceSync) {
254                clearStyles();
255            } else {
256                schedule(clearStyles);
257            }
258        }
259    }
260
261    /**
262     * Patch native attribute APIs so data-sf-style writes become immediately observable.
263     * This removes timing differences between render modes when components read dimensions
264     * right after render.
265     */
266    const nativeSetAttribute = Element.prototype.setAttribute;
267    const nativeRemoveAttribute = Element.prototype.removeAttribute;
268    Element.prototype.setAttribute = function (name, value) {
269        nativeSetAttribute.call(this, name, value);
270        if (name === DATA_STYLE_ATTR && this.nodeType === 1) {
271            applySfStyle(this, value, true);
272        }
273    };
274
275    Element.prototype.removeAttribute = function (name) {
276        const hadDataStyle = name === DATA_STYLE_ATTR && this.hasAttribute(DATA_STYLE_ATTR);
277        nativeRemoveAttribute.call(this, name);
278        if (hadDataStyle && this.nodeType === 1 && !this.isDataAttrRemoved) {
279            clearSfStyle(this, true);
280        }
281    };
282
283    /**
284     * Process a single element if it has data-sf-style.
285     */
286    function processElement(element) {
287        if (!element || element.nodeType !== 1) return;
288        if (element.hasAttribute(DATA_STYLE_ATTR)) {
289            applySfStyle(element, element.getAttribute(DATA_STYLE_ATTR), true);
290        }
291    }
292
293    /**
294     * Recursively process a subtree for elements with data-sf-style attributes.
295     * 
296     * Used both on initial load and when new nodes are added to the DOM.
297     * 
298     * @param {Node} root - The root node of the subtree (usually an Element)
299     */
300    function processTree(root) {
301        if (!root || root.nodeType !== 1) return;
302        // Process root itself
303        processElement(root);
304        // Process descendants
305        if (root.querySelectorAll) {
306            root.querySelectorAll('[data-sf-style]').forEach(processElement);
307        }
308    }
309
310    /**
311       * MutationObserver callback - reacts to DOM changes.
312       * 
313       * Handles:
314       * New nodes being added (childList mutations)
315       * Changes to data-sf-style attribute
316       */
317    const observer = new MutationObserver(mutationsList => {
318        for (const mutation of mutationsList) {
319            if (mutation.type === 'childList') {
320                // New nodes added
321                mutation.addedNodes.forEach(node => processTree(node));
322            } else if (mutation.type === 'attributes') {
323                const element = mutation.target;
324                if (mutation.attributeName === DATA_STYLE_ATTR) {
325                    const styleValue = element.getAttribute(DATA_STYLE_ATTR);
326                    // Attribute removed
327                    if (styleValue === null) {
328                        // To ensure if its not called from removal of data attr we intentionally did
329                        if (!element.isDataAttrRemoved)
330                            clearSfStyle(element, true);
331                    } else {
332                        // Attribute changed/added
333                        applySfStyle(element, styleValue, true);
334                    }
335                } else if (mutation.attributeName === 'style') {
336                    if (element.hasAttribute(DATA_STYLE_ATTR)) {
337                        // data-sf-style still present: re-apply our styles to prevent visible flicker.
338                        // JS-override detection inside applySfStyle guards individual properties.
339                        applySfStyle(element, element.getAttribute(DATA_STYLE_ATTR), true);
340                    } else if (element.sfAppliedStyleMap !== undefined) {
341                        // data-sf-style already removed by sf-utils, but component JS just changed
342                        // the inline style. Sync the cache to the current live values so that
343                        // when Blazor re-renders and re-adds data-sf-style, applySfStyle correctly
344                        // identifies JS-overridden properties and leaves them untouched.
345                        const liveMap = {};
346                        Object.keys(element.sfAppliedStyleMap).forEac
346h(prop => {
347                            liveMap[prop] = {
348                                value: element.style.getPropertyValue(prop),
349                                priority: element.style.getPropertyPriority(prop)
350                            };
351                        });
352                        element.sfAppliedStyleMap = liveMap;
353                        // Keep sfAppliedStyleText unchanged — it still reflects the last
354                        // data-sf-style string so applySfStyle can detect a re-stamp.
355                    }
356                }
357            }
358        }
359    });
360
361    // Observe the entire document for subtree changes, new nodes, and specific attributes
362    observer.observe(document.documentElement, {
363        subtree: true,
364        childList: true,
365        attributes: true,
366        // Limited attribute monitoring to only what we need
367        attributeFilter: [DATA_STYLE_ATTR, 'style'] // narrow filter for performance
368    });
369
370    // Process the existing DOM once at startup
371    processTree(document.documentElement);
372
373    /**
374       * Public API exposed on window.SfStyleWatcher
375       * 
376       * Useful for manual refresh
377       */
378    window.SfStyleWatcher = {
379        refresh(root = document.documentElement) {
380            processTree(root);
381        },
382        flush() {
383            flushScheduled();
384        },
385        // Stop observing mutations
386        disconnect() {
387            observer.disconnect();
388        }
389    };
390})();

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.