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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
.column-error_status .dashicons-editor-help {
color: #767676;
}
.column-sources .dashicons,
.column-sources_with_invalid_output .dashicons {
margin-right: 5px;
}
.column-source .dashicons-admin-plugins,
.column-sources_with_invalid_output .dashicons-admin-plugins {
color: #64a2e9;
}
.column-source .dashicons-admin-appearance,
.column-sources_with_invalid_output .dashicons-admin-appearance {
color: #ebb04f;
}
.column-source, .dashicons-wordpress-alt,
.column-sources_with_invalid_output .dashicons-wordpress-alt {
color: #92b371;
}
.amp-logo-icon {
background-image: url( '../images/amp-logo-icon.svg' );
background-color: transparent;
background-size: 20px 20px;
height: 20px;
width: 20px;
display: inline-block;
}
.column-error_status .error-status {
line-height: 20px;
display: inline-block;
position: relative;
vertical-align: top;
margin-left: 10px;
}
td.column-found_elements_and_attributes {
color: #970010;
}
td.column-found_elements_and_attributes div {
margin-bottom: 0.6rem;
}
.column-error_status .dashicons-flag.new {
color: #d98501;
}
.column-error_status .dashicons-yes.new {
color: #ff0000;
}
.column-error_status .dashicons-warning.rejected {
color: #68c6ff;
}
.column-sources .source,
.column-sources_with_invalid_output .source {
margin-bottom: 10px;
display: block;
}
.wrap .wp-heading-inline + .page-title-action {
margin-left: 1rem;
}

View File

@ -0,0 +1,123 @@
.amp-toggle {
position: relative;
display: inline-block;
width: 30px;
height: 15px;
top: 15px;
left: 130px;
}
.amp-toggle input,
.amp-toggle input.disabled {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
margin: 0;
padding: 0;
z-index: 1;
}
.amp-toggle .slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 34px;
background-color: #555D66;
-webkit-transition: .3s;
transition: .3s;
transition-property: background-color, transform, -webkit-transform, -ms-transform, opacity;
}
.amp-toggle input:focus,
.amp-toggle input:active {
outline: none;
}
.amp-toggle input:hover + .slider,
.amp-toggle input:focus + .slider,
.amp-toggle input:active + .slider {
box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8);
}
.amp-toggle .slider:before {
position: absolute;
content: "";
height: 13px;
width: 13px;
top: 1px;
left: 1px;
border-radius: 50%;
background-color: transparent;
background-image: url( '../images/amp-white-icon.svg' );
background-size: 13px 13px;
-webkit-transition: .3s;
transition: .3s;
}
.amp-toggle input:checked + .slider {
background-color: #0379C4;
}
.amp-toggle input:checked + .slider:before {
-webkit-transform: translateX( 15px );
-ms-transform: translateX( 15px );
transform: translateX( 15px );
}
.amp-toggle input.disabled + .slider {
opacity: 0.7;
}
.amp-toggle .tooltip {
position: absolute;
bottom: 25px;
left: -115px;
width: 230px;
font-size: 13px;
text-align: center;
color: #FFFFFF;
background: #191E23;
padding: 15px;
z-index: 1;
cursor: default;
display: none;
}
.amp-toggle .tooltip a {
color: #00a0d2;
}
.amp-toggle .tooltip a:hover,
.amp-toggle .tooltip a:focus,
.amp-toggle .tooltip a:active {
color: #54cbf1;
}
.amp-toggle .tooltip:before {
position: absolute;
bottom: -8px;
left: 120px;
content: "";
border: solid;
border-color: #191E23 transparent;
border-width: 8px 8px 0 8px;
}
.js .accordion-section-title:after {
z-index: 0;
}
#customize-footer-actions .collapse-sidebar-label {
font-size: 11px;
margin-left: -3px;
}
.devices-wrapper .preview-desktop {
border-left: 1px solid #DDDDDD !important;
}
.wp-full-overlay-footer .devices button:before {
vertical-align: initial;
}

View File

@ -0,0 +1,30 @@
/*
* Prevent cases of amp-img converted from img to appear with stretching by using object-fit to scale.
* See <https://github.com/ampproject/amphtml/issues/21371#issuecomment-475443219>.
* Also use object-fit:contain in worst case scenario when we can't figure out dimensions for an image.
*/
amp-img.amp-wp-enforced-sizes[layout=intrinsic] > img,
.amp-wp-unknown-size > img {
object-fit: contain;
}
amp-fit-text blockquote,
amp-fit-text h1,
amp-fit-text h2,
amp-fit-text h3,
amp-fit-text h4,
amp-fit-text h5,
amp-fit-text h6 {
font-size: inherit;
}
/**
* Override a style rule in Twenty Sixteen and Twenty Seventeen.
* It set display:none for audio elements.
* This selector is the same, though it adds body and uses amp-audio instead of audio.
*/
body amp-audio:not([controls]) {
display: inline-block;
height: auto;
}

View File

@ -0,0 +1,4 @@
.is-amp-fit-text + .blocks-font-size > .components-font-size-picker__buttons,
.is-amp-fit-text + .blocks-font-size > .components-font-size-picker__custom-input {
display: none;
}

View File

@ -0,0 +1,19 @@
/**
* For the custom AMP implementation of the 'playlist' shortcode.
*/
.wp-playlist .wp-playlist-current-item img {
margin-right: 0;
}
.wp-playlist .wp-playlist-current-item amp-img {
float: left;
margin-right: 10px;
}
.wp-playlist audio {
display: block;
}
.wp-playlist .amp-carousel-button {
visibility: hidden;
}

View File

@ -0,0 +1,79 @@
/**
* 1.0 AMP preview.
*
* Submit box preview buttons.
*/
/* Core preview button */
.wp-core-ui #preview-action.has-amp-preview #post-preview {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
float: none;
}
/* AMP preview button */
.wp-core-ui #amp-post-preview.preview {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
text-indent: -9999px;
padding-right: 14px;
padding-left: 14px;
position: relative;
}
.wp-core-ui #amp-post-preview.preview::after {
content: "icon";
background: no-repeat center url( '../images/amp-icon.svg' );
background-size: 14px !important;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: block;
position: absolute;
}
.wp-core-ui #amp-post-preview.preview.disabled::after {
opacity: 0.6;
}
/* AMP status */
.misc-amp-status .amp-icon {
float: left;
background: transparent url( '../images/amp-icon.svg' ) no-repeat left;
background-size: 17px;
width: 17px;
height: 17px;
margin: 0 8px 0 1px;
}
#amp-status-select fieldset {
margin: 7px 0 0 1px;
}
#amp-status-select .notice {
margin: 10px 0 -5px 3px;
}
.amp-status-actions {
margin-top: 10px;
}
@media screen and ( max-width: 782px ) {
#amp-status-select {
line-height: 280%;
}
}
.amp-block-validation-errors {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
font-size: 13px;
line-height: 1.5;
}
.amp-block-validation-errors .amp-block-validation-errors__summary {
margin: 0.5em 0;
padding: 2px;
}
.amp-block-validation-errors .amp-block-validation-errors__list {
padding-left: 2.5em;
}

View File

@ -0,0 +1,279 @@
#col-left {
display: none;
}
#col-right {
float: none;
width: auto;
}
/* Move the 'All dates' filter to the right of the new status and type filters */
#filter-by-date {
float: none;
}
/* Improve column widths */
td.column-details pre,
td.column-sources pre {
overflow: auto;
}
th.column-created_date_gmt,
th.column-error_type {
width: 15%;
}
td.column-error .error-code {
font-family: Consolas, Monaco, monospace;
}
th.column-status {
width: 15%;
}
.fixed th.column-posts {
width: 10%;
}
/* Details column */
.column-details .details-attributes__summary {
display: flex;
justify-content: space-between;
align-items: center;
}
details[open] .details-attributes__summary {
font-weight: 600;
margin-bottom: 15px;
}
.column-details .details-attributes__summary::-webkit-details-marker,
.column-details .notice details > summary::-webkit-details-marker {
display: none;
}
.details-attributes__summary::after,
.single-error-detail-summary::after {
order: 99;
width: 12px;
height: 12px;
background-image: url("../images/down-triangle.svg");
background-size: cover;
background-position: center;
content: "";
}
tr.expanded .details-attributes__summary::after,
details.single-error-detail[open] .single-error-detail-summary::after {
transform: rotate(180deg);
}
.notice .detailed {
padding-left: 15px;
}
.notice .detailed details {
padding-bottom: 16px;
}
.notice .detailed details .detailed {
padding-left: 32px;
font-family: Consolas, Monaco, monospace;
}
.details-attributes__title code,
.notice .detailed summary code {
display: inline-block;
min-width: 240px;
margin-left: 18px;
font-weight: 600;
}
.details-attributes__title code {
margin-left: 0;
}
.details-attributes__list {
margin-top: 0;
padding-left: 0;
list-style: none;
font-family: Consolas, Monaco, monospace;
}
.details-attributes__list li {
word-break: break-all;
}
.details-attributes__attr {
font-weight: 600;
}
.column-sources_with_invalid_output details[open] .details-attributes__summary {
margin-bottom: 5px;
}
.column-sources_with_invalid_output details > div {
padding-left: 25px;
}
/* Error details toggle button */
.manage-column.column-sources_with_invalid_output .error-details-toggle {
margin: 0;
}
.error-details-toggle {
float: right;
display: flex;
flex-direction: column;
height: 14px;
padding: 0;
margin-top: 4px;
background: none;
border: none;
cursor: pointer;
}
.error-details-toggle.is-open {
transform: rotate(180deg);
}
.column-details .error-details-toggle::before,
.column-details .error-details-toggle::after {
width: 12px;
height: 12px;
background-image: url("../images/down-triangle.svg");
background-size: cover;
background-position: center;
content: "";
}
/* Status text icons */
.status-text {
display: flex;
align-items: center;
padding-bottom: 0.6rem;
}
.status-text::before {
margin-right: 10px;
background-size: 20px 20px;
height: 20px;
width: 20px;
content: "";
min-width: 20px;
}
.status-text.sanitized::before {
background-image: url("../images/amp-logo-icon.svg");
}
.status-text.new::before {
background-image: url("../images/baseline-error.svg");
}
.status-text.new.new-accepted::before, .status-text.new.accepted::before{
background-image: url("../images/baseline-error-green.svg");
}
.status-text.new.new-rejected::before, .status-text.new.rejected::before {
background-image: url("../images/baseline-error-red.svg");
}
.status-text.accepted::before {
background-image: url("../images/baseline-check-circle-green.svg");
}
.status-text.rejected::before {
background-image: url("../images/error-rejected.svg");
}
.single-error-detail {
margin: 5px 0 5px 0;
}
.single-error-detail-summary:after {
display: inline-block;
}
.single-error-detail-summary strong {
margin-right: 10px;
font-size: 15px;
}
.single-error-detail ul.secondary-details-array .details-attributes__attr {
margin-left: 20px;
}
.single-error-detail ul.secondary-details-array .details-attributes__value {
margin-left: 30px;
}
.single-error-detail .details-attributes__value {
margin-left: 10px;
}
body.taxonomy-amp_validation_error .wp-list-table .new th,
body.taxonomy-amp_validation_error .wp-list-table .new td,
tr.expanded.new + tr > td:first-of-type,
body.post-type-amp_validated_url .wp-list-table .new th,
body.post-type-amp_validated_url .wp-list-table .new td {
background-color: #fef7f1;
}
body.taxonomy-amp_validation_error .wp-list-table .new th.check-column,
tr.expanded.new + tr > td:first-of-type,
body.post-type-amp_validated_url .wp-list-table .new th.check-column {
border-left: 4px solid #d54e21;
}
body.taxonomy-amp_validation_error .wp-list-table .new th.check-column input {
margin-left: 4px;
}
.row-actions .amp_validation_error_accept > a {
color: #006505;
}
.row-actions .amp_validation_error_reject > a {
color: #a00;
}
.notice.accept-reject-error {
display: flex;
margin-bottom: 0;
}
.notice.accept-reject-error > p {
display: inline-block;
font-size: 14px;
flex-grow: 10;
margin-right: 20px;
}
.notice.accept-reject-error > .button {
display: inline-block;
margin: 5px 5px 0 5px;
padding: 0 26px 2px;
flex-grow: 1;
text-align: center;
}
.notice.accept-reject-error > .button.accept {
/* @todo Add green colors */
}
.notice.accept-reject-error > .button.reject {
/* @todo Add red colors */
}
.notice.error-details {
margin-top: 1px;
}
.wp-heading-inline .status-text {
display: inline-flex;
margin-left: 10px;
vertical-align: middle;
padding-bottom: 0;
}
.wp-heading-inline code {
font-size: 1rem;
}
/** Details post action. */
.details button {
display: inline-block;
background: none;
border: none;
padding: 0;
text-align: left;
color: #0073aa;
cursor: pointer;
text-decoration: underline;
}

View File

@ -0,0 +1,164 @@
/** Arrow icon on title in error column. */
.column-error > .single-url-detail-toggle {
position: relative;
width: 100%;
padding: 5px 36px 5px 0;
background: none;
border: none;
text-align: left;
line-height: 1.682;
color: #0073aa;
cursor: pointer;
}
.column-error > .single-url-detail-toggle::after {
position: absolute;
top: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
width: 12px;
height: 18px;
margin-top: 5px;
background-image: url("../images/down-triangle.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
content: "";
}
tr.expanded .single-url-detail-toggle::after {
transform: rotate(180deg);
}
/** Striped table overrides. */
table.striped > tbody > tr.odd {
background: #f9f9f9;
}
table.striped > tbody > tr.even {
background: #fff;
}
/** Hide original details content. */
.details-attributes > .detailed {
display: none;
}
/** Details post action. */
.details button {
display: inline-block;
background: none;
border: none;
padding: 0;
text-align: left;
color: #0073aa;
cursor: pointer;
text-decoration: underline;
}
/** Details row styles. */
.details details.details-attributes:hover {
cursor: pointer;
}
.details ul.detailed {
padding: 0 32px;
margin-top: 0;
}
.details div.detailed {
padding-left: 30px;
margin-top: 10px;
font-family: Consolas, Monaco, monospace;
}
.details .detailed details {
padding-bottom: 16px;
}
.details .detailed summary code {
display: inline-block;
min-width: 240px;
margin-left: 12px;
font-weight: 600;
}
.column-status select {
vertical-align: top;
}
.column-status img {
width: 1.5rem;
margin-top: 0.2rem;
}
#number-errors {
text-align: center;
background-color: #d3d3d3b8;
color: #1e8cbecc;
}
#url-post-filter {
float: none;
display: inline;
}
.tablenav.top,
.tablenav.bottom {
display: none;
}
.amp-validated-url a {
text-decoration: none;
}
.curtime.misc-pub-section {
margin-top: 0.5rem;
}
/* Give enough width to prevent the widest column status, 'New Accepted,' from forcing the <select> below the icon. */
.wp-list-table th.column-status {
width: 150px;
}
.wp-list-table th.column-sources_with_invalid_output {
width: 20%;
}
.wp-list-table th.column-error {
width: 25%;
}
.wp-list-table th.column-details {
width: 20%;
}
/** Add space between list table and the filter and search box above it. */
#post-body-content button.action,
#post-body-content #url-post-filter,
#post-body-content .search-box {
margin-bottom: 4px;
}
#post-body-content button.reject {
margin-left: 4px;
}
#accept-reject-buttons:not(.hidden) {
display: inline-block;
}
#vertical-divider {
display: inline-block;
vertical-align: middle;
width: 15px;
margin-right: 15px;
height: 30px;
border-right: 1px solid #a0a5aa;
}
.amp-validation-error-status {
width: auto;
float: none;
}

View File

@ -0,0 +1,7 @@
/* @todo This should be moved to admin-tables.css which is then enqueued on both screens. */
.tooltip-button {
margin: 0 6px;
cursor: pointer;
color: #767676;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="62px" height="62px" viewBox="0 0 62 62" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>AMP-Icon</title>
<g id="AMP-Icon" fill="#82878c">
<path d="M41.6288667,28.1614333 L28.6243667,49.8035667 L26.2683667,49.8035667 L28.5975,35.7016667 L21.3838,35.7109667 C21.3838,35.7109667 21.3156,35.7130333 21.2835667,35.7130333 C20.6336,35.7130333 20.1076333,35.1870667 20.1076333,34.5371 C20.1076333,34.2581 20.367,33.7858667 20.367,33.7858667 L33.3291333,12.1695667 L35.7244,12.1799 L33.3363667,26.3035 L40.5872667,26.2942 C40.5872667,26.2942 40.6647667,26.2931667 40.7019667,26.2931667 C41.3519333,26.2931667 41.8779,26.8191333 41.8779,27.4691 C41.8779,27.7326 41.7745667,27.9640667 41.6278333,28.1604 L41.6288667,28.1614333 Z M31,0 C13.8787,0 0,13.8797333 0,31 C0,48.1213 13.8787,62 31,62 C48.1202667,62 62,48.1213 62,31 C62,13.8797333 48.1202667,0 31,0 L31,0 Z" id="Fill-1"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1012 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
<g id="AMP-Logo-Icon" fill="#0075C2">
<path d="M13.3,9.1l-4,6.6H8.5l0.7-4.3l-2.2,0h0c-0.2,0-0.4-0.2-0.4-0.4c0-0.1,0.1-0.2,0.1-0.2l4-6.6l0.7,0l-0.7,4.3l2.2,0l0,0
c0.2,0,0.4,0.2,0.4,0.4C13.4,9,13.3,9.1,13.3,9.1L13.3,9.1z M10,0.5c-5.3,0-9.6,4.3-9.6,9.5c0,5.3,4.3,9.5,9.6,9.5
c5.3,0,9.6-4.3,9.6-9.5C19.6,4.7,15.3,0.5,10,0.5z"/></g>
</svg>

After

Width:  |  Height:  |  Size: 684 B

View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="908.3604125976562 898.2000122070312 183.05340576171875 200.39996337890625" style="enable-background:new 0 0 2000 2000;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.8;fill:none;stroke:url(#SVGID_1_);stroke-width:9.6838;stroke-miterlimit:10;enable-background:new ;}
.st1{opacity:0.8;fill:none;stroke:url(#SVGID_2_);stroke-width:10.1244;stroke-miterlimit:10;enable-background:new ;}
.st2{fill:none;stroke:url(#SVGID_3_);stroke-width:2.4095;stroke-miterlimit:10;}
.st3{fill:url(#SVGID_4_);}
.st4{fill:#FFFFFF;stroke:url(#SVGID_5_);stroke-width:2.4095;stroke-miterlimit:10;}
.st5{fill:none;stroke:url(#SVGID_6_);stroke-width:2.4095;stroke-miterlimit:10;}
.st6{fill:#0DD7FF;fill-opacity:0.7;}
.st7{opacity:0.7;fill:#0DD7FF;enable-background:new ;}
.st8{fill:url(#SVGID_7_);fill-opacity:0.75;}
.st9{fill:none;stroke:url(#SVGID_8_);stroke-width:2.4095;stroke-linecap:round;stroke-miterlimit:10;}
.st10{fill:none;stroke:url(#SVGID_9_);stroke-width:2.4095;stroke-linecap:round;stroke-miterlimit:10;}
.st11{fill:#FFFFFF;fill-opacity:0.75;stroke:url(#SVGID_10_);stroke-width:2.4095;stroke-miterlimit:10;}
.st12{fill:url(#SVGID_11_);fill-opacity:0.75;}
.st13{fill:none;stroke:url(#SVGID_12_);stroke-width:2.4095;stroke-linecap:round;stroke-miterlimit:10;}
.st14{fill:none;stroke:url(#SVGID_13_);stroke-width:2.4095;stroke-linecap:round;stroke-miterlimit:10;}
.st15{fill:#FFFFFF;}
.st16{fill:#167DD2;}
.st17{opacity:0.3;fill:#0DD7FF;enable-background:new ;}
.st18{opacity:0.5;fill:none;stroke:#FFFFFF;stroke-width:2.4095;stroke-linecap:round;stroke-miterlimit:10;}
</style>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-12002.9561" y1="893.2319" x2="-11948.1289" y2="893.2319" gradientTransform="matrix(0.3305 -0.9438 -0.9438 -0.3305 5754.7773 -10044.6777)">
<stop offset="0" style="stop-color:#0389FF"/>
<stop offset="0.5" style="stop-color:#0DD7FF"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<line class="st0" x1="954.3" y1="935.3" x2="953.8" y2="989.9"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-12020.958" y1="879.0432" x2="-11920.0166" y2="879.0432" gradientTransform="matrix(0.3305 -0.9438 -0.9438 -0.3305 5754.7773 -10044.6777)">
<stop offset="0" style="stop-color:#0389FF"/>
<stop offset="0.5" style="stop-color:#0DD7FF"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<line class="st1" x1="969.7" y1="910.9" x2="968.5" y2="1014"/>
<g>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1008.2053" y1="1053.8057" x2="1008.2053" y2="1103.8019" gradientTransform="matrix(1 0 0 -1 0 2002)">
<stop offset="0" style="stop-color:#1C79C4"/>
<stop offset="0.51" style="stop-color:#0389FF"/>
<stop offset="1" style="stop-color:#0DD7FF"/>
</linearGradient>
<line class="st2" x1="1008.2" y1="948.2" x2="1008.2" y2="900.8"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="1008.2053" y1="1053.8057" x2="1008.2053" y2="1103.8019" gradientTransform="matrix(1 0 0 -1 0 2002)">
<stop offset="0" style="stop-color:#1C79C4"/>
<stop offset="0.51" style="stop-color:#0389FF"/>
<stop offset="1" style="stop-color:#0DD7FF"/>
</linearGradient>
<polygon class="st3" points="1018.1,908.8 1016.3,910.4 1008.2,901.7 1000.1,910.4 998.4,908.8 1008.2,898.2 "/>
</g>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="941.6317" y1="1014.0682" x2="1040.9495" y2="1014.0682">
<stop offset="0" style="stop-color:#187CCE"/>
<stop offset="1" style="stop-color:#0DD5FE"/>
</linearGradient>
<polygon class="st4" points="942.8,1098.6 1039.7,1045.5 1039.7,929.6 942.8,982.7 "/>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="943.079" y1="1027.4312" x2="1041.1453" y2="1027.4312" gradientTransform="matrix(1 0 0 -1 0 2002)">
<stop offset="0" style="stop-color:#1C79C4"/>
<stop offset="0.51" style="stop-color:#0389FF"/>
<stop offset="1" style="stop-color:#0DD7FF"/>
</linearGradient>
<line class="st5" x1="1040.6" y1="948" x2="943.7" y2="1001.1"/>
<ellipse transform="matrix(0.1104 -0.9939 0.9939 0.1104 -124.5549 1829.4717)" class="st6" cx="959.7" cy="984.3" rx="2.8" ry="2.8"/>
<ellipse transform="matrix(0.1094 -0.994 0.994 0.1094 -133.3753 1826.4724)" class="st6" cx="952.6" cy="987.7" rx="2.8" ry="2.8"/>
<ellipse transform="matrix(0.1094 -0.994 0.994 0.1094 -111.5842 1834.7726)" class="st6" cx="968.1" cy="979.7" rx="2.8" ry="2.8"/>
<ellipse transform="matrix(0.1172 -0.9931 0.9931 0.1172 23.327 1872.063)" class="st7" cx="1064.6" cy="922.9" rx="3.4" ry="3.5"/>
<ellipse transform="matrix(0.1172 -0.9931 0.9931 0.1172 -144.5006 1781.6594)" class="st7" cx="929.9" cy="972.1" rx="3.4" ry="3.5"/>
<ellipse transform="matrix(0.1172 -0.9931 0.9931 0.1172 -261.0735 1874.2338)" class="st7" cx="923.7" cy="1084" rx="3.4" ry="3.5"/>
<ellipse transform="matrix(0.1172 -0.9931 0.9931 0.1172 -116.1821 1770.166)" class="st7" cx="937.6" cy="950.4" rx="3.4" ry="3.5"/>
<ellipse transform="matrix(0.1172 -0.9931 0.9931 0.1172 -177.6883 1787.7156)" class="st7" cx="916.7" cy="993.8" rx="3.4" ry="3.5"/>
<ellipse transform="matrix(0.1172 -0.9931 0.9931 0.1172 2.8353 1930.9661)" class="st7" cx="1087.5" cy="963.9" rx="3.4" ry="3.5"/>
<ellipse transform="matrix(0.1172 -0.9931 0.9931 0.1172 14.4208 1901.3986)" class="st7" cx="1076.7" cy="942.6" rx="3.4" ry="3.5"/>
<ellipse transform="matrix(0.1172 -0.9931 0.9931 0.1172 -255.1249 1856.6938)" class="st7" cx="916.8" cy="1071.8" rx="3.4" ry="3.5"/>
<ellipse transform="matrix(0.1172 -0.9931 0.9931 0.1172 -141.707 1747.8772)" class="st7" cx="912.3" cy="953.6" rx="3.4" ry="3.5"/>
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="988.4651" y1="1030.0048" x2="988.4651" y2="980.6591">
<stop offset="0" style="stop-color:#187CCE"/>
<stop offset="0" style="stop-color:#187FD0"/>
<stop offset="1" style="stop-color:#0DD5FE"/>
</linearGradient>
<polygon class="st8" points="972.5,1030 1004.4,1013.6 1004.4,980.7 972.5,997 "/>
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="976.1646" y1="969.6558" x2="1000.2975" y2="969.6558" gradientTransform="matrix(1 0 0 -1 0 2002)">
<stop offset="0" style="stop-color:#1C79C4"/>
<stop offset="0.51" style="stop-color:#0389FF"/>
<stop offset="1" style="stop-color:#0DD7FF"/>
</linearGradient>
<line class="st9" x1="977.4" y1="1038.1" x2="999.1" y2="1026.6"/>
<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="976.1646" y1="961.2227" x2="1000.2975" y2="961.2227" gradientTransform="matrix(1 0 0 -1 0 2002)">
<stop offset="0" style="stop-color:#1C79C4"/>
<stop offset="0.51" style="stop-color:#0389FF"/>
<stop offset="1" style="stop-color:#0DD7FF"/>
</linearGradient>
<line class="st10" x1="977.4" y1="1046.5" x2="999.1" y2="1035"/>
<g>
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="1016.4717" y1="1038.5374" x2="1063.432" y2="1038.5374">
<stop offset="0" style="stop-color:#187CCE"/>
<stop offset="1" style="stop-color:#0DD5FE"/>
</linearGradient>
<polygon class="st11" points="1062.2,981.4 1017.7,1004.3 1017.7,1095.7 1062.2,1072.8 "/>
<linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="1039.6508" y1="1046.2155" x2="1039.6508" y2="996.8698">
<stop offset="0" style="stop-color:#187CCE"/>
<stop offset="0" style="stop-color:#187FD0"/>
<stop offset="1" style="stop-color:#0DD5FE"/>
</linearGradient>
<polygon class="st12" points="1023.7,1046.2 1055.6,1029.9 1055.6,996.9 1023.7,1013.2 "/>
<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="1023.5676" y1="954.687" x2="1056.0122" y2="954.687" gradientTransform="matrix(1 0 0 -1 0 2002)">
<stop offset="0" style="stop-color:#1C79C4"/>
<stop offset="0.51" style="stop-color:#0389FF"/>
<stop offset="1" style="stop-color:#0DD7FF"/>
</linearGradient>
<line class="st13" x1="1024.8" y1="1055.3" x2="1054.8" y2="1039.4"/>
<linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="1023.5676" y1="945.752" x2="1056.0122" y2="945.752" gradientTransform="matrix(1 0 0 -1 0 2002)">
<stop offset="0" style="stop-color:#1C79C4"/>
<stop offset="0.51" style="stop-color:#0389FF"/>
<stop offset="1" style="stop-color:#0DD7FF"/>
</linearGradient>
<line class="st14" x1="1024.8" y1="1064.2" x2="1054.8" y2="1048.3"/>
<path class="st15" d="M1043.8,1018.9l-5,11.6l-0.9,0.3l0.9-6.7l-2.8,0.9l0,0c-0.2,0.1-0.5-0.1-0.5-0.4c0-0.1,0.1-0.4,0.1-0.4
l5-11.6l0.9-0.3l-0.9,6.8l2.8-0.9l0,0c0.3-0.1,0.5,0.1,0.5,0.4C1043.8,1018.6,1043.8,1018.8,1043.8,1018.9L1043.8,1018.9z"/>
</g>
<polygon class="st16" points="968.6,1083 943.7,1097.1 943.7,1002.4 968.6,989 "/>
<polygon class="st17" points="1013.1,975.4 1032.9,964.4 1032.9,973.6 1013.6,984.7 "/>
<polygon class="st17" points="1013.1,988.5 1032.9,977.4 1032.9,982.8 1013.6,993.7 "/>
<path class="st15" d="M956.2,1000c-4.7,2.2-8.6,8.1-8.6,13.1c0,5,3.9,7.4,8.6,5.2c4.7-2.2,8.6-8.1,8.6-13.1
C964.8,1000.2,960.9,997.8,956.2,1000z M948.4,1012.7c0-1.2,0.2-2.4,0.7-3.7l3.7,9.1C950.2,1018,948.4,1016,948.4,1012.7z
M956.2,1017.4c-0.8,0.3-1.5,0.6-2.2,0.7l2.3-8.2l2.4,5.8c0,0,0,0.1,0.1,0.1C958,1016.4,957.1,1017,956.2,1017.4z M957.2,1004.8
c0.5-0.2,0.9-0.5,0.9-0.5c0.4-0.2,0.4-0.9-0.1-0.7c0,0-1.3,0.7-2.1,1.1c-0.8,0.3-2,0.8-2,0.8c-0.4,0.2-0.5,0.9,0,0.7
c0,0,0.4-0.1,0.8-0.3l1.2,3l-1.7,6.2l-2.8-7.6c0.5-0.2,0.9-0.5,0.9-0.5c0.4-0.2,0.4-0.9-0.1-0.7c0,0-1.3,0.7-2.1,1.1
c-0.1,0.1-0.3,0.1-0.5,0.2c1.4-2.9,3.8-5.4,6.5-6.7c2-0.9,3.9-1,5.2-0.2c0,0-0.1,0-0.1,0c-0.8,0.3-1.3,1.3-1.3,2.1
c0,0.7,0.4,1.1,0.8,1.6c0.3,0.4,0.6,1,0.6,2c0,0.7-0.3,1.6-0.6,2.9l-0.8,3.1L957.2,1004.8z M963,1002.1c0.6,0.9,1,2.1,1,3.5
c0,3-1.5,6.4-3.8,8.9l2.3-8.4c0.4-1.4,0.6-2.4,0.6-3.2C963,1002.6,963,1002.3,963,1002.1z"/>
<path class="st18" d="M970.6,1021.2"/>
<line class="st18" x1="948.6" y1="1028.4" x2="963.6" y2="1020.5"/>
<line class="st18" x1="948.6" y1="1036.3" x2="963.6" y2="1028.4"/>
<line class="st18" x1="948.6" y1="1044.3" x2="963.6" y2="1036.3"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="62px" height="62px" viewBox="0 0 62 62" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>AMP-White-Icon</title>
<g id="amp-logo-internal-site" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="AMP-Brand-White-Icon" fill="#FFFFFF">
<path d="M41.6288667,28.1614333 L28.6243667,49.8035667 L26.2683667,49.8035667 L28.5975,35.7016667 L21.3838,35.7109667 C21.3838,35.7109667 21.3156,35.7130333 21.2835667,35.7130333 C20.6336,35.7130333 20.1076333,35.1870667 20.1076333,34.5371 C20.1076333,34.2581 20.367,33.7858667 20.367,33.7858667 L33.3291333,12.1695667 L35.7244,12.1799 L33.3363667,26.3035 L40.5872667,26.2942 C40.5872667,26.2942 40.6647667,26.2931667 40.7019667,26.2931667 C41.3519333,26.2931667 41.8779,26.8191333 41.8779,27.4691 C41.8779,27.7326 41.7745667,27.9640667 41.6278333,28.1604 L41.6288667,28.1614333 Z M31,0 C13.8787,0 0,13.8797333 0,31 C0,48.1213 13.8787,62 31,62 C48.1202667,62 62,48.1213 62,31 C62,13.8797333 48.1202667,0 31,0 L31,0 Z" id="Fill-1"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="#85b649" />
</svg>

After

Width:  |  Height:  |  Size: 283 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h20v20H0V0z"/>
<g id="AMP-Warning" fill="#84b741">
<path d="M10,1c-5,0-9,4.1-9,9s4.1,9,9,9s9-4.1,9-9S15,1,10,1z M10.9,14.6H9.1v-1.8H11v1.8H10.9z M10.9,10.9H9.1V5.4H11v5.4H10.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 615 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h20v20H0V0z"/>
<g id="AMP-Warning" fill="#FF0000">
<path d="M10,1c-5,0-9,4.1-9,9s4.1,9,9,9s9-4.1,9-9S15,1,10,1z M10.9,14.6H9.1v-1.8H11v1.8H10.9z M10.9,10.9H9.1V5.4H11v5.4H10.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 615 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h20v20H0V0z"/>
<g id="AMP-Warning" fill="#E38000">
<path d="M10,1c-5,0-9,4.1-9,9s4.1,9,9,9s9-4.1,9-9S15,1,10,1z M10.9,14.6H9.1v-1.8H11v1.8H10.9z M10.9,10.9H9.1V5.4H11v5.4H10.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 615 B

View File

@ -0,0 +1,3 @@
<svg height="1024" width="767.5" xmlns="http://www.w3.org/2000/svg">
<path d="M0 384l383.75 383.75L767.5 384H0z" fill="#0073aa" />
</svg>

After

Width:  |  Height:  |  Size: 140 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><rect x="0" fill="none" width="20" height="20"/><g fill="#FF0E57"><path d="M17 10c0-3.87-3.14-7-7-7-3.87 0-7 3.13-7 7s3.13 7 7 7c3.86 0 7-3.13 7-7zm-6.3 1.48H9.14v-.43c0-.38.08-.7.24-.98s.46-.57.88-.89c.41-.29.68-.53.81-.71.14-.18.2-.39.2-.62 0-.25-.09-.44-.28-.58-.19-.13-.45-.19-.79-.19-.58 0-1.25.19-2 .57l-.64-1.28c.87-.49 1.8-.74 2.77-.74.81 0 1.45.2 1.92.58.48.39.71.91.71 1.55 0 .43-.09.8-.29 1.11-.19.32-.57.67-1.11 1.06-.38.28-.61.49-.71.63-.1.15-.15.34-.15.57v.35zm-1.47 2.74c-.18-.17-.27-.42-.27-.73 0-.33.08-.58.26-.75s.43-.25.77-.25c.32 0 .57.09.75.26s.27.42.27.74c0 .3-.09.55-.27.72-.18.18-.43.27-.75.27-.33 0-.58-.09-.76-.26z"/></g></svg>

After

Width:  |  Height:  |  Size: 713 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><rect x="0" fill="none" width="20" height="20"/><g fill="#FF0000"><path d="M12.12 10l3.53 3.53-2.12 2.12L10 12.12l-3.54 3.54-2.12-2.12L7.88 10 4.34 6.46l2.12-2.12L10 7.88l3.54-3.53 2.12 2.12z"/></g></svg>

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

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