Willkommen bei WordPress. Dies ist dein erster Beitrag. Bearbeite oder lösche ihn und beginne mit dem Schreiben!
Hallo Welt!
von raredesign | Dez 3, 2019 | Allgemein | 0 Kommentare
Cokiee Shell
Current Path : /var/www/web28/html/wp-content/plugins/autodescription/lib/js/ |
Current File : //var/www/web28/html/wp-content/plugins/autodescription/lib/js/post.js |
/** * This file holds The SEO Framework plugin's JS code for the Post SEO Settings. * 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 - 2024 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 tsfPost values in an object to avoid polluting global namespace. * * @since 4.0.0 * * @constructor */ window.tsfPost = function () { /** * Data property injected by WordPress l10n handler. * * @since 4.0.0 * @access public * @type {(Object<string,*>)|boolean|null} l10n Localized strings */ const l10n = tsfPostL10n; /** * @since 4.1.0 * @access private * @type {string} */ const _titleId = 'autodescription_title'; /** * @since 4.1.0 * @access private * @type {string} */ const _descId = 'autodescription_description'; /** * @since 5.1.0 * @access private * @type {string} */ const _canonicalId = 'autodescription_canonical'; /** * @since 4.2.0 * @access private * @type {string} */ const _socialGroup = 'autodescription_social_singular'; /** * Registers on resize/orientationchange listeners and debounces to only run * at set intervals. * * For Flexbox implementation. * * @since 4.0.0 * @access private */ function _doFlexResizeListener() { if ( ! document.querySelector( '.tsf-flex' ) ) return; const wrapper = document.getElementById( 'tsf-flex-inpost-tabs-wrapper' ); const overflowAnimationFrame = new Map(); const calculateTextOverflow = target => { const innerWrap = target.querySelector( '.tsf-flex-nav-tab-inner' ), navNames = target.querySelectorAll( '.tsf-flex-nav-name' ); if ( innerWrap.clientWidth <= target.clientWidth ) { if ( +( target.dataset.displayedNames || 1 ) ) return; // Names are displayed by default on-load. Ergo, 1 by default. target.dataset.displayedNames = 1; navNames.forEach( element => { element.style.display = null; tsfUI.fadeIn( element ); } ); } else { if ( ! +( target.dataset.displayedNames || 1 ) ) return; target.dataset.displayedNames = 0; // Don't animate, we're overflowing--rectify that ASAP. navNames.forEach( element => { element.style.display = 'none' } ); } if ( +target.dataset.displayedNames ) { if ( innerWrap.clientWidth > target.clientWidth ) { // Don't animate, we're overflowing--rectify that ASAP. navNames.forEach( element => { element.style.display = 'none' } ); target.dataset.displayedNames = 0; } else { // Loop once just to be certain, for the browser may be too slow to notice the offset change. // Usually, this only happens once when the navNames are meant to be displayed (target width growing). setTimeout( () => { cancelAnimationFrame( overflowAnimationFrame.get( target.id ) ); overflowAnimationFrame.set( target.id, requestAnimationFrame( () => calculateTextOverflow( target ) ) ); }, 1000/144, // 144hz. ); } } } const prepareCalculateTextOverflow = event => { const target = event.detail.target || wrapper; if ( target ) overflowAnimationFrame.set( target.id, requestAnimationFrame( () => calculateTextOverflow( target ) ) ); } window.addEventListener( 'tsf-flex-resize', prepareCalculateTextOverflow ); /** * Triggers resize on event. * * @function * @param {HTMLElement|undefined} target The target that's being resized. Optional. */ const triggerResize = target => { window.dispatchEvent( new CustomEvent( 'tsf-flex-resize', { bubbles: false, cancelable: false, detail: { target, }, }, ) ); } let resizeAnimationFrame = {}; const resizeObserver = new ResizeObserver( entries => { // There should be only one entry... Nevertheless, let's loop this for we might add more metaboxes. for ( const entry of entries ) { let target = entry.target; cancelAnimationFrame( resizeAnimationFrame[ target.id ] ); resizeAnimationFrame[ target.id ] = requestAnimationFrame( () => { // No support for all major browsers yet. Neither for entry.contentRect. // entry.dataset.boxSizeWidth ||= contentBoxSize.inlineSize; target.dataset.lastWidth ||= 0; if ( +target.clientWidth !== +target.dataset.lastWidth ) { target.dataset.lastWidth = target.clientWidth; triggerResize( target ); } } ); } } ); wrapper && resizeObserver.observe( wrapper ); // Trigger after setup triggerResize(); } /** * Sets the navigation tabs content equal to the buttons. * * @since 4.0.0 * @since 4.1.3 Now offloaded to tsfTabs. * @access private */ function _initTabs() { tsfTabs.initStack( 'tsfSettings', { tabToggledEvent: new CustomEvent( 'tsf-flex-tab-toggled' ), HTMLClasses: { wrapper: 'tsf-flex-nav-tab-wrapper', tabRadio: 'tsf-flex-nav-tab-radio', tabLabel: 'tsf-flex-nav-tab-label', activeTab: 'tsf-flex-tab-active', // change to tsf-flex-nav-tab-active? // TODO make this tsf-flex-tab-active-content (force -content affix?) activeTabContent: 'tsf-flex-tab-content-active', }, fixHistory: true, // doesn't work since the inputs reset on navigation; enabled for future-proofing. } ); } /** * Returns the visibility setting from the Classic editor, as WordPress's PHP would interpret it. * We could optimize this (it runs thrice in a row without a debouncer), but that's not for this function to fix. * If WordPress didn't have this bug, we needn't have done these lookups at all. * * @since 5.0.5 * @access private * * @return {string} 'public', 'password', or 'private'. */ function _getClassicVisibility() { let visibility = [ ...document.getElementsByName( 'visibility' ) ].filter( e => e.checked )?.[0]?.value; // If password type is selected, but no password is set (or a falsy one), then assume public. This is a bug in WP. if ( 'password' === visibility ) { const val = document.getElementById( 'post_password' )?.value; if ( val?.length && '0' === val ) visibility = 'public'; } return visibility; } /** * Registers the post privacy listener. * * @since 5.1.0 * @access private * * @param {callable} callback */ function _registerPostPrivacyListener( callback ) { // Block Editor. document.addEventListener( 'tsf-updated-block-editor-visibility', event => callback( event.detail.value ) ); // Debounce the callback for Classic Editor, because toggling visibility will also trigger a password input event. callback = tsfUtils.debounce( callback, 20 ); // Magic number. The duplicate event happens in under a few ms; this is also imperceptible. // Classic Editor. document.querySelector( '#visibility .save-post-visibility' ) ?.addEventListener( 'click', () => callback( _getClassicVisibility() ) ); } /** * Initializes canonical URL meta input listeners. * * @since 4.0.0 * @since 4.1.2 Changed name from _initCanonicalInput * @since 4.1.4 Now no longer proceeds on absence of element ID 'autodescription_noindex'. * @since 5.1.0 Refactored to support dynamic URL structures. * @access private */ function _initVisibilityListeners() { const noindexSelect = document.getElementById( 'autodescription_noindex' ), canonicalInput = document.getElementById( 'autodescription_canonical' ); const urlDataParts = new Map(); // Prefixed with B because I don't trust using 'protected' (might become reserved). const BPROTECTED = 0b01, BNOINDEX = 0b10; let canonicalPhState = 0b00; tsfCanonical.setInputElement( canonicalInput ); const state = JSON.parse( document.getElementById( `tsf-canonical-data_${_canonicalId}` )?.dataset.state || 0 ); if ( state ) { tsfCanonical.updateStateOf( _canonicalId, 'allowReferenceChange', ! state.refCanonicalLocked ); tsfCanonical.updateStateOf( _canonicalId, 'defaultCanonical', state.defaultCanonical.trim() ); tsfCanonical.updateStateOf( _canonicalId, 'preferredScheme', state.preferredScheme.trim() ); tsfCanonical.updateStateOf( _canonicalId, 'urlStructure', state.urlStructure ); } tsfCanonical.enqueueTriggerUnregisteredInput( _canonicalId ); /** * @since 4.1.2 * * @function */ const updateCanonicalPlaceholder = () => { tsfCanonical.updateStateOf( _canonicalId, 'showUrlPlaceholder', ( canonicalPhState & BPROTECTED ) || ( canonicalPhState & BNOINDEX ) ? false : true, ); tsfCanonical.updateStateOf( _canonicalId, 'urlDataParts', Object.fromEntries( urlDataParts.entries() ), ); } if ( tsfCanonical.usingPermalinks && canonicalInput ) { // We rewrote %pagename% to %postname% at `Meta\URI\Utils::get_url_permastruct()`. const writePostname = tsfCanonical.structIncludes( _canonicalId, '%postname%' ); const writeDate = tsfCanonical.structIncludes( _canonicalId, [ '%year%', '%monthnum%', '%day%', '%hour%', '%minute%', '%second%' ] ); const writeTerm = {}; const writeAuthor = tsfCanonical.structIncludes( _canonicalId, '%author%' ); let postSlug = '', postTitle = '', authorSlug = '', dateString = '', parentSlugs = [], termSlugs = []; // Unpack post slugs. if ( writePostname ) { tsfPostSlugs.store( state.parentPostSlugs ); // We preemptively write here because the selection might be unavailable. parentSlugs = state.parentPostSlugs.map( post => post.slug ); // isHierarchical is checked in PHP for this. } // Add support for every registered taxonomy. We do this because the terms are not always available. state.supportedTaxonomies.forEach( taxonomy => { writeTerm[ taxonomy ] = tsfCanonical.structIncludes( _canonicalId, `%${taxonomy}%` ); // Should always be true... } ); // Unpack term slugs per taxonomy. for ( const [ taxonomy, terms ] of Object.entries( state.parentTermSlugs ) ) { tsfTermSlugs.store( terms, taxonomy ); termSlugs[ taxonomy ] = terms.map( term => term.slug ); } // Unpack author slugs. if ( writeAuthor ) { tsfAuthorSlugs.store( state.authorSlugs ); authorSlug = state.authorSlugs?.[0]?.slug; // There should only be one. } /** * @since 4.0.0 * @since 5.1.0 Now obtains an accurate canonical URL via AJAX. * * @function */ const updateCanonical = () => { if ( writePostname ) { let activeSlug = ''; if ( postSlug.length ) { // postName always gets trimmed to the first 200 characters. // Parent slugs have already had the same treatment by WP Core, so we ignore those. activeSlug = tsfCanonical.sanitizeSlug( postSlug.substring( 0, 200 ) ); if ( '0' === activeSlug ) // '0' will be ignored by WP. activeSlug = ''; } // Slug falls back to the title. if ( ! activeSlug.length && postTitle.length ) activeSlug = tsfCanonical.sanitizeSlug( postTitle.substring( 0, 200 ) ); // However, if the title is '0', it'll be used (and the page becomes unreachable). if ( ! activeSlug.length ) activeSlug = l10n.params.id; // We rewrote %pagename% to %postname% at `Meta\URI\Utils::get_url_permastruct()` urlDataParts.set( `%postname%`, [ ...parentSlugs, activeSlug ].join( '/' ) ); } // Just write these without checks; there's no meaningful performance hit. urlDataParts .set( `%post_id%`, l10n.params.id ) .set( `%author%`, authorSlug ); if ( writeDate ) { const date = new Date( dateString ); const padDate = v => String( v ).padStart( 2, '0' ); urlDataParts .set( `%year%`, date.getFullYear() ) .set( `%monthnum%`, padDate( date.getMonth() + 1 ) ) .set( `%day%`, padDate( date.getDate() ) ) .set( `%hour%`, padDate( date.getHours() ) ) .set( `%minute%`, padDate( date.getMinutes() ) ) .set( `%second%`, padDate( date.getSeconds() ) ); // This doesn't even work on the front-end. } for ( const taxonomy in writeTerm ) { // If is writeable, then set the URL data part. writeTerm[ taxonomy ] && urlDataParts .set( `%${taxonomy}%`, Object.values( termSlugs[ taxonomy ] ?? {} ).join( '/' ), ); } updateCanonicalPlaceholder(); } const queueUpdateCanonical = tsfUtils.debounce( updateCanonical, 1000/60 ); // 60 fps. document.addEventListener( 'tsf-updated-block-editor', async event => { // This event is already debounced. No need to debounce it again. switch ( event.detail.type ) { case 'title': if ( writePostname ) { // The canonical URL falls back to the title. postTitle = event.detail.postData.get( 'title' ); queueUpdateCanonical(); } break; case 'slug': if ( writePostname ) { postSlug = event.detail.postData.get( 'slug' ); queueUpdateCanonical(); } break; case 'parent': if ( writePostname ) { parentSlugs = await tsfPostSlugs.get( event.detail.postData.get( 'parent' ) ); queueUpdateCanonical(); } break; case 'author': if ( writeAuthor ) { authorSlug = await tsfAuthorSlugs.get( event.detail.postData.get( 'author' ) ); queueUpdateCanonical(); } break; case 'date': if ( writeDate ) { dateString = new Date( event.detail.postData.get( 'date' ) ).toISOString(); queueUpdateCanonical(); } } } ); if ( Object.values( writeTerm ).includes( true ) ) { // We're not debouncing this because the event is difficult to trigger in quick succession. const updateParentTermSlugsViaPrimary = tsfUtils.debounce( async event => { const taxonomy = event.detail.taxonomy; if ( writeTerm[ taxonomy ] ) { termSlugs[ taxonomy ] = await tsfTermSlugs.get( event.detail.id, taxonomy ); queueUpdateCanonical(); } }, 100, // Magic number. High enough to prevent self-DoS, low enough to be responsive. ); document.addEventListener( `tsf-updated-primary-term`, updateParentTermSlugsViaPrimary ); } // Classic Editor. if ( ! l10n.params.isBlockEditor ) { if ( writePostname ) { // 'editable-post-name' and 'sample-permalink' get destroyed on slug change, so we can't rely on them. // editSlugBox is only available when the post is not a draft for "publish posts" capability. const editSlugBox = document.getElementById( 'edit-slug-box' ); const postNameInput = document.getElementById( 'post_name' ); const titleInput = document.getElementById( 'title' ); const parentIdInput = document.getElementById( 'parent_id' ); const updatePostName = () => { // Title isn't used directly, but may be used if the slug isn't set. postTitle = titleInput?.value ?? '' postSlug = postNameInput?.value ?? document.getElementById( 'editable-post-name-full' )?.innerText ?? ''; queueUpdateCanonical(); } titleInput?.addEventListener( 'input', updatePostName ); postNameInput?.addEventListener( 'input', updatePostName ); updatePostName(); if ( editSlugBox ) { // Observe these for changes. Otherwise, we'd have to rely on a multitude of jQuery callbacks affecting it. new MutationObserver( mutationList => { for ( const mutation of mutationList ) { // Look for the reintroduction of the "slug is edited" box. if ( mutation.addedNodes.entries().some( ( [ , node ] ) => 'editable-post-name-full' === node.id ) ) { updatePostName(); break; } } } ).observe( editSlugBox, { childList: true, subtree: true }, ); } if ( parentIdInput ) { const updateParentSlug = tsfUtils.debounce( async () => { parentSlugs = await tsfPostSlugs.get( parentIdInput.value ); queueUpdateCanonical(); }, 100, // Magic number. High enough to prevent self-DoS, low enough to be responsive. ); parentIdInput.addEventListener( 'input', updateParentSlug ); updateParentSlug(); } } if ( writeAuthor ) { const authorIdInput = document.getElementById( 'post_author_override' ) ?? document.getElementById( 'post_author' ); if ( authorIdInput ) { // We debounce the event listener since we're making AJAX requests. const updateAuthor = tsfUtils.debounce( async () => { authorSlug = await tsfAuthorSlugs.get( authorIdInput.value ); queueUpdateCanonical(); }, 100, // Magic number. High enough to prevent self-DoS, low enough to be responsive. ); authorIdInput.addEventListener( 'input', updateAuthor ); updateAuthor(); } } if ( writeDate ) { const dateFields = [ // These fields don't show the date very accurately when posted "immediately" (new post). // TODO fixme? We'd need to run a clock. It would be a cool gimmick, though. document.getElementById( 'aa' ), document.getElementById( 'mm' ), document.getElementById( 'jj' ), document.getElementById( 'hh' ), document.getElementById( 'mn' ), document.getElementById( 'ss' ), ]; const useDateFields = ! dateFields.some( v => v === null ); const getActiveDateValues = () => { const values = dateFields.map( field => field.value ); // WordPress compensated for the 0-index month, we need to revert that. if ( values[1] ) --values[1]; return values.map( v => v ?? '00' ); } const updateDateString = () => { dateString = useDateFields ? new Date( ...getActiveDateValues() ).toISOString() : state.publishDate; queueUpdateCanonical(); } dateFields.forEach( field => { field?.addEventListener( 'change', updateDateString ); } ); updateDateString(); } queueUpdateCanonical(); } } if ( noindexSelect ) { /** * @since 4.1.2 * * @function * @param {string} visibility */ const setRobotsDefaultIndexingState = visibility => { let _defaultIndexOption = noindexSelect.querySelector( '[value="0"]' ), indexDefaultValue = ''; switch ( visibility ) { case 'password': case 'private': indexDefaultValue = 'noindex'; canonicalPhState |= BPROTECTED; break; default: case 'public': indexDefaultValue = noindexSelect.dataset.defaultUnprotected; canonicalPhState &= ~BPROTECTED; break; } if ( _defaultIndexOption ) _defaultIndexOption.innerHTML = noindexSelect.dataset.defaultI18n.replace( '%s', tsf.escapeString( tsf.decodeEntities( indexDefaultValue ) ) ); updateCanonicalPlaceholder(); } _registerPostPrivacyListener( setRobotsDefaultIndexingState ); if ( l10n.states.isPrivate ) { setRobotsDefaultIndexingState( 'private' ); } else if ( l10n.states.isProtected ) { setRobotsDefaultIndexingState( 'password' ); } else { setRobotsDefaultIndexingState( 'public' ); } /** * @since 4.1.2 * * @function * @param {Number} value */ const setRobotsIndexingState = value => { let type = ''; switch ( +value ) { case 0: // default, unset since unknown. type = noindexSelect.dataset.defaultUnprotected; break; case -1: // index type = 'index'; break; case 1: // noindex type = 'noindex'; break; } if ( 'noindex' === type ) { canonicalPhState |= BNOINDEX; } else { canonicalPhState &= ~BNOINDEX; } updateCanonicalPlaceholder(); } noindexSelect.addEventListener( 'change', event => setRobotsIndexingState( event.target.value ) ); setRobotsIndexingState( noindexSelect.value ); } } /** * Initializes title meta input listeners. * * @since 4.0.0 * @access private */ function _initTitleListeners() { const titleInput = document.getElementById( _titleId ); if ( ! titleInput ) return; tsfTitle.setInputElement( titleInput ); const state = JSON.parse( document.getElementById( `tsf-title-data_${_titleId}` )?.dataset.state || 0 ); if ( state ) { tsfTitle.updateStateOf( _titleId, 'allowReferenceChange', ! state.refTitleLocked ); tsfTitle.updateStateOf( _titleId, 'defaultTitle', state.defaultTitle ); tsfTitle.updateStateOf( _titleId, 'addAdditions', state.addAdditions ); tsfTitle.updateStateOf( _titleId, 'additionValue', state.additionValue ); tsfTitle.updateStateOf( _titleId, 'additionPlacement', state.additionPlacement ); } /** * Updates title additions, based on singular settings change. * * @function * @param {Event} event */ const updateTitleAdditions = event => { let addAdditions = ! event.target.checked; if ( l10n.params.additionsForcedDisabled ) { addAdditions = false; } else if ( l10n.params.additionsForcedEnabled ) { addAdditions = true; } tsfTitle.updateStateOf( _titleId, 'addAdditions', addAdditions ); } const blogNameTrigger = document.getElementById( 'autodescription_title_no_blogname' ); if ( blogNameTrigger ) { blogNameTrigger.addEventListener( 'change', updateTitleAdditions ); blogNameTrigger.dispatchEvent( new Event( 'change' ) ); } /** * Sets private/protected visibility state. * * @function * @param {string} visibility */ const setTitleVisibilityPrefix = visibility => { let prefixValue = ''; switch ( visibility ) { case 'password': prefixValue = tsfTitle.protectedPrefix; break; case 'private': prefixValue = tsfTitle.privatePrefix; break; default: case 'public': prefixValue = ''; break; } tsfTitle.updateStateOf( _titleId, 'prefixValue', prefixValue ); } _registerPostPrivacyListener( setTitleVisibilityPrefix ); if ( l10n.states.isPrivate ) { setTitleVisibilityPrefix( 'private' ); } else if ( l10n.states.isProtected ) { setTitleVisibilityPrefix( 'password' ); } /** * Updates default title placeholder. * * @function * @param {string} value */ const updateDefaultTitle = val => { val = val?.trim() || ''; tsfTitle.updateStateOf( _titleId, 'defaultTitle', ( tsfTitle.stripTitleTags ? tsf.stripTags( val ) : val ) || tsfTitle.untitledTitle, ); } // The homepage listens to a static preset value. Update all others. if ( ! l10n.params.isFront ) { document.querySelector( '#titlewrap #title' ) // Extra specific to only target Classic Editor. ?.addEventListener( 'input', event => { updateDefaultTitle( event.target.value ) }, ); document.addEventListener( 'tsf-updated-block-editor-title', event => updateDefaultTitle( event.detail.value ) ); } tsfTitle.enqueueUnregisteredInputTrigger( _titleId ); } /** * Initializes description meta input listeners. * * @since 4.0.0 * @since 4.1.2 Now prefills the 'useDefaultDescription' accordingly. * @access private */ function _initDescriptionListeners() { const descInput = document.getElementById( _descId ); if ( ! descInput ) return; tsfDescription.setInputElement( descInput ); const state = JSON.parse( document.getElementById( `tsf-description-data_${_descId}` )?.dataset.state || 0 ); if ( state ) { tsfDescription.updateStateOf( _descId, 'allowReferenceChange', ! state.refDescriptionLocked ); tsfDescription.updateStateOf( _descId, 'defaultDescription', state.defaultDescription.trim() ); } tsfDescription.enqueueUnregisteredInputTrigger( _descId ); /** * Sets private/protected visibility state. * * @function * @param {string} visibility */ const setDescriptionVisibility = visibility => { let oldUseDefaultDescription = tsfDescription.getStateOf( _descId, 'useDefaultDescription' ), useDefaultDescription = true; switch ( visibility ) { case 'password': case 'private': useDefaultDescription = false; break; default: case 'public': useDefaultDescription = true; break; } if ( useDefaultDescription !== oldUseDefaultDescription ) tsfDescription.updateStateOf( _descId, 'useDefaultDescription', useDefaultDescription ); } _registerPostPrivacyListener( setDescriptionVisibility ); if ( l10n.states.isPrivate ) { setDescriptionVisibility( 'private' ); } else if ( l10n.states.isProtected ) { setDescriptionVisibility( 'password' ); } } /** * Initializes social meta input listeners. * * @since 4.2.0 * @access private */ function _initSocialListeners() { tsfSocial.setInputInstance( _socialGroup, _titleId, _descId ); const groupData = JSON.parse( document.getElementById( `tsf-social-data_${_socialGroup}` )?.dataset.settings || 0, ); if ( ! groupData ) return; tsfSocial.updateStateOf( _socialGroup, 'addAdditions', groupData.og.state.addAdditions ); // tw Also has one. Maybe future. tsfSocial.updateStateOf( _socialGroup, 'defaults', { ogTitle: groupData.og.state.defaultTitle, twTitle: groupData.tw.state.defaultTitle, ogDesc: groupData.og.state.defaultDesc, twDesc: groupData.tw.state.defaultDesc, } ); tsfSocial.updateStateOf( _socialGroup, 'inputLocks', { ogTitle: groupData.og.state?.titleLock || false, twTitle: groupData.tw.state?.titleLock || false, ogDesc: groupData.og.state?.descLock || false, twDesc: groupData.tw.state?.descLock || false, } ); } /** * Initializes uncategorized general tab meta input listeners. * * @since 4.0.0 * @since 4.1.0 Removed postbox-toggled listener, since tsf-flex-resize is all-encapsulating now. * @access private */ function _initGeneralListeners() { const enqueueGeneralInputListeners = () => { tsfTitle.enqueueUnregisteredInputTrigger( _titleId ); tsfDescription.enqueueUnregisteredInputTrigger( _descId ); } document.getElementById( 'tsf-flex-inpost-tab-general' ) ?.addEventListener( 'tsf-flex-tab-toggled', enqueueGeneralInputListeners ); window.addEventListener( 'tsf-flex-resize', enqueueGeneralInputListeners ); } /** * Updates the SEO Bar and meta description placeholders on successful save. * * @since 4.0.0 * @access private */ function _initUpdateMetaBox() { if ( ! l10n.params.isBlockEditor ) return; const seobar = document.querySelector( '.tsf-seo-bar' ); // We only use this because it looks nice. The rest is implied via the counter updates. const seobarAjaxLoader = document.querySelector( '#tsf-doing-it-right-wrap .tsf-ajax' ); const imageUrl = document.getElementById( 'autodescription_socialimage-url' ); const _ogDescription = tsfSocial.getInputInstance( _socialGroup )?.inputs?.ogDesc; const _twDescription = tsfSocial.getInputInstance( _socialGroup )?.inputs?.twDesc; const getData = { seobar: !! seobar, metadescription: !! document.getElementById( _descId ), ogdescription: !! _ogDescription, twdescription: !! _twDescription, imageurl: !! imageUrl, } const onSuccess = response => { response = tsf.convertJSONResponse( response ); // Wait the same amount of time as the SEO Bar, so to sync the changes. const fadeTime = 75; setTimeout( () => { tsfDescription.updateStateOf( _descId, 'defaultDescription', response.data.metadescription.trim() ); const socialDefaults = tsfSocial.getStateOf( _socialGroup, 'defaults' ); socialDefaults.ogDesc = response.data.ogdescription.trim(); socialDefaults.twDesc = response.data.twdescription.trim(); tsfSocial.updateStateOf( _socialGroup, 'defaults', socialDefaults ); if ( imageUrl ) { // Is this necessary? It's safer than assuming, though :) imageUrl.placeholder = tsf.decodeEntities( response.data.imageurl ); imageUrl.dispatchEvent( new Event( 'change' ) ); tsfTT.triggerReset(); } tsfAys.reset(); }, fadeTime, ); seobar && tsfUI.fadeOut( seobar, fadeTime, () => { seobarAjaxLoader && tsf.unsetAjaxLoader( seobarAjaxLoader, true ); seobar.innerHTML = response.data.seobar; tsfUI.fadeIn( seobar, fadeTime, () => { tsfTT.triggerReset(); } ) }, ); }; const onFailure = () => { seobarAjaxLoader && tsf.unsetAjaxLoader( seobarAjaxLoader, false ); } document.addEventListener( 'tsf-gutenberg-onsave', () => { // Reset ajax loader, we only do that for the SEO Bar. seobarAjaxLoader && tsf.resetAjaxLoader( seobarAjaxLoader ); // Set ajax loader. seobarAjaxLoader && tsf.setAjaxLoader( seobarAjaxLoader ); wp.ajax.send( 'tsf_update_post_data', { data: { nonce: l10n.nonces.edit_post[ l10n.params.id ], post_id: l10n.params.id, get: getData, }, timeout: 7000, }, ).done( onSuccess ).fail( onFailure ); } ); } /** * Initializes settings scripts on TSF-load. * * @since 4.0.0 * @since 5.1.0 Added error handling. * @access private */ function _loadSettings() { // One is not reliant on the other; this way, if one crashes, the rest still works. [ _initVisibilityListeners, _initTitleListeners, _initDescriptionListeners, _initSocialListeners, _initGeneralListeners, ].forEach( fn => { try { fn(); } catch ( error ) { console.error( `Error in ${fn.name}:`, error ); } } ); } /** * Initializes settings scripts on TSF-ready. * * @since 4.0.0 * @since 4.1.0 Now registers the refNa title input. * @access private */ function _readySettings() { // Initializes flex tab resize listeners. _doFlexResizeListener(); // Initializes flex tab listeners and fixes positions. _initTabs(); // Set Gutenberg update listeners. _initUpdateMetaBox(); } 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', _loadSettings ); document.body.addEventListener( 'tsf-ready', _readySettings ); }, }, { l10n, } ); }(); window.tsfPost.load();
Cokiee Shell Web 1.0, Coded By Razor
Neueste Kommentare