[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/media/system/js/ -> core.js (source)

   1  /**
   2   * --------------------------------------------------------------------------
   3   * Bootstrap (v5.1.3): util/sanitizer.js
   4   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
   5   * --------------------------------------------------------------------------
   6   */
   7  const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);
   8  /**
   9   * A pattern that recognizes a commonly useful subset of URLs that are safe.
  10   *
  11   * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
  12   */
  13  
  14  const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i;
  15  /**
  16   * A pattern that matches safe data URLs. Only matches image, video and audio types.
  17   *
  18   * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
  19   */
  20  
  21  const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;
  22  
  23  const allowedAttribute = (attribute, allowedAttributeList) => {
  24    const attributeName = attribute.nodeName.toLowerCase();
  25  
  26    if (allowedAttributeList.includes(attributeName)) {
  27      if (uriAttributes.has(attributeName)) {
  28        return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue));
  29      }
  30  
  31      return true;
  32    }
  33  
  34    const regExp = allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp); // Check if a regular expression validates the attribute.
  35  
  36    for (let i = 0, len = regExp.length; i < len; i++) {
  37      if (regExp[i].test(attributeName)) {
  38        return true;
  39      }
  40    }
  41  
  42    return false;
  43  };
  44  function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {
  45    if (!unsafeHtml.length) {
  46      return unsafeHtml;
  47    }
  48  
  49    if (sanitizeFn && typeof sanitizeFn === 'function') {
  50      return sanitizeFn(unsafeHtml);
  51    }
  52  
  53    const domParser = new window.DOMParser();
  54    const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
  55    const elements = [].concat(...createdDocument.body.querySelectorAll('*'));
  56  
  57    for (let i = 0, len = elements.length; i < len; i++) {
  58      const element = elements[i];
  59      const elementName = element.nodeName.toLowerCase();
  60  
  61      if (!Object.keys(allowList).includes(elementName)) {
  62        element.remove();
  63        continue;
  64      }
  65  
  66      const attributeList = [].concat(...element.attributes);
  67      const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);
  68      attributeList.forEach(attribute => {
  69        if (!allowedAttribute(attribute, allowedAttributes)) {
  70          element.removeAttribute(attribute.nodeName);
  71        }
  72      });
  73    }
  74  
  75    return createdDocument.body.innerHTML;
  76  }
  77  
  78  /**
  79   * @copyright  (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
  80   * @license    GNU General Public License version 2 or later; see LICENSE.txt
  81   */
  82  const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
  83  const DATA_ATTRIBUTE_PATTERN = /^data-[\w-]*$/i;
  84  const DefaultAllowlist = {
  85    // Global attributes allowed on any supplied element below.
  86    '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN, DATA_ATTRIBUTE_PATTERN],
  87    a: ['target', 'href', 'title', 'rel'],
  88    area: [],
  89    b: [],
  90    br: [],
  91    col: [],
  92    code: [],
  93    div: [],
  94    em: [],
  95    hr: [],
  96    h1: [],
  97    h2: [],
  98    h3: [],
  99    h4: [],
 100    h5: [],
 101    h6: [],
 102    i: [],
 103    img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
 104    li: [],
 105    ol: [],
 106    p: [],
 107    pre: [],
 108    s: [],
 109    small: [],
 110    span: [],
 111    sub: [],
 112    sup: [],
 113    strong: [],
 114    u: [],
 115    ul: [],
 116    button: ['type'],
 117    input: ['accept', 'alt', 'autocomplete', 'autofocus', 'capture', 'checked', 'dirname', 'disabled', 'height', 'list', 'max', 'maxlength', 'min', 'minlength', 'multiple', 'type', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'size', 'src', 'step', 'value', 'width', 'inputmode'],
 118    select: ['name'],
 119    textarea: ['name'],
 120    option: ['value', 'selected']
 121  }; // Only define the Joomla namespace if not defined.
 122  
 123  window.Joomla = window.Joomla || {}; // Only define editors if not defined
 124  
 125  window.Joomla.editors = window.Joomla.editors || {}; // An object to hold each editor instance on page, only define if not defined.
 126  
 127  window.Joomla.editors.instances = window.Joomla.editors.instances || {
 128    /**
 129     * *****************************************************************
 130     * All Editors MUST register, per instance, the following callbacks:
 131     * *****************************************************************
 132     *
 133     * getValue         Type  Function  Should return the complete data from the editor
 134     *                                  Example: () => { return this.element.value; }
 135     * setValue         Type  Function  Should replace the complete data of the editor
 136     *                                  Example: (text) => { return this.element.value = text; }
 137     * getSelection     Type  Function  Should return the selected text from the editor
 138     *                                  Example: function () { return this.selectedText; }
 139     * disable          Type  Function  Toggles the editor into disabled mode. When the editor is
 140     *                                  active then everything should be usable. When inactive the
 141     *                                  editor should be unusable AND disabled for form validation
 142     *                                  Example: (bool) => { return this.disable = value; }
 143     * replaceSelection Type  Function  Should replace the selected text of the editor
 144     *                                  If nothing selected, will insert the data at the cursor
 145     *                                  Example:
 146     *                                  (text) => {
 147     *                                    return insertAtCursor(this.element, text);
 148     *                                    }
 149     *
 150     * USAGE (assuming that jform_articletext is the textarea id)
 151     * {
 152     * To get the current editor value:
 153     *  Joomla.editors.instances['jform_articletext'].getValue();
 154     * To set the current editor value:
 155     *  Joomla.editors.instances['jform_articletext'].setValue('Joomla! rocks');
 156     * To replace(selection) or insert a value at  the current editor cursor (replaces the J3
 157     * jInsertEditorText API):
 158     *  replaceSelection:
 159     *  Joomla.editors.instances['jform_articletext'].replaceSelection('Joomla! rocks')
 160     * }
 161     *
 162     * *********************************************************
 163     * ANY INTERACTION WITH THE EDITORS SHOULD USE THE ABOVE API
 164     * *********************************************************
 165     */
 166  };
 167  window.Joomla.Modal = window.Joomla.Modal || {
 168    /**
 169     * *****************************************************************
 170     * Modals should implement
 171     * *****************************************************************
 172     *
 173     * getCurrent  Type  Function  Should return the modal element
 174     * setCurrent  Type  Function  Should set the modal element
 175     * current     Type  {node}    The modal element
 176     *
 177     * USAGE (assuming that exampleId is the modal id)
 178     * To get the current modal element:
 179     *   Joomla.Modal.current; // Returns node element, eg: document.getElementById('exampleId')
 180     * To set the current modal element:
 181     *   Joomla.Modal.setCurrent(document.getElementById('exampleId'));
 182     *
 183     * *************************************************************
 184     * Joomla's UI modal uses `element.close();` to close the modal
 185     * and `element.open();` to open the modal
 186     * If you are using another modal make sure the same
 187     * functionality is bound to the modal element
 188     * @see media/legacy/bootstrap.init.js
 189     * *************************************************************
 190     */
 191    current: '',
 192    setCurrent: element => {
 193      window.Joomla.current = element;
 194    },
 195    getCurrent: () => window.Joomla.current
 196  };
 197  
 198  (Joomla => {
 199    /**
 200     * Method to Extend Objects
 201     *
 202     * @param  {Object}  destination
 203     * @param  {Object}  source
 204     *
 205     * @return Object
 206     */
 207  
 208    Joomla.extend = (destination, source) => {
 209      let newDestination = destination;
 210      /**
 211       * Technically null is an object, but trying to treat the destination as one in this
 212       * context will error out.
 213       * So emulate jQuery.extend(), and treat a destination null as an empty object.
 214       */
 215  
 216      if (destination === null) {
 217        newDestination = {};
 218      }
 219  
 220      [].slice.call(Object.keys(source)).forEach(key => {
 221        newDestination[key] = source[key];
 222      });
 223      return destination;
 224    };
 225    /**
 226     * Joomla options storage
 227     *
 228     * @type {{}}
 229     *
 230     * @since 3.7.0
 231     */
 232  
 233  
 234    Joomla.optionsStorage = Joomla.optionsStorage || null;
 235    /**
 236     * Get script(s) options
 237     *
 238     * @param  {String}  key  Name in Storage
 239     * @param  {mixed}   def  Default value if nothing found
 240     *
 241     * @return {mixed}
 242     *
 243     * @since 3.7.0
 244     */
 245  
 246    Joomla.getOptions = (key, def) => {
 247      // Load options if they not exists
 248      if (!Joomla.optionsStorage) {
 249        Joomla.loadOptions();
 250      }
 251  
 252      return Joomla.optionsStorage[key] !== undefined ? Joomla.optionsStorage[key] : def;
 253    };
 254    /**
 255     * Load new options from given options object or from Element
 256     *
 257     * @param  {Object|undefined}  options  The options object to load.
 258     * Eg {"com_foobar" : {"option1": 1, "option2": 2}}
 259     *
 260     * @since 3.7.0
 261     */
 262  
 263  
 264    Joomla.loadOptions = options => {
 265      // Load form the script container
 266      if (!options) {
 267        const elements = [].slice.call(document.querySelectorAll('.joomla-script-options.new'));
 268        let counter = 0;
 269        elements.forEach(element => {
 270          const str = element.text || element.textContent;
 271          const option = JSON.parse(str);
 272  
 273          if (option) {
 274            Joomla.loadOptions(option);
 275            counter += 1;
 276          }
 277  
 278          element.className = element.className.replace(' new', ' loaded');
 279        });
 280  
 281        if (counter) {
 282          return;
 283        }
 284      } // Initial loading
 285  
 286  
 287      if (!Joomla.optionsStorage) {
 288        Joomla.optionsStorage = options || {};
 289      } else if (options) {
 290        // Merge with existing
 291        [].slice.call(Object.keys(options)).forEach(key => {
 292          /**
 293           * If both existing and new options are objects, merge them with Joomla.extend().
 294           * But test for new option being null, as null is an object, but we want to allow
 295           * clearing of options with ...
 296           *
 297           * Joomla.loadOptions({'joomla.jtext': null});
 298           */
 299          if (options[key] !== null && typeof Joomla.optionsStorage[key] === 'object' && typeof options[key] === 'object') {
 300            Joomla.optionsStorage[key] = Joomla.extend(Joomla.optionsStorage[key], options[key]);
 301          } else {
 302            Joomla.optionsStorage[key] = options[key];
 303          }
 304        });
 305      }
 306    };
 307    /**
 308     * Custom behavior for JavaScript I18N in Joomla! 1.6
 309     *
 310     * @type {{}}
 311     *
 312     * Allows you to call Joomla.Text._() to get a translated JavaScript string
 313     * pushed in with Text::script() in Joomla.
 314     */
 315  
 316  
 317    Joomla.Text = {
 318      strings: {},
 319  
 320      /**
 321       * Translates a string into the current language.
 322       *
 323       * @param {String} key   The string to translate
 324       * @param {String} def   Default string
 325       *
 326       * @returns {String}
 327       */
 328      _: (key, def) => {
 329        let newKey = key;
 330        let newDef = def; // Check for new strings in the optionsStorage, and load them
 331  
 332        const newStrings = Joomla.getOptions('joomla.jtext');
 333  
 334        if (newStrings) {
 335          Joomla.Text.load(newStrings); // Clean up the optionsStorage from useless data
 336  
 337          Joomla.loadOptions({
 338            'joomla.jtext': null
 339          });
 340        }
 341  
 342        newDef = newDef === undefined ? newKey : newDef;
 343        newKey = newKey.toUpperCase();
 344        return Joomla.Text.strings[newKey] !== undefined ? Joomla.Text.strings[newKey] : newDef;
 345      },
 346  
 347      /**
 348       * Load new strings in to Joomla.Text
 349       *
 350       * @param {Object} object  Object with new strings
 351       * @returns {Joomla.Text}
 352       */
 353      load: object => {
 354        [].slice.call(Object.keys(object)).forEach(key => {
 355          Joomla.Text.strings[key.toUpperCase()] = object[key];
 356        });
 357        return Joomla.Text;
 358      }
 359    };
 360    /**
 361     * For B/C we still support Joomla.JText
 362     *
 363     * @type {{}}
 364     *
 365     * @deprecated 5.0
 366     */
 367  
 368    Joomla.JText = Joomla.Text;
 369    /**
 370     * Generic submit form
 371     *
 372     * @param  {String}  task      The given task
 373     * @param  {node}    form      The form element
 374     * @param  {bool}    validate  The form element
 375     *
 376     * @returns  {void}
 377     */
 378  
 379    Joomla.submitform = (task, form, validate) => {
 380      let newForm = form;
 381      const newTask = task;
 382  
 383      if (!newForm) {
 384        newForm = document.getElementById('adminForm');
 385      }
 386  
 387      if (newTask) {
 388        newForm.task.value = newTask;
 389      } // Toggle HTML5 validation
 390  
 391  
 392      newForm.noValidate = !validate;
 393  
 394      if (!validate) {
 395        newForm.setAttribute('novalidate', '');
 396      } else if (newForm.hasAttribute('novalidate')) {
 397        newForm.removeAttribute('novalidate');
 398      } // Submit the form.
 399      // Create the input type="submit"
 400  
 401  
 402      const button = document.createElement('input');
 403      button.classList.add('hidden');
 404      button.type = 'submit'; // Append it and click it
 405  
 406      newForm.appendChild(button).click(); // If "submit" was prevented, make sure we don't get a build up of buttons
 407  
 408      newForm.removeChild(button);
 409    };
 410    /**
 411     * Default function. Can be overridden by the component to add custom logic
 412     *
 413     * @param  {String}  task            The given task
 414     * @param  {String}  formSelector    The form selector eg '#adminForm'
 415     * @param  {bool}    validate        The form element
 416     *
 417     * @returns {void}
 418     */
 419  
 420  
 421    Joomla.submitbutton = (task, formSelector, validate) => {
 422      let form = document.querySelector(formSelector || 'form.form-validate');
 423      let newValidate = validate;
 424  
 425      if (typeof formSelector === 'string' && form === null) {
 426        form = document.querySelector(`#$formSelector}`);
 427      }
 428  
 429      if (form) {
 430        if (newValidate === undefined || newValidate === null) {
 431          const pressbutton = task.split('.');
 432          let cancelTask = form.getAttribute('data-cancel-task');
 433  
 434          if (!cancelTask) {
 435            cancelTask = `$pressbutton[0]}.cancel`;
 436          }
 437  
 438          newValidate = task !== cancelTask;
 439        }
 440  
 441        if (!newValidate || document.formvalidator.isValid(form)) {
 442          Joomla.submitform(task, form);
 443        }
 444      } else {
 445        Joomla.submitform(task);
 446      }
 447    };
 448    /**
 449     * USED IN: all list forms.
 450     *
 451     * Toggles the check state of a group of boxes
 452     *
 453     * Checkboxes must have an id attribute in the form cb0, cb1...
 454     *
 455     * @param {mixed}  checkbox The number of box to 'check', for a checkbox element
 456     * @param {string} stub     An alternative field name
 457     *
 458     * @return {boolean}
 459     */
 460  
 461  
 462    Joomla.checkAll = (checkbox, stub) => {
 463      if (!checkbox.form) {
 464        return false;
 465      }
 466  
 467      const currentStab = stub || 'cb';
 468      const elements = [].slice.call(checkbox.form.elements);
 469      let state = 0;
 470      elements.forEach(element => {
 471        if (element.type === checkbox.type && element.id.indexOf(currentStab) === 0) {
 472          element.checked = checkbox.checked;
 473          state += element.checked ? 1 : 0;
 474        }
 475      });
 476  
 477      if (checkbox.form.boxchecked) {
 478        checkbox.form.boxchecked.value = state;
 479        checkbox.form.boxchecked.dispatchEvent(new CustomEvent('change', {
 480          bubbles: true,
 481          cancelable: true
 482        }));
 483      }
 484  
 485      return true;
 486    };
 487    /**
 488     * USED IN: administrator/components/com_cache/views/cache/tmpl/default.php
 489     * administrator/components/com_installer/views/discover/tmpl/default_item.php
 490     * administrator/components/com_installer/views/update/tmpl/default_item.php
 491     * administrator/components/com_languages/helpers/html/languages.php
 492     * libraries/joomla/html/html/grid.php
 493     *
 494     * @param  {boolean}  isitchecked  Flag for checked
 495     * @param  {node}     form         The form
 496     *
 497     * @return  {void}
 498     */
 499  
 500  
 501    Joomla.isChecked = (isitchecked, form) => {
 502      let newForm = form;
 503  
 504      if (typeof newForm === 'undefined') {
 505        newForm = document.getElementById('adminForm');
 506      } else if (typeof form === 'string') {
 507        newForm = document.getElementById(form);
 508      }
 509  
 510      newForm.boxchecked.value = isitchecked ? parseInt(newForm.boxchecked.value, 10) + 1 : parseInt(newForm.boxchecked.value, 10) - 1;
 511      newForm.boxchecked.dispatchEvent(new CustomEvent('change', {
 512        bubbles: true,
 513        cancelable: true
 514      })); // If we don't have a checkall-toggle, done.
 515  
 516      if (!newForm.elements['checkall-toggle']) {
 517        return;
 518      } // Toggle main toggle checkbox depending on checkbox selection
 519  
 520  
 521      let c = true;
 522      let i;
 523      let e;
 524      let n; // eslint-disable-next-line no-plusplus
 525  
 526      for (i = 0, n = newForm.elements.length; i < n; i++) {
 527        e = newForm.elements[i];
 528  
 529        if (e.type === 'checkbox' && e.name !== 'checkall-toggle' && !e.checked) {
 530          c = false;
 531          break;
 532        }
 533      }
 534  
 535      newForm.elements['checkall-toggle'].checked = c;
 536    };
 537    /**
 538     * USED IN: libraries/joomla/html/html/grid.php
 539     * In other words, on any reorderable table
 540     *
 541     * @param  {string}  order  The order value
 542     * @param  {string}  dir    The direction
 543     * @param  {string}  task   The task
 544     * @param  {node}    form   The form
 545     *
 546     * return  {void}
 547     */
 548  
 549  
 550    Joomla.tableOrdering = (order, dir, task, form) => {
 551      let newForm = form;
 552  
 553      if (typeof newForm === 'undefined') {
 554        newForm = document.getElementById('adminForm');
 555      } else if (typeof form === 'string') {
 556        newForm = document.getElementById(form);
 557      }
 558  
 559      newForm.filter_order.value = order;
 560      newForm.filter_order_Dir.value = dir;
 561      Joomla.submitform(task, newForm);
 562    };
 563    /**
 564     * USED IN: all over :)
 565     *
 566     * @param  {string}  id    The id
 567     * @param  {string}  task  The task
 568     * @param  {string}  form  The optional form
 569     *
 570     * @return {boolean}
 571     */
 572  
 573  
 574    Joomla.listItemTask = (id, task, form = null) => {
 575      let newForm = form;
 576  
 577      if (form !== null) {
 578        newForm = document.getElementById(form);
 579      } else {
 580        newForm = document.adminForm;
 581      }
 582  
 583      const cb = newForm[id];
 584      let i = 0;
 585      let cbx;
 586  
 587      if (!cb) {
 588        return false;
 589      } // eslint-disable-next-line no-constant-condition
 590  
 591  
 592      while (true) {
 593        cbx = newForm[`cb$i}`];
 594  
 595        if (!cbx) {
 596          break;
 597        }
 598  
 599        cbx.checked = false;
 600        i += 1;
 601      }
 602  
 603      cb.checked = true;
 604      newForm.boxchecked.value = 1;
 605      Joomla.submitform(task, newForm);
 606      return false;
 607    };
 608    /**
 609     * Method to replace all request tokens on the page with a new one.
 610     *
 611     * @param {String}  newToken  The token
 612     *
 613     * Used in Joomla Installation
 614     */
 615  
 616  
 617    Joomla.replaceTokens = newToken => {
 618      if (!/^[0-9A-F]{32}$/i.test(newToken)) {
 619        return;
 620      }
 621  
 622      const elements = [].slice.call(document.getElementsByTagName('input'));
 623      elements.forEach(element => {
 624        if (element.type === 'hidden' && element.value === '1' && element.name.length === 32) {
 625          element.name = newToken;
 626        }
 627      });
 628    };
 629    /**
 630     * Method to perform AJAX request
 631     *
 632     * @param {Object} options   Request options:
 633     * {
 634     *    url:     'index.php', Request URL
 635     *    method:  'GET',       Request method GET (default), POST
 636     *    data:    null,        Data to be sent, see
 637     *                https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/send
 638     *    perform: true,        Perform the request immediately
 639     *              or return XMLHttpRequest instance and perform it later
 640     *    headers: null,        Object of custom headers, eg {'X-Foo': 'Bar', 'X-Bar': 'Foo'}
 641     *    promise: false        Whether return a Promise instance.
 642     *              When true then next options is ignored: perform, onSuccess, onError, onComplete
 643     *
 644     *    onBefore:  (xhr) => {}            // Callback on before the request
 645     *    onSuccess: (response, xhr) => {}, // Callback on the request success
 646     *    onError:   (xhr) => {},           // Callback on the request error
 647     *    onComplete: (xhr) => {},          // Callback on the request completed, with/without error
 648     * }
 649     *
 650     * @return XMLHttpRequest|Boolean
 651     *
 652     * @example
 653     *
 654     *   Joomla.request({
 655     *    url: 'index.php?option=com_example&view=example',
 656     *    onSuccess: (response, xhr) => {
 657     *     JSON.parse(response);
 658     *    }
 659     *   })
 660     *
 661     * @see    https://developer.mozilla.org/docs/Web/API/XMLHttpRequest
 662     */
 663  
 664  
 665    Joomla.request = options => {
 666      // Prepare the options
 667      const newOptions = Joomla.extend({
 668        url: '',
 669        method: 'GET',
 670        data: null,
 671        perform: true,
 672        promise: false
 673      }, options); // Setup XMLHttpRequest instance
 674  
 675      const createRequest = (onSuccess, onError) => {
 676        const xhr = new XMLHttpRequest();
 677        xhr.open(newOptions.method, newOptions.url, true); // Set the headers
 678  
 679        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
 680        xhr.setRequestHeader('X-Ajax-Engine', 'Joomla!');
 681  
 682        if (newOptions.method !== 'GET') {
 683          const token = Joomla.getOptions('csrf.token', '');
 684  
 685          if (token) {
 686            xhr.setRequestHeader('X-CSRF-Token', token);
 687          }
 688  
 689          if (typeof newOptions.data === 'string' && (!newOptions.headers || !newOptions.headers['Content-Type'])) {
 690            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
 691          }
 692        } // Custom headers
 693  
 694  
 695        if (newOptions.headers) {
 696          [].slice.call(Object.keys(newOptions.headers)).forEach(key => {
 697            // Allow request without Content-Type
 698            // eslint-disable-next-line no-empty
 699            if (key === 'Content-Type' && newOptions.headers['Content-Type'] === 'false') ; else {
 700              xhr.setRequestHeader(key, newOptions.headers[key]);
 701            }
 702          });
 703        }
 704  
 705        xhr.onreadystatechange = () => {
 706          // Request not finished
 707          if (xhr.readyState !== 4) {
 708            return;
 709          } // Request finished and response is ready
 710  
 711  
 712          if (xhr.status === 200) {
 713            if (newOptions.promise) {
 714              // A Promise accepts only one argument
 715              onSuccess.call(window, xhr);
 716            } else {
 717              onSuccess.call(window, xhr.responseText, xhr);
 718            }
 719          } else {
 720            onError.call(window, xhr);
 721          }
 722  
 723          if (newOptions.onComplete && !newOptions.promise) {
 724            newOptions.onComplete.call(window, xhr);
 725          }
 726        }; // Do request
 727  
 728  
 729        if (newOptions.perform) {
 730          if (newOptions.onBefore && newOptions.onBefore.call(window, xhr) === false) {
 731            // Request interrupted
 732            if (newOptions.promise) {
 733              onSuccess.call(window, xhr);
 734            }
 735  
 736            return xhr;
 737          }
 738  
 739          xhr.send(newOptions.data);
 740        }
 741  
 742        return xhr;
 743      }; // Return a Promise
 744  
 745  
 746      if (newOptions.promise) {
 747        return new Promise((resolve, reject) => {
 748          newOptions.perform = true;
 749          createRequest(resolve, reject);
 750        });
 751      } // Return a Request
 752  
 753  
 754      try {
 755        return createRequest(newOptions.onSuccess || (() => {}), newOptions.onError || (() => {}));
 756      } catch (error) {
 757        // eslint-disable-next-line no-unused-expressions,no-console
 758        console.error(error);
 759        return false;
 760      }
 761    };
 762  
 763    let lastRequestPromise;
 764    /**
 765     * Joomla Request queue.
 766     *
 767     * A FIFO queue of requests to execute serially. Used to prevent simultaneous execution of
 768     * multiple requests against the server which could trigger its Denial of Service protection.
 769     *
 770     * @param {object} options Options for Joomla.request()
 771     * @returns {Promise}
 772     */
 773  
 774    Joomla.enqueueRequest = options => {
 775      if (!options.promise) {
 776        throw new Error('Joomla.enqueueRequest supports only Joomla.request as Promise');
 777      }
 778  
 779      if (!lastRequestPromise) {
 780        lastRequestPromise = Joomla.request(options);
 781      } else {
 782        lastRequestPromise = lastRequestPromise.then(() => Joomla.request(options));
 783      }
 784  
 785      return lastRequestPromise;
 786    };
 787    /**
 788     *
 789     * @param {string} unsafeHtml The html for sanitization
 790     * @param {object} allowList The list of HTMLElements with an array of allowed attributes
 791     * @param {function} sanitizeFn A custom sanitization function
 792     *
 793     * @return string
 794     */
 795  
 796  
 797    Joomla.sanitizeHtml = (unsafeHtml, allowList, sanitizeFn) => {
 798      const allowed = allowList === undefined || allowList === null ? DefaultAllowlist : { ...DefaultAllowlist,
 799        ...allowList
 800      };
 801      return sanitizeHtml(unsafeHtml, allowed, sanitizeFn);
 802    };
 803    /**
 804     * Treat AJAX errors.
 805     * Used by some javascripts such as sendtestmail.js and permissions.js
 806     *
 807     * @param   {object}  xhr         XHR object.
 808     * @param   {string}  textStatus  Type of error that occurred.
 809     * @param   {string}  error       Textual portion of the HTTP status.
 810     *
 811     * @return  {object}  JavaScript object containing the system error message.
 812     *
 813     * @since  3.6.0
 814     */
 815  
 816  
 817    Joomla.ajaxErrorsMessages = (xhr, textStatus) => {
 818      const msg = {};
 819  
 820      if (textStatus === 'parsererror') {
 821        // For jQuery jqXHR
 822        const buf = []; // Html entity encode.
 823  
 824        let encodedJson = xhr.responseText.trim(); // eslint-disable-next-line no-plusplus
 825  
 826        for (let i = encodedJson.length - 1; i >= 0; i--) {
 827          buf.unshift(['&#', encodedJson[i].charCodeAt(), ';'].join(''));
 828        }
 829  
 830        encodedJson = buf.join('');
 831        msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_PARSE').replace('%s', encodedJson)];
 832      } else if (textStatus === 'nocontent') {
 833        msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_NO_CONTENT')];
 834      } else if (textStatus === 'timeout') {
 835        msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_TIMEOUT')];
 836      } else if (textStatus === 'abort') {
 837        msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_CONNECTION_ABORT')];
 838      } else if (xhr.responseJSON && xhr.responseJSON.message) {
 839        // For vanilla XHR
 840        msg.error = [`$Joomla.Text._('JLIB_JS_AJAX_ERROR_OTHER').replace('%s', xhr.status)} <em>$xhr.responseJSON.message}</em>`];
 841      } else if (xhr.statusText) {
 842        msg.error = [`$Joomla.Text._('JLIB_JS_AJAX_ERROR_OTHER').replace('%s', xhr.status)} <em>$xhr.statusText}</em>`];
 843      } else {
 844        msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_OTHER').replace('%s', xhr.status)];
 845      }
 846  
 847      return msg;
 848    };
 849  })(Joomla);


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