/
var
/
www
/
barefootlaw.org
/
wp-content
/
plugins
/
autodescription
/
lib
/
js
/
Upload File
HOME
/** * This file holds The SEO Framework plugin's JS code for TSF description fields. * Serve JavaScript as an addition, not as an ends or means. * * @author Sybre Waaijer <https://cyberwire.nl/> * @link <https://wordpress.org/plugins/autodescription/> */ /** * The SEO Framework plugin * Copyright (C) 2019 - 2023 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 'use strict'; /** * Holds tsfDescription values in an object to avoid polluting global namespace. * * This is a self-constructed function assigned as an object. * * @since 4.0.0 * * @constructor */ window.tsfDescription = function() { /** * @since 4.1.0 * @type {Map<string,Element>} The input element instances. */ const descriptionInputInstances = new Map(); /** * @since 4.1.0 * @access private * @type {(Object<string,Object<string,*>)} the query state. */ const states = {}; /** * Sets input element for all listeners. Must be called prior interacting with this object. * Resets the state for the input ID. * * @since 4.0.0 * @since 4.1.0 Now creates an instance in a map this object, and returns it. * @access public * * @param {Element} element * @return {Element} */ const setInputElement = element => { descriptionInputInstances.set( element.id, element ); states[ element.id ] = { allowReferenceChange: true, defaultDescription: '', useDefaultDescription: true, }; _loadDescriptionActions( element ); return getInputElement( element.id ); } /** * Gets input element, if exists. * * @since 4.1.0 * @access public * * @param {string} id The element ID. * @return {Element} */ const getInputElement = id => descriptionInputInstances.get( id ); /** * Returns state of ID. * * @since 4.1.0 * @access public * * @param {string} id The input element ID. * @param {(string|undefined)} part The part to return. Leave empty to return the whole state. * @return {(Object<string, *>)|*|null} */ const getStateOf = ( id, part ) => part ? states[ id ]?.[ part ] : states[ id ]; /** * Updates state of ID. * * There's no need to escape the input, it may be double-escaped if you do so. * * @since 4.1.0 * @since 4.1.2 Added part `useDefaultDescription`. * @since 4.2.0 Now remains intert on a non-change. * @access public * * @param {string} id The input element ID. * @param {string} part The state index to change. * @param {*} value The value to set the state to. */ const updateStateOf = ( id, part, value ) => { if ( states[ id ][ part ] === value ) return; states[ id ][ part ] = value; switch ( part ) { case 'allowReferenceChange': case 'defaultDescription': case 'useDefaultDescription': default: enqueueTriggerInput( id ); break; } } /** * Updates state of all elements. * * There's no need to escape the input, it may be double-escaped if you do so. * * @since 4.1.0 * @since 4.2.0 Added a 3rd parameter, allowing you to exclude updates for certain elements. * @access public * * @param {string} part The state index to change. * @param {*} value The value to set the state to. * @param {string|string[]} except The input element IDs to exclude from updates. */ const updateStateAll = ( part, value, except ) => { except = Array.isArray( except ) ? except : [ except ]; descriptionInputInstances.forEach( element => { if ( except.includes( element.id ) ) return; updateStateOf( element.id, part, value ); } ); } /** * Returns description references of ID. * * @since 4.1.0 * @access public * * @param {string} id The input element ID. * @return {HTMLElement[]} */ const _getDescriptionReferences = id => { let references = [ document.getElementById( `tsf-description-reference_${id}` ) ]; if ( getStateOf( id, 'hasLegacy' ) ) { let legacy = document.getElementById( 'tsf-description-reference' ); legacy && references.unshift( legacy ); } return references; } /** * Updates the description reference. * * Used by the character counters, pixel counters, and social meta inputs. * * @since 4.0.0 * @since 4.1.0 Now supports multiple instances. * @since 4.1.2 Now listens to `useDefaultDescription` when reference isn't locked. * @access private * * @function * @param {Event} event */ const _setReferenceDescription = event => { const references = _getDescriptionReferences( event.target.id ); if ( ! references[0] ) return; const allowReferenceChange = getStateOf( event.target.id, 'allowReferenceChange' ), useDefaultDescription = allowReferenceChange ? getStateOf( event.target.id, 'useDefaultDescription' ) : true; let text = ( allowReferenceChange && event.target.value.trim() ) || ( useDefaultDescription && getStateOf( event.target.id, 'defaultDescription' ) ) || ''; const referenceValue = tsf.escapeString( tsf.decodeEntities( tsf.sDoubleSpace( tsf.sTabs( tsf.sSingleLine( text ).trim() ) ) ) ); const changeEvent = new Event( 'change' ); references.forEach( reference => { // We require the event below when adjusting some states... Don't uncomment this. // if ( reference.innerHTML = referenceValue ) return; reference.innerHTML = referenceValue; // Fires change event. Defered to another thread. setTimeout( () => { reference.dispatchEvent( changeEvent ) }, 0 ); } ); } /** * Updates the title placeholder. * * @since 4.0.0 * @since 4.1.0 Now consistently sets a reliable placeholder. * @access private * * @function * @param {Event} event */ const _updatePlaceholder = event => { event.target.placeholder = _getDescriptionReferences( event.target.id )[0].textContent; } /** * Updates used separator and all examples thereof. * * @since 3.0.4 1. Threshold "too long" has been increased from 155 to 300. * 2. Threshold "far too long" has been increased to 330 from 175. * @since 3.1.0 Now uses the new guidelines via a filterable function in PHP. * * @function * @param {Event} event */ const _updateCounter = event => { const counter = document.getElementById( `${event.target.id}_chars` ), reference = _getDescriptionReferences( event.target.id )[0]; if ( ! counter ) return; tsfC?.updateCharacterCounter( { e: counter, text: reference.innerHTML, field: 'description', type: 'search', } ); } /** * Updates the pixel counter bound to the input. * * @since 4.0.0 * @access private * * @function * @param {Event} event */ const _updatePixels = event => { const pixels = document.getElementById( `${event.target.id}_pixels` ), reference = _getDescriptionReferences( event.target.id )[0]; if ( ! pixels ) return; tsfC?.updatePixelCounter( { e: pixels, text: reference.innerHTML, field: 'description', type: 'search', } ); } /** * Triggers meta description input. * * @since 4.0.0 * @since 4.1.0 Now allows for a first parameter to be set. * @access public * * @function * @param {string} id The input id. When not set, all inputs will be triggered. */ const triggerInput = id => { if ( id ) { getInputElement( id )?.dispatchEvent( new Event( 'input' ) ); } else { // We don't want it to loop infinitely. Check element.id value first. descriptionInputInstances.forEach( element => element.id && triggerInput( element.id ) ); } } /** * Triggers counter updates. * * @since 4.0.0 * @since 4.1.0 Now allows for a first parameter to be set. * @access public * * @function * @param {string} id The input id. When not set, all inputs will be triggered. */ const triggerCounter = id => { if ( id ) { getInputElement( id )?.dispatchEvent( new CustomEvent( 'tsf-update-description-counter' ) ); } else { // We don't want it to loop infinitely. Check element.id value first. descriptionInputInstances.forEach( element => element.id && triggerCounter( element.id ) ); } } /** * Updates placements, placeholders and counters. * * @since 4.0.0 * @access private * @see triggerInput * @uses _onUpdateCounterTrigger * * @function * @param {Event} event */ const _onUpdateDescriptionsTrigger = event => { _setReferenceDescription( event ); _updatePlaceholder( event ); _onUpdateCounterTrigger( event ); } /** * Updates character counters. * * @since 4.0.0 * @access private * @see triggerCounter * * @function * @param {Event} event */ const _onUpdateCounterTrigger = event => { _updateCounter( event ); _updatePixels( event ); } let _enqueueTriggerInputBuffer = {}; /** * Triggers meta description input. * * @since 4.0.0 * @since 4.1.0 Added first parameter, id. * @since 4.1.1 Now passes the right parameter to the input event. * @access public * * @function * @param {string} id The input ID. */ const enqueueTriggerInput = id => { ( id in _enqueueTriggerInputBuffer ) && clearTimeout( _enqueueTriggerInputBuffer[ id ] ); _enqueueTriggerInputBuffer[ id ] = setTimeout( () => triggerInput( id ), 1000/60 ); // 60fps } /** * Triggers description update, without affecting tsfAys change listeners. * * @since 4.0.0 * @since 4.1.0 Now allows for a first parameter to be set. * @access public * * @function * @param {Event} * @param {string} id The input id. When not set, all inputs will be triggered. */ const triggerUnregisteredInput = id => { if ( 'tsfAys' in window ) { let wereSettingsChanged = tsfAys.areSettingsChanged(); triggerInput( id ); // Only reset if we polluted the change listener, and only if a change wasn't already registered. if ( ! wereSettingsChanged && tsfAys.areSettingsChanged() ) tsfAys.reset(); } else { triggerInput( id ); } } let _unregisteredTriggerBuffer = {}; /** * Enqueues unregistered description input triggers. * * @since 4.0.0 * @since 4.1.0 Now allows for a first parameter to be set. * @access public * * @function * @param {string} id The input id. When not set, all inputs will be triggered. */ const enqueueUnregisteredInputTrigger = id => { ( id in _unregisteredTriggerBuffer ) && clearTimeout( _unregisteredTriggerBuffer[ id ] ); _unregisteredTriggerBuffer[ id ] = setTimeout( () => triggerUnregisteredInput( id ), 1000/60 ); // 60 fps } let prevWidth = window.innerWidth; /** * Triggers input event for descriptions in set intervals on window resize. * * This only happens if boundaries are surpassed to reduce CPU usage. * This boundary is 782 pixels, because that forces input fields to change. * in WordPress. * * This happens to all description inputs; as WordPress switches * from Desktop to Mobile view at 782 pixels. * * @since 4.0.0 * @access private * @see ...\wp-admin\js\common.js * * @function */ const _doResize = () => { const width = window.innerWidth; if ( prevWidth < width ) { if ( prevWidth <= 782 && width >= 782 ) { triggerUnregisteredInput(); } } else { if ( prevWidth >= 782 && width <= 782 ) { triggerUnregisteredInput(); } } prevWidth = width; } /** * Reinitializes the description input action callbacks. * * @since 4.0.0 * @access private * * @function * @param {Element} descriptionInput */ const _loadDescriptionActions = descriptionInput => { if ( ! descriptionInput instanceof Element ) return; descriptionInput.addEventListener( 'input', _onUpdateDescriptionsTrigger ); descriptionInput.addEventListener( 'tsf-update-description-counter', _onUpdateCounterTrigger ); enqueueUnregisteredInputTrigger( descriptionInput.id ); } /** * Initializes the description environment. * * @since 4.1.0 * @since 4.1.1 No longer passes the event to the enqueueUnregisteredInputTrigger() callback. * @access private * * @function */ const _initAllDescriptionActions = () => { // Triggers input changes on resize after hitting thresholds. window.addEventListener( 'tsf-resize', _doResize ); // When counters are updated, trigger an input; which will reassess them. window.addEventListener( 'tsf-counter-updated', () => enqueueUnregisteredInputTrigger() ); } return Object.assign( { /** * Initialises all aspects of the scripts. * You shouldn't call this. * * @since 4.0.0 * @access protected * * @function */ load: () => { document.body.addEventListener( 'tsf-onload', _initAllDescriptionActions ); } }, { setInputElement, getInputElement, getStateOf, updateStateOf, updateStateAll, triggerCounter, triggerInput, enqueueTriggerInput, triggerUnregisteredInput, enqueueUnregisteredInputTrigger, // this should've been enqueueTriggerUnregisteredInput... } ); }(); window.tsfDescription.load();