PageSourceSearch

https://hanahanbang.co.kr/js/block/textcount.1791261834.js

js hanahanbang.co.kr collected 2026-10-07 13:19:13 UTC 68,050 bytes, 1,951 lines download raw bytes

1(function(window, document, $) {
2    if(window.clTextCount) return;
3
4    var observer = null;
5    var domObserver = null;
6    var domSyncTimer = null;
7    var domSyncRoots = [];
8    var localLayoutRefreshTimer = null;
9    var localLayoutRefreshBound = false;
10    var fallbackBound = false;
11    var fallbackTimer = null;
12    var repeatOnScroll = false;
13    var textCountStyleInjected = false;
14    var requestFrame = window.requestAnimationFrame || function(callback) {
15        return setTimeout(callback, 16);
16    };
17    var cancelFrame = window.cancelAnimationFrame || function(frame) {
18        clearTimeout(frame);
19    };
20
21    function normalizeNumber(value, fallback) {
22        value = parseInt(value, 10);
23        if(isNaN(value)) return fallback;
24        return value;
25    }
26
27    function parseNumberText(text, fallback) {
28        if(typeof text != 'string') return fallback;
29
30        text = text.replace(/[,.]/g, '').replace(/\s/g, '');
31        if(!/^-?\d+$/.test(text)) return fallback;
32
33        return normalizeNumber(text, fallback);
34    }
35
36    function getStyle(el, options) {
37        var style = options && typeof options.style != 'undefined' ? options.style : el.getAttribute('data-style');
38
39        style = parseInt(style, 10);
40        if(isNaN(style) || style < 1 || style > 3) return 1;
41
42        return style;
43    }
44
45    function getDuration(el, options) {
46        var delay = options && typeof options.delay != 'undefined' ? options.delay : el.getAttribute('data-delay');
47
48        delay = parseFloat(delay);
49        if(isNaN(delay)) delay = 1;
50        if(delay < 0) delay = 0;
51        if(delay > 20) delay = 20;
52
53        return delay * 1000;
54    }
55
56    function getRepeatOption(options) {
57        if(options && typeof options.repeat != 'undefined') return options.repeat === true;
58        return repeatOnScroll;
59    }
60
61    function addGroupSeparator(value, separator) {
62        value = String(value);
63        return value.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
64    }
65
66    function addComma(value) {
67        return addGroupSeparator(value, ',');
68    }
69
70    function formatWithTargetDecimalPoints(value, targetText) {
71        var parts = String(targetText || '').replace(/^-/, '').replace(/,/g, '').split('.');
72        var formatted = [];
73        var decimalPlaces = 0;
74        var partLength;
75        var i;
76
77        value = String(value);
78        for(i = 1; i < parts.length; i++) decimalPlaces += parts[i].length;
79        while(value.length <= decimalPlaces) value = '0' + value;
80
81        for(i = parts.length - 1; i > 0; i--) {
82            partLength = parts[i].length;
83            formatted.unshift(partLength ? value.slice(-partLength) : '');
84            if(partLength) value = value.slice(0, -partLength);
85        }
86
87        formatted.unshift(targetText.indexOf(',') > -1 ? addComma(value) : value);
88        return formatted.join('.');
89    }
90
91    function formatWithTargetSeparators(value, targetText) {
92        targetText = String(targetText || '').replace(/\s/g, '');
93
94        if(targetText.indexOf('.') > -1) return formatWithTargetDecimalPoints(value, targetText);
95        if(targetText.indexOf(',') > -1) return addComma(value);
96
97        return String(value);
98    }
99
100    function getDigitLength(text) {
101        var digits = String(text || '').replace(/[^\d]/g, '');
102        return digits.length;
103    }
104
105    function formatValue(value, targetText) {
106        var negative = value < 0;
107        var text = String(Math.abs(Math.round(value)));
108        var targetLength = getDigitLength(targetText);
109        var targetDigits = String(targetText || '').replace(/[^\d]/g, '');
110        var useLeadingZero = targetDigits.length > 1 && targetDigits.charAt(0) == '0';
111
112        if(useLeadingZero && text.length < targetLength) {
113            while(text.length < targetLength) text = '0' + text;
114        }
115
116        text = formatWithTargetSeparators(text, targetText);
117
118        return (negative ? '-' : '') + text;
119    }
120
121    function restoreOriginalContent(el) {
122        if(!el) return;
123
124        if(typeof el._clTextCountOriginalHTML != 'undefined') {
125            el.innerHTML = el._clTextCountOriginalHTML;
126        } else if(typeof el._clTextCountOriginalText != 'undefined') {
127            el.textContent = el._clTextCountOriginalText;
128        }
129    }
130
131    function getTextCountCountUpLineContainer(el) {
132        var target = el;
133        var computed;
134
135        while(target && target.nodeType == 1 && target != document.body && target != document.documentElement) {
136            computed = window.getComputedStyle ? window.getComputedStyle(target, null) : null;
137            if(computed && /^(block|list-item|table-cell|flex|grid)$/.test(computed.display || '')) return target;
138            target = target.parentNode;
139        }
140
141        return el && el.parentNode && el.parentNode.nodeType == 1 ? el.parentNode : null;
142    }
143
144    function findTextCountCountUpWrapPoint(el) {
145        var container = getTextCountCountUpLineContainer(el);
146        var rects = el && el.getClientRects ? el.getClientRects() : null;
147        var previousRect = rects && rects.length ? rects[rects.length - 1] : null;
148        var walker;
149        var node;
150        var range;
151        var rect;
152        var fragment;
153        var textCount;
154        var threshold;
155        var i;
156
157        if(!container || !previousRect || !document.createTreeWalker) return null;
158
159        walker = document.createTreeWalker(container, window.NodeFilter ? window.NodeFilter.SHOW_TEXT : 4, null, false);
160        while((node = walker.nextNode())) {
161            if(el.contains(node) || !(el.compareDocumentPosition(node) & 4)) continue;
162            if(getTextCountCountUpLineContainer(node.parentNode) != container) continue;
163
164            for(i = 0; i < node.nodeValue.length; i++) {
165                if(/[\s\u200B-\u200D\uFEFF]/.test(node.nodeValue.charAt(i))) continue;
166
167                try {
168                    range = document.createRange();
169                    range.setStart(node, i);
170                    range.setEnd(node, i + 1);
171                    rects = range.getClientRects();
172                    rect = rects && rects.length ? rects[0] : null;
173                } catch(e) {
174                    rect = null;
175                }
176                if(!rect) continue;
177
178                threshold = Math.max(3, Math.min(previousRect.height || 0, rect.height || 0
178) * 0.6);
179                if(rect.bottom - previousRect.bottom > threshold) {
180                    try {
181                        range = document.createRange();
182                        range.setStartAfter(el);
183                        range.setEnd(node, i);
184                        fragment = range.cloneContents();
185                        if(fragment.querySelector && fragment.querySelector('br')) return null;
186                    } catch(e) {}
187
188                    // A following counter is one inline unit; never insert a break inside it.
189                    textCount = closestElement(node.parentNode, '.textCount');
190                    return {
191                        container: container,
192                        node: textCount ? (getTextCountLocalHost(textCount) || textCount) : node,
193                        offset: textCount ? 0 : i
194                    };
195                }
196
197                previousRect = rect;
198            }
199        }
200
201        return null;
202    }
203
204    function restoreTextCountCountUpWrap(el) {
205        var state;
206
207        if(!el || !el._clTextCountCountUpWrapState) return;
208
209        state = el._clTextCountCountUpWrapState;
210        el._clTextCountCountUpWrapState = null;
211        state.users = Math.max(0, (state.users || 1) - 1);
212        if(state.users) return;
213
214        if(state.breakNode && state.breakNode.parentNode) state.breakNode.parentNode.removeChild(state.breakNode);
215        if(state.leftNode && state.rightNode && state.leftNode.parentNode && state.leftNode.parentNode == state.rightNode.parentNode) {
216            state.leftNode.nodeValue += state.rightNode.nodeValue;
217            state.rightNode.parentNode.removeChild(state.rightNode);
218        }
219        if(state.container && state.container._clTextCountCountUpWrapState == state) {
220            state.container._clTextCountCountUpWrapState = null;
221        }
222    }
223
224    function preserveTextCountCountUpWrap(el) {
225        var container;
226        var point;
227        var state;
228        var rightNode;
229        var breakNode;
230
231        if(!el) return false;
232
233        restoreTextCountCountUpWrap(el);
234        container = getTextCountCountUpLineContainer(el);
235        if(!container) return false;
236
237        state = container._clTextCountCountUpWrapState;
238        if(state) {
239            state.users++;
240            el._clTextCountCountUpWrapState = state;
241            return true;
242        }
243
244        point = findTextCountCountUpWrapPoint(el);
245        if(!point) return false;
246
247        rightNode = point.offset > 0 ? point.node.splitText(point.offset) : point.node;
248        breakNode = document.createElement('br');
249        rightNode.parentNode.insertBefore(breakNode, rightNode);
250
251        state = {
252            container: point.container,
253            breakNode: breakNode,
254            leftNode: point.offset > 0 ? point.node : null,
255            rightNode: point.offset > 0 ? rightNode : null,
256            users: 1
257        };
258        point.container._clTextCountCountUpWrapState = state;
259        el._clTextCountCountUpWrapState = state;
260        return true;
261    }
262
263    function clearRunning(el, restore) {
264        if(!el) return;
265
266        clearTextCountLocalTimer(el);
267
268        if(el._clTextCountDelayTimer) {
269            clearTimeout(el._clTextCountDelayTimer);
270            el._clTextCountDelayTimer = null;
271            el._clTextCountSlideEffectDelayPending = false;
272        }
273
274        if(el._clTextCountFrame) {
275            cancelFrame(el._clTextCountFrame);
276            el._clTextCountFrame = null;
277        }
278
279        if(el._clTextCountTimer) {
280            clearTimeout(el._clTextCountTimer);
281            el._clTextCountTimer = null;
282        }
283
284        restoreTextCountCountUpWrap(el);
285        if(restore) restoreOriginalContent(el);
286
287        el._clTextCountSegments = null;
288    }
289
290    function finish(el) {
291        var text;
292
293        if(!el) return;
294
295        clearRunning(el, false);
296
297        if(el._clTextCountLocalHost || (el.parentNode && $(el.parentNode).hasClass('cl-text-count-local-host'))) {
298            text = (el.textContent || '').replace(/[\u200B-\u200D\uFEFF]/g, '').trim();
299            if(parseNumberText(text, null) !== null) {
300                renderTextCountLocal(el, {
301                    style: getStyle(el),
302                    animate: false
303                });
304            }
305            el._clTextCountOriginalHTML = undefined;
306            el._clTextCountOriginalText = undefined;
307            return;
308        }
309
310        restoreOriginalContent(el);
311
312        el._clTextCountOriginalHTML = undefined;
313        el._clTextCountOriginalText = undefined;
314    }
315
316    function prepareTextCountCountUpSegments(el) {
317        var walker;
318        var nodes = [];
319        var lengths = [];
320        var node;
321        var value;
322
323        if(!el || !document.createTreeWalker) return null;
324
325        walker = document.createTreeWalker(el, window.NodeFilter ? window.NodeFilter.SHOW_TEXT : 4, null, false);
326        while((node = walker.nextNode())) {
327            value = String(node.nodeValue || '').replace(/[\u200B-\u200D\uFEFF]/g, '');
328            if(value === '') continue;
329            nodes.push(node);
330            lengths.push(value.length);
331        }
332
333        if(nodes.length < 2) return null;
334
335        return {
336            nodes: nodes,
337            lengths: lengths
338        };
339    }
340
341    function setTextCountSegmentText(el, text) {
342        var state = el && el._clTextCountSegments;
343        var offset = 0;
344        var length;
345        var i;
346
347        if(!state || !state.nodes || state.nodes.length < 2) return false;
348
349        for(i = 0; i < state.nodes.length; i++) {
350            if(!el.contains(state.nodes[i])) return false;
351            length = i == state.nodes.length - 1 ? text.length - offset : state.lengths[i];
352            state.nodes[i].nodeValue = text.substr(offset, Math.max(0, length));
353            offset += Math.max(0, length);
354        }
355
356        return true;
357    }
358
359    function setText(el, text) {
360        var target;
361
362        if(setTextCountSegmentText(el, text)) return;
363        target = getTextCountAnimationContentTarget(el);
364        (target || el).textContent = text;
365    }
366
367    function injectTextCountStyle() {
368        var style;
369        var head;
370
371        if(textCountStyleInjected || document.getElementById('clTextCountStyle')) return;
372
373        head = document.getElementsByTagName('head')[0];
374        if(!head) return;
375
376        style = document.createElement('style');
377        style.id = 'clTextCountStyle';
378        style.type = 'text/css';
379        style.appendChild(document.createTextNode(
380            '.cl-text-count-local-host{position:relative;display:inline-block;box-sizing:content-box;white-space:pre;vertical-align:baseline;}' +
381            '.cl-text-count-local-host>.cl-text-count-local-source{position:relative;z-index:1;display:inline-block;width:100%;min-width:100%;max-width:100%;box-sizing:border-box;white-space:pre;caret-color:var(--cl-text-count-local-color,currentColor);}' +
382            '.cl-text-count-local-host>.cl-text-count-local-source,.cl-text-count-local-host>.cl-text-count-local-source *{color:transparent!important;-webkit-text-fill-color:transparent!important;text-shadow:none!important;text-decoration-color:transparent!important;}' +
383            '.cl-text-count-local-host>.cl-text-count-local-source::selection,.cl-text-count-local-host>.cl-text-count-local-source *::selection{color:transparent!important;-webkit-text-fill-color:transparent!important;background:rgba(71,137,231,.35)!important;}' +
384            '.cl-text-count-local-host>.cl-text-count-local-source::-moz-selection,.cl-text-count-local-host>.cl-text-count-local-source *::-moz-selection{color:transparent!important;background:rgba(71,137,231,.35)!important;}' +
385            '.cl-text-count-local-host.cl-text-count-local-editing>.cl-text-count-local-source,.cl-text-count-local-host.cl-text-count-local-editing>.cl-text-count-local-source *{color:var(--cl-text-count-local-color,currentColor)!important;-webkit-text-fill-color:var(--cl-text-count-local-color,currentColor)!important;}' +
386            '.cl-text-count-local-host.cl-text-count-local-editing>.cl-text-count-local-source::selection,.cl-text-count-local-host.cl-text-count-local-editing>.cl-text-count-local-source *::selection{color:var(--cl-text-count-local-color,currentColor)!important;-webkit-text-fill-color:var(--cl-text-count-local-color,currentColor)!important;}' +
387            '.cl-text-count-local-host.cl-text-count-local-editing>.cl-text-count-local-view{visibility:hidden;}
387' +
388            '.cl-text-count-local-host.cl-text-count-local-selected::before{content:"";position:absolute;z-index:0;inset:0;background:rgba(71,137,231,.35);pointer-events:none;}' +
389            '.cl-text-count-local-host.cl-text-count-local-selected>.cl-text-count-local-source::selection{background:transparent!important;}' +
390            '.cl-text-count-local-host.cl-text-count-local-selected>.cl-text-count-local-source::-moz-selection{background:transparent!important;}' +
391            '.cl-text-count-local-view{position:absolute;z-index:2;left:0;top:0;display:inline-flex;align-items:baseline;width:100%;height:100%;overflow:visible;pointer-events:none;user-select:none;-webkit-user-select:none;white-space:pre;}' +
392            '.cl-text-count-local-slot{position:relative;display:inline-block;flex:0 0 auto;height:var(--cl-text-count-local-height,1em);line-height:var(--cl-text-count-local-height,inherit);overflow:hidden;text-align:center;vertical-align:baseline;}' +
393            '.cl-text-count-local-track{display:block;transform:translateY(0);will-change:transform;}' +
394            '.cl-text-count-local-value{display:block;height:var(--cl-text-count-local-height,1em);line-height:var(--cl-text-count-local-height,inherit);text-align:center;white-space:pre;}'
395        ));
396        head.appendChild(style);
397        textCountStyleInjected = true;
398    }
399
400    function isTextCountFormattingElement(el) {
401        var tagName = el && el.nodeName ? el.nodeName.toLowerCase() : '';
402        var styleText = el && el.getAttribute ? (el.getAttribute('style') || '').toLowerCase() : '';
403
404        return /^(em|i|u|s|strike|strong|b)$/.test(tagName) ||
405            (tagName == 'span' && /(font-style\s*:\s*italic|font-weight\s*:\s*(bold|[6-9]00)|text-decoration[^;]*(underline|line-through))/.test(styleText));
406    }
407
408    function normalizeTextCountText(text) {
409        return String(text || '').replace(/[\u200B-\u200D\uFEFF]/g, '').replace(/\s/g, '');
410    }
411
412    function getSingleMeaningfulElementChild(el) {
413        var child;
414        var target = null;
415        var i;
416
417        if(!el || !el.childNodes) return null;
418
419        for(i = 0; i < el.childNodes.length; i++) {
420            child = el.childNodes[i];
421
422            if(child.nodeType == 1) {
423                if(child.className && String(child.className).indexOf('fr-marker') > -1) continue;
424                if(target) return null;
425                target = child;
426            } else if(child.nodeType == 3) {
427                if(normalizeTextCountText(child.nodeValue) !== '') return null;
428            } else if(child.nodeType != 8) {
429                return null;
430            }
431        }
432
433        return target;
434    }
435
436    function getTextCountAnimationContentTarget(el) {
437        var baseText = normalizeTextCountText(el && el.textContent);
438        var current = el;
439        var target = null;
440        var child;
441
442        if(baseText === '' || parseNumberText(baseText, null) === null) return null;
443
444        while(current) {
445            child = getSingleMeaningfulElementChild(current);
446            if(!isTextCountFormattingElement(child)) break;
447            if(normalizeTextCountText(child.textContent) !== baseText) break;
448
449            target = child;
450            current = child;
451        }
452
453        return target;
454    }
455
456    function measureTextCountRollHeight(el, style) {
457        var measure;
458        var rect;
459        var offsetHeight;
460        var text;
461
462        if(!el || !document.body) return 0;
463
464        measure = document.createElement('span');
465        text = el.textContent || '8';
466
467        measure.textContent = text;
468        measure.style.position = 'absolute';
469        measure.style.left = '-99999px';
470        measure.style.top = '-99999px';
471        measure.style.visibility = 'hidden';
472        measure.style.whiteSpace = 'pre';
473        measure.style.pointerEvents = 'none';
474        measure.style.display = 'inline-block';
475        measure.style.transform = 'none';
476        measure.style.webkitTransform = 'none';
477
478        if(style) {
479            measure.style.font = style.font;
480            measure.style.fontFamily = style.fontFamily;
481            measure.style.fontSize = style.fontSize;
482            measure.style.fontStyle = style.fontStyle;
483            measure.style.fontWeight = style.fontWeight;
484            measure.style.fontStretch = style.fontStretch;
485            measure.style.lineHeight = style.lineHeight;
486            measure.style.letterSpacing = style.letterSpacing;
487            measure.style.wordSpacing = style.wordSpacing;
488            measure.style.textTransform = style.textTransform;
489        }
490
491        document.body.appendChild(measure);
492        rect = measure.getBoundingClientRect ? measure.getBoundingClientRect() : null;
493        offsetHeight = measure.offsetHeight;
494        document.body.removeChild(measure);
495
496        if(rect && rect.height) return Math.ceil(rect.height);
497
498        return offsetHeight ? Math.ceil(offsetHeight) : 0;
499    }
500
501    function getTextCountRollHeight(el) {
502        var style = window.getComputedStyle ? window.getComputedStyle(el, null) : null;
503        var lineHeight = style ? parseFloat(style.lineHeight) : 0;
504        var fontSize = style ? parseFloat(style.fontSize) : 0;
505        var measuredHeight;
506        var rect;
507
508        measuredHeight = measureTextCountRollHeight(el, style);
509        if(measuredHeight) return measuredHeight;
510
511        rect = el && el.getBoundingClientRect ? el.getBoundingClientRect() : null;
512        if(rect && rect.height) return Math.ceil(rect.height);
513
514        if(fontSize && !isNaN(fontSize)) return Math.ceil(fontSize);
515
516        if(lineHeight && !isNaN(lineHeight)) return Math.ceil(lineHeight);
517
518        return 16;
519    }
520
521    function closestElement(el, selector) {
522        while(el && el.nodeType == 1) {
523            if(el.matches && el.matches(selector)) return el;
524            el = el.parentNode;
525        }
526
527        return null;
528    }
529
530    function getShowcaseSlideEffectTarget(el, block, slideTarget) {
531        var effect = block ? block.getAttribute('data-slide-effect') : '';
532        var slideCase = block ? block.getAttribute('data-case') : '';
533        var caption;
534        var current;
535        var zoomTarget;
536
537        if(!slideTarget) return null;
538
539        if(effect == 'up' && (slideCase == 'slideshow72' || slideCase == 'slideshow64')) {
540            caption = closestElement(el, '.carousel-caption');
541            if(caption && block.contains(caption)) return caption;
542        }
543
544        if(effect == 'zoomin' || effect == 'zoomout') {
545            zoomTarget = slideTarget.querySelector ? slideTarget.querySelector('.zoomAnimation') : null;
546            if(zoomTarget) return zoomTarget;
547        }
548
549        current = el;
550        while(current && current.parentNode && current.parentNode !== slideTarget) {
551            current = current.parentNode;
552        }
553
554        if(current && current.parentNode === slideTarget) return current;
555
556        return slideTarget.children && slideTarget.children.length ? slideTarget.children[0] : slideTarget;
557    }
558
559    function getRunningAnimationWait(el) {
560        var animations;
561        var timing;
562        var currentTime;
563        var remaining;
564        var wait = 0;
565        var i;
566
567        if(!el || !el.getAnimations) return null;
568
569        animations = el.getAnimations();
570        for(i = 0; i < animations.length; i++) {
571            if(animations[i].playState == 'finished' || animations[i].playState == 'idle') continue;
572
573            timing = animations[i].effect && animations[i].effect.getComputedTiming ? animations[i].effect.getComputedTiming() : null;
574            currentTime = typeof animations[i].currentTime == 'number' ? animations[i].currentTime : 0;
575            remaining = timing && typeof timing.endTime == 'number' ? timing.endTime - currentTime : 0;
576
577            if(remaining > wait) wait = remaining;
578        }
579
580        return wait;
581    }
582
583    function parseCSSTimeList(value) {
584        var parts = String(value || '').split(',');
585        var max = 0;
586        var item;
587        var time;
588        var i;
589
590        for(i = 0; i < parts.length; i++) {
591            item = $.trim(parts[i]);
592            if(!item) continue;
593
594            time = parseFloat(item);
595            if(isNaN(time)) continue;
596
597            if(item.indexOf('ms') > -1) time = time;
598            else time = time * 1000;
599
600            if(time > max) max = time;
601        }
602
603        return max;
604    }
605
606    function getStyleAnimationWait(el) {
607        var style;
608
609        if(!el || !window.getComputedStyle) return 0;
610
611        style = window.getComputedStyle(el, null);
612        if(!style || !style.animationName || style.animationName == 'none') return 0;
613
614        return parseCSSTimeList(style.animationDelay) + parseCSSTimeList(style.animationDuration);
615    }
616
617    function getShowcaseSlideEffectWait(el, options) {
618        var block;
619        var slideTarget;
620        var animatedTarget;
621        var blockWait;
622        var targetWait;
623        var fallbackWait;
624        var wait;
625
626        if(options && (options.preview || options.skipSlideEffectDelay)) return 0;
627        if(el._clTextCountSlideEffectDelayDone || el._clTextCountSlideEffectDelayPending) return 0;
628
629        block = closestElement(el, '[data-type="showcase"][data-slide-effect]');
630        if(!block || !block.getAttribute('data-slide-effect')) return 0;
631        if(block.classList && block.classList.contains('slide-effect-aos-ready')) return 50;
632
633        slideTarget = closestElement(el, '.slide-effect-target');
634        if(!slideTarget || !block.contains(slideTarget)) return 0;
635
636        animatedTarget = getShowcaseSlideEffectTarget(el, block, slideTarget);
637        targetWait = getRunningAnimationWait(animatedTarget);
638        blockWait = getRunningAnimationWait(block);
639
640        if(targetWait !== null || blockWait !== null) {
641            wait = Math.max(targetWait || 0, blockWait || 0);
642            return wait > 0 ? wait + 80 : 0;
643        }
644
645        fallbackWait = Math.max(getStyleAnimationWait(animatedTarget), getStyleAnimationWait(block));
646
647        return fallbackWait > 0 ? fallbackWait + 80 : 0;
648    }
649
650    function scheduleTextCountRun(el, options, wait) {
651        el._clTextCountSlideEffectDelayPending = true;
652        el._clTextCountDelayTimer = setTimeout(function() {
653            var block = closestElement(el, '[data-type="showcase"][data-sli
653de-effect]');
654            var delayedOptions = $.extend({}, options);
655
656            if(!block || (!block.getAnimations && !block.classList.contains('slide-effect-aos-ready'))) delayedOptions.skipSlideEffectDelay = true;
657
658            el._clTextCountDelayTimer = null;
659            el._clTextCountSlideEffectDelayPending = false;
660
661            if(getRepeatOption(options) && !isInView(el)) {
662                el._clTextCountInView = false;
663                return;
664            }
665
666            run(el, delayedOptions);
667        }, wait);
668    }
669
670    function buildTextCountRollingSequence(start, target, count, direction) {
671        var pool = '0123456789';
672        var startIndex = pool.indexOf(start);
673        var targetIndex = pool.indexOf(target);
674        var sequence = [];
675        var total;
676        var i;
677
678        if(startIndex < 0 || targetIndex < 0) {
679            sequence.push(target);
680            return sequence;
681        }
682
683        if(direction == 'down') {
684            total = count * pool.length + ((targetIndex - startIndex + pool.length) % pool.length);
685            sequence.push(target);
686            for(i = 1; i <= total; i++) {
687                sequence.push(pool.charAt(((targetIndex - i) % pool.length + pool.length) % pool.length));
688            }
689            return sequence;
690        }
691
692        total = count * pool.length + ((targetIndex - startIndex + pool.length) % pool.length);
693        sequence.push(start);
694        for(i = 1; i <= total; i++) {
695            sequence.push(pool.charAt((startIndex + i) % pool.length));
696        }
697
698        return sequence;
699    }
700
701    function buildTextCountOdometerSequence(start, target, index) {
702        var pool = '0123456789';
703        var startIndex = pool.indexOf(start);
704        var targetIndex = pool.indexOf(target);
705        var rounds = 2 + (index % 3);
706        var sequence = [];
707        var total;
708        var i;
709
710        if(startIndex < 0 || targetIndex < 0) {
711            sequence.push(target);
712            return sequence;
713        }
714
715        total = rounds * pool.length + ((targetIndex - startIndex + pool.length) % pool.length);
716        sequence.push(start);
717        for(i = 1; i <= total; i++) {
718            sequence.push(pool.charAt((startIndex + i) % pool.length));
719        }
720
721        return sequence;
722    }
723
724    function easeTextCountProgress(progress) {
725        return 1 - Math.pow(1 - progress, 3);
726    }
727
728    function getTextCountStepTime(step, totalSteps, duration) {
729        var ratio;
730
731        if(step <= 0) return 0;
732        if(step >= totalSteps) return duration;
733
734        ratio = step / totalSteps;
735        return (1 - Math.pow(1 - ratio, 1 / 3)) * duration;
736    }
737
738    function animate(el, start, target, duration, render) {
739        var startTime = Date.now();
740
741        if(duration <= 0) {
742            finish(el);
743            return;
744        }
745
746        function tick() {
747            var progress = Math.min(1, (Date.now() - startTime) / duration);
748            var value = start + ((target - start) * easeTextCountProgress(progress));
749
750            render(progress, value);
751
752            if(progress < 1) {
753                el._clTextCountFrame = requestFrame(tick);
754            } else {
755                finish(el);
756            }
757        }
758
759        render(0, start);
760        el._clTextCountFrame = requestFrame(tick);
761    }
762
763    function runCountUp(el, start, target, targetText, duration) {
764        var totalSteps = Math.abs(target - start);
765        var direction = target >= start ? 1 : -1;
766        var startedAt;
767        var step = 0;
768
769        if(totalSteps > 0 && totalSteps <= 120 && duration > 0) {
770            startedAt = Date.now();
771            setText(el, formatValue(start, targetText));
772
773            function next() {
774                var wait;
775
776                step++;
777                if(step >= totalSteps) {
778                    setText(el, targetText);
779                    el._clTextCountTimer = setTimeout(function() {
780                        finish(el);
781                    }, 120);
782                    return;
783                }
784
785                setText(el, formatValue(start + (direction * step), targetText));
786                wait = getTextCountStepTime(step + 1, totalSteps, duration) - (Date.now() - startedAt);
787                el._clTextCountTimer = setTimeout(next, Math.max(0, wait));
788            }
789
790            el._clTextCountTimer = setTimeout(next, Math.max(0, getTextCountStepTime(1, totalSteps, duration)));
791            return;
792        }
793
794        animate(el, start, target, duration, function(progress, value) {
795            setText(el, progress >= 1 ? targetText : formatValue(value, targetText));
796        });
797    }
798
799    function clearTextCountLocalTimer(el) {
800        if(!el || !el._clTextCountLocalTimer) return;
801
802        clearTimeout(el._clTextCountLocalTimer);
803        el._clTextCountLocalTimer = null;
804    }
805
806    function getTextCountLocalMetrics(el, text) {
807        var wasHidden = $(el).hasClass('cl-text-count-local-source');
808        var styleSource;
809        var computed;
810        var measure;
811        var rect;
812        var widths = {};
813        var widestDigit = 0;
814        var totalWidth = 0;
815        var itemHeight;
816        var props = [
817            'font-family',
818            'font-size',
819            'font-style',
820            'font-weight',
821            'font-stretch',
822            'font-variant',
823            'font-kerning',
824            'font-feature-settings',
825            'font-variation-settings',
826            'text-transform',
827            'text-decoration',
828            'text-decoration-color',
829            'text-decoration-line',
830            'text-decoration-style',
831            'text-decoration-thickness',
832            'text-underline-offset',
833            'text-shadow',
834            'line-height',
835            'color'
836        ];
837        var textStyle = {};
838        var chars = '0123456789' + text;
839        var ch;
840        var i;
841
842        if(!el || !document.body || !window.getComputedStyle) return null;
843
844        if(wasHidden) $(el).removeClass('cl-text-count-local-source');
845        styleSource = getTextCountAnimationContentTarget(el) || el;
846        computed = window.getComputedStyle(styleSource, null);
847        itemHeight = getTextCountRollHeight(styleSource);
848
849        measure = document.createElement('span');
850        measure.style.position = 'absolute';
851        measure.style.left = '-99999px';
852        measure.style.top = '-99999px';
853        measure.style.visibility = 'hidden';
854        measure.style.pointerEvents = 'none';
855        measure.style.whiteSpace = 'pre';
856        measure.style.letterSpacing = '0';
857
858        for(i = 0; i < props.length; i++) {
859            textStyle[props[i]] = computed.getPropertyValue(props[i]);
860            measure.style.setProperty(props[i], textStyle[props[i]]);
861        }
862
863        document.body.appendChild(measure);
864        for(i = 0; i < chars.length; i++) {
865            ch = chars.charAt(i);
866            if(typeof widths[ch] != 'undefined') continue;
867
868            measure.textContent = ch;
869            rect = measure.getBoundingClientRect();
870            widths[ch] = rect && rect.width ? rect.width : 0;
871            if(/\d/.test(ch)) widestDigit = Math.max(widestDigit, widths[ch]);
872        }
873        document.body.removeChild(measure);
874
875        if(wasHidden) $(el).addClass('cl-text-count-local-source');
876        if(!widestDigit) return null;
877
878        for(i = 0; i < text.length; i++) {
879            ch = text.charAt(i);
880            totalWidth += /\d/.test(ch) ? widestDigit : (widths[ch] || 0);
881        }
882
883        return {
884            digitWidth: widestDigit,
885            totalWidth: totalWidth,
886            itemHeight: itemHeight,
887            widths: widths,
888            textStyle: textStyle
889        };
890    }
891
892    function getTextCountLocalNodeOffset(root, node, offset) {
893        var range;
894
895        if(!root || !node || !document.createRange) return null;
896
897        try {
898            range = document.createRange();
899            range.selectNodeContents(root);
900            range.setEnd(node, offset);
901            return range.toString().length;
902        } catch(e) {
903            return null;
904        }
905    }
906
907    function getTextCountLocalSelectionBookmark(el) {
908        var selection = window.getSelection ? window.getSelection() : null;
909        var range;
910        var start;
911        var end;
912        var startsInside;
913        var endsInside;
914
915        if(!selection || !selection.rangeCount || !el) return null;
916
917        range = selection.getRangeAt(0);
918        startsInside = el == range.startContainer || el.contains(range.startContainer);
919        endsInside = el == range.endContainer || el.contains(range.endContainer);
920        if(!startsInside && !endsInside) return null;
921
922        start = startsInside ? getTextCountLocalNodeOffset(el, range.startContainer, range.startOffset) : null;
923        end = endsInside ? getTextCountLocalNodeOffset(el, range.endContainer, range.endOffset) 
923: null;
924        if((startsInside && start === null) || (endsInside && end === null)) return null;
925
926        return {
927            start: start,
928            end: end,
929            range: range.cloneRange()
930        };
931    }
932
933    function getTextCountLocalOffsetPosition(root, offset) {
934        var walker;
935        var node;
936        var length;
937        var remaining = Math.max(0, offset);
938        var lastText = null;
939
940        if(!root || !document.createTreeWalker) return null;
941
942        walker = document.createTreeWalker(root, window.NodeFilter ? window.NodeFilter.SHOW_TEXT : 4, null, false);
943        while((node = walker.nextNode())) {
944            length = (node.nodeValue || '').length;
945            if(remaining <= length) {
946                return {
947                    node: node,
948                    offset: remaining
949                };
950            }
951            remaining -= length;
952            lastText = node;
953        }
954
955        if(lastText) {
956            return {
957                node: lastText,
958                offset: (lastText.nodeValue || '').length
959            };
960        }
961
962        return {
963            node: root,
964            offset: 0
965        };
966    }
967
968    function restoreTextCountLocalSelectionBookmark(el, bookmark) {
969        var start;
970        var end;
971        var range;
972        var selection;
973
974        if(!el || !bookmark || !document.createRange || !window.getSelection) return false;
975
976        // Keep outside endpoints live while restoring endpoints moved with the source.
977        start = bookmark.start !== null ? getTextCountLocalOffsetPosition(el, bookmark.start) : {
978            node: bookmark.range.startContainer,
979            offset: bookmark.range.startOffset
980        };
981        end = bookmark.end !== null ? getTextCountLocalOffsetPosition(el, bookmark.end) : {
982            node: bookmark.range.endContainer,
983            offset: bookmark.range.endOffset
984        };
985        if(!start || !end) return false;
986
987        try {
988            range = document.createRange();
989            range.setStart(start.node, start.offset);
990            range.setEnd(end.node, end.offset);
991            selection = window.getSelection();
992            selection.removeAllRanges();
993            selection.addRange(range);
994            return true;
995        } catch(e) {
996            return false;
997        }
998    }
999
1000    function ensureTextCountLocalHost(el) {
1001        var host;
1002        var view;
1003        var selectionBookmark;
1004
1005        if(!el || !el.parentNode) return null;
1006
1007        host = el._clTextCountLocalHost;
1008        if(host && host.parentNode && host.contains(el)) {
1009            view = $(host).children('.cl-text-count-local-view').get(0);
1010        } else if($(el.parentNode).hasClass('cl-text-count-local-host')) {
1011            host = el.parentNode;
1012            view = $(host).children('.cl-text-count-local-view').get(0);
1013        } else {
1014            selectionBookmark = getTextCountLocalSelectionBookmark(el);
1015            host = document.createElement('span');
1016            host.className = 'cl-text-count-local-host';
1017            el.parentNode.insertBefore(host, el);
1018            host.appendChild(el);
1019        }
1020
1021        if(!view) {
1022            view = document.createElement('span');
1023            view.className = 'cl-text-count-local-view';
1024            view.setAttribute('aria-hidden', 'true');
1025            view.setAttribute('contenteditable', 'false');
1026            host.appendChild(view);
1027        }
1028
1029        el._clTextCountLocalHost = host;
1030        el._clTextCountLocalView = view;
1031        if(selectionBookmark) restoreTextCountLocalSelectionBookmark(el, selectionBookmark);
1032
1033        return host;
1034    }
1035
1036    function applyTextCountLocalViewStyle(view, metrics) {
1037        var props = metrics && metrics.textStyle ? metrics.textStyle : {};
1038        var prop;
1039
1040        if(!view) return;
1041
1042        for(prop in props) {
1043            if(!props.hasOwnProperty(prop)) continue;
1044            view.style.setProperty(prop, props[prop]);
1045        }
1046        view.style.letterSpacing = '0';
1047        view.style.setProperty('--cl-text-count-local-height', metrics.itemHeight + 'px');
1048    }
1049
1050    function alignTextCountLocalView(el, host, view) {
1051        var sourceRect;
1052        var hostRect;
1053        var top;
1054        var height;
1055
1056        if(!el || !host || !view || !el.getBoundingClientRect || !host.getBoundingClientRect) return;
1057
1058        if(el.offsetParent === host) {
1059            top = el.offsetTop;
1060            height = el.offsetHeight;
1061        } else {
1062            sourceRect = el.getBoundingClientRect();
1063            hostRect = host.getBoundingClientRect();
1064            top = sourceRect.top - hostRect.top;
1065            height = sourceRect.height;
1066        }
1067
1068        view.style.top = top + 'px';
1069        view.style.height = height + 'px';
1070    }
1071
1072    function applyTextCountLocalValueDecoration(value, textStyle) {
1073        var line = textStyle && textStyle['text-decoration-line'];
1074        var color = textStyle && textStyle['text-decoration-color'];
1075
1076        if(!value || !line || line == 'none') return;
1077
1078        value.style.setProperty('text-decoration-line', line);
1079        value.style.setProperty('text-decoration-color', color || 'currentColor');
1080        value.style.setProperty('text-decoration-style', textStyle['text-decoration-style'] || 'solid');
1081        value.style.setProperty('text-decoration-thickness', textStyle['text-decoration-thickness'] || 'auto');
1082        value.style.setProperty('text-underline-offset', textStyle['text-underline-offset'] || 'auto');
1083    }
1084
1085    function createTextCountLocalValue(ch, textStyle) {
1086        var value = document.createElement('span');
1087
1088        value.className = 'cl-text-count-local-value';
1089        value.setAttribute('data-char', ch);
1090        value.textContent = ch;
1091        applyTextCountLocalValueDecoration(value, textStyle);
1092
1093        return value;
1094    }
1095
1096    function createTextCountLocalSlot(ch, width, textStyle) {
1097        var slot = document.createElement('span');
1098
1099        slot.className = 'cl-text-count-local-slot';
1100        slot.style.width = width + 'px';
1101        slot.appendChild(createTextCountLocalValue(ch, textStyle));
1102
1103        return slot;
1104    }
1105
1106    function renderTextCountLocalStatic(el, text, metrics) {
1107        var host = ensureTextCountLocalHost(el);
1108        var view;
1109        var ch;
1110        var i;
1111
1112        if(!host || !metrics) return false;
1113
1114        view = el._clTextCountLocalView;
1115        clearTextCountLocalTimer(el);
1116        host.style.width = metrics.totalWidth + 'px';
1117        host.style.minWidth = metrics.totalWidth + 'px';
1118        host.style.maxWidth = metrics.totalWidth + 'px';
1119        view.innerHTML = '';
1120        applyTextCountLocalViewStyle(view, metrics);
1121        el.style.setProperty('--cl-text-count-local-color', metrics.textStyle.color || 'currentColor');
1122
1123        for(i = 0; i < text.length; i++) {
1124            ch = text.charAt(i);
1125            view.appendChild(createTextCountLocalSlot(ch, /\d/.test(ch) ? metrics.digitWidth : (metrics.widths[ch] || 0), metrics.textStyle));
1126        }
1127
1128        $(el).addClass('cl-text-count-local-source');
1129        alignTextCountLocalView(el, host, view);
1130        return true;
1131    }
1132
1133    function createTextCountLocalAnimatedSlot(ch, width, sequence, duration, delay, direction, itemHeight, textStyle) {
1134        var slot = document.createElement('span');
1135        var track = document.createElement('span');
1136        var travelOffset = (sequence.length - 1) * itemHeight;
1137        var i;
1138
1139        slot.className = 'cl-text-count-local-slot';
1140        slot.style.width = width + 'px';
1141        track.className = 'cl-text-count-local-track';
1142        track.style.transition = 'transform ' + duration + 'ms cubic-bezier(0.16, 1, 0.3, 1) ' + delay + 'ms';
1143        track._clTextCountLocalFinalTransform = direction == 'down' ? 'translateY(0)' : 'translateY(-' + travelOffset + 'px)';
1144        if(direction == 'down') track.style.transform = 'translateY(-' + travelOffset + 'px)';
1145
1146        for(i = 0; i < sequence.length; i++) {
1147            track.appendChild(createTextCountLocalValue(sequence[i], textStyle));
1148        }
1149
1150        slot.appendChild(track);
1151        return track;
1152    }
1153
1154    function runTextCountLocal(el, style, targetText, duration) {
1155        var metrics = getTextCountLocalMetrics(el, targetText);
1156        var host;
1157        var view;
1158        var tracks = [];
1159        var maxEndTime = 0;
1160        var sequence;
1161        var direction;
1162        var digitIndex = 0;
1163        var digitDelay;
1164        var digitDuration;
1165        var track;
1166        var ch;
1167        var i;
1168
1169        if(!metrics) return false;
1170        if(duration <= 0) return renderTextCountLocalStatic(el, targetText, metrics);
1171
1172        host = ensureTextCountLocalHost(el);
1173        if(!host) return false;
1174
1175        view = el._clTextCountLocalView;
1176        clearTextCountLocalTimer(el);
1177        host.style.width = metrics.totalWidth + 'px';
1178        host.style.minWidth = metrics.totalWidth + 'px';
1179        host.style.maxWidth = metrics.totalWidth + 'px';
1180        view.innerHTML = '';
1181        applyTextCountLocalViewStyle(view, metrics);
1182        el.style.setProperty('--cl-text-count-local-color', metrics.textStyle.color || 'currentColor');
1183        $(el).addClass('cl-text-count-local-source');
1184
1185        for(i = 0; i < targetText.length; i++) {
1186            ch = targetText.charAt(i);
1187            if(!/\d/.test(ch)) {
1188                view.appendChild(createTextCountLocalSlot(ch, metrics.widths[ch] || 0, metrics.textStyle));
1189                continue;
1190            }
1191
1192            if(style == 2) {
1193                direction = digitIndex % 2 == 0 ? 'up' : 'down';
1194                digitDelay = 0;
1195                digitDuration = Math.max(160, duration);
1196                sequence = buildTextCountRollingSequence('0', ch, 2 + (digitIndex % 2), direction);
1197            } else {
1198                direction = 'up';
1199                digitDelay = Math.min(digitIndex * 35, Math.floor(duration * 0.15));
1200                digitDuration = Math.max(160, duration - digitDelay);
1201                sequence = buildTextCountOdometerSequence('0', ch, digitIndex);
1202            }
1203
1204            track = createTextCountLocalAnimatedSlot(ch, metrics.digitWidth, sequence, digitDuration, digitDelay, direction, metrics.itemHeight, metrics.textStyle);
1205            view.appendChild(track.parentNode);
1206            tracks.push(track);
1207            maxEndTime = Math.max(maxEndTime, digitDuration + digitDelay);
1208            digitIndex++;
1209        }
1210
1211        alignTextCountLocalView(el, host, view);
1212
1213        setTimeout(function() {
1214            var i;
1215
1216            for(i = 0; i < tracks.length; i++) {
1217                tracks[i].style.transform = tracks[i]._clTextCountLocalFinalTransform || 'translateY(0)';
1218            }
1219        }, 30);
1220
1221        el._clTextCountLocalTimer = setTimeout(function() {
1222            el._clTextCountLocalTimer = null;
1223            renderTextCountLocalStatic(el, targetText, getTextCountLocalMetrics(el, targetText) || metrics);
1224        }, maxEndTime + 180);
1225
1226        return true;
1227    }
1228
1229    function destroyTextCountLocal(el) {
1230        var host;
1231        var parent;
1232        var selectionBookmark;
1233
1234        if(!el) return false;
1235
1236        clearTextCountLocalTimer(el);
1237        selectionBookmark = getTextCountLocalSelectionBookmark(el);
1238        host = el._clTextCountLocalHost;
1239        if(host && (!host.parentNode || !host.contains(el))) host = null;
1240        if(!host && el.parentNode && $(el.parentNode).hasClass('cl-text-count-local-host')) host = el.parentNode;
1241        $(el).removeClass('cl-text-count-local-source');
1242        if(el.style) {
1243            el.style.removeProperty('--cl-text-count-local-color');
1244            if(!$.trim(el.getAttribute('style') || '')) el.removeAttribute('style');
1245        }
1246
1247        if(host && host.parentNode) {
1248            parent = host.parentNode;
1249            parent.insertBefore(el, host);
1250            parent.removeChild(host);
1251        }
1252
1253        el._clTextCountLocalHost = null;
1254        el._clTextCountLocalView = null;
1255        if(selectionBookmark) restoreTextCountLocalSelectionBookmark(el, selectionBookmark);
1256        return !!host;
1257    }
1258
1259    function getTextCountLocalHost(el) {
1260        var host;
1261
1262        if(!el) return null;
1263
1264        host = el._clTextCountLocalHost;
1265        if(host && host.parentNode && host.contains(el)) return host;
1266        if(el.parentNode && $(el.parentNode).hasClass('cl-text-count-local-host')) return el.parentNode;
1267
1268        return null;
1269    }
1270
1271    function setTextCountLocalEditing(el, editing) {
1272        var host = getTextCountLocalHost(el);
1273
1274        if(!host) return false;
1275
1276        $(host).toggleClass('cl-text-count-local-editing', !!editing);
1277        return true;
1278    }
1279
1280    function isTextCountLocalEditing(el) {
1281        var host = getTextCountLocalHost(el);
1282
1283        return !!(host && $(host).hasClass('cl-text-count-local-editing'));
1284    }
1285
1286    function renderTextCountLocal(el, options) {
1287        var text;
1288        var style;
1289        var duration;
1290
1291        options = options || {};
1292        if(!el) return false;
1293        injectTextCountStyle();
1294
1295        text = (el.textContent || '').replace(/[\u200B-\u200D\uFEFF]/g, '').trim();
1296        if(parseNumberText(text, null) === null) {
1297            destroyTextCountLocal(el);
1298            return false;
1299        }
1300
1301        style = getStyle(el, options);
1302        if(style != 2 && style != 3) {
1303            destroyTextCountLocal(el);
1304            return false;
1305        }
1306
1307        duration = options.animate === false ? 0 : getDuration(el, options);
1308        return runTextCountLocal(el, style, text, duration);
1309    }
1310
1311    function run(el, options) {
1312        var targetText;
1313        var target;
1314        var duration;
1315        var style;
1316        var repeat;
1317        var slideEffectWait;
1318
1319        options = options || {};
1320        if(!el) return false;
1321        repeat = getRepeatOption(options);
1322        if(!options.force && !repeat && el._clTextCountPlayed) return false;
1323        if(el._clTextCountSlideEffectDelayPending && !options.preview && !options.skipSlideEffectDelay) return true;
1324
1325        slideEffectWait = getShowcaseSlideEffectWait(el, options);
1326        if(slideEffectWait > 0) {
1327            scheduleTextCountRun(el, options, slideEffectWait);
1328            return true;
1329        }
1330
1331        clearRunning(el, true);
1332
1333        targetText = (el.textContent || '').replace(/\u200B/g, '').trim();
1334        target = parseNumberText(targetText, null);
1335        if(target === null) return false;
1336
1337        style = getStyle(el, options);
1338        duration = options.animate === false ? 0 : getDuration(el, options);
1339
1340        if(style == 2 || style == 3) {
1341            el._clTextCountOriginalHTML = undefined;
1342            el._clTextCountOriginalText = undefined;
1343            if(!options.preview) {
1344                el._clTextCountPlayed = true;
1345                el._clTextCountSlideEffectDelayDone = true;
1346            }
1347            return renderTextCountLocal(el, options);
1348        }
1349
1350        destroyTextCountLocal(el);
1351
1352        el._clTextCountOriginalHTML = el.innerHTML;
1353        el._clTextCountOriginalText = targetText;
1354        el._clTextCountSegments = prepareTextCountCountUpSegments(el);
1355        if(!options.preview) {
1356            el._clTextCountPlayed = true;
1357            el._clTextCountSlideEffectDelayDone = true;
1358        }
1359
1360        preserveTextCountCountUpWrap(el);
1361        runCountUp(el, 0, target, targetText, duration);
1362
1363        return true;
1364    }
1365
1366    function isInView(el) {
1367        var rect;
1368        var height;
1369        var width;
1370
1371        if(!el || !el.getBoundingClientRect) return false;
1372
1373        rect = el.getBoundingClientRect();
1374        height = window.innerHeight || document.documentElement.clientHeight;
1375        width = window.innerWidth || document.documentElement.clientWidth;
1376
1377        return rect.bottom >= 0 && rect.right >= 0 && rect.top <= height && rect.left <= width;
1378    }
1379
1380    function collect(context) {
1381        var root = context || document;
1382        var targets = [];
1383
1384        if(root.nodeType == 1 && root.matches && root.matches('.textCount')) {
1385            targets.push(root);
1386        }
1387
1388        if(root.querySelectorAll) {
1389            targets = targets.concat(Array.prototype.slice.call(root.querySelectorAll('.textCount')));
1390        }
1391
1392        return targets;
1393    }
1394
1395    function refreshRunningTextCountLocalWidth(el) {
1396        var text;
1397        var metrics;
1398        var host;
1399        var view;
1400        var slots;
1401        var ch;
1402        var i;
1403
1404        if(!el || !el._clTextCountLocalTimer) return false;
1405
1406        text = (el.textContent || '').replace(/[\u200B-\u200D\uFEFF]/g, '').trim();
1407        if(parseNumberText(text, null) === null) return false;
1408
1409        metrics = getTextCountLocalMetrics(el, text);
1410        host = getTextCountLocalHost(el);
1411        view = el._clTextCountLocalView || (host && $(host).children('.cl-text-count-local-view').get(0));
1412        slots = view ? $(view).children('.cl-text-count-local-slot').toArray() : [];
1413        if(!metrics || !host || slots.length != text.length) return false;
1414
1415        host.style.width = metrics.totalWidth + 'px';
1416        host.style.minWidth = metrics.totalWidth + 'px';
1417        host.style.maxWidth = metrics.totalWidth + 'px';
1418
1419        for(i = 0; i < slots.length; i++) {
1420            ch = text.charAt(i);
1421            slots[i].style.width = (/\d/.test(ch) ? metrics.digitWidth : (metrics.widths[ch] || 0)) + 'px';
1422        }
1423
1424        alignTextCountLocalView(el, host, view);
1425        return true;
1426    }
1427
1428    function refreshRenderedTextCountLocals() {
1429        var targets = collect(document);
1430        var target;
1431        var style;
1432        var i;
1433
1434        for(i = 0; i < targets.length; i++) {
1435            target = targets[i];
1436            if(isTextCountActiveEditorTarget(target) || !getTextCountLocalHost(target)) continue;
1437            if(target._clTextCountLocalTimer) {
1438                refreshRunningTextCountLocalWidth(target);
1439                continue;
1440            }
1441
1442            style = getStyle(target);
1443            if(style == 2 || style == 3) {
1444                renderTextCountLocal(target, {
1445                    style: style,
1446                    animate: false
1447                });
1448            } else {
1449                destroyTextCountLocal(target);
1450            }
1451        }
1452    }
1453
1454    function scheduleTextCountLocalLayoutRefresh(delay) {
1455        if(localLayoutRefreshTimer) clearTimeout(localLayoutRefreshTimer);
1456
1457        localLayoutRefreshTimer = setTimeout(function() {
1458            localLayoutRefreshTimer = null;
1459            refreshRenderedTextCountLocals();
1460        }, delay || 0);
1461    }
1462
1463    function bindTextCountLocalLayoutRefresh() {
1464        if(localLayoutRefreshBound) return;
1465
1466        localLayoutRefreshBound = true;
1467        $(window).on('resize.clTextCountLocalLayout', function() {
1468            scheduleTextCountLocalLayoutRefresh(80);
1469        });
1470
1471        if(document.fonts && document.fonts.ready) {
1472            document.fonts.ready.then(function() {
1473                scheduleTextCountLocalLayoutRefresh(0);
1474            });
1475        }
1476        if(document.fonts && document.fonts.addEventListener) {
1477            document.fonts.addEventListener('loadingdone', function() {
1478                scheduleTextCountLocalLayoutRefresh(0);
1479            });
1480        }
1481    }
1482
1483    function checkFallback() {
1484        var targets = collect(document);
1485        var i;
1486        var inView;
1487
1488        for(i = 0; i < targets.length; i++) {
1489            if(isTextCountActiveEditorTarget(targets[i])) continue;
1490
1491            inView = isInView(targets[i]);
1492
1493            if(repeatOnScroll) {
1494                if(inView && !targets[i]._clTextCountInView) {
1495                    targets[i]._clTextCountInView = true;
1496                    run(targets[i], {
1497                        force: true,
1498                        repeat: true
1499                    });
1500                } else if(!inView) {
1501                    targets[i]._clTextCountInView = false;
1502                }
1503            } else if(inView) {
1504                run(targets[i]);
1505            }
1506        }
1507    }
1508
1509    function bindFallback(runNow) {
1510        if(fallbackBound) {
1511            if(runNow) setTimeout(checkFallback, 0);
1512            return;
1513        }
1514
1515        fallbackBound = true;
1516        $(window).on('scroll.clTextCount resize.clTextCount', function() {
1517            clearTimeout(fallbackTimer);
1518            fallbackTimer = setTimeout(checkFallback, 80);
1519        });
1520
1521        if(runNow) setTimeout(checkFallback, 0);
1522    }
1523
1524    function observeTarget(el) {
1525        if(!observer || !el || el._clTextCountObserved || isTextCountActiveEditorTarget(el)) return;
1526
1527        el._clTextCountObserved = true;
1528        observer.observe(el);
1529    }
1530
1531    function isTextCountActiveEditorTarget(el) {
1532        var editorRoot;
1533
1534        if(!el || el.nodeType != 1) return false;
1535        if(el._clTextCountPopupPreview === true) return true;
1536
1537        editorRoot = $(el).closest('.fr-element').get(0);
1538        return !!(editorRoot && editorRoot._clTextCountEditorOwned === true);
1539    }
1540
1541    function isTextCountConfigMode(el) {
1542        if(document.body && $(document.body).hasClass('mode-config')) return true;
1543        return !!(el && $(el).closest('.dsgn-body.mode-config').length);
1544    }
1545
1546    function renderTextCountConfigStatic(el) {
1547        var style = getStyle(el);
1548
1549        if(style == 2 || style == 3) {
1550            return renderTextCountLocal(el, { animate: false });
1551        }
1552
1553        destroyTextCountLocal(el);
1554        return true;
1555    }
1556
1557    function queueTextCountDomSync(root) {
1558        if(!root || domSyncRoots.indexOf(root) != -1) return;
1559
1560        domSyncRoots.push(root);
1561        if(domSyncTimer) return;
1562
1563        domSyncTimer = setTimeout(function() {
1564            var roots = domSyncRoots.slice();
1565            var targets;
1566            var target;
1567            var host;
1568            var text;
1569            var style;
1570            var i;
1571            var j;
1572
1573            domSyncTimer = null;
1574            domSyncRoots = [];
1575
1576            for(i = 0; i < roots.length; i++) {
1577                if(!roots[i] || !document.documentElement.contains(roots[i])) continue;
1578
1579                targets = collect(roots[i]);
1580                for(j = 0; j < targets.length; j++) {
1581                    target = targets[j];
1582                    if(isTextCountActiveEditorTarget(target)) continue;
1583
1584                    host = target.parentNode && $(target.parentNode).hasClass('cl-text-count-local-host') ? target.parentNode : null;
1585                    style = getStyle(target);
1586
1587                    if(host) {
1588                        if((style == 2 || style == 3) && !$(host).children('.cl-text-count-local-view').length) {
1589                            text = (target.textContent || '').replace(/[\u200B-\u200D\uFEFF]/g, '').trim();
1590                            if(parseNumberText(text, null) !== null) renderTextCountLocal(target, { animate: false });
1591                        }
1592                        continue;
1593                    }
1594
1595                    if(isTextCountConfigMode(target)) {
1596                        renderTextCountConfigStatic(target);
1597                        continue;
1598                    }
1599
1600                    if(target._clTextCountPlayed) continue;
1601                    if(isInView(target)) {
1602                        run(target, {
1603                            force: true,
1604                            repeat: repeatOnScroll
1605                        });
1606                    } else if(observer) {
1607                        observeTarget(target);
1608                    }
1609                }
1610            }
1611        }, 0);
1612    }
1613
1614    function ensureTextCountDomObserver() {
1615        if(domObserver || !window.MutationObserver || !document.body) return;
1616
1617        domObserver = new MutationObserver(function(records) {
1618            var record;
1619            var mutationTarget;
1620            var source;
1621            var i;
1622            var j;
1623
1624            for(i = 0; i < records.length; i++) {
1625                record = records[i];
1626                mutationTarget = record.target && record.target.nodeType == 3 ? record.target.parentNode : record.target;
1627                if(mutationTarget && $(mutationTarget).closest('.cl-text-count-local-view').length) continue;
1628
1629                source = mutationTarget ? $(mutationTarget).closest('.textCount').get(0) : null;
1630                if(source && getTextCountLocalHost(source) && !isTextCountActiveEditorTarget(source)) {
1631                    scheduleTextCountLocalLayoutRefresh(0);
1632                }
1633
1634                if(record.target && record.target.nodeType == 1 && $(record.target).hasClass('cl-text-count-local-host') && !isTextCountActiveEditorTarget(record.target)) {
1635                    queueTextCountDomSync(record.target);
1636                }
1637                for(j = 0; j < record.addedNodes.length; j++) {
1638                    if(record.addedNodes[j].nodeType == 1 && !isTextCountActiveEditorTarget(record.addedNodes[j])) {
1639                        queueTextCountDomSync(record.addedNodes[j]);
1640                    }
1641                }
1642            }
1643        });
1644        domObserver.observe(document.body, {
1645            childList: true,
1646            characterData: true,
1647            subtree: true
1648        });
1649    }
1650
1651    function init(context, repeat) {
1652        var targets;
1653        var runtimeTargets = [];
1654        var style;
1655        var i;
1656
1657        if(typeof context == 'boolean') {
1658            repeat = context;
1659            context = document;
1660        }
1661
1662        if(typeof repeat != 'undefined') repeatOnScroll = repeat === true;
1663
1664        ensureTextCountDomObserver();
1665        bindTextCountLocalLayoutRefresh();
1666
1667        targets = collect(context || document);
1668        for(i = 0; i < targets.length; i++) {
1669            if(isTextCountActiveEditorTarget(targets[i])) continue;
1670
1671            if(isTextCountConfigMode(targets[i])) {
1672                renderTextCountConfigStatic(targets[i]);
1673            } else {
1674                style = getStyle(targets[i]);
1675                if(style == 2 || style == 3) {
1676                    renderTextCountLocal(targets[i], {
1677                        style: style,
1678                        animate: false
1679                    });
1680                }
1681                runtimeTargets.push(targets[i]);
1682            }
1683        }
1684        targets = runtimeTargets;
1685
1686        if('IntersectionObserver' in window) {
1687            if(!observer) {
1688                observer = new IntersectionObserver(function(entries) {
1689                    var i;
1690                    var target;
1691
1692                    for(i = 0; i < entries.length; i++) {
1693                        target = entries[i].target;
1694
1695                        if(isTextCountActiveEditorTarget(target)) {
1696                            observer.unobserve(target);
1697                            target._clTextCountObserved = false;
1698                            continue;
1699                        }
1700
1701                        if(entries[i].isIntersecting) {
1702                            if(repeatOnScroll) {
1703                                if(target._clTextCountInView) continue;
1704
1705                                target._clTextCountInView = true;
1706                                run(target, {
1707                                    force: true,
1708                                    repeat: true
1709                                });
1710                            } else {
1711                                run(target);
1712                                observer.unobserve(target);
1713                                target._clTextCountObserved = false;
1714                            }
1715                        } else if(repeatOnScroll) {
1716                            target._clTextCountInView = false;
1717                        }
1718                    }
1719                }, {
1720                    threshold: 0.2
1721                });
1722            }
1723
1724            for(i = 0; i < targets.length; i++) {
1725                if(repeatOnScroll || !targets[i]._clTextCountPlayed) observeTarget(targets[i]);
1726            }
1727        } else {
1728            bindFallback(true);
1729        }
1730    }
1731
1732    function clean(context) {
1733        var targets;
1734        var i;
1735
1736        targets = collect(context || document);
1737
1738        for(i = 0; i < targets.length; i++) {
1739            if(!destroyTextCountLocal(targets[i])) finish(targets[i]);
1740        }
1741    }
1742
1743    function cleanForSave(context) {
1744        var root = context || document;
1745
1746        clean(root);
1747        return cleanInvalid(root);
1748    }
1749
1750    function stripTextCountAttrs(el) {
1751        if(!el) return;
1752
1753        destroyTextCountLocal(el);
1754
1755        $(el)
1756            .removeClass('textCount')
1757            .removeAttr('data-style data-delay');
1758
1759        if(!$(el).attr('class')) $(el).removeAttr('class');
1760    }
1761
1762    function findTextCountNumberSegment(text) {
1763        var regexp = /-?\d(?:[\d,.]*\d)?/g;
1764        var match;
1765
1766        text = String(text || '');
1767        while((match = regexp.exec(text)) !== null) {
1768            if(parseNumberText(match[0], null) !== null) {
1769                return {
1770                    text: match[0],
1771                    start: match.index,
1772                    end: match.index + match[0].length
1773                };
1774            }
1775        }
1776
1777        return null;
1778    }
1779
1780    function copyTextCountAttrs(source, target) {
1781        var attrs = ['data-style','data-delay'];
1782        var i;
1783
1784        if(!source || !target || !target.setAttribute) return;
1785
1786        $(target).addClass('textCount');
1787        for(i = 0; i < attrs.length; i++) {
1788            if(source.hasAttribute && source.hasAttribute(attrs[i])) {
1789                target.setAttribute(attrs[i], source.getAttribute(attrs[i]));
1790            }
1791        }
1792    }
1793
1794    function createTextCountSplitNode(textCount, styleChild, text, keepTextCount) {
1795        var outer;
1796        var inner;
1797
1798        if(text === '') return null;
1799
1800        if(styleChild && styleChild !== textCount) {
1801            outer = textCount.cloneNode(false);
1802            if(keepTextCount) {
1803                copyTextCountAttrs(textCount, outer);
1804            } else {
1805                stripTextCountAttrs(outer);
1806            }
1807
1808            inner = styleChild.cloneNode(false);
1809            inner.textContent = text;
1810
1811            if(!keepTextCount && outer.nodeName == 'SPAN' && !outer.attributes.length) {
1812                return inner;
1813            }
1814
1815            outer.appendChild(inner);
1816            return outer;
1817        }
1818
1819        outer = textCount.cloneNode(false);
1820        if(keepTextCount) {
1821            copyTextCountAttrs(textCount, outer);
1822        } else {
1823            stripTextCountAttrs(outer);
1824        }
1825        outer.textContent = text;
1826
1827        if(!keepTextCount && outer.nodeName == 'SPAN' && !outer.attributes.length) {
1828            return document.createTextNode(text);
1829        }
1830
1831        return outer;
1832    }
1833
1834    function splitMixedTextCount(target) {
1835        var text;
1836        var segment;
1837        var styleChild;
1838        var parent;
1839        var fragment;
1840        var beforeNode;
1841        var numberNode;
1842        var afterNode;
1843
1844        if(!target || !target.parentNode) return false;
1845
1846        text = target.textContent || '';
1847        if(normalizeTextCountText(text) === '') return false;
1848        if(parseNumberText(normalizeTextCountText(text), null) !== null) return false;
1849
1850        segment = findTextCountNumberSegment(text);
1851        if(!segment) return false;
1852
1853        styleChild = getSingleMeaningfulElementChild(target);
1854        if(!isTextCountFormattingElement(styleChild) || (styleChild.textContent || '') !== text) {
1855            styleChild = null;
1856        }
1857
1858        fragment = document.createDocumentFragment();
1859        beforeNode = createTextCountSplitNode(target, styleChild, text.slice(0, segment.start), false);
1860        numberNode = createTextCountSplitNode(target, styleChild, segment.text, true);
1861        afterNode = createTextCountSplitNode(target, styleChild, text.slice(segment.end), false);
1862
1863        if(beforeNode) fragment.appendChild(beforeNode);
1864        if(numberNode) fragment.appendChild(numberNode);
1865        if(afterNode) fragment.appendChild(afterNode);
1866
1867        parent = target.parentNode;
1868        parent.insertBefore(fragment, target);
1869        parent.removeChild(target);
1870
1871        return true;
1872    }
1873
1874    function unwrapElement(el) {
1875        var parent;
1876        var firstNode;
1877        var lastNode;
1878
1879        if(!el || !el.parentNode) return null;
1880
1881        parent = el.parentNode;
1882        firstNode = el.firstChild;
1883        lastNode = el.lastChild;
1884        while(el.firstChild) parent.insertBefore(el.firstChild, el);
1885        parent.removeChild(el);
1886
1887        return {
1888            parent: parent,
1889            firstNode: firstNode,
1890            lastNode: lastNode
1891        };
1892    }
1893
1894    function isInvalidTextCount(el) {
1895        var text;
1896
1897        if(!el || !$(el).hasClass('textCount')) return false;
1898        text = normalizeTextCountText(el.textContent || '');
1899        return text === '' || parseNumberText(text, null) === null;
1900    }
1901
1902    function cleanInvalid(context) {
1903        var targets = collect(context || document);
1904        var changed = false;
1905        var target;
1906        var i;
1907
1908        for(i = targets.length - 1; i >= 0; i--) {
1909            target = targets[i];
1910            if(isTextCountLocalEditing(target)) continue;
1911            if(!isInvalidTextCount(target)) continue;
1912
1913            destroyTextCountLocal(target);
1914            clearRunning(target, false);
1915            if(splitMixedTextCount(target)) {
1916                changed = true;
1917                continue;
1918            }
1919
1920            stripTextCountAttrs(target);
1921
1922            if(target.nodeName == 'SPAN' && !target.attributes.length) {
1923                unwrapElement(target);
1924            }
1925
1926            changed = true;
1927        }
1928
1929        return changed;
1930    }
1931
1932    window.clTextCount = {
1933        init: init,
1934        run: run,
1935        renderLocal: renderTextCountLocal,
1936        destroyLocal: destroyTextCountLocal,
1937        setLocalEditing: setTextCountLocalEditing,
1938        isLocalEditing: isTextCountLocalEditing,
1939        clean: clean,
1940        cleanForSave: cleanForSave,
1941        cleanInvalid: cleanInvalid,
1942        stripAttrs: stripTextCountAttrs,
1943        unwrapElement: unwrapElement
1944    };
1945
1946    $(function() {
1947        setTimeout(function() {
1948            window.clTextCount.init(document, true);
1949        }, 0);
1950    });
1951})(window, document, jQuery);

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.