[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/media/system/js/fields/ -> joomla-field-subform-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    function _inheritsLoose(subClass, superClass) {
  24      subClass.prototype = Object.create(superClass.prototype);
  25      subClass.prototype.constructor = subClass;
  26  
  27      _setPrototypeOf(subClass, superClass);
  28    }
  29  
  30    function _getPrototypeOf(o) {
  31      _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
  32        return o.__proto__ || Object.getPrototypeOf(o);
  33      };
  34      return _getPrototypeOf(o);
  35    }
  36  
  37    function _setPrototypeOf(o, p) {
  38      _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
  39        o.__proto__ = p;
  40        return o;
  41      };
  42  
  43      return _setPrototypeOf(o, p);
  44    }
  45  
  46    function _isNativeReflectConstruct() {
  47      if (typeof Reflect === "undefined" || !Reflect.construct) return false;
  48      if (Reflect.construct.sham) return false;
  49      if (typeof Proxy === "function") return true;
  50  
  51      try {
  52        Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
  53        return true;
  54      } catch (e) {
  55        return false;
  56      }
  57    }
  58  
  59    function _construct(Parent, args, Class) {
  60      if (_isNativeReflectConstruct()) {
  61        _construct = Reflect.construct;
  62      } else {
  63        _construct = function _construct(Parent, args, Class) {
  64          var a = [null];
  65          a.push.apply(a, args);
  66          var Constructor = Function.bind.apply(Parent, a);
  67          var instance = new Constructor();
  68          if (Class) _setPrototypeOf(instance, Class.prototype);
  69          return instance;
  70        };
  71      }
  72  
  73      return _construct.apply(null, arguments);
  74    }
  75  
  76    function _isNativeFunction(fn) {
  77      return Function.toString.call(fn).indexOf("[native code]") !== -1;
  78    }
  79  
  80    function _wrapNativeSuper(Class) {
  81      var _cache = typeof Map === "function" ? new Map() : undefined;
  82  
  83      _wrapNativeSuper = function _wrapNativeSuper(Class) {
  84        if (Class === null || !_isNativeFunction(Class)) return Class;
  85  
  86        if (typeof Class !== "function") {
  87          throw new TypeError("Super expression must either be null or a function");
  88        }
  89  
  90        if (typeof _cache !== "undefined") {
  91          if (_cache.has(Class)) return _cache.get(Class);
  92  
  93          _cache.set(Class, Wrapper);
  94        }
  95  
  96        function Wrapper() {
  97          return _construct(Class, arguments, _getPrototypeOf(this).constructor);
  98        }
  99  
 100        Wrapper.prototype = Object.create(Class.prototype, {
 101          constructor: {
 102            value: Wrapper,
 103            enumerable: false,
 104            writable: true,
 105            configurable: true
 106          }
 107        });
 108        return _setPrototypeOf(Wrapper, Class);
 109      };
 110  
 111      return _wrapNativeSuper(Class);
 112    }
 113  
 114    function _assertThisInitialized(self) {
 115      if (self === void 0) {
 116        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
 117      }
 118  
 119      return self;
 120    }
 121  
 122    /**
 123     * @copyright  (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
 124     * @license    GNU General Public License version 2 or later; see LICENSE.txt
 125     */
 126    (function (customElements) {
 127      var KEYCODE = {
 128        SPACE: 32,
 129        ESC: 27,
 130        ENTER: 13
 131      };
 132      /**
 133       * Helper for testing whether a selection modifier is pressed
 134       * @param {Event} event
 135       *
 136       * @returns {boolean|*}
 137       */
 138  
 139      function hasModifier(event) {
 140        return event.ctrlKey || event.metaKey || event.shiftKey;
 141      }
 142  
 143      var JoomlaFieldSubform = /*#__PURE__*/function (_HTMLElement) {
 144        _inheritsLoose(JoomlaFieldSubform, _HTMLElement);
 145  
 146        function JoomlaFieldSubform() {
 147          var _this;
 148  
 149          _this = _HTMLElement.call(this) || this;
 150  
 151          var that = _assertThisInitialized(_this); // Get the rows container
 152  
 153  
 154          _this.containerWithRows = _assertThisInitialized(_this);
 155  
 156          if (_this.rowsContainer) {
 157            var allContainers = _this.querySelectorAll(_this.rowsContainer); // Find closest, and exclude nested
 158  
 159  
 160            Array.from(allContainers).forEach(function (container) {
 161              if (container.closest('joomla-field-subform') === _assertThisInitialized(_this)) {
 162                _this.containerWithRows = container;
 163              }
 164            });
 165          } // Keep track of row index, this is important to avoid a name duplication
 166          // Note: php side should reset the indexes each time, eg: $value = array_values($value);
 167  
 168  
 169          _this.lastRowIndex = _this.getRows().length - 1; // Template for the repeating group
 170  
 171          _this.template = ''; // Prepare a row template, and find available field names
 172  
 173          _this.prepareTemplate(); // Bind buttons
 174  
 175  
 176          if (_this.buttonAdd || _this.buttonRemove) {
 177            _this.addEventListener('click', function (event) {
 178              var btnAdd = null;
 179              var btnRem = null;
 180  
 181              if (that.buttonAdd) {
 182                btnAdd = event.target.matches(that.buttonAdd) ? event.target : event.target.closest(that.buttonAdd);
 183              }
 184  
 185              if (that.buttonRemove) {
 186                btnRem = event.target.matches(that.buttonRemove) ? event.target : event.target.closest(that.buttonRemove);
 187              } // Check active, with extra check for nested joomla-field-subform
 188  
 189  
 190              if (btnAdd && btnAdd.closest('joomla-field-subform') === that) {
 191                var row = btnAdd.closest(that.repeatableElement);
 192                row = row && row.closest('joomla-field-subform') === that ? row : null;
 193                that.addRow(row);
 194                event.preventDefault();
 195              } else if (btnRem && btnRem.closest('joomla-field-subform') === that) {
 196                var _row = btnRem.closest(that.repeatableElement);
 197  
 198                that.removeRow(_row);
 199                event.preventDefault();
 200              }
 201            });
 202  
 203            _this.addEventListener('keydown', function (event) {
 204              if (event.keyCode !== KEYCODE.SPACE) return;
 205              var isAdd = that.buttonAdd && event.target.matches(that.buttonAdd);
 206              var isRem = that.buttonRemove && event.target.matches(that.buttonRemove);
 207  
 208              if ((isAdd || isRem) && event.target.closest('joomla-field-subform') === that) {
 209                var row = event.target.closest(that.repeatableElement);
 210                row = row && row.closest('joomla-field-subform') === that ? row : null;
 211  
 212                if (isRem && row) {
 213                  that.removeRow(row);
 214                } else if (isAdd) {
 215                  that.addRow(row);
 216                }
 217  
 218                event.preventDefault();
 219              }
 220            });
 221          } // Sorting
 222  
 223  
 224          if (_this.buttonMove) {
 225            _this.setUpDragSort();
 226          }
 227  
 228          return _this;
 229        }
 230        /**
 231         * Search for existing rows
 232         * @returns {HTMLElement[]}
 233         */
 234  
 235  
 236        var _proto = JoomlaFieldSubform.prototype;
 237  
 238        _proto.getRows = function getRows() {
 239          var _this2 = this;
 240  
 241          var rows = Array.from(this.containerWithRows.children);
 242          var result = []; // Filter out the rows
 243  
 244          rows.forEach(function (row) {
 245            if (row.matches(_this2.repeatableElement)) {
 246              result.push(row);
 247            }
 248          });
 249          return result;
 250        }
 251        /**
 252         * Prepare a row template
 253         */
 254        ;
 255  
 256        _proto.prepareTemplate = function prepareTemplate() {
 257          var tmplElement = [].slice.call(this.children).filter(function (el) {
 258            return el.classList.contains('subform-repeatable-template-section');
 259          });
 260  
 261          if (tmplElement[0]) {
 262            this.template = tmplElement[0].innerHTML;
 263          }
 264  
 265          if (!this.template) {
 266            throw new Error('The row template is required for the subform element to work');
 267          }
 268        }
 269        /**
 270         * Add new row
 271         * @param {HTMLElement} after
 272         * @returns {HTMLElement}
 273         */
 274        ;
 275  
 276        _proto.addRow = function addRow(after) {
 277          // Count how many we already have
 278          var count = this.getRows().length;
 279  
 280          if (count >= this.maximum) {
 281            return null;
 282          } // Make a new row from the template
 283  
 284  
 285          var tmpEl;
 286  
 287          if (this.containerWithRows.nodeName === 'TBODY' || this.containerWithRows.nodeName === 'TABLE') {
 288            tmpEl = document.createElement('tbody');
 289          } else {
 290            tmpEl = document.createElement('div');
 291          }
 292  
 293          tmpEl.innerHTML = this.template;
 294          var row = tmpEl.children[0]; // Add to container
 295  
 296          if (after) {
 297            after.parentNode.insertBefore(row, after.nextSibling);
 298          } else {
 299            this.containerWithRows.append(row);
 300          } // Add draggable attributes
 301  
 302  
 303          if (this.buttonMove) {
 304            row.setAttribute('draggable', 'false');
 305            row.setAttribute('aria-grabbed', 'false');
 306            row.setAttribute('tabindex', '0');
 307          } // Marker that it is new
 308  
 309  
 310          row.setAttribute('data-new', '1'); // Fix names and ids, and reset values
 311  
 312          this.fixUniqueAttributes(row, count); // Tell about the new row
 313  
 314          this.dispatchEvent(new CustomEvent('subform-row-add', {
 315            detail: {
 316              row: row
 317            },
 318            bubbles: true
 319          }));
 320          row.dispatchEvent(new CustomEvent('joomla:updated', {
 321            bubbles: true,
 322            cancelable: true
 323          }));
 324          return row;
 325        }
 326        /**
 327         * Remove the row
 328         * @param {HTMLElement} row
 329         */
 330        ;
 331  
 332        _proto.removeRow = function removeRow(row) {
 333          // Count how much we have
 334          var count = this.getRows().length;
 335  
 336          if (count <= this.minimum) {
 337            return;
 338          } // Tell about the row will be removed
 339  
 340  
 341          this.dispatchEvent(new CustomEvent('subform-row-remove', {
 342            detail: {
 343              row: row
 344            },
 345            bubbles: true
 346          }));
 347          row.dispatchEvent(new CustomEvent('joomla:removed', {
 348            bubbles: true,
 349            cancelable: true
 350          }));
 351          row.parentNode.removeChild(row);
 352        }
 353        /**
 354         * Fix name and id for fields that are in the row
 355         * @param {HTMLElement} row
 356         * @param {Number} count
 357         */
 358        ;
 359  
 360        _proto.fixUniqueAttributes = function fixUniqueAttributes(row, count) {
 361          var _this3 = this;
 362  
 363          var countTmp = count || 0;
 364          var group = row.getAttribute('data-group'); // current group name
 365  
 366          var basename = row.getAttribute('data-base-name');
 367          var countnew = Math.max(this.lastRowIndex, countTmp);
 368          var groupnew = basename + countnew; // new group name
 369  
 370          this.lastRowIndex = countnew + 1;
 371          row.setAttribute('data-group', groupnew); // Fix inputs that have a "name" attribute
 372  
 373          var haveName = row.querySelectorAll('[name]');
 374          var ids = {}; // Collect id for fix checkboxes and radio
 375          // Filter out nested
 376  
 377          haveName = [].slice.call(haveName).filter(function (el) {
 378            if (el.nodeName === 'JOOMLA-FIELD-SUBFORM') {
 379              // Skip self in .closest() call
 380              return el.parentElement.closest('joomla-field-subform') === _this3;
 381            }
 382  
 383            return el.closest('joomla-field-subform') === _this3;
 384          });
 385          haveName.forEach(function (elem) {
 386            var $el = elem;
 387            var name = $el.getAttribute('name');
 388            var aria = $el.getAttribute('aria-describedby');
 389            var id = name.replace(/(\[\]$)/g, '').replace(/(\]\[)/g, '__').replace(/\[/g, '_').replace(/\]/g, ''); // id from name
 390  
 391            var nameNew = name.replace("[" + group + "][", "[" + groupnew + "]["); // New name
 392  
 393            var idNew = id.replace(group, groupnew).replace(/\W/g, '_'); // Count new id
 394  
 395            var countMulti = 0; // count for multiple radio/checkboxes
 396  
 397            var forOldAttr = id; // Fix "for" in the labels
 398  
 399            if ($el.type === 'checkbox' && name.match(/\[\]$/)) {
 400              // <input type="checkbox" name="name[]"> fix
 401              // Recount id
 402              countMulti = ids[id] ? ids[id].length : 0;
 403  
 404              if (!countMulti) {
 405                // Set the id for fieldset and group label
 406                var fieldset = $el.closest('fieldset.checkboxes');
 407                var elLbl = row.querySelector("label[for=\"" + id + "\"]");
 408  
 409                if (fieldset) {
 410                  fieldset.setAttribute('id', idNew);
 411                }
 412  
 413                if (elLbl) {
 414                  elLbl.setAttribute('for', idNew);
 415                  elLbl.setAttribute('id', idNew + "-lbl");
 416                }
 417              }
 418  
 419              forOldAttr += countMulti;
 420              idNew += countMulti;
 421            } else if ($el.type === 'radio') {
 422              // <input type="radio"> fix
 423              // Recount id
 424              countMulti = ids[id] ? ids[id].length : 0;
 425  
 426              if (!countMulti) {
 427                // Set the id for fieldset and group label
 428                var _fieldset = $el.closest('fieldset.radio');
 429  
 430                var _elLbl = row.querySelector("label[for=\"" + id + "\"]");
 431  
 432                if (_fieldset) {
 433                  _fieldset.setAttribute('id', idNew);
 434                }
 435  
 436                if (_elLbl) {
 437                  _elLbl.setAttribute('for', idNew);
 438  
 439                  _elLbl.setAttribute('id', idNew + "-lbl");
 440                }
 441              }
 442  
 443              forOldAttr += countMulti;
 444              idNew += countMulti;
 445            } // Cache already used id
 446  
 447  
 448            if (ids[id]) {
 449              ids[id].push(true);
 450            } else {
 451              ids[id] = [true];
 452            } // Replace the name to new one
 453  
 454  
 455            $el.name = nameNew;
 456  
 457            if ($el.id) {
 458              $el.id = idNew;
 459            }
 460  
 461            if (aria) {
 462              $el.setAttribute('aria-describedby', nameNew + "-desc");
 463            } // Check if there is a label for this input
 464  
 465  
 466            var lbl = row.querySelector("label[for=\"" + forOldAttr + "\"]");
 467  
 468            if (lbl) {
 469              lbl.setAttribute('for', idNew);
 470              lbl.setAttribute('id', idNew + "-lbl");
 471            }
 472          });
 473        }
 474        /**
 475         * Use of HTML Drag and Drop API
 476         * https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API
 477         * https://www.sitepoint.com/accessible-drag-drop/
 478         */
 479        ;
 480  
 481        _proto.setUpDragSort = function setUpDragSort() {
 482          var that = this; // Self reference
 483  
 484          var item = null; // Storing the selected item
 485  
 486          var touched = false; // We have a touch events
 487          // Find all existing rows and add draggable attributes
 488  
 489          var rows = Array.from(this.getRows());
 490          rows.forEach(function (row) {
 491            row.setAttribute('draggable', 'false');
 492            row.setAttribute('aria-grabbed', 'false');
 493            row.setAttribute('tabindex', '0');
 494          }); // Helper method to test whether Handler was clicked
 495  
 496          function getMoveHandler(element) {
 497            return !element.form // This need to test whether the element is :input
 498            && element.matches(that.buttonMove) ? element : element.closest(that.buttonMove);
 499          } // Helper method to move row to selected position
 500  
 501  
 502          function switchRowPositions(src, dest) {
 503            var isRowBefore = false;
 504  
 505            if (src.parentNode === dest.parentNode) {
 506              for (var cur = src; cur; cur = cur.previousSibling) {
 507                if (cur === dest) {
 508                  isRowBefore = true;
 509                  break;
 510                }
 511              }
 512            }
 513  
 514            if (isRowBefore) {
 515              dest.parentNode.insertBefore(src, dest);
 516            } else {
 517              dest.parentNode.insertBefore(src, dest.nextSibling);
 518            }
 519          }
 520          /**
 521           *  Touch interaction:
 522           *
 523           *  - a touch of "move button" marks a row draggable / "selected",
 524           *     or deselect previous selected
 525           *
 526           *  - a touch of "move button" in the destination row will move
 527           *     a selected row to a new position
 528           */
 529  
 530  
 531          this.addEventListener('touchstart', function (event) {
 532            touched = true; // Check for .move button
 533  
 534            var handler = getMoveHandler(event.target);
 535            var row = handler ? handler.closest(that.repeatableElement) : null;
 536  
 537            if (!row || row.closest('joomla-field-subform') !== that) {
 538              return;
 539            } // First selection
 540  
 541  
 542            if (!item) {
 543              row.setAttribute('draggable', 'true');
 544              row.setAttribute('aria-grabbed', 'true');
 545              item = row;
 546            } else {
 547              // Second selection
 548              // Move to selected position
 549              if (row !== item) {
 550                switchRowPositions(item, row);
 551              }
 552  
 553              item.setAttribute('draggable', 'false');
 554              item.setAttribute('aria-grabbed', 'false');
 555              item = null;
 556            }
 557  
 558            event.preventDefault();
 559          }); // Mouse interaction
 560          // - mouse down, enable "draggable" and allow to drag the row,
 561          // - mouse up, disable "draggable"
 562  
 563          this.addEventListener('mousedown', function (_ref) {
 564            var target = _ref.target;
 565            if (touched) return; // Check for .move button
 566  
 567            var handler = getMoveHandler(target);
 568            var row = handler ? handler.closest(that.repeatableElement) : null;
 569  
 570            if (!row || row.closest('joomla-field-subform') !== that) {
 571              return;
 572            }
 573  
 574            row.setAttribute('draggable', 'true');
 575            row.setAttribute('aria-grabbed', 'true');
 576            item = row;
 577          });
 578          this.addEventListener('mouseup', function () {
 579            if (item && !touched) {
 580              item.setAttribute('draggable', 'false');
 581              item.setAttribute('aria-grabbed', 'false');
 582              item = null;
 583            }
 584          }); // Keyboard interaction
 585          // - "tab" to navigate to needed row,
 586          // - modifier (ctr,alt,shift) + "space" select the row,
 587          // - "tab" to select destination,
 588          // - "enter" to place selected row in to destination
 589          // - "esc" to cancel selection
 590  
 591          this.addEventListener('keydown', function (event) {
 592            if (event.keyCode !== KEYCODE.ESC && event.keyCode !== KEYCODE.SPACE && event.keyCode !== KEYCODE.ENTER || event.target.form || !event.target.matches(that.repeatableElement)) {
 593              return;
 594            }
 595  
 596            var row = event.target; // Make sure we handle correct children
 597  
 598            if (!row || row.closest('joomla-field-subform') !== that) {
 599              return;
 600            } // Space is the selection or unselection keystroke
 601  
 602  
 603            if (event.keyCode === KEYCODE.SPACE && hasModifier(event)) {
 604              // Unselect previously selected
 605              if (row.getAttribute('aria-grabbed') === 'true') {
 606                row.setAttribute('draggable', 'false');
 607                row.setAttribute('aria-grabbed', 'false');
 608                item = null;
 609              } else {
 610                // Select new
 611                // If there was previously selected
 612                if (item) {
 613                  item.setAttribute('draggable', 'false');
 614                  item.setAttribute('aria-grabbed', 'false');
 615                  item = null;
 616                } // Mark new selection
 617  
 618  
 619                row.setAttribute('draggable', 'true');
 620                row.setAttribute('aria-grabbed', 'true');
 621                item = row;
 622              } // Prevent default to suppress any native actions
 623  
 624  
 625              event.preventDefault();
 626            } // Escape is the abort keystroke (for any target element)
 627  
 628  
 629            if (event.keyCode === KEYCODE.ESC && item) {
 630              item.setAttribute('draggable', 'false');
 631              item.setAttribute('aria-grabbed', 'false');
 632              item = null;
 633            } // Enter, to place selected item in selected position
 634  
 635  
 636            if (event.keyCode === KEYCODE.ENTER && item) {
 637              item.setAttribute('draggable', 'false');
 638              item.setAttribute('aria-grabbed', 'false'); // Do nothing here
 639  
 640              if (row === item) {
 641                item = null;
 642                return;
 643              } // Move the item to selected position
 644  
 645  
 646              switchRowPositions(item, row);
 647              event.preventDefault();
 648              item = null;
 649            }
 650          }); // dragstart event to initiate mouse dragging
 651  
 652          this.addEventListener('dragstart', function (_ref2) {
 653            var dataTransfer = _ref2.dataTransfer;
 654  
 655            if (item) {
 656              // We going to move the row
 657              dataTransfer.effectAllowed = 'move'; // This need to work in Firefox and IE10+
 658  
 659              dataTransfer.setData('text', '');
 660            }
 661          });
 662          this.addEventListener('dragover', function (event) {
 663            if (item) {
 664              event.preventDefault();
 665            }
 666          }); // Handle drag action, move element to hovered position
 667  
 668          this.addEventListener('dragenter', function (_ref3) {
 669            var target = _ref3.target;
 670  
 671            // Make sure the target in the correct container
 672            if (!item || target.parentElement.closest('joomla-field-subform') !== that) {
 673              return;
 674            } // Find a hovered row
 675  
 676  
 677            var row = target.closest(that.repeatableElement); // One more check for correct parent
 678  
 679            if (!row || row.closest('joomla-field-subform') !== that) return;
 680            switchRowPositions(item, row);
 681          }); // dragend event to clean-up after drop or abort
 682          // which fires whether or not the drop target was valid
 683  
 684          this.addEventListener('dragend', function () {
 685            if (item) {
 686              item.setAttribute('draggable', 'false');
 687              item.setAttribute('aria-grabbed', 'false');
 688              item = null;
 689            }
 690          });
 691        };
 692  
 693        _createClass(JoomlaFieldSubform, [{
 694          key: "buttonAdd",
 695          get: // Attribute getters
 696          function get() {
 697            return this.getAttribute('button-add');
 698          }
 699        }, {
 700          key: "buttonRemove",
 701          get: function get() {
 702            return this.getAttribute('button-remove');
 703          }
 704        }, {
 705          key: "buttonMove",
 706          get: function get() {
 707            return this.getAttribute('button-move');
 708          }
 709        }, {
 710          key: "rowsContainer",
 711          get: function get() {
 712            return this.getAttribute('rows-container');
 713          }
 714        }, {
 715          key: "repeatableElement",
 716          get: function get() {
 717            return this.getAttribute('repeatable-element');
 718          }
 719        }, {
 720          key: "minimum",
 721          get: function get() {
 722            return this.getAttribute('minimum');
 723          }
 724        }, {
 725          key: "maximum",
 726          get: function get() {
 727            return this.getAttribute('maximum');
 728          }
 729        }, {
 730          key: "name",
 731          get: function get() {
 732            return this.getAttribute('name');
 733          },
 734          set: function set(value) {
 735            // Update the template
 736            this.template = this.template.replace(new RegExp(" name=\"" + this.name.replace(/[[\]]/g, '\\$&'), 'g'), " name=\"" + value);
 737            this.setAttribute('name', value);
 738          }
 739        }]);
 740  
 741        return JoomlaFieldSubform;
 742      }( /*#__PURE__*/_wrapNativeSuper(HTMLElement));
 743  
 744      customElements.define('joomla-field-subform', JoomlaFieldSubform);
 745    })(customElements);
 746  
 747  })();


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