How to use additions method in wpt

Best JavaScript code snippet using wpt

title.js

Source:title.js Github

copy

Full Screen

1/**2 * This file holds The SEO Framework plugin's JS code for TSF title fields.3 * Serve JavaScript as an addition, not as an ends or means.4 *5 * @author Sybre Waaijer <https://cyberwire.nl/>6 * @link <https://wordpress.org/plugins/autodescription/>7 */8/**9 * The SEO Framework plugin10 * Copyright (C) 2019 - 2022 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)11 *12 * This program is free software: you can redistribute it and/or modify13 * it under the terms of the GNU General Public License version 3 as published14 * by the Free Software Foundation.15 *16 * This program is distributed in the hope that it will be useful,17 * but WITHOUT ANY WARRANTY; without even the implied warranty of18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the19 * GNU General Public License for more details.20 *21 * You should have received a copy of the GNU General Public License22 * along with this program. If not, see <http://www.gnu.org/licenses/>.23 */24'use strict';25/**26 * Holds tsfTitle values in an object to avoid polluting global namespace.27 *28 * Only one instance should act on this per window.29 *30 * @since 4.0.031 *32 * @constructor33 */34window.tsfTitle = function() {35 /**36 * Data property injected by WordPress l10n handler.37 *38 * @since 4.0.039 * @access public40 * @type {(Object<string, *>)|boolean|null} l10n Localized strings.41 */42 const l10n = 'undefined' !== typeof tsfTitleL10n && tsfTitleL10n;43 /**44 * @since 4.0.045 * @access public46 * @type {String}47 */48 const untitledTitle = tsf.escapeString( l10n.params.untitledTitle );49 /**50 * @since 4.1.051 * @access public52 * @type {String}53 */54 const protectedPrefix = tsf.escapeString( l10n.i18n.protectedTitle );55 /**56 * @since 4.1.057 * @access public58 * @type {String}59 */60 const privatePrefix = tsf.escapeString( l10n.i18n.privateTitle );61 /**62 * @since 4.1.063 * @access public64 * @type {Boolean}65 */66 const stripTitleTags = !! l10n.params.stripTitleTags;67 /**68 * @since 4.1.069 * @type {(Map<string,Element>)} The input element instances.70 */71 const titleInputInstances = new Map();72 /**73 * @since 4.1.074 * @access private75 * @type {(Object<string,Object<string,*>)} the query state.76 */77 const states = {};78 /**79 * @since 4.1.080 * @access private81 * @type {(Map<string,string>)} The input element instances.82 */83 const additionsStack = new Map();84 /**85 * @since 4.1.086 * @access private87 * @type {(Map<string,string>)} The input element instances.88 */89 const prefixStack = new Map();90 /**91 * @since 4.1.092 * @internal Use getStateOf() instead.93 * @access private94 *95 * @param {String} id96 * @param {String} value97 * @return {String} The additions value.98 */99 const _getAdditionsValue = id => additionsStack.get( id ) || '';100 /**101 * @since 4.1.0102 * @internal Use getStateOf() instead.103 * @access private104 *105 * @param {String} id106 * @param {String} value107 * @return {String} The prefix value.108 */109 const _getPrefixValue = id => prefixStack.get( id ) || '';110 /**111 * @since 4.1.0112 * @internal Use updateStateOf() instead.113 * @access private114 *115 * @param {String} id116 * @param {String} value117 * @return {String} The new value.118 */119 const _setAdditionsValue = ( id, value ) => additionsStack.set( id, value ) && _getAdditionsValue( id );120 /**121 * @since 4.1.0122 * @internal Use updateStateOf() instead.123 * @access private124 *125 * @param {String} id126 * @param {String} value127 * @return {String} The new value.128 */129 const _setPrefixValue = ( id, value ) => prefixStack.set( id, value ) && _getPrefixValue( id );130 /**131 * @since 4.1.0132 * @access private133 * @return {Element}134 */135 const _getHoverPrefixElement = id => document.getElementById( `tsf-title-placeholder-prefix_${id}` ) || document.createElement( 'span' );136 /**137 * @since 4.1.0138 * @access private139 * @return {Element}140 */141 const _getHoverAdditionsElement = id => document.getElementById( `tsf-title-placeholder-additions_${id}` ) || document.createElement( 'span' );142 /**143 * Sets input element for all listeners. Must be called prior interacting with this object.144 * Resets the state for the input ID.145 *146 * @since 4.0.0147 * @since 4.1.0 Now creates an instance in a map this object, and returns it.148 * @access public149 *150 * @param {Element} element151 */152 const setInputElement = element => {153 titleInputInstances.set( element.id, element );154 states[ element.id ] = {155 showPrefix: true,156 allowReferenceChange: true,157 defaultTitle: '',158 separator: l10n.states.titleSeparator,159 prefixPlacement: l10n.states.prefixPlacement,160 }161 _loadTitleActions( element );162 return getInputElement( element.id );163 }164 /**165 * Gets input element, if exists.166 *167 * @since 4.1.0168 * @access public169 *170 * @param {string} id The element ID.171 * @return {Element}172 */173 const getInputElement = id => titleInputInstances.get( id );174 /**175 * Returns state of ID.176 *177 * @since 4.1.0178 * @access public179 *180 * @param {string} id The input element ID.181 * @param {(string|undefined)} part The part to return. Leave empty to return the whole state.182 * @return {(Object<string,*>)|*|null}183 */184 const getStateOf = ( id, part ) => part ? states[ id ]?.[ part ] : states[ id ];185 /**186 * Updates state of ID.187 *188 * There's no need to escape the input, it may be double-escaped if you do so.189 *190 * @since 4.1.0191 * @since 4.2.0 Now remains intert on a non-change.192 * @access public193 *194 * @param {string} id The input element ID.195 * @param {string} part The state index to change.196 * @param {*} value The value to set the state to.197 */198 const updateStateOf = ( id, part, value ) => {199 if ( states[ id ][ part ] === value ) return;200 states[ id ][ part ] = value;201 switch ( part ) {202 case 'showPrefix':203 case 'prefixValue':204 case 'prefixPlacement':205 _updatePrefixValue( id );206 enqueueTriggerInput( id );207 break;208 case 'addAdditions':209 case 'separator':210 case 'additionValue':211 case 'additionPlacement':212 _updateAdditionsValue( id );213 enqueueTriggerInput( id );214 break;215 case 'allowReferenceChange':216 case 'defaultTitle':217 default:218 enqueueTriggerInput( id );219 break;220 }221 }222 /**223 * Updates state of all elements.224 *225 * There's no need to escape the input, it may be double-escaped if you do so.226 *227 * @since 4.1.0228 * @since 4.2.0 Added a 3rd parameter, allowing you to exclude updates for certain elements.229 * @access public230 *231 * @param {string} part The state index to change.232 * @param {*} value The value to set the state to.233 * @param {string|string[]} except The input element IDs to exclude from updates.234 */235 const updateStateAll = ( part, value, except ) => {236 except = Array.isArray( except ) ? except : [ except ];237 titleInputInstances.forEach( element => {238 if ( except.includes( element.id ) ) return;239 updateStateOf( element.id, part, value );240 } );241 }242 /**243 * Returns title references of ID.244 *245 * @since 4.1.0246 * @access public247 *248 * @param {string} id The input element ID.249 * @return {HTMLElement[]}250 */251 const _getTitleReferences = id => {252 let references = [ document.getElementById( `tsf-title-reference_${id}` ) ];253 if ( getStateOf( id, 'hasLegacy' ) ) {254 let legacy = document.getElementById( 'tsf-title-reference' );255 legacy && references.unshift( legacy );256 }257 return references;258 }259 /**260 * Returns title references with no-additions (Na) of ID.261 *262 * @since 4.1.0263 * @access public264 *265 * @param {string} id The input element ID.266 * @return {HTMLElement[]}267 */268 const _getTitleNaReferences = id => [ document.getElementById( `tsf-title-noadditions-reference_${id}` ) ];269 /**270 * Updates the title reference.271 *272 * Used by the character counters, pixel counters, and social meta inputs.273 *274 * @since 4.0.0275 * @since 4.0.6 Now changes behavior depending on RTL-status.276 * @since 4.1.0 1. Now also sets references without the additions.277 * 2. Now supports multiple instances.278 * @access private279 *280 * @function281 * @param {Event} event282 * @return {HTMLElement[]}283 */284 const _setReferenceTitle = event => {285 const references = _getTitleReferences( event.target.id ),286 referencesNa = _getTitleNaReferences( event.target.id );287 if ( ! references[0] || ! referencesNa[0] ) return;288 const allowReferenceChange = getStateOf( event.target.id, 'allowReferenceChange' );289 let text = allowReferenceChange && event.target.value.trim() || getStateOf( event.target.id, 'defaultTitle' ) || '',290 textNa = text;291 if ( text.length && allowReferenceChange ) {292 let prefix = _getPrefixValue( event.target.id ),293 additions = _getAdditionsValue( event.target.id );294 if ( prefix.length && getStateOf( event.target.id, 'showPrefix' ) ) {295 switch ( getStateOf( event.target.id, 'prefixPlacement' ) ) {296 case 'before':297 if ( window.isRtl ) {298 text = text + prefix;299 } else {300 text = prefix + text;301 }302 break;303 case 'after':304 if ( window.isRtl ) {305 text = prefix + text;306 } else {307 text = text + prefix;308 }309 break;310 }311 textNa = text;312 }313 if ( additions.length ) {314 switch ( getStateOf( event.target.id, 'additionPlacement' ) ) {315 case 'before':316 text = additions + text;317 break;318 case 'after':319 text = text + additions;320 break;321 }322 }323 }324 const referenceValue = tsf.escapeString(325 tsf.decodeEntities(326 tsf.sDoubleSpace(327 tsf.sTabs(328 tsf.sSingleLine(329 text330 ).trim()331 )332 )333 ) );334 const referenceNaValue = tsf.escapeString(335 tsf.decodeEntities(336 tsf.sDoubleSpace(337 tsf.sTabs(338 tsf.sSingleLine(339 textNa340 ).trim()341 )342 )343 ) );344 const changeEvent = new Event( 'change' );345 references.forEach( reference => {346 // We require the event below when adjusting some states... Don't uncomment this.347 // if ( reference.innerHTML === referenceValue ) return;348 reference.innerHTML = referenceValue;349 // Fires change event. Defered to another thread.350 setTimeout( () => { reference.dispatchEvent( changeEvent ) }, 0 );351 } );352 referencesNa.forEach( referenceNa => {353 // We require the event below when adjusting some states... Don't uncomment this.354 // if ( referenceNa.innerHTML === referenceNaValue ) return;355 referenceNa.innerHTML = referenceNaValue;356 // Fires change event. Defered to another thread.357 setTimeout( () => { referenceNa.dispatchEvent( changeEvent ) }, 0 );358 } );359 }360 /**361 * Updates hover additions.362 *363 * @since 4.0.0364 * @since 4.1.0 Now supports multiple instances.365 * @access private366 *367 * @param {string} id The input ID.368 */369 const _updateAdditionsValue = id => {370 let value = '',371 additionsValue = '',372 separator = '';373 if ( getStateOf( id, 'addAdditions' ) ) {374 additionsValue = tsf.escapeString( tsf.decodeEntities( getStateOf( id, 'additionValue' ) ) );375 separator = getStateOf( id, 'separator' );376 }377 if ( additionsValue ) {378 switch ( getStateOf( id, 'additionPlacement' ) ) {379 case 'before':380 value = `${additionsValue} ${separator} `;381 break;382 case 'after':383 value = ` ${separator} ${additionsValue}`;384 break;385 }386 }387 _getHoverAdditionsElement( id ).innerHTML = _setAdditionsValue( id, value || '' );388 }389 /**390 * Updates hover prefix.391 *392 * @since 4.0.0393 * @since 4.0.6 Now changes behavior depending on RTL-status.394 * @since 4.1.0 Now supports multiple instances.395 * @access private396 *397 * @param {string} id The input ID.398 */399 const _updatePrefixValue = id => {400 let value = '',401 showPrefix = getStateOf( id, 'showPrefix' ),402 prefixValue = getStateOf( id, 'prefixValue' );403 if ( showPrefix && prefixValue ) {404 switch ( getStateOf( id, 'prefixPlacement' ) ) {405 case 'before':406 if ( window.isRtl ) {407 value = ` ${prefixValue}`;408 } else {409 value = `${prefixValue} `;410 }411 break;412 case 'after':413 if ( window.isRtl ) {414 value = `${prefixValue} `;415 } else {416 value = ` ${prefixValue}`;417 }418 break;419 }420 }421 _getHoverPrefixElement( id ).innerHTML = _setPrefixValue( id, value || '' );422 }423 /**424 * Updates the title hover prefix and additions placement.425 *426 * @since 4.0.0427 * @since 4.1.0 Now supports multiple instances.428 * @since 4.2.0 1. No longer relies on jQuery.429 * 2. Now supports dynamic border sizes, this means that you can430 * make a skewed-proportioned input element, and the hovers431 * will align properly with the input text.432 * @access private433 *434 * @function435 * @param {Event} event436 */437 const _updateHoverPlacement = event => {438 let hoverAdditionsElement = _getHoverAdditionsElement( event.target.id ),439 hoverPrefixElement = _getHoverPrefixElement( event.target.id );440 if ( ! hoverAdditionsElement && ! hoverPrefixElement )441 return;442 const input = event.target,443 inputValue = event.target.value;444 const hasPrefixValue = _getPrefixValue( event.target.id ).length && getStateOf( event.target.id, 'showPrefix' ),445 hasAdditionsValue = !! _getAdditionsValue( event.target.id ).length;446 if ( ! hasPrefixValue && hoverPrefixElement )447 hoverPrefixElement.style.display = 'none';448 if ( ! hasAdditionsValue && hoverAdditionsElement )449 hoverAdditionsElement.style.display = 'none';450 if ( ! hasPrefixValue && ! hasAdditionsValue ) {451 //= Both items are emptied through settings.452 input.style.textIndent = 'initial';453 return;454 }455 if ( ! inputValue.length ) {456 //= Input is emptied.457 input.style.textIndent = 'initial';458 if ( hoverPrefixElement ) hoverPrefixElement.style.display = 'none';459 if ( hoverAdditionsElement ) hoverAdditionsElement.style.display = 'none';460 return;461 }462 const inputStyles = getComputedStyle( input ),463 inputRect = input.getBoundingClientRect();464 // Quick and dirty.465 const paddingTop = parseInt( inputStyles.paddingTop ),466 paddingRight = parseInt( inputStyles.paddingRight ),467 paddingBottom = parseInt( inputStyles.paddingBottom ),468 paddingLeft = parseInt( inputStyles.paddingLeft ),469 borderTop = parseInt( inputStyles.borderTopWidth ),470 borderRight = parseInt( inputStyles.borderRightWidth ),471 borderBottom = parseInt( inputStyles.borderBottomWidth ),472 borderLeft = parseInt( inputStyles.borderTopWidth ),473 marginTop = parseInt( inputStyles.marginTop ),474 marginRight = parseInt( inputStyles.marginRight ),475 marginBottom = parseInt( inputStyles.marginBottom ),476 marginLeft = parseInt( inputStyles.marginLeft );477 const offsetPosition = window.isRtl ? 'right' : 'left',478 leftOffset = paddingLeft + borderLeft + marginLeft,479 rightOffset = paddingRight + borderRight + marginRight;480 let fontStyleCSS = {481 display: inputStyles.display,482 lineHeight: inputStyles.lineHeight,483 fontFamily: inputStyles.fontFamily,484 fontWeight: inputStyles.fontWeight,485 fontSize: inputStyles.fontSize,486 letterSpacing: inputStyles.letterSpacing,487 marginTop: `${marginTop}px`,488 marginBottom: `${marginBottom}px`,489 paddingTop: `${paddingTop}px`,490 paddingBottom: `${paddingBottom}px`,491 border: `0 solid transparent`,492 borderTopWidth: `${borderTop}px`,493 borderBottomWidth: `${borderBottom}px`,494 };495 let additionsMaxWidth = 0,496 additionsOffset = 0,497 prefixOffset = 0,498 totalIndent = 0,499 prefixMaxWidth = 0;500 if ( hasPrefixValue ) {501 Object.assign(502 hoverPrefixElement.style,503 fontStyleCSS,504 { maxWidth: 'initial' },505 );506 prefixMaxWidth = hoverPrefixElement.getBoundingClientRect().width;507 prefixOffset += leftOffset; // rightOffset for RTL? -> difficult to determine?508 }509 if ( hasAdditionsValue ) {510 Object.assign(511 hoverAdditionsElement.style,512 fontStyleCSS,513 { maxWidth: 'initial' },514 );515 const offsetElement = document.getElementById( `tsf-title-offset_${event.target.id}` );516 offsetElement.textContent = inputValue;517 Object.assign(518 offsetElement.style,519 {520 fontFamily: fontStyleCSS.fontFamily,521 fontWeight: fontStyleCSS.fontWeight,522 fontSize: fontStyleCSS.fontSize,523 letterSpacing: fontStyleCSS.letterSpacing,524 },525 );526 const textWidth = offsetElement.getBoundingClientRect().width;527 const additionsWidth = hoverAdditionsElement.getBoundingClientRect().width;528 switch ( getStateOf( event.target.id, 'additionPlacement' ) ) {529 case 'before':530 additionsMaxWidth = inputRect.width - rightOffset - paddingLeft - borderLeft - textWidth - prefixMaxWidth;531 if ( additionsMaxWidth < 0 ) {532 // Add negative width to the prefix element, so it may stay its size, and hide the additions first.533 prefixMaxWidth += additionsMaxWidth;534 additionsMaxWidth = 0;535 }536 additionsMaxWidth = additionsMaxWidth < additionsWidth ? additionsMaxWidth : additionsWidth;537 if ( additionsMaxWidth < 0 )538 additionsMaxWidth = 0;539 totalIndent += additionsMaxWidth;540 prefixOffset += additionsMaxWidth;541 // "We" write to the right, so we take the leftoffset. TODO RTL?542 additionsOffset += leftOffset;543 break;544 case 'after':545 additionsMaxWidth = inputRect.width - leftOffset - paddingRight - borderRight - textWidth - prefixMaxWidth;546 if ( additionsMaxWidth < 0 ) {547 // Add negative width to the prefix element, so it may stay its size, and hide the additions first.548 prefixMaxWidth += additionsMaxWidth;549 additionsMaxWidth = 0;550 }551 additionsMaxWidth = additionsMaxWidth < additionsWidth ? additionsMaxWidth : additionsWidth;552 if ( additionsMaxWidth < 0 )553 additionsMaxWidth = 0;554 // "We" write to the right, so we take the leftoffset. TODO RTL?555 additionsOffset += leftOffset + textWidth + prefixMaxWidth;556 break;557 }558 }559 prefixMaxWidth = prefixMaxWidth < 0 ? 0 : prefixMaxWidth;560 if ( hasPrefixValue ) {561 Object.assign(562 hoverPrefixElement.style,563 {564 [offsetPosition]: `${prefixOffset}px`,565 maxWidth: `${prefixMaxWidth}px`,566 }567 );568 // Only set if there's actually a prefix.569 totalIndent += prefixMaxWidth;570 }571 if ( hasAdditionsValue ) {572 Object.assign(573 hoverAdditionsElement.style,574 {575 [offsetPosition]: `${additionsOffset}px`,576 maxWidth: `${additionsMaxWidth}px`,577 }578 );579 }580 input.style.textIndent = `${totalIndent}px`;581 }582 /**583 * Updates the title placeholder.584 *585 * @since 4.0.0586 * @since 4.1.0 Now consistently sets a reliable placeholder.587 * @access private588 *589 * @function590 * @param {Event} event591 */592 const _updatePlaceholder = event => {593 event.target.placeholder = _getTitleReferences( event.target.id )[0].textContent;594 }595 /**596 * Updates the character counter bound to the input.597 *598 * @since 4.0.0599 * @access private600 *601 * @function602 * @param {Event} event603 */604 const _updateCounter = event => {605 if ( ! ( 'tsfC' in window ) ) return;606 let counter = document.getElementById( `${event.target.id}_chars` ),607 reference = _getTitleReferences( event.target.id )[0];608 if ( ! counter ) return;609 tsfC.updateCharacterCounter( {610 e: counter,611 text: reference.innerHTML,612 field: 'title',613 type: 'search',614 } );615 }616 /**617 * Updates the pixel counter bound to the input.618 *619 * @since 4.0.0620 * @access private621 *622 * @function623 * @param {Event} event624 */625 const _updatePixels = event => {626 if ( ! ( 'tsfC' in window ) ) return;627 let pixels = document.getElementById( `${event.target.id}_pixels` ),628 reference = _getTitleReferences( event.target.id )[0];629 if ( ! pixels ) return;630 tsfC.updatePixelCounter( {631 e: pixels,632 text: reference.innerHTML,633 field: 'title',634 type: 'search',635 } );636 }637 /**638 * Triggers meta title input.639 *640 * @since 4.0.0641 * @since 4.1.0 Now allows for a first parameter to be set.642 * @access public643 *644 * @function645 * @param {string} id The input id. When not set, all inputs will be triggered.646 */647 const triggerInput = id => {648 if ( id ) {649 let el = getInputElement( id );650 el && el.dispatchEvent( new Event( 'input' ) );651 } else {652 // We don't want it to loop infinitely. Check element.id value first.653 titleInputInstances.forEach( element => element.id && triggerInput( element.id ) );654 }655 }656 /**657 * Triggers counter updates.658 *659 * @since 4.0.0660 * @since 4.1.0 Now allows for a first parameter to be set.661 * @access public662 *663 * @function664 * @param {string} id The input id. When not set, all inputs will be triggered.665 */666 const triggerCounter = id => {667 if ( id ) {668 let el = getInputElement( id );669 el && el.dispatchEvent( new CustomEvent( 'tsf-update-title-counter' ) );670 } else {671 // We don't want it to loop infinitely. Check element.id value first.672 titleInputInstances.forEach( element => element.id && triggerCounter( element.id ) );673 }674 }675 /**676 * Updates placements, placeholders and counters.677 *678 * @since 4.0.0679 * @access private680 * @see triggerInput681 * @uses _onUpdateCounterTrigger682 *683 * @function684 * @param {Event} event685 */686 const _onUpdateTitlesTrigger = event => {687 _updateHoverPlacement( event );688 _setReferenceTitle( event );689 _updatePlaceholder( event );690 _onUpdateCounterTrigger( event );691 }692 /**693 * Updates character counters.694 *695 * @since 4.0.0696 * @access private697 * @see triggerCounter698 *699 * @function700 * @param {Event} event701 */702 const _onUpdateCounterTrigger = event => {703 _updateCounter( event );704 _updatePixels( event );705 }706 let _enqueueTriggerInputBuffer = {};707 /**708 * Triggers meta title input.709 *710 * @since 4.0.0711 * @since 4.1.1 Added first parameter, id.712 * @access public713 *714 * @function715 * @param {string} id The input ID.716 */717 const enqueueTriggerInput = id => {718 ( id in _enqueueTriggerInputBuffer ) && clearTimeout( _enqueueTriggerInputBuffer[ id ] );719 _enqueueTriggerInputBuffer[ id ] = setTimeout( () => triggerInput( id ), 1000/60 ); // 60 fps720 }721 /**722 * Triggers meta title update, without affecting tsfAys change listeners.723 *724 * @since 4.0.0725 * @since 4.1.0 Now allows for a first parameter to be set.726 * @access public727 *728 * @function729 * @param {string} id The input id. When not set, all inputs will be triggered.730 */731 const triggerUnregisteredInput = id => {732 if ( 'tsfAys' in window ) {733 let wereSettingsChanged = tsfAys.areSettingsChanged();734 triggerInput( id );735 // Only reset if we polluted the change listener, and only if a change wasn't already registered.736 if ( ! wereSettingsChanged && tsfAys.areSettingsChanged() )737 tsfAys.reset();738 } else {739 triggerInput( id );740 }741 }742 let _unregisteredTriggerBuffer = {};743 /**744 * Enqueues unregistered title input triggers.745 *746 * @since 4.0.0747 * @since 4.1.0 Now allows for a first parameter to be set.748 * @access public749 *750 * @function751 * @param {string} id The input id. When not set, all inputs will be triggered.752 */753 const enqueueUnregisteredInputTrigger = id => {754 ( id in _unregisteredTriggerBuffer ) && clearTimeout( _unregisteredTriggerBuffer[ id ] );755 _unregisteredTriggerBuffer[ id ] = setTimeout( () => triggerUnregisteredInput( id ), 1000/60 ); // 60 fps756 }757 /**758 * Makes user click act naturally by selecting the adjacent Title text759 * input and move cursor all the way to the end.760 *761 * @since 4.0.0762 * @since 4.1.0 Now supports multiple instances.763 * @TODO can we not just make the floaty mcfloattitle transparent to clicks?764 * @access private765 *766 * @function767 * @param {Event} event768 */769 const _focusTitleInput = event => {770 let input = document.getElementById( event.target.dataset.for );771 if ( ! input ) return;772 let type = event.target.classList.contains( 'tsf-title-placeholder-additions' ) ? 'additions' : 'prefix',773 inputValue = input.value;774 // Make sure the input is focussed, if it wasn't already.775 input.focus();776 switch ( event.detail ) {777 case 3:778 input.setSelectionRange( 0, inputValue.length );779 break;780 case 2:781 let start, end;782 if (783 'additions' === type && 'after' === getStateOf( input.id, 'additionPlacement' )784 || 'prefix' === type && window.isRtl785 ) {786 start = inputValue.replace( /(\w+|\s+)$/u, '' ).length;787 end = inputValue.length;788 } else {789 start = 0;790 end = inputValue.length - inputValue.replace( /^(\s+|\w+)/u, '' ).length;791 }792 input.setSelectionRange( start, end );793 break;794 case 1:795 default:796 // Set length to end if the placeholder is clicked; to 0 otherwise (prefix clicked).797 let length = 'additions' === type && 'after' === getStateOf( input.id, 'additionPlacement' )798 ? inputValue.length799 : 0;800 input.setSelectionRange( length, length );801 break;802 }803 }804 /**805 * Prevents focus on event.806 *807 * @since 4.1.0808 *809 * @param {Event} event810 * @return {void}811 */812 const _preventFocus = event => event.preventDefault();813 let prevWidth = window.innerWidth;814 /**815 * Triggers input event for titles in set intervals on window resize.816 *817 * This only happens if boundaries are surpassed to reduce CPU usage.818 * This boundary is 782 pixels, because that forces input fields to change.819 * in WordPress.820 *821 * This happens to all title inputs; as WordPress switches822 * from Desktop to Mobile view at 782 pixels.823 *824 * @since 4.0.0825 * @access private826 * @see ...\wp-admin\js\common.js827 *828 * @function829 */830 const _doResize = () => {831 let width = window.innerWidth;832 if ( prevWidth < width ) {833 if ( prevWidth <= 782 && width >= 782 ) {834 triggerUnregisteredInput();835 }836 } else {837 if ( prevWidth >= 782 && width <= 782 ) {838 triggerUnregisteredInput();839 }840 }841 prevWidth = width;842 }843 /**844 * Initializes the title environment.845 *846 * @since 4.1.0847 * @since 4.1.1 No longer passes the event to the enqueueUnregisteredInputTrigger() callback.848 * @access private849 *850 * @function851 */852 const _initAllTitleActions = () => {853 // Triggers input changes on resize after hitting thresholds.854 window.addEventListener( 'tsf-resize', _doResize );855 // When counters are updated, trigger an input; which will reassess them.856 window.addEventListener( 'tsf-counter-updated', () => enqueueUnregisteredInputTrigger() );857 }858 /**859 * Initializes the title input action callbacks.860 *861 * @since 4.0.0862 * @access private863 *864 * @function865 * @param {Element} titleInput866 */867 const _loadTitleActions = titleInput => {868 if ( ! titleInput instanceof Element ) return;869 titleInput.addEventListener( 'input', _onUpdateTitlesTrigger );870 titleInput.addEventListener( 'tsf-update-title-counter', _onUpdateCounterTrigger );871 let hoverPrefix = _getHoverPrefixElement( titleInput.id ),872 hoverAdditions = _getHoverAdditionsElement( titleInput.id );873 hoverPrefix.addEventListener( 'click', _focusTitleInput );874 hoverAdditions.addEventListener( 'click', _focusTitleInput );875 // Don't allow focus of the floating elements.876 hoverPrefix.addEventListener( 'mousedown', _preventFocus );877 hoverAdditions.addEventListener( 'mousedown', _preventFocus );878 _updateAdditionsValue( titleInput.id );879 _updatePrefixValue( titleInput.id );880 enqueueUnregisteredInputTrigger( titleInput.id );881 }882 return Object.assign( {883 /**884 * Initialises all aspects of the scripts.885 * You shouldn't call this.886 *887 * @since 4.0.0888 * @access protected889 *890 * @function891 */892 load: () => {893 document.body.addEventListener( 'tsf-onload', _initAllTitleActions );894 },895 }, {896 setInputElement,897 getInputElement,898 getStateOf,899 updateStateOf,900 updateStateAll,901 triggerCounter,902 triggerInput,903 enqueueTriggerInput,904 triggerUnregisteredInput,905 enqueueUnregisteredInputTrigger, // this should've been enqueueTriggerUnregisteredInput...906 }, {907 l10n,908 untitledTitle,909 privatePrefix,910 protectedPrefix,911 stripTitleTags,912 } );913}();...

Full Screen

Full Screen

coffee-order.service.ts

Source:coffee-order.service.ts Github

copy

Full Screen

1import { Injectable } from '@angular/core';2import { BehaviorSubject, Observable, of } from 'rxjs';3import { AdditionTypes, CoffeeAddition, CoffeeAdditionOption, DairyFoam } from '../models/coffee-products';4import { CoffeeOrder } from '../models/coffee-order';5import { demoAdditions, demoCappuccino, demoOrderCappuccino, demoOrderLatte } from '../models/demo-data';6/* Dear Reader: Don't worry about this code too much! We're faking a "coffee products" API here, just know that we're returning observables of coffee orders, and there are a few void functions for adding additions */7@Injectable()8export class CoffeeOrderService {9 private order: BehaviorSubject<CoffeeOrder>;10 public order$: Observable<CoffeeOrder>11 constructor() { 12 this.order = new BehaviorSubject(demoOrderLatte);13 this.order$ = this.order.asObservable() as Observable<CoffeeOrder>;14 }15 public getDemoAdditions(): Observable<CoffeeAddition[]> {16 return of(demoAdditions);17 }18 private getOptionsTotal(options: CoffeeAdditionOption[]): number {19 return options.reduce((total, option) => {20 total += option.price * option.quantity;21 return total;22 }, 0);23 }24 private getAdditionsTotal(additions: CoffeeAddition[]): number {25 return additions.reduce((total, addition) => {26 total += this.getOptionsTotal(addition.selectedOptions);27 return total;28 }, 0);29 }30 public convertToCappuccinoOrder(order: CoffeeOrder): void {31 const additionsToMap = order.additions.filter(addition => addition.name !== AdditionTypes.FOAM);32 let newOrder = demoOrderCappuccino;33 const updatedDefaultAdditions = newOrder.additions.map((addition) => {34 const existingAddition = additionsToMap.find(currentAddition => currentAddition.id === addition.id);35 return (existingAddition ? existingAddition : addition);36 });37 newOrder.additions = [...updatedDefaultAdditions, ...additionsToMap];38 this.order.next(newOrder);39 }40 public addAddition(order: CoffeeOrder, addition: CoffeeAddition): void {41 const filteredAdditions = order.additions.filter(currentAddition => currentAddition.id !== addition.id);42 const additions = [...filteredAdditions, addition];43 const total = order.product.price + this.getAdditionsTotal(additions);44 this.order.next({...order, additions, total});45 }46 public removeAddition(order: CoffeeOrder, addition: CoffeeAddition): void {47 const additionInstance = order.additions.find(currentAddition => currentAddition.id === addition.id);48 const additions = order.additions.filter((currentAddition) => {49 if (currentAddition.id !== additionInstance.id) {50 return true;51 } else {52 return additionInstance.selectedOptions.map((option) => {53 return (currentAddition.selectedOptions.find(currentOption => currentOption.id === option.id));54 });55 }56 });57 const additionTotal = additions.reduce((total, addition) => {58 total = addition.selectedOptions.reduce((total, option) => {59 total += option.price * option.quantity;60 return total;61 }, 0)62 return total;63 }, 0)64 const total = order.product.price + additionTotal;65 this.order.next({...order, additions, total});66 }67 public addFoam(order: CoffeeOrder, addition: CoffeeAddition, selectedFoam: DairyFoam): void {68 const updatedAddition = {...addition, selectedFoam};69 const additions = [...order.additions, {...updatedAddition}];70 this.order.next({...order, additions});71 }72 public removeFoam(order: CoffeeOrder, addition: CoffeeAddition): void {73 const updatedAddition = {...addition, selectedFoam: null};74 const additions = [...order.additions, {...updatedAddition}];75 this.order.next({...order, additions});76 }...

Full Screen

Full Screen

coffee-order.component.ts

Source:coffee-order.component.ts Github

copy

Full Screen

1import { Component, Input, OnInit } from '@angular/core';2import { Observable } from 'rxjs';3import { map } from 'rxjs/operators';4import { AdditionTypes, CoffeeAddition } from '../models/coffee-products';5import { CoffeeOrder } from '../models/coffee-order';6import { CoffeeOrderService } from '../services/coffee-order.service';7@Component({8 selector: 'app-coffee-order',9 templateUrl: './coffee-order.component.html',10 styleUrls: ['./coffee-order.component.css']11})12export class CoffeeOrderComponent implements OnInit {13 @Input()14 coffeeOrder: CoffeeOrder;15 allAdditions$: Observable<CoffeeAddition[]>16 standardAdditions$: Observable<CoffeeAddition[]>17 syrupAdditions$: Observable<CoffeeAddition[]>18 dairyAdditions$: Observable<CoffeeAddition[]>19 foamAdditions$: Observable<CoffeeAddition[]>20 constructor(private orderService: CoffeeOrderService) { }21 ngOnInit() {22 this.allAdditions$ = this.orderService.getDemoAdditions();23 this.standardAdditions$ = this.allAdditions$.pipe(24 map(additions => additions.filter(addition => addition.name !== AdditionTypes.SYRUP 25 && addition.name !== AdditionTypes.DAIRY26 && addition.name !== AdditionTypes.FOAM))27 );28 this.syrupAdditions$ = this.allAdditions$.pipe(29 map(additions => additions.filter(addition => addition.name === AdditionTypes.SYRUP))30 );31 this.dairyAdditions$ = this.allAdditions$.pipe(32 map(additions => additions.filter(addition => addition.name === AdditionTypes.DAIRY))33 );34 this.foamAdditions$ = this.allAdditions$.pipe(35 map(additions => additions.filter(addition => addition.name === AdditionTypes.FOAM))36 );37 }...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('./wpt.js');2wpt.additions(5, 10, function(err, result) {3 if (err) {4 console.log(err);5 } else {6 console.log(result);7 }8});9var wpt = require('./wpt.js');10wpt.subtractions(5, 10, function(err, result) {11 if (err) {12 console.log(err);13 } else {14 console.log(result);15 }16});17var wpt = require('./wpt.js');18wpt.multiplications(5, 10, function(err, result) {19 if (err) {20 console.log(err);21 } else {22 console.log(result);23 }24});25var wpt = require('./wpt.js');26wpt.divisions(5, 10, function(err, result) {27 if (err) {28 console.log(err);29 } else {30 console.log(result);31 }32});33var wpt = require('./wpt.js');34wpt.modulas(5, 10, function(err, result) {35 if (err) {36 console.log(err);37 } else {38 console.log(result);39 }40});41var wpt = require('./wpt.js');42wpt.squareroots(5, 10, function(err, result) {43 if (err) {44 console.log(err);45 } else {46 console.log(result);47 }48});49var wpt = require('./wpt.js');50wpt.powers(5, 10, function(err, result) {51 if (err) {52 console.log(err);53 } else {54 console.log(result);55 }56});57var wpt = require('./wpt.js');58wpt.factorial(5, 10, function(err, result) {59 if (err) {60 console.log(err);61 } else {62 console.log(result);63 }64});

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptools = require('wptools');2wptools.page('Barack Obama').then(function(page) {3 return page.additions();4}).then(function(result) {5 console.log(result);6});7var wptools = require('wptools');8wptools.page('Barack Obama').then(function(page) {9 return page.additions();10}).then(function(result) {11 console.log(result);12});13var wptools = require('wptools');14wptools.page('Barack Obama').then(function(page) {15 return page.additions();16}).then(function(result) {17 console.log(result);18});19var wptools = require('wptools');20wptools.page('Barack Obama').then(function(page) {21 return page.additions();22}).then(function(result) {23 console.log(result);24});25var wptools = require('wptools');26wptools.page('Barack Obama').then(function(page) {27 return page.additions();28}).then(function(result) {29 console.log(result);30});31var wptools = require('wptools');32wptools.page('Barack Obama').then(function(page) {33 return page.additions();34}).then(function(result) {35 console.log(result);36});37var wptools = require('wptools');38wptools.page('Barack Obama').then(function(page) {39 return page.additions();40}).then(function(result) {41 console.log(result);42});43var wptools = require('wptools');44wptools.page('Barack Obama').then(function(page) {45 return page.additions();46}).then(function(result) {47 console.log(result);48});49var wptools = require('w

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('./wpt.js');2wpt.additions(1, 2, function(err, result) {3if (err) {4console.log(err);5} else {6console.log(result);7}8});9var wpt = require('./wpt.js');10wpt.additions(1, 2, function(err, result) {11if (err) {12console.log(err);13} else {14console.log(result);15}16});17var wpt = require('./wpt.js');18wpt.additions(1, 2, function(err, result) {19if (err) {20console.log(err);21} else {22console.log(result);23}24});25var wpt = require('./wpt.js');26wpt.additions(1, 2, function(err, result) {27if (err) {28console.log(err);29} else {30console.log(result);31}32});33var wpt = require('./wpt.js');34wpt.additions(1, 2, function(err, result) {35if (err) {36console.log(err);37} else {38console.log(result);39}40});41var wpt = require('./wpt.js');42wpt.additions(1, 2, function(err, result) {43if (err) {44console.log(err);45} else {46console.log(result);47}48});49var wpt = require('./wpt.js');50wpt.additions(1, 2, function(err, result) {51if (err) {52console.log(err);53} else {54console.log(result);55}56});57var wpt = require('./wpt.js');58wpt.additions(1, 2, function(err, result) {59if (err) {60console.log(err);61} else {62console.log(result);63}64});65var wpt = require('./wpt.js');66wpt.additions(1, 2, function(err, result) {67if (err) {68console.log(err);69} else {70console.log(result);71}72});

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptools = require('wptools');2var fs = require('fs');3var path = require('path');4additions.get(function(err, resp) {5 if (err) {6 console.log(err);7 } else {8 console.log(resp);9 fs.writeFile(path.join(__dirname, 'additions.json'), JSON.stringify(resp), function(err) {10 if (err) {11 console.log(err);12 } else {13 console.log("File saved!");14 }15 });16 }17});

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('wpt');2var wpt = new WebPageTest('www.webpagetest.org', 'A.5f5b5a5e1e8b2f2c2a5a5a5a5a5a5a5a');3 if (err) return console.error(err);4 console.log(data);5});6var wpt = require('wpt');7var wpt = new WebPageTest('www.webpagetest.org', 'A.5f5b5a5e1e8b2f2c2a5a5a5a5a5a5a5a');8 if (err) return console.error(err);9 console.log(data);10});11var wpt = require('wpt');12var wpt = new WebPageTest('www.webpagetest.org', 'A.5f5b5a5e1e8b2f2c2a5a5a5a5a5a5a5a');13 if (err) return console.error(err);14 console.log(data);15});16var wpt = require('wpt');17var wpt = new WebPageTest('www.webpagetest.org', 'A.5f5b5a5e1e8b2f2c2a5a5a5a5a5a5a5a');18 if (err) return console.error(err);19 console.log(data);20});21var wpt = require('wpt');22var wpt = new WebPageTest('www.webpagetest.org', 'A.5f5b5a5e1e8b2f2c2a5a5a5a5a5a5a

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run wpt automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful