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.