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