2023-01-23 11:03:31 +01:00

351 lines
10 KiB
JavaScript

/* exported ampCustomizeControls */
/* eslint no-magic-numbers: [ "error", { "ignore": [ 0, 1, 250] } ] */
var ampCustomizeControls = ( function( api, $ ) { // eslint-disable-line no-unused-vars
'use strict';
var component = {
data: {
queryVar: 'amp',
panelId: '',
ampUrl: '',
l10n: {
unavailableMessage: '',
unavailableLinkText: ''
}
},
tooltipTimeout: 5000,
tooltipVisible: new api.Value( false ),
tooltipFocused: new api.Value( 0 )
};
/**
* Boot using data sent inline.
*
* @param {Object} data Object data.
* @return {void}
*/
component.boot = function boot( data ) {
component.data = data;
function initPanel() {
api.panel( component.data.panelId, component.panelReady );
}
if ( api.state ) {
component.addState();
api.bind( 'ready', initPanel );
} else { // WP<4.9.
api.bind( 'ready', function() {
component.addState(); // Needed for WP<4.9.
initPanel();
} );
}
};
/**
* Add state for AMP.
*
* @return {void}
*/
component.addState = function addState() {
api.state.add( 'ampEnabled', new api.Value( false ) );
api.state.add( 'ampAvailable', new api.Value( false ) );
};
/**
* Check if the URL is AMPified.
*
* @param {string} url URL.
* @return {boolean} whether it is an AMP URL.
*/
component.isAmpUrl = function isAmpUrl( url ) {
var urlParser = document.createElement( 'a' ),
regexEndpoint = new RegExp( '\\/' + component.data.queryVar + '\\/?$' );
urlParser.href = url;
if ( ! _.isUndefined( wp.customize.utils.parseQueryString( urlParser.search.substr( 1 ) )[ component.data.queryVar ] ) ) {
return true;
}
return regexEndpoint.test( urlParser.pathname );
};
/**
* Create an non-AMP version of a URL.
*
* @param {string} url URL.
* @return {string} non-AMPified URL.
*/
component.unampifyUrl = function unampifyUrl( url ) {
var urlParser = document.createElement( 'a' ),
regexEndpoint = new RegExp( '\\/' + component.data.queryVar + '\\/?$' ),
params;
urlParser.href = url;
urlParser.pathname = urlParser.pathname.replace( regexEndpoint, '' );
if ( 1 < urlParser.search.length ) {
params = wp.customize.utils.parseQueryString( urlParser.search.substr( 1 ) );
delete params[ component.data.queryVar ];
urlParser.search = $.param( params );
}
return urlParser.href;
};
/**
* Create an AMP version of a URL.
*
* @param {string} url URL.
* @return {string} AMPified URL.
*/
component.ampifyUrl = function ampifyUrl( url ) {
var urlParser = document.createElement( 'a' );
urlParser.href = component.unampifyUrl( url );
if ( urlParser.search.length ) {
urlParser.search += '&';
}
urlParser.search += component.data.queryVar + '=1';
return urlParser.href;
};
/**
* Try to close the tooltip after a given timeout.
*
* @return {void}
*/
component.tryToCloseTooltip = function tryToCloseTooltip() {
clearTimeout( component.tooltipTimeoutId );
component.tooltipTimeoutId = setTimeout( function() {
if ( ! component.tooltipVisible.get() ) {
return;
}
if ( 0 < component.tooltipFocused.get() ) {
component.tryToCloseTooltip();
} else {
component.tooltipVisible.set( false );
}
}, component.tooltipTimeout );
};
/**
* Make current URL AMPified if toggle is on.
*
* @param {string} url URL.
* @return {string} AMPified URL.
*/
component.setCurrentAmpUrl = function setCurrentAmpUrl( url ) {
var enabled = api.state( 'ampEnabled' ).get();
if ( ! enabled && component.isAmpUrl( url ) ) {
return component.unampifyUrl( url );
} else if ( enabled && ! component.isAmpUrl( url ) ) {
return component.ampifyUrl( url );
}
return url;
};
/**
* Swap to AMP version of URL in preview.
*
* @return {void}
*/
component.updatePreviewUrl = function updatePreviewUrl() {
api.previewer.previewUrl.set( component.setCurrentAmpUrl( api.previewer.previewUrl.get() ) );
};
/**
* Enable AMP and navigate to the given URL.
*
* @param {string} url - URL.
* @return {void}
*/
component.enableAndNavigateToUrl = function enableAndNavigateToUrl( url ) {
api.state( 'ampEnabled' ).set( true );
api.previewer.previewUrl.set( url );
};
/**
* Update panel notifications.
*
* @return {void}
*/
component.updatePanelNotifications = function updatePanelNotifications() {
var panel = api.panel( component.data.panelId ),
containers;
containers = panel.sections().concat( [ panel ] );
if ( api.state( 'ampAvailable' ).get() ) {
_.each( containers, function( container ) {
container.notifications.remove( 'amp_unavailable' );
} );
} else {
_.each( containers, function( container ) {
container.notifications.add( new api.Notification( 'amp_unavailable', {
message: component.data.l10n.unavailableMessage,
type: 'info',
linkText: component.data.l10n.unavailableLinkText,
url: component.data.ampUrl,
templateId: 'customize-amp-unavailable-notification',
render: function() {
var li = api.Notification.prototype.render.call( this );
li.find( 'a' ).on( 'click', function( event ) {
event.preventDefault();
component.enableAndNavigateToUrl( this.href );
} );
return li;
}
} ) );
} );
}
};
/**
* Hook up all AMP preview interactions once panel is ready.
*
* @param {wp.customize.Panel} panel The AMP panel.
* @return {void}
*/
component.panelReady = function panelReady( panel ) {
var ampToggleContainer, checkbox, tooltip, tooltipLink;
ampToggleContainer = $( wp.template( 'customize-amp-enabled-toggle' )( {
message: component.data.l10n.unavailableMessage,
linkText: component.data.l10n.unavailableLinkText,
url: component.data.ampUrl
} ) );
checkbox = ampToggleContainer.find( 'input[type=checkbox]' );
tooltip = ampToggleContainer.find( '.tooltip' );
tooltipLink = tooltip.find( 'a' );
// AMP panel triggers the input toggle for AMP preview.
panel.expanded.bind( function( expanded ) {
if ( ! expanded ) {
return;
}
if ( api.state( 'ampAvailable' ).get() ) {
api.state( 'ampEnabled' ).set( panel.expanded.get() );
} else if ( ! panel.notifications ) {
/*
* This is only done if panel notifications aren't supported.
* If they are (as of 4.9) then a notification will be shown
* in the panel and its sections when AMP is not available.
*/
setTimeout( function() {
component.tooltipVisible.set( true );
}, 250 );
}
} );
if ( panel.notifications ) {
api.state( 'ampAvailable' ).bind( component.updatePanelNotifications );
component.updatePanelNotifications();
api.section.bind( 'add', component.updatePanelNotifications );
}
// Enable AMP toggle if available and mobile device selected.
api.previewedDevice.bind( function( device ) {
if ( api.state( 'ampAvailable' ).get() ) {
api.state( 'ampEnabled' ).set( 'mobile' === device );
}
} );
// Message coming from previewer.
api.previewer.bind( 'amp-status', function( data ) {
api.state( 'ampAvailable' ).set( data.available );
} );
function setInitialAmpEnabledState( data ) {
api.state( 'ampEnabled' ).set( data.enabled );
api.previewer.unbind( 'amp-status', setInitialAmpEnabledState );
}
api.previewer.bind( 'amp-status', setInitialAmpEnabledState );
/*
* Persist the presence or lack of the amp=1 param when navigating in the preview,
* even if current page is not yet supported.
*/
api.previewer.previewUrl.validate = ( function( prevValidate ) {
return function( value ) {
var val = prevValidate.call( this, value );
if ( val ) {
val = component.setCurrentAmpUrl( val );
}
return val;
};
}( api.previewer.previewUrl.validate ) );
// Listen for ampEnabled state changes.
api.state( 'ampEnabled' ).bind( function( enabled ) {
checkbox.prop( 'checked', enabled );
component.updatePreviewUrl();
} );
// Listen for ampAvailable state changes.
api.state( 'ampAvailable' ).bind( function( available ) {
checkbox.toggleClass( 'disabled', ! available );
// Show the unavailable tooltip if AMP is enabled.
if ( api.state( 'ampEnabled' ).get() ) {
component.tooltipVisible.set( ! available );
}
} );
// Adding checkbox toggle before device selection.
$( '.devices-wrapper' ).before( ampToggleContainer );
// User clicked link within tooltip, go to linked post in preview.
tooltipLink.on( 'click', function( event ) {
event.preventDefault();
component.enableAndNavigateToUrl( this.href );
} );
// Toggle visibility of tooltip based on tooltipVisible state.
component.tooltipVisible.bind( function( visible ) {
tooltip.attr( 'aria-hidden', visible ? 'false' : 'true' );
if ( visible ) {
$( document ).on( 'click.amp-toggle-outside', function( event ) {
if ( ! $.contains( ampToggleContainer[ 0 ], event.target ) ) {
component.tooltipVisible.set( false );
}
} );
tooltip.fadeIn();
component.tryToCloseTooltip();
} else {
tooltip.fadeOut();
component.tooltipFocused.set( 0 );
$( document ).off( 'click.amp-toggle-outside' );
}
} );
// Handle click on checkbox to either enable the AMP preview or show the tooltip.
checkbox.on( 'click', function() {
this.checked = ! this.checked; // Undo what we just did, since state is managed in ampAvailable change handler.
if ( api.state( 'ampAvailable' ).get() ) {
api.state( 'ampEnabled' ).set( ! api.state( 'ampEnabled' ).get() );
} else {
component.tooltipVisible.set( true );
}
} );
// Keep track of the user's state interacting with the tooltip.
tooltip.on( 'mouseenter', function() {
if ( ! api.state( 'ampAvailable' ).get() ) {
component.tooltipVisible.set( true );
}
component.tooltipFocused.set( component.tooltipFocused.get() + 1 );
} );
tooltip.on( 'mouseleave', function() {
component.tooltipFocused.set( component.tooltipFocused.get() - 1 );
} );
tooltipLink.on( 'focus', function() {
if ( ! api.state( 'ampAvailable' ).get() ) {
component.tooltipVisible.set( true );
}
component.tooltipFocused.set( component.tooltipFocused.get() + 1 );
} );
tooltipLink.on( 'blur', function() {
component.tooltipFocused.set( component.tooltipFocused.get() - 1 );
} );
};
return component;
}( wp.customize, jQuery ) );