Current File : /home/honehdyv/readbtooom.com/wp-content/plugins/autodescription/lib/js/ays.js |
/**
* This file holds The SEO Framework plugin's JS code for Are You Sure notifications.
* 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 tsfAys values in an object to avoid polluting global namespace.
*
* @since 4.0.0
*
* @constructor
*/
window.tsfAys = function() {
/**
* Data property injected by WordPress l10n handler.
*
* @since 4.0.0
* @access public
* @type {(Object<string, *>)|boolean|null} l10n Localized strings
*/
const l10n = 'undefined' !== typeof tsfAysL10n && tsfAysL10n;
/**
* Handles the settings state.
*
* @since 4.0.0
* @access private
* @type {boolean}
*/
let _settingsChanged = false;
/**
* Determines the initialization state.
*
* @since 4.0.0
* @access private
* @type {boolean}
*/
let _loadedListeners = false;
/**
* Holds the registered change listeners map.
*
* @since 4.1.1
* @access private
* @type {Map<Element,string>} string: event Type attached.
*/
let _registeredChangeListeners = new Map();
/**
* Returns changed state.
*
* @since 4.0.0
* @todo deprecate
* @access public
*
* @function
* @return {boolean}
*/
const getChangedState = () => areSettingsChanged();
/**
* Returns changed state.
*
* @since 4.1.0
* @access public
*
* @function
* @return {boolean}
*/
const areSettingsChanged = () => _settingsChanged;
/**
* Transforms elements and queries to an array from nodelists.
*
* @since 4.1.1
* @access private
*
* @function
* @param {Element|Document|string|string[]} elements
* @return {(Element|Document)[]}
*/
const _getNodeArray = elements => ( elements instanceof Element || elements instanceof Document )
? [ elements ]
: [ ...document.querySelectorAll( Array.isArray( elements ) ? elements.join( ', ' ) : elements ) ];
/**
* Registers changed state.
*
* @since 4.0.0
* @access public
*
* @function
*/
const registerChange = () => {
_settingsChanged = true;
}
/**
* Deregisters changed state.
*
* @since 4.0.0
* @access public
*
* @function
*/
const deregisterChange = () => {
_settingsChanged = false;
}
let _debounceReset = void 0;
/**
* Resets all listeners and deregisters changes.
*
* Deregistration only happens after first initialization.
*
* @since 4.0.0
* @since 4.1.1 debounced reloading of the listeners.
* @access public
*
* @function
*/
const reset = () => {
deregisterChange();
if ( _loadedListeners ) {
clearTimeout( _debounceReset );
// High timeout. Resets should only happen during failures or changing document states; the latter of which is slow.
_debounceReset = setTimeout( reloadDefaultListeners, 1e3 );
}
}
/**
* Unloads the change listener, a saving action is expected that causes the user to unload the page.
* Also tries to reregister it after 1 second, may other scripts interrupt the unloading.
*
* @since 4.0.0
* @access private
*
* @function
*/
const _triggerUnload = () => {
let wereSettingsChanged = areSettingsChanged();
deregisterChange();
setTimeout( () => {
reset();
wereSettingsChanged && registerChange();
}, 1000 );
}
/**
* Triggers registered change listener. Also exempt future change listeners,
* for once we registered a change, none is required thereafter.
*
* @since 4.0.0
* @since 4.1.1 Now only passes trusted events.
* @access private
*
* @function
* @param {event} event
*/
const _triggerChange = event => {
if ( ! event.isTrusted ) return;
registerChange();
_exemptFutureChanges();
}
/**
* Exempts listeners to reduce CPU usage.
*
* @since 4.0.0
* @since 4.1.1 Now defuncts all registered change listers without relying on event details.
* @access private
*
* @function
*/
const _exemptFutureChanges = () => {
_registeredChangeListeners.forEach( ( eventType, element ) => {
element.removeEventListener( eventType, _triggerChange );
} );
_registeredChangeListeners.clear();
}
/**
* Registers change listener.
* Should be used on input elements that may be saved.
*
* @since 4.0.0
* @since 4.1.1 1. No longer uses jQuery for the events.
* 2. Now filters the exceptions in place.
* @access public
*
* @function
* @param {(Element|string|string[])} elements The elements to register.
* @param {string} eventType The event type to listen to.
*/
const registerChangeListener = ( elements, eventType ) => {
_getNodeArray( elements )
.filter( el => ! el.classList.contains( 'tsf-input-not-saved' ) && 'hidden' !== el.type )
.forEach( el => {
_registeredChangeListeners.set( el, eventType );
el.addEventListener( eventType, _triggerChange )
} );
}
/**
* Registers force reset listener.
* Should be used on input elements that save the document, but doesn't reload the document.
*
* @since 4.0.0
* @access public
*
* @function
* @param {(Element|string|string[])} elements The elements to register.
* @param {string} eventType The event type to listen to.
*/
const registerResetListener = ( elements, eventType ) => {
_getNodeArray( elements ).forEach( el => {
el.addEventListener( eventType, reset );
} );
}
/**
* Registers unload listener.
* Should be used on input elements that save the document, and also reloads the document.
*
* @since 4.0.0
* @access public
*
* @function
* @param {(Element|string|string[])} elements The elements to register.
* @param {string} eventType The event type to listen to.
*/
const registerUnloadListener = ( elements, eventType ) => {
_getNodeArray( elements ).forEach( el => {
el.addEventListener( eventType, _triggerUnload )
} );
}
/**
* Registers default listeners.
* Also triggers an event, so other developers may consistently add their listeners.
*
* @since 4.0.0
* @access public
*
* @function
*/
const reloadDefaultListeners = () => {
_loadedListeners = false;
//= Mouse input
registerChangeListener(
[
'.tsf-metaboxes input[type=radio][name]',
'.tsf-metaboxes input[type=checkbox][name]',
'.tsf-metaboxes select[name]',
'#tsf-inpost-box .inside input[type=radio][name]',
'#tsf-inpost-box .inside input[type=checkbox][name]',
'#tsf-inpost-box .inside select[name]',
'.tsf-term-meta input[type=radio][name]',
'.tsf-term-meta input[type=checkbox][name]',
'.tsf-term-meta select[name]',
],
'change'
);
//= Text input
registerChangeListener(
[
'.tsf-metaboxes input:not([type=radio]):not([type=checkbox])[name]',
'.tsf-metaboxes textarea[name]',
'#tsf-inpost-box .inside input:not([type=radio]):not([type=checkbox])[name]',
'#tsf-inpost-box .inside textarea[name]',
'.tsf-term-meta input:not([type=radio]):not([type=checkbox])[name]',
'.tsf-term-meta textarea[name]',
],
'input'
);
//= Non-redirect, Gutenberg save action.
registerResetListener(
document,
'tsf-gutenberg-onsave-completed'
);
//= Redirect-save actions.
registerUnloadListener(
[
'.tsf-metaboxes input[type=submit]',
'#publishing-action input[type=submit]',
'#save-action input[type=submit]',
'a.submitdelete',
'.edit-tag-actions input[type=submit]',
'.edit-tag-actions .delete',
],
'click'
);
document.dispatchEvent( new CustomEvent( 'tsf-registered-ays-listeners' ) );
_loadedListeners = true;
}
/**
* Alerts the user that there are unsaved changes before unloading the tab/window.
*
* @since 4.1.0
* @access private
* @link https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload
*
* @function
* @param {event} event
*/
const _alertUserBeforeunload = event => {
if ( areSettingsChanged() ) {
// Every other browser:
event.preventDefault();
// Chromium requirement:
event.returnValue = l10n.i18n['saveAlert'];
}
}
/**
* Initializes AYS scripts on tsf-interactive.
*
* @since 4.0.0
* @since 4.1.1 Now binds to the tsf-interactive event, instead of tsf-ready.
* @access private
*
* @function
*/
const _readyAys = () => {
// Initialise form field changing flag.
reloadDefaultListeners();
areSettingsChanged() && console.warn( 'tsfAys: Doing it wrong! Settings were changed prior ready-state. Debug me.' );
// Undo accidental load-sequence state changes.
deregisterChange();
//= Alert onbeforeunload
window.addEventListener( 'beforeunload', _alertUserBeforeunload );
}
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-interactive', _readyAys );
}
}, {
reset,
getChangedState,
areSettingsChanged,
registerChange,
deregisterChange,
registerChangeListener,
registerResetListener,
registerUnloadListener,
reloadDefaultListeners
}, {
l10n
} );
}();
window.tsfAys.load();