[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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);
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 |