PageSourceSearch

https://paymento.io/core/modules/794d228af6/assets/js/split.js

js paymento.io collected 2026-10-02 04:47:59 UTC 16,970 bytes, 566 lines download raw bytes

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.