PDF rausgenommen

This commit is contained in:
aschwarz
2023-01-23 11:03:31 +01:00
parent 82d562a322
commit a6523903eb
28078 changed files with 4247552 additions and 2 deletions

View File

@ -0,0 +1,37 @@
/**
* Adds an admin pointer that describes new features in 1.0.
*/
/* exported ampAdminPointer */
/* global ajaxurl, jQuery */
var ampAdminPointer = ( function( $ ) { // eslint-disable-line no-unused-vars
'use strict';
return {
/**
* Loads the pointer.
*
* @param {Object} data - Module data.
* @return {void}
*/
load: function load( data ) {
var options = $.extend(
data.pointer.options,
{
/**
* Makes a POST request to store the pointer ID as dismissed for this user.
*/
close: function() {
$.post( ajaxurl, {
pointer: data.pointer.pointer_id,
action: 'dismiss-wp-pointer'
} );
}
}
);
$( data.pointer.target ).pointer( options ).pointer( 'open' );
}
};
}( jQuery ) );

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,534 @@
/**
* Validates blocks for AMP compatibility.
*
* This uses the REST API response from saving a page to find validation errors.
* If one exists for a block, it display it inline with a Notice component.
*/
/* exported ampBlockValidation */
/* global wp, _ */
var ampBlockValidation = ( function() { // eslint-disable-line no-unused-vars
'use strict';
var module = {
/**
* Data exported from server.
*
* @param {Object}
*/
data: {
i18n: {},
ampValidityRestField: '',
isSanitizationAutoAccepted: false
},
/**
* Name of the store.
*
* @param {string}
*/
storeName: 'amp/blockValidation',
/**
* Holds the last states which are used for comparisons.
*
* @param {Object}
*/
lastStates: {
noticesAreReset: false,
validationErrors: [],
blockOrder: [],
blockValidationErrors: {}
},
/**
* Boot module.
*
* @param {Object} data - Module data.
* @return {void}
*/
boot: function boot( data ) {
module.data = data;
wp.i18n.setLocaleData( module.data.i18n, 'amp' );
wp.hooks.addFilter(
'editor.BlockEdit',
'amp/add-notice',
module.conditionallyAddNotice,
99 // eslint-disable-line
);
module.store = module.registerStore();
wp.data.subscribe( module.handleValidationErrorsStateChange );
},
/**
* Register store.
*
* @return {Object} Store.
*/
registerStore: function registerStore() {
return wp.data.registerStore( module.storeName, {
reducer: function( _state, action ) {
var state = _state || {
blockValidationErrorsByClientId: {}
};
switch ( action.type ) {
case 'UPDATE_BLOCKS_VALIDATION_ERRORS':
return _.extend( {}, state, {
blockValidationErrorsByClientId: action.blockValidationErrorsByClientId
} );
default:
return state;
}
},
actions: {
updateBlocksValidationErrors: function( blockValidationErrorsByClientId ) {
return {
type: 'UPDATE_BLOCKS_VALIDATION_ERRORS',
blockValidationErrorsByClientId: blockValidationErrorsByClientId
};
}
},
selectors: {
getBlockValidationErrors: function( state, clientId ) {
return state.blockValidationErrorsByClientId[ clientId ] || [];
}
}
} );
},
/**
* Checks if AMP is enabled for this post.
*
* @return {boolean} Returns true when the AMP toggle is on; else, false is returned.
*/
isAMPEnabled: function isAMPEnabled() {
var meta = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'meta' );
if ( meta && meta.amp_status && window.wpAmpEditor.possibleStati.includes( meta.amp_status ) ) {
return 'enabled' === meta.amp_status;
}
return window.wpAmpEditor.defaultStatus;
},
/**
* Checks if the validate errors state change handler should wait before processing.
*
* @return {boolean} Whether should wait.
*/
waitToHandleStateChange: function waitToHandleStateChange() {
var currentPost;
// @todo Gutenberg currently is not persisting isDirty state if changes are made during save request. Block order mismatch.
// We can only align block validation errors with blocks in editor when in saved state, since only here will the blocks be aligned with the validation errors.
if ( wp.data.select( 'core/editor' ).isEditedPostDirty() || ( ! wp.data.select( 'core/editor' ).isEditedPostDirty() && wp.data.select( 'core/editor' ).isEditedPostNew() ) ) {
return true;
}
// Wait for the current post to be set up.
currentPost = wp.data.select( 'core/editor' ).getCurrentPost();
if ( ! currentPost.hasOwnProperty( 'id' ) ) {
return true;
}
return false;
},
/**
* Handle state change regarding validation errors.
*
* This is essentially a JS implementation of \AMP_Validation_Manager::print_edit_form_validation_status() in PHP.
*
* @return {void}
*/
handleValidationErrorsStateChange: function handleValidationErrorsStateChange() {
var currentPost, validationErrors, blockValidationErrors, noticeOptions, noticeMessage, blockErrorCount, ampValidity, rejectedErrors;
if ( ! module.isAMPEnabled() ) {
if ( ! module.lastStates.noticesAreReset ) {
module.lastStates.validationErrors = [];
module.lastStates.noticesAreReset = true;
module.resetWarningNotice();
module.resetBlockNotices();
}
return;
}
if ( module.waitToHandleStateChange() ) {
return;
}
currentPost = wp.data.select( 'core/editor' ).getCurrentPost();
ampValidity = currentPost[ module.data.ampValidityRestField ] || {};
// Show all validation errors which have not been explicitly acknowledged as accepted.
validationErrors = _.map(
_.filter( ampValidity.results, function( result ) {
// @todo Show VALIDATION_ERROR_ACK_REJECTED_STATUS differently since moderated?
return (
0 /* \AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_NEW_REJECTED_STATUS */ === result.status ||
1 /* \AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_NEW_ACCEPTED_STATUS */ === result.status ||
2 /* \AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_ACK_REJECTED_STATUS */ === result.status // eslint-disable-line no-magic-numbers
);
} ),
function( result ) {
return result.error;
}
);
// Short-circuit if there was no change to the validation errors.
if ( ! module.didValidationErrorsChange( validationErrors ) ) {
if ( ! validationErrors.length && ! module.lastStates.noticesAreReset ) {
module.lastStates.noticesAreReset = true;
module.resetWarningNotice();
}
return;
}
module.lastStates.validationErrors = validationErrors;
module.lastStates.noticesAreReset = false;
// Remove any existing notice.
module.resetWarningNotice();
noticeMessage = wp.i18n.sprintf(
/* translators: %s: number of issues */
wp.i18n._n(
'There is %s issue from AMP validation which needs review.',
'There are %s issues from AMP validation which need review.',
validationErrors.length,
'amp'
),
validationErrors.length
);
try {
blockValidationErrors = module.getBlocksValidationErrors();
module.lastStates.blockValidationErrors = blockValidationErrors.byClientId;
wp.data.dispatch( module.storeName ).updateBlocksValidationErrors( blockValidationErrors.byClientId );
blockErrorCount = validationErrors.length - blockValidationErrors.other.length;
if ( blockErrorCount > 0 ) {
noticeMessage += ' ' + wp.i18n.sprintf(
/* translators: %s: number of block errors. */
wp.i18n._n(
'%s issue is directly due to content here.',
'%s issues are directly due to content here.',
blockErrorCount,
'amp'
),
blockErrorCount
);
} else if ( validationErrors.length === 1 ) {
noticeMessage += ' ' + wp.i18n.__( 'The issue is not directly due to content here.', 'amp' );
} else {
noticeMessage += ' ' + wp.i18n.__( 'The issues are not directly due to content here.', 'amp' );
}
} catch ( e ) {
// Clear out block validation errors in case the block sand errors cannot be aligned.
module.resetBlockNotices();
if ( validationErrors.length === 1 ) {
noticeMessage += ' ' + wp.i18n.__( 'The issue may not be due to content here', 'amp' );
} else {
noticeMessage += ' ' + wp.i18n.__( 'Some issues may be due to content here.', 'amp' );
}
}
rejectedErrors = _.filter( ampValidity.results, function( result ) {
return (
0 /* \AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_NEW_REJECTED_STATUS */ === result.status ||
2 /* \AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_ACK_REJECTED_STATUS */ === result.status // eslint-disable-line no-magic-numbers
);
} );
noticeMessage += ' ';
// Auto-acceptance is from either checking 'Automatically accept sanitization...' or from being in Native mode.
if ( module.data.isSanitizationAutoAccepted ) {
if ( 0 === rejectedErrors.length ) {
noticeMessage += wp.i18n.__( 'However, your site is configured to automatically accept sanitization of the offending markup.', 'amp' );
} else {
noticeMessage += wp.i18n._n(
'Your site is configured to automatically accept sanitization errors, but this error could be from when auto-acceptance was not selected, or from manually rejecting an error.',
'Your site is configured to automatically accept sanitization errors, but these errors could be from when auto-acceptance was not selected, or from manually rejecting an error.',
validationErrors.length,
'amp'
);
}
} else {
noticeMessage += wp.i18n.__( 'Non-accepted validation errors prevent AMP from being served, and the user will be redirected to the non-AMP version.', 'amp' );
}
noticeOptions = {
id: 'amp-errors-notice'
};
if ( ampValidity.review_link ) {
noticeOptions.actions = [
{
label: wp.i18n.__( 'Review issues', 'amp' ),
url: ampValidity.review_link
}
];
}
// Display notice if there were validation errors.
if ( validationErrors.length > 0 ) {
wp.data.dispatch( 'core/notices' ).createNotice( 'warning', noticeMessage, noticeOptions );
}
module.validationWarningNoticeId = noticeOptions.id;
},
/**
* Checks if the validation errors have changed.
*
* @param {Object[]} validationErrors A list of validation errors.
* @return {boolean|*} Returns true when the validation errors change.
*/
didValidationErrorsChange: function didValidationErrorsChange( validationErrors ) {
if ( module.areBlocksOutOfSync() ) {
module.lastStates.validationErrors = [];
}
return (
module.lastStates.validationErrors.length !== validationErrors.length ||
( validationErrors && ! _.isEqual( module.lastStates.validationErrors, validationErrors ) )
);
},
/**
* Checks if the block order is out of sync.
*
* Block change on page load and can get out of sync during normal editing and saving processes. This method gives a check to determine if an "out of sync" condition occurred.
*
* @return {boolean} Whether out of sync.
*/
areBlocksOutOfSync: function areBlocksOutOfSync() {
var blockOrder = wp.data.select( 'core/editor' ).getBlockOrder();
if ( module.lastStates.blockOrder.length !== blockOrder.length || ! _.isEqual( module.lastStates.blockOrder, blockOrder ) ) {
module.lastStates.blockOrder = blockOrder;
return true;
}
return false;
},
/**
* Resets the validation warning notice.
*
* @return {void}
*/
resetWarningNotice: function resetWarningNotice() {
if ( module.validationWarningNoticeId ) {
wp.data.dispatch( 'core/notices' ).removeNotice( module.validationWarningNoticeId );
module.validationWarningNoticeId = null;
}
},
/**
* Resets the block level validation errors.
*
* @return {void}
*/
resetBlockNotices: function resetBlockNotices() {
wp.data.dispatch( module.storeName ).updateBlocksValidationErrors( {} );
},
/**
* Get flattened block order.
*
* @param {Object[]} blocks - List of blocks which maty have nested blocks inside them.
* @return {string[]} Block IDs in flattened order.
*/
getFlattenedBlockOrder: function getFlattenedBlockOrder( blocks ) {
var blockOrder = [];
_.each( blocks, function( block ) {
blockOrder.push( block.clientId );
if ( block.innerBlocks.length > 0 ) {
Array.prototype.push.apply( blockOrder, module.getFlattenedBlockOrder( block.innerBlocks ) );
}
} );
return blockOrder;
},
/**
* Update blocks' validation errors in the store.
*
* @return {Object} Validation errors grouped by block ID other ones.
*/
getBlocksValidationErrors: function getBlocksValidationErrors() {
var acceptedStatus, blockValidationErrorsByClientId, editorSelect, currentPost, blockOrder, validationErrors, otherValidationErrors;
acceptedStatus = 3; // eslint-disable-line no-magic-numbers
editorSelect = wp.data.select( 'core/editor' );
currentPost = editorSelect.getCurrentPost();
validationErrors = _.map(
_.filter( currentPost[ module.data.ampValidityRestField ].results, function( result ) {
return result.term_status !== acceptedStatus; // If not accepted by the user.
} ),
function( result ) {
return result.error;
}
);
blockOrder = module.getFlattenedBlockOrder( editorSelect.getBlocks() );
otherValidationErrors = [];
blockValidationErrorsByClientId = {};
_.each( blockOrder, function( clientId ) {
blockValidationErrorsByClientId[ clientId ] = [];
} );
_.each( validationErrors, function( validationError ) {
var i, source, clientId, block, matched;
if ( ! validationError.sources ) {
otherValidationErrors.push( validationError );
return;
}
// Find the inner-most nested block source only; ignore any nested blocks.
matched = false;
for ( i = validationError.sources.length - 1; 0 <= i; i-- ) {
source = validationError.sources[ i ];
// Skip sources that are not for blocks.
if ( ! source.block_name || _.isUndefined( source.block_content_index ) || currentPost.id !== source.post_id ) {
continue;
}
// Look up the block ID by index, assuming the blocks of content in the editor are the same as blocks rendered on frontend.
clientId = blockOrder[ source.block_content_index ];
if ( _.isUndefined( clientId ) ) {
throw new Error( 'undefined_block_index' );
}
// Sanity check that block exists for clientId.
block = editorSelect.getBlock( clientId );
if ( ! block ) {
throw new Error( 'block_lookup_failure' );
}
// Check the block type in case a block is dynamically added/removed via the_content filter to cause alignment error.
if ( block.name !== source.block_name ) {
throw new Error( 'ordered_block_alignment_mismatch' );
}
blockValidationErrorsByClientId[ clientId ].push( validationError );
matched = true;
// Stop looking for sources, since we aren't looking for parent blocks.
break;
}
if ( ! matched ) {
otherValidationErrors.push( validationError );
}
} );
return {
byClientId: blockValidationErrorsByClientId,
other: otherValidationErrors
};
},
/**
* Get message for validation error.
*
* @param {Object} validationError - Validation error.
* @param {string} validationError.code - Validation error code.
* @param {string} [validationError.node_name] - Node name.
* @param {string} [validationError.message] - Validation error message.
* @return {wp.element.Component[]|string[]} Validation error message.
*/
getValidationErrorMessage: function getValidationErrorMessage( validationError ) {
if ( validationError.message ) {
return validationError.message;
}
if ( 'invalid_element' === validationError.code && validationError.node_name ) {
return [
wp.i18n.__( 'Invalid element: ' ),
wp.element.createElement( 'code', { key: 'name' }, validationError.node_name )
];
} else if ( 'invalid_attribute' === validationError.code && validationError.node_name ) {
return [
wp.i18n.__( 'Invalid attribute: ' ),
wp.element.createElement( 'code', { key: 'name' }, validationError.parent_name ? wp.i18n.sprintf( '%s[%s]', validationError.parent_name, validationError.node_name ) : validationError.node_name )
];
}
return [
wp.i18n.__( 'Error code: ', 'amp' ),
wp.element.createElement( 'code', { key: 'name' }, validationError.code || wp.i18n.__( 'unknown' ) )
];
},
/**
* Wraps the edit() method of a block, and conditionally adds a Notice.
*
* @param {Function} BlockEdit - The original edit() method of the block.
* @return {Function} The edit() method, conditionally wrapped in a notice for AMP validation error(s).
*/
conditionallyAddNotice: function conditionallyAddNotice( BlockEdit ) {
return function( ownProps ) {
var validationErrors,
mergedProps;
function AmpNoticeBlockEdit( props ) {
var edit, details;
edit = wp.element.createElement(
BlockEdit,
props
);
if ( 0 === props.ampBlockValidationErrors.length ) {
return edit;
}
details = wp.element.createElement( 'details', { className: 'amp-block-validation-errors' }, [
wp.element.createElement( 'summary', { key: 'summary', className: 'amp-block-validation-errors__summary' }, wp.i18n.sprintf(
wp.i18n._n(
'There is %s issue from AMP validation.',
'There are %s issues from AMP validation.',
props.ampBlockValidationErrors.length,
'amp'
),
props.ampBlockValidationErrors.length
) ),
wp.element.createElement(
'ul',
{ key: 'list', className: 'amp-block-validation-errors__list' },
_.map( props.ampBlockValidationErrors, function( error, key ) {
return wp.element.createElement( 'li', { key: key }, module.getValidationErrorMessage( error ) );
} )
)
] );
return wp.element.createElement(
wp.element.Fragment, {},
wp.element.createElement(
wp.components.Notice,
{
status: 'warning',
isDismissible: false
},
details
),
edit
);
}
if ( ! module.lastStates.blockValidationErrors[ ownProps.clientId ] ) {
validationErrors = wp.data.select( module.storeName ).getBlockValidationErrors( ownProps.clientId );
module.lastStates.blockValidationErrors[ ownProps.clientId ] = validationErrors;
}
mergedProps = _.extend( {}, ownProps, {
ampBlockValidationErrors: module.lastStates.blockValidationErrors[ ownProps.clientId ]
} );
return AmpNoticeBlockEdit( mergedProps );
};
}
};
return module;
}() );

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,350 @@
/* 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 ) );

View File

@ -0,0 +1,25 @@
/* exported ampCustomizePreview */
var ampCustomizePreview = ( function( api ) { // eslint-disable-line no-unused-vars
'use strict';
var component = {};
/**
* Boot using data sent inline.
*
* @param {Object} data - PHP exports.
* @param {boolean} data.available - Whether AMP is available.
* @param {boolean} data.enabled - Whether AMP is enabled.
* @return {void}
*/
component.boot = function boot( data ) {
api.bind( 'preview-ready', function() {
api.preview.bind( 'active', function() {
api.preview.send( 'amp-status', data );
} );
} );
};
return component;
}( wp.customize ) );

View File

@ -0,0 +1,48 @@
/* global amp_customizer_design, console */
( function( $ ) {
'use strict';
// Nav bar text color.
wp.customize( 'amp_customizer[header_color]', function( value ) {
value.bind( function( to ) {
$( '.amp-wp-header a' ).css( 'color', to );
$( '.amp-wp-header div' ).css( 'color', to );
$( '.amp-wp-header .amp-wp-site-icon' ).css( 'border-color', to ).css( 'background-color', to );
} );
} );
// Nav bar background color.
wp.customize( 'amp_customizer[header_background_color]', function( value ) {
value.bind( function( to ) {
$( 'html, .amp-wp-header' ).css( 'background-color', to );
$( '.amp-wp-article a, .amp-wp-article a:visited, .amp-wp-footer a, .amp-wp-footer a:visited' ).css( 'color', to );
$( 'blockquote, .amp-wp-byline amp-img' ).css( 'border-color', to );
} );
} );
// AMP background color scheme.
wp.customize( 'amp_customizer[color_scheme]', function( value ) {
value.bind( function( to ) {
var colors = amp_customizer_design.color_schemes[ to ]; // eslint-disable-line
if ( ! colors ) {
console.error( 'Selected color scheme "%s" not registered.', to ); // eslint-disable-line
return;
}
$( 'body' ).css( 'background-color', colors.theme_color );
$( 'body, a:hover, a:active, a:focus, blockquote, .amp-wp-article, .amp-wp-title' ).css( 'color', colors.text_color );
$( '.amp-wp-meta, .wp-caption .wp-caption-text, .amp-wp-tax-category, .amp-wp-tax-tag, .amp-wp-footer p' ).css( 'color', colors.muted_text_color );
$( '.wp-caption .wp-caption-text, .amp-wp-comments-link a, .amp-wp-footer' ).css( 'border-color', colors.border_color );
$( '.amp-wp-iframe-placeholder, amp-carousel, amp-iframe, amp-youtube, amp-instagram, amp-vine' ).css( 'background-color', colors.border_color );
} );
} );
// Site title.
wp.customize( 'blogname', function( setting ) {
setting.bind( function( title ) {
$( '.amp-wp-header .amp-site-title, .amp-wp-footer h2' ).text( title );
} );
} );
}( jQuery ) );

View File

@ -0,0 +1,847 @@
/* exported ampEditorBlocks */
/* eslint no-magic-numbers: [ "error", { "ignore": [ 1, -1, 0, 4 ] } ] */
var ampEditorBlocks = ( function() { // eslint-disable-line no-unused-vars
var component, __;
__ = wp.i18n.__;
component = {
/**
* Holds data.
*/
data: {
ampLayoutOptions: [
{
value: 'nodisplay',
label: __( 'No Display', 'amp' ),
notAvailable: [
'core-embed/vimeo',
'core-embed/dailymotion',
'core-embed/hulu',
'core-embed/reddit',
'core-embed/soundcloud'
]
},
{
// Not supported by amp-audio and amp-pixel.
value: 'fixed',
label: __( 'Fixed', 'amp' ),
notAvailable: [
'core-embed/soundcloud'
]
},
{
// To ensure your AMP element displays, you must specify a width and height for the containing element.
value: 'responsive',
label: __( 'Responsive', 'amp' ),
notAvailable: [
'core-embed/soundcloud'
]
},
{
value: 'fixed-height',
label: __( 'Fixed height', 'amp' ),
notAvailable: []
},
{
value: 'fill',
label: __( 'Fill', 'amp' ),
notAvailable: [
'core-embed/soundcloud'
]
},
{
value: 'flex-item',
label: __( 'Flex Item', 'amp' ),
notAvailable: [
'core-embed/soundcloud'
]
},
{
// Not supported by video.
value: 'intrinsic',
label: __( 'Intrinsic', 'amp' ),
notAvailable: [
'core-embed/youtube',
'core-embed/facebook',
'core-embed/instagram',
'core-embed/vimeo',
'core-embed/dailymotion',
'core-embed/hulu',
'core-embed/reddit',
'core-embed/soundcloud'
]
}
],
defaultWidth: 608, // Max-width in the editor.
defaultHeight: 400,
mediaBlocks: [
'core/image',
'core/video'
],
textBlocks: [
'core/paragraph',
'core/heading',
'core/code',
'core/quote',
'core/subhead'
],
ampSettingsLabel: __( 'AMP Settings' ),
fontSizes: {
small: 14,
larger: 48
},
ampPanelLabel: __( 'AMP Settings' )
},
hasThemeSupport: true
};
/**
* Add filters.
*
* @param {Object} data Data.
*/
component.boot = function boot( data ) {
if ( data ) {
_.extend( component.data, data );
}
wp.hooks.addFilter( 'blocks.registerBlockType', 'ampEditorBlocks/addAttributes', component.addAMPAttributes );
wp.hooks.addFilter( 'blocks.getSaveElement', 'ampEditorBlocks/filterSave', component.filterBlocksSave );
wp.hooks.addFilter( 'editor.BlockEdit', 'ampEditorBlocks/filterEdit', component.filterBlocksEdit );
wp.hooks.addFilter( 'blocks.getSaveContent.extraProps', 'ampEditorBlocks/addExtraAttributes', component.addAMPExtraProps );
};
/**
* Check if layout is available for the block.
*
* @param {string} blockName Block name.
* @param {Object} option Layout option object.
* @return {boolean} If is available.
*/
component.isLayoutAvailable = function isLayoutAvailable( blockName, option ) {
return -1 === option.notAvailable.indexOf( blockName );
};
/**
* Get layout options depending on the block.
*
* @param {string} blockName Block name.
* @return {[*]} Options.
*/
component.getLayoutOptions = function getLayoutOptions( blockName ) {
var layoutOptions = [
{
value: '',
label: __( 'Default', 'amp' )
}
];
_.each( component.data.ampLayoutOptions, function( option ) {
if ( component.isLayoutAvailable( blockName, option ) ) {
layoutOptions.push( {
value: option.value,
label: option.label
} );
}
} );
return layoutOptions;
};
/**
* Add extra data-amp-layout attribute to save to DB.
*
* @param {Object} props Properties.
* @param {Object} blockType Block type.
* @param {Object} attributes Attributes.
* @return {Object} Props.
*/
component.addAMPExtraProps = function addAMPExtraProps( props, blockType, attributes ) {
var ampAttributes = {};
// Shortcode props are handled differently.
if ( 'core/shortcode' === blockType.name ) {
return props;
}
// AMP blocks handle layout and other props on their own.
if ( 'amp/' === blockType.name.substr( 0, 4 ) ) {
return props;
}
if ( attributes.ampLayout ) {
ampAttributes[ 'data-amp-layout' ] = attributes.ampLayout;
}
if ( attributes.ampNoLoading ) {
ampAttributes[ 'data-amp-noloading' ] = attributes.ampNoLoading;
}
if ( attributes.ampLightbox ) {
ampAttributes[ 'data-amp-lightbox' ] = attributes.ampLightbox;
}
if ( attributes.ampCarousel ) {
ampAttributes[ 'data-amp-carousel' ] = attributes.ampCarousel;
}
return _.extend( ampAttributes, props );
};
/**
* Add AMP attributes (in this test case just ampLayout) to every core block.
*
* @param {Object} settings Settings.
* @param {string} name Block name.
* @return {Object} Settings.
*/
component.addAMPAttributes = function addAMPAttributes( settings, name ) {
// AMP Carousel settings.
if ( 'core/shortcode' === name || 'core/gallery' === name ) {
if ( ! settings.attributes ) {
settings.attributes = {};
}
settings.attributes.ampCarousel = {
type: 'boolean'
};
settings.attributes.ampLightbox = {
type: 'boolean'
};
}
// Add AMP Lightbox settings.
if ( 'core/image' === name ) {
if ( ! settings.attributes ) {
settings.attributes = {};
}
settings.attributes.ampLightbox = {
type: 'boolean'
};
}
// Fit-text for text blocks.
if ( -1 !== component.data.textBlocks.indexOf( name ) ) {
if ( ! settings.attributes ) {
settings.attributes = {};
}
settings.attributes.ampFitText = {
default: false
};
settings.attributes.minFont = {
default: component.data.fontSizes.small,
source: 'attribute',
selector: 'amp-fit-text',
attribute: 'min-font-size'
};
settings.attributes.maxFont = {
default: component.data.fontSizes.larger,
source: 'attribute',
selector: 'amp-fit-text',
attribute: 'max-font-size'
};
settings.attributes.height = {
default: 50,
source: 'attribute',
selector: 'amp-fit-text',
attribute: 'height'
};
}
// Layout settings for embeds and media blocks.
if ( 0 === name.indexOf( 'core-embed' ) || -1 !== component.data.mediaBlocks.indexOf( name ) ) {
if ( ! settings.attributes ) {
settings.attributes = {};
}
settings.attributes.ampLayout = {
type: 'string'
};
settings.attributes.ampNoLoading = {
type: 'boolean'
};
}
return settings;
};
/**
* Filters blocks edit function of all blocks.
*
* @param {Function} BlockEdit Edit function.
* @return {Function} Edit function.
*/
component.filterBlocksEdit = function filterBlocksEdit( BlockEdit ) {
var el = wp.element.createElement;
return function( props ) {
var attributes = props.attributes,
name = props.name,
ampLayout,
inspectorControls;
ampLayout = attributes.ampLayout;
if ( 'core/shortcode' === name ) {
// Lets remove amp-carousel from edit view.
if ( component.hasGalleryShortcodeCarouselAttribute( attributes.text || '' ) ) {
props.setAttributes( { text: component.removeAmpCarouselFromShortcodeAtts( attributes.text ) } );
}
// Lets remove amp-lightbox from edit view.
if ( component.hasGalleryShortcodeLightboxAttribute( attributes.text || '' ) ) {
props.setAttributes( { text: component.removeAmpLightboxFromShortcodeAtts( attributes.text ) } );
}
inspectorControls = component.setUpShortcodeInspectorControls( props );
if ( '' === inspectorControls ) {
// Return original.
return [
el( BlockEdit, _.extend( {
key: 'original'
}, props ) )
];
}
} else if ( 'core/gallery' === name ) {
inspectorControls = component.setUpGalleryInpsectorControls( props );
} else if ( 'core/image' === name ) {
inspectorControls = component.setUpImageInpsectorControls( props );
} else if ( -1 !== component.data.mediaBlocks.indexOf( name ) || 0 === name.indexOf( 'core-embed/' ) ) {
inspectorControls = component.setUpInspectorControls( props );
} else if ( -1 !== component.data.textBlocks.indexOf( name ) ) {
inspectorControls = component.setUpTextBlocksInspectorControls( props );
}
// Return just inspector controls in case of 'nodisplay'.
if ( ampLayout && 'nodisplay' === ampLayout ) {
return [
inspectorControls
];
}
return [
el( BlockEdit, _.extend( {
key: 'original'
}, props ) ),
inspectorControls
];
};
};
/**
* Set width and height in case of image block.
*
* @param {Object} props Props.
* @param {string} layout Layout.
*/
component.setImageBlockLayoutAttributes = function setImageBlockLayoutAttributes( props, layout ) {
var attributes = props.attributes;
switch ( layout ) {
case 'fixed-height':
if ( ! attributes.height ) {
props.setAttributes( { height: component.data.defaultHeight } );
}
// Lightbox doesn't work with fixed height, so unset it.
if ( attributes.ampLightbox ) {
props.setAttributes( { ampLightbox: false } );
}
break;
case 'fixed':
if ( ! attributes.height ) {
props.setAttributes( { height: component.data.defaultHeight } );
}
if ( ! attributes.width ) {
props.setAttributes( { width: component.data.defaultWidth } );
}
break;
}
};
/**
* Default setup for inspector controls.
*
* @param {Object} props Props.
* @return {Object|Element|*|{$$typeof, type, key, ref, props, _owner}} Inspector Controls.
*/
component.setUpInspectorControls = function setUpInspectorControls( props ) {
var isSelected = props.isSelected,
el = wp.element.createElement,
InspectorControls = wp.editor.InspectorControls,
PanelBody = wp.components.PanelBody;
return isSelected && (
el( InspectorControls, { key: 'inspector' },
el( PanelBody, { title: component.data.ampPanelLabel },
component.getAmpLayoutControl( props ),
component.getAmpNoloadingToggle( props )
)
)
);
};
/**
* Get AMP Layout select control.
*
* @param {Object} props Props.
* @return {Object} Element.
*/
component.getAmpLayoutControl = function getAmpLayoutControl( props ) {
var ampLayout = props.attributes.ampLayout,
el = wp.element.createElement,
SelectControl = wp.components.SelectControl,
name = props.name,
label = __( 'AMP Layout' );
if ( 'core/image' === name ) {
label = __( 'AMP Layout (modifies width/height)' );
}
return el( SelectControl, {
label: label,
value: ampLayout,
options: component.getLayoutOptions( name ),
onChange: function( value ) {
props.setAttributes( { ampLayout: value } );
if ( 'core/image' === props.name ) {
component.setImageBlockLayoutAttributes( props, value );
}
}
} );
};
/**
* Get AMP Noloading toggle control.
*
* @param {Object} props Props.
* @return {Object} Element.
*/
component.getAmpNoloadingToggle = function getAmpNoloadingToggle( props ) {
var ampNoLoading = props.attributes.ampNoLoading,
el = wp.element.createElement,
ToggleControl = wp.components.ToggleControl,
label = __( 'AMP Noloading' );
return el( ToggleControl, {
label: label,
checked: ampNoLoading,
onChange: function() {
props.setAttributes( { ampNoLoading: ! ampNoLoading } );
}
} );
};
/**
* Setup inspector controls for text blocks.
*
* @todo Consider wrapping the render function to delete the original font size in text settings when ampFitText.
*
* @param {Object} props Props.
* @return {Object|Element|*|{$$typeof, type, key, ref, props, _owner}} Inspector Controls.
*/
component.setUpTextBlocksInspectorControls = function setUpInspectorControls( props ) {
var inspectorPanelBodyArgs,
ampFitText = props.attributes.ampFitText,
minFont = props.attributes.minFont,
maxFont = props.attributes.maxFont,
height = props.attributes.height,
isSelected = props.isSelected,
el = wp.element.createElement,
InspectorControls = wp.editor.InspectorControls,
TextControl = wp.components.TextControl,
FontSizePicker = wp.components.FontSizePicker,
ToggleControl = wp.components.ToggleControl,
PanelBody = wp.components.PanelBody,
label = __( 'Use AMP Fit Text' ),
FONT_SIZES = [
{
name: 'small',
shortName: __( 'S' ),
size: 14
},
{
name: 'regular',
shortName: __( 'M' ),
size: 16
},
{
name: 'large',
shortName: __( 'L' ),
size: 36
},
{
name: 'larger',
shortName: __( 'XL' ),
size: 48
}
];
if ( ! isSelected ) {
return null;
}
inspectorPanelBodyArgs = [
PanelBody,
{ title: component.data.ampSettingsLabel, className: ampFitText ? 'is-amp-fit-text' : '' },
el( ToggleControl, {
label: label,
checked: ampFitText,
onChange: function() {
props.setAttributes( { ampFitText: ! ampFitText } );
}
} )
];
if ( ampFitText ) {
inspectorPanelBodyArgs.push.apply( inspectorPanelBodyArgs, [
el( TextControl, {
label: __( 'Height' ),
value: height,
min: 1,
onChange: function( nextHeight ) {
props.setAttributes( { height: nextHeight } );
}
} ),
parseInt( maxFont ) > parseInt( height ) && el(
wp.components.Notice,
{
status: 'error',
isDismissible: false
},
__( 'The height must be greater than the max font size.' )
),
el( PanelBody, { title: __( 'Minimum font size' ) },
el( FontSizePicker, {
fallbackFontSize: 14,
value: minFont,
fontSizes: FONT_SIZES,
onChange: function( nextMinFont ) {
if ( ! nextMinFont ) {
nextMinFont = component.data.fontSizes.small; // @todo Supplying fallbackFontSize should be done automatically by the component?
}
if ( parseInt( nextMinFont ) <= parseInt( maxFont ) ) {
props.setAttributes( { minFont: nextMinFont } );
}
}
} )
),
parseInt( minFont ) > parseInt( maxFont ) && el(
wp.components.Notice,
{
status: 'error',
isDismissible: false
},
__( 'The min font size must less than the max font size.' )
),
el( PanelBody, { title: __( 'Maximum font size' ) },
el( FontSizePicker, {
value: maxFont,
fallbackFontSize: 48,
fontSizes: FONT_SIZES,
onChange: function( nextMaxFont ) {
if ( ! nextMaxFont ) {
nextMaxFont = component.data.fontSizes.larger; // @todo Supplying fallbackFontSize should be done automatically by the component?
}
props.setAttributes( {
maxFont: nextMaxFont,
height: Math.max( nextMaxFont, height )
} );
}
} )
)
] );
}
return (
el( InspectorControls, { key: 'inspector' },
el.apply( null, inspectorPanelBodyArgs )
)
);
};
/**
* Set up inspector controls for shortcode block.
* Adds ampCarousel attribute in case of gallery shortcode.
*
* @param {Object} props Props.
* @return {Object} Inspector controls.
*/
component.setUpShortcodeInspectorControls = function setUpShortcodeInspectorControls( props ) {
var isSelected = props.isSelected,
el = wp.element.createElement,
InspectorControls = wp.editor.InspectorControls,
PanelBody = wp.components.PanelBody;
if ( component.isGalleryShortcode( props.attributes ) ) {
return isSelected && (
el( InspectorControls, { key: 'inspector' },
el( PanelBody, { title: component.data.ampPanelLabel },
component.data.hasThemeSupport && component.getAmpCarouselToggle( props ),
component.getAmpLightboxToggle( props )
)
)
);
}
return '';
};
/**
* Get AMP Lightbox toggle control.
*
* @param {Object} props Props.
* @return {Object} Element.
*/
component.getAmpLightboxToggle = function getAmpLightboxToggle( props ) {
var ampLightbox = props.attributes.ampLightbox,
el = wp.element.createElement,
ToggleControl = wp.components.ToggleControl,
label = __( 'Add lightbox effect' );
return el( ToggleControl, {
label: label,
checked: ampLightbox,
onChange: function( nextValue ) {
props.setAttributes( { ampLightbox: ! ampLightbox } );
if ( nextValue ) {
// Lightbox doesn't work with fixed height, so change.
if ( 'fixed-height' === props.attributes.ampLayout ) {
props.setAttributes( { ampLayout: 'fixed' } );
}
// In case of lightbox set linking images to 'none'.
if ( props.attributes.linkTo && 'none' !== props.attributes.linkTo ) {
props.setAttributes( { linkTo: 'none' } );
}
}
}
} );
};
/**
* Get AMP Carousel toggle control.
*
* @param {Object} props Props.
* @return {Object} Element.
*/
component.getAmpCarouselToggle = function getAmpCarouselToggle( props ) {
var ampCarousel = props.attributes.ampCarousel,
el = wp.element.createElement,
ToggleControl = wp.components.ToggleControl,
label = __( 'Display as carousel' );
return el( ToggleControl, {
label: label,
checked: ampCarousel,
onChange: function() {
props.setAttributes( { ampCarousel: ! ampCarousel } );
}
} );
};
/**
* Set up inspector controls for Image block.
*
* @param {Object} props Props.
* @return {Object} Inspector Controls.
*/
component.setUpImageInpsectorControls = function setUpImageInpsectorControls( props ) {
var isSelected = props.isSelected,
el = wp.element.createElement,
InspectorControls = wp.editor.InspectorControls,
PanelBody = wp.components.PanelBody;
return isSelected && (
el( InspectorControls, { key: 'inspector' },
el( PanelBody, { title: component.data.ampPanelLabel },
component.getAmpLayoutControl( props ),
component.getAmpNoloadingToggle( props ),
component.getAmpLightboxToggle( props )
)
)
);
};
/**
* Set up inspector controls for Gallery block.
* Adds ampCarousel attribute for displaying the output as amp-carousel.
*
* @param {Object} props Props.
* @return {Object} Inspector controls.
*/
component.setUpGalleryInpsectorControls = function setUpGalleryInpsectorControls( props ) {
var isSelected = props.isSelected,
el = wp.element.createElement,
InspectorControls = wp.editor.InspectorControls,
PanelBody = wp.components.PanelBody;
return isSelected && (
el( InspectorControls, { key: 'inspector' },
el( PanelBody, { title: component.data.ampPanelLabel },
component.data.hasThemeSupport && component.getAmpCarouselToggle( props ),
component.getAmpLightboxToggle( props )
)
)
);
};
/**
* Filters blocks' save function.
*
* @param {Object} element Element.
* @param {string} blockType Block type.
* @param {Object} attributes Attributes.
* @return {Object} Output element.
*/
component.filterBlocksSave = function filterBlocksSave( element, blockType, attributes ) {
var text = attributes.text || '',
fitTextProps = {
layout: 'fixed-height',
children: element
};
if ( 'core/shortcode' === blockType.name && component.isGalleryShortcode( attributes ) ) {
if ( ! attributes.ampLightbox ) {
if ( component.hasGalleryShortcodeLightboxAttribute( attributes.text || '' ) ) {
text = component.removeAmpLightboxFromShortcodeAtts( attributes.text );
}
}
if ( attributes.ampCarousel ) {
// If the text contains amp-carousel or amp-lightbox, lets remove it.
if ( component.hasGalleryShortcodeCarouselAttribute( text ) ) {
text = component.removeAmpCarouselFromShortcodeAtts( text );
}
// If lightbox is not set, we can return here.
if ( ! attributes.ampLightbox ) {
if ( attributes.text !== text ) {
return wp.element.createElement(
wp.element.RawHTML,
{},
text
);
}
// Else lets return original.
return element;
}
} else if ( ! component.hasGalleryShortcodeCarouselAttribute( attributes.text || '' ) ) {
// Add amp-carousel=false attribute to the shortcode.
text = attributes.text.replace( '[gallery', '[gallery amp-carousel=false' );
} else {
text = attributes.text;
}
if ( attributes.ampLightbox && ! component.hasGalleryShortcodeLightboxAttribute( text ) ) {
text = text.replace( '[gallery', '[gallery amp-lightbox=true' );
}
if ( attributes.text !== text ) {
return wp.element.createElement(
wp.element.RawHTML,
{},
text
);
}
} else if ( -1 !== component.data.textBlocks.indexOf( blockType.name ) && attributes.ampFitText ) {
if ( attributes.minFont ) {
fitTextProps[ 'min-font-size' ] = attributes.minFont;
}
if ( attributes.maxFont ) {
fitTextProps[ 'max-font-size' ] = attributes.maxFont;
}
if ( attributes.height ) {
fitTextProps.height = attributes.height;
}
return wp.element.createElement( 'amp-fit-text', fitTextProps );
}
return element;
};
/**
* Check if AMP Lightbox is set.
*
* @param {Object} attributes Attributes.
* @return {boolean} If is set.
*/
component.hasAmpLightboxSet = function hasAmpLightboxSet( attributes ) {
return attributes.ampLightbox && false !== attributes.ampLightbox;
};
/**
* Check if AMP Carousel is set.
*
* @param {Object} attributes Attributes.
* @return {boolean} If is set.
*/
component.hasAmpCarouselSet = function hasAmpCarouselSet( attributes ) {
return attributes.ampCarousel && false !== attributes.ampCarousel;
};
/**
* Check if AMP NoLoading is set.
*
* @param {Object} attributes Attributes.
* @return {boolean} If is set.
*/
component.hasAmpNoLoadingSet = function hasAmpNoLoadingSet( attributes ) {
return attributes.ampNoLoading && false !== attributes.ampNoLoading;
};
/**
* Check if AMP Layout is set.
*
* @param {Object} attributes Attributes.
* @return {boolean} If AMP Layout is set.
*/
component.hasAmpLayoutSet = function hasAmpLayoutSet( attributes ) {
return attributes.ampLayout && attributes.ampLayout.length;
};
/**
* Removes amp-carousel=false from attributes.
*
* @param {string} shortcode Shortcode text.
* @return {string} Modified shortcode.
*/
component.removeAmpCarouselFromShortcodeAtts = function removeAmpCarouselFromShortcodeAtts( shortcode ) {
return shortcode.replace( ' amp-carousel=false', '' );
};
/**
* Removes amp-lightbox=true from attributes.
*
* @param {string} shortcode Shortcode text.
* @return {string} Modified shortcode.
*/
component.removeAmpLightboxFromShortcodeAtts = function removeAmpLightboxFromShortcodeAtts( shortcode ) {
return shortcode.replace( ' amp-lightbox=true', '' );
};
/**
* Check if shortcode includes amp-carousel attribute.
*
* @param {string} text Shortcode.
* @return {boolean} If has amp-carousel.
*/
component.hasGalleryShortcodeCarouselAttribute = function hasGalleryShortcodeCarouselAttribute( text ) {
return -1 !== text.indexOf( 'amp-carousel=false' );
};
/**
* Check if shortcode includes amp-lightbox attribute.
*
* @param {string} text Shortcode.
* @return {boolean} If has amp-lightbox.
*/
component.hasGalleryShortcodeLightboxAttribute = function hasGalleryShortcodeLightboxAttribute( text ) {
return -1 !== text.indexOf( 'amp-lightbox=true' );
};
/**
* Check if shortcode is gallery shortcode.
*
* @param {Object} attributes Attributes.
* @return {boolean} If is gallery shortcode.
*/
component.isGalleryShortcode = function isGalleryShortcode( attributes ) {
return attributes.text && -1 !== attributes.text.indexOf( 'gallery' );
};
return component;
}() );

View File

@ -0,0 +1,179 @@
/* exported ampPostMetaBox */
/**
* AMP Post Meta Box.
*
* @todo Rename this to be just the ampEditPostScreen?
*
* @since 0.6
*/
var ampPostMetaBox = ( function( $ ) { // eslint-disable-line no-unused-vars
'use strict';
var component = {
/**
* Holds data.
*
* @since 0.6
*/
data: {
canonical: false, // Overridden by amp_is_canonical().
previewLink: '',
enabled: true, // Overridden by post_supports_amp( $post ).
canSupport: true, // Overridden by count( AMP_Post_Type_Support::get_support_errors( $post ) ) === 0.
statusInputName: '',
l10n: {
ampPreviewBtnLabel: ''
}
},
/**
* Toggle animation speed.
*
* @since 0.6
*/
toggleSpeed: 200,
/**
* Core preview button selector.
*
* @since 0.6
*/
previewBtnSelector: '#post-preview',
/**
* AMP preview button selector.
*
* @since 0.6
*/
ampPreviewBtnSelector: '#amp-post-preview'
};
/**
* Boot plugin.
*
* @since 0.6
* @param {Object} data Object data.
* @return {void}
*/
component.boot = function boot( data ) {
component.data = data;
$( document ).ready( function() {
component.statusRadioInputs = $( '[name="' + component.data.statusInputName + '"]' );
if ( component.data.enabled && ! component.data.canonical ) {
component.addPreviewButton();
}
component.listen();
} );
};
/**
* Events listener.
*
* @since 0.6
* @return {void}
*/
component.listen = function listen() {
$( component.ampPreviewBtnSelector ).on( 'click.amp-post-preview', function( e ) {
e.preventDefault();
component.onAmpPreviewButtonClick();
} );
component.statusRadioInputs.prop( 'disabled', true ); // Prevent cementing setting default status as overridden status.
$( '.edit-amp-status, [href="#amp_status"]' ).click( function( e ) {
e.preventDefault();
component.statusRadioInputs.prop( 'disabled', false );
component.toggleAmpStatus( $( e.target ) );
} );
$( '#submitpost input[type="submit"]' ).on( 'click', function() {
$( component.ampPreviewBtnSelector ).addClass( 'disabled' );
} );
};
/**
* Add AMP Preview button.
*
* @since 0.6
* @return {void}
*/
component.addPreviewButton = function addPreviewButton() {
var previewBtn = $( component.previewBtnSelector );
previewBtn
.clone()
.insertAfter( previewBtn )
.prop( {
href: component.data.previewLink,
id: component.ampPreviewBtnSelector.replace( '#', '' )
} )
.text( component.data.l10n.ampPreviewBtnLabel )
.parent()
.addClass( 'has-amp-preview' );
};
/**
* AMP Preview button click handler.
*
* We trigger the Core preview link for events propagation purposes.
*
* @since 0.6
* @return {void}
*/
component.onAmpPreviewButtonClick = function onAmpPreviewButtonClick() {
var $input;
// Flag the AMP preview referer.
$input = $( '<input>' )
.prop( {
type: 'hidden',
name: 'amp-preview',
value: 'do-preview'
} )
.insertAfter( component.ampPreviewBtnSelector );
// Trigger Core preview button and remove AMP flag.
$( component.previewBtnSelector ).click();
$input.remove();
};
/**
* Add AMP status toggle.
*
* @since 0.6
* @param {Object} $target Event target.
* @return {void}
*/
component.toggleAmpStatus = function toggleAmpStatus( $target ) {
var $container = $( '#amp-status-select' ),
status = $container.data( 'amp-status' ),
$checked,
editAmpStatus = $( '.edit-amp-status' );
// Don't modify status on cancel button click.
if ( ! $target.hasClass( 'button-cancel' ) ) {
status = component.statusRadioInputs.filter( ':checked' ).val();
}
$checked = $( '#amp-status-' + status );
// Toggle elements.
editAmpStatus.fadeToggle( component.toggleSpeed, function() {
if ( editAmpStatus.is( ':visible' ) ) {
editAmpStatus.focus();
} else {
$container.find( 'input[type="radio"]' ).first().focus();
}
} );
$container.slideToggle( component.toggleSpeed );
// Update status.
if ( component.data.canSupport ) {
$container.data( 'amp-status', status );
$checked.prop( 'checked', true );
$( '.amp-status-text' ).text( $checked.next().text() );
}
};
return component;
}( window.jQuery ) );

View File

@ -0,0 +1,9 @@
/* global URLS */
// See AMP_Service_Workers::add_amp_runtime_caching() and <https://github.com/ampproject/amp-by-example/blob/a4d798cac6a534e0c46e78944a2718a8dab3c057/boilerplate-generator/templates/files/serviceworkerJs.js#L9-L22>.
{
self.addEventListener( 'install', event => {
event.waitUntil(
caches.open( wp.serviceWorker.core.cacheNames.runtime ).then( cache => cache.addAll( URLS ) )
);
} );
}

View File

@ -0,0 +1,409 @@
/* exported ampValidatedUrlPostEditScreen */
const ampValidatedUrlPostEditScreen = ( function() { // eslint-disable-line no-unused-vars
let component = {
data: {
l10n: {
unsaved_changes: '',
showing_number_errors: '',
page_heading: '',
show_all: '',
amp_enabled: false
}
}
};
/**
* The id for the 'Showing x of y errors' notice.
*
* @var {string}
*/
component.idNumberErrors = 'number-errors';
/**
* The id for the 'Show all' button.
*
* @var {string}
*/
component.showAllId = 'show-all-errors';
/**
* Boot.
*
* @param {Object} data Data.
* @param {Object} data.l10n Translations.
*/
component.boot = function boot( data ) {
Object.assign( component.data, data );
component.handleShowAll();
component.handleFiltering();
component.handleSearching();
component.handleStatusChange();
component.handleBulkActions();
component.changeHeading();
component.watchForUnsavedChanges();
component.showAMPIconIfEnabled();
};
/**
* Add prompt when leaving page due to unsaved changes.
*/
component.addBeforeUnloadPrompt = function addBeforeUnloadPrompt() {
if ( component.beforeUnloadPromptAdded ) {
return;
}
window.addEventListener( 'beforeunload', component.onBeforeUnload );
// Remove prompt when clicking trash or update.
document.querySelector( '#major-publishing-actions' ).addEventListener( 'click', function() {
window.removeEventListener( 'beforeunload', component.onBeforeUnload );
} );
component.beforeUnloadPromptAdded = true;
};
/**
* Watch for unsaved changes.
*
* Add an beforeunload warning when attempting to leave the page when there are unsaved changes,
* unless the user is pressing the trash link or update button.
*/
component.watchForUnsavedChanges = function watchForUnsavedChanges() {
const onChange = function( event ) {
if ( event.target.matches( 'select' ) ) {
document.getElementById( 'post' ).removeEventListener( 'change', onChange );
component.addBeforeUnloadPrompt();
}
};
document.getElementById( 'post' ).addEventListener( 'change', onChange );
};
/**
* Show message at beforeunload.
*
* @param {Event} event - The beforeunload event.
* @return {string} Message.
*/
component.onBeforeUnload = function onBeforeUnload( event ) {
event.preventDefault();
event.returnValue = component.data.l10n.unsaved_changes;
return component.data.l10n.unsaved_changes;
};
/**
* Updates the <tr> with 'Showing x of y validation errors' at the top of the list table with the current count.
* If this does not exist yet, it creates the element.
*
* @param {number} numberErrorsDisplaying - The number of errors displaying.
* @param {number} totalErrors - The total number of errors, displaying or not.
*/
component.updateShowingErrorsRow = function updateShowingErrorsRow( numberErrorsDisplaying, totalErrors ) {
const showAllButton = document.getElementById( component.showAllId );
let thead, th,
tr = document.getElementById( component.idNumberErrors );
const theadQuery = document.getElementsByTagName( 'thead' );
// Only create the <tr> if it does not exist yet.
if ( theadQuery[ 0 ] && ! tr ) {
thead = theadQuery[ 0 ];
tr = document.createElement( 'tr' );
th = document.createElement( 'th' );
th.setAttribute( 'id', component.idNumberErrors );
th.setAttribute( 'colspan', '6' );
tr.appendChild( th );
thead.appendChild( tr );
}
// If all of the errors are displaying, hide the 'Show all' button and the count notice.
if ( showAllButton && numberErrorsDisplaying === totalErrors ) {
showAllButton.classList.add( 'hidden' );
tr.classList.add( 'hidden' );
} else if ( null !== numberErrorsDisplaying ) {
// Update the number of errors displaying and create a 'Show all' button if it does not exist yet.
document.getElementById( component.idNumberErrors ).innerText = component.data.l10n.showing_number_errors.replace( '%1$s', numberErrorsDisplaying );
document.getElementById( component.idNumberErrors ).classList.remove( 'hidden' );
component.conditionallyCreateShowAllButton();
if ( document.getElementById( component.showAllId ) ) {
document.getElementById( component.showAllId ).classList.remove( 'hidden' );
}
}
};
/**
* Conditionally creates and appends a 'Show all' button.
*/
component.conditionallyCreateShowAllButton = function conditionallyCreateShowAllButton() {
const buttonContainer = document.getElementById( 'url-post-filter' );
let showAllButton = document.getElementById( component.showAllId );
// There is no 'Show all' <button> yet, but there is a container element for it, create the <button>
if ( ! showAllButton && buttonContainer ) {
showAllButton = document.createElement( 'button' );
showAllButton.id = component.showAllId;
showAllButton.classList.add( 'button' );
showAllButton.innerText = component.data.l10n.show_all;
buttonContainer.appendChild( showAllButton );
}
};
/**
* On clicking the 'Show all' <button>, this displays all of the validation errors.
* Then, it hides this 'Show all' <button> and the notice for the number of errors showing.
*/
component.handleShowAll = function handleShowAll() {
const onClick = function( event ) {
const validationErrors = document.querySelectorAll( '[data-error-type]' );
if ( ! event.target.matches( '#' + component.showAllId ) ) {
return;
}
event.preventDefault();
// Iterate through all of the errors, and remove the 'hidden' class.
validationErrors.forEach( function( element ) {
element.parentElement.parentElement.classList.remove( 'hidden' );
} );
/*
* Update the notice to indicate that all of the errors are displaying.
* Like 'Showing 5 of 5 validation errors'.
*/
component.updateShowingErrorsRow( validationErrors.length, validationErrors.length );
// Hide this 'Show all' button.
event.target.classList.add( 'hidden' );
// Change the value of the error type <select> element to 'All Error Types'.
document.getElementById( 'amp_validation_error_type' ).selectedIndex = 0;
};
document.getElementById( 'url-post-filter' ).addEventListener( 'click', onClick );
};
/**
* Handles filtering by error type, triggered by clicking 'Apply Filter'.
*
* Gets the value of the error type <select> element.
* And hides all <tr> elements that do not have the same type of this value.
* If 'All Error Types' is selected, this displays all errors.
*/
component.handleFiltering = function handleFiltering() {
const onChange = function( event ) {
const showAllButton = document.getElementById( component.showAllId );
if ( ! event.target.matches( 'select' ) ) {
return;
}
event.preventDefault();
const isAllErrorTypesSelected = ( '-1' === event.target.value );
const errorTypeQuery = document.querySelectorAll( '[data-error-type]' );
// If the user has chosen 'All Error Types' from the <select>, hide the 'Show all' button.
if ( isAllErrorTypesSelected && showAllButton ) {
showAllButton.classList.add( 'hidden' );
}
/*
* Iterate through all of the <tr> elements in the list table.
* If the error type does not match the value (selected error type), hide them.
*/
let numberErrorsDisplaying = 0;
errorTypeQuery.forEach( function( element ) {
const errorType = element.getAttribute( 'data-error-type' );
// If 'All Error Types' was selected, this should display all errors.
if ( isAllErrorTypesSelected || ! event.target.value || event.target.value === errorType ) {
element.parentElement.parentElement.classList.remove( 'hidden' );
numberErrorsDisplaying++;
} else {
element.parentElement.parentElement.classList.add( 'hidden' );
}
} );
component.updateShowingErrorsRow( numberErrorsDisplaying, errorTypeQuery.length );
};
document.getElementById( 'amp_validation_error_type' ).addEventListener( 'change', onChange );
};
/**
* Handles searching for errors via the <input> and the 'Search Errors' <button>.
*/
component.handleSearching = function handleSearching() {
const onClick = function( event ) {
event.preventDefault();
if ( ! event.target.matches( 'input' ) ) {
return;
}
const searchQuery = document.getElementById( 'invalid-url-search-search-input' ).value;
const detailsQuery = document.querySelectorAll( 'tbody .column-details' );
/*
* Iterate through the 'Details' column of each row.
* If the search query is not present, hide the row.
*/
let numberErrorsDisplaying = 0;
detailsQuery.forEach( function( element ) {
let isSearchQueryPresent = false;
element.querySelectorAll( '.detailed' ).forEach( function( detailed ) {
if ( -1 !== detailed.innerText.indexOf( searchQuery ) ) {
isSearchQueryPresent = true;
}
} );
if ( isSearchQueryPresent ) {
element.parentElement.classList.remove( 'hidden' );
numberErrorsDisplaying++;
} else {
element.parentElement.classList.add( 'hidden' );
}
} );
component.updateShowingErrorsRow( numberErrorsDisplaying, detailsQuery.length );
};
document.getElementById( 'search-submit' ).addEventListener( 'click', onClick );
};
/**
* Update icon for select element.
*
* @param {HTMLSelectElement} select Select element.
*/
component.updateSelectIcon = function updateSelectIcon( select ) {
const newOption = select.options[ select.selectedIndex ];
if ( newOption ) {
const iconSrc = newOption.getAttribute( 'data-status-icon' );
select.parentNode.querySelector( 'img' ).setAttribute( 'src', iconSrc );
}
};
/**
* Handles a change in the error status, like from 'New' to 'Accepted'.
*
* Gets the data-status-icon value from the newly-selected <option>.
* And sets this as the src of the status icon <img>.
*/
component.handleStatusChange = function handleStatusChange() {
const setRowStatusClass = function( { row, select } ) {
const acceptedValue = 3;
const rejectedValue = 2;
const status = parseInt( select.options[ select.selectedIndex ].value );
row.classList.toggle( 'new', isNaN( status ) );
row.classList.toggle( 'accepted', acceptedValue === status );
row.classList.toggle( 'rejected', rejectedValue === status );
};
const onChange = function( { event, row, select } ) {
if ( event.target.matches( 'select' ) ) {
component.updateSelectIcon( event.target );
setRowStatusClass( { row, select } );
}
};
document.querySelectorAll( 'tr[id^="tag-"]' ).forEach( function( row ) {
const select = row.querySelector( '.amp-validation-error-status' );
if ( select ) {
setRowStatusClass( { row, select } );
select.addEventListener( 'change', function( event ) {
onChange( { event, row, select } );
} );
}
} );
};
/**
* On checking a bulk action checkbox, this ensures that the 'Accept' and 'Reject' buttons are present. Handle clicking on buttons.
*
* They're hidden until one of these boxes is checked.
* Also, on unchecking the last checked box, this hides these buttons.
*/
component.handleBulkActions = function handleBulkActions() {
const acceptButton = document.querySelector( 'button.action.accept' );
const rejectButton = document.querySelector( 'button.action.reject' );
const acceptAndRejectContainer = document.getElementById( 'accept-reject-buttons' );
const onChange = function( event ) {
let areThereCheckedBoxes;
if ( ! event.target.matches( '[type=checkbox]' ) ) {
return;
}
if ( event.target.checked ) {
// This checkbox was checked, so ensure the buttons display.
acceptAndRejectContainer.classList.remove( 'hidden' );
} else {
/*
* This checkbox was unchecked.
* So find if there are any other checkboxes that are checked.
* If not, hide the 'Accept' and 'Reject' buttons.
*/
areThereCheckedBoxes = false;
document.querySelectorAll( '.check-column [type=checkbox]' ).forEach( function( element ) {
if ( element.checked ) {
areThereCheckedBoxes = true;
}
} );
if ( ! areThereCheckedBoxes ) {
acceptAndRejectContainer.classList.add( 'hidden' );
}
}
};
document.querySelectorAll( '.check-column [type=checkbox]' ).forEach( function( element ) {
element.addEventListener( 'change', onChange );
} );
// Handle click on accept button.
acceptButton.addEventListener( 'click', function() {
Array.prototype.forEach.call( document.querySelectorAll( 'select.amp-validation-error-status' ), function( select ) {
if ( select.closest( 'tr' ).querySelector( '.check-column input[type=checkbox]' ).checked ) {
select.value = '3';
component.updateSelectIcon( select );
component.addBeforeUnloadPrompt();
}
} );
} );
// Handle click on reject button.
rejectButton.addEventListener( 'click', function() {
Array.prototype.forEach.call( document.querySelectorAll( 'select.amp-validation-error-status' ), function( select ) {
if ( select.closest( 'tr' ).querySelector( '.check-column input[type=checkbox]' ).checked ) {
select.value = '2';
component.updateSelectIcon( select );
component.addBeforeUnloadPrompt();
}
} );
} );
};
/**
* Changes the page heading and document title, as this doesn't look to be possible with a PHP filter.
*/
component.changeHeading = function changeHeading() {
const headingQuery = document.getElementsByClassName( 'wp-heading-inline' );
if ( headingQuery[ 0 ] && component.data.l10n.page_heading ) {
headingQuery[ 0 ].innerText = component.data.l10n.page_heading;
document.title = component.data.l10n.page_heading + document.title;
}
};
/**
* Adds the AMP icon to the page heading if AMP is enabled on this URL.
*/
component.showAMPIconIfEnabled = function() {
const heading = document.querySelector( 'h1.wp-heading-inline' );
if ( heading && true === component.data.l10n.amp_enabled ) {
const ampIcon = document.createElement( 'span' );
ampIcon.classList.add( 'status-text', 'sanitized' );
heading.appendChild( ampIcon );
}
};
return component;
}() );

View File

@ -0,0 +1,34 @@
/* exported ampValidatedUrlsIndex */
const ampValidatedUrlsIndex = ( function() { // eslint-disable-line no-unused-vars
let component = {
classes: {}
};
/**
* The class for the new status
*
* @type {string}
*/
component.classes.new = 'new';
/**
* Boot.
*/
component.boot = function boot() {
component.highlightRowsWithNewStatus();
};
/**
* Highlight rows with new status.
*/
component.highlightRowsWithNewStatus = function highlightRowsWithNewStatus() {
document.querySelectorAll( 'tr[id^="post-"]' ).forEach( function( row ) {
if ( row.querySelector( 'span.status-text.' + component.classes.new ) ) {
row.classList.add( 'new' );
}
} );
};
return component;
}() );

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,16 @@
// WIP Pointer function
function sourcesPointer() {
jQuery( document ).on( 'click', '.tooltip-button', function() {
jQuery( this ).pointer( {
content: jQuery( this ).next( '.tooltip' ).attr( 'data-content' ),
position: {
edge: 'left',
align: 'center'
},
pointerClass: 'wp-pointer wp-pointer--tooltip'
} ).pointer( 'open' );
} );
}
// Run at DOM ready.
jQuery( sourcesPointer );

View File

@ -0,0 +1 @@
!function(e){var n={};function t(d){if(n[d])return n[d].exports;var o=n[d]={i:d,l:!1,exports:{}};return e[d].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,d){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:d})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var d=Object.create(null);if(t.r(d),Object.defineProperty(d,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(d,o,function(n){return e[n]}.bind(null,o));return d},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=9)}({9:function(module,__webpack_exports__,__webpack_require__){"use strict";eval("__webpack_require__.r(__webpack_exports__);\n\n// CONCATENATED MODULE: ./node_modules/@wordpress/dom-ready/build-module/index.js\n/**\n * Specify a function to execute when the DOM is fully loaded.\n *\n * @param {Function} callback A function to execute after the DOM is ready.\n *\n * @example\n * ```js\n * import domReady from '@wordpress/dom-ready';\n *\n * domReady( function() {\n * \t//do something after DOM loads.\n * } );\n * ```\n *\n * @return {void}\n */\nvar domReady = function domReady(callback) {\n if (document.readyState === 'complete' || // DOMContentLoaded + Images/Styles/etc loaded, so we call directly.\n document.readyState === 'interactive' // DOMContentLoaded fires at this point, so we call directly.\n ) {\n return callback();\n } // DOMContentLoaded has not fired yet, delay callback until then.\n\n\n document.addEventListener('DOMContentLoaded', callback);\n};\n\n/* harmony default export */ var build_module = (domReady);\n//# sourceMappingURL=index.js.map\n// CONCATENATED MODULE: ./assets/src/wp-dom-ready.js\n\n\nif (!window.wp) {\n\twindow.wp = {};\n}\n\nwp.domReady = build_module;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiOS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9Ad29yZHByZXNzL2RvbS1yZWFkeS9idWlsZC1tb2R1bGUvaW5kZXguanM/ZGE4MSIsIndlYnBhY2s6Ly8vLi9hc3NldHMvc3JjL3dwLWRvbS1yZWFkeS5qcz8yMTJmIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU3BlY2lmeSBhIGZ1bmN0aW9uIHRvIGV4ZWN1dGUgd2hlbiB0aGUgRE9NIGlzIGZ1bGx5IGxvYWRlZC5cbiAqXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFjayBBIGZ1bmN0aW9uIHRvIGV4ZWN1dGUgYWZ0ZXIgdGhlIERPTSBpcyByZWFkeS5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBganNcbiAqIGltcG9ydCBkb21SZWFkeSBmcm9tICdAd29yZHByZXNzL2RvbS1yZWFkeSc7XG4gKlxuICogZG9tUmVhZHkoIGZ1bmN0aW9uKCkge1xuICogXHQvL2RvIHNvbWV0aGluZyBhZnRlciBET00gbG9hZHMuXG4gKiB9ICk7XG4gKiBgYGBcbiAqXG4gKiBAcmV0dXJuIHt2b2lkfVxuICovXG52YXIgZG9tUmVhZHkgPSBmdW5jdGlvbiBkb21SZWFkeShjYWxsYmFjaykge1xuICBpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2NvbXBsZXRlJyB8fCAvLyBET01Db250ZW50TG9hZGVkICsgSW1hZ2VzL1N0eWxlcy9ldGMgbG9hZGVkLCBzbyB3ZSBjYWxsIGRpcmVjdGx5LlxuICBkb2N1bWVudC5yZWFkeVN0YXRlID09PSAnaW50ZXJhY3RpdmUnIC8vIERPTUNvbnRlbnRMb2FkZWQgZmlyZXMgYXQgdGhpcyBwb2ludCwgc28gd2UgY2FsbCBkaXJlY3RseS5cbiAgKSB7XG4gICAgICByZXR1cm4gY2FsbGJhY2soKTtcbiAgICB9IC8vIERPTUNvbnRlbnRMb2FkZWQgaGFzIG5vdCBmaXJlZCB5ZXQsIGRlbGF5IGNhbGxiYWNrIHVudGlsIHRoZW4uXG5cblxuICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgY2FsbGJhY2spO1xufTtcblxuZXhwb3J0IGRlZmF1bHQgZG9tUmVhZHk7XG4vLyMgc291cmNlTWFwcGluZ1VSTD1pbmRleC5qcy5tYXAiLCJpbXBvcnQgZG9tUmVhZHkgZnJvbSAnQHdvcmRwcmVzcy9kb20tcmVhZHknO1xuXG5pZiAoIXdpbmRvdy53cCkge1xuXHR3aW5kb3cud3AgPSB7fTtcbn1cblxud3AuZG9tUmVhZHkgPSBkb21SZWFkeTsiXSwibWFwcGluZ3MiOiI7OztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///9\n")}});

File diff suppressed because one or more lines are too long