1'use strict'; 2 3var root = document; 4var createText = root.createTextNode.bind(root); 5 6/** 7 * # setProperty 8 * Apply a CSS var 9 * @param {HTMLElement} el 10 * @param {string} varName 11 * @param {string|number} value 12 */ 13function setProperty(el, varName, value) { 14 el.style.setProperty(varName, value); 15} 16 17/** 18 * 19 * @param {!HTMLElement} el 20 * @param {!HTMLElement} child 21 */ 22function appendChild(el, child) { 23 return el.appendChild(child); 24} 25 26/** 27 * 28 * @param {!HTMLElement} parent 29 * @param {string} key 30 * @param {string} text 31 * @param {boolean} whitespace 32 */ 33function createElement(parent, key, text, whitespace) { 34 var el = root.createElement('span'); 35 key && (el.className = key); 36 if (text) { 37 !whitespace && el.setAttribute("data-" + key, text); 38 el.textContent = text; 39 } 40 return parent && appendChild(parent, el) || el; 41} 42 43/** 44 * 45 * @param {!HTMLElement} el 46 * @param {string} key 47 */ 48function getData(el, key) { 49 return el.getAttribute("data-" + key); 50} 51 52/** 53 * 54 * @param {import('../types').Target} e 55 * @param {!HTMLElement} parent 56 * @returns {!Array<!HTMLElement>} 57 */ 58function $(e, parent) { 59 return !e || e.length == 0 ? 60 // null or empty string returns empty array 61 [] : e.nodeName ? 62 // a single element is wrapped in an array 63 [e] : 64 // selector and NodeList are converted to Element[] 65 [].slice.call(e[0].nodeName ? e : (parent || root).querySelectorAll(e)); 66} 67 68/** 69 * Creates and fills an array with the value provided 70 * @param {number} len 71 * @param {() => T} valueProvider 72 * @return {T} 73 * @template T 74 */ 75function Array2D(len) { 76 var a = []; 77 for (; len--;) { 78 a[len] = []; 79 } 80 return a; 81} 82 83/** 84 * A for loop wrapper used to reduce js minified size. 85 * @param {!Array<T>} items 86 * @param {function(T):void} consumer 87 * @template T 88 */ 89function each(items, consumer) { 90 items && items.some(consumer); 91} 92 93/** 94 * @param {T} obj 95 * @return {function(string):*} 96 * @template T 97 */ 98function selectFrom(obj) { 99 return function (key) { 100 return obj[key]; 101 }; 102} 103 104/** 105 * # Splitting.index 106 * Index split elements and add them to a Splitting instance. 107 * 108 * @param {HTMLElement} element 109 * @param {string} key 110 * @param {!Array<!HTMLElement> | !Array<!Array<!HTMLElement>>} items 111 */ 112function index(element, key, items) { 113 var prefix = '--' + key; 114 var cssVar = prefix + "-index"; 115 each(items, function (items, i) { 116 if (Array.isArray(items)) { 117 each(items, function (item) { 118 setProperty(item, cssVar, i); 119 }); 120 } else { 121 setProperty(items, cssVar, i); 122 } 123 }); 124 setProperty(element, prefix + "-total", items.length); 125} 126 127/** 128 * @type {Record<string, import('./types').ISplittingPlugin>} 129 */ 130var plugins = {}; 131 132/** 133 * @param {string} by 134 * @param {string} parent 135 * @param {!Array<string>} deps 136 * @return {!Array<string>} 137 */ 138function resolvePlugins(by, parent, deps) { 139 // skip if already visited this dependency 140 var index = deps.indexOf(by); 141 if (index == -1) { 142 // if new to dependency array, add to the beginning 143 deps.unshift(by); 144 145 // recursively call this function for all dependencies 146 var plugin = plugins[by]; 147 if (!plugin) { 148 throw new Error("plugin not loaded: " + by); 149 } 150 each(plugin.depends, function (p) { 151 resolvePlugins(p, by, deps); 152 }); 153 } else { 154 // if this dependency was added already move to the left of 155 // the parent dependency so it gets loaded in order 156 var indexOfParent = deps.indexOf(parent); 157 deps.splice(index, 1); 158 deps.splice(indexOfParent, 0, by); 159 } 160 return deps; 161} 162 163/** 164 * Internal utility for creating plugins... essentially to reduce 165 * the size of the library 166 * @param {string} by 167 * @param {string} key 168 * @param {string[]} depends 169 * @param {Function} split 170 * @returns {import('./types').ISplittingPlugin} 171 */ 172function createPlugin(by, depends, key, split) { 173 return { 174 by: by, 175 depends: depends, 176 key: key, 177 split: split 178 }; 179} 180 181/** 182 * 183 * @param {string} by 184 * @returns {import('./types').ISplittingPlugin[]} 185 */ 186function resolve(by) { 187 return resolvePlugins(by, 0, []).map(selectFrom(plugins)); 188} 189 190/**
191 * Adds a new plugin to splitting 192 * @param {import('./types').ISplittingPlugin} opts 193 */ 194function add(opts) { 195 plugins[opts.by] = opts; 196} 197 198/** 199 * # Splitting.split 200 * Split an element's textContent into individual elements 201 * @param {!HTMLElement} el Element to split 202 * @param {string} key 203 * @param {string} splitOn 204 * @param {boolean} includePrevious 205 * @param {boolean} preserveWhitespace 206 * @return {!Array<!HTMLElement>} 207 */ 208function splitText(el, key, splitOn, includePrevious, preserveWhitespace, isCut = false) { 209 // Combine any strange text nodes or empty whitespace. 210 el.normalize(); 211 212 // Use fragment to prevent unnecessary DOM thrashing. 213 var elements = []; 214 var F = document.createDocumentFragment(); 215 if (includePrevious) { 216 elements.push(el.previousSibling); 217 } 218 var allElements = []; 219 $(el.childNodes).some(function (next) { 220 /* Split images too*/ 221 if (next.nodeType === 1 && (next.classList.contains('ui-e-highlight-image') || next.classList.contains('ui-e-highlight-icon') || next.nodeName == 'IMG')) { 222 elements.push(next); 223 } 224 /* Split images too*/ 225 226 if (next.tagName && !next.hasChildNodes()) { 227 // keep elements without child nodes (no text and no children) 228 allElements.push(next); 229 return; 230 } 231 // Recursively run through child nodes 232 if (next.childNodes && next.childNodes.length) { 233 allElements.push(next); 234 elements.push.apply(elements, splitText(next, key, splitOn, includePrevious, preserveWhitespace)); 235 return; 236 } 237 238 // Get the text to split, trimming out the whitespace 239 /** @type {string} */ 240 var wholeText = next.wholeText || ''; 241 var contents = wholeText.trim(); 242 243 // If there's no text left after trimming whitespace, continue the loop 244 if (contents.length) { 245 // insert leading space if there was one 246 if (wholeText[0] === ' ') { 247 allElements.push(createText(' ')); 248 } 249 // Concatenate the split text children back into the full array 250 each(contents.split(splitOn), function (splitText, i) { 251 if (i && preserveWhitespace) { 252 allElements.push(createElement(F, "whitespace", " ", preserveWhitespace)); 253 } 254 var splitEl = createElement(F, key, splitText); 255 if (isCut && key === 'char') { 256 let wrap = root.createElement('span'); 257 wrap.className = 'ui-e-cut'; 258 wrap.appendChild(splitEl); 259 splitEl = wrap; 260 } 261 elements.push(splitEl); 262 allElements.push(splitEl); 263 }); 264 // insert trailing space if there was one 265 if (wholeText[wholeText.length - 1] === ' ') { 266 allElements.push(createText(' ')); 267 } 268 } 269 }); 270 each(allElements, function (el) { 271 appendChild(F, el); 272 }); 273 274 // Clear out the existing element 275 el.innerHTML = ""; 276 appendChild(el, F); 277 return elements; 278} 279function copy(dest, src) { 280 for (var k in src) { 281 dest[k] = src[k]; 282 } 283 return dest; 284} 285var WORDS = 'words'; 286var wordPlugin = createPlugin(/* by= */WORDS, /* depends= */0, /* key= */'word', /* split= */function (el, options) { 287 return splitText(el, 'word', /\s+/, 0, 1, options.isCut); 288}); 289var CHARS = "chars"; 290var charPlugin = createPlugin(/* by= */CHARS, /* depends= */[WORDS], /* key= */"char", /* split= */function (el, options, ctx) { 291 var results = []; 292 each(ctx[WORDS], function (word, i) { 293 results.push.apply(results, splitText(word, "char", "", false, options.whitespace && i, options.isCut)); 294 }); 295 return results; 296}); 297 298/** 299 * # Splitting 300 * 301 * @param {import('./types').ISplittingOptions} opts 302 * @return {!Array<*>} 303 */ 304function Splitting(opts) { 305 opts = opts || {}; 306 var key = opts.key; 307 return $(opts.target || '[data-splitting]').map(function (el) { 308 var ctx = el['ð']; 309 if (!opts.force && ctx) { 310 return ctx; 311 } 312 ctx = el['ð'] = { 313 el: el 314 }; 315 var by = opts.by || getData(el, 'splitting'); 316 if (!by || by == 'true') { 317 by = CHARS; 318 } 319 var items = resolve(by); 320 var opts2 = copy({}, opts); 321 each(items, function (plugin) { 322 if (plugin.split) { 323 var pluginBy = plugin.by; 324 var key2 = (key ? '-' + key : '') + plugin.key; 325 var results = plugin.split(el, opts2, ctx); 326 key2 && index(el, key2, results); 327 ctx[pluginBy] = results; 328 el.classList.add(pluginBy); 329 } 330 }); 331 el.classList.add('splitting'); 332 return ctx; 333 }); 334} 335 336/** 337 * # Splitting.html 338 * 339 * @param {import('./types').ISplittingOptions} opts 340 */ 341function html(opts) { 342 opts = opts || {}; 343 var parent = opts.target = createElement();
344 parent.innerHTML = opts.content; 345 Splitting(opts); 346 return parent.outerHTML; 347} 348Splitting.html = html; 349Splitting.add = add; 350 351/** 352 * Detects the grid by measuring which elements align to a side of it. 353 * @param {!HTMLElement} el 354 * @param {import('../core/types').ISplittingOptions} options 355 * @param {*} side 356 */ 357function detectGrid(el, options, side) { 358 var items = $(options.matching || el.children, el); 359 var c = {}; 360 each(items, function (w) { 361 var val = Math.round(w[side]); 362 (c[val] || (c[val] = [])).push(w); 363 }); 364 return Object.keys(c).map(Number).sort(byNumber).map(selectFrom(c)); 365} 366 367/** 368 * Sorting function for numbers. 369 * @param {number} a 370 * @param {number} b 371 * @return {number} 372 */ 373function byNumber(a, b) { 374 return a - b; 375} 376var linePlugin = createPlugin(/* by= */'lines', /* depends= */[WORDS], /* key= */'line', /* split= */function (el, options, ctx) { 377 return detectGrid(el, { 378 matching: ctx[WORDS] 379 }, 'offsetTop', options.isCut); 380}); 381 382// install plugins 383// word/char plugins 384add(wordPlugin); 385add(charPlugin); 386add(linePlugin); 387window.addEventListener('DOMContentLoaded', () => { 388 class SPLIT extends elementorModules.frontend.handlers.Base { 389 bindEvents() { 390 if (this.getElementSettings('ui_animate_split') === 'ui-split-animate') { 391 jQuery(this.$element).css('opacity', 0); 392 this.processAnimation(); 393 } 394 } 395 processAnimation() { 396 if (elementorFrontend.isEditMode()) { 397 this.unsplit(); 398 setTimeout(() => { 399 this.split(); 400 }, 20); 401 setTimeout(() => { 402 this.animate(); 403 }, 80); 404 } else { 405 this.split(); 406 this.animate(); 407 408 // Retriggers on language change (currently supporting only GTranslate) 409 if (!!document.querySelector('script[src*="/gtranslate/js"]')) { 410 const observer = new MutationObserver(mutationsList => { 411 for (const mutation of mutationsList) { 412 if (mutation.type === 'attributes' && mutation.attributeName === 'lang') { 413 observer.disconnect(); 414 // Wait a little since the html lang apparently changes before the text 415 setTimeout(() => { 416 this.split(); 417 this.animate(); 418 }, 750); 419 } 420 } 421 }); 422 observer.observe(document.documentElement, { 423 attributes: true 424 }); 425 } 426 } 427 } 428 429 /** 430 * Check if the optimized markup experiment is enabled and return it, or return the proper class for the inner widget container if requested 431 * 432 * @param {boolean} classname If true, return the inner widget container class instead of the experiment status 433 * 434 * @return {string|boolean} The experiment value or the class name 435 */ 436 isMarkupOptimized(classname = false) { 437 const experiment = elementorFrontend.config.experimentalFeatures.e_optimized_markup; 438 if (classname) { 439 return experiment ? '' : '.elementor-widget-container'; 440 } 441 return experiment; 442 } 443 onElementChange(prop) { 444 var is_split = this.getElementSettings('ui_animate_split') === 'ui-split-animate'; 445 if (prop === 'ui_animate_split' && is_split) { 446 this.unsplit(); 447 setTimeout(() => { 448 this.split(); 449 }, 80); 450 } 451 if (prop === 'ui_animate_split_by' && is_split) { 452 this.unsplit(); 453 setTimeout(() => { 454 this.split(); 455 }, 80); 456 } 457 if (is_split) { 458 if (prop.indexOf('ui_animate') !== -1) { 459 this.unsplit(); 460 setTimeout(() => { 461 this.split(); 462 }, 80); 463 this.$element.find('.' + this.get_split(false)).attr('class', this.get_split(false) + ''); 464 setTimeout(() => { 465 this.animate(); 466 }, 100); 467 } 468 } else if (prop === 'ui_animate_split') { 469 this.unsplit(); 470 } 471 return; 472 } 473 split() { 474 // If `googtrans` cookie is set, it means the GTranslate plugin is already translating 475 // the page texts, forcing us to not split text so it can be properly translated 476 if (document.querySelector('script[src*="/gtranslate/js"]') && document.cookie.split('; ').find(cookie => cookie.startsWith('googtrans' + '='))) { 477 return; 478 } 479 let isCut = false;
480 const split = this.get_split(); 481 const container = this.isMarkupOptimized(true); 482 let el = this.isMarkupOptimized ? this.$element.find(`${container} > *:not(style):not(.ui-e-highlight-icon):not(.ui-e-highlight-image)`) : this.$element.find('*:not(style):not(.ui-e-highlight-icon):not(.ui-e-highlight-image)'); 483 if (el.length == 0) { 484 if (this.isMarkupOptimized) { 485 this.$element.wrapInner('<div class=\"elementor-text-editor\"></div>'); 486 } else { 487 this.$element.find(container).wrapInner('<div class=\"elementor-text-editor\"></div>'); 488 } 489 el = this.isMarkupOptimized ? this.$element.find('*:not(style)') : this.$element.find(`${container} > *:not(style)`); 490 } 491 let animation = this.getElementSettings('ui_animate_split_style'); 492 if (animation.indexOf('cut') !== -1) { 493 animation = animation.replace(' cut', ''); 494 el.addClass('ui-e-cut'); 495 isCut = true; 496 } 497 Splitting({ 498 target: el, 499 by: split, 500 key: 'ui-', 501 isCut 502 }); 503 el.addClass('ui-e-' + animation); 504 } 505 unsplit() { 506 const avoidTargets = '*:not(style)'; 507 const container = this.isMarkupOptimized(true); 508 const content = this.isMarkupOptimized ? this.$element.find(`${container} > ${avoidTargets}`)[0] : this.$element.find(`${avoidTargets}`)[0]; 509 const transformedMarkup = content.innerHTML; 510 if (!content) { 511 return; 512 } 513 if (content.classList.contains('splitting')) { 514 content.innerHTML = transformedMarkup.replace(/<span class="whitespace">(\s)<\/span>/g, '$1').replace(/<span class="char" data-char="\S+" style="--char-index:\s?\d+;">(\S+)<\/span>/g, '$1').replace(/ aria-hidden="true"/g, '').replace(/<span class="word" data-word="\S+" style="--word-index:\s?\d+;( --line-index:\s?\d+;)?">(\S+)<\/span>/g, '$2'); 515 content.classList.remove('splitting'); 516 } 517 } 518 animate() { 519 const $el = jQuery(this.$element); 520 const animation = this.getElementSettings('ui_animate_split_style'); 521 const animateChild = this.$element.find('.' + this.get_split(false)); 522 const el = this.$element.find('.elementor-widget-container > *:not(style)'); 523 animateChild.removeClass(animation); 524 el.addClass('ui-e-' + animation); 525 const observer = new IntersectionObserver((entries, observer) => { 526 entries.forEach(entry => { 527 if (entry.isIntersecting) { 528 $el.css('opacity', 1); 529 setTimeout(() => { 530 el.removeClass('ui-e-' + animation); 531 }, 100); 532 animateChild.addClass('ui-e-animated'); 533 animateChild.addClass(animation); 534 observer.unobserve(this.$element[0]); 535 } 536 }); 537 }, { 538 rootMargin: '0px 0px -10% 0px' 539 }); 540 observer.observe(this.$element[0]); 541 setTimeout(() => { 542 jQuery(this.$element).removeClass('elementor-invisible'); 543 }, 1); 544 } 545 get_split(asClass = true) { 546 let split = this.getElementSettings('ui_animate_split_by'); 547 if (!asClass) { 548 split = split === 'lines' ? 'words' : split; 549 return split.slice(0, -1); 550 } 551 return split; 552 } 553 } 554 jQuery(window).on('elementor/frontend/init', () => { 555 const addHandler = $element => { 556 elementorFrontend.elementsHandler.addHandler(SPLIT, { 557 $element 558 }); 559 }; 560 elementorFrontend.hooks.addAction('frontend/element_ready/heading.default', addHandler); 561 elementorFrontend.hooks.addAction('frontend/element_ready/text-editor.default', addHandler); 562 elementorFrontend.hooks.addAction('frontend/element_ready/uicore-the-title.default', addHandler); 563 elementorFrontend.hooks.addAction('frontend/element_ready/uicore-page-description.default', addHandler); 564 elementorFrontend.hooks.addAction('frontend/element_ready/highlighted-text.default', addHandler); 565 }); 566}, false);
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.