PATH:
home
/
niazsaze
/
public_html
/
wp-content
/
plugins
/
wp-statistics
/
assets
/
dev
/
javascript
/
filters
function FilterGenerator(containerSelector) { this.container = document.querySelector(containerSelector); if (!this.container) { throw new Error(`Container ${containerSelector} not found`); } } /** * Checks if an element with the specified name already exists in the container. * @param {string} name - The name attribute to check. * @returns {boolean} - True if the element exists, false otherwise. */ FilterGenerator.prototype.elementExists = function (name) { return !!this.container.querySelector(`[name="${name}"]`); }; /** * Creates a wrapper div with global and unique classes. * @param {string} name - Field name for unique class. * @returns {HTMLElement} - The wrapper `<div>`. */ FilterGenerator.prototype.ensureFooterExists = function () { let footer = this.container.querySelector('.wps-filter-footer'); if (!footer) { footer = document.createElement('div'); footer.classList.add('wps-filter-footer'); this.container.appendChild(footer); } return footer; }; FilterGenerator.prototype.createWrapper = function (name, reset = false) { const wrapper = document.createElement('div'); if (reset) { wrapper.classList.add(name); return wrapper; } wrapper.classList.add('wps-filter-item', `wps-filter-${name}`); return wrapper; }; /** * Creates a `<span>` label for a field. * @param {string} label - Label text. * @returns {HTMLElement} - The label `<span>`. */ FilterGenerator.prototype.createLabel = function (label, name) { const labelElement = document.createElement('span'); labelElement.className = 'wps-filter-label'; labelElement.id = `wps-label-${name}`; labelElement.textContent = label; return labelElement; }; /** * Creates a `<select>` element and appends it to the container. * @param {Object} config - Configuration for the select element. * @param {string} config.name - Name attribute of the select. * @param {string} config.label - Label of the select. * @param {string} config.classes - CSS classes for the select. * @param {Object} config.attributes - Additional attributes for the select. * @param {Object} config.placeholder - Additional placeholder for the select. * @param {string} panel - Additional panel condition for the select. * @returns {HTMLElement} - The created `<select>` element. */ FilterGenerator.prototype.createSelect = function ({name, label, classes = '', attributes = {}, placeholder = ''}, panel = null) { if (this.elementExists(name)) { return null; } let panelId = null; if (panel) { panelId = attributes['data-type']; this.container = document.querySelector(`#wps-filter-${panelId} .wps-dropdown`); } const wrapper = this.createWrapper(name); const labelElement = this.createLabel(label, name); const select = document.createElement('select'); select.name = name; select.className = classes; select.setAttribute('aria-label', `wps-label-${name}`); for (const [key, value] of Object.entries(attributes)) { select.setAttribute(key, value); } wrapper.appendChild(labelElement); wrapper.appendChild(select); if (panel || attributes['data-searchable']) { const defaultValue = select.getAttribute('data-default'); this.createOptions(select, {}, placeholder, defaultValue); } this.container.appendChild(wrapper); this.enableSearchableSelect(select, name, attributes, panel, panelId, placeholder); if (typeof jQuery !== 'undefined' && jQuery.fn.select2 && !panel && !attributes['data-searchable']) { jQuery(select).select2({ width: '100%', }); } // Return the select element for further manipulation return select; }; /** * Initializes Select2 on a given select element if applicable. * Ensures that AJAX-based filtering is enabled and handles errors gracefully. * * @param {HTMLElement} select - The select element to enhance with Select2. * @param {string} name - The name attribute of the select element. * @param {Object} attributes - Additional attributes, including `data-searchable`. * @param {boolean} panel - (Optional) Flag indicating if the select element is part of a filter panel. * @param {string} panelId - (Optional) The ID of the filter panel container used to scope the dropdown UI. * @param {string} [placeholder=''] - (Optional) Placeholder text to display when no option is selected. */ FilterGenerator.prototype.enableSearchableSelect = function (select, name, attributes, panel, panelId, placeholder = '') { if (!attributes['data-searchable'] || typeof jQuery === 'undefined' || !jQuery.fn.select2) { return; } let source = name; source = attributes['data-type'] || ''; if (attributes['data-source']) { source = attributes['data-source']; } let panelParams = {}; if (panel) { panelParams = { dropdownParent: jQuery(`#wps-filter-${panelId}`), dropdownAutoWidth: true, dropdownCssClass: 'wps-select2-filter-dropdown' } } const queryString = window.location.search; const initialize = jQuery(select).select2({ ajax: { delay: 500, url: wps_js.global.ajax_url, dataType: 'json', data: function (params) { const query = { wps_nonce: wps_js.global.rest_api_nonce, search: params.term, source: source, action: 'wp_statistics_search_filter', paged: params.page || 1, queryString: queryString, }; if (wps_js.isset(wps_js.global, 'request_params')) { const requestParams = wps_js.global.request_params; if (requestParams.page) { query.page = requestParams.page; } if (requestParams.author_id) { query.author_id = requestParams.author_id; } if (requestParams.pt) { query.post_type = requestParams.pt; } if (requestParams.pt) { query.post_id = requestParams.pid; } } return query; }, processResults: function (data) { return { results: data.map(item => ({ id: item.id, text: item.text, })), }; }, error: function (xhr, status, error) { console.error('AJAX request error:', status, error); }, }, minimumInputLength: 1, allowClear: true, placeholder: placeholder || wps_js._('all'), ...panelParams }); if (panel) { // Event listeners initialize.on('select2:open', () => jQuery(`#wps-filter-${panelId} .wps-dropdown`).addClass('active')); initialize.on('select2:close', () => jQuery(`#wps-filter-${panelId} .wps-dropdown`).removeClass('active')); jQuery(`#wps-filter-${panelId}`).on('click', () => initialize.select2('open')); initialize.on('change', function () { const selectedOption = jQuery(`#wps-filter-${panelId}`).find('option:selected'); const url = selectedOption.val(); if (url) { window.location.href = url; } }); } }; /** * Appends `<option>` elements to a given `<select>` element. * @param {HTMLElement} select - The `<select>` element. * @param {Array} options - Array of option objects `{ value, label, selected }`. * @param {string|null} [placeholder=null] - Optional placeholder text to be used as the default option. * @param {string|null} [defaultValue=null] - A default value that, if provided, will be preselected. */ FilterGenerator.prototype.createOptions = function (select, options = [], placeholder = null, defatulValue = null) { if (!(select instanceof HTMLSelectElement)) { throw new Error('Invalid <select> element provided.'); } if (placeholder) { const defaultOption = document.createElement('option'); defaultOption.value = defatulValue || ''; defaultOption.textContent = placeholder; defaultOption.selected = true; select.appendChild(defaultOption); } if (options.length > 0) { options.forEach(option => { const opt = document.createElement('option'); opt.value = option.value || ''; opt.textContent = option.label || ''; if (placeholder !== null && option.value === placeholder) { opt.selected = true; } select.appendChild(opt); }); } // Initialize Select2 for the updated select (if applicable) if (typeof jQuery !== 'undefined' && jQuery.fn.select2 && options.length > 0) { jQuery(select).select2({ width: '100%', }); } }; /** * Generates an `<input>` element and appends it to the container. * @param {Object} config - Configuration for the input element. * @param {string} config.name - Name attribute of the input. * @param {string} config.label - Label of the input. * @param {string} config.type - Type of the input (e.g., text, number). * @param {string} config.classes - CSS classes for the input. * @param {string} config.placeholder - Placeholder text for the input. * @param {Object} config.attributes - Additional attributes for the input. */ FilterGenerator.prototype.createInput = function ({name, label, type = 'text', classes = '', placeholder = '', attributes = {}}) { if (this.elementExists(name)) { return null; } const wrapper = this.createWrapper(name); const labelElement = this.createLabel(label, name); const input = document.createElement('input'); input.name = name; input.type = type; input.className = classes; input.placeholder = placeholder; input.setAttribute('aria-label', `wps-label-${name}`); for (const [key, value] of Object.entries(attributes)) { input.setAttribute(key, value); } wrapper.appendChild(labelElement); wrapper.appendChild(input); this.container.appendChild(wrapper); }; /** * Generates a `<button>` element and appends it to the container. * @param {Object} config - Configuration for the button element. * @param {string} config.label - Button text. * @param {string} config.classes - CSS classes for the button. * @param {Function} config.onClick - Click event handler for the button. * @param {Object} config.attributes - Additional attributes for the button. */ FilterGenerator.prototype.createButton = function ({name, label, classes = '', onClick, attributes = {}}) { const buttonId = `button-${name.replace(/\s+/g, '-').toLowerCase()}`; if (this.container.querySelector(`#${buttonId}`)) { return; } const wrapper = this.createWrapper(name); const button = document.createElement('button'); button.textContent = label; button.className = classes; button.id = buttonId; button.setAttribute('aria-label', `wps-label-${label}`); for (const [key, value] of Object.entries(attributes)) { button.setAttribute(key, value); } if (typeof onClick === 'function') { button.addEventListener('click', onClick); } wrapper.appendChild(button); const footer = this.ensureFooterExists(); footer.appendChild(wrapper); }; /** * Generates a dropdown menu dynamically from the AJAX response. * @param {Object} filterConfig - The filter configuration object. * @param {Array} filterData - The structured response from PHP (list of post types). */ FilterGenerator.prototype.createDropdown = function (filterConfig, filterData) { if (!filterConfig || !filterConfig.containerSelector) { console.warn("Invalid filter configuration provided for dropdown."); return; } const container = document.querySelector(filterConfig.containerSelector); if (!container) { console.warn(`Container ${filterConfig.containerSelector} not found.`); return; } const {baseUrl, selectedOption, lockCustomPostTypes, args} = filterData; let defaultValue = wps_js._('all'); if (filterConfig.attributes.hasOwnProperty('data-default')) { defaultValue = filterConfig.attributes['data-default']; } let dropdownHTML = ` <div class="wps-dropdown" role="combobox" aria-haspopup="listbox" aria-label="${filterConfig.label || 'Post Type Filter'}"> <span class="selectedItemLabel" id="wps-dropdown-label-${filterConfig.containerSelector.replace(/[^a-zA-Z0-9]/g, '-')}" for="wps-dropdown-button-${filterConfig.containerSelector.replace(/[^a-zA-Z0-9]/g, '-')}">${filterConfig.label || "Post Type"}: </span> <button type="button" class="dropbtn" class="dropbtn" id="wps-dropdown-button-${filterConfig.containerSelector.replace(/[^a-zA-Z0-9]/g, '-')}" aria-expanded="false"> <span>${selectedOption ? this.getFilterName(filterData, defaultValue) : defaultValue}</span> </button> <div class="dropdown-content" role="listbox"><div class="dropdown-scroll"> `; if (filterConfig.searchable) { dropdownHTML += `<input type="text" class="wps-search-dropdown" aria-label="Search ${filterConfig.label || 'Post Type'}">`; } if (!filterConfig.selected && defaultValue) { dropdownHTML += `<a href="${baseUrl}" data-index="0" class="${!selectedOption ? 'selected' : ''}" role="option">${defaultValue}</a>`; } if (args) { args.forEach((item, key) => { let classList = []; if (selectedOption === item.slug) classList.push("selected"); if (lockCustomPostTypes && item.slug !== "post" && item.slug !== "page") classList.push("disabled"); if (item.premium) { dropdownHTML += ` <a data-target="wp-statistics-data-plus" title="${item.name}" class="js-wps-openPremiumModal ${classList.join(" ")}" role="option" aria-label="${item.name} (Premium)"> ${item.name} </a>`; } else { dropdownHTML += ` <a href="${item.url}" data-index="${key}" title="${item.name}" class="${classList.join(" ")}" role="option" aria-label="${item.name}"> ${item.name} </a>`; } }); } dropdownHTML += '</div></div></div>'; container.innerHTML = dropdownHTML; if (filterConfig.searchable) { this.enableSearchableDropdown(); } }; /** * Utility function to get the selected filter name from filterData. * @param {Array} filterData - The structured response from PHP. * @param {string} defaultValue - The fallback value if no selected option is found. * @returns {string} - The name of the selected post type. */ FilterGenerator.prototype.getFilterName = function (filterData, defaultValue) { const {selectedOption, args} = filterData; const selectedItem = args.find(item => item.slug === selectedOption); return selectedItem ? selectedItem.name : defaultValue; }; /** * Enables search functionality inside dropdowns when `searchable` is true. */ FilterGenerator.prototype.enableSearchableDropdown = function () { const searchInputs = document.querySelectorAll(".wps-search-dropdown"); searchInputs.forEach(input => { input.addEventListener("click", (event) => { event.stopPropagation(); }); input.addEventListener("input", function () { const filter = this.value.toLowerCase(); const items = this.parentElement.querySelectorAll("a[role='option']"); items.forEach(item => { const text = item.textContent.toLowerCase(); item.style.display = text.includes(filter) ? "block" : "none"; }); }); }); };
[-] panel.js
[edit]
[-] generator.js
[edit]
[-] modal.js
[edit]
[+]
..