351 lines
10 KiB
JavaScript
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 ) );
|