[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/media/system/js/ -> highlight-es5.js (source)

   1  (function () {
   2    'use strict';
   3  
   4    function _defineProperties(target, props) {
   5      for (var i = 0; i < props.length; i++) {
   6        var descriptor = props[i];
   7        descriptor.enumerable = descriptor.enumerable || false;
   8        descriptor.configurable = true;
   9        if ("value" in descriptor) descriptor.writable = true;
  10        Object.defineProperty(target, descriptor.key, descriptor);
  11      }
  12    }
  13  
  14    function _createClass(Constructor, protoProps, staticProps) {
  15      if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  16      if (staticProps) _defineProperties(Constructor, staticProps);
  17      Object.defineProperty(Constructor, "prototype", {
  18        writable: false
  19      });
  20      return Constructor;
  21    }
  22  
  23    /**
  24     * A NodeIterator with iframes support and a method to check if an element is
  25     * matching a specified selector
  26     * @example
  27     * const iterator = new DOMIterator(
  28     *     document.querySelector("#context"), true
  29     * );
  30     * iterator.forEachNode(NodeFilter.SHOW_TEXT, node => {
  31     *     console.log(node);
  32     * }, node => {
  33     *     if(DOMIterator.matches(node.parentNode, ".ignore")){
  34     *         return NodeFilter.FILTER_REJECT;
  35     *     } else {
  36     *         return NodeFilter.FILTER_ACCEPT;
  37     *     }
  38     * }, () => {
  39     *     console.log("DONE");
  40     * });
  41     * @todo Outsource into separate repository
  42     */
  43    var DOMIterator = /*#__PURE__*/function () {
  44      /**
  45       * @param {HTMLElement|HTMLElement[]|NodeList|string} ctx - The context DOM
  46       * element, an array of DOM elements, a NodeList or a selector
  47       * @param {boolean} [iframes=true] - A boolean indicating if iframes should
  48       * be handled
  49       * @param {string[]} [exclude=[]] - An array containing exclusion selectors
  50       * for iframes
  51       * @param {number} [iframesTimeout=5000] - A number indicating the ms to
  52       * wait before an iframe should be skipped, in case the load event isn't
  53       * fired. This also applies if the user is offline and the resource of the
  54       * iframe is online (either by the browsers "offline" mode or because
  55       * there's no internet connection)
  56       */
  57      function DOMIterator(ctx, iframes, exclude, iframesTimeout) {
  58        if (iframes === void 0) {
  59          iframes = true;
  60        }
  61  
  62        if (exclude === void 0) {
  63          exclude = [];
  64        }
  65  
  66        if (iframesTimeout === void 0) {
  67          iframesTimeout = 5000;
  68        }
  69  
  70        /**
  71         * The context of the instance. Either a DOM element, an array of DOM
  72         * elements, a NodeList or a selector
  73         * @type {HTMLElement|HTMLElement[]|NodeList|string}
  74         * @access protected
  75         */
  76        this.ctx = ctx;
  77        /**
  78         * Boolean indicating if iframe support is enabled
  79         * @type {boolean}
  80         * @access protected
  81         */
  82  
  83        this.iframes = iframes;
  84        /**
  85         * An array containing exclusion selectors for iframes
  86         * @type {string[]}
  87         */
  88  
  89        this.exclude = exclude;
  90        /**
  91         * The maximum ms to wait for a load event before skipping an iframe
  92         * @type {number}
  93         */
  94  
  95        this.iframesTimeout = iframesTimeout;
  96      }
  97      /**
  98       * Checks if the specified DOM element matches the selector
  99       * @param  {HTMLElement} element - The DOM element
 100       * @param  {string|string[]} selector - The selector or an array with
 101       * selectors
 102       * @return {boolean}
 103       * @access public
 104       */
 105  
 106  
 107      DOMIterator.matches = function matches(element, selector) {
 108        var selectors = typeof selector === 'string' ? [selector] : selector,
 109            fn = element.matches || element.matchesSelector || element.msMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.webkitMatchesSelector;
 110  
 111        if (fn) {
 112          var match = false;
 113          selectors.every(function (sel) {
 114            if (fn.call(element, sel)) {
 115              match = true;
 116              return false;
 117            }
 118  
 119            return true;
 120          });
 121          return match;
 122        } else {
 123          // may be false e.g. when el is a textNode
 124          return false;
 125        }
 126      }
 127      /**
 128       * Returns all contexts filtered by duplicates (even nested)
 129       * @return {HTMLElement[]} - An array containing DOM contexts
 130       * @access protected
 131       */
 132      ;
 133  
 134      var _proto = DOMIterator.prototype;
 135  
 136      _proto.getContexts = function getContexts() {
 137        var ctx,
 138            filteredCtx = [];
 139  
 140        if (typeof this.ctx === 'undefined' || !this.ctx) {
 141          // e.g. null
 142          ctx = [];
 143        } else if (NodeList.prototype.isPrototypeOf(this.ctx)) {
 144          ctx = Array.prototype.slice.call(this.ctx);
 145        } else if (Array.isArray(this.ctx)) {
 146          ctx = this.ctx;
 147        } else if (typeof this.ctx === 'string') {
 148          ctx = Array.prototype.slice.call(document.querySelectorAll(this.ctx));
 149        } else {
 150          // e.g. HTMLElement or element inside iframe
 151          ctx = [this.ctx];
 152        } // filter duplicate text nodes
 153  
 154  
 155        ctx.forEach(function (ctx) {
 156          var isDescendant = filteredCtx.filter(function (contexts) {
 157            return contexts.contains(ctx);
 158          }).length > 0;
 159  
 160          if (filteredCtx.indexOf(ctx) === -1 && !isDescendant) {
 161            filteredCtx.push(ctx);
 162          }
 163        });
 164        return filteredCtx;
 165      }
 166      /**
 167       * @callback DOMIterator~getIframeContentsSuccessCallback
 168       * @param {HTMLDocument} contents - The contentDocument of the iframe
 169       */
 170  
 171      /**
 172       * Calls the success callback function with the iframe document. If it can't
 173       * be accessed it calls the error callback function
 174       * @param {HTMLElement} ifr - The iframe DOM element
 175       * @param {DOMIterator~getIframeContentsSuccessCallback} successFn
 176       * @param {function} [errorFn]
 177       * @access protected
 178       */
 179      ;
 180  
 181      _proto.getIframeContents = function getIframeContents(ifr, successFn, errorFn) {
 182        if (errorFn === void 0) {
 183          errorFn = function errorFn() {};
 184        }
 185  
 186        var doc;
 187  
 188        try {
 189          var ifrWin = ifr.contentWindow;
 190          doc = ifrWin.document;
 191  
 192          if (!ifrWin || !doc) {
 193            // no permission = null. Undefined in Phantom
 194            throw new Error('iframe inaccessible');
 195          }
 196        } catch (e) {
 197          errorFn();
 198        }
 199  
 200        if (doc) {
 201          successFn(doc);
 202        }
 203      }
 204      /**
 205       * Checks if an iframe is empty (if about:blank is the shown page)
 206       * @param {HTMLElement} ifr - The iframe DOM element
 207       * @return {boolean}
 208       * @access protected
 209       */
 210      ;
 211  
 212      _proto.isIframeBlank = function isIframeBlank(ifr) {
 213        var bl = 'about:blank',
 214            src = ifr.getAttribute('src').trim(),
 215            href = ifr.contentWindow.location.href;
 216        return href === bl && src !== bl && src;
 217      }
 218      /**
 219       * Observes the onload event of an iframe and calls the success callback or
 220       * the error callback if the iframe is inaccessible. If the event isn't
 221       * fired within the specified {@link DOMIterator#iframesTimeout}, then it'll
 222       * call the error callback too
 223       * @param {HTMLElement} ifr - The iframe DOM element
 224       * @param {DOMIterator~getIframeContentsSuccessCallback} successFn
 225       * @param {function} errorFn
 226       * @access protected
 227       */
 228      ;
 229  
 230      _proto.observeIframeLoad = function observeIframeLoad(ifr, successFn, errorFn) {
 231        var _this = this;
 232  
 233        var called = false,
 234            tout = null;
 235  
 236        var listener = function listener() {
 237          if (called) {
 238            return;
 239          }
 240  
 241          called = true;
 242          clearTimeout(tout);
 243  
 244          try {
 245            if (!_this.isIframeBlank(ifr)) {
 246              ifr.removeEventListener('load', listener);
 247  
 248              _this.getIframeContents(ifr, successFn, errorFn);
 249            }
 250          } catch (e) {
 251            // isIframeBlank maybe throws throws an error
 252            errorFn();
 253          }
 254        };
 255  
 256        ifr.addEventListener('load', listener);
 257        tout = setTimeout(listener, this.iframesTimeout);
 258      }
 259      /**
 260       * Callback when the iframe is ready
 261       * @callback DOMIterator~onIframeReadySuccessCallback
 262       * @param {HTMLDocument} contents - The contentDocument of the iframe
 263       */
 264  
 265      /**
 266       * Callback if the iframe can't be accessed
 267       * @callback DOMIterator~onIframeReadyErrorCallback
 268       */
 269  
 270      /**
 271       * Calls the callback if the specified iframe is ready for DOM access
 272       * @param  {HTMLElement} ifr - The iframe DOM element
 273       * @param  {DOMIterator~onIframeReadySuccessCallback} successFn - Success
 274       * callback
 275       * @param {DOMIterator~onIframeReadyErrorCallback} errorFn - Error callback
 276       * @see {@link http://stackoverflow.com/a/36155560/3894981} for
 277       * background information
 278       * @access protected
 279       */
 280      ;
 281  
 282      _proto.onIframeReady = function onIframeReady(ifr, successFn, errorFn) {
 283        try {
 284          if (ifr.contentWindow.document.readyState === 'complete') {
 285            if (this.isIframeBlank(ifr)) {
 286              this.observeIframeLoad(ifr, successFn, errorFn);
 287            } else {
 288              this.getIframeContents(ifr, successFn, errorFn);
 289            }
 290          } else {
 291            this.observeIframeLoad(ifr, successFn, errorFn);
 292          }
 293        } catch (e) {
 294          // accessing document failed
 295          errorFn();
 296        }
 297      }
 298      /**
 299       * Callback when all iframes are ready for DOM access
 300       * @callback DOMIterator~waitForIframesDoneCallback
 301       */
 302  
 303      /**
 304       * Iterates over all iframes and calls the done callback when all of them
 305       * are ready for DOM access (including nested ones)
 306       * @param {HTMLElement} ctx - The context DOM element
 307       * @param {DOMIterator~waitForIframesDoneCallback} done - Done callback
 308       */
 309      ;
 310  
 311      _proto.waitForIframes = function waitForIframes(ctx, done) {
 312        var _this2 = this;
 313  
 314        var eachCalled = 0;
 315        this.forEachIframe(ctx, function () {
 316          return true;
 317        }, function (ifr) {
 318          eachCalled++;
 319  
 320          _this2.waitForIframes(ifr.querySelector('html'), function () {
 321            if (! --eachCalled) {
 322              done();
 323            }
 324          });
 325        }, function (handled) {
 326          if (!handled) {
 327            done();
 328          }
 329        });
 330      }
 331      /**
 332       * Callback allowing to filter an iframe. Must return true when the element
 333       * should remain, otherwise false
 334       * @callback DOMIterator~forEachIframeFilterCallback
 335       * @param {HTMLElement} iframe - The iframe DOM element
 336       */
 337  
 338      /**
 339       * Callback for each iframe content
 340       * @callback DOMIterator~forEachIframeEachCallback
 341       * @param {HTMLElement} content - The iframe document
 342       */
 343  
 344      /**
 345       * Callback if all iframes inside the context were handled
 346       * @callback DOMIterator~forEachIframeEndCallback
 347       * @param {number} handled - The number of handled iframes (those who
 348       * wheren't filtered)
 349       */
 350  
 351      /**
 352       * Iterates over all iframes inside the specified context and calls the
 353       * callbacks when they're ready. Filters iframes based on the instance
 354       * exclusion selectors
 355       * @param {HTMLElement} ctx - The context DOM element
 356       * @param {DOMIterator~forEachIframeFilterCallback} filter - Filter callback
 357       * @param {DOMIterator~forEachIframeEachCallback} each - Each callback
 358       * @param {DOMIterator~forEachIframeEndCallback} [end] - End callback
 359       * @access protected
 360       */
 361      ;
 362  
 363      _proto.forEachIframe = function forEachIframe(ctx, filter, each, end) {
 364        var _this3 = this;
 365  
 366        if (end === void 0) {
 367          end = function end() {};
 368        }
 369  
 370        var ifr = ctx.querySelectorAll('iframe'),
 371            open = ifr.length,
 372            handled = 0;
 373        ifr = Array.prototype.slice.call(ifr);
 374  
 375        var checkEnd = function checkEnd() {
 376          if (--open <= 0) {
 377            end(handled);
 378          }
 379        };
 380  
 381        if (!open) {
 382          checkEnd();
 383        }
 384  
 385        ifr.forEach(function (ifr) {
 386          if (DOMIterator.matches(ifr, _this3.exclude)) {
 387            checkEnd();
 388          } else {
 389            _this3.onIframeReady(ifr, function (con) {
 390              if (filter(ifr)) {
 391                handled++;
 392                each(con);
 393              }
 394  
 395              checkEnd();
 396            }, checkEnd);
 397          }
 398        });
 399      }
 400      /**
 401       * Creates a NodeIterator on the specified context
 402       * @see {@link https://developer.mozilla.org/en/docs/Web/API/NodeIterator}
 403       * @param {HTMLElement} ctx - The context DOM element
 404       * @param {DOMIterator~whatToShow} whatToShow
 405       * @param {DOMIterator~filterCb} filter
 406       * @return {NodeIterator}
 407       * @access protected
 408       */
 409      ;
 410  
 411      _proto.createIterator = function createIterator(ctx, whatToShow, filter) {
 412        return document.createNodeIterator(ctx, whatToShow, filter, false);
 413      }
 414      /**
 415       * Creates an instance of DOMIterator in an iframe
 416       * @param {HTMLDocument} contents - Iframe document
 417       * @return {DOMIterator}
 418       * @access protected
 419       */
 420      ;
 421  
 422      _proto.createInstanceOnIframe = function createInstanceOnIframe(contents) {
 423        return new DOMIterator(contents.querySelector('html'), this.iframes);
 424      }
 425      /**
 426       * Checks if an iframe occurs between two nodes, more specifically if an
 427       * iframe occurs before the specified node and after the specified prevNode
 428       * @param {HTMLElement} node - The node that should occur after the iframe
 429       * @param {HTMLElement} prevNode - The node that should occur before the
 430       * iframe
 431       * @param {HTMLElement} ifr - The iframe to check against
 432       * @return {boolean}
 433       * @access protected
 434       */
 435      ;
 436  
 437      _proto.compareNodeIframe = function compareNodeIframe(node, prevNode, ifr) {
 438        var compCurr = node.compareDocumentPosition(ifr),
 439            prev = Node.DOCUMENT_POSITION_PRECEDING;
 440  
 441        if (compCurr & prev) {
 442          if (prevNode !== null) {
 443            var compPrev = prevNode.compareDocumentPosition(ifr),
 444                after = Node.DOCUMENT_POSITION_FOLLOWING;
 445  
 446            if (compPrev & after) {
 447              return true;
 448            }
 449          } else {
 450            return true;
 451          }
 452        }
 453  
 454        return false;
 455      }
 456      /**
 457       * @typedef {DOMIterator~getIteratorNodeReturn}
 458       * @type {object.<string>}
 459       * @property {HTMLElement} prevNode - The previous node or null if there is
 460       * no
 461       * @property {HTMLElement} node - The current node
 462       */
 463  
 464      /**
 465       * Returns the previous and current node of the specified iterator
 466       * @param {NodeIterator} itr - The iterator
 467       * @return {DOMIterator~getIteratorNodeReturn}
 468       * @access protected
 469       */
 470      ;
 471  
 472      _proto.getIteratorNode = function getIteratorNode(itr) {
 473        var prevNode = itr.previousNode();
 474        var node;
 475  
 476        if (prevNode === null) {
 477          node = itr.nextNode();
 478        } else {
 479          node = itr.nextNode() && itr.nextNode();
 480        }
 481  
 482        return {
 483          prevNode: prevNode,
 484          node: node
 485        };
 486      }
 487      /**
 488       * An array containing objects. The object key "val" contains an iframe
 489       * DOM element. The object key "handled" contains a boolean indicating if
 490       * the iframe was handled already.
 491       * It wouldn't be enough to save all open or all already handled iframes.
 492       * The information of open iframes is necessary because they may occur after
 493       * all other text nodes (and compareNodeIframe would never be true). The
 494       * information of already handled iframes is necessary as otherwise they may
 495       * be handled multiple times
 496       * @typedef DOMIterator~checkIframeFilterIfr
 497       * @type {object[]}
 498       */
 499  
 500      /**
 501       * Checks if an iframe wasn't handled already and if so, calls
 502       * {@link DOMIterator#compareNodeIframe} to check if it should be handled.
 503       * Information wheter an iframe was or wasn't handled is given within the
 504       * <code>ifr</code> dictionary
 505       * @param {HTMLElement} node - The node that should occur after the iframe
 506       * @param {HTMLElement} prevNode - The node that should occur before the
 507       * iframe
 508       * @param {HTMLElement} currIfr - The iframe to check
 509       * @param {DOMIterator~checkIframeFilterIfr} ifr - The iframe dictionary.
 510       * Will be manipulated (by reference)
 511       * @return {boolean} Returns true when it should be handled, otherwise false
 512       * @access protected
 513       */
 514      ;
 515  
 516      _proto.checkIframeFilter = function checkIframeFilter(node, prevNode, currIfr, ifr) {
 517        var key = false,
 518            // false === doesn't exist
 519        handled = false;
 520        ifr.forEach(function (ifrDict, i) {
 521          if (ifrDict.val === currIfr) {
 522            key = i;
 523            handled = ifrDict.handled;
 524          }
 525        });
 526  
 527        if (this.compareNodeIframe(node, prevNode, currIfr)) {
 528          if (key === false && !handled) {
 529            ifr.push({
 530              val: currIfr,
 531              handled: true
 532            });
 533          } else if (key !== false && !handled) {
 534            ifr[key].handled = true;
 535          }
 536  
 537          return true;
 538        }
 539  
 540        if (key === false) {
 541          ifr.push({
 542            val: currIfr,
 543            handled: false
 544          });
 545        }
 546  
 547        return false;
 548      }
 549      /**
 550       * Creates an iterator on all open iframes in the specified array and calls
 551       * the end callback when finished
 552       * @param {DOMIterator~checkIframeFilterIfr} ifr
 553       * @param {DOMIterator~whatToShow} whatToShow
 554       * @param  {DOMIterator~forEachNodeCallback} eCb - Each callback
 555       * @param {DOMIterator~filterCb} fCb
 556       * @access protected
 557       */
 558      ;
 559  
 560      _proto.handleOpenIframes = function handleOpenIframes(ifr, whatToShow, eCb, fCb) {
 561        var _this4 = this;
 562  
 563        ifr.forEach(function (ifrDict) {
 564          if (!ifrDict.handled) {
 565            _this4.getIframeContents(ifrDict.val, function (con) {
 566              _this4.createInstanceOnIframe(con).forEachNode(whatToShow, eCb, fCb);
 567            });
 568          }
 569        });
 570      }
 571      /**
 572       * Iterates through all nodes in the specified context and handles iframe
 573       * nodes at the correct position
 574       * @param {DOMIterator~whatToShow} whatToShow
 575       * @param {HTMLElement} ctx - The context
 576       * @param  {DOMIterator~forEachNodeCallback} eachCb - Each callback
 577       * @param {DOMIterator~filterCb} filterCb - Filter callback
 578       * @param {DOMIterator~forEachNodeEndCallback} doneCb - End callback
 579       * @access protected
 580       */
 581      ;
 582  
 583      _proto.iterateThroughNodes = function iterateThroughNodes(whatToShow, ctx, eachCb, filterCb, doneCb) {
 584        var _this5 = this;
 585  
 586        var itr = this.createIterator(ctx, whatToShow, filterCb);
 587  
 588        var ifr = [],
 589            elements = [],
 590            node,
 591            prevNode,
 592            retrieveNodes = function retrieveNodes() {
 593          var _this5$getIteratorNod = _this5.getIteratorNode(itr);
 594  
 595          prevNode = _this5$getIteratorNod.prevNode;
 596          node = _this5$getIteratorNod.node;
 597          return node;
 598        };
 599  
 600        while (retrieveNodes()) {
 601          if (this.iframes) {
 602            this.forEachIframe(ctx, function (currIfr) {
 603              // note that ifr will be manipulated here
 604              return _this5.checkIframeFilter(node, prevNode, currIfr, ifr);
 605            }, function (con) {
 606              _this5.createInstanceOnIframe(con).forEachNode(whatToShow, function (ifrNode) {
 607                return elements.push(ifrNode);
 608              }, filterCb);
 609            });
 610          } // it's faster to call the each callback in an array loop
 611          // than in this while loop
 612  
 613  
 614          elements.push(node);
 615        }
 616  
 617        elements.forEach(function (node) {
 618          eachCb(node);
 619        });
 620  
 621        if (this.iframes) {
 622          this.handleOpenIframes(ifr, whatToShow, eachCb, filterCb);
 623        }
 624  
 625        doneCb();
 626      }
 627      /**
 628       * Callback for each node
 629       * @callback DOMIterator~forEachNodeCallback
 630       * @param {HTMLElement} node - The DOM text node element
 631       */
 632  
 633      /**
 634       * Callback if all contexts were handled
 635       * @callback DOMIterator~forEachNodeEndCallback
 636       */
 637  
 638      /**
 639       * Iterates over all contexts and initializes
 640       * {@link DOMIterator#iterateThroughNodes iterateThroughNodes} on them
 641       * @param {DOMIterator~whatToShow} whatToShow
 642       * @param  {DOMIterator~forEachNodeCallback} each - Each callback
 643       * @param {DOMIterator~filterCb} filter - Filter callback
 644       * @param {DOMIterator~forEachNodeEndCallback} done - End callback
 645       * @access public
 646       */
 647      ;
 648  
 649      _proto.forEachNode = function forEachNode(whatToShow, each, filter, done) {
 650        var _this6 = this;
 651  
 652        if (done === void 0) {
 653          done = function done() {};
 654        }
 655  
 656        var contexts = this.getContexts();
 657        var open = contexts.length;
 658  
 659        if (!open) {
 660          done();
 661        }
 662  
 663        contexts.forEach(function (ctx) {
 664          var ready = function ready() {
 665            _this6.iterateThroughNodes(whatToShow, ctx, each, filter, function () {
 666              if (--open <= 0) {
 667                // call end all contexts were handled
 668                done();
 669              }
 670            });
 671          }; // wait for iframes to avoid recursive calls, otherwise this would
 672          // perhaps reach the recursive function call limit with many nodes
 673  
 674  
 675          if (_this6.iframes) {
 676            _this6.waitForIframes(ctx, ready);
 677          } else {
 678            ready();
 679          }
 680        });
 681      }
 682      /**
 683       * Callback to filter nodes. Can return e.g. NodeFilter.FILTER_ACCEPT or
 684       * NodeFilter.FILTER_REJECT
 685       * @see {@link http://tinyurl.com/zdczmm2}
 686       * @callback DOMIterator~filterCb
 687       * @param {HTMLElement} node - The node to filter
 688       */
 689  
 690      /**
 691       * @typedef DOMIterator~whatToShow
 692       * @see {@link http://tinyurl.com/zfqqkx2}
 693       * @type {number}
 694       */
 695      ;
 696  
 697      return DOMIterator;
 698    }();
 699    /**
 700     * Marks search terms in DOM elements
 701     * @example
 702     * new Mark(document.querySelector(".context")).mark("lorem ipsum");
 703     * @example
 704     * new Mark(document.querySelector(".context")).markRegExp(/lorem/gmi);
 705     */
 706  
 707  
 708    var Mark$1 = /*#__PURE__*/function () {
 709      // eslint-disable-line no-unused-vars
 710  
 711      /**
 712       * @param {HTMLElement|HTMLElement[]|NodeList|string} ctx - The context DOM
 713       * element, an array of DOM elements, a NodeList or a selector
 714       */
 715      function Mark$1(ctx) {
 716        /**
 717         * The context of the instance. Either a DOM element, an array of DOM
 718         * elements, a NodeList or a selector
 719         * @type {HTMLElement|HTMLElement[]|NodeList|string}
 720         * @access protected
 721         */
 722        this.ctx = ctx;
 723        /**
 724         * Specifies if the current browser is a IE (necessary for the node
 725         * normalization bug workaround). See {@link Mark#unwrapMatches}
 726         * @type {boolean}
 727         * @access protected
 728         */
 729  
 730        this.ie = false;
 731        var ua = window.navigator.userAgent;
 732  
 733        if (ua.indexOf('MSIE') > -1 || ua.indexOf('Trident') > -1) {
 734          this.ie = true;
 735        }
 736      }
 737      /**
 738       * Options defined by the user. They will be initialized from one of the
 739       * public methods. See {@link Mark#mark}, {@link Mark#markRegExp},
 740       * {@link Mark#markRanges} and {@link Mark#unmark} for option properties.
 741       * @type {object}
 742       * @param {object} [val] - An object that will be merged with defaults
 743       * @access protected
 744       */
 745  
 746  
 747      var _proto2 = Mark$1.prototype;
 748  
 749      /**
 750       * Logs a message if log is enabled
 751       * @param {string} msg - The message to log
 752       * @param {string} [level="debug"] - The log level, e.g. <code>warn</code>
 753       * <code>error</code>, <code>debug</code>
 754       * @access protected
 755       */
 756      _proto2.log = function log(msg, level) {
 757        if (level === void 0) {
 758          level = 'debug';
 759        }
 760  
 761        var log = this.opt.log;
 762  
 763        if (!this.opt.debug) {
 764          return;
 765        }
 766  
 767        if (typeof log === 'object' && typeof log[level] === 'function') {
 768          log[level]("mark.js: " + msg);
 769        }
 770      }
 771      /**
 772       * Escapes a string for usage within a regular expression
 773       * @param {string} str - The string to escape
 774       * @return {string}
 775       * @access protected
 776       */
 777      ;
 778  
 779      _proto2.escapeStr = function escapeStr(str) {
 780        // eslint-disable-next-line no-useless-escape
 781        return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
 782      }
 783      /**
 784       * Creates a regular expression string to match the specified search
 785       * term including synonyms, diacritics and accuracy if defined
 786       * @param  {string} str - The search term to be used
 787       * @return {string}
 788       * @access protected
 789       */
 790      ;
 791  
 792      _proto2.createRegExp = function createRegExp(str) {
 793        if (this.opt.wildcards !== 'disabled') {
 794          str = this.setupWildcardsRegExp(str);
 795        }
 796  
 797        str = this.escapeStr(str);
 798  
 799        if (Object.keys(this.opt.synonyms).length) {
 800          str = this.createSynonymsRegExp(str);
 801        }
 802  
 803        if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
 804          str = this.setupIgnoreJoinersRegExp(str);
 805        }
 806  
 807        if (this.opt.diacritics) {
 808          str = this.createDiacriticsRegExp(str);
 809        }
 810  
 811        str = this.createMergedBlanksRegExp(str);
 812  
 813        if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
 814          str = this.createJoinersRegExp(str);
 815        }
 816  
 817        if (this.opt.wildcards !== 'disabled') {
 818          str = this.createWildcardsRegExp(str);
 819        }
 820  
 821        str = this.createAccuracyRegExp(str);
 822        return str;
 823      }
 824      /**
 825       * Creates a regular expression string to match the defined synonyms
 826       * @param  {string} str - The search term to be used
 827       * @return {string}
 828       * @access protected
 829       */
 830      ;
 831  
 832      _proto2.createSynonymsRegExp = function createSynonymsRegExp(str) {
 833        var syn = this.opt.synonyms,
 834            sens = this.opt.caseSensitive ? '' : 'i',
 835            // add replacement character placeholder before and after the
 836        // synonym group
 837        joinerPlaceholder = this.opt.ignoreJoiners || this.opt.ignorePunctuation.length ? "\0" : '';
 838  
 839        for (var index in syn) {
 840          if (syn.hasOwnProperty(index)) {
 841            var value = syn[index],
 842                k1 = this.opt.wildcards !== 'disabled' ? this.setupWildcardsRegExp(index) : this.escapeStr(index),
 843                k2 = this.opt.wildcards !== 'disabled' ? this.setupWildcardsRegExp(value) : this.escapeStr(value);
 844  
 845            if (k1 !== '' && k2 !== '') {
 846              str = str.replace(new RegExp("(" + this.escapeStr(k1) + "|" + this.escapeStr(k2) + ")", "gm" + sens), joinerPlaceholder + ("(" + this.processSynomyms(k1) + "|") + (this.processSynomyms(k2) + ")") + joinerPlaceholder);
 847            }
 848          }
 849        }
 850  
 851        return str;
 852      }
 853      /**
 854       * Setup synonyms to work with ignoreJoiners and or ignorePunctuation
 855       * @param {string} str - synonym key or value to process
 856       * @return {string} - processed synonym string
 857       */
 858      ;
 859  
 860      _proto2.processSynomyms = function processSynomyms(str) {
 861        if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
 862          str = this.setupIgnoreJoinersRegExp(str);
 863        }
 864  
 865        return str;
 866      }
 867      /**
 868       * Sets up the regular expression string to allow later insertion of
 869       * wildcard regular expression matches
 870       * @param  {string} str - The search term to be used
 871       * @return {string}
 872       * @access protected
 873       */
 874      ;
 875  
 876      _proto2.setupWildcardsRegExp = function setupWildcardsRegExp(str) {
 877        // replace single character wildcard with unicode 0001
 878        str = str.replace(/(?:\\)*\?/g, function (val) {
 879          return val.charAt(0) === '\\' ? '?' : "\x01";
 880        }); // replace multiple character wildcard with unicode 0002
 881  
 882        return str.replace(/(?:\\)*\*/g, function (val) {
 883          return val.charAt(0) === '\\' ? '*' : "\x02";
 884        });
 885      }
 886      /**
 887       * Sets up the regular expression string to allow later insertion of
 888       * wildcard regular expression matches
 889       * @param  {string} str - The search term to be used
 890       * @return {string}
 891       * @access protected
 892       */
 893      ;
 894  
 895      _proto2.createWildcardsRegExp = function createWildcardsRegExp(str) {
 896        // default to "enable" (i.e. to not include spaces)
 897        // "withSpaces" uses `[\\S\\s]` instead of `.` because the latter
 898        // does not match new line characters
 899        var spaces = this.opt.wildcards === 'withSpaces';
 900        return str // replace unicode 0001 with a RegExp class to match any single
 901        // character, or any single non-whitespace character depending
 902        // on the setting
 903        .replace(/\u0001/g, spaces ? '[\\S\\s]?' : '\\S?') // replace unicode 0002 with a RegExp class to match zero or
 904        // more characters, or zero or more non-whitespace characters
 905        // depending on the setting
 906        .replace(/\u0002/g, spaces ? '[\\S\\s]*?' : '\\S*');
 907      }
 908      /**
 909       * Sets up the regular expression string to allow later insertion of
 910       * designated characters (soft hyphens & zero width characters)
 911       * @param  {string} str - The search term to be used
 912       * @return {string}
 913       * @access protected
 914       */
 915      ;
 916  
 917      _proto2.setupIgnoreJoinersRegExp = function setupIgnoreJoinersRegExp(str) {
 918        // adding a "null" unicode character as it will not be modified by the
 919        // other "create" regular expression functions
 920        return str.replace(/[^(|)\\]/g, function (val, indx, original) {
 921          // don't add a null after an opening "(", around a "|" or before
 922          // a closing "(", or between an escapement (e.g. \+)
 923          var nextChar = original.charAt(indx + 1);
 924  
 925          if (/[(|)\\]/.test(nextChar) || nextChar === '') {
 926            return val;
 927          } else {
 928            return val + "\0";
 929          }
 930        });
 931      }
 932      /**
 933       * Creates a regular expression string to allow ignoring of designated
 934       * characters (soft hyphens, zero width characters & punctuation) based on
 935       * the specified option values of <code>ignorePunctuation</code> and
 936       * <code>ignoreJoiners</code>
 937       * @param  {string} str - The search term to be used
 938       * @return {string}
 939       * @access protected
 940       */
 941      ;
 942  
 943      _proto2.createJoinersRegExp = function createJoinersRegExp(str) {
 944        var joiner = [];
 945        var ignorePunctuation = this.opt.ignorePunctuation;
 946  
 947        if (Array.isArray(ignorePunctuation) && ignorePunctuation.length) {
 948          joiner.push(this.escapeStr(ignorePunctuation.join('')));
 949        }
 950  
 951        if (this.opt.ignoreJoiners) {
 952          // u+00ad = soft hyphen
 953          // u+200b = zero-width space
 954          // u+200c = zero-width non-joiner
 955          // u+200d = zero-width joiner
 956          joiner.push("\\u00ad\\u200b\\u200c\\u200d");
 957        }
 958  
 959        return joiner.length ? str.split(/\u0000+/).join("[" + joiner.join('') + "]*") : str;
 960      }
 961      /**
 962       * Creates a regular expression string to match diacritics
 963       * @param  {string} str - The search term to be used
 964       * @return {string}
 965       * @access protected
 966       */
 967      ;
 968  
 969      _proto2.createDiacriticsRegExp = function createDiacriticsRegExp(str) {
 970        var sens = this.opt.caseSensitive ? '' : 'i',
 971            dct = this.opt.caseSensitive ? ['aàáảãạăằắẳẵặâầấẩẫậäåāą', 'AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ', 'cçćč', 'CÇĆČ', 'dđď', 'DĐĎ', 'eèéẻẽẹêềếểễệëěēę', 'EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ', 'iìíỉĩịîïī', 'IÌÍỈĨỊÎÏĪ', 'lł', 'LŁ', 'nñňń', 'NÑŇŃ', 'oòóỏõọôồốổỗộơởỡớờợöøō', 'OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ', 'rř', 'RŘ', 'sšśșş', 'SŠŚȘŞ', 'tťțţ', 'TŤȚŢ', 'uùúủũụưừứửữựûüůū', 'UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ', 'yýỳỷỹỵÿ', 'YÝỲỶỸỴŸ', 'zžżź', 'ZŽŻŹ'] : ['aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ', 'cçćčCÇĆČ', 'dđďDĐĎ', 'eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ', 'iìíỉĩịîïīIÌÍỈĨỊÎÏĪ', 'lłLŁ', 'nñňńNÑŇŃ', 'oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ', 'rřRŘ', 'sšśșşSŠŚȘŞ', 'tťțţTŤȚŢ', 'uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ', 'yýỳỷỹỵÿYÝỲỶỸỴŸ', 'zžżźZŽŻŹ'];
 972        var handled = [];
 973        str.split('').forEach(function (ch) {
 974          dct.every(function (dct) {
 975            // Check if the character is inside a diacritics list
 976            if (dct.indexOf(ch) !== -1) {
 977              // Check if the related diacritics list was not
 978              // handled yet
 979              if (handled.indexOf(dct) > -1) {
 980                return false;
 981              } // Make sure that the character OR any other
 982              // character in the diacritics list will be matched
 983  
 984  
 985              str = str.replace(new RegExp("[" + dct + "]", "gm" + sens), "[" + dct + "]");
 986              handled.push(dct);
 987            }
 988  
 989            return true;
 990          });
 991        });
 992        return str;
 993      }
 994      /**
 995       * Creates a regular expression string that merges whitespace characters
 996       * including subsequent ones into a single pattern, one or multiple
 997       * whitespaces
 998       * @param  {string} str - The search term to be used
 999       * @return {string}
1000       * @access protected
1001       */
1002      ;
1003  
1004      _proto2.createMergedBlanksRegExp = function createMergedBlanksRegExp(str) {
1005        return str.replace(/[\s]+/gmi, '[\\s]+');
1006      }
1007      /**
1008       * Creates a regular expression string to match the specified string with
1009       * the defined accuracy. As in the regular expression of "exactly" can be
1010       * a group containing a blank at the beginning, all regular expressions will
1011       * be created with two groups. The first group can be ignored (may contain
1012       * the said blank), the second contains the actual match
1013       * @param  {string} str - The searm term to be used
1014       * @return {str}
1015       * @access protected
1016       */
1017      ;
1018  
1019      _proto2.createAccuracyRegExp = function createAccuracyRegExp(str) {
1020        var _this7 = this;
1021  
1022        var chars = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~¡¿';
1023        var acc = this.opt.accuracy,
1024            val = typeof acc === 'string' ? acc : acc.value,
1025            ls = typeof acc === 'string' ? [] : acc.limiters,
1026            lsJoin = '';
1027        ls.forEach(function (limiter) {
1028          lsJoin += "|" + _this7.escapeStr(limiter);
1029        });
1030  
1031        switch (val) {
1032          case 'partially':
1033          default:
1034            return "()(" + str + ")";
1035  
1036          case 'complementary':
1037            lsJoin = '\\s' + (lsJoin ? lsJoin : this.escapeStr(chars));
1038            return "()([^" + lsJoin + "]*" + str + "[^" + lsJoin + "]*)";
1039  
1040          case 'exactly':
1041            return "(^|\\s" + lsJoin + ")(" + str + ")(?=$|\\s" + lsJoin + ")";
1042        }
1043      }
1044      /**
1045       * @typedef Mark~separatedKeywords
1046       * @type {object.<string>}
1047       * @property {array.<string>} keywords - The list of keywords
1048       * @property {number} length - The length
1049       */
1050  
1051      /**
1052       * Returns a list of keywords dependent on whether separate word search
1053       * was defined. Also it filters empty keywords
1054       * @param {array} sv - The array of keywords
1055       * @return {Mark~separatedKeywords}
1056       * @access protected
1057       */
1058      ;
1059  
1060      _proto2.getSeparatedKeywords = function getSeparatedKeywords(sv) {
1061        var _this8 = this;
1062  
1063        var stack = [];
1064        sv.forEach(function (kw) {
1065          if (!_this8.opt.separateWordSearch) {
1066            if (kw.trim() && stack.indexOf(kw) === -1) {
1067              stack.push(kw);
1068            }
1069          } else {
1070            kw.split(' ').forEach(function (kwSplitted) {
1071              if (kwSplitted.trim() && stack.indexOf(kwSplitted) === -1) {
1072                stack.push(kwSplitted);
1073              }
1074            });
1075          }
1076        });
1077        return {
1078          // sort because of https://git.io/v6USg
1079          'keywords': stack.sort(function (a, b) {
1080            return b.length - a.length;
1081          }),
1082          'length': stack.length
1083        };
1084      }
1085      /**
1086       * Check if a value is a number
1087       * @param {number|string} value - the value to check;
1088       * numeric strings allowed
1089       * @return {boolean}
1090       * @access protected
1091       */
1092      ;
1093  
1094      _proto2.isNumeric = function isNumeric(value) {
1095        // http://stackoverflow.com/a/16655847/145346
1096        // eslint-disable-next-line eqeqeq
1097        return Number(parseFloat(value)) == value;
1098      }
1099      /**
1100       * @typedef Mark~rangeObject
1101       * @type {object}
1102       * @property {number} start - The start position within the composite value
1103       * @property {number} length - The length of the string to mark within the
1104       * composite value.
1105       */
1106  
1107      /**
1108       * @typedef Mark~setOfRanges
1109       * @type {object[]}
1110       * @property {Mark~rangeObject}
1111       */
1112  
1113      /**
1114       * Returns a processed list of integer offset indexes that do not overlap
1115       * each other, and remove any string values or additional elements
1116       * @param {Mark~setOfRanges} array - unprocessed raw array
1117       * @return {Mark~setOfRanges} - processed array with any invalid entries
1118       * removed
1119       * @throws Will throw an error if an array of objects is not passed
1120       * @access protected
1121       */
1122      ;
1123  
1124      _proto2.checkRanges = function checkRanges(array) {
1125        var _this9 = this;
1126  
1127        // start and length indexes are included in an array of objects
1128        // [{start: 0, length: 1}, {start: 4, length: 5}]
1129        // quick validity check of the first entry only
1130        if (!Array.isArray(array) || Object.prototype.toString.call(array[0]) !== '[object Object]') {
1131          this.log('markRanges() will only accept an array of objects');
1132          this.opt.noMatch(array);
1133          return [];
1134        }
1135  
1136        var stack = [];
1137        var last = 0;
1138        array // acending sort to ensure there is no overlap in start & end
1139        // offsets
1140        .sort(function (a, b) {
1141          return a.start - b.start;
1142        }).forEach(function (item) {
1143          var _this9$callNoMatchOnI = _this9.callNoMatchOnInvalidRanges(item, last),
1144              start = _this9$callNoMatchOnI.start,
1145              end = _this9$callNoMatchOnI.end,
1146              valid = _this9$callNoMatchOnI.valid;
1147  
1148          if (valid) {
1149            // preserve item in case there are extra key:values within
1150            item.start = start;
1151            item.length = end - start;
1152            stack.push(item);
1153            last = end;
1154          }
1155        });
1156        return stack;
1157      }
1158      /**
1159       * @typedef Mark~validObject
1160       * @type {object}
1161       * @property {number} start - The start position within the composite value
1162       * @property {number} end - The calculated end position within the composite
1163       * value.
1164       * @property {boolean} valid - boolean value indicating that the start and
1165       * calculated end range is valid
1166       */
1167  
1168      /**
1169        * Initial validation of ranges for markRanges. Preliminary checks are done
1170        * to ensure the start and length values exist and are not zero or non-
1171        * numeric
1172        * @param {Mark~rangeObject} range - the current range object
1173        * @param {number} last - last index of range
1174        * @return {Mark~validObject}
1175        * @access protected
1176        */
1177      ;
1178  
1179      _proto2.callNoMatchOnInvalidRanges = function callNoMatchOnInvalidRanges(range, last) {
1180        var start,
1181            end,
1182            valid = false;
1183  
1184        if (range && typeof range.start !== 'undefined') {
1185          start = parseInt(range.start, 10);
1186          end = start + parseInt(range.length, 10); // ignore overlapping values & non-numeric entries
1187  
1188          if (this.isNumeric(range.start) && this.isNumeric(range.length) && end - last > 0 && end - start > 0) {
1189            valid = true;
1190          } else {
1191            this.log('Ignoring invalid or overlapping range: ' + ("" + JSON.stringify(range)));
1192            this.opt.noMatch(range);
1193          }
1194        } else {
1195          this.log("Ignoring invalid range: " + JSON.stringify(range));
1196          this.opt.noMatch(range);
1197        }
1198  
1199        return {
1200          start: start,
1201          end: end,
1202          valid: valid
1203        };
1204      }
1205      /**
1206       * Check valid range for markRanges. Check ranges with access to the context
1207       * string. Range values are double checked, lengths that extend the mark
1208       * beyond the string length are limitied and ranges containing only
1209       * whitespace are ignored
1210       * @param {Mark~rangeObject} range - the current range object
1211       * @param {number} originalLength - original length of the context string
1212       * @param {string} string - current content string
1213       * @return {Mark~validObject}
1214       * @access protected
1215       */
1216      ;
1217  
1218      _proto2.checkWhitespaceRanges = function checkWhitespaceRanges(range, originalLength, string) {
1219        var end,
1220            valid = true,
1221            // the max value changes after the DOM is manipulated
1222        max = string.length,
1223            // adjust offset to account for wrapped text node
1224        offset = originalLength - max,
1225            start = parseInt(range.start, 10) - offset; // make sure to stop at max
1226  
1227        start = start > max ? max : start;
1228        end = start + parseInt(range.length, 10);
1229  
1230        if (end > max) {
1231          end = max;
1232          this.log("End range automatically set to the max value of " + max);
1233        }
1234  
1235        if (start < 0 || end - start < 0 || start > max || end > max) {
1236          valid = false;
1237          this.log("Invalid range: " + JSON.stringify(range));
1238          this.opt.noMatch(range);
1239        } else if (string.substring(start, end).replace(/\s+/g, '') === '') {
1240          valid = false; // whitespace only; even if wrapped it is not visible
1241  
1242          this.log('Skipping whitespace only range: ' + JSON.stringify(range));
1243          this.opt.noMatch(range);
1244        }
1245  
1246        return {
1247          start: start,
1248          end: end,
1249          valid: valid
1250        };
1251      }
1252      /**
1253       * @typedef Mark~getTextNodesDict
1254       * @type {object.<string>}
1255       * @property {string} value - The composite value of all text nodes
1256       * @property {object[]} nodes - An array of objects
1257       * @property {number} nodes.start - The start position within the composite
1258       * value
1259       * @property {number} nodes.end - The end position within the composite
1260       * value
1261       * @property {HTMLElement} nodes.node - The DOM text node element
1262       */
1263  
1264      /**
1265       * Callback
1266       * @callback Mark~getTextNodesCallback
1267       * @param {Mark~getTextNodesDict}
1268       */
1269  
1270      /**
1271       * Calls the callback with an object containing all text nodes (including
1272       * iframe text nodes) with start and end positions and the composite value
1273       * of them (string)
1274       * @param {Mark~getTextNodesCallback} cb - Callback
1275       * @access protected
1276       */
1277      ;
1278  
1279      _proto2.getTextNodes = function getTextNodes(cb) {
1280        var _this10 = this;
1281  
1282        var val = '',
1283            nodes = [];
1284        this.iterator.forEachNode(NodeFilter.SHOW_TEXT, function (node) {
1285          nodes.push({
1286            start: val.length,
1287            end: (val += node.textContent).length,
1288            node: node
1289          });
1290        }, function (node) {
1291          if (_this10.matchesExclude(node.parentNode)) {
1292            return NodeFilter.FILTER_REJECT;
1293          } else {
1294            return NodeFilter.FILTER_ACCEPT;
1295          }
1296        }, function () {
1297          cb({
1298            value: val,
1299            nodes: nodes
1300          });
1301        });
1302      }
1303      /**
1304       * Checks if an element matches any of the specified exclude selectors. Also
1305       * it checks for elements in which no marks should be performed (e.g.
1306       * script and style tags) and optionally already marked elements
1307       * @param  {HTMLElement} el - The element to check
1308       * @return {boolean}
1309       * @access protected
1310       */
1311      ;
1312  
1313      _proto2.matchesExclude = function matchesExclude(el) {
1314        return DOMIterator.matches(el, this.opt.exclude.concat([// ignores the elements itself, not their childrens (selector *)
1315        'script', 'style', 'title', 'head', 'html']));
1316      }
1317      /**
1318       * Wraps the instance element and class around matches that fit the start
1319       * and end positions within the node
1320       * @param  {HTMLElement} node - The DOM text node
1321       * @param  {number} start - The position where to start wrapping
1322       * @param  {number} end - The position where to end wrapping
1323       * @return {HTMLElement} Returns the splitted text node that will appear
1324       * after the wrapped text node
1325       * @access protected
1326       */
1327      ;
1328  
1329      _proto2.wrapRangeInTextNode = function wrapRangeInTextNode(node, start, end) {
1330        var hEl = !this.opt.element ? 'mark' : this.opt.element,
1331            startNode = node.splitText(start),
1332            ret = startNode.splitText(end - start);
1333        var repl = document.createElement(hEl);
1334        repl.setAttribute('data-markjs', 'true');
1335  
1336        if (this.opt.className) {
1337          repl.setAttribute('class', this.opt.className);
1338        }
1339  
1340        repl.textContent = startNode.textContent;
1341        startNode.parentNode.replaceChild(repl, startNode);
1342        return ret;
1343      }
1344      /**
1345       * @typedef Mark~wrapRangeInMappedTextNodeDict
1346       * @type {object.<string>}
1347       * @property {string} value - The composite value of all text nodes
1348       * @property {object[]} nodes - An array of objects
1349       * @property {number} nodes.start - The start position within the composite
1350       * value
1351       * @property {number} nodes.end - The end position within the composite
1352       * value
1353       * @property {HTMLElement} nodes.node - The DOM text node element
1354       */
1355  
1356      /**
1357       * Each callback
1358       * @callback Mark~wrapMatchesEachCallback
1359       * @param {HTMLElement} node - The wrapped DOM element
1360       * @param {number} lastIndex - The last matching position within the
1361       * composite value of text nodes
1362       */
1363  
1364      /**
1365       * Filter callback
1366       * @callback Mark~wrapMatchesFilterCallback
1367       * @param {HTMLElement} node - The matching text node DOM element
1368       */
1369  
1370      /**
1371       * Determines matches by start and end positions using the text node
1372       * dictionary even across text nodes and calls
1373       * {@link Mark#wrapRangeInTextNode} to wrap them
1374       * @param  {Mark~wrapRangeInMappedTextNodeDict} dict - The dictionary
1375       * @param  {number} start - The start position of the match
1376       * @param  {number} end - The end position of the match
1377       * @param  {Mark~wrapMatchesFilterCallback} filterCb - Filter callback
1378       * @param  {Mark~wrapMatchesEachCallback} eachCb - Each callback
1379       * @access protected
1380       */
1381      ;
1382  
1383      _proto2.wrapRangeInMappedTextNode = function wrapRangeInMappedTextNode(dict, start, end, filterCb, eachCb) {
1384        var _this11 = this;
1385  
1386        // iterate over all text nodes to find the one matching the positions
1387        dict.nodes.every(function (n, i) {
1388          var sibl = dict.nodes[i + 1];
1389  
1390          if (typeof sibl === 'undefined' || sibl.start > start) {
1391            if (!filterCb(n.node)) {
1392              return false;
1393            } // map range from dict.value to text node
1394  
1395  
1396            var s = start - n.start,
1397                e = (end > n.end ? n.end : end) - n.start,
1398                startStr = dict.value.substr(0, n.start),
1399                endStr = dict.value.substr(e + n.start);
1400            n.node = _this11.wrapRangeInTextNode(n.node, s, e); // recalculate positions to also find subsequent matches in the
1401            // same text node. Necessary as the text node in dict now only
1402            // contains the splitted part after the wrapped one
1403  
1404            dict.value = startStr + endStr;
1405            dict.nodes.forEach(function (k, j) {
1406              if (j >= i) {
1407                if (dict.nodes[j].start > 0 && j !== i) {
1408                  dict.nodes[j].start -= e;
1409                }
1410  
1411                dict.nodes[j].end -= e;
1412              }
1413            });
1414            end -= e;
1415            eachCb(n.node.previousSibling, n.start);
1416  
1417            if (end > n.end) {
1418              start = n.end;
1419            } else {
1420              return false;
1421            }
1422          }
1423  
1424          return true;
1425        });
1426      }
1427      /**
1428       * Filter callback before each wrapping
1429       * @callback Mark~wrapMatchesFilterCallback
1430       * @param {string} match - The matching string
1431       * @param {HTMLElement} node - The text node where the match occurs
1432       */
1433  
1434      /**
1435       * Callback for each wrapped element
1436       * @callback Mark~wrapMatchesEachCallback
1437       * @param {HTMLElement} element - The marked DOM element
1438       */
1439  
1440      /**
1441       * Callback on end
1442       * @callback Mark~wrapMatchesEndCallback
1443       */
1444  
1445      /**
1446       * Wraps the instance element and class around matches within single HTML
1447       * elements in all contexts
1448       * @param {RegExp} regex - The regular expression to be searched for
1449       * @param {number} ignoreGroups - A number indicating the amount of RegExp
1450       * matching groups to ignore
1451       * @param {Mark~wrapMatchesFilterCallback} filterCb
1452       * @param {Mark~wrapMatchesEachCallback} eachCb
1453       * @param {Mark~wrapMatchesEndCallback} endCb
1454       * @access protected
1455       */
1456      ;
1457  
1458      _proto2.wrapMatches = function wrapMatches(regex, ignoreGroups, filterCb, eachCb, endCb) {
1459        var _this12 = this;
1460  
1461        var matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1;
1462        this.getTextNodes(function (dict) {
1463          dict.nodes.forEach(function (node) {
1464            node = node.node;
1465            var match;
1466  
1467            while ((match = regex.exec(node.textContent)) !== null && match[matchIdx] !== '') {
1468              if (!filterCb(match[matchIdx], node)) {
1469                continue;
1470              }
1471  
1472              var pos = match.index;
1473  
1474              if (matchIdx !== 0) {
1475                for (var i = 1; i < matchIdx; i++) {
1476                  pos += match[i].length;
1477                }
1478              }
1479  
1480              node = _this12.wrapRangeInTextNode(node, pos, pos + match[matchIdx].length);
1481              eachCb(node.previousSibling); // reset index of last match as the node changed and the
1482              // index isn't valid anymore http://tinyurl.com/htsudjd
1483  
1484              regex.lastIndex = 0;
1485            }
1486          });
1487          endCb();
1488        });
1489      }
1490      /**
1491       * Callback for each wrapped element
1492       * @callback Mark~wrapMatchesAcrossElementsEachCallback
1493       * @param {HTMLElement} element - The marked DOM element
1494       */
1495  
1496      /**
1497       * Filter callback before each wrapping
1498       * @callback Mark~wrapMatchesAcrossElementsFilterCallback
1499       * @param {string} match - The matching string
1500       * @param {HTMLElement} node - The text node where the match occurs
1501       */
1502  
1503      /**
1504       * Callback on end
1505       * @callback Mark~wrapMatchesAcrossElementsEndCallback
1506       */
1507  
1508      /**
1509       * Wraps the instance element and class around matches across all HTML
1510       * elements in all contexts
1511       * @param {RegExp} regex - The regular expression to be searched for
1512       * @param {number} ignoreGroups - A number indicating the amount of RegExp
1513       * matching groups to ignore
1514       * @param {Mark~wrapMatchesAcrossElementsFilterCallback} filterCb
1515       * @param {Mark~wrapMatchesAcrossElementsEachCallback} eachCb
1516       * @param {Mark~wrapMatchesAcrossElementsEndCallback} endCb
1517       * @access protected
1518       */
1519      ;
1520  
1521      _proto2.wrapMatchesAcrossElements = function wrapMatchesAcrossElements(regex, ignoreGroups, filterCb, eachCb, endCb) {
1522        var _this13 = this;
1523  
1524        var matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1;
1525        this.getTextNodes(function (dict) {
1526          var match;
1527  
1528          while ((match = regex.exec(dict.value)) !== null && match[matchIdx] !== '') {
1529            // calculate range inside dict.value
1530            var start = match.index;
1531  
1532            if (matchIdx !== 0) {
1533              for (var i = 1; i < matchIdx; i++) {
1534                start += match[i].length;
1535              }
1536            }
1537  
1538            var end = start + match[matchIdx].length; // note that dict will be updated automatically, as it'll change
1539            // in the wrapping process, due to the fact that text
1540            // nodes will be splitted
1541  
1542            _this13.wrapRangeInMappedTextNode(dict, start, end, function (node) {
1543              return filterCb(match[matchIdx], node);
1544            }, function (node, lastIndex) {
1545              regex.lastIndex = lastIndex;
1546              eachCb(node);
1547            });
1548          }
1549  
1550          endCb();
1551        });
1552      }
1553      /**
1554       * Callback for each wrapped element
1555       * @callback Mark~wrapRangeFromIndexEachCallback
1556       * @param {HTMLElement} element - The marked DOM element
1557       * @param {Mark~rangeObject} range - the current range object; provided
1558       * start and length values will be numeric integers modified from the
1559       * provided original ranges.
1560       */
1561  
1562      /**
1563       * Filter callback before each wrapping
1564       * @callback Mark~wrapRangeFromIndexFilterCallback
1565       * @param {HTMLElement} node - The text node which includes the range
1566       * @param {Mark~rangeObject} range - the current range object
1567       * @param {string} match - string extracted from the matching range
1568       * @param {number} counter - A counter indicating the number of all marks
1569       */
1570  
1571      /**
1572       * Callback on end
1573       * @callback Mark~wrapRangeFromIndexEndCallback
1574       */
1575  
1576      /**
1577       * Wraps the indicated ranges across all HTML elements in all contexts
1578       * @param {Mark~setOfRanges} ranges
1579       * @param {Mark~wrapRangeFromIndexFilterCallback} filterCb
1580       * @param {Mark~wrapRangeFromIndexEachCallback} eachCb
1581       * @param {Mark~wrapRangeFromIndexEndCallback} endCb
1582       * @access protected
1583       */
1584      ;
1585  
1586      _proto2.wrapRangeFromIndex = function wrapRangeFromIndex(ranges, filterCb, eachCb, endCb) {
1587        var _this14 = this;
1588  
1589        this.getTextNodes(function (dict) {
1590          var originalLength = dict.value.length;
1591          ranges.forEach(function (range, counter) {
1592            var _this14$checkWhitespa = _this14.checkWhitespaceRanges(range, originalLength, dict.value),
1593                start = _this14$checkWhitespa.start,
1594                end = _this14$checkWhitespa.end,
1595                valid = _this14$checkWhitespa.valid;
1596  
1597            if (valid) {
1598              _this14.wrapRangeInMappedTextNode(dict, start, end, function (node) {
1599                return filterCb(node, range, dict.value.substring(start, end), counter);
1600              }, function (node) {
1601                eachCb(node, range);
1602              });
1603            }
1604          });
1605          endCb();
1606        });
1607      }
1608      /**
1609       * Unwraps the specified DOM node with its content (text nodes or HTML)
1610       * without destroying possibly present events (using innerHTML) and
1611       * normalizes the parent at the end (merge splitted text nodes)
1612       * @param  {HTMLElement} node - The DOM node to unwrap
1613       * @access protected
1614       */
1615      ;
1616  
1617      _proto2.unwrapMatches = function unwrapMatches(node) {
1618        var parent = node.parentNode;
1619        var docFrag = document.createDocumentFragment();
1620  
1621        while (node.firstChild) {
1622          docFrag.appendChild(node.removeChild(node.firstChild));
1623        }
1624  
1625        parent.replaceChild(docFrag, node);
1626  
1627        if (!this.ie) {
1628          // use browser's normalize method
1629          parent.normalize();
1630        } else {
1631          // custom method (needs more time)
1632          this.normalizeTextNode(parent);
1633        }
1634      }
1635      /**
1636       * Normalizes text nodes. It's a workaround for the native normalize method
1637       * that has a bug in IE (see attached link). Should only be used in IE
1638       * browsers as it's slower than the native method.
1639       * @see {@link http://tinyurl.com/z5asa8c}
1640       * @param {HTMLElement} node - The DOM node to normalize
1641       * @access protected
1642       */
1643      ;
1644  
1645      _proto2.normalizeTextNode = function normalizeTextNode(node) {
1646        if (!node) {
1647          return;
1648        }
1649  
1650        if (node.nodeType === 3) {
1651          while (node.nextSibling && node.nextSibling.nodeType === 3) {
1652            node.nodeValue += node.nextSibling.nodeValue;
1653            node.parentNode.removeChild(node.nextSibling);
1654          }
1655        } else {
1656          this.normalizeTextNode(node.firstChild);
1657        }
1658  
1659        this.normalizeTextNode(node.nextSibling);
1660      }
1661      /**
1662       * Callback when finished
1663       * @callback Mark~commonDoneCallback
1664       * @param {number} totalMatches - The number of marked elements
1665       */
1666  
1667      /**
1668       * @typedef Mark~commonOptions
1669       * @type {object.<string>}
1670       * @property {string} [element="mark"] - HTML element tag name
1671       * @property {string} [className] - An optional class name
1672       * @property {string[]} [exclude] - An array with exclusion selectors.
1673       * Elements matching those selectors will be ignored
1674       * @property {boolean} [iframes=false] - Whether to search inside iframes
1675       * @property {Mark~commonDoneCallback} [done]
1676       * @property {boolean} [debug=false] - Wheter to log messages
1677       * @property {object} [log=window.console] - Where to log messages (only if
1678       * debug is true)
1679       */
1680  
1681      /**
1682       * Callback for each marked element
1683       * @callback Mark~markRegExpEachCallback
1684       * @param {HTMLElement} element - The marked DOM element
1685       */
1686  
1687      /**
1688       * Callback if there were no matches
1689       * @callback Mark~markRegExpNoMatchCallback
1690       * @param {RegExp} regexp - The regular expression
1691       */
1692  
1693      /**
1694       * Callback to filter matches
1695       * @callback Mark~markRegExpFilterCallback
1696       * @param {HTMLElement} textNode - The text node which includes the match
1697       * @param {string} match - The matching string for the RegExp
1698       * @param {number} counter - A counter indicating the number of all marks
1699       */
1700  
1701      /**
1702       * These options also include the common options from
1703       * {@link Mark~commonOptions}
1704       * @typedef Mark~markRegExpOptions
1705       * @type {object.<string>}
1706       * @property {Mark~markRegExpEachCallback} [each]
1707       * @property {Mark~markRegExpNoMatchCallback} [noMatch]
1708       * @property {Mark~markRegExpFilterCallback} [filter]
1709       */
1710  
1711      /**
1712       * Marks a custom regular expression
1713       * @param  {RegExp} regexp - The regular expression
1714       * @param  {Mark~markRegExpOptions} [opt] - Optional options object
1715       * @access public
1716       */
1717      ;
1718  
1719      _proto2.markRegExp = function markRegExp(regexp, opt) {
1720        var _this15 = this;
1721  
1722        this.opt = opt;
1723        this.log("Searching with expression \"" + regexp + "\"");
1724        var totalMatches = 0,
1725            fn = 'wrapMatches';
1726  
1727        var eachCb = function eachCb(element) {
1728          totalMatches++;
1729  
1730          _this15.opt.each(element);
1731        };
1732  
1733        if (this.opt.acrossElements) {
1734          fn = 'wrapMatchesAcrossElements';
1735        }
1736  
1737        this[fn](regexp, this.opt.ignoreGroups, function (match, node) {
1738          return _this15.opt.filter(node, match, totalMatches);
1739        }, eachCb, function () {
1740          if (totalMatches === 0) {
1741            _this15.opt.noMatch(regexp);
1742          }
1743  
1744          _this15.opt.done(totalMatches);
1745        });
1746      }
1747      /**
1748       * Callback for each marked element
1749       * @callback Mark~markEachCallback
1750       * @param {HTMLElement} element - The marked DOM element
1751       */
1752  
1753      /**
1754       * Callback if there were no matches
1755       * @callback Mark~markNoMatchCallback
1756       * @param {RegExp} term - The search term that was not found
1757       */
1758  
1759      /**
1760       * Callback to filter matches
1761       * @callback Mark~markFilterCallback
1762       * @param {HTMLElement} textNode - The text node which includes the match
1763       * @param {string} match - The matching term
1764       * @param {number} totalCounter - A counter indicating the number of all
1765       * marks
1766       * @param {number} termCounter - A counter indicating the number of marks
1767       * for the specific match
1768       */
1769  
1770      /**
1771       * @typedef Mark~markAccuracyObject
1772       * @type {object.<string>}
1773       * @property {string} value - A accuracy string value
1774       * @property {string[]} limiters - A custom array of limiters. For example
1775       * <code>["-", ","]</code>
1776       */
1777  
1778      /**
1779       * @typedef Mark~markAccuracySetting
1780       * @type {string}
1781       * @property {"partially"|"complementary"|"exactly"|Mark~markAccuracyObject}
1782       * [accuracy="partially"] - Either one of the following string values:
1783       * <ul>
1784       *   <li><i>partially</i>: When searching for "lor" only "lor" inside
1785       *   "lorem" will be marked</li>
1786       *   <li><i>complementary</i>: When searching for "lor" the whole word
1787       *   "lorem" will be marked</li>
1788       *   <li><i>exactly</i>: When searching for "lor" only those exact words
1789       *   will be marked. In this example nothing inside "lorem". This value
1790       *   is equivalent to the previous option <i>wordBoundary</i></li>
1791       * </ul>
1792       * Or an object containing two properties:
1793       * <ul>
1794       *   <li><i>value</i>: One of the above named string values</li>
1795       *   <li><i>limiters</i>: A custom array of string limiters for accuracy
1796       *   "exactly" or "complementary"</li>
1797       * </ul>
1798       */
1799  
1800      /**
1801       * @typedef Mark~markWildcardsSetting
1802       * @type {string}
1803       * @property {"disabled"|"enabled"|"withSpaces"}
1804       * [wildcards="disabled"] - Set to any of the following string values:
1805       * <ul>
1806       *   <li><i>disabled</i>: Disable wildcard usage</li>
1807       *   <li><i>enabled</i>: When searching for "lor?m", the "?" will match zero
1808       *   or one non-space character (e.g. "lorm", "loram", "lor3m", etc). When
1809       *   searching for "lor*m", the "*" will match zero or more non-space
1810       *   characters (e.g. "lorm", "loram", "lor123m", etc).</li>
1811       *   <li><i>withSpaces</i>: When searching for "lor?m", the "?" will
1812       *   match zero or one space or non-space character (e.g. "lor m", "loram",
1813       *   etc). When searching for "lor*m", the "*" will match zero or more space
1814       *   or non-space characters (e.g. "lorm", "lore et dolor ipsum", "lor: m",
1815       *   etc).</li>
1816       * </ul>
1817       */
1818  
1819      /**
1820       * @typedef Mark~markIgnorePunctuationSetting
1821       * @type {string[]}
1822       * @property {string} The strings in this setting will contain punctuation
1823       * marks that will be ignored:
1824       * <ul>
1825       *   <li>These punctuation marks can be between any characters, e.g. setting
1826       *   this option to <code>["'"]</code> would match "Worlds", "World's" and
1827       *   "Wo'rlds"</li>
1828       *   <li>One or more apostrophes between the letters would still produce a
1829       *   match (e.g. "W'o''r'l'd's").</li>
1830       *   <li>A typical setting for this option could be as follows:
1831       *   <pre>ignorePunctuation: ":;.,-–—‒_(){}[]!'\"+=".split(""),</pre> This
1832       *   setting includes common punctuation as well as a minus, en-dash,
1833       *   em-dash and figure-dash
1834       *   ({@link https://en.wikipedia.org/wiki/Dash#Figure_dash ref}), as well
1835       *   as an underscore.</li>
1836       * </ul>
1837       */
1838  
1839      /**
1840       * These options also include the common options from
1841       * {@link Mark~commonOptions}
1842       * @typedef Mark~markOptions
1843       * @type {object.<string>}
1844       * @property {boolean} [separateWordSearch=true] - Whether to search for
1845       * each word separated by a blank instead of the complete term
1846       * @property {boolean} [diacritics=true] - If diacritic characters should be
1847       * matched. ({@link https://en.wikipedia.org/wiki/Diacritic Diacritics})
1848       * @property {object} [synonyms] - An object with synonyms. The key will be
1849       * a synonym for the value and the value for the key
1850       * @property {Mark~markAccuracySetting} [accuracy]
1851       * @property {Mark~markWildcardsSetting} [wildcards]
1852       * @property {boolean} [acrossElements=false] - Whether to find matches
1853       * across HTML elements. By default, only matches within single HTML
1854       * elements will be found
1855       * @property {boolean} [ignoreJoiners=false] - Whether to ignore word
1856       * joiners inside of key words. These include soft-hyphens, zero-width
1857       * space, zero-width non-joiners and zero-width joiners.
1858       * @property {Mark~markIgnorePunctuationSetting} [ignorePunctuation]
1859       * @property {Mark~markEachCallback} [each]
1860       * @property {Mark~markNoMatchCallback} [noMatch]
1861       * @property {Mark~markFilterCallback} [filter]
1862       */
1863  
1864      /**
1865       * Marks the specified search terms
1866       * @param {string|string[]} [sv] - Search value, either a search string or
1867       * an array containing multiple search strings
1868       * @param  {Mark~markOptions} [opt] - Optional options object
1869       * @access public
1870       */
1871      ;
1872  
1873      _proto2.mark = function mark(sv, opt) {
1874        var _this16 = this;
1875  
1876        this.opt = opt;
1877        var totalMatches = 0,
1878            fn = 'wrapMatches';
1879  
1880        var _this$getSeparatedKey = this.getSeparatedKeywords(typeof sv === 'string' ? [sv] : sv),
1881            kwArr = _this$getSeparatedKey.keywords,
1882            kwArrLen = _this$getSeparatedKey.length,
1883            sens = this.opt.caseSensitive ? '' : 'i',
1884            handler = function handler(kw) {
1885          // async function calls as iframes are async too
1886          var regex = new RegExp(_this16.createRegExp(kw), "gm" + sens),
1887              matches = 0;
1888  
1889          _this16.log("Searching with expression \"" + regex + "\"");
1890  
1891          _this16[fn](regex, 1, function (term, node) {
1892            return _this16.opt.filter(node, kw, totalMatches, matches);
1893          }, function (element) {
1894            matches++;
1895            totalMatches++;
1896  
1897            _this16.opt.each(element);
1898          }, function () {
1899            if (matches === 0) {
1900              _this16.opt.noMatch(kw);
1901            }
1902  
1903            if (kwArr[kwArrLen - 1] === kw) {
1904              _this16.opt.done(totalMatches);
1905            } else {
1906              handler(kwArr[kwArr.indexOf(kw) + 1]);
1907            }
1908          });
1909        };
1910  
1911        if (this.opt.acrossElements) {
1912          fn = 'wrapMatchesAcrossElements';
1913        }
1914  
1915        if (kwArrLen === 0) {
1916          this.opt.done(totalMatches);
1917        } else {
1918          handler(kwArr[0]);
1919        }
1920      }
1921      /**
1922       * Callback for each marked element
1923       * @callback Mark~markRangesEachCallback
1924       * @param {HTMLElement} element - The marked DOM element
1925       * @param {array} range - array of range start and end points
1926       */
1927  
1928      /**
1929       * Callback if a processed range is invalid, out-of-bounds, overlaps another
1930       * range, or only matches whitespace
1931       * @callback Mark~markRangesNoMatchCallback
1932       * @param {Mark~rangeObject} range - a range object
1933       */
1934  
1935      /**
1936       * Callback to filter matches
1937       * @callback Mark~markRangesFilterCallback
1938       * @param {HTMLElement} node - The text node which includes the range
1939       * @param {array} range - array of range start and end points
1940       * @param {string} match - string extracted from the matching range
1941       * @param {number} counter - A counter indicating the number of all marks
1942       */
1943  
1944      /**
1945       * These options also include the common options from
1946       * {@link Mark~commonOptions}
1947       * @typedef Mark~markRangesOptions
1948       * @type {object.<string>}
1949       * @property {Mark~markRangesEachCallback} [each]
1950       * @property {Mark~markRangesNoMatchCallback} [noMatch]
1951       * @property {Mark~markRangesFilterCallback} [filter]
1952       */
1953  
1954      /**
1955       * Marks an array of objects containing a start with an end or length of the
1956       * string to mark
1957       * @param  {Mark~setOfRanges} rawRanges - The original (preprocessed)
1958       * array of objects
1959       * @param  {Mark~markRangesOptions} [opt] - Optional options object
1960       * @access public
1961       */
1962      ;
1963  
1964      _proto2.markRanges = function markRanges(rawRanges, opt) {
1965        var _this17 = this;
1966  
1967        this.opt = opt;
1968        var totalMatches = 0,
1969            ranges = this.checkRanges(rawRanges);
1970  
1971        if (ranges && ranges.length) {
1972          this.log('Starting to mark with the following ranges: ' + JSON.stringify(ranges));
1973          this.wrapRangeFromIndex(ranges, function (node, range, match, counter) {
1974            return _this17.opt.filter(node, range, match, counter);
1975          }, function (element, range) {
1976            totalMatches++;
1977  
1978            _this17.opt.each(element, range);
1979          }, function () {
1980            _this17.opt.done(totalMatches);
1981          });
1982        } else {
1983          this.opt.done(totalMatches);
1984        }
1985      }
1986      /**
1987       * Removes all marked elements inside the context with their HTML and
1988       * normalizes the parent at the end
1989       * @param  {Mark~commonOptions} [opt] - Optional options object
1990       * @access public
1991       */
1992      ;
1993  
1994      _proto2.unmark = function unmark(opt) {
1995        var _this18 = this;
1996  
1997        this.opt = opt;
1998        var sel = this.opt.element ? this.opt.element : '*';
1999        sel += '[data-markjs]';
2000  
2001        if (this.opt.className) {
2002          sel += "." + this.opt.className;
2003        }
2004  
2005        this.log("Removal selector \"" + sel + "\"");
2006        this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT, function (node) {
2007          _this18.unwrapMatches(node);
2008        }, function (node) {
2009          var matchesSel = DOMIterator.matches(node, sel),
2010              matchesExclude = _this18.matchesExclude(node);
2011  
2012          if (!matchesSel || matchesExclude) {
2013            return NodeFilter.FILTER_REJECT;
2014          } else {
2015            return NodeFilter.FILTER_ACCEPT;
2016          }
2017        }, this.opt.done);
2018      };
2019  
2020      _createClass(Mark$1, [{
2021        key: "opt",
2022        get: function get() {
2023          return this._opt;
2024        }
2025        /**
2026         * An instance of DOMIterator
2027         * @type {DOMIterator}
2028         * @access protected
2029         */
2030        ,
2031        set: function set(val) {
2032          this._opt = Object.assign({}, {
2033            'element': '',
2034            'className': '',
2035            'exclude': [],
2036            'iframes': false,
2037            'iframesTimeout': 5000,
2038            'separateWordSearch': true,
2039            'diacritics': true,
2040            'synonyms': {},
2041            'accuracy': 'partially',
2042            'acrossElements': false,
2043            'caseSensitive': false,
2044            'ignoreJoiners': false,
2045            'ignoreGroups': 0,
2046            'ignorePunctuation': [],
2047            'wildcards': 'disabled',
2048            'each': function each() {},
2049            'noMatch': function noMatch() {},
2050            'filter': function filter() {
2051              return true;
2052            },
2053            'done': function done() {},
2054            'debug': false,
2055            'log': window.console
2056          }, val);
2057        }
2058      }, {
2059        key: "iterator",
2060        get: function get() {
2061          // always return new instance in case there were option changes
2062          return new DOMIterator(this.ctx, this.opt.iframes, this.opt.exclude, this.opt.iframesTimeout);
2063        }
2064      }]);
2065  
2066      return Mark$1;
2067    }();
2068  
2069    function Mark(ctx) {
2070      var _this19 = this;
2071  
2072      var instance = new Mark$1(ctx);
2073  
2074      this.mark = function (sv, opt) {
2075        instance.mark(sv, opt);
2076        return _this19;
2077      };
2078  
2079      this.markRegExp = function (sv, opt) {
2080        instance.markRegExp(sv, opt);
2081        return _this19;
2082      };
2083  
2084      this.markRanges = function (sv, opt) {
2085        instance.markRanges(sv, opt);
2086        return _this19;
2087      };
2088  
2089      this.unmark = function (opt) {
2090        instance.unmark(opt);
2091        return _this19;
2092      };
2093  
2094      return this;
2095    }
2096  
2097    var defaultOptions = {
2098      exclude: [],
2099      separateWordSearch: true,
2100      accuracy: 'partially',
2101      diacritics: true,
2102      synonyms: {},
2103      iframes: false,
2104      iframesTimeout: 5000,
2105      acrossElements: true,
2106      caseSensitive: false,
2107      ignoreJoiners: false,
2108      wildcards: 'disabled',
2109      compatibility: false
2110    };
2111  
2112    if (Joomla.getOptions && typeof Joomla.getOptions === 'function' && Joomla.getOptions('highlight')) {
2113      var scriptOptions = Joomla.getOptions('highlight');
2114      scriptOptions.forEach(function (currentOpts) {
2115        var options = Object.assign({}, defaultOptions, currentOpts); // Continue only if the element exists
2116  
2117        if (!options.compatibility) {
2118          var element = document.querySelector("." + options.class);
2119  
2120          if (element) {
2121            var instance = new Mark(element); // Loop through the terms
2122  
2123            options.highLight.forEach(function (term) {
2124              instance.mark(term, options);
2125            });
2126          }
2127        } else {
2128          var start = document.querySelector("#" + options.start);
2129          document.querySelector("#" + options.end);
2130          var parent = start.parentNode;
2131          var targetNodes = [];
2132          var allElems = Array.from(parent.childNodes);
2133          allElems.forEach(function (element) {
2134            {
2135              return;
2136            }
2137          });
2138          targetNodes.forEach(function (node) {
2139            var instance = new Mark(node); // Loop through the terms
2140  
2141            options.highLight.map(function (term) {
2142              return instance.mark(term, options);
2143            });
2144          });
2145        }
2146      });
2147    }
2148  
2149  })();


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer