[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

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

   1  Joomla = window.Joomla || {};
   2  
   3  (Joomla => {
   4    /**
   5     * Method that resets the filter inputs and submits the relative form
   6     *
   7     * @param {HTMLElement}  element  The element that initiates the call
   8     * @returns {void}
   9     * @since   4.0.0
  10     */
  11  
  12    Joomla.resetFilters = element => {
  13      const {
  14        form
  15      } = element;
  16  
  17      if (!form) {
  18        throw new Error('Element must be inside a form!');
  19      }
  20  
  21      const elementsArray = [].slice.call(form.elements);
  22  
  23      if (elementsArray.length) {
  24        const newElementsArray = [];
  25        elementsArray.forEach(elem => {
  26          // Skip the token, the task, the boxchecked and the calling element
  27          if (elem.getAttribute('name') === 'task' || elem.getAttribute('name') === 'boxchecked' || elem.value === '1' && /^[0-9A-F]{32}$/i.test(elem.name) || elem === element) {
  28            return;
  29          }
  30  
  31          newElementsArray.push(elem);
  32        }); // Reset all filters
  33  
  34        newElementsArray.forEach(elem => {
  35          elem.value = '';
  36        });
  37        form.submit();
  38      }
  39    };
  40  
  41    class Searchtools {
  42      constructor(elem, options) {
  43        const defaults = {
  44          // Form options
  45          formSelector: '.js-stools-form',
  46          // Search
  47          searchFieldSelector: '.js-stools-field-search',
  48          clearBtnSelector: '.js-stools-btn-clear',
  49          // Global container
  50          mainContainerSelector: '.js-stools',
  51          // Filter fields
  52          searchBtnSelector: '.js-stools-btn-search',
  53          filterBtnSelector: '.js-stools-btn-filter',
  54          filterContainerSelector: '.js-stools-container-filters',
  55          filtersHidden: true,
  56          // List fields
  57          listBtnSelector: '.js-stools-btn-list',
  58          listContainerSelector: '.js-stools-container-list',
  59          listHidden: true,
  60          // Ordering specific
  61          orderColumnSelector: '.js-stools-column-order',
  62          orderBtnSelector: '.js-stools-btn-order',
  63          orderFieldSelector: '.js-stools-field-order',
  64          orderFieldName: 'list[fullordering]',
  65          limitFieldSelector: '.js-stools-field-limit',
  66          defaultLimit: 20,
  67          activeOrder: null,
  68          activeDirection: 'ASC',
  69          // Extra
  70          clearListOptions: false
  71        };
  72        this.element = elem;
  73        this.options = Joomla.extend(defaults, options); // Initialise selectors
  74  
  75        this.theForm = document.querySelector(this.options.formSelector); // Filters
  76  
  77        this.filterButton = document.querySelector(`$this.options.formSelector} $this.options.filterBtnSelector}`);
  78        this.filterContainer = document.querySelector(`$this.options.formSelector} $this.options.filterContainerSelector}`) ? document.querySelector(`$this.options.formSelector} $this.options.filterContainerSelector}`) : '';
  79        this.filtersHidden = this.options.filtersHidden; // List fields
  80  
  81        this.listButton = document.querySelector(this.options.listBtnSelector);
  82        this.listContainer = document.querySelector(`$this.options.formSelector} $this.options.listContainerSelector}`);
  83        this.listHidden = this.options.listHidden; // Main container
  84  
  85        this.mainContainer = document.querySelector(this.options.mainContainerSelector); // Search
  86  
  87        this.searchButton = document.querySelector(`$this.options.formSelector} $this.options.searchBtnSelector}`);
  88        this.searchField = document.querySelector(`$this.options.formSelector} $this.options.searchFieldSelector}`);
  89        this.searchString = null;
  90        this.clearButton = document.querySelector(this.options.clearBtnSelector); // Ordering
  91  
  92        this.orderCols = Array.prototype.slice.call(document.querySelectorAll(`$this.options.formSelector} $this.options.orderColumnSelector}`));
  93        this.orderField = document.querySelector(`$this.options.formSelector} $this.options.orderFieldSelector}`); // Limit
  94  
  95        this.limitField = document.querySelector(`$this.options.formSelector} $this.options.limitFieldSelector}`); // Init trackers
  96  
  97        this.activeColumn = null;
  98        this.activeDirection = this.options.activeDirection;
  99        this.activeOrder = this.options.activeOrder;
 100        this.activeLimit = null; // Extra options
 101  
 102        this.clearListOptions = this.options.clearListOptions;
 103        const self = this; // Get values
 104  
 105        this.searchString = this.searchField ? this.searchField.value : ''; // Do some binding
 106  
 107        this.showFilters = this.showFilters.bind(this);
 108        this.hideFilters = this.hideFilters.bind(this);
 109        this.showList = this.showList.bind(this);
 110        this.hideList = this.hideList.bind(this);
 111        this.toggleFilters = this.toggleFilters.bind(this);
 112        this.toggleList = this.toggleList.bind(this);
 113        this.checkFilter = this.checkFilter.bind(this);
 114        this.clear = this.clear.bind(this);
 115        this.createOrderField = this.createOrderField.bind(this);
 116        this.checkActiveStatus = this.checkActiveStatus.bind(this);
 117        this.activeFilter = this.activeFilter.bind(this);
 118        this.deactiveFilter = this.deactiveFilter.bind(this);
 119        this.getFilterFields = this.getFilterFields.bind(this);
 120        this.getListFields = this.getListFields.bind(this);
 121        this.hideContainer = this.hideContainer.bind(this);
 122        this.showContainer = this.showContainer.bind(this);
 123        this.toggleContainer = this.toggleContainer.bind(this);
 124        this.toggleDirection = this.toggleDirection.bind(this);
 125        this.updateFieldValue = this.updateFieldValue.bind(this);
 126        this.findOption = this.findOption.bind(this);
 127  
 128        if (this.filterContainer && this.filterContainer.classList.contains('js-stools-container-filters-visible')) {
 129          this.showFilters();
 130          this.showList();
 131        } else {
 132          this.hideFilters();
 133          this.hideList();
 134        }
 135  
 136        if (this.filterButton) {
 137          this.filterButton.addEventListener('click', e => {
 138            self.toggleFilters();
 139            e.stopPropagation();
 140            e.preventDefault();
 141          });
 142        }
 143  
 144        if (this.listButton) {
 145          this.listButton.addEventListener('click', e => {
 146            self.toggleList();
 147            e.stopPropagation();
 148            e.preventDefault();
 149          });
 150        } // Do we need to add to mark filter as enabled?
 151  
 152  
 153        this.getFilterFields().forEach(i => {
 154          self.checkFilter(i);
 155          i.addEventListener('change', () => {
 156            self.checkFilter(i);
 157          });
 158        });
 159  
 160        if (this.clearButton) {
 161          this.clearButton.addEventListener('click', self.clear);
 162        } // Check/create ordering field
 163  
 164  
 165        this.createOrderField();
 166        this.orderCols.forEach(item => {
 167          item.addEventListener('click', ({
 168            target
 169          }) => {
 170            const element = target.tagName.toLowerCase() === 'span' ? target.parentNode : target; // Order to set
 171  
 172            const newOrderCol = element.getAttribute('data-order');
 173            const newDirection = element.getAttribute('data-direction');
 174            const newOrdering = `$newOrderCol} $newDirection}`; // The data-order attribute is required
 175  
 176            if (newOrderCol.length) {
 177              self.activeColumn = newOrderCol;
 178  
 179              if (newOrdering !== self.activeOrder) {
 180                self.activeDirection = newDirection;
 181                self.activeOrder = newOrdering; // Update the order field
 182  
 183                self.updateFieldValue(self.orderField, newOrdering);
 184              } else {
 185                self.toggleDirection();
 186              }
 187  
 188              self.theForm.submit();
 189            }
 190          });
 191        });
 192        this.checkActiveStatus(this);
 193      }
 194  
 195      checkFilter(element) {
 196        if (element.tagName.toLowerCase() === 'select') {
 197          const option = element.querySelector('option:checked');
 198  
 199          if (option) {
 200            if (option.value !== '') {
 201              this.activeFilter(element, this);
 202            } else {
 203              this.deactiveFilter(element, this);
 204            }
 205          }
 206        } else if (element.value !== '') {
 207          this.activeFilter(element, this);
 208        } else {
 209          this.deactiveFilter(element, this);
 210        }
 211      }
 212  
 213      clear() {
 214        const self = this;
 215  
 216        if (self.searchField) {
 217          self.searchField.value = '';
 218        }
 219  
 220        self.getFilterFields().forEach(i => {
 221          i.value = '';
 222          self.checkFilter(i);
 223  
 224          if (window.jQuery && window.jQuery.chosen) {
 225            window.jQuery(i).trigger('chosen:updated');
 226          }
 227        });
 228  
 229        if (self.clearListOptions) {
 230          self.getListFields().forEach(i => {
 231            i.value = '';
 232            self.checkFilter(i);
 233  
 234            if (window.jQuery && window.jQuery.chosen) {
 235              window.jQuery(i).trigger('chosen:updated');
 236            }
 237          }); // Special case to limit box to the default config limit
 238  
 239          document.querySelector('#list_limit').value = self.options.defaultLimit;
 240  
 241          if (window.jQuery && window.jQuery.chosen) {
 242            window.jQuery('#list_limit').trigger('chosen:updated');
 243          }
 244        }
 245  
 246        self.theForm.submit();
 247      } // eslint-disable-next-line class-methods-use-this
 248  
 249  
 250      updateFilterCount(count) {
 251        if (this.clearButton) {
 252          this.clearButton.disabled = count === 0 && !this.searchString.length;
 253        }
 254      } // eslint-disable-next-line class-methods-use-this
 255  
 256  
 257      checkActiveStatus(cont) {
 258        let activeFilterCount = 0;
 259        this.getFilterFields().forEach(item => {
 260          if (item.classList.contains('active')) {
 261            activeFilterCount += 1;
 262            cont.filterButton.classList.remove('btn-secondary');
 263            cont.filterButton.classList.add('btn-primary');
 264          }
 265        }); // If there are no active filters - remove the filtered caption area from the table
 266  
 267        if (activeFilterCount === 0) {
 268          const filteredByCaption = document.getElementById('filteredBy');
 269  
 270          if (filteredByCaption) {
 271            filteredByCaption.parentNode.removeChild(filteredByCaption);
 272          }
 273        } // Disable clear button when no filter is active and search is empty
 274  
 275  
 276        if (this.clearButton) {
 277          this.clearButton.disabled = activeFilterCount === 0 && !this.searchString.length;
 278        }
 279      } // eslint-disable-next-line class-methods-use-this
 280  
 281  
 282      activeFilter(element) {
 283        element.classList.add('active');
 284        const chosenId = `#$element.getAttribute('id')}`;
 285        const tmpEl = element.querySelector(chosenId);
 286  
 287        if (tmpEl) {
 288          tmpEl.classList.add('active');
 289        } // Add all active filters to the table caption for screen-readers
 290  
 291  
 292        const filteredByCaption = document.getElementById('filteredBy');
 293        const isHidden = Object.prototype.hasOwnProperty.call(element.attributes, 'type') && element.attributes.type.value === 'hidden'; // The caption won't exist if no items match the filters so check for the element first
 294  
 295        if (filteredByCaption && !isHidden) {
 296          let captionContent = '';
 297  
 298          if (element.tagName.toLowerCase() === 'select') {
 299            if (element.multiple === true) {
 300              const selectedOptions = element.querySelectorAll('option:checked');
 301              const selectedTextValues = [].slice.call(selectedOptions).map(el => el.text);
 302              captionContent = `$element.labels[0].textContent} - $selectedTextValues.join()}`;
 303            } else {
 304              captionContent = `$element.labels[0].textContent} - $element.options[element.selectedIndex].text}`;
 305            }
 306          } else {
 307            captionContent = `$element.labels[0].textContent} - $element.value}`;
 308          }
 309  
 310          filteredByCaption.textContent += captionContent;
 311        }
 312      } // eslint-disable-next-line class-methods-use-this
 313  
 314  
 315      deactiveFilter(element) {
 316        element.classList.remove('active');
 317        const chosenId = `#$element.getAttribute('id')}`;
 318        const tmpEl = element.querySelector(chosenId);
 319  
 320        if (tmpEl) {
 321          tmpEl.classList.remove('active');
 322        }
 323      } // eslint-disable-next-line consistent-return
 324  
 325  
 326      getFilterFields() {
 327        if (this.filterContainer) {
 328          return Array.prototype.slice.call(this.filterContainer.querySelectorAll('select,input'));
 329        }
 330  
 331        return [];
 332      }
 333  
 334      getListFields() {
 335        return Array.prototype.slice.call(this.listContainer.querySelectorAll('select'));
 336      } // Common container functions
 337      // eslint-disable-next-line class-methods-use-this
 338  
 339  
 340      hideContainer(container) {
 341        if (container) {
 342          container.classList.remove('js-stools-container-filters-visible');
 343          document.body.classList.remove('filters-shown');
 344        }
 345      } // eslint-disable-next-line class-methods-use-this
 346  
 347  
 348      showContainer(container) {
 349        container.classList.add('js-stools-container-filters-visible');
 350        document.body.classList.add('filters-shown');
 351      }
 352  
 353      toggleContainer(container) {
 354        if (container.classList.contains('js-stools-container-filters-visible')) {
 355          this.hideContainer(container);
 356        } else {
 357          this.showContainer(container);
 358        }
 359      } // List container management
 360  
 361  
 362      hideList() {
 363        this.hideContainer(this.filterContainer);
 364      }
 365  
 366      showList() {
 367        this.showContainer(this.filterContainer);
 368      }
 369  
 370      toggleList() {
 371        this.toggleContainer(this.filterContainer);
 372      } // Filters container management
 373  
 374  
 375      hideFilters() {
 376        this.hideContainer(this.filterContainer);
 377      }
 378  
 379      showFilters() {
 380        this.showContainer(this.filterContainer);
 381      }
 382  
 383      toggleFilters() {
 384        this.toggleContainer(this.filterContainer);
 385      }
 386  
 387      toggleDirection() {
 388        const self = this;
 389        let newDirection = 'ASC';
 390  
 391        if (self.activeDirection.toUpperCase() === 'ASC') {
 392          newDirection = 'DESC';
 393        }
 394  
 395        self.activeDirection = newDirection;
 396        self.activeOrder = `$self.activeColumn} $newDirection}`;
 397        self.updateFieldValue(self.orderField, self.activeOrder);
 398      }
 399  
 400      createOrderField() {
 401        const self = this;
 402  
 403        if (!this.orderField) {
 404          this.orderField = document.createElement('input');
 405          this.orderField.setAttribute('type', 'hidden');
 406          this.orderField.setAttribute('id', 'js-stools-field-order');
 407          this.orderField.setAttribute('class', 'js-stools-field-order');
 408          this.orderField.setAttribute('name', self.options.orderFieldName);
 409          this.orderField.setAttribute('value', `$self.activeOrder} $this.activeDirection}`);
 410          this.theForm.append(this.orderField);
 411        } // Add missing columns to the order select
 412  
 413  
 414        if (this.orderField.tagName.toLowerCase() === 'select') {
 415          const allOptions = [].slice.call(this.orderField.options);
 416          allOptions.forEach(option => {
 417            let value = option.getAttribute('data-order');
 418            const name = option.getAttribute('data-name');
 419            const direction = option.getAttribute('data-direction');
 420  
 421            if (value && value.length) {
 422              value = `$value} $direction}`;
 423              let $option = self.findOption(self.orderField, value);
 424  
 425              if (!$option.length) {
 426                $option = document.createElement('option');
 427                $option.text = name;
 428                $option.value = value; // If it is the active option select it
 429  
 430                if (option.classList.contains('active')) {
 431                  $option.setAttribute('selected', 'selected');
 432                } // Append the option and repopulate the chosen field
 433  
 434  
 435                this.orderFieldName.innerHTML += Joomla.sanitizeHtml($option);
 436              }
 437            }
 438          });
 439  
 440          if (window.jQuery && window.jQuery.chosen) {
 441            window.jQuery(this.orderField).trigger('chosen:updated');
 442          }
 443        }
 444  
 445        this.activeOrder = this.orderField.value;
 446      } // eslint-disable-next-line class-methods-use-this
 447  
 448  
 449      updateFieldValue(field, newValue) {
 450        const type = field.getAttribute('type');
 451  
 452        if (type === 'hidden' || type === 'text') {
 453          field.setAttribute('value', newValue);
 454        } else if (field.tagName.toLowerCase() === 'select') {
 455          const allOptions = [].slice.call(field.options);
 456          let desiredOption; // Select the option result
 457  
 458          allOptions.forEach(option => {
 459            if (option.value === newValue) {
 460              desiredOption = option;
 461            }
 462          });
 463  
 464          if (desiredOption && desiredOption.length) {
 465            desiredOption.setAttribute('selected', 'selected');
 466          } else {
 467            // If the option does not exist create it on the fly
 468            const option = document.createElement('option');
 469            option.text = newValue;
 470            option.value = newValue;
 471            option.setAttribute('selected', 'selected'); // Append the option and repopulate the chosen field
 472  
 473            field.appendChild(option);
 474          }
 475  
 476          field.value = newValue; // Trigger the chosen update
 477  
 478          if (window.jQuery && window.jQuery.chosen) {
 479            field.trigger('chosen:updated');
 480          }
 481        }
 482      } // eslint-disable-next-line class-methods-use-this,consistent-return
 483  
 484  
 485      findOption(select, value) {
 486        // eslint-disable-next-line no-plusplus
 487        for (let i = 0, l = select.length; l > i; i++) {
 488          if (select[i].value === value) {
 489            return select[i];
 490          }
 491        }
 492      }
 493  
 494    }
 495  
 496    const onBoot = () => {
 497      if (Joomla.getOptions('searchtools')) {
 498        const options = Joomla.getOptions('searchtools');
 499        const element = document.querySelector(options.selector); // eslint-disable-next-line no-new
 500  
 501        new Searchtools(element, options);
 502      }
 503  
 504      const sort = document.getElementById('sorted');
 505      const order = document.getElementById('orderedBy');
 506  
 507      if (sort && sort.hasAttribute('data-caption') && order) {
 508        const orderedBy = sort.getAttribute('data-caption');
 509        order.textContent += orderedBy;
 510      }
 511  
 512      if (sort && sort.hasAttribute('data-sort')) {
 513        const ariasort = sort.getAttribute('data-sort');
 514        sort.parentNode.setAttribute('aria-sort', ariasort);
 515      } // Cleanup
 516  
 517  
 518      document.removeEventListener('DOMContentLoaded', onBoot);
 519    }; // Execute on DOM Loaded Event
 520  
 521  
 522    document.addEventListener('DOMContentLoaded', onBoot);
 523  })(Joomla);


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