Version Description
(2018-05-03) =
- Render an entire site as "Native AMP" if the theme calls
add_theme_support( 'amp' )
. See #857, #852, #865, #888. Props westonruter, kaitnyl, ThierryA. - Use the AMP spec to automatically discover the required AMP component scripts to include on the page while post-processing. See #882, #885. Props westonruter.
- Automatically concatenate stylesheets from
style
tags with loaded stylesheets fromlink
tags combined in onestyle[amp-custom]
. See #887, #890, #935. Props westonruter. - Update serialization to use HTML instead of XML; update minimum version of PHP fro, 5.2 to 5.3. See #891.
- Add support for widgets. See #870. Props kienstra.
- Add support for forms. See #907, #923. Props DavidCramer.
- Use "Paired Mode" if the theme calls
add_theme_support( 'amp' )
and passes a'template_dir'
value for the AMP templates. See #856, #877. Props westonruter, kaitnyl. - Add AMP implementations of audio/video playlists. See #954. Props kienstra.
- Allow full Customization when the theme supports
'amp'
. See #952. Props westonruter. - Add support for all default WordPress widgets. See #921, #917. Props kienstra, westonruter.
- Add support for more default embeds: Issuu, Post, Meetup, Reddit, Screencast, Tumblr, and WordPress Plugin Directory. See #889. Props kaitnyl.
- Allow native WordPress commenting, in fully valid AMP. See #1024, #1029, #871, #909. Props DavidCramer, westonruter.
- Add a UI for displaying validation errors, including invalid tags and attributes, with tracing for the source for each error according to which theme/plugin's shortcode, widget, or other hook is responsible. Includes debug mode to suspend sanitizer. See #971, #1012, #1016. Props westonruter, kienstra.
- On activating a plugin, validate a front-end page and display a notice if there were errors. See #971. Props westonruter, kienstra.
- Creation of AMP-related notifications, on entering invalid content in the 'classic' editor. See #912. Props kienstra, westonruter, ThierryA.
- Optionally use
<amp-live-list>
to display comments, avoiding full-page refreshes on adding comments. And enable making requests for an<amp-live-list>
, like for displaying posts. See #1029, #915. Props DavidCramer, westonruter. - Support
<amp-bind>
, enabling more dynamic elements. See #895. Props westonruter. - Add output buffering, ensuring the entire page is valid AMP. See #929, #857, #931. Props westonruter, ThierryA.
- Add validation of host names in URLs. See #983. Props rubengonzalezmrf.
- Add WP-CLI scripts to test AMP support of comments and widgets. See #924, #859. Props DavidCramer, kienstra.
- Improve test coverage, including for
AMP_Theme_Support
. See #1034. Props DavidCramer, kienstra. - Update the generated sanitizer file to the AMP spec, and simplify the file that generates it. See #929, #926. Props westonruter.
- Several sanitizer updates, including for styles, and preventing valid tags from being removed. See #935, #944, #952. Props westonruter, davisshaver.
- Improve sanitization of
<amp-img>
,<amp-video>
, and<amp-iframe>
. See #937, #1054. Props kienstra, amedina. - Fix an issue where the JSON inside
<script type="application/json">
was wrapped with CDATA. See #891. Props westonruter. - Allow use of AMP components outside of AMP documents, including in PWA. See #1013. Props westonruter.
- Access the AMP query var with
amp_get_slug()
, instead ofAMP_QUERY_VAR
. See #986. Props westonruter, mjangda. - Update build scripts, including PHP versions in
.travis.yml
. See #1058, #949. Props westonruter. - Prevent New Relic script from being injected in AMP responses. See #932. Props westonruter.
- Fix handling of 0 and empty height/width attributes. See #979. Props davisshaver.
For a full list of the closed issues and merged pull requests in this release, see the 0.7 milestone.
Contributors in this release, including design, development, testing, and project management: Adam Silverstein (adamsilverstein), Alberto Medina (amedina), Christian Chung (christianc1), Claudio Sossi, David Cramer (DavidCramer), Davis Shaver (davisshaver), Douglas Paul (douglyuckling), Jason Johnston (jhnstn), Joshua Wold (jwold), Kaitlyn (kaitnyl), Leo Postovoit (postphotos), Mackenzie Hartung (MackenzieHartung), Maxim Siebert (MaximSiebert), Mike Crantea (mehigh), Mohammad Jangda (mjangda), Oscar Sanchez (oscarssanchez), Philip John (philipjohn), Piotr Delawski (delawski), Renato Alves (renatonascalves), Rubn (rubengonzalezmrf), Ryan Kienstra (kienstra), Thierry Muller (ThierryA), vortfu, Weston Ruter (westonruter), Ziga Sancin (zigasancin).
Release Info
Developer | westonruter |
Plugin | AMP for WordPress |
Version | 0.7.0 |
Comparing to | |
See all releases |
Code changes from version 0.6.2 to 0.7.0
- amp.php +128 -60
- assets/css/amp-default.css +11 -0
- assets/css/amp-playlist-shortcode.css +19 -0
- assets/js/amp-post-meta-box.js +4 -1
- includes/admin/class-amp-customizer.php +1 -1
- includes/admin/class-amp-post-meta-box.php +9 -2
- includes/admin/functions.php +28 -14
- includes/amp-frontend-actions.php +14 -2
- includes/amp-helper-functions.php +542 -6
- includes/amp-post-template-actions.php +29 -49
- includes/class-amp-autoloader.php +15 -0
- includes/class-amp-comment-walker.php +121 -0
- includes/class-amp-post-type-support.php +3 -3
- includes/class-amp-theme-support.php +1201 -0
- includes/embeds/class-amp-base-embed-handler.php +10 -0
- includes/embeds/class-amp-dailymotion-embed.php +0 -11
- includes/embeds/class-amp-facebook-embed.php +0 -11
- includes/embeds/class-amp-gallery-embed.php +92 -49
- includes/embeds/class-amp-instagram-embed.php +0 -11
- includes/embeds/class-amp-issuu-embed-handler.php +65 -0
- includes/embeds/class-amp-meetup-embed-handler.php +45 -0
- includes/embeds/class-amp-pinterest-embed.php +0 -11
- includes/embeds/class-amp-playlist-embed-handler.php +324 -0
- includes/embeds/class-amp-reddit-embed-handler.php +74 -0
- includes/embeds/class-amp-soundcloud-embed.php +1 -28
- includes/embeds/class-amp-tumblr-embed-handler.php +60 -0
- includes/embeds/class-amp-twitter-embed.php +0 -11
- includes/embeds/class-amp-vimeo-embed.php +24 -11
- includes/embeds/class-amp-vine-embed.php +0 -11
- includes/embeds/class-amp-youtube-embed.php +25 -12
- includes/options/class-amp-options-manager.php +2 -2
- includes/options/class-amp-options-menu.php +15 -5
- includes/sanitizers/class-amp-allowed-tags-generated.php +3953 -1320
- includes/sanitizers/class-amp-audio-sanitizer.php +1 -37
- includes/sanitizers/class-amp-base-sanitizer.php +122 -48
- includes/sanitizers/class-amp-blacklist-sanitizer.php +11 -9
- includes/sanitizers/class-amp-comments-sanitizer.php +156 -0
- includes/sanitizers/class-amp-form-sanitizer.php +141 -0
- includes/sanitizers/class-amp-iframe-sanitizer.php +7 -40
- includes/sanitizers/class-amp-img-sanitizer.php +70 -49
- includes/sanitizers/class-amp-playbuzz-sanitizer.php +1 -38
- includes/sanitizers/class-amp-rule-spec.php +63 -16
- includes/sanitizers/class-amp-style-sanitizer.php +431 -45
- includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php +594 -129
- includes/sanitizers/class-amp-video-sanitizer.php +5 -40
- includes/templates/class-amp-content-sanitizer.php +58 -13
- includes/templates/class-amp-post-template.php +4 -124
- includes/utils/class-amp-dom-utils.php +320 -70
- includes/utils/class-amp-validation-utils.php +1848 -0
- includes/widgets/class-amp-widget-archives.php +103 -0
- includes/widgets/class-amp-widget-categories.php +91 -0
- includes/widgets/class-amp-widget-media-video.php +33 -0
- includes/widgets/class-amp-widget-recent-comments.php +38 -0
- includes/widgets/class-amp-widget-text.php +33 -0
- readme.txt +55 -12
- wpcom-helper.php +97 -31
@@ -3,9 +3,9 @@
|
|
3 |
* Plugin Name: AMP
|
4 |
* Description: Add AMP support to your WordPress site.
|
5 |
* Plugin URI: https://github.com/automattic/amp-wp
|
6 |
-
* Author:
|
7 |
-
* Author URI: https://
|
8 |
-
* Version: 0.
|
9 |
* Text Domain: amp
|
10 |
* Domain Path: /languages/
|
11 |
* License: GPLv2 or later
|
@@ -13,9 +13,26 @@
|
|
13 |
* @package AMP
|
14 |
*/
|
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
define( 'AMP__FILE__', __FILE__ );
|
17 |
define( 'AMP__DIR__', dirname( __FILE__ ) );
|
18 |
-
define( 'AMP__VERSION', '0.
|
19 |
|
20 |
require_once AMP__DIR__ . '/includes/class-amp-autoloader.php';
|
21 |
AMP_Autoloader::register();
|
@@ -38,7 +55,7 @@ function amp_deactivate() {
|
|
38 |
// We need to manually remove the amp endpoint
|
39 |
global $wp_rewrite;
|
40 |
foreach ( $wp_rewrite->endpoints as $index => $endpoint ) {
|
41 |
-
if (
|
42 |
unset( $wp_rewrite->endpoints[ $index ] );
|
43 |
break;
|
44 |
}
|
@@ -47,6 +64,15 @@ function amp_deactivate() {
|
|
47 |
flush_rewrite_rules();
|
48 |
}
|
49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
/**
|
51 |
* Set up AMP.
|
52 |
*
|
@@ -56,27 +82,13 @@ function amp_deactivate() {
|
|
56 |
* @since 0.6
|
57 |
*/
|
58 |
function amp_after_setup_theme() {
|
|
|
|
|
59 |
if ( false === apply_filters( 'amp_is_enabled', true ) ) {
|
60 |
return;
|
61 |
}
|
62 |
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Filter the AMP query variable.
|
66 |
-
*
|
67 |
-
* @since 0.3.2
|
68 |
-
* @param string $query_var The AMP query variable.
|
69 |
-
*/
|
70 |
-
define( 'AMP_QUERY_VAR', apply_filters( 'amp_query_var', 'amp' ) );
|
71 |
-
}
|
72 |
-
|
73 |
-
add_action( 'init', 'amp_init' );
|
74 |
-
add_action( 'admin_init', 'AMP_Options_Manager::register_settings' );
|
75 |
-
add_filter( 'amp_post_template_analytics', 'amp_add_custom_analytics' );
|
76 |
-
add_action( 'wp_loaded', 'amp_post_meta_box' );
|
77 |
-
add_action( 'wp_loaded', 'amp_add_options_menu' );
|
78 |
-
add_action( 'parse_query', 'amp_correct_query_when_is_front_page' );
|
79 |
-
AMP_Post_Type_Support::add_post_type_support();
|
80 |
}
|
81 |
add_action( 'after_setup_theme', 'amp_after_setup_theme', 5 );
|
82 |
|
@@ -96,28 +108,83 @@ function amp_init() {
|
|
96 |
|
97 |
load_plugin_textdomain( 'amp', false, plugin_basename( AMP__DIR__ ) . '/languages' );
|
98 |
|
99 |
-
add_rewrite_endpoint(
|
100 |
|
|
|
|
|
|
|
101 |
add_filter( 'request', 'amp_force_query_var_value' );
|
102 |
-
add_action( '
|
|
|
|
|
|
|
103 |
|
104 |
// Redirect the old url of amp page to the updated url.
|
105 |
add_filter( 'old_slug_redirect_url', 'amp_redirect_old_slug_to_new_url' );
|
106 |
|
107 |
if ( class_exists( 'Jetpack' ) && ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
|
108 |
-
require_once
|
109 |
}
|
|
|
|
|
|
|
110 |
}
|
111 |
|
112 |
// Make sure the `amp` query var has an explicit value.
|
113 |
// Avoids issues when filtering the deprecated `query_string` hook.
|
114 |
function amp_force_query_var_value( $query_vars ) {
|
115 |
-
if ( isset( $query_vars[
|
116 |
-
$query_vars[
|
117 |
}
|
118 |
return $query_vars;
|
119 |
}
|
120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
/**
|
122 |
* Fix up WP_Query for front page when amp query var is present.
|
123 |
*
|
@@ -136,7 +203,7 @@ function amp_correct_query_when_is_front_page( WP_Query $query ) {
|
|
136 |
$query->is_home()
|
137 |
&&
|
138 |
// Is AMP endpoint.
|
139 |
-
false !== $query->get(
|
140 |
&&
|
141 |
// Is query not yet fixed uo up to be front page.
|
142 |
! $query->is_front_page()
|
@@ -148,7 +215,7 @@ function amp_correct_query_when_is_front_page( WP_Query $query ) {
|
|
148 |
get_option( 'page_on_front' )
|
149 |
&&
|
150 |
// See line in WP_Query::parse_query() at <https://github.com/WordPress/wordpress-develop/blob/0baa8ae/src/wp-includes/class-wp-query.php#L961>.
|
151 |
-
0 === count( array_diff( array_keys( wp_parse_args( $query->query ) ), array(
|
152 |
);
|
153 |
if ( $is_front_page_query ) {
|
154 |
$query->is_home = false;
|
@@ -159,38 +226,28 @@ function amp_correct_query_when_is_front_page( WP_Query $query ) {
|
|
159 |
}
|
160 |
|
161 |
/**
|
162 |
-
*
|
163 |
*
|
164 |
-
*
|
|
|
|
|
|
|
|
|
165 |
*
|
166 |
-
* @
|
167 |
*/
|
168 |
-
function
|
169 |
-
|
170 |
-
if (
|
171 |
-
return;
|
172 |
}
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
*
|
178 |
-
* @var WP_Post $post
|
179 |
-
*/
|
180 |
-
$post = get_queried_object();
|
181 |
-
if ( ! post_supports_amp( $post ) ) {
|
182 |
-
if ( $is_amp_endpoint ) {
|
183 |
-
wp_safe_redirect( get_permalink( $post->ID ), 302 ); // Temporary redirect because AMP may be supported in future.
|
184 |
-
exit;
|
185 |
}
|
186 |
-
return;
|
187 |
-
}
|
188 |
-
|
189 |
-
if ( $is_amp_endpoint ) {
|
190 |
-
amp_prepare_render();
|
191 |
-
} else {
|
192 |
-
amp_add_frontend_actions();
|
193 |
}
|
|
|
194 |
}
|
195 |
|
196 |
function amp_load_classes() {
|
@@ -248,9 +305,14 @@ function amp_render_post( $post ) {
|
|
248 |
* which is not ideal for any code that expects to run in an AMP context.
|
249 |
* Let's force the value to be true while we render AMP.
|
250 |
*/
|
251 |
-
$was_set = isset( $wp_query->query_vars[
|
252 |
if ( ! $was_set ) {
|
253 |
-
$wp_query->query_vars[
|
|
|
|
|
|
|
|
|
|
|
254 |
}
|
255 |
|
256 |
/**
|
@@ -267,17 +329,23 @@ function amp_render_post( $post ) {
|
|
267 |
$template->load();
|
268 |
|
269 |
if ( ! $was_set ) {
|
270 |
-
unset( $wp_query->query_vars[
|
271 |
}
|
272 |
}
|
273 |
|
274 |
/**
|
275 |
* Bootstraps the AMP customizer.
|
276 |
*
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
* @since 0.4
|
278 |
*/
|
279 |
function _amp_bootstrap_customizer() {
|
280 |
-
add_action( 'after_setup_theme', 'amp_init_customizer' );
|
281 |
}
|
282 |
add_action( 'plugins_loaded', '_amp_bootstrap_customizer', 9 ); // Should be hooked before priority 10 on 'plugins_loaded' to properly unhook core panels.
|
283 |
|
@@ -292,7 +360,7 @@ add_action( 'plugins_loaded', '_amp_bootstrap_customizer', 9 ); // Should be hoo
|
|
292 |
function amp_redirect_old_slug_to_new_url( $link ) {
|
293 |
|
294 |
if ( is_amp_endpoint() ) {
|
295 |
-
$link = trailingslashit( trailingslashit( $link ) .
|
296 |
}
|
297 |
|
298 |
return $link;
|
3 |
* Plugin Name: AMP
|
4 |
* Description: Add AMP support to your WordPress site.
|
5 |
* Plugin URI: https://github.com/automattic/amp-wp
|
6 |
+
* Author: WordPress.com VIP, XWP, Google, and contributors
|
7 |
+
* Author URI: https://github.com/Automattic/amp-wp/graphs/contributors
|
8 |
+
* Version: 0.7.0
|
9 |
* Text Domain: amp
|
10 |
* Domain Path: /languages/
|
11 |
* License: GPLv2 or later
|
13 |
* @package AMP
|
14 |
*/
|
15 |
|
16 |
+
/**
|
17 |
+
* Print admin notice regarding having an old version of PHP.
|
18 |
+
*
|
19 |
+
* @since 0.7
|
20 |
+
*/
|
21 |
+
function _amp_print_php_version_admin_notice() {
|
22 |
+
?>
|
23 |
+
<div class="notice notice-error">
|
24 |
+
<p><?php esc_html_e( 'The AMP plugin requires PHP 5.3+. Please contact your host to update your PHP version.', 'amp' ); ?></p>
|
25 |
+
</div>
|
26 |
+
<?php
|
27 |
+
}
|
28 |
+
if ( version_compare( phpversion(), '5.3', '<' ) ) {
|
29 |
+
add_action( 'admin_notices', '_amp_print_php_version_admin_notice' );
|
30 |
+
return;
|
31 |
+
}
|
32 |
+
|
33 |
define( 'AMP__FILE__', __FILE__ );
|
34 |
define( 'AMP__DIR__', dirname( __FILE__ ) );
|
35 |
+
define( 'AMP__VERSION', '0.7.0' );
|
36 |
|
37 |
require_once AMP__DIR__ . '/includes/class-amp-autoloader.php';
|
38 |
AMP_Autoloader::register();
|
55 |
// We need to manually remove the amp endpoint
|
56 |
global $wp_rewrite;
|
57 |
foreach ( $wp_rewrite->endpoints as $index => $endpoint ) {
|
58 |
+
if ( amp_get_slug() === $endpoint[1] ) {
|
59 |
unset( $wp_rewrite->endpoints[ $index ] );
|
60 |
break;
|
61 |
}
|
64 |
flush_rewrite_rules();
|
65 |
}
|
66 |
|
67 |
+
/*
|
68 |
+
* Register AMP scripts regardless of whether AMP is enabled or it is the AMP endpoint
|
69 |
+
* for the sake of being able to use AMP components on non-AMP documents ("dirty AMP").
|
70 |
+
*/
|
71 |
+
add_action( 'wp_default_scripts', 'amp_register_default_scripts' );
|
72 |
+
|
73 |
+
// Ensure async and custom-element/custom-template attributes are present on script tags.
|
74 |
+
add_filter( 'script_loader_tag', 'amp_filter_script_loader_tag', PHP_INT_MAX, 2 );
|
75 |
+
|
76 |
/**
|
77 |
* Set up AMP.
|
78 |
*
|
82 |
* @since 0.6
|
83 |
*/
|
84 |
function amp_after_setup_theme() {
|
85 |
+
amp_get_slug(); // Ensure AMP_QUERY_VAR is set.
|
86 |
+
|
87 |
if ( false === apply_filters( 'amp_is_enabled', true ) ) {
|
88 |
return;
|
89 |
}
|
90 |
|
91 |
+
add_action( 'init', 'amp_init', 0 ); // Must be 0 because widgets_init happens at init priority 1.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
}
|
93 |
add_action( 'after_setup_theme', 'amp_after_setup_theme', 5 );
|
94 |
|
108 |
|
109 |
load_plugin_textdomain( 'amp', false, plugin_basename( AMP__DIR__ ) . '/languages' );
|
110 |
|
111 |
+
add_rewrite_endpoint( amp_get_slug(), EP_PERMALINK );
|
112 |
|
113 |
+
AMP_Validation_Utils::init();
|
114 |
+
AMP_Theme_Support::init();
|
115 |
+
AMP_Post_Type_Support::add_post_type_support();
|
116 |
add_filter( 'request', 'amp_force_query_var_value' );
|
117 |
+
add_action( 'admin_init', 'AMP_Options_Manager::register_settings' );
|
118 |
+
add_action( 'wp_loaded', 'amp_post_meta_box' );
|
119 |
+
add_action( 'wp_loaded', 'amp_add_options_menu' );
|
120 |
+
add_action( 'parse_query', 'amp_correct_query_when_is_front_page' );
|
121 |
|
122 |
// Redirect the old url of amp page to the updated url.
|
123 |
add_filter( 'old_slug_redirect_url', 'amp_redirect_old_slug_to_new_url' );
|
124 |
|
125 |
if ( class_exists( 'Jetpack' ) && ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
|
126 |
+
require_once AMP__DIR__ . '/jetpack-helper.php';
|
127 |
}
|
128 |
+
|
129 |
+
// Add actions for legacy post templates.
|
130 |
+
add_action( 'wp', 'amp_maybe_add_actions' );
|
131 |
}
|
132 |
|
133 |
// Make sure the `amp` query var has an explicit value.
|
134 |
// Avoids issues when filtering the deprecated `query_string` hook.
|
135 |
function amp_force_query_var_value( $query_vars ) {
|
136 |
+
if ( isset( $query_vars[ amp_get_slug() ] ) && '' === $query_vars[ amp_get_slug() ] ) {
|
137 |
+
$query_vars[ amp_get_slug() ] = 1;
|
138 |
}
|
139 |
return $query_vars;
|
140 |
}
|
141 |
|
142 |
+
/**
|
143 |
+
* Conditionally add AMP actions or render the 'paired mode' template(s).
|
144 |
+
*
|
145 |
+
* If the request is for an AMP page and this is in 'canonical mode,' redirect to the non-AMP page.
|
146 |
+
* It won't need this plugin's template system, nor the frontend actions like the 'rel' link.
|
147 |
+
*
|
148 |
+
* @global WP_Query $wp_query
|
149 |
+
* @since 0.2
|
150 |
+
* @return void
|
151 |
+
*/
|
152 |
+
function amp_maybe_add_actions() {
|
153 |
+
|
154 |
+
// Short-circuit when theme supports AMP, as everything is handled by AMP_Theme_Support.
|
155 |
+
if ( current_theme_supports( 'amp' ) ) {
|
156 |
+
return;
|
157 |
+
}
|
158 |
+
|
159 |
+
// The remaining logic here is for paired mode running in themes that don't support AMP, the template system in AMP<=0.6.
|
160 |
+
global $wp_query;
|
161 |
+
if ( ! ( is_singular() || $wp_query->is_posts_page ) || is_feed() ) {
|
162 |
+
return;
|
163 |
+
}
|
164 |
+
|
165 |
+
$is_amp_endpoint = is_amp_endpoint();
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Queried post object.
|
169 |
+
*
|
170 |
+
* @var WP_Post $post
|
171 |
+
*/
|
172 |
+
$post = get_queried_object();
|
173 |
+
if ( ! post_supports_amp( $post ) ) {
|
174 |
+
if ( $is_amp_endpoint ) {
|
175 |
+
wp_safe_redirect( get_permalink( $post->ID ), 302 ); // Temporary redirect because AMP may be supported in future.
|
176 |
+
exit;
|
177 |
+
}
|
178 |
+
return;
|
179 |
+
}
|
180 |
+
|
181 |
+
if ( $is_amp_endpoint ) {
|
182 |
+
amp_prepare_render();
|
183 |
+
} else {
|
184 |
+
amp_add_frontend_actions();
|
185 |
+
}
|
186 |
+
}
|
187 |
+
|
188 |
/**
|
189 |
* Fix up WP_Query for front page when amp query var is present.
|
190 |
*
|
203 |
$query->is_home()
|
204 |
&&
|
205 |
// Is AMP endpoint.
|
206 |
+
false !== $query->get( amp_get_slug(), false )
|
207 |
&&
|
208 |
// Is query not yet fixed uo up to be front page.
|
209 |
! $query->is_front_page()
|
215 |
get_option( 'page_on_front' )
|
216 |
&&
|
217 |
// See line in WP_Query::parse_query() at <https://github.com/WordPress/wordpress-develop/blob/0baa8ae/src/wp-includes/class-wp-query.php#L961>.
|
218 |
+
0 === count( array_diff( array_keys( wp_parse_args( $query->query ) ), array( amp_get_slug(), 'preview', 'page', 'paged', 'cpage' ) ) )
|
219 |
);
|
220 |
if ( $is_front_page_query ) {
|
221 |
$query->is_home = false;
|
226 |
}
|
227 |
|
228 |
/**
|
229 |
+
* Whether this is in 'canonical mode.'
|
230 |
*
|
231 |
+
* Themes can register support for this with `add_theme_support( 'amp' )`.
|
232 |
+
* Then, this will change the plugin from 'paired mode,' and it won't use its own templates.
|
233 |
+
* Nor output frontend markup like the 'rel' link. If the theme registers support for AMP with:
|
234 |
+
* `add_theme_support( 'amp', array( 'template_dir' => 'my-amp-templates' ) )`
|
235 |
+
* it will retain 'paired mode.
|
236 |
*
|
237 |
+
* @return boolean Whether this is in AMP 'canonical mode'.
|
238 |
*/
|
239 |
+
function amp_is_canonical() {
|
240 |
+
$support = get_theme_support( 'amp' );
|
241 |
+
if ( true === $support ) {
|
242 |
+
return true;
|
243 |
}
|
244 |
+
if ( is_array( $support ) ) {
|
245 |
+
$args = array_shift( $support );
|
246 |
+
if ( empty( $args['template_dir'] ) ) {
|
247 |
+
return true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
249 |
}
|
250 |
+
return false;
|
251 |
}
|
252 |
|
253 |
function amp_load_classes() {
|
305 |
* which is not ideal for any code that expects to run in an AMP context.
|
306 |
* Let's force the value to be true while we render AMP.
|
307 |
*/
|
308 |
+
$was_set = isset( $wp_query->query_vars[ amp_get_slug() ] );
|
309 |
if ( ! $was_set ) {
|
310 |
+
$wp_query->query_vars[ amp_get_slug() ] = true;
|
311 |
+
}
|
312 |
+
|
313 |
+
// Prevent New Relic from causing invalid AMP responses due the NREUM script it injects after the meta charset.
|
314 |
+
if ( extension_loaded( 'newrelic' ) ) {
|
315 |
+
newrelic_disable_autorum();
|
316 |
}
|
317 |
|
318 |
/**
|
329 |
$template->load();
|
330 |
|
331 |
if ( ! $was_set ) {
|
332 |
+
unset( $wp_query->query_vars[ amp_get_slug() ] );
|
333 |
}
|
334 |
}
|
335 |
|
336 |
/**
|
337 |
* Bootstraps the AMP customizer.
|
338 |
*
|
339 |
+
* Uses the priority of 12 for the 'after_setup_theme' action.
|
340 |
+
* Many themes run `add_theme_support()` on the 'after_setup_theme' hook, at the default priority of 10.
|
341 |
+
* And that function's documentation suggests adding it to that action.
|
342 |
+
* So this enables themes to `add_theme_support( 'amp' )`.
|
343 |
+
* And `amp_init_customizer()` will be able to recognize theme support by calling `amp_is_canonical()`.
|
344 |
+
*
|
345 |
* @since 0.4
|
346 |
*/
|
347 |
function _amp_bootstrap_customizer() {
|
348 |
+
add_action( 'after_setup_theme', 'amp_init_customizer', 12 );
|
349 |
}
|
350 |
add_action( 'plugins_loaded', '_amp_bootstrap_customizer', 9 ); // Should be hooked before priority 10 on 'plugins_loaded' to properly unhook core panels.
|
351 |
|
360 |
function amp_redirect_old_slug_to_new_url( $link ) {
|
361 |
|
362 |
if ( is_amp_endpoint() ) {
|
363 |
+
$link = trailingslashit( trailingslashit( $link ) . amp_get_slug() );
|
364 |
}
|
365 |
|
366 |
return $link;
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.amp-wp-enforced-sizes {
|
2 |
+
/** Our sizes fallback is 100vw, and we have a padding on the container; the max-width here prevents the element from overflowing. **/
|
3 |
+
max-width: 100%;
|
4 |
+
margin: 0 auto;
|
5 |
+
}
|
6 |
+
|
7 |
+
.amp-wp-unknown-size img {
|
8 |
+
/** Worst case scenario when we can't figure out dimensions for an image. **/
|
9 |
+
/** Force the image into a box of fixed dimensions and use object-fit to scale. **/
|
10 |
+
object-fit: contain;
|
11 |
+
}
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* For the custom AMP implementation of the 'playlist' shortcode.
|
3 |
+
*/
|
4 |
+
.wp-playlist .wp-playlist-current-item img {
|
5 |
+
margin-right: 0;
|
6 |
+
}
|
7 |
+
|
8 |
+
.wp-playlist .wp-playlist-current-item amp-img {
|
9 |
+
float: left;
|
10 |
+
margin-right: 10px;
|
11 |
+
}
|
12 |
+
|
13 |
+
.wp-playlist audio {
|
14 |
+
display: block;
|
15 |
+
}
|
16 |
+
|
17 |
+
.wp-playlist .amp-carousel-button {
|
18 |
+
visibility: hidden;
|
19 |
+
}
|
@@ -18,6 +18,7 @@ var ampPostMetaBox = ( function( $ ) {
|
|
18 |
* @since 0.6
|
19 |
*/
|
20 |
data: {
|
|
|
21 |
previewLink: '',
|
22 |
enabled: true, // Overridden by post_supports_amp( $post ).
|
23 |
canSupport: true, // Overridden by count( AMP_Post_Type_Support::get_support_errors( $post ) ) === 0.
|
@@ -60,7 +61,7 @@ var ampPostMetaBox = ( function( $ ) {
|
|
60 |
component.data = data;
|
61 |
$( document ).ready( function() {
|
62 |
component.statusRadioInputs = $( '[name="' + component.data.statusInputName + '"]' );
|
63 |
-
if ( component.data.enabled ) {
|
64 |
component.addPreviewButton();
|
65 |
}
|
66 |
component.listen();
|
@@ -160,6 +161,8 @@ var ampPostMetaBox = ( function( $ ) {
|
|
160 |
editAmpStatus.fadeToggle( component.toggleSpeed, function() {
|
161 |
if ( editAmpStatus.is( ':visible' ) ) {
|
162 |
editAmpStatus.focus();
|
|
|
|
|
163 |
}
|
164 |
} );
|
165 |
$container.slideToggle( component.toggleSpeed );
|
18 |
* @since 0.6
|
19 |
*/
|
20 |
data: {
|
21 |
+
canonical: false, // Overridden by amp_is_canonical().
|
22 |
previewLink: '',
|
23 |
enabled: true, // Overridden by post_supports_amp( $post ).
|
24 |
canSupport: true, // Overridden by count( AMP_Post_Type_Support::get_support_errors( $post ) ) === 0.
|
61 |
component.data = data;
|
62 |
$( document ).ready( function() {
|
63 |
component.statusRadioInputs = $( '[name="' + component.data.statusInputName + '"]' );
|
64 |
+
if ( component.data.enabled && ! component.data.canonical ) {
|
65 |
component.addPreviewButton();
|
66 |
}
|
67 |
component.listen();
|
161 |
editAmpStatus.fadeToggle( component.toggleSpeed, function() {
|
162 |
if ( editAmpStatus.is( ':visible' ) ) {
|
163 |
editAmpStatus.focus();
|
164 |
+
} else {
|
165 |
+
$container.find( 'input[type="radio"]' ).first().focus();
|
166 |
}
|
167 |
} );
|
168 |
$container.slideToggle( component.toggleSpeed );
|
@@ -134,7 +134,7 @@ class AMP_Template_Customizer {
|
|
134 |
|
135 |
wp_add_inline_script( 'amp-customize-controls', sprintf( 'ampCustomizeControls.boot( %s );',
|
136 |
wp_json_encode( array(
|
137 |
-
'queryVar' =>
|
138 |
'panelId' => self::PANEL_ID,
|
139 |
'ampUrl' => amp_admin_get_preview_permalink(),
|
140 |
'l10n' => array(
|
134 |
|
135 |
wp_add_inline_script( 'amp-customize-controls', sprintf( 'ampCustomizeControls.boot( %s );',
|
136 |
wp_json_encode( array(
|
137 |
+
'queryVar' => amp_get_slug(),
|
138 |
'panelId' => self::PANEL_ID,
|
139 |
'ampUrl' => amp_admin_get_preview_permalink(),
|
140 |
'l10n' => array(
|
@@ -121,6 +121,8 @@ class AMP_Post_Meta_Box {
|
|
121 |
isset( $screen->base )
|
122 |
&&
|
123 |
'post' === $screen->base
|
|
|
|
|
124 |
);
|
125 |
if ( ! $validate ) {
|
126 |
return;
|
@@ -143,7 +145,8 @@ class AMP_Post_Meta_Box {
|
|
143 |
);
|
144 |
wp_add_inline_script( self::ASSETS_HANDLE, sprintf( 'ampPostMetaBox.boot( %s );',
|
145 |
wp_json_encode( array(
|
146 |
-
'previewLink' => esc_url_raw( add_query_arg(
|
|
|
147 |
'enabled' => post_supports_amp( $post ),
|
148 |
'canSupport' => count( AMP_Post_Type_Support::get_support_errors( $post ) ) === 0,
|
149 |
'statusInputName' => self::STATUS_INPUT_NAME,
|
@@ -164,7 +167,11 @@ class AMP_Post_Meta_Box {
|
|
164 |
$verify = (
|
165 |
isset( $post->ID )
|
166 |
&&
|
|
|
|
|
167 |
current_user_can( 'edit_post', $post->ID )
|
|
|
|
|
168 |
);
|
169 |
|
170 |
if ( true !== $verify ) {
|
@@ -230,7 +237,7 @@ class AMP_Post_Meta_Box {
|
|
230 |
);
|
231 |
|
232 |
if ( $is_amp ) {
|
233 |
-
$link = add_query_arg(
|
234 |
}
|
235 |
|
236 |
return $link;
|
121 |
isset( $screen->base )
|
122 |
&&
|
123 |
'post' === $screen->base
|
124 |
+
&&
|
125 |
+
is_post_type_viewable( $post->post_type )
|
126 |
);
|
127 |
if ( ! $validate ) {
|
128 |
return;
|
145 |
);
|
146 |
wp_add_inline_script( self::ASSETS_HANDLE, sprintf( 'ampPostMetaBox.boot( %s );',
|
147 |
wp_json_encode( array(
|
148 |
+
'previewLink' => esc_url_raw( add_query_arg( amp_get_slug(), '', get_preview_post_link( $post ) ) ),
|
149 |
+
'canonical' => amp_is_canonical(),
|
150 |
'enabled' => post_supports_amp( $post ),
|
151 |
'canSupport' => count( AMP_Post_Type_Support::get_support_errors( $post ) ) === 0,
|
152 |
'statusInputName' => self::STATUS_INPUT_NAME,
|
167 |
$verify = (
|
168 |
isset( $post->ID )
|
169 |
&&
|
170 |
+
is_post_type_viewable( $post->post_type )
|
171 |
+
&&
|
172 |
current_user_can( 'edit_post', $post->ID )
|
173 |
+
&&
|
174 |
+
! amp_is_canonical()
|
175 |
);
|
176 |
|
177 |
if ( true !== $verify ) {
|
237 |
);
|
238 |
|
239 |
if ( $is_amp ) {
|
240 |
+
$link = add_query_arg( amp_get_slug(), true, $link );
|
241 |
}
|
242 |
|
243 |
return $link;
|
@@ -15,8 +15,16 @@ define( 'AMP_CUSTOMIZER_QUERY_VAR', 'customize_amp' );
|
|
15 |
|
16 |
/**
|
17 |
* Sets up the AMP template editor for the Customizer.
|
|
|
|
|
|
|
|
|
18 |
*/
|
19 |
function amp_init_customizer() {
|
|
|
|
|
|
|
|
|
20 |
// Fire up the AMP Customizer.
|
21 |
add_action( 'customize_register', array( 'AMP_Template_Customizer', 'init' ), 500 );
|
22 |
|
@@ -40,7 +48,7 @@ function amp_admin_get_preview_permalink() {
|
|
40 |
*/
|
41 |
$post_type = (string) apply_filters( 'amp_customizer_post_type', 'post' );
|
42 |
|
43 |
-
if ( ! post_type_supports( $post_type,
|
44 |
return null;
|
45 |
}
|
46 |
|
@@ -107,23 +115,29 @@ function amp_add_options_menu() {
|
|
107 |
/**
|
108 |
* Add custom analytics.
|
109 |
*
|
|
|
|
|
|
|
|
|
|
|
110 |
* @param array $analytics Analytics.
|
111 |
* @return array Analytics.
|
112 |
*/
|
113 |
-
function amp_add_custom_analytics( $analytics ) {
|
114 |
-
$
|
115 |
-
|
116 |
-
if ( ! $analytics_entries ) {
|
117 |
-
return $analytics;
|
118 |
-
}
|
119 |
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
|
|
|
|
|
|
|
|
|
|
127 |
|
128 |
return $analytics;
|
129 |
}
|
15 |
|
16 |
/**
|
17 |
* Sets up the AMP template editor for the Customizer.
|
18 |
+
*
|
19 |
+
* If this is in AMP canonical mode, exit.
|
20 |
+
* There's no need for the 'AMP' Customizer panel,
|
21 |
+
* And this does not need to toggle between the AMP and normal display.
|
22 |
*/
|
23 |
function amp_init_customizer() {
|
24 |
+
if ( amp_is_canonical() ) {
|
25 |
+
return;
|
26 |
+
}
|
27 |
+
|
28 |
// Fire up the AMP Customizer.
|
29 |
add_action( 'customize_register', array( 'AMP_Template_Customizer', 'init' ), 500 );
|
30 |
|
48 |
*/
|
49 |
$post_type = (string) apply_filters( 'amp_customizer_post_type', 'post' );
|
50 |
|
51 |
+
if ( ! post_type_supports( $post_type, amp_get_slug() ) ) {
|
52 |
return null;
|
53 |
}
|
54 |
|
115 |
/**
|
116 |
* Add custom analytics.
|
117 |
*
|
118 |
+
* This is currently only used for legacy AMP post templates.
|
119 |
+
*
|
120 |
+
* @since 0.5
|
121 |
+
* @see amp_get_analytics()
|
122 |
+
*
|
123 |
* @param array $analytics Analytics.
|
124 |
* @return array Analytics.
|
125 |
*/
|
126 |
+
function amp_add_custom_analytics( $analytics = array() ) {
|
127 |
+
$analytics = amp_get_analytics( $analytics );
|
|
|
|
|
|
|
|
|
128 |
|
129 |
+
/**
|
130 |
+
* Add amp-analytics tags.
|
131 |
+
*
|
132 |
+
* This filter allows you to easily insert any amp-analytics tags without needing much heavy lifting.
|
133 |
+
* This filter should be used to alter entries for legacy AMP templates.
|
134 |
+
*
|
135 |
+
* @since 0.4
|
136 |
+
*
|
137 |
+
* @param array $analytics An associative array of the analytics entries we want to output. Each array entry must have a unique key, and the value should be an array with the following keys: `type`, `attributes`, `script_data`. See readme for more details.
|
138 |
+
* @param WP_Post $post The current post.
|
139 |
+
*/
|
140 |
+
$analytics = apply_filters( 'amp_post_template_analytics', $analytics, get_queried_object() );
|
141 |
|
142 |
return $analytics;
|
143 |
}
|
@@ -10,6 +10,8 @@ add_action( 'wp_head', 'amp_frontend_add_canonical' );
|
|
10 |
/**
|
11 |
* Add amphtml link to frontend.
|
12 |
*
|
|
|
|
|
13 |
* @since 0.2
|
14 |
*/
|
15 |
function amp_frontend_add_canonical() {
|
@@ -17,12 +19,22 @@ function amp_frontend_add_canonical() {
|
|
17 |
/**
|
18 |
* Filters whether to show the amphtml link on the frontend.
|
19 |
*
|
|
|
20 |
* @since 0.2
|
21 |
*/
|
22 |
if ( false === apply_filters( 'amp_frontend_show_canonical', true ) ) {
|
23 |
return;
|
24 |
}
|
25 |
|
26 |
-
$amp_url =
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
}
|
10 |
/**
|
11 |
* Add amphtml link to frontend.
|
12 |
*
|
13 |
+
* @todo This function's name is incorrect. It's not about adding a canonical link but adding the amphtml link.
|
14 |
+
*
|
15 |
* @since 0.2
|
16 |
*/
|
17 |
function amp_frontend_add_canonical() {
|
19 |
/**
|
20 |
* Filters whether to show the amphtml link on the frontend.
|
21 |
*
|
22 |
+
* @todo This filter's name is incorrect. It's not about adding a canonical link but adding the amphtml link.
|
23 |
* @since 0.2
|
24 |
*/
|
25 |
if ( false === apply_filters( 'amp_frontend_show_canonical', true ) ) {
|
26 |
return;
|
27 |
}
|
28 |
|
29 |
+
$amp_url = null;
|
30 |
+
if ( is_singular() ) {
|
31 |
+
$amp_url = amp_get_permalink( get_queried_object_id() );
|
32 |
+
} elseif ( isset( $_SERVER['REQUEST_URI'] ) ) {
|
33 |
+
$host_url = preg_replace( '#(^https?://[^/]+)/.*#', '$1', home_url( '/' ) );
|
34 |
+
$self_url = esc_url_raw( $host_url . wp_unslash( $_SERVER['REQUEST_URI'] ) );
|
35 |
+
$amp_url = add_query_arg( amp_get_slug(), '', $self_url );
|
36 |
+
}
|
37 |
+
if ( $amp_url ) {
|
38 |
+
printf( '<link rel="amphtml" href="%s">', esc_url( $amp_url ) );
|
39 |
+
}
|
40 |
}
|
@@ -5,6 +5,36 @@
|
|
5 |
* @package AMP
|
6 |
*/
|
7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
/**
|
9 |
* Retrieves the full AMP-specific permalink for the given post ID.
|
10 |
*
|
@@ -32,12 +62,16 @@ function amp_get_permalink( $post_id ) {
|
|
32 |
return $pre_url;
|
33 |
}
|
34 |
|
35 |
-
|
36 |
-
|
37 |
-
if ( empty( $structure ) || ! empty( $parsed_url['query'] ) || is_post_type_hierarchical( get_post_type( $post_id ) ) ) {
|
38 |
-
$amp_url = add_query_arg( AMP_QUERY_VAR, '', get_permalink( $post_id ) );
|
39 |
} else {
|
40 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
}
|
42 |
|
43 |
/**
|
@@ -51,6 +85,25 @@ function amp_get_permalink( $post_id ) {
|
|
51 |
return apply_filters( 'amp_get_permalink', $amp_url, $post_id );
|
52 |
}
|
53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
/**
|
55 |
* Determine whether a given post supports AMP.
|
56 |
*
|
@@ -63,6 +116,10 @@ function amp_get_permalink( $post_id ) {
|
|
63 |
* @return bool Whether the post supports AMP.
|
64 |
*/
|
65 |
function post_supports_amp( $post ) {
|
|
|
|
|
|
|
|
|
66 |
$errors = AMP_Post_Type_Support::get_support_errors( $post );
|
67 |
|
68 |
// Return false if an error is found.
|
@@ -114,11 +171,19 @@ function post_supports_amp( $post ) {
|
|
114 |
* @return bool Whether it is the AMP endpoint.
|
115 |
*/
|
116 |
function is_amp_endpoint() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
if ( 0 === did_action( 'parse_query' ) ) {
|
118 |
_doing_it_wrong( __FUNCTION__, sprintf( esc_html__( "is_amp_endpoint() was called before the 'parse_query' hook was called. This function will always return 'false' before the 'parse_query' hook is called.", 'amp' ) ), '0.4.2' );
|
119 |
}
|
120 |
|
121 |
-
return false !== get_query_var(
|
122 |
}
|
123 |
|
124 |
/**
|
@@ -130,3 +195,474 @@ function is_amp_endpoint() {
|
|
130 |
function amp_get_asset_url( $file ) {
|
131 |
return plugins_url( sprintf( 'assets/%s', $file ), AMP__FILE__ );
|
132 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
* @package AMP
|
6 |
*/
|
7 |
|
8 |
+
/**
|
9 |
+
* Get the slug used in AMP for the query var, endpoint, and post type support.
|
10 |
+
*
|
11 |
+
* The return value can be overridden by previously defining a AMP_QUERY_VAR
|
12 |
+
* constant or by adding a 'amp_query_var' filter, but *warning* this ability
|
13 |
+
* may be deprecated in the future. Normally the slug should be just 'amp'.
|
14 |
+
*
|
15 |
+
* @since 0.7
|
16 |
+
* @return string Slug used for query var, endpoint, and post type support.
|
17 |
+
*/
|
18 |
+
function amp_get_slug() {
|
19 |
+
if ( defined( 'AMP_QUERY_VAR' ) ) {
|
20 |
+
return AMP_QUERY_VAR;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Filter the AMP query variable.
|
25 |
+
*
|
26 |
+
* Warning: This filter may become deprecated.
|
27 |
+
*
|
28 |
+
* @since 0.3.2
|
29 |
+
* @param string $query_var The AMP query variable.
|
30 |
+
*/
|
31 |
+
$query_var = apply_filters( 'amp_query_var', 'amp' );
|
32 |
+
|
33 |
+
define( 'AMP_QUERY_VAR', $query_var );
|
34 |
+
|
35 |
+
return $query_var;
|
36 |
+
}
|
37 |
+
|
38 |
/**
|
39 |
* Retrieves the full AMP-specific permalink for the given post ID.
|
40 |
*
|
62 |
return $pre_url;
|
63 |
}
|
64 |
|
65 |
+
if ( amp_is_canonical() ) {
|
66 |
+
$amp_url = get_permalink( $post_id );
|
|
|
|
|
67 |
} else {
|
68 |
+
$parsed_url = wp_parse_url( get_permalink( $post_id ) );
|
69 |
+
$structure = get_option( 'permalink_structure' );
|
70 |
+
if ( empty( $structure ) || ! empty( $parsed_url['query'] ) || is_post_type_hierarchical( get_post_type( $post_id ) ) ) {
|
71 |
+
$amp_url = add_query_arg( amp_get_slug(), '', get_permalink( $post_id ) );
|
72 |
+
} else {
|
73 |
+
$amp_url = trailingslashit( get_permalink( $post_id ) ) . user_trailingslashit( amp_get_slug(), 'single_amp' );
|
74 |
+
}
|
75 |
}
|
76 |
|
77 |
/**
|
85 |
return apply_filters( 'amp_get_permalink', $amp_url, $post_id );
|
86 |
}
|
87 |
|
88 |
+
/**
|
89 |
+
* Remove the AMP endpoint (and query var) from a given URL.
|
90 |
+
*
|
91 |
+
* @since 0.7
|
92 |
+
*
|
93 |
+
* @param string $url URL.
|
94 |
+
* @return string URL with AMP stripped.
|
95 |
+
*/
|
96 |
+
function amp_remove_endpoint( $url ) {
|
97 |
+
|
98 |
+
// Strip endpoint.
|
99 |
+
$url = preg_replace( ':/' . preg_quote( amp_get_slug(), ':' ) . '(?=/?(\?|#|$)):', '', $url );
|
100 |
+
|
101 |
+
// Strip query var.
|
102 |
+
$url = remove_query_arg( amp_get_slug(), $url );
|
103 |
+
|
104 |
+
return $url;
|
105 |
+
}
|
106 |
+
|
107 |
/**
|
108 |
* Determine whether a given post supports AMP.
|
109 |
*
|
116 |
* @return bool Whether the post supports AMP.
|
117 |
*/
|
118 |
function post_supports_amp( $post ) {
|
119 |
+
if ( amp_is_canonical() ) {
|
120 |
+
return true;
|
121 |
+
}
|
122 |
+
|
123 |
$errors = AMP_Post_Type_Support::get_support_errors( $post );
|
124 |
|
125 |
// Return false if an error is found.
|
171 |
* @return bool Whether it is the AMP endpoint.
|
172 |
*/
|
173 |
function is_amp_endpoint() {
|
174 |
+
if ( is_admin() || is_feed() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
|
175 |
+
return false;
|
176 |
+
}
|
177 |
+
|
178 |
+
if ( amp_is_canonical() ) {
|
179 |
+
return true;
|
180 |
+
}
|
181 |
+
|
182 |
if ( 0 === did_action( 'parse_query' ) ) {
|
183 |
_doing_it_wrong( __FUNCTION__, sprintf( esc_html__( "is_amp_endpoint() was called before the 'parse_query' hook was called. This function will always return 'false' before the 'parse_query' hook is called.", 'amp' ) ), '0.4.2' );
|
184 |
}
|
185 |
|
186 |
+
return false !== get_query_var( amp_get_slug(), false );
|
187 |
}
|
188 |
|
189 |
/**
|
195 |
function amp_get_asset_url( $file ) {
|
196 |
return plugins_url( sprintf( 'assets/%s', $file ), AMP__FILE__ );
|
197 |
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Get AMP boilerplate code.
|
201 |
+
*
|
202 |
+
* @since 0.7
|
203 |
+
* @link https://www.ampproject.org/docs/reference/spec#boilerplate
|
204 |
+
*
|
205 |
+
* @return string Boilerplate code.
|
206 |
+
*/
|
207 |
+
function amp_get_boilerplate_code() {
|
208 |
+
return '<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>'
|
209 |
+
. '<noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>';
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Register default scripts for AMP components.
|
214 |
+
*
|
215 |
+
* @param WP_Scripts $wp_scripts Scripts.
|
216 |
+
*/
|
217 |
+
function amp_register_default_scripts( $wp_scripts ) {
|
218 |
+
|
219 |
+
// AMP Runtime.
|
220 |
+
$handle = 'amp-runtime';
|
221 |
+
$wp_scripts->add(
|
222 |
+
$handle,
|
223 |
+
'https://cdn.ampproject.org/v0.js',
|
224 |
+
array(),
|
225 |
+
null
|
226 |
+
);
|
227 |
+
$wp_scripts->add_data( $handle, 'amp_script_attributes', array(
|
228 |
+
'async' => true,
|
229 |
+
) );
|
230 |
+
|
231 |
+
// Shadow AMP API.
|
232 |
+
$handle = 'amp-shadow';
|
233 |
+
$wp_scripts->add(
|
234 |
+
$handle,
|
235 |
+
'https://cdn.ampproject.org/shadow-v0.js',
|
236 |
+
array(),
|
237 |
+
null
|
238 |
+
);
|
239 |
+
$wp_scripts->add_data( $handle, 'amp_script_attributes', array(
|
240 |
+
'async' => true,
|
241 |
+
) );
|
242 |
+
|
243 |
+
// Get all AMP components as defined in the spec.
|
244 |
+
$extensions = array();
|
245 |
+
foreach ( AMP_Allowed_Tags_Generated::get_allowed_tags() as $allowed_tag ) {
|
246 |
+
foreach ( $allowed_tag as $rule_spec ) {
|
247 |
+
if ( ! empty( $rule_spec[ AMP_Rule_Spec::TAG_SPEC ]['requires_extension'] ) ) {
|
248 |
+
$extensions = array_merge(
|
249 |
+
$extensions,
|
250 |
+
$rule_spec[ AMP_Rule_Spec::TAG_SPEC ]['requires_extension']
|
251 |
+
);
|
252 |
+
}
|
253 |
+
}
|
254 |
+
}
|
255 |
+
$extensions = array_unique( $extensions );
|
256 |
+
|
257 |
+
foreach ( $extensions as $extension ) {
|
258 |
+
$src = sprintf(
|
259 |
+
'https://cdn.ampproject.org/v0/%s-%s.js',
|
260 |
+
$extension,
|
261 |
+
'latest'
|
262 |
+
);
|
263 |
+
|
264 |
+
$wp_scripts->add(
|
265 |
+
$extension,
|
266 |
+
$src,
|
267 |
+
array( 'amp-runtime' ),
|
268 |
+
null
|
269 |
+
);
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
/**
|
274 |
+
* Add AMP script attributes to enqueued scripts.
|
275 |
+
*
|
276 |
+
* @link https://core.trac.wordpress.org/ticket/12009
|
277 |
+
* @since 0.7
|
278 |
+
*
|
279 |
+
* @param string $tag The script tag.
|
280 |
+
* @param string $handle The script handle.
|
281 |
+
* @return string Script loader tag.
|
282 |
+
*/
|
283 |
+
function amp_filter_script_loader_tag( $tag, $handle ) {
|
284 |
+
$prefix = 'https://cdn.ampproject.org/';
|
285 |
+
$src = wp_scripts()->registered[ $handle ]->src;
|
286 |
+
if ( 0 !== strpos( $src, $prefix ) ) {
|
287 |
+
return $tag;
|
288 |
+
}
|
289 |
+
|
290 |
+
/*
|
291 |
+
* All scripts from AMP CDN should be loaded async.
|
292 |
+
* See <https://www.ampproject.org/docs/integration/pwa-amp/amp-in-pwa#include-"shadow-amp"-in-your-progressive-web-app>.
|
293 |
+
*/
|
294 |
+
$attributes = array(
|
295 |
+
'async' => true,
|
296 |
+
);
|
297 |
+
|
298 |
+
// Add custom-template and custom-element attributes. All component scripts look like https://cdn.ampproject.org/v0/:name-:version.js.
|
299 |
+
if ( 'v0' === strtok( substr( $src, strlen( $prefix ) ), '/' ) ) {
|
300 |
+
/*
|
301 |
+
* Per the spec, "Most extensions are custom-elements." In fact, there is only one custom template. So we hard-code it here.
|
302 |
+
*
|
303 |
+
* @link https://github.com/ampproject/amphtml/blob/cd685d4e62153557519553ffa2183aedf8c93d62/validator/validator.proto#L326-L328
|
304 |
+
* @link https://github.com/ampproject/amphtml/blob/cd685d4e62153557519553ffa2183aedf8c93d62/extensions/amp-mustache/validator-amp-mustache.protoascii#L27
|
305 |
+
*/
|
306 |
+
if ( 'amp-mustache' === $handle ) {
|
307 |
+
$attributes['custom-template'] = $handle;
|
308 |
+
} else {
|
309 |
+
$attributes['custom-element'] = $handle;
|
310 |
+
}
|
311 |
+
}
|
312 |
+
|
313 |
+
// Add each attribute (if it hasn't already been added).
|
314 |
+
foreach ( $attributes as $key => $value ) {
|
315 |
+
if ( ! preg_match( ":\s$key(=|>|\s):", $tag ) ) {
|
316 |
+
if ( true === $value ) {
|
317 |
+
$attribute_string = sprintf( ' %s', esc_attr( $key ) );
|
318 |
+
} else {
|
319 |
+
$attribute_string = sprintf( ' %s="%s"', esc_attr( $key ), esc_attr( $value ) );
|
320 |
+
}
|
321 |
+
$tag = preg_replace(
|
322 |
+
':(?=></script>):',
|
323 |
+
$attribute_string,
|
324 |
+
$tag,
|
325 |
+
1
|
326 |
+
);
|
327 |
+
}
|
328 |
+
}
|
329 |
+
|
330 |
+
return $tag;
|
331 |
+
}
|
332 |
+
|
333 |
+
/**
|
334 |
+
* Retrieve analytics data added in backend.
|
335 |
+
*
|
336 |
+
* @since 0.7
|
337 |
+
*
|
338 |
+
* @param array $analytics Analytics entries.
|
339 |
+
* @return array Analytics.
|
340 |
+
*/
|
341 |
+
function amp_get_analytics( $analytics = array() ) {
|
342 |
+
$analytics_entries = AMP_Options_Manager::get_option( 'analytics', array() );
|
343 |
+
|
344 |
+
/**
|
345 |
+
* Add amp-analytics tags.
|
346 |
+
*
|
347 |
+
* This filter allows you to easily insert any amp-analytics tags without needing much heavy lifting.
|
348 |
+
* This filter should be used to alter entries for paired mode.
|
349 |
+
*
|
350 |
+
* @since 0.7
|
351 |
+
*
|
352 |
+
* @param array $analytics_entries An associative array of the analytics entries we want to output. Each array entry must have a unique key, and the value should be an array with the following keys: `type`, `attributes`, `script_data`. See readme for more details.
|
353 |
+
*/
|
354 |
+
$analytics_entries = apply_filters( 'amp_analytics_entries', $analytics_entries );
|
355 |
+
|
356 |
+
if ( ! $analytics_entries ) {
|
357 |
+
return $analytics;
|
358 |
+
}
|
359 |
+
|
360 |
+
foreach ( $analytics_entries as $entry_id => $entry ) {
|
361 |
+
$analytics[ $entry_id ] = array(
|
362 |
+
'type' => $entry['type'],
|
363 |
+
'attributes' => array(),
|
364 |
+
'config_data' => json_decode( $entry['config'] ),
|
365 |
+
);
|
366 |
+
}
|
367 |
+
|
368 |
+
return $analytics;
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* Print analytics data.
|
373 |
+
*
|
374 |
+
* @since 0.7
|
375 |
+
*
|
376 |
+
* @param array|string $analytics Analytics entries, or empty string when called via wp_footer action.
|
377 |
+
*/
|
378 |
+
function amp_print_analytics( $analytics ) {
|
379 |
+
if ( '' === $analytics ) {
|
380 |
+
$analytics = array();
|
381 |
+
}
|
382 |
+
$analytics_entries = amp_get_analytics( $analytics );
|
383 |
+
|
384 |
+
if ( empty( $analytics_entries ) ) {
|
385 |
+
return;
|
386 |
+
}
|
387 |
+
|
388 |
+
// Can enter multiple configs within backend.
|
389 |
+
foreach ( $analytics_entries as $id => $analytics_entry ) {
|
390 |
+
if ( ! isset( $analytics_entry['type'], $analytics_entry['attributes'], $analytics_entry['config_data'] ) ) {
|
391 |
+
/* translators: %1$s is analytics entry ID, %2$s is actual entry keys. */
|
392 |
+
_doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'Analytics entry for %1$s is missing one of the following keys: `type`, `attributes`, or `config_data` (array keys: %2$s)', 'amp' ), esc_html( $id ), esc_html( implode( ', ', array_keys( $analytics_entry ) ) ) ), '0.3.2' );
|
393 |
+
continue;
|
394 |
+
}
|
395 |
+
$script_element = AMP_HTML_Utils::build_tag( 'script', array(
|
396 |
+
'type' => 'application/json',
|
397 |
+
), wp_json_encode( $analytics_entry['config_data'] ) );
|
398 |
+
|
399 |
+
$amp_analytics_attr = array_merge( array(
|
400 |
+
'id' => $id,
|
401 |
+
'type' => $analytics_entry['type'],
|
402 |
+
), $analytics_entry['attributes'] );
|
403 |
+
|
404 |
+
echo AMP_HTML_Utils::build_tag( 'amp-analytics', $amp_analytics_attr, $script_element ); // WPCS: XSS OK.
|
405 |
+
}
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* Get content embed handlers.
|
410 |
+
*
|
411 |
+
* @since 0.7
|
412 |
+
*
|
413 |
+
* @param WP_Post $post Post that the content belongs to. Deprecated when theme supports AMP, as embeds may apply
|
414 |
+
* to non-post data (e.g. Text widget).
|
415 |
+
* @return array Embed handlers.
|
416 |
+
*/
|
417 |
+
function amp_get_content_embed_handlers( $post = null ) {
|
418 |
+
if ( current_theme_supports( 'amp' ) && $post ) {
|
419 |
+
_deprecated_argument( __FUNCTION__, '0.7', esc_html__( 'The $post argument is deprecated when theme supports AMP.', 'amp' ) );
|
420 |
+
$post = null;
|
421 |
+
}
|
422 |
+
|
423 |
+
/**
|
424 |
+
* Filters the content embed handlers.
|
425 |
+
*
|
426 |
+
* @since 0.2
|
427 |
+
* @since 0.7 Deprecated $post parameter.
|
428 |
+
*
|
429 |
+
* @param array $handlers Handlers.
|
430 |
+
* @param WP_Post $post Post. Deprecated. It will be null when `amp_is_canonical()`.
|
431 |
+
*/
|
432 |
+
return apply_filters( 'amp_content_embed_handlers',
|
433 |
+
array(
|
434 |
+
'AMP_Twitter_Embed_Handler' => array(),
|
435 |
+
'AMP_YouTube_Embed_Handler' => array(),
|
436 |
+
'AMP_DailyMotion_Embed_Handler' => array(),
|
437 |
+
'AMP_Vimeo_Embed_Handler' => array(),
|
438 |
+
'AMP_SoundCloud_Embed_Handler' => array(),
|
439 |
+
'AMP_Instagram_Embed_Handler' => array(),
|
440 |
+
'AMP_Issuu_Embed_Handler' => array(),
|
441 |
+
'AMP_Meetup_Embed_Handler' => array(),
|
442 |
+
'AMP_Vine_Embed_Handler' => array(),
|
443 |
+
'AMP_Facebook_Embed_Handler' => array(),
|
444 |
+
'AMP_Pinterest_Embed_Handler' => array(),
|
445 |
+
'AMP_Playlist_Embed_Handler' => array(),
|
446 |
+
'AMP_Reddit_Embed_Handler' => array(),
|
447 |
+
'AMP_Tumblr_Embed_Handler' => array(),
|
448 |
+
'AMP_Gallery_Embed_Handler' => array(),
|
449 |
+
'WPCOM_AMP_Polldaddy_Embed' => array(),
|
450 |
+
),
|
451 |
+
$post
|
452 |
+
);
|
453 |
+
}
|
454 |
+
|
455 |
+
/**
|
456 |
+
* Get content sanitizers.
|
457 |
+
*
|
458 |
+
* @since 0.7
|
459 |
+
*
|
460 |
+
* @param WP_Post $post Post that the content belongs to. Deprecated when theme supports AMP, as sanitizers apply
|
461 |
+
* to non-post data (e.g. Text widget).
|
462 |
+
* @return array Embed handlers.
|
463 |
+
*/
|
464 |
+
function amp_get_content_sanitizers( $post = null ) {
|
465 |
+
if ( current_theme_supports( 'amp' ) && $post ) {
|
466 |
+
_deprecated_argument( __FUNCTION__, '0.7', esc_html__( 'The $post argument is deprecated when theme supports AMP.', 'amp' ) );
|
467 |
+
$post = null;
|
468 |
+
}
|
469 |
+
|
470 |
+
/**
|
471 |
+
* Filters the content sanitizers.
|
472 |
+
*
|
473 |
+
* @since 0.2
|
474 |
+
* @since 0.7 Deprecated $post parameter. It will be null when `amp_is_canonical()`.
|
475 |
+
*
|
476 |
+
* @param array $handlers Handlers.
|
477 |
+
* @param WP_Post $post Post. Deprecated.
|
478 |
+
*/
|
479 |
+
$sanitizers = apply_filters( 'amp_content_sanitizers',
|
480 |
+
array(
|
481 |
+
'AMP_Img_Sanitizer' => array(),
|
482 |
+
'AMP_Form_Sanitizer' => array(),
|
483 |
+
'AMP_Comments_Sanitizer' => array(),
|
484 |
+
'AMP_Video_Sanitizer' => array(),
|
485 |
+
'AMP_Audio_Sanitizer' => array(),
|
486 |
+
'AMP_Playbuzz_Sanitizer' => array(),
|
487 |
+
'AMP_Iframe_Sanitizer' => array(
|
488 |
+
'add_placeholder' => true,
|
489 |
+
),
|
490 |
+
'AMP_Style_Sanitizer' => array(),
|
491 |
+
'AMP_Tag_And_Attribute_Sanitizer' => array(), // Note: This whitelist sanitizer must come at the end to clean up any remaining issues the other sanitizers didn't catch.
|
492 |
+
),
|
493 |
+
$post
|
494 |
+
);
|
495 |
+
|
496 |
+
// Force style sanitizer and whitelist sanitizer to be at end.
|
497 |
+
foreach ( array( 'AMP_Style_Sanitizer', 'AMP_Tag_And_Attribute_Sanitizer' ) as $class_name ) {
|
498 |
+
if ( isset( $sanitizers[ $class_name ] ) ) {
|
499 |
+
$sanitizer = $sanitizers[ $class_name ];
|
500 |
+
unset( $sanitizers[ $class_name ] );
|
501 |
+
$sanitizers[ $class_name ] = $sanitizer;
|
502 |
+
}
|
503 |
+
}
|
504 |
+
|
505 |
+
return $sanitizers;
|
506 |
+
}
|
507 |
+
|
508 |
+
/**
|
509 |
+
* Grabs featured image or the first attached image for the post.
|
510 |
+
*
|
511 |
+
* @since 0.7 This originally was located in the private method AMP_Post_Template::get_post_image_metadata().
|
512 |
+
*
|
513 |
+
* @param WP_Post|int $post Post or post ID.
|
514 |
+
* @return array|false $post_image_meta Post image metadata, or false if not found.
|
515 |
+
*/
|
516 |
+
function amp_get_post_image_metadata( $post = null ) {
|
517 |
+
$post = get_post( $post );
|
518 |
+
if ( ! $post ) {
|
519 |
+
return false;
|
520 |
+
}
|
521 |
+
|
522 |
+
$post_image_meta = null;
|
523 |
+
$post_image_id = false;
|
524 |
+
|
525 |
+
if ( has_post_thumbnail( $post->ID ) ) {
|
526 |
+
$post_image_id = get_post_thumbnail_id( $post->ID );
|
527 |
+
} else {
|
528 |
+
$attached_image_ids = get_posts(
|
529 |
+
array(
|
530 |
+
'post_parent' => $post->ID,
|
531 |
+
'post_type' => 'attachment',
|
532 |
+
'post_mime_type' => 'image',
|
533 |
+
'posts_per_page' => 1,
|
534 |
+
'orderby' => 'menu_order',
|
535 |
+
'order' => 'ASC',
|
536 |
+
'fields' => 'ids',
|
537 |
+
'suppress_filters' => false,
|
538 |
+
)
|
539 |
+
);
|
540 |
+
|
541 |
+
if ( ! empty( $attached_image_ids ) ) {
|
542 |
+
$post_image_id = array_shift( $attached_image_ids );
|
543 |
+
}
|
544 |
+
}
|
545 |
+
|
546 |
+
if ( ! $post_image_id ) {
|
547 |
+
return false;
|
548 |
+
}
|
549 |
+
|
550 |
+
$post_image_src = wp_get_attachment_image_src( $post_image_id, 'full' );
|
551 |
+
|
552 |
+
if ( is_array( $post_image_src ) ) {
|
553 |
+
$post_image_meta = array(
|
554 |
+
'@type' => 'ImageObject',
|
555 |
+
'url' => $post_image_src[0],
|
556 |
+
'width' => $post_image_src[1],
|
557 |
+
'height' => $post_image_src[2],
|
558 |
+
);
|
559 |
+
}
|
560 |
+
|
561 |
+
return $post_image_meta;
|
562 |
+
}
|
563 |
+
|
564 |
+
/**
|
565 |
+
* Get schema.org metadata for the current query.
|
566 |
+
*
|
567 |
+
* @since 0.7
|
568 |
+
* @see AMP_Post_Template::build_post_data() Where the logic in this function originally existed.
|
569 |
+
*
|
570 |
+
* @return array $metadata All schema.org metadata for the post.
|
571 |
+
*/
|
572 |
+
function amp_get_schemaorg_metadata() {
|
573 |
+
$metadata = array(
|
574 |
+
'@context' => 'http://schema.org',
|
575 |
+
'publisher' => array(
|
576 |
+
'@type' => 'Organization',
|
577 |
+
'name' => get_bloginfo( 'name' ),
|
578 |
+
),
|
579 |
+
);
|
580 |
+
|
581 |
+
/**
|
582 |
+
* Filters the site icon used in AMP responses.
|
583 |
+
*
|
584 |
+
* In general the `get_site_icon_url` filter should be used instead.
|
585 |
+
*
|
586 |
+
* @since 0.3
|
587 |
+
* @todo Why is the size set to 32px?
|
588 |
+
*
|
589 |
+
* @param string $site_icon_url
|
590 |
+
*/
|
591 |
+
$site_icon_url = apply_filters( 'amp_site_icon_url', get_site_icon_url( AMP_Post_Template::SITE_ICON_SIZE ) );
|
592 |
+
if ( $site_icon_url ) {
|
593 |
+
$metadata['publisher']['logo'] = array(
|
594 |
+
'@type' => 'ImageObject',
|
595 |
+
'url' => $site_icon_url,
|
596 |
+
'height' => AMP_Post_Template::SITE_ICON_SIZE,
|
597 |
+
'width' => AMP_Post_Template::SITE_ICON_SIZE,
|
598 |
+
);
|
599 |
+
}
|
600 |
+
|
601 |
+
$post = get_queried_object();
|
602 |
+
if ( $post instanceof WP_Post ) {
|
603 |
+
$metadata = array_merge(
|
604 |
+
$metadata,
|
605 |
+
array(
|
606 |
+
'@type' => is_page() ? 'WebPage' : 'BlogPosting',
|
607 |
+
'mainEntityOfPage' => get_permalink(),
|
608 |
+
'headline' => get_the_title(),
|
609 |
+
'datePublished' => date( 'c', get_the_date( 'U', $post->ID ) ),
|
610 |
+
'dateModified' => date( 'c', get_the_date( 'U', $post->ID ) ),
|
611 |
+
)
|
612 |
+
);
|
613 |
+
|
614 |
+
$post_author = get_userdata( $post->post_author );
|
615 |
+
if ( $post_author ) {
|
616 |
+
$metadata['author'] = array(
|
617 |
+
'@type' => 'Person',
|
618 |
+
'name' => html_entity_decode( $post_author->display_name, ENT_QUOTES, get_bloginfo( 'charset' ) ),
|
619 |
+
);
|
620 |
+
}
|
621 |
+
|
622 |
+
$image_metadata = amp_get_post_image_metadata( $post );
|
623 |
+
if ( $image_metadata ) {
|
624 |
+
$metadata['image'] = $image_metadata;
|
625 |
+
}
|
626 |
+
|
627 |
+
/**
|
628 |
+
* Filters Schema.org metadata for a post.
|
629 |
+
*
|
630 |
+
* The 'post_template' in the filter name here is due to this filter originally being introduced in `AMP_Post_Template`.
|
631 |
+
* In general the `amp_schemaorg_metadata` filter should be used instead.
|
632 |
+
*
|
633 |
+
* @since 0.3
|
634 |
+
*
|
635 |
+
* @param array $metadata Metadata.
|
636 |
+
* @param WP_Post $post Post.
|
637 |
+
*/
|
638 |
+
$metadata = apply_filters( 'amp_post_template_metadata', $metadata, $post );
|
639 |
+
}
|
640 |
+
|
641 |
+
/**
|
642 |
+
* Filters Schema.org metadata for a query.
|
643 |
+
*
|
644 |
+
* Check the the main query for the context for which metadata should be added.
|
645 |
+
*
|
646 |
+
* @since 0.7
|
647 |
+
*
|
648 |
+
* @param array $metadata Metadata.
|
649 |
+
*/
|
650 |
+
$metadata = apply_filters( 'amp_schemaorg_metadata', $metadata );
|
651 |
+
|
652 |
+
return $metadata;
|
653 |
+
}
|
654 |
+
|
655 |
+
/**
|
656 |
+
* Output schema.org metadata.
|
657 |
+
*
|
658 |
+
* @since 0.7
|
659 |
+
*/
|
660 |
+
function amp_print_schemaorg_metadata() {
|
661 |
+
$metadata = amp_get_schemaorg_metadata();
|
662 |
+
if ( empty( $metadata ) ) {
|
663 |
+
return;
|
664 |
+
}
|
665 |
+
?>
|
666 |
+
<script type="application/ld+json"><?php echo wp_json_encode( $metadata ); ?></script>
|
667 |
+
<?php
|
668 |
+
}
|
@@ -14,7 +14,7 @@ function amp_post_template_init_hooks() {
|
|
14 |
add_action( 'amp_post_template_head', 'amp_post_template_add_scripts' );
|
15 |
add_action( 'amp_post_template_head', 'amp_post_template_add_fonts' );
|
16 |
add_action( 'amp_post_template_head', 'amp_post_template_add_boilerplate_css' );
|
17 |
-
add_action( 'amp_post_template_head', '
|
18 |
add_action( 'amp_post_template_head', 'amp_add_generator_metadata' );
|
19 |
add_action( 'amp_post_template_css', 'amp_post_template_add_styles', 99 );
|
20 |
add_action( 'amp_post_template_data', 'amp_post_template_add_analytics_script' );
|
@@ -46,23 +46,27 @@ function amp_post_template_add_canonical( $amp_template ) {
|
|
46 |
/**
|
47 |
* Print scripts.
|
48 |
*
|
|
|
|
|
49 |
* @param AMP_Post_Template $amp_template Template.
|
50 |
*/
|
51 |
function amp_post_template_add_scripts( $amp_template ) {
|
|
|
|
|
|
|
|
|
|
|
52 |
$scripts = $amp_template->get( 'amp_component_scripts', array() );
|
53 |
-
foreach ( $scripts as $
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
esc_attr( $custom_type ),
|
58 |
-
esc_attr( $element ),
|
59 |
-
esc_url( $script )
|
60 |
-
);
|
61 |
}
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
|
|
66 |
}
|
67 |
|
68 |
/**
|
@@ -79,26 +83,22 @@ function amp_post_template_add_fonts( $amp_template ) {
|
|
79 |
|
80 |
/**
|
81 |
* Print boilerplate CSS.
|
|
|
|
|
|
|
82 |
*/
|
83 |
function amp_post_template_add_boilerplate_css() {
|
84 |
-
|
85 |
-
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
|
86 |
-
<?php
|
87 |
}
|
88 |
|
89 |
/**
|
90 |
* Print Schema.org metadata.
|
91 |
*
|
92 |
-
* @
|
93 |
*/
|
94 |
-
function amp_post_template_add_schemaorg_metadata(
|
95 |
-
|
96 |
-
|
97 |
-
return;
|
98 |
-
}
|
99 |
-
?>
|
100 |
-
<script type="application/ld+json"><?php echo wp_json_encode( $metadata ); ?></script>
|
101 |
-
<?php
|
102 |
}
|
103 |
|
104 |
/**
|
@@ -133,31 +133,11 @@ function amp_post_template_add_analytics_script( $data ) {
|
|
133 |
/**
|
134 |
* Print analytics data.
|
135 |
*
|
136 |
-
* @
|
137 |
*/
|
138 |
-
function amp_post_template_add_analytics_data(
|
139 |
-
$
|
140 |
-
|
141 |
-
return;
|
142 |
-
}
|
143 |
-
|
144 |
-
foreach ( $analytics_entries as $id => $analytics_entry ) {
|
145 |
-
if ( ! isset( $analytics_entry['type'], $analytics_entry['attributes'], $analytics_entry['config_data'] ) ) {
|
146 |
-
/* translators: %1$s is analytics entry ID, %2$s is actual entry keys. */
|
147 |
-
_doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'Analytics entry for %1$s is missing one of the following keys: `type`, `attributes`, or `config_data` (array keys: %2$s)', 'amp' ), esc_html( $id ), esc_html( implode( ', ', array_keys( $analytics_entry ) ) ) ), '0.3.2' );
|
148 |
-
continue;
|
149 |
-
}
|
150 |
-
$script_element = AMP_HTML_Utils::build_tag( 'script', array(
|
151 |
-
'type' => 'application/json',
|
152 |
-
), wp_json_encode( $analytics_entry['config_data'] ) );
|
153 |
-
|
154 |
-
$amp_analytics_attr = array_merge( array(
|
155 |
-
'id' => $id,
|
156 |
-
'type' => $analytics_entry['type'],
|
157 |
-
), $analytics_entry['attributes'] );
|
158 |
-
|
159 |
-
echo AMP_HTML_Utils::build_tag( 'amp-analytics', $amp_analytics_attr, $script_element ); // WPCS: XSS OK.
|
160 |
-
}
|
161 |
}
|
162 |
|
163 |
/**
|
14 |
add_action( 'amp_post_template_head', 'amp_post_template_add_scripts' );
|
15 |
add_action( 'amp_post_template_head', 'amp_post_template_add_fonts' );
|
16 |
add_action( 'amp_post_template_head', 'amp_post_template_add_boilerplate_css' );
|
17 |
+
add_action( 'amp_post_template_head', 'amp_print_schemaorg_metadata' );
|
18 |
add_action( 'amp_post_template_head', 'amp_add_generator_metadata' );
|
19 |
add_action( 'amp_post_template_css', 'amp_post_template_add_styles', 99 );
|
20 |
add_action( 'amp_post_template_data', 'amp_post_template_add_analytics_script' );
|
46 |
/**
|
47 |
* Print scripts.
|
48 |
*
|
49 |
+
* @see amp_register_default_scripts()
|
50 |
+
* @see amp_filter_script_loader_tag()
|
51 |
* @param AMP_Post_Template $amp_template Template.
|
52 |
*/
|
53 |
function amp_post_template_add_scripts( $amp_template ) {
|
54 |
+
|
55 |
+
// Just in case the runtime has been overridden by amp_post_template_data filter.
|
56 |
+
wp_scripts()->registered['amp-runtime']->src = $amp_template->get( 'amp_runtime_script' );
|
57 |
+
|
58 |
+
// Make sure any filtered extension script URLs get updated in registered scripts before printing.
|
59 |
$scripts = $amp_template->get( 'amp_component_scripts', array() );
|
60 |
+
foreach ( $scripts as $handle => $value ) {
|
61 |
+
if ( is_string( $value ) && wp_script_is( $handle, 'registered' ) ) {
|
62 |
+
wp_scripts()->registered[ $handle ]->src = $value;
|
63 |
+
}
|
|
|
|
|
|
|
|
|
64 |
}
|
65 |
+
|
66 |
+
wp_print_scripts( array_merge(
|
67 |
+
array( 'amp-runtime' ),
|
68 |
+
array_keys( $scripts )
|
69 |
+
) );
|
70 |
}
|
71 |
|
72 |
/**
|
83 |
|
84 |
/**
|
85 |
* Print boilerplate CSS.
|
86 |
+
*
|
87 |
+
* @since 0.3
|
88 |
+
* @see amp_get_boilerplate_code()
|
89 |
*/
|
90 |
function amp_post_template_add_boilerplate_css() {
|
91 |
+
echo amp_get_boilerplate_code(); // WPCS: xss ok.
|
|
|
|
|
92 |
}
|
93 |
|
94 |
/**
|
95 |
* Print Schema.org metadata.
|
96 |
*
|
97 |
+
* @deprecated Since 0.7
|
98 |
*/
|
99 |
+
function amp_post_template_add_schemaorg_metadata() {
|
100 |
+
_deprecated_function( __FUNCTION__, '0.7', 'amp_print_schemaorg_metadata' );
|
101 |
+
amp_print_schemaorg_metadata();
|
|
|
|
|
|
|
|
|
|
|
102 |
}
|
103 |
|
104 |
/**
|
133 |
/**
|
134 |
* Print analytics data.
|
135 |
*
|
136 |
+
* @since 0.3.2
|
137 |
*/
|
138 |
+
function amp_post_template_add_analytics_data() {
|
139 |
+
$analytics = amp_add_custom_analytics();
|
140 |
+
amp_print_analytics( $analytics );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
}
|
142 |
|
143 |
/**
|
@@ -29,6 +29,8 @@ class AMP_Autoloader {
|
|
29 |
* @var string[]
|
30 |
*/
|
31 |
private static $_classmap = array(
|
|
|
|
|
32 |
'AMP_Template_Customizer' => 'includes/admin/class-amp-customizer',
|
33 |
'AMP_Post_Meta_Box' => 'includes/admin/class-amp-post-meta-box',
|
34 |
'AMP_Post_Type_Support' => 'includes/class-amp-post-type-support',
|
@@ -37,8 +39,13 @@ class AMP_Autoloader {
|
|
37 |
'AMP_Facebook_Embed_Handler' => 'includes/embeds/class-amp-facebook-embed',
|
38 |
'AMP_Gallery_Embed_Handler' => 'includes/embeds/class-amp-gallery-embed',
|
39 |
'AMP_Instagram_Embed_Handler' => 'includes/embeds/class-amp-instagram-embed',
|
|
|
|
|
40 |
'AMP_Pinterest_Embed_Handler' => 'includes/embeds/class-amp-pinterest-embed',
|
|
|
|
|
41 |
'AMP_SoundCloud_Embed_Handler' => 'includes/embeds/class-amp-soundcloud-embed',
|
|
|
42 |
'AMP_Twitter_Embed_Handler' => 'includes/embeds/class-amp-twitter-embed',
|
43 |
'AMP_Vimeo_Embed_Handler' => 'includes/embeds/class-amp-vimeo-embed',
|
44 |
'AMP_Vine_Embed_Handler' => 'includes/embeds/class-amp-vine-embed',
|
@@ -63,6 +70,8 @@ class AMP_Autoloader {
|
|
63 |
'AMP_Blacklist_Sanitizer' => 'includes/sanitizers/class-amp-blacklist-sanitizer',
|
64 |
'AMP_Iframe_Sanitizer' => 'includes/sanitizers/class-amp-iframe-sanitizer',
|
65 |
'AMP_Img_Sanitizer' => 'includes/sanitizers/class-amp-img-sanitizer',
|
|
|
|
|
66 |
'AMP_Playbuzz_Sanitizer' => 'includes/sanitizers/class-amp-playbuzz-sanitizer',
|
67 |
'AMP_Style_Sanitizer' => 'includes/sanitizers/class-amp-style-sanitizer',
|
68 |
'AMP_Tag_And_Attribute_Sanitizer' => 'includes/sanitizers/class-amp-tag-and-attribute-sanitizer',
|
@@ -75,8 +84,14 @@ class AMP_Autoloader {
|
|
75 |
'AMP_DOM_Utils' => 'includes/utils/class-amp-dom-utils',
|
76 |
'AMP_HTML_Utils' => 'includes/utils/class-amp-html-utils',
|
77 |
'AMP_Image_Dimension_Extractor' => 'includes/utils/class-amp-image-dimension-extractor',
|
|
|
78 |
'AMP_String_Utils' => 'includes/utils/class-amp-string-utils',
|
79 |
'AMP_WP_Utils' => 'includes/utils/class-amp-wp-utils',
|
|
|
|
|
|
|
|
|
|
|
80 |
'WPCOM_AMP_Polldaddy_Embed' => 'wpcom/class-amp-polldaddy-embed',
|
81 |
'AMP_Test_Stub_Sanitizer' => 'tests/stubs',
|
82 |
'AMP_Test_World_Sanitizer' => 'tests/stubs',
|
29 |
* @var string[]
|
30 |
*/
|
31 |
private static $_classmap = array(
|
32 |
+
'AMP_Theme_Support' => 'includes/class-amp-theme-support',
|
33 |
+
'AMP_Comment_Walker' => 'includes/class-amp-comment-walker',
|
34 |
'AMP_Template_Customizer' => 'includes/admin/class-amp-customizer',
|
35 |
'AMP_Post_Meta_Box' => 'includes/admin/class-amp-post-meta-box',
|
36 |
'AMP_Post_Type_Support' => 'includes/class-amp-post-type-support',
|
39 |
'AMP_Facebook_Embed_Handler' => 'includes/embeds/class-amp-facebook-embed',
|
40 |
'AMP_Gallery_Embed_Handler' => 'includes/embeds/class-amp-gallery-embed',
|
41 |
'AMP_Instagram_Embed_Handler' => 'includes/embeds/class-amp-instagram-embed',
|
42 |
+
'AMP_Issuu_Embed_Handler' => 'includes/embeds/class-amp-issuu-embed-handler',
|
43 |
+
'AMP_Meetup_Embed_Handler' => 'includes/embeds/class-amp-meetup-embed-handler',
|
44 |
'AMP_Pinterest_Embed_Handler' => 'includes/embeds/class-amp-pinterest-embed',
|
45 |
+
'AMP_Playlist_Embed_Handler' => 'includes/embeds/class-amp-playlist-embed-handler',
|
46 |
+
'AMP_Reddit_Embed_Handler' => 'includes/embeds/class-amp-reddit-embed-handler',
|
47 |
'AMP_SoundCloud_Embed_Handler' => 'includes/embeds/class-amp-soundcloud-embed',
|
48 |
+
'AMP_Tumblr_Embed_Handler' => 'includes/embeds/class-amp-tumblr-embed-handler',
|
49 |
'AMP_Twitter_Embed_Handler' => 'includes/embeds/class-amp-twitter-embed',
|
50 |
'AMP_Vimeo_Embed_Handler' => 'includes/embeds/class-amp-vimeo-embed',
|
51 |
'AMP_Vine_Embed_Handler' => 'includes/embeds/class-amp-vine-embed',
|
70 |
'AMP_Blacklist_Sanitizer' => 'includes/sanitizers/class-amp-blacklist-sanitizer',
|
71 |
'AMP_Iframe_Sanitizer' => 'includes/sanitizers/class-amp-iframe-sanitizer',
|
72 |
'AMP_Img_Sanitizer' => 'includes/sanitizers/class-amp-img-sanitizer',
|
73 |
+
'AMP_Comments_Sanitizer' => 'includes/sanitizers/class-amp-comments-sanitizer',
|
74 |
+
'AMP_Form_Sanitizer' => 'includes/sanitizers/class-amp-form-sanitizer',
|
75 |
'AMP_Playbuzz_Sanitizer' => 'includes/sanitizers/class-amp-playbuzz-sanitizer',
|
76 |
'AMP_Style_Sanitizer' => 'includes/sanitizers/class-amp-style-sanitizer',
|
77 |
'AMP_Tag_And_Attribute_Sanitizer' => 'includes/sanitizers/class-amp-tag-and-attribute-sanitizer',
|
84 |
'AMP_DOM_Utils' => 'includes/utils/class-amp-dom-utils',
|
85 |
'AMP_HTML_Utils' => 'includes/utils/class-amp-html-utils',
|
86 |
'AMP_Image_Dimension_Extractor' => 'includes/utils/class-amp-image-dimension-extractor',
|
87 |
+
'AMP_Validation_Utils' => 'includes/utils/class-amp-validation-utils',
|
88 |
'AMP_String_Utils' => 'includes/utils/class-amp-string-utils',
|
89 |
'AMP_WP_Utils' => 'includes/utils/class-amp-wp-utils',
|
90 |
+
'AMP_Widget_Archives' => 'includes/widgets/class-amp-widget-archives',
|
91 |
+
'AMP_Widget_Categories' => 'includes/widgets/class-amp-widget-categories',
|
92 |
+
'AMP_Widget_Media_Video' => 'includes/widgets/class-amp-widget-media-video',
|
93 |
+
'AMP_Widget_Recent_Comments' => 'includes/widgets/class-amp-widget-recent-comments',
|
94 |
+
'AMP_Widget_Text' => 'includes/widgets/class-amp-widget-text',
|
95 |
'WPCOM_AMP_Polldaddy_Embed' => 'wpcom/class-amp-polldaddy-embed',
|
96 |
'AMP_Test_Stub_Sanitizer' => 'tests/stubs',
|
97 |
'AMP_Test_World_Sanitizer' => 'tests/stubs',
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Comment_Walker
|
4 |
+
*
|
5 |
+
* @package AMP
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class AMP_Comment_Walker
|
10 |
+
*
|
11 |
+
* Walker to wrap comments in mustache tags for amp-template.
|
12 |
+
*/
|
13 |
+
class AMP_Comment_Walker extends Walker_Comment {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* The original comments arguments.
|
17 |
+
*
|
18 |
+
* @since 0.7
|
19 |
+
* @var array
|
20 |
+
*/
|
21 |
+
public $args;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Holds the timestamp of the most reacent comment in a thread.
|
25 |
+
*
|
26 |
+
* @since 0.7
|
27 |
+
* @var array
|
28 |
+
*/
|
29 |
+
private $comment_thread_age = array();
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Starts the element output.
|
33 |
+
*
|
34 |
+
* @since 0.7.0
|
35 |
+
*
|
36 |
+
* @see Walker::start_el()
|
37 |
+
* @see wp_list_comments()
|
38 |
+
* @global int $comment_depth
|
39 |
+
* @global WP_Comment $comment
|
40 |
+
*
|
41 |
+
* @param string $output Used to append additional content. Passed by reference.
|
42 |
+
* @param WP_Comment $comment Comment data object.
|
43 |
+
* @param int $depth Optional. Depth of the current comment in reference to parents. Default 0.
|
44 |
+
* @param array $args Optional. An array of arguments. Default empty array.
|
45 |
+
* @param int $id Optional. ID of the current comment. Default 0 (unused).
|
46 |
+
*/
|
47 |
+
public function start_el( &$output, $comment, $depth = 0, $args = array(), $id = 0 ) {
|
48 |
+
|
49 |
+
$new_out = '';
|
50 |
+
parent::start_el( $new_out, $comment, $depth, $args, $id );
|
51 |
+
|
52 |
+
if ( 'div' === $args['style'] ) {
|
53 |
+
$tag = '<div';
|
54 |
+
} else {
|
55 |
+
$tag = '<li';
|
56 |
+
}
|
57 |
+
$new_tag = $tag . ' data-sort-time="' . esc_attr( strtotime( $comment->comment_date ) ) . '"';
|
58 |
+
|
59 |
+
if ( ! empty( $this->comment_thread_age[ $comment->comment_ID ] ) ) {
|
60 |
+
$new_tag .= ' data-update-time="' . esc_attr( $this->comment_thread_age[ $comment->comment_ID ] ) . '"';
|
61 |
+
}
|
62 |
+
|
63 |
+
$output .= $new_tag . substr( ltrim( $new_out ), strlen( $tag ) );
|
64 |
+
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Output amp-list template code and place holder for comments.
|
69 |
+
*
|
70 |
+
* @since 0.7
|
71 |
+
* @see Walker::paged_walk()
|
72 |
+
*
|
73 |
+
* @param WP_Comment[] $elements List of comment Elements.
|
74 |
+
* @param int $max_depth The maximum hierarchical depth.
|
75 |
+
* @param int $page_num The specific page number, beginning with 1.
|
76 |
+
* @param int $per_page Per page counter.
|
77 |
+
*
|
78 |
+
* @return string XHTML of the specified page of elements.
|
79 |
+
*/
|
80 |
+
public function paged_walk( $elements, $max_depth, $page_num, $per_page ) {
|
81 |
+
if ( empty( $elements ) || $max_depth < -1 ) {
|
82 |
+
return '';
|
83 |
+
}
|
84 |
+
|
85 |
+
$this->build_thread_latest_date( $elements );
|
86 |
+
|
87 |
+
$args = array_slice( func_get_args(), 4 );
|
88 |
+
|
89 |
+
return parent::paged_walk( $elements, $max_depth, $page_num, $per_page, $args[0] );
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Find the timestamp of the latest child comment of a thread to set the updated time.
|
94 |
+
*
|
95 |
+
* @since 0.7
|
96 |
+
*
|
97 |
+
* @param WP_Comment[] $elements The list of comments to get thread times for.
|
98 |
+
* @param int $time $the timestamp to check against.
|
99 |
+
* @param bool $is_child Flag used to set the the value or return the time.
|
100 |
+
* @return int Latest time.
|
101 |
+
*/
|
102 |
+
protected function build_thread_latest_date( $elements, $time = 0, $is_child = false ) {
|
103 |
+
|
104 |
+
foreach ( $elements as $element ) {
|
105 |
+
|
106 |
+
$children = $element->get_children();
|
107 |
+
$this_time = strtotime( $element->comment_date );
|
108 |
+
if ( ! empty( $children ) ) {
|
109 |
+
$this_time = $this->build_thread_latest_date( $children, $this_time, true );
|
110 |
+
}
|
111 |
+
if ( $this_time > $time ) {
|
112 |
+
$time = $this_time;
|
113 |
+
}
|
114 |
+
if ( false === $is_child ) {
|
115 |
+
$this->comment_thread_age[ $element->comment_ID ] = $time;
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
return $time;
|
120 |
+
}
|
121 |
+
}
|
@@ -54,7 +54,7 @@ class AMP_Post_Type_Support {
|
|
54 |
AMP_Options_Manager::get_option( 'supported_post_types', array() )
|
55 |
);
|
56 |
foreach ( $post_types as $post_type ) {
|
57 |
-
add_post_type_support( $post_type,
|
58 |
}
|
59 |
}
|
60 |
|
@@ -73,7 +73,7 @@ class AMP_Post_Type_Support {
|
|
73 |
$errors = array();
|
74 |
|
75 |
// Because `add_rewrite_endpoint` doesn't let us target specific post_types.
|
76 |
-
if ( ! post_type_supports( $post->post_type,
|
77 |
$errors[] = 'post-type-support';
|
78 |
}
|
79 |
|
@@ -90,7 +90,7 @@ class AMP_Post_Type_Support {
|
|
90 |
* @param int $post_id Post ID.
|
91 |
* @param WP_Post $post Post.
|
92 |
*/
|
93 |
-
if ( true === apply_filters( 'amp_skip_post', false, $post->ID, $post ) ) {
|
94 |
$errors[] = 'skip-post';
|
95 |
}
|
96 |
|
54 |
AMP_Options_Manager::get_option( 'supported_post_types', array() )
|
55 |
);
|
56 |
foreach ( $post_types as $post_type ) {
|
57 |
+
add_post_type_support( $post_type, amp_get_slug() );
|
58 |
}
|
59 |
}
|
60 |
|
73 |
$errors = array();
|
74 |
|
75 |
// Because `add_rewrite_endpoint` doesn't let us target specific post_types.
|
76 |
+
if ( isset( $post->post_type ) && ! post_type_supports( $post->post_type, amp_get_slug() ) ) {
|
77 |
$errors[] = 'post-type-support';
|
78 |
}
|
79 |
|
90 |
* @param int $post_id Post ID.
|
91 |
* @param WP_Post $post Post.
|
92 |
*/
|
93 |
+
if ( isset( $post->ID ) && true === apply_filters( 'amp_skip_post', false, $post->ID, $post ) ) {
|
94 |
$errors[] = 'skip-post';
|
95 |
}
|
96 |
|
@@ -0,0 +1,1201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Theme_Support
|
4 |
+
*
|
5 |
+
* @package AMP
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class AMP_Theme_Support
|
10 |
+
*
|
11 |
+
* Callbacks for adding AMP-related things when theme support is added.
|
12 |
+
*/
|
13 |
+
class AMP_Theme_Support {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Replaced with the necessary scripts depending on components used in output.
|
17 |
+
*
|
18 |
+
* @var string
|
19 |
+
*/
|
20 |
+
const SCRIPTS_PLACEHOLDER = '<!-- AMP:SCRIPTS_PLACEHOLDER -->';
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Sanitizer classes.
|
24 |
+
*
|
25 |
+
* @var array
|
26 |
+
*/
|
27 |
+
protected static $sanitizer_classes = array();
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Embed handlers.
|
31 |
+
*
|
32 |
+
* @var AMP_Base_Embed_Handler[]
|
33 |
+
*/
|
34 |
+
protected static $embed_handlers = array();
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Template types.
|
38 |
+
*
|
39 |
+
* @var array
|
40 |
+
*/
|
41 |
+
protected static $template_types = array(
|
42 |
+
'paged', // Deprecated.
|
43 |
+
'index',
|
44 |
+
'404',
|
45 |
+
'archive',
|
46 |
+
'author',
|
47 |
+
'category',
|
48 |
+
'tag',
|
49 |
+
'taxonomy',
|
50 |
+
'date',
|
51 |
+
'home',
|
52 |
+
'front_page',
|
53 |
+
'page',
|
54 |
+
'search',
|
55 |
+
'single',
|
56 |
+
'embed',
|
57 |
+
'singular',
|
58 |
+
'attachment',
|
59 |
+
);
|
60 |
+
|
61 |
+
/**
|
62 |
+
* AMP-specific query vars that were purged.
|
63 |
+
*
|
64 |
+
* @since 0.7
|
65 |
+
* @see AMP_Theme_Support::purge_amp_query_vars()
|
66 |
+
* @var string[]
|
67 |
+
*/
|
68 |
+
public static $purged_amp_query_vars = array();
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Headers sent (or attempted to be sent).
|
72 |
+
*
|
73 |
+
* @since 0.7
|
74 |
+
* @see AMP_Theme_Support::send_header()
|
75 |
+
* @var array[]
|
76 |
+
*/
|
77 |
+
public static $headers_sent = array();
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Whether output buffering has started.
|
81 |
+
*
|
82 |
+
* @since 0.7
|
83 |
+
* @var bool
|
84 |
+
*/
|
85 |
+
protected static $is_output_buffering = false;
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Initialize.
|
89 |
+
*/
|
90 |
+
public static function init() {
|
91 |
+
if ( ! current_theme_supports( 'amp' ) ) {
|
92 |
+
return;
|
93 |
+
}
|
94 |
+
|
95 |
+
self::purge_amp_query_vars();
|
96 |
+
self::handle_xhr_request();
|
97 |
+
|
98 |
+
require_once AMP__DIR__ . '/includes/amp-post-template-actions.php';
|
99 |
+
|
100 |
+
// Validate theme support usage.
|
101 |
+
$support = get_theme_support( 'amp' );
|
102 |
+
if ( WP_DEBUG && is_array( $support ) ) {
|
103 |
+
$args = array_shift( $support );
|
104 |
+
if ( ! is_array( $args ) ) {
|
105 |
+
trigger_error( esc_html__( 'Expected AMP theme support arg to be array.', 'amp' ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
106 |
+
} elseif ( count( array_diff( array_keys( $args ), array( 'template_dir', 'available_callback', 'comments_live_list' ) ) ) !== 0 ) {
|
107 |
+
trigger_error( esc_html__( 'Expected AMP theme support to only have template_dir and/or available_callback.', 'amp' ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
add_action( 'widgets_init', array( __CLASS__, 'register_widgets' ) );
|
112 |
+
|
113 |
+
/*
|
114 |
+
* Note that wp action is use instead of template_redirect because some themes/plugins output
|
115 |
+
* the response at this action and then short-circuit with exit. So this is why the the preceding
|
116 |
+
* action to template_redirect--the wp action--is used instead.
|
117 |
+
*/
|
118 |
+
add_action( 'wp', array( __CLASS__, 'finish_init' ), PHP_INT_MAX );
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Finish initialization once query vars are set.
|
123 |
+
*
|
124 |
+
* @since 0.7
|
125 |
+
*/
|
126 |
+
public static function finish_init() {
|
127 |
+
if ( ! is_amp_endpoint() ) {
|
128 |
+
// Add amphtml link when paired mode is available.
|
129 |
+
if ( self::is_paired_available() ) {
|
130 |
+
amp_add_frontend_actions(); // @todo This function is poor in how it requires a file that then does add_action().
|
131 |
+
if ( ! has_action( 'wp_head', 'amp_frontend_add_canonical' ) ) {
|
132 |
+
add_action( 'wp_head', 'amp_frontend_add_canonical' );
|
133 |
+
}
|
134 |
+
}
|
135 |
+
return;
|
136 |
+
}
|
137 |
+
|
138 |
+
if ( amp_is_canonical() ) {
|
139 |
+
self::redirect_canonical_amp();
|
140 |
+
} else {
|
141 |
+
self::register_paired_hooks();
|
142 |
+
}
|
143 |
+
|
144 |
+
self::add_hooks();
|
145 |
+
self::$sanitizer_classes = amp_get_content_sanitizers();
|
146 |
+
self::$embed_handlers = self::register_content_embed_handlers();
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Redirect to canonical URL if the AMP URL was loaded, since canonical is now AMP.
|
151 |
+
*
|
152 |
+
* @since 0.7
|
153 |
+
*/
|
154 |
+
public static function redirect_canonical_amp() {
|
155 |
+
if ( false !== get_query_var( amp_get_slug(), false ) ) { // Because is_amp_endpoint() now returns true if amp_is_canonical().
|
156 |
+
$url = preg_replace( '#^(https?://.+?)(/.*)$#', '$1', home_url( '/' ) );
|
157 |
+
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
|
158 |
+
$url .= wp_unslash( $_SERVER['REQUEST_URI'] );
|
159 |
+
}
|
160 |
+
|
161 |
+
$url = amp_remove_endpoint( $url );
|
162 |
+
|
163 |
+
wp_safe_redirect( $url, 302 ); // Temporary redirect because canonical may change in future.
|
164 |
+
exit;
|
165 |
+
}
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Determines whether paired mode is available.
|
170 |
+
*
|
171 |
+
* When 'amp' theme support has not been added or canonical mode is enabled, then this returns false.
|
172 |
+
* Returns true when there is a template_dir defined in theme support, and if a defined available_callback
|
173 |
+
* returns true.
|
174 |
+
*
|
175 |
+
* @return bool Whether available.
|
176 |
+
*/
|
177 |
+
public static function is_paired_available() {
|
178 |
+
$support = get_theme_support( 'amp' );
|
179 |
+
if ( empty( $support ) || amp_is_canonical() ) {
|
180 |
+
return false;
|
181 |
+
}
|
182 |
+
|
183 |
+
if ( is_singular() && ! post_supports_amp( get_queried_object() ) ) {
|
184 |
+
return false;
|
185 |
+
}
|
186 |
+
|
187 |
+
$args = array_shift( $support );
|
188 |
+
|
189 |
+
if ( isset( $args['available_callback'] ) && is_callable( $args['available_callback'] ) ) {
|
190 |
+
return call_user_func( $args['available_callback'] );
|
191 |
+
}
|
192 |
+
return true;
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Determine whether the user is in the Customizer preview iframe.
|
197 |
+
*
|
198 |
+
* @since 0.7
|
199 |
+
*
|
200 |
+
* @return bool Whether in Customizer preview iframe.
|
201 |
+
*/
|
202 |
+
public static function is_customize_preview_iframe() {
|
203 |
+
global $wp_customize;
|
204 |
+
return is_customize_preview() && $wp_customize->get_messenger_channel();
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Register hooks for paired mode.
|
209 |
+
*/
|
210 |
+
public static function register_paired_hooks() {
|
211 |
+
foreach ( self::$template_types as $template_type ) {
|
212 |
+
add_filter( "{$template_type}_template_hierarchy", array( __CLASS__, 'filter_paired_template_hierarchy' ) );
|
213 |
+
}
|
214 |
+
add_filter( 'template_include', array( __CLASS__, 'filter_paired_template_include' ), 100 );
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Register hooks.
|
219 |
+
*/
|
220 |
+
public static function add_hooks() {
|
221 |
+
|
222 |
+
// Remove core actions which are invalid AMP.
|
223 |
+
remove_action( 'wp_head', 'wp_post_preview_js', 1 );
|
224 |
+
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
|
225 |
+
remove_action( 'wp_print_styles', 'print_emoji_styles' );
|
226 |
+
remove_action( 'wp_head', 'wp_oembed_add_host_js' );
|
227 |
+
|
228 |
+
// Prevent MediaElement.js scripts/styles from being enqueued.
|
229 |
+
add_filter( 'wp_video_shortcode_library', function() {
|
230 |
+
return 'amp';
|
231 |
+
} );
|
232 |
+
add_filter( 'wp_audio_shortcode_library', function() {
|
233 |
+
return 'amp';
|
234 |
+
} );
|
235 |
+
|
236 |
+
/*
|
237 |
+
* Add additional markup required by AMP <https://www.ampproject.org/docs/reference/spec#required-markup>.
|
238 |
+
* Note that the meta[name=viewport] is not added here because a theme may want to define one with additional
|
239 |
+
* properties than included in the default configuration. If a theme doesn't include one, then the meta viewport
|
240 |
+
* will be added when output buffering is finished. Note that meta charset _is_ output here because the output
|
241 |
+
* buffer will need it to parse the document properly, and it must be exactly as is to be valid AMP. Nevertheless,
|
242 |
+
* in this case too we should defer to the theme as well to output the meta charset because it is possible the
|
243 |
+
* install is not on utf-8 and we may need to do a encoding conversion.
|
244 |
+
*/
|
245 |
+
add_action( 'wp_print_styles', array( __CLASS__, 'print_amp_styles' ), 0 ); // Print boilerplate before theme and plugin stylesheets.
|
246 |
+
add_action( 'wp_head', 'amp_add_generator_metadata', 20 );
|
247 |
+
|
248 |
+
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
|
249 |
+
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'dequeue_customize_preview_scripts' ), 1000 );
|
250 |
+
add_filter( 'customize_partial_render', array( __CLASS__, 'filter_customize_partial_render' ) );
|
251 |
+
|
252 |
+
add_action( 'wp_footer', 'amp_print_analytics' );
|
253 |
+
|
254 |
+
/*
|
255 |
+
* Disable admin bar because admin-bar.css (28K) and Dashicons (48K) alone
|
256 |
+
* combine to surpass the 50K limit imposed for the amp-custom style.
|
257 |
+
*/
|
258 |
+
add_filter( 'show_admin_bar', '__return_false', 100 );
|
259 |
+
|
260 |
+
/*
|
261 |
+
* Start output buffering at very low priority for sake of plugins and themes that use template_redirect
|
262 |
+
* instead of template_include.
|
263 |
+
*/
|
264 |
+
$priority = defined( 'PHP_INT_MIN' ) ? PHP_INT_MIN : ~PHP_INT_MAX; // phpcs:ignore PHPCompatibility.PHP.NewConstants.php_int_minFound
|
265 |
+
add_action( 'template_redirect', array( __CLASS__, 'start_output_buffering' ), $priority );
|
266 |
+
|
267 |
+
// Add validation hooks *after* output buffering has started for the response.
|
268 |
+
if ( AMP_Validation_Utils::should_validate_response() ) {
|
269 |
+
AMP_Validation_Utils::add_validation_hooks();
|
270 |
+
}
|
271 |
+
|
272 |
+
// Commenting hooks.
|
273 |
+
add_filter( 'wp_list_comments_args', array( __CLASS__, 'set_comments_walker' ), PHP_INT_MAX );
|
274 |
+
add_filter( 'comment_form_defaults', array( __CLASS__, 'filter_comment_form_defaults' ) );
|
275 |
+
add_filter( 'comment_reply_link', array( __CLASS__, 'filter_comment_reply_link' ), 10, 4 );
|
276 |
+
add_filter( 'cancel_comment_reply_link', array( __CLASS__, 'filter_cancel_comment_reply_link' ), 10, 3 );
|
277 |
+
add_action( 'comment_form', array( __CLASS__, 'amend_comment_form' ), 100 );
|
278 |
+
remove_action( 'comment_form', 'wp_comment_form_unfiltered_html_nonce' );
|
279 |
+
add_filter( 'wp_kses_allowed_html', array( __CLASS__, 'whitelist_layout_in_wp_kses_allowed_html' ), 10 );
|
280 |
+
|
281 |
+
// @todo Add character conversion.
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Remove query vars that come in requests such as for amp-live-list.
|
286 |
+
*
|
287 |
+
* WordPress should generally not respond differently to requests when these parameters
|
288 |
+
* are present. In some cases, when a query param such as __amp_source_origin is present
|
289 |
+
* then it would normally get included into pagination links generated by get_pagenum_link().
|
290 |
+
* The whitelist sanitizer empties out links that contain this string as it matches the
|
291 |
+
* blacklisted_value_regex. So by preemptively scrubbing any reference to these query vars
|
292 |
+
* we can ensure that WordPress won't end up referencing them in any way.
|
293 |
+
*
|
294 |
+
* @since 0.7
|
295 |
+
*/
|
296 |
+
public static function purge_amp_query_vars() {
|
297 |
+
$query_vars = array(
|
298 |
+
'__amp_source_origin',
|
299 |
+
'_wp_amp_action_xhr_converted',
|
300 |
+
'amp_latest_update_time',
|
301 |
+
'amp_last_check_time',
|
302 |
+
);
|
303 |
+
|
304 |
+
// Scrub input vars.
|
305 |
+
foreach ( $query_vars as $query_var ) {
|
306 |
+
if ( ! isset( $_GET[ $query_var ] ) ) { // phpcs:ignore
|
307 |
+
continue;
|
308 |
+
}
|
309 |
+
self::$purged_amp_query_vars[ $query_var ] = wp_unslash( $_GET[ $query_var ] ); // phpcs:ignore
|
310 |
+
unset( $_REQUEST[ $query_var ], $_GET[ $query_var ] );
|
311 |
+
$scrubbed = true;
|
312 |
+
}
|
313 |
+
|
314 |
+
if ( isset( $scrubbed ) ) {
|
315 |
+
$build_query = function( $query ) use ( $query_vars ) {
|
316 |
+
$pattern = '/^(' . join( '|', $query_vars ) . ')(?==|$)/';
|
317 |
+
$pairs = array();
|
318 |
+
foreach ( explode( '&', $query ) as $pair ) {
|
319 |
+
if ( ! preg_match( $pattern, $pair ) ) {
|
320 |
+
$pairs[] = $pair;
|
321 |
+
}
|
322 |
+
}
|
323 |
+
return join( '&', $pairs );
|
324 |
+
};
|
325 |
+
|
326 |
+
// Scrub QUERY_STRING.
|
327 |
+
if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {
|
328 |
+
$_SERVER['QUERY_STRING'] = $build_query( $_SERVER['QUERY_STRING'] );
|
329 |
+
}
|
330 |
+
|
331 |
+
// Scrub REQUEST_URI.
|
332 |
+
if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
|
333 |
+
list( $path, $query ) = explode( '?', $_SERVER['REQUEST_URI'], 2 );
|
334 |
+
|
335 |
+
$pairs = $build_query( $query );
|
336 |
+
$_SERVER['REQUEST_URI'] = $path;
|
337 |
+
if ( ! empty( $pairs ) ) {
|
338 |
+
$_SERVER['REQUEST_URI'] .= "?{$pairs}";
|
339 |
+
}
|
340 |
+
}
|
341 |
+
}
|
342 |
+
}
|
343 |
+
|
344 |
+
/**
|
345 |
+
* Send an HTTP response header.
|
346 |
+
*
|
347 |
+
* This largely exists to facilitate unit testing but it also provides a better interface for sending headers.
|
348 |
+
*
|
349 |
+
* @since 0.7.0
|
350 |
+
*
|
351 |
+
* @param string $name Header name.
|
352 |
+
* @param string $value Header value.
|
353 |
+
* @param array $args {
|
354 |
+
* Args to header().
|
355 |
+
*
|
356 |
+
* @type bool $replace Whether to replace a header previously sent. Default true.
|
357 |
+
* @type int $status_code Status code to send with the sent header.
|
358 |
+
* }
|
359 |
+
* @return bool Whether the header was sent.
|
360 |
+
*/
|
361 |
+
public static function send_header( $name, $value, $args = array() ) {
|
362 |
+
$args = array_merge(
|
363 |
+
array(
|
364 |
+
'replace' => true,
|
365 |
+
'status_code' => null,
|
366 |
+
),
|
367 |
+
$args
|
368 |
+
);
|
369 |
+
|
370 |
+
self::$headers_sent[] = array_merge( compact( 'name', 'value' ), $args );
|
371 |
+
if ( headers_sent() ) {
|
372 |
+
return false;
|
373 |
+
}
|
374 |
+
|
375 |
+
header(
|
376 |
+
sprintf( '%s: %s', $name, $value ),
|
377 |
+
$args['replace'],
|
378 |
+
$args['status_code']
|
379 |
+
);
|
380 |
+
return true;
|
381 |
+
}
|
382 |
+
|
383 |
+
/**
|
384 |
+
* Hook into a POST form submissions, such as the comment form or some other form submission.
|
385 |
+
*
|
386 |
+
* @since 0.7.0
|
387 |
+
*/
|
388 |
+
public static function handle_xhr_request() {
|
389 |
+
$is_amp_xhr = (
|
390 |
+
! empty( self::$purged_amp_query_vars['_wp_amp_action_xhr_converted'] )
|
391 |
+
&&
|
392 |
+
! empty( self::$purged_amp_query_vars['__amp_source_origin'] )
|
393 |
+
&&
|
394 |
+
( ! empty( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] )
|
395 |
+
);
|
396 |
+
if ( ! $is_amp_xhr ) {
|
397 |
+
return;
|
398 |
+
}
|
399 |
+
|
400 |
+
// Send AMP response header.
|
401 |
+
$origin = wp_validate_redirect( wp_sanitize_redirect( esc_url_raw( self::$purged_amp_query_vars['__amp_source_origin'] ) ) );
|
402 |
+
if ( $origin ) {
|
403 |
+
self::send_header( 'AMP-Access-Control-Allow-Source-Origin', $origin, array( 'replace' => true ) );
|
404 |
+
}
|
405 |
+
|
406 |
+
// Intercept POST requests which redirect.
|
407 |
+
add_filter( 'wp_redirect', array( __CLASS__, 'intercept_post_request_redirect' ), PHP_INT_MAX );
|
408 |
+
|
409 |
+
// Add special handling for redirecting after comment submission.
|
410 |
+
add_filter( 'comment_post_redirect', array( __CLASS__, 'filter_comment_post_redirect' ), PHP_INT_MAX, 2 );
|
411 |
+
|
412 |
+
// Add die handler for AMP error display, most likely due to problem with comment.
|
413 |
+
add_filter( 'wp_die_handler', function() {
|
414 |
+
return array( __CLASS__, 'handle_wp_die' );
|
415 |
+
} );
|
416 |
+
|
417 |
+
}
|
418 |
+
|
419 |
+
/**
|
420 |
+
* Strip tags that are not allowed in amp-mustache.
|
421 |
+
*
|
422 |
+
* @since 0.7.0
|
423 |
+
*
|
424 |
+
* @param string $text Text to sanitize.
|
425 |
+
* @return string Sanitized text.
|
426 |
+
*/
|
427 |
+
protected static function wp_kses_amp_mustache( $text ) {
|
428 |
+
$amp_mustache_allowed_html_tags = array( 'strong', 'b', 'em', 'i', 'u', 's', 'small', 'mark', 'del', 'ins', 'sup', 'sub' );
|
429 |
+
return wp_kses( $text, array_fill_keys( $amp_mustache_allowed_html_tags, array() ) );
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* Handle comment_post_redirect to ensure page reload is done when comments_live_list is not supported, while sending back a success message when it is.
|
434 |
+
*
|
435 |
+
* @since 0.7.0
|
436 |
+
*
|
437 |
+
* @param string $url Comment permalink to redirect to.
|
438 |
+
* @param WP_Comment $comment Posted comment.
|
439 |
+
* @return string URL.
|
440 |
+
*/
|
441 |
+
public static function filter_comment_post_redirect( $url, $comment ) {
|
442 |
+
$theme_support = get_theme_support( 'amp' );
|
443 |
+
|
444 |
+
// Cause a page refresh if amp-live-list is not implemented for comments via add_theme_support( 'amp', array( 'comments_live_list' => true ) ).
|
445 |
+
if ( empty( $theme_support[0]['comments_live_list'] ) ) {
|
446 |
+
/*
|
447 |
+
* Add the comment ID to the URL to force AMP to refresh the page.
|
448 |
+
* This is ideally a temporary workaround to deal with https://github.com/ampproject/amphtml/issues/14170
|
449 |
+
*/
|
450 |
+
$url = add_query_arg( 'comment', $comment->comment_ID, $url );
|
451 |
+
|
452 |
+
// Pass URL along to wp_redirect().
|
453 |
+
return $url;
|
454 |
+
}
|
455 |
+
|
456 |
+
// Create a success message to display to the user.
|
457 |
+
if ( '1' === (string) $comment->comment_approved ) {
|
458 |
+
$message = __( 'Your comment has been posted.', 'amp' );
|
459 |
+
} else {
|
460 |
+
$message = __( 'Your comment is awaiting moderation.', 'default' ); // Note core string re-use.
|
461 |
+
}
|
462 |
+
|
463 |
+
/**
|
464 |
+
* Filters the message when comment submitted success message when
|
465 |
+
*
|
466 |
+
* @since 0.7
|
467 |
+
*/
|
468 |
+
$message = apply_filters( 'amp_comment_posted_message', $message, $comment );
|
469 |
+
|
470 |
+
// Message will be shown in template defined by AMP_Theme_Support::amend_comment_form().
|
471 |
+
wp_send_json( array(
|
472 |
+
'message' => self::wp_kses_amp_mustache( $message ),
|
473 |
+
) );
|
474 |
+
}
|
475 |
+
|
476 |
+
/**
|
477 |
+
* New error handler for AMP form submission.
|
478 |
+
*
|
479 |
+
* @since 0.7.0
|
480 |
+
* @see wp_die()
|
481 |
+
*
|
482 |
+
* @param WP_Error|string $error The error to handle.
|
483 |
+
* @param string|int $title Optional. Error title. If `$message` is a `WP_Error` object,
|
484 |
+
* error data with the key 'title' may be used to specify the title.
|
485 |
+
* If `$title` is an integer, then it is treated as the response
|
486 |
+
* code. Default empty.
|
487 |
+
* @param string|array|int $args {
|
488 |
+
* Optional. Arguments to control behavior. If `$args` is an integer, then it is treated
|
489 |
+
* as the response code. Default empty array.
|
490 |
+
*
|
491 |
+
* @type int $response The HTTP response code. Default 200 for Ajax requests, 500 otherwise.
|
492 |
+
* }
|
493 |
+
*/
|
494 |
+
public static function handle_wp_die( $error, $title = '', $args = array() ) {
|
495 |
+
if ( is_int( $title ) ) {
|
496 |
+
$status_code = $title;
|
497 |
+
} elseif ( is_int( $args ) ) {
|
498 |
+
$status_code = $args;
|
499 |
+
} elseif ( is_array( $args ) && isset( $args['response'] ) ) {
|
500 |
+
$status_code = $args['response'];
|
501 |
+
} else {
|
502 |
+
$status_code = 500;
|
503 |
+
}
|
504 |
+
status_header( $status_code );
|
505 |
+
|
506 |
+
if ( is_wp_error( $error ) ) {
|
507 |
+
$error = $error->get_error_message();
|
508 |
+
}
|
509 |
+
|
510 |
+
// Message will be shown in template defined by AMP_Theme_Support::amend_comment_form().
|
511 |
+
wp_send_json( array(
|
512 |
+
'error' => self::wp_kses_amp_mustache( $error ),
|
513 |
+
) );
|
514 |
+
}
|
515 |
+
|
516 |
+
/**
|
517 |
+
* Intercept the response to a POST request.
|
518 |
+
*
|
519 |
+
* @since 0.7.0
|
520 |
+
* @see wp_redirect()
|
521 |
+
*
|
522 |
+
* @param string $location The location to redirect to.
|
523 |
+
*/
|
524 |
+
public static function intercept_post_request_redirect( $location ) {
|
525 |
+
|
526 |
+
// Make sure relative redirects get made absolute.
|
527 |
+
$parsed_location = array_merge(
|
528 |
+
array(
|
529 |
+
'scheme' => 'https',
|
530 |
+
'host' => wp_parse_url( home_url(), PHP_URL_HOST ),
|
531 |
+
'path' => isset( $_SERVER['REQUEST_URI'] ) ? strtok( wp_unslash( $_SERVER['REQUEST_URI'] ), '?' ) : '/',
|
532 |
+
),
|
533 |
+
wp_parse_url( $location )
|
534 |
+
);
|
535 |
+
|
536 |
+
$absolute_location = '';
|
537 |
+
if ( 'https' === $parsed_location['scheme'] ) {
|
538 |
+
$absolute_location .= $parsed_location['scheme'] . ':';
|
539 |
+
}
|
540 |
+
$absolute_location .= '//' . $parsed_location['host'];
|
541 |
+
if ( isset( $parsed_location['port'] ) ) {
|
542 |
+
$absolute_location .= ':' . $parsed_location['port'];
|
543 |
+
}
|
544 |
+
$absolute_location .= $parsed_location['path'];
|
545 |
+
if ( isset( $parsed_location['query'] ) ) {
|
546 |
+
$absolute_location .= '?' . $parsed_location['query'];
|
547 |
+
}
|
548 |
+
if ( isset( $parsed_location['fragment'] ) ) {
|
549 |
+
$absolute_location .= '#' . $parsed_location['fragment'];
|
550 |
+
}
|
551 |
+
|
552 |
+
self::send_header( 'AMP-Redirect-To', $absolute_location );
|
553 |
+
self::send_header( 'Access-Control-Expose-Headers', 'AMP-Redirect-To' );
|
554 |
+
|
555 |
+
wp_send_json_success();
|
556 |
+
}
|
557 |
+
|
558 |
+
/**
|
559 |
+
* Register/override widgets.
|
560 |
+
*
|
561 |
+
* @global WP_Widget_Factory
|
562 |
+
* @return void
|
563 |
+
*/
|
564 |
+
public static function register_widgets() {
|
565 |
+
global $wp_widget_factory;
|
566 |
+
foreach ( $wp_widget_factory->widgets as $registered_widget ) {
|
567 |
+
$registered_widget_class_name = get_class( $registered_widget );
|
568 |
+
if ( ! preg_match( '/^WP_Widget_(.+)$/', $registered_widget_class_name, $matches ) ) {
|
569 |
+
continue;
|
570 |
+
}
|
571 |
+
$amp_class_name = 'AMP_Widget_' . $matches[1];
|
572 |
+
if ( ! class_exists( $amp_class_name ) || is_a( $amp_class_name, $registered_widget_class_name ) ) {
|
573 |
+
continue;
|
574 |
+
}
|
575 |
+
|
576 |
+
unregister_widget( $registered_widget_class_name );
|
577 |
+
register_widget( $amp_class_name );
|
578 |
+
}
|
579 |
+
}
|
580 |
+
|
581 |
+
/**
|
582 |
+
* Register content embed handlers.
|
583 |
+
*
|
584 |
+
* This was copied from `AMP_Content::register_embed_handlers()` due to being a private method
|
585 |
+
* and due to `AMP_Content` not being well suited for use in AMP canonical.
|
586 |
+
*
|
587 |
+
* @see AMP_Content::register_embed_handlers()
|
588 |
+
* @global int $content_width
|
589 |
+
* @return AMP_Base_Embed_Handler[] Handlers.
|
590 |
+
*/
|
591 |
+
public static function register_content_embed_handlers() {
|
592 |
+
global $content_width;
|
593 |
+
|
594 |
+
$embed_handlers = array();
|
595 |
+
foreach ( amp_get_content_embed_handlers() as $embed_handler_class => $args ) {
|
596 |
+
|
597 |
+
/**
|
598 |
+
* Embed handler.
|
599 |
+
*
|
600 |
+
* @type AMP_Base_Embed_Handler $embed_handler
|
601 |
+
*/
|
602 |
+
$embed_handler = new $embed_handler_class( array_merge(
|
603 |
+
array(
|
604 |
+
'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat.
|
605 |
+
),
|
606 |
+
$args
|
607 |
+
) );
|
608 |
+
|
609 |
+
if ( ! is_subclass_of( $embed_handler, 'AMP_Base_Embed_Handler' ) ) {
|
610 |
+
/* translators: %s is embed handler */
|
611 |
+
_doing_it_wrong( __METHOD__, esc_html( sprintf( __( 'Embed Handler (%s) must extend `AMP_Embed_Handler`', 'amp' ), $embed_handler_class ) ), '0.1' );
|
612 |
+
continue;
|
613 |
+
}
|
614 |
+
|
615 |
+
$embed_handler->register_embed();
|
616 |
+
$embed_handlers[] = $embed_handler;
|
617 |
+
}
|
618 |
+
|
619 |
+
return $embed_handlers;
|
620 |
+
}
|
621 |
+
|
622 |
+
/**
|
623 |
+
* Add the comments template placeholder marker
|
624 |
+
*
|
625 |
+
* @param array $args the args for the comments list..
|
626 |
+
* @return array Args to return.
|
627 |
+
*/
|
628 |
+
public static function set_comments_walker( $args ) {
|
629 |
+
$amp_walker = new AMP_Comment_Walker();
|
630 |
+
$args['walker'] = $amp_walker;
|
631 |
+
return $args;
|
632 |
+
}
|
633 |
+
|
634 |
+
/**
|
635 |
+
* Adds the form submit success and fail templates.
|
636 |
+
*/
|
637 |
+
public static function amend_comment_form() {
|
638 |
+
?>
|
639 |
+
<?php if ( is_singular() && ! amp_is_canonical() ) : ?>
|
640 |
+
<input type="hidden" name="redirect_to" value="<?php echo esc_url( amp_get_permalink( get_the_ID() ) ); ?>">
|
641 |
+
<?php endif; ?>
|
642 |
+
|
643 |
+
<div submit-success>
|
644 |
+
<template type="amp-mustache">
|
645 |
+
<p>{{{message}}}</p>
|
646 |
+
</template>
|
647 |
+
</div>
|
648 |
+
<div submit-error>
|
649 |
+
<template type="amp-mustache">
|
650 |
+
<p class="amp-comment-submit-error">{{{error}}}</p>
|
651 |
+
</template>
|
652 |
+
</div>
|
653 |
+
<?php
|
654 |
+
}
|
655 |
+
|
656 |
+
/**
|
657 |
+
* Prepends template hierarchy with template_dir for AMP paired mode templates.
|
658 |
+
*
|
659 |
+
* @see get_query_template()
|
660 |
+
*
|
661 |
+
* @param array $templates Template hierarchy.
|
662 |
+
* @returns array Templates.
|
663 |
+
*/
|
664 |
+
public static function filter_paired_template_hierarchy( $templates ) {
|
665 |
+
$support = get_theme_support( 'amp' );
|
666 |
+
$args = array_shift( $support );
|
667 |
+
if ( isset( $args['template_dir'] ) ) {
|
668 |
+
$amp_templates = array();
|
669 |
+
foreach ( $templates as $template ) {
|
670 |
+
$amp_templates[] = $args['template_dir'] . '/' . $template;
|
671 |
+
}
|
672 |
+
$templates = $amp_templates;
|
673 |
+
}
|
674 |
+
return $templates;
|
675 |
+
}
|
676 |
+
|
677 |
+
/**
|
678 |
+
* Redirect to the non-canonical URL when the template to include is empty.
|
679 |
+
*
|
680 |
+
* This is a failsafe in case an index.php is not located in the AMP template_dir,
|
681 |
+
* and the available_callback fails to omit a given request from being available in AMP.
|
682 |
+
*
|
683 |
+
* @param string $template Template to include.
|
684 |
+
* @return string Template to include.
|
685 |
+
*/
|
686 |
+
public static function filter_paired_template_include( $template ) {
|
687 |
+
if ( empty( $template ) || ! self::is_paired_available() ) {
|
688 |
+
wp_safe_redirect( self::get_current_canonical_url(), 302 ); // Temporary redirect because support may come later.
|
689 |
+
exit;
|
690 |
+
}
|
691 |
+
return $template;
|
692 |
+
}
|
693 |
+
|
694 |
+
/**
|
695 |
+
* Get canonical URL for current request.
|
696 |
+
*
|
697 |
+
* @see rel_canonical()
|
698 |
+
* @global WP $wp
|
699 |
+
* @global WP_Rewrite $wp_rewrite
|
700 |
+
* @link https://www.ampproject.org/docs/reference/spec#canon.
|
701 |
+
* @link https://core.trac.wordpress.org/ticket/18660
|
702 |
+
*
|
703 |
+
* @return string Canonical non-AMP URL.
|
704 |
+
*/
|
705 |
+
public static function get_current_canonical_url() {
|
706 |
+
global $wp, $wp_rewrite;
|
707 |
+
|
708 |
+
$url = null;
|
709 |
+
if ( is_singular() ) {
|
710 |
+
$url = wp_get_canonical_url();
|
711 |
+
}
|
712 |
+
|
713 |
+
// For non-singular queries, make use of the request URI and public query vars to determine canonical URL.
|
714 |
+
if ( empty( $url ) ) {
|
715 |
+
$added_query_vars = $wp->query_vars;
|
716 |
+
if ( ! $wp_rewrite->permalink_structure || empty( $wp->request ) ) {
|
717 |
+
$url = home_url( '/' );
|
718 |
+
} else {
|
719 |
+
$url = home_url( user_trailingslashit( $wp->request ) );
|
720 |
+
parse_str( $wp->matched_query, $matched_query_vars );
|
721 |
+
foreach ( $wp->query_vars as $key => $value ) {
|
722 |
+
|
723 |
+
// Remove query vars that were matched in the rewrite rules for the request.
|
724 |
+
if ( isset( $matched_query_vars[ $key ] ) ) {
|
725 |
+
unset( $added_query_vars[ $key ] );
|
726 |
+
}
|
727 |
+
}
|
728 |
+
}
|
729 |
+
}
|
730 |
+
|
731 |
+
if ( ! empty( $added_query_vars ) ) {
|
732 |
+
$url = add_query_arg( $added_query_vars, $url );
|
733 |
+
}
|
734 |
+
|
735 |
+
return amp_remove_endpoint( $url );
|
736 |
+
}
|
737 |
+
|
738 |
+
/**
|
739 |
+
* Get the ID for the amp-state.
|
740 |
+
*
|
741 |
+
* @since 0.7
|
742 |
+
*
|
743 |
+
* @param int $post_id Post ID.
|
744 |
+
* @return string ID for amp-state.
|
745 |
+
*/
|
746 |
+
public static function get_comment_form_state_id( $post_id ) {
|
747 |
+
return sprintf( 'commentform_post_%d', $post_id );
|
748 |
+
}
|
749 |
+
|
750 |
+
/**
|
751 |
+
* Filter comment form args to an element with [text] AMP binding wrap the title reply.
|
752 |
+
*
|
753 |
+
* @since 0.7
|
754 |
+
* @see comment_form()
|
755 |
+
*
|
756 |
+
* @param array $args Comment form args.
|
757 |
+
* @return array Filtered comment form args.
|
758 |
+
*/
|
759 |
+
public static function filter_comment_form_defaults( $args ) {
|
760 |
+
$state_id = self::get_comment_form_state_id( get_the_ID() );
|
761 |
+
|
762 |
+
$text_binding = sprintf(
|
763 |
+
'%s.replyToName ? %s : %s',
|
764 |
+
$state_id,
|
765 |
+
str_replace(
|
766 |
+
'%s',
|
767 |
+
sprintf( '" + %s.replyToName + "', $state_id ),
|
768 |
+
wp_json_encode( $args['title_reply_to'] )
|
769 |
+
),
|
770 |
+
wp_json_encode( $args['title_reply'] )
|
771 |
+
);
|
772 |
+
|
773 |
+
$args['title_reply_before'] .= sprintf(
|
774 |
+
'<span [text]="%s">',
|
775 |
+
esc_attr( $text_binding )
|
776 |
+
);
|
777 |
+
$args['cancel_reply_before'] = '</span>' . $args['cancel_reply_before'];
|
778 |
+
return $args;
|
779 |
+
}
|
780 |
+
|
781 |
+
/**
|
782 |
+
* Modify the comment reply link for AMP.
|
783 |
+
*
|
784 |
+
* @since 0.7
|
785 |
+
* @see get_comment_reply_link()
|
786 |
+
*
|
787 |
+
* @param string $link The HTML markup for the comment reply link.
|
788 |
+
* @param array $args An array of arguments overriding the defaults.
|
789 |
+
* @param WP_Comment $comment The object of the comment being replied.
|
790 |
+
* @return string Comment reply link.
|
791 |
+
*/
|
792 |
+
public static function filter_comment_reply_link( $link, $args, $comment ) {
|
793 |
+
|
794 |
+
// Continue to show default link to wp-login when user is not logged-in.
|
795 |
+
if ( get_option( 'comment_registration' ) && ! is_user_logged_in() ) {
|
796 |
+
return $link;
|
797 |
+
}
|
798 |
+
|
799 |
+
$state_id = self::get_comment_form_state_id( get_the_ID() );
|
800 |
+
$tap_state = array(
|
801 |
+
$state_id => array(
|
802 |
+
'replyToName' => $comment->comment_author,
|
803 |
+
'values' => array(
|
804 |
+
'comment_parent' => (string) $comment->comment_ID,
|
805 |
+
),
|
806 |
+
),
|
807 |
+
);
|
808 |
+
|
809 |
+
// @todo Figure out how to support add_below. Instead of moving the form, what about letting the form get a fixed position?
|
810 |
+
$link = sprintf(
|
811 |
+
'<a rel="nofollow" class="comment-reply-link" href="%s" on="%s" aria-label="%s">%s</a>',
|
812 |
+
esc_attr( '#' . $args['respond_id'] ),
|
813 |
+
esc_attr( sprintf( 'tap:AMP.setState( %s )', wp_json_encode( $tap_state ) ) ),
|
814 |
+
esc_attr( sprintf( $args['reply_to_text'], $comment->comment_author ) ),
|
815 |
+
$args['reply_text']
|
816 |
+
);
|
817 |
+
return $link;
|
818 |
+
}
|
819 |
+
|
820 |
+
/**
|
821 |
+
* Filters the cancel comment reply link HTML.
|
822 |
+
*
|
823 |
+
* @since 0.7
|
824 |
+
* @see get_cancel_comment_reply_link()
|
825 |
+
*
|
826 |
+
* @param string $formatted_link The HTML-formatted cancel comment reply link.
|
827 |
+
* @param string $link Cancel comment reply link URL.
|
828 |
+
* @param string $text Cancel comment reply link text.
|
829 |
+
* @return string Cancel reply link.
|
830 |
+
*/
|
831 |
+
public static function filter_cancel_comment_reply_link( $formatted_link, $link, $text ) {
|
832 |
+
unset( $formatted_link, $link );
|
833 |
+
if ( empty( $text ) ) {
|
834 |
+
$text = __( 'Click here to cancel reply.', 'default' );
|
835 |
+
}
|
836 |
+
|
837 |
+
$state_id = self::get_comment_form_state_id( get_the_ID() );
|
838 |
+
$tap_state = array(
|
839 |
+
$state_id => array(
|
840 |
+
'replyToName' => '',
|
841 |
+
'values' => array(
|
842 |
+
'comment_parent' => '0',
|
843 |
+
),
|
844 |
+
),
|
845 |
+
);
|
846 |
+
|
847 |
+
$respond_id = 'respond'; // Hard-coded in comment_form() and default value in get_comment_reply_link().
|
848 |
+
return sprintf(
|
849 |
+
'<a id="cancel-comment-reply-link" href="%s" %s [hidden]="%s" on="%s">%s</a>',
|
850 |
+
esc_url( remove_query_arg( 'replytocom' ) . '#' . $respond_id ),
|
851 |
+
isset( $_GET['replytocom'] ) ? '' : ' hidden', // phpcs:ignore
|
852 |
+
esc_attr( sprintf( '%s.values.comment_parent == "0"', self::get_comment_form_state_id( get_the_ID() ) ) ),
|
853 |
+
esc_attr( sprintf( 'tap:AMP.setState( %s )', wp_json_encode( $tap_state ) ) ),
|
854 |
+
esc_html( $text )
|
855 |
+
);
|
856 |
+
}
|
857 |
+
|
858 |
+
/**
|
859 |
+
* Print AMP boilerplate and custom styles.
|
860 |
+
*/
|
861 |
+
public static function print_amp_styles() {
|
862 |
+
echo amp_get_boilerplate_code() . "\n"; // WPCS: XSS OK.
|
863 |
+
echo "<style amp-custom></style>\n"; // This will by populated by AMP_Style_Sanitizer.
|
864 |
+
}
|
865 |
+
|
866 |
+
/**
|
867 |
+
* Ensure markup required by AMP <https://www.ampproject.org/docs/reference/spec#required-markup>.
|
868 |
+
*
|
869 |
+
* Ensure meta[charset], meta[name=viewport], and link[rel=canonical]; a the whitelist sanitizer
|
870 |
+
* may have removed an illegal meta[http-equiv] or meta[name=viewport]. Core only outputs a
|
871 |
+
* canonical URL by default if a singular post.
|
872 |
+
*
|
873 |
+
* @since 0.7
|
874 |
+
* @todo All of this might be better placed inside of a sanitizer.
|
875 |
+
*
|
876 |
+
* @param DOMDocument $dom Doc.
|
877 |
+
*/
|
878 |
+
public static function ensure_required_markup( DOMDocument $dom ) {
|
879 |
+
$head = $dom->getElementsByTagName( 'head' )->item( 0 );
|
880 |
+
if ( ! $head ) {
|
881 |
+
$head = $dom->createElement( 'head' );
|
882 |
+
$dom->documentElement->insertBefore( $head, $dom->documentElement->firstChild );
|
883 |
+
}
|
884 |
+
$meta_charset = null;
|
885 |
+
$meta_viewport = null;
|
886 |
+
foreach ( $head->getElementsByTagName( 'meta' ) as $meta ) {
|
887 |
+
/**
|
888 |
+
* Meta.
|
889 |
+
*
|
890 |
+
* @var DOMElement $meta
|
891 |
+
*/
|
892 |
+
if ( $meta->hasAttribute( 'charset' ) && 'utf-8' === strtolower( $meta->getAttribute( 'charset' ) ) ) { // @todo Also look for meta[http-equiv="Content-Type"]?
|
893 |
+
$meta_charset = $meta;
|
894 |
+
} elseif ( 'viewport' === $meta->getAttribute( 'name' ) ) {
|
895 |
+
$meta_viewport = $meta;
|
896 |
+
}
|
897 |
+
}
|
898 |
+
if ( ! $meta_charset ) {
|
899 |
+
// Warning: This probably means the character encoding needs to be converted.
|
900 |
+
$meta_charset = AMP_DOM_Utils::create_node( $dom, 'meta', array(
|
901 |
+
'charset' => 'utf-8',
|
902 |
+
) );
|
903 |
+
$head->insertBefore( $meta_charset, $head->firstChild );
|
904 |
+
}
|
905 |
+
if ( ! $meta_viewport ) {
|
906 |
+
$meta_viewport = AMP_DOM_Utils::create_node( $dom, 'meta', array(
|
907 |
+
'name' => 'viewport',
|
908 |
+
'content' => 'width=device-width,minimum-scale=1',
|
909 |
+
) );
|
910 |
+
$head->insertBefore( $meta_viewport, $meta_charset->nextSibling );
|
911 |
+
}
|
912 |
+
// Prevent schema.org duplicates.
|
913 |
+
$has_schema_org_metadata = false;
|
914 |
+
foreach ( $head->getElementsByTagName( 'script' ) as $script ) {
|
915 |
+
if ( 'application/ld+json' === $script->getAttribute( 'type' ) && false !== strpos( $script->nodeValue, 'schema.org' ) ) {
|
916 |
+
$has_schema_org_metadata = true;
|
917 |
+
break;
|
918 |
+
}
|
919 |
+
}
|
920 |
+
if ( ! $has_schema_org_metadata ) {
|
921 |
+
$script = $dom->createElement( 'script', wp_json_encode( amp_get_schemaorg_metadata() ) );
|
922 |
+
$script->setAttribute( 'type', 'application/ld+json' );
|
923 |
+
$head->appendChild( $script );
|
924 |
+
}
|
925 |
+
// Ensure rel=canonical link.
|
926 |
+
$rel_canonical = null;
|
927 |
+
foreach ( $head->getElementsByTagName( 'link' ) as $link ) {
|
928 |
+
if ( 'canonical' === $link->getAttribute( 'rel' ) ) {
|
929 |
+
$rel_canonical = $link;
|
930 |
+
break;
|
931 |
+
}
|
932 |
+
}
|
933 |
+
if ( ! $rel_canonical ) {
|
934 |
+
$rel_canonical = AMP_DOM_Utils::create_node( $dom, 'link', array(
|
935 |
+
'rel' => 'canonical',
|
936 |
+
'href' => self::get_current_canonical_url(),
|
937 |
+
) );
|
938 |
+
$head->appendChild( $rel_canonical );
|
939 |
+
}
|
940 |
+
}
|
941 |
+
|
942 |
+
/**
|
943 |
+
* Dequeue Customizer assets which are not necessary outside the preview iframe.
|
944 |
+
*
|
945 |
+
* Prevent enqueueing customize-preview styles if not in customizer preview iframe.
|
946 |
+
* These are only needed for when there is live editing of content, such as selective refresh.
|
947 |
+
*
|
948 |
+
* @since 0.7
|
949 |
+
*/
|
950 |
+
public static function dequeue_customize_preview_scripts() {
|
951 |
+
|
952 |
+
// Dequeue styles unnecessary unless in customizer preview iframe when editing (such as for edit shortcuts).
|
953 |
+
if ( ! self::is_customize_preview_iframe() ) {
|
954 |
+
wp_dequeue_style( 'customize-preview' );
|
955 |
+
foreach ( wp_styles()->registered as $handle => $dependency ) {
|
956 |
+
if ( in_array( 'customize-preview', $dependency->deps, true ) ) {
|
957 |
+
wp_dequeue_style( $handle );
|
958 |
+
}
|
959 |
+
}
|
960 |
+
}
|
961 |
+
}
|
962 |
+
|
963 |
+
/**
|
964 |
+
* Start output buffering.
|
965 |
+
*
|
966 |
+
* @since 0.7
|
967 |
+
* @see AMP_Theme_Support::finish_output_buffering()
|
968 |
+
*/
|
969 |
+
public static function start_output_buffering() {
|
970 |
+
/*
|
971 |
+
* Disable the New Relic Browser agent on AMP responses.
|
972 |
+
* This prevents th New Relic from causing invalid AMP responses due the NREUM script it injects after the meta charset:
|
973 |
+
* https://docs.newrelic.com/docs/browser/new-relic-browser/troubleshooting/google-amp-validator-fails-due-3rd-party-script
|
974 |
+
* Sites with New Relic will need to specially configure New Relic for AMP:
|
975 |
+
* https://docs.newrelic.com/docs/browser/new-relic-browser/installation/monitor-amp-pages-new-relic-browser
|
976 |
+
*/
|
977 |
+
if ( function_exists( 'newrelic_disable_autorum' ) ) {
|
978 |
+
newrelic_disable_autorum();
|
979 |
+
}
|
980 |
+
|
981 |
+
ob_start( array( __CLASS__, 'finish_output_buffering' ) );
|
982 |
+
self::$is_output_buffering = true;
|
983 |
+
}
|
984 |
+
|
985 |
+
/**
|
986 |
+
* Determine whether output buffering has started.
|
987 |
+
*
|
988 |
+
* @since 0.7
|
989 |
+
* @see AMP_Theme_Support::start_output_buffering()
|
990 |
+
* @see AMP_Theme_Support::finish_output_buffering()
|
991 |
+
*
|
992 |
+
* @return bool Whether output buffering has started.
|
993 |
+
*/
|
994 |
+
public static function is_output_buffering() {
|
995 |
+
return self::$is_output_buffering;
|
996 |
+
}
|
997 |
+
|
998 |
+
/**
|
999 |
+
* Finish output buffering.
|
1000 |
+
*
|
1001 |
+
* @since 0.7
|
1002 |
+
* @see AMP_Theme_Support::start_output_buffering()
|
1003 |
+
*
|
1004 |
+
* @param string $response Buffered Response.
|
1005 |
+
* @return string Processed Response.
|
1006 |
+
*/
|
1007 |
+
public static function finish_output_buffering( $response ) {
|
1008 |
+
self::$is_output_buffering = false;
|
1009 |
+
return self::prepare_response( $response );
|
1010 |
+
}
|
1011 |
+
|
1012 |
+
/**
|
1013 |
+
* Filter rendered partial to convert to AMP.
|
1014 |
+
*
|
1015 |
+
* @see WP_Customize_Partial::render()
|
1016 |
+
*
|
1017 |
+
* @param string|mixed $partial Rendered partial.
|
1018 |
+
* @return string|mixed Filtered partial.
|
1019 |
+
* @global int $content_width
|
1020 |
+
*/
|
1021 |
+
public static function filter_customize_partial_render( $partial ) {
|
1022 |
+
global $content_width;
|
1023 |
+
if ( is_string( $partial ) && preg_match( '/<\w/', $partial ) ) {
|
1024 |
+
$dom = AMP_DOM_Utils::get_dom_from_content( $partial );
|
1025 |
+
$args = array(
|
1026 |
+
'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat.
|
1027 |
+
'use_document_element' => false,
|
1028 |
+
'allow_dirty_styles' => true,
|
1029 |
+
'allow_dirty_scripts' => false,
|
1030 |
+
);
|
1031 |
+
AMP_Content_Sanitizer::sanitize_document( $dom, self::$sanitizer_classes, $args ); // @todo Include script assets in response?
|
1032 |
+
$partial = AMP_DOM_Utils::get_content_from_dom( $dom );
|
1033 |
+
}
|
1034 |
+
return $partial;
|
1035 |
+
}
|
1036 |
+
|
1037 |
+
/**
|
1038 |
+
* Process response to ensure AMP validity.
|
1039 |
+
*
|
1040 |
+
* @since 0.7
|
1041 |
+
*
|
1042 |
+
* @param string $response HTML document response. By default it expects a complete document.
|
1043 |
+
* @param array $args {
|
1044 |
+
* Args to send to the preprocessor/sanitizer.
|
1045 |
+
*
|
1046 |
+
* @type callable $remove_invalid_callback Function to call whenever a node is removed due to being invalid.
|
1047 |
+
* }
|
1048 |
+
* @return string AMP document response.
|
1049 |
+
* @global int $content_width
|
1050 |
+
*/
|
1051 |
+
public static function prepare_response( $response, $args = array() ) {
|
1052 |
+
global $content_width;
|
1053 |
+
|
1054 |
+
/*
|
1055 |
+
* Check if the response starts with HTML markup.
|
1056 |
+
* Without this check, JSON responses will be erroneously corrupted,
|
1057 |
+
* being wrapped in HTML documents.
|
1058 |
+
*/
|
1059 |
+
if ( '<' !== substr( ltrim( $response ), 0, 1 ) ) {
|
1060 |
+
return $response;
|
1061 |
+
}
|
1062 |
+
|
1063 |
+
$is_validation_debug_mode = ! empty( $_REQUEST[ AMP_Validation_Utils::DEBUG_QUERY_VAR ] ); // WPCS: csrf ok.
|
1064 |
+
|
1065 |
+
$args = array_merge(
|
1066 |
+
array(
|
1067 |
+
'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat.
|
1068 |
+
'use_document_element' => true,
|
1069 |
+
'allow_dirty_styles' => self::is_customize_preview_iframe(), // Dirty styles only needed when editing (e.g. for edit shortcodes).
|
1070 |
+
'allow_dirty_scripts' => is_customize_preview(), // Scripts are always needed to inject changeset UUID.
|
1071 |
+
'disable_invalid_removal' => $is_validation_debug_mode,
|
1072 |
+
),
|
1073 |
+
$args
|
1074 |
+
);
|
1075 |
+
|
1076 |
+
/*
|
1077 |
+
* Make sure that <meta charset> is present in output prior to parsing.
|
1078 |
+
* Note that the meta charset is supposed to appear within the first 1024 bytes.
|
1079 |
+
* See <https://www.w3.org/International/questions/qa-html-encoding-declarations>.
|
1080 |
+
*/
|
1081 |
+
if ( ! preg_match( '#<meta[^>]+charset=#i', substr( $response, 0, 1024 ) ) ) {
|
1082 |
+
$response = preg_replace(
|
1083 |
+
'/(<head[^>]*>)/i',
|
1084 |
+
'$1' . sprintf( '<meta charset="%s">', esc_attr( get_bloginfo( 'charset' ) ) ),
|
1085 |
+
$response,
|
1086 |
+
1
|
1087 |
+
);
|
1088 |
+
}
|
1089 |
+
$dom = AMP_DOM_Utils::get_dom( $response );
|
1090 |
+
|
1091 |
+
$xpath = new DOMXPath( $dom );
|
1092 |
+
|
1093 |
+
$head = $dom->getElementsByTagName( 'head' )->item( 0 );
|
1094 |
+
|
1095 |
+
if ( isset( $head ) ) {
|
1096 |
+
// Make sure scripts from the body get moved to the head.
|
1097 |
+
foreach ( $xpath->query( '//body//script[ @custom-element or @custom-template ]' ) as $script ) {
|
1098 |
+
$head->appendChild( $script );
|
1099 |
+
}
|
1100 |
+
}
|
1101 |
+
|
1102 |
+
// Ensure the mandatory amp attribute is present on the html element, as otherwise it will be stripped entirely.
|
1103 |
+
if ( ! $dom->documentElement->hasAttribute( 'amp' ) && ! $dom->documentElement->hasAttribute( '⚡️' ) ) {
|
1104 |
+
$dom->documentElement->setAttribute( 'amp', '' );
|
1105 |
+
}
|
1106 |
+
|
1107 |
+
$assets = AMP_Content_Sanitizer::sanitize_document( $dom, self::$sanitizer_classes, $args );
|
1108 |
+
|
1109 |
+
self::ensure_required_markup( $dom );
|
1110 |
+
|
1111 |
+
// @todo If 'utf-8' is not the blog charset, then we'll need to do some character encoding conversation or "entityification".
|
1112 |
+
if ( 'utf-8' !== strtolower( get_bloginfo( 'charset' ) ) ) {
|
1113 |
+
/* translators: %s is the charset of the current site */
|
1114 |
+
trigger_error( esc_html( sprintf( __( 'The database has the %s encoding when it needs to be utf-8 to work with AMP.', 'amp' ), get_bloginfo( 'charset' ) ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
1115 |
+
}
|
1116 |
+
|
1117 |
+
if ( AMP_Validation_Utils::should_validate_response() ) {
|
1118 |
+
AMP_Validation_Utils::finalize_validation( $dom, array(
|
1119 |
+
'remove_source_comments' => ! $is_validation_debug_mode,
|
1120 |
+
) );
|
1121 |
+
}
|
1122 |
+
|
1123 |
+
$response = "<!DOCTYPE html>\n";
|
1124 |
+
$response .= AMP_DOM_Utils::get_content_from_dom_node( $dom, $dom->documentElement );
|
1125 |
+
|
1126 |
+
$amp_scripts = $assets['scripts'];
|
1127 |
+
foreach ( self::$embed_handlers as $embed_handler ) {
|
1128 |
+
$amp_scripts = array_merge(
|
1129 |
+
$amp_scripts,
|
1130 |
+
$embed_handler->get_scripts()
|
1131 |
+
);
|
1132 |
+
}
|
1133 |
+
|
1134 |
+
// Allow for embed handlers to override the default extension version by defining a different URL.
|
1135 |
+
foreach ( $amp_scripts as $handle => $value ) {
|
1136 |
+
if ( is_string( $value ) && wp_script_is( $handle, 'registered' ) ) {
|
1137 |
+
wp_scripts()->registered[ $handle ]->src = $value;
|
1138 |
+
}
|
1139 |
+
}
|
1140 |
+
|
1141 |
+
/*
|
1142 |
+
* Inject additional AMP component scripts which have been discovered by the sanitizers into the head.
|
1143 |
+
* This is adapted from wp_scripts()->do_items(), but it runs only the bare minimum required to output
|
1144 |
+
* the missing scripts, without allowing other filters to apply which may cause an invalid AMP response.
|
1145 |
+
*/
|
1146 |
+
$script_tags = '';
|
1147 |
+
foreach ( array_diff( array_keys( $amp_scripts ), wp_scripts()->done ) as $handle ) {
|
1148 |
+
if ( ! wp_script_is( $handle, 'registered' ) ) {
|
1149 |
+
continue;
|
1150 |
+
}
|
1151 |
+
$script_dep = wp_scripts()->registered[ $handle ];
|
1152 |
+
$script_tags .= amp_filter_script_loader_tag(
|
1153 |
+
sprintf(
|
1154 |
+
"<script type='text/javascript' src='%s'></script>\n", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
|
1155 |
+
esc_url( $script_dep->src )
|
1156 |
+
),
|
1157 |
+
$handle
|
1158 |
+
);
|
1159 |
+
}
|
1160 |
+
if ( ! empty( $script_tags ) ) {
|
1161 |
+
$response = preg_replace(
|
1162 |
+
'#(?=</head>)#',
|
1163 |
+
$script_tags,
|
1164 |
+
$response,
|
1165 |
+
1
|
1166 |
+
);
|
1167 |
+
}
|
1168 |
+
|
1169 |
+
return $response;
|
1170 |
+
}
|
1171 |
+
|
1172 |
+
/**
|
1173 |
+
* Adds 'data-amp-layout' to the allowed <img> attributes for wp_kses().
|
1174 |
+
*
|
1175 |
+
* @since 0.7
|
1176 |
+
*
|
1177 |
+
* @param array $context Allowed tags and their allowed attributes.
|
1178 |
+
* @return array $context Filtered allowed tags and attributes.
|
1179 |
+
*/
|
1180 |
+
public static function whitelist_layout_in_wp_kses_allowed_html( $context ) {
|
1181 |
+
if ( ! empty( $context['img']['width'] ) && ! empty( $context['img']['height'] ) ) {
|
1182 |
+
$context['img']['data-amp-layout'] = true;
|
1183 |
+
}
|
1184 |
+
|
1185 |
+
return $context;
|
1186 |
+
}
|
1187 |
+
|
1188 |
+
/**
|
1189 |
+
* Enqueue AMP assets if this is an AMP endpoint.
|
1190 |
+
*
|
1191 |
+
* @since 0.7
|
1192 |
+
*
|
1193 |
+
* @return void
|
1194 |
+
*/
|
1195 |
+
public static function enqueue_assets() {
|
1196 |
+
wp_enqueue_script( 'amp-runtime' );
|
1197 |
+
|
1198 |
+
// Enqueue default styles expected by sanitizer.
|
1199 |
+
wp_enqueue_style( 'amp-default', amp_get_asset_url( 'css/amp-default.css' ), array(), AMP__VERSION );
|
1200 |
+
}
|
1201 |
+
}
|
@@ -27,6 +27,16 @@ abstract class AMP_Base_Embed_Handler {
|
|
27 |
) );
|
28 |
}
|
29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
public function get_scripts() {
|
31 |
return array();
|
32 |
}
|
27 |
) );
|
28 |
}
|
29 |
|
30 |
+
/**
|
31 |
+
* Get mapping of AMP component names to AMP script URLs.
|
32 |
+
*
|
33 |
+
* This is normally no longer needed because the whitelist
|
34 |
+
* sanitizer will automatically detect the need for them via
|
35 |
+
* the spec.
|
36 |
+
*
|
37 |
+
* @see AMP_Tag_And_Attribute_Sanitizer::get_scripts()
|
38 |
+
* @return array Scripts.
|
39 |
+
*/
|
40 |
public function get_scripts() {
|
41 |
return array();
|
42 |
}
|
@@ -18,9 +18,6 @@ class AMP_DailyMotion_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
18 |
protected $DEFAULT_WIDTH = 600;
|
19 |
protected $DEFAULT_HEIGHT = 338;
|
20 |
|
21 |
-
private static $script_slug = 'amp-dailymotion';
|
22 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-dailymotion-0.1.js';
|
23 |
-
|
24 |
function __construct( $args = array() ) {
|
25 |
parent::__construct( $args );
|
26 |
|
@@ -41,14 +38,6 @@ class AMP_DailyMotion_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
41 |
remove_shortcode( 'dailymotion' );
|
42 |
}
|
43 |
|
44 |
-
public function get_scripts() {
|
45 |
-
if ( ! $this->did_convert_elements ) {
|
46 |
-
return array();
|
47 |
-
}
|
48 |
-
|
49 |
-
return array( self::$script_slug => self::$script_src );
|
50 |
-
}
|
51 |
-
|
52 |
public function shortcode( $attr ) {
|
53 |
$video_id = false;
|
54 |
|
18 |
protected $DEFAULT_WIDTH = 600;
|
19 |
protected $DEFAULT_HEIGHT = 338;
|
20 |
|
|
|
|
|
|
|
21 |
function __construct( $args = array() ) {
|
22 |
parent::__construct( $args );
|
23 |
|
38 |
remove_shortcode( 'dailymotion' );
|
39 |
}
|
40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
public function shortcode( $attr ) {
|
42 |
$video_id = false;
|
43 |
|
@@ -14,9 +14,6 @@ class AMP_Facebook_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
14 |
protected $DEFAULT_WIDTH = 600;
|
15 |
protected $DEFAULT_HEIGHT = 400;
|
16 |
|
17 |
-
private static $script_slug = 'amp-facebook';
|
18 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-facebook-0.1.js';
|
19 |
-
|
20 |
public function register_embed() {
|
21 |
wp_embed_register_handler( 'amp-facebook', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
22 |
}
|
@@ -25,14 +22,6 @@ class AMP_Facebook_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
25 |
wp_embed_unregister_handler( 'amp-facebook', -1 );
|
26 |
}
|
27 |
|
28 |
-
public function get_scripts() {
|
29 |
-
if ( ! $this->did_convert_elements ) {
|
30 |
-
return array();
|
31 |
-
}
|
32 |
-
|
33 |
-
return array( self::$script_slug => self::$script_src );
|
34 |
-
}
|
35 |
-
|
36 |
public function oembed( $matches, $attr, $url, $rawattr ) {
|
37 |
return $this->render( array( 'url' => $url ) );
|
38 |
}
|
14 |
protected $DEFAULT_WIDTH = 600;
|
15 |
protected $DEFAULT_HEIGHT = 400;
|
16 |
|
|
|
|
|
|
|
17 |
public function register_embed() {
|
18 |
wp_embed_register_handler( 'amp-facebook', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
19 |
}
|
22 |
wp_embed_unregister_handler( 'amp-facebook', -1 );
|
23 |
}
|
24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
public function oembed( $matches, $attr, $url, $rawattr ) {
|
26 |
return $this->render( array( 'url' => $url ) );
|
27 |
}
|
@@ -7,27 +7,29 @@
|
|
7 |
|
8 |
/**
|
9 |
* Class AMP_Gallery_Embed_Handler
|
|
|
|
|
10 |
*/
|
11 |
class AMP_Gallery_Embed_Handler extends AMP_Base_Embed_Handler {
|
12 |
-
private static $script_slug = 'amp-carousel';
|
13 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-carousel-0.1.js';
|
14 |
|
|
|
|
|
|
|
15 |
public function register_embed() {
|
16 |
-
|
17 |
-
}
|
18 |
-
|
19 |
-
public function unregister_embed() {
|
20 |
-
remove_shortcode( 'gallery' );
|
21 |
-
}
|
22 |
-
|
23 |
-
public function get_scripts() {
|
24 |
-
if ( ! $this->did_convert_elements ) {
|
25 |
-
return array();
|
26 |
-
}
|
27 |
-
|
28 |
-
return array( self::$script_slug => self::$script_src );
|
29 |
}
|
30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
public function shortcode( $attr ) {
|
32 |
$post = get_post();
|
33 |
|
@@ -40,46 +42,47 @@ class AMP_Gallery_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
40 |
}
|
41 |
|
42 |
$atts = shortcode_atts( array(
|
43 |
-
'order'
|
44 |
-
'orderby'
|
45 |
-
'id'
|
46 |
-
'include'
|
47 |
-
'exclude'
|
48 |
-
'size'
|
|
|
49 |
), $attr, 'gallery' );
|
50 |
|
51 |
$id = intval( $atts['id'] );
|
52 |
|
53 |
if ( ! empty( $atts['include'] ) ) {
|
54 |
$attachments = get_posts( array(
|
55 |
-
'include'
|
56 |
-
'post_status'
|
57 |
-
'post_type'
|
58 |
'post_mime_type' => 'image',
|
59 |
-
'order'
|
60 |
-
'orderby'
|
61 |
-
'fields'
|
62 |
) );
|
63 |
} elseif ( ! empty( $atts['exclude'] ) ) {
|
64 |
$attachments = get_children( array(
|
65 |
-
'post_parent'
|
66 |
-
'exclude'
|
67 |
-
'post_status'
|
68 |
-
'post_type'
|
69 |
'post_mime_type' => 'image',
|
70 |
-
'order'
|
71 |
-
'orderby'
|
72 |
-
'fields'
|
73 |
) );
|
74 |
} else {
|
75 |
$attachments = get_children( array(
|
76 |
-
'post_parent'
|
77 |
-
'post_status'
|
78 |
-
'post_type'
|
79 |
'post_mime_type' => 'image',
|
80 |
-
'order'
|
81 |
-
'orderby'
|
82 |
-
'fields'
|
83 |
) );
|
84 |
}
|
85 |
|
@@ -95,9 +98,17 @@ class AMP_Gallery_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
95 |
continue;
|
96 |
}
|
97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
$urls[] = array(
|
99 |
-
'
|
100 |
-
'
|
|
|
101 |
'height' => $height,
|
102 |
);
|
103 |
}
|
@@ -107,6 +118,26 @@ class AMP_Gallery_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
107 |
) );
|
108 |
}
|
109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
public function render( $args ) {
|
111 |
$this->did_convert_elements = true;
|
112 |
|
@@ -119,24 +150,36 @@ class AMP_Gallery_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
119 |
}
|
120 |
|
121 |
$images = array();
|
122 |
-
foreach ( $args['images'] as $
|
123 |
-
$
|
124 |
'amp-img',
|
125 |
array(
|
126 |
-
'src'
|
127 |
-
'width'
|
128 |
-
'height' => $
|
129 |
'layout' => 'responsive',
|
130 |
)
|
131 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
}
|
133 |
|
134 |
return AMP_HTML_Utils::build_tag(
|
135 |
'amp-carousel',
|
136 |
array(
|
137 |
-
'width'
|
138 |
'height' => $this->args['height'],
|
139 |
-
'type'
|
140 |
'layout' => 'responsive',
|
141 |
),
|
142 |
implode( PHP_EOL, $images )
|
7 |
|
8 |
/**
|
9 |
* Class AMP_Gallery_Embed_Handler
|
10 |
+
*
|
11 |
+
* @since 0.2
|
12 |
*/
|
13 |
class AMP_Gallery_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
|
|
|
14 |
|
15 |
+
/**
|
16 |
+
* Register embed.
|
17 |
+
*/
|
18 |
public function register_embed() {
|
19 |
+
add_filter( 'post_gallery', array( $this, 'override_gallery' ), 10, 2 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
}
|
21 |
|
22 |
+
/**
|
23 |
+
* Unregister embed.
|
24 |
+
*/
|
25 |
+
public function unregister_embed() {}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Shortcode handler.
|
29 |
+
*
|
30 |
+
* @param array $attr Shortcode attributes.
|
31 |
+
* @return string Rendered gallery.
|
32 |
+
*/
|
33 |
public function shortcode( $attr ) {
|
34 |
$post = get_post();
|
35 |
|
42 |
}
|
43 |
|
44 |
$atts = shortcode_atts( array(
|
45 |
+
'order' => 'ASC',
|
46 |
+
'orderby' => 'menu_order ID',
|
47 |
+
'id' => $post ? $post->ID : 0,
|
48 |
+
'include' => '',
|
49 |
+
'exclude' => '',
|
50 |
+
'size' => array( $this->args['width'], $this->args['height'] ),
|
51 |
+
'link' => 'none',
|
52 |
), $attr, 'gallery' );
|
53 |
|
54 |
$id = intval( $atts['id'] );
|
55 |
|
56 |
if ( ! empty( $atts['include'] ) ) {
|
57 |
$attachments = get_posts( array(
|
58 |
+
'include' => $atts['include'],
|
59 |
+
'post_status' => 'inherit',
|
60 |
+
'post_type' => 'attachment',
|
61 |
'post_mime_type' => 'image',
|
62 |
+
'order' => $atts['order'],
|
63 |
+
'orderby' => $atts['orderby'],
|
64 |
+
'fields' => 'ids',
|
65 |
) );
|
66 |
} elseif ( ! empty( $atts['exclude'] ) ) {
|
67 |
$attachments = get_children( array(
|
68 |
+
'post_parent' => $id,
|
69 |
+
'exclude' => $atts['exclude'],
|
70 |
+
'post_status' => 'inherit',
|
71 |
+
'post_type' => 'attachment',
|
72 |
'post_mime_type' => 'image',
|
73 |
+
'order' => $atts['order'],
|
74 |
+
'orderby' => $atts['orderby'],
|
75 |
+
'fields' => 'ids',
|
76 |
) );
|
77 |
} else {
|
78 |
$attachments = get_children( array(
|
79 |
+
'post_parent' => $id,
|
80 |
+
'post_status' => 'inherit',
|
81 |
+
'post_type' => 'attachment',
|
82 |
'post_mime_type' => 'image',
|
83 |
+
'order' => $atts['order'],
|
84 |
+
'orderby' => $atts['orderby'],
|
85 |
+
'fields' => 'ids',
|
86 |
) );
|
87 |
}
|
88 |
|
98 |
continue;
|
99 |
}
|
100 |
|
101 |
+
$href = null;
|
102 |
+
if ( ! empty( $atts['link'] ) && 'file' === $atts['link'] ) {
|
103 |
+
$href = $url;
|
104 |
+
} elseif ( ! empty( $atts['link'] ) && 'post' === $atts['link'] ) {
|
105 |
+
$href = get_attachment_link( $attachment_id );
|
106 |
+
}
|
107 |
+
|
108 |
$urls[] = array(
|
109 |
+
'href' => $href,
|
110 |
+
'url' => $url,
|
111 |
+
'width' => $width,
|
112 |
'height' => $height,
|
113 |
);
|
114 |
}
|
118 |
) );
|
119 |
}
|
120 |
|
121 |
+
/**
|
122 |
+
* Override the output of gallery_shortcode().
|
123 |
+
*
|
124 |
+
* The 'Gallery' widget also uses this function.
|
125 |
+
* This ensures that it outputs an <amp-carousel>.
|
126 |
+
*
|
127 |
+
* @param string $html Markup to filter, possibly ''.
|
128 |
+
* @param array $attributes Shortcode attributes.
|
129 |
+
* @return string $html Markup for the gallery.
|
130 |
+
*/
|
131 |
+
public function override_gallery( $html, $attributes ) {
|
132 |
+
return $this->shortcode( $attributes );
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Render.
|
137 |
+
*
|
138 |
+
* @param array $args Args.
|
139 |
+
* @return string Rendered.
|
140 |
+
*/
|
141 |
public function render( $args ) {
|
142 |
$this->did_convert_elements = true;
|
143 |
|
150 |
}
|
151 |
|
152 |
$images = array();
|
153 |
+
foreach ( $args['images'] as $props ) {
|
154 |
+
$image = AMP_HTML_Utils::build_tag(
|
155 |
'amp-img',
|
156 |
array(
|
157 |
+
'src' => $props['url'],
|
158 |
+
'width' => $props['width'],
|
159 |
+
'height' => $props['height'],
|
160 |
'layout' => 'responsive',
|
161 |
)
|
162 |
);
|
163 |
+
|
164 |
+
if ( ! empty( $props['href'] ) ) {
|
165 |
+
$image = AMP_HTML_Utils::build_tag(
|
166 |
+
'a',
|
167 |
+
array(
|
168 |
+
'href' => $props['href'],
|
169 |
+
),
|
170 |
+
$image
|
171 |
+
);
|
172 |
+
}
|
173 |
+
|
174 |
+
$images[] = $image;
|
175 |
}
|
176 |
|
177 |
return AMP_HTML_Utils::build_tag(
|
178 |
'amp-carousel',
|
179 |
array(
|
180 |
+
'width' => $this->args['width'],
|
181 |
'height' => $this->args['height'],
|
182 |
+
'type' => 'slides',
|
183 |
'layout' => 'responsive',
|
184 |
),
|
185 |
implode( PHP_EOL, $images )
|
@@ -17,9 +17,6 @@ class AMP_Instagram_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
17 |
protected $DEFAULT_WIDTH = 600;
|
18 |
protected $DEFAULT_HEIGHT = 600;
|
19 |
|
20 |
-
private static $script_slug = 'amp-instagram';
|
21 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-instagram-0.1.js';
|
22 |
-
|
23 |
public function register_embed() {
|
24 |
wp_embed_register_handler( 'amp-instagram', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
25 |
add_shortcode( 'instagram', array( $this, 'shortcode' ) );
|
@@ -30,14 +27,6 @@ class AMP_Instagram_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
30 |
remove_shortcode( 'instagram' );
|
31 |
}
|
32 |
|
33 |
-
public function get_scripts() {
|
34 |
-
if ( ! $this->did_convert_elements ) {
|
35 |
-
return array();
|
36 |
-
}
|
37 |
-
|
38 |
-
return array( self::$script_slug => self::$script_src );
|
39 |
-
}
|
40 |
-
|
41 |
public function shortcode( $attr ) {
|
42 |
$url = false;
|
43 |
|
17 |
protected $DEFAULT_WIDTH = 600;
|
18 |
protected $DEFAULT_HEIGHT = 600;
|
19 |
|
|
|
|
|
|
|
20 |
public function register_embed() {
|
21 |
wp_embed_register_handler( 'amp-instagram', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
22 |
add_shortcode( 'instagram', array( $this, 'shortcode' ) );
|
27 |
remove_shortcode( 'instagram' );
|
28 |
}
|
29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
public function shortcode( $attr ) {
|
31 |
$url = false;
|
32 |
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Issuu_Embed_Handler
|
4 |
+
*
|
5 |
+
* @package AMP
|
6 |
+
* @since 0.7
|
7 |
+
*/
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class AMP_Issuu_Embed_Handler
|
11 |
+
*/
|
12 |
+
class AMP_Issuu_Embed_Handler extends AMP_Base_Embed_Handler {
|
13 |
+
/**
|
14 |
+
* Regex matched to produce output amp-iframe.
|
15 |
+
*
|
16 |
+
* @const string
|
17 |
+
*/
|
18 |
+
const URL_PATTERN = '#https?://(www\.)?issuu\.com/.+/docs/.+#i';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Register embed.
|
22 |
+
*/
|
23 |
+
public function register_embed() {
|
24 |
+
add_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10, 3 );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Unregister embed.
|
29 |
+
*/
|
30 |
+
public function unregister_embed() {
|
31 |
+
remove_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10 );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Filter oEmbed HTML for Meetup to prepare it for AMP.
|
36 |
+
*
|
37 |
+
* @param mixed $return The shortcode callback function to call.
|
38 |
+
* @param string $url The attempted embed URL.
|
39 |
+
* @param array $attr An array of shortcode attributes.
|
40 |
+
* @return string Embed.
|
41 |
+
*/
|
42 |
+
public function filter_embed_oembed_html( $return, $url, $attr ) {
|
43 |
+
$parsed_url = wp_parse_url( $url );
|
44 |
+
if ( false !== strpos( $parsed_url['host'], 'issuu.com' ) ) {
|
45 |
+
if ( preg_match( '/width\s*:\s*(\d+)px/', $return, $matches ) ) {
|
46 |
+
$attr['width'] = $matches[1];
|
47 |
+
}
|
48 |
+
if ( preg_match( '/height\s*:\s*(\d+)px/', $return, $matches ) ) {
|
49 |
+
$attr['height'] = $matches[1];
|
50 |
+
}
|
51 |
+
|
52 |
+
$return = AMP_HTML_Utils::build_tag(
|
53 |
+
'amp-iframe',
|
54 |
+
array(
|
55 |
+
'width' => $attr['width'],
|
56 |
+
'height' => $attr['height'],
|
57 |
+
'src' => $url,
|
58 |
+
'sandbox' => 'allow-scripts allow-same-origin',
|
59 |
+
)
|
60 |
+
);
|
61 |
+
}
|
62 |
+
return $return;
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Meetup_Embed_Handler
|
4 |
+
*
|
5 |
+
* @package AMP
|
6 |
+
* @since 0.7
|
7 |
+
*/
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class AMP_Meetup_Embed_Handler
|
11 |
+
*/
|
12 |
+
class AMP_Meetup_Embed_Handler extends AMP_Base_Embed_Handler {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Register embed.
|
16 |
+
*/
|
17 |
+
public function register_embed() {
|
18 |
+
add_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10, 2 );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Unregister embed.
|
23 |
+
*/
|
24 |
+
public function unregister_embed() {
|
25 |
+
remove_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10 );
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Filter oEmbed HTML for Meetup to prepare it for AMP.
|
30 |
+
*
|
31 |
+
* @param string $cache Cache for oEmbed.
|
32 |
+
* @param string $url Embed URL.
|
33 |
+
* @return string Embed.
|
34 |
+
*/
|
35 |
+
public function filter_embed_oembed_html( $cache, $url ) {
|
36 |
+
$parsed_url = wp_parse_url( $url );
|
37 |
+
if ( false !== strpos( $parsed_url['host'], 'meetup.com' ) ) {
|
38 |
+
|
39 |
+
// Supply the width/height so that we don't have to make requests to look them up later.
|
40 |
+
$cache = str_replace( '<img ', '<img width="50" height="50" ', $cache );
|
41 |
+
}
|
42 |
+
return $cache;
|
43 |
+
}
|
44 |
+
}
|
45 |
+
|
@@ -15,9 +15,6 @@ class AMP_Pinterest_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
15 |
protected $DEFAULT_WIDTH = 450;
|
16 |
protected $DEFAULT_HEIGHT = 750;
|
17 |
|
18 |
-
private static $script_slug = 'amp-pinterest';
|
19 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-pinterest-0.1.js';
|
20 |
-
|
21 |
public function register_embed() {
|
22 |
wp_embed_register_handler(
|
23 |
'amp-pinterest',
|
@@ -30,14 +27,6 @@ class AMP_Pinterest_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
30 |
wp_embed_unregister_handler('amp-pinterest', -1);
|
31 |
}
|
32 |
|
33 |
-
public function get_scripts() {
|
34 |
-
if ( ! $this->did_convert_elements) {
|
35 |
-
return array();
|
36 |
-
}
|
37 |
-
|
38 |
-
return array( self::$script_slug => self::$script_src);
|
39 |
-
}
|
40 |
-
|
41 |
public function oembed( $matches, $attr, $url, $rawattr ) {
|
42 |
return $this->render( array( 'url' => $url ) );
|
43 |
}
|
15 |
protected $DEFAULT_WIDTH = 450;
|
16 |
protected $DEFAULT_HEIGHT = 750;
|
17 |
|
|
|
|
|
|
|
18 |
public function register_embed() {
|
19 |
wp_embed_register_handler(
|
20 |
'amp-pinterest',
|
27 |
wp_embed_unregister_handler('amp-pinterest', -1);
|
28 |
}
|
29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
public function oembed( $matches, $attr, $url, $rawattr ) {
|
31 |
return $this->render( array( 'url' => $url ) );
|
32 |
}
|
@@ -0,0 +1,324 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Playlist_Embed_Handler
|
4 |
+
*
|
5 |
+
* @package AMP
|
6 |
+
* @since 0.7
|
7 |
+
*/
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class AMP_Playlist_Embed_Handler
|
11 |
+
*
|
12 |
+
* Creates AMP-compatible markup for the WordPress 'playlist' shortcode.
|
13 |
+
*
|
14 |
+
* @package AMP
|
15 |
+
*/
|
16 |
+
class AMP_Playlist_Embed_Handler extends AMP_Base_Embed_Handler {
|
17 |
+
|
18 |
+
/**
|
19 |
+
* The tag of the shortcode.
|
20 |
+
*
|
21 |
+
* @var string
|
22 |
+
*/
|
23 |
+
const SHORTCODE = 'playlist';
|
24 |
+
|
25 |
+
/**
|
26 |
+
* The default height of the thumbnail image for 'audio' playlist tracks.
|
27 |
+
*
|
28 |
+
* @var int
|
29 |
+
*/
|
30 |
+
const DEFAULT_THUMB_HEIGHT = 64;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* The default width of the thumbnail image for 'audio' playlist tracks.
|
34 |
+
*
|
35 |
+
* @var int
|
36 |
+
*/
|
37 |
+
const DEFAULT_THUMB_WIDTH = 48;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* The max width of the audio thumbnail image.
|
41 |
+
*
|
42 |
+
* This corresponds to the max-width in wp-mediaelement.css:
|
43 |
+
* .wp-playlist .wp-playlist-current-item img
|
44 |
+
*
|
45 |
+
* @var int
|
46 |
+
*/
|
47 |
+
const THUMB_MAX_WIDTH = 60;
|
48 |
+
|
49 |
+
/**
|
50 |
+
* The height of the carousel.
|
51 |
+
*
|
52 |
+
* @var int
|
53 |
+
*/
|
54 |
+
const CAROUSEL_HEIGHT = 160;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* The pattern to get the playlist data.
|
58 |
+
*
|
59 |
+
* @var string
|
60 |
+
*/
|
61 |
+
const PLAYLIST_REGEX = ':<script type="application/json" class="wp-playlist-script">(.+?)</script>:s';
|
62 |
+
|
63 |
+
/**
|
64 |
+
* The ID of individual playlist.
|
65 |
+
*
|
66 |
+
* @var int
|
67 |
+
*/
|
68 |
+
public static $playlist_id = 0;
|
69 |
+
|
70 |
+
/**
|
71 |
+
* The removed shortcode callback.
|
72 |
+
*
|
73 |
+
* @var callable
|
74 |
+
*/
|
75 |
+
public $removed_shortcode_callback;
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Registers the playlist shortcode.
|
79 |
+
*
|
80 |
+
* @global array $shortcode_tags
|
81 |
+
* @return void
|
82 |
+
*/
|
83 |
+
public function register_embed() {
|
84 |
+
global $shortcode_tags;
|
85 |
+
if ( shortcode_exists( self::SHORTCODE ) ) {
|
86 |
+
$this->removed_shortcode_callback = $shortcode_tags[ self::SHORTCODE ];
|
87 |
+
}
|
88 |
+
add_shortcode( self::SHORTCODE, array( $this, 'shortcode' ) );
|
89 |
+
remove_action( 'wp_playlist_scripts', 'wp_playlist_scripts' );
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Unregisters the playlist shortcode.
|
94 |
+
*
|
95 |
+
* @return void
|
96 |
+
*/
|
97 |
+
public function unregister_embed() {
|
98 |
+
if ( $this->removed_shortcode_callback ) {
|
99 |
+
add_shortcode( self::SHORTCODE, $this->removed_shortcode_callback );
|
100 |
+
$this->removed_shortcode_callback = null;
|
101 |
+
}
|
102 |
+
add_action( 'wp_playlist_scripts', 'wp_playlist_scripts' );
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Enqueues the playlist styling.
|
107 |
+
*
|
108 |
+
* @return void
|
109 |
+
*/
|
110 |
+
public function enqueue_styles() {
|
111 |
+
wp_enqueue_style(
|
112 |
+
'amp-playlist-shortcode',
|
113 |
+
amp_get_asset_url( 'css/amp-playlist-shortcode.css' ),
|
114 |
+
array( 'wp-mediaelement' ),
|
115 |
+
AMP__VERSION
|
116 |
+
);
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Gets AMP-compliant markup for the playlist shortcode.
|
121 |
+
*
|
122 |
+
* Uses the JSON that wp_playlist_shortcode() produces.
|
123 |
+
* Gets the markup, based on the type of playlist.
|
124 |
+
*
|
125 |
+
* @param array $attr The playlist attributes.
|
126 |
+
* @return string Playlist shortcode markup.
|
127 |
+
*/
|
128 |
+
public function shortcode( $attr ) {
|
129 |
+
$data = $this->get_data( $attr );
|
130 |
+
if ( isset( $data['type'] ) && ( 'audio' === $data['type'] ) ) {
|
131 |
+
return $this->audio_playlist( $data );
|
132 |
+
} elseif ( isset( $data['type'] ) && ( 'video' === $data['type'] ) ) {
|
133 |
+
return $this->video_playlist( $data );
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Gets an AMP-compliant audio playlist.
|
139 |
+
*
|
140 |
+
* @param array $data Data.
|
141 |
+
* @return string Playlist shortcode markup, or an empty string.
|
142 |
+
*/
|
143 |
+
public function audio_playlist( $data ) {
|
144 |
+
if ( ! isset( $data['tracks'] ) ) {
|
145 |
+
return '';
|
146 |
+
}
|
147 |
+
self::$playlist_id++;
|
148 |
+
$container_id = 'wpPlaylist' . self::$playlist_id . 'Carousel';
|
149 |
+
$state_id = 'wpPlaylist' . self::$playlist_id;
|
150 |
+
$amp_state = array(
|
151 |
+
'selectedIndex' => 0,
|
152 |
+
);
|
153 |
+
|
154 |
+
$this->enqueue_styles();
|
155 |
+
ob_start();
|
156 |
+
?>
|
157 |
+
<div class="wp-playlist wp-audio-playlist wp-playlist-light">
|
158 |
+
<amp-state id="<?php echo esc_attr( $state_id ); ?>">
|
159 |
+
<script type="application/json"><?php echo wp_json_encode( $amp_state ); ?></script>
|
160 |
+
</amp-state>
|
161 |
+
<amp-carousel id="<?php echo esc_attr( $container_id ); ?>" [slide]="<?php echo esc_attr( $state_id . '.selectedIndex' ); ?>" height="<?php echo esc_attr( self::CAROUSEL_HEIGHT ); ?>" width="auto" type="slides">
|
162 |
+
<?php
|
163 |
+
foreach ( $data['tracks'] as $track ) :
|
164 |
+
$title = $this->get_title( $track );
|
165 |
+
$image_url = isset( $track['thumb']['src'] ) ? $track['thumb']['src'] : '';
|
166 |
+
$dimensions = $this->get_thumb_dimensions( $track );
|
167 |
+
?>
|
168 |
+
<div>
|
169 |
+
<div class="wp-playlist-current-item">
|
170 |
+
<?php if ( $image_url ) : ?>
|
171 |
+
<amp-img src="<?php echo esc_url( $image_url ); ?>" height="<?php echo esc_attr( $dimensions['height'] ); ?>" width="<?php echo esc_attr( $dimensions['width'] ); ?>"></amp-img>
|
172 |
+
<?php endif; ?>
|
173 |
+
<div class="wp-playlist-caption">
|
174 |
+
<span class="wp-playlist-item-meta wp-playlist-item-title"><?php echo esc_html( $title ); ?></span>
|
175 |
+
</div>
|
176 |
+
</div>
|
177 |
+
<amp-audio width="auto" height="50" src="<?php echo esc_url( $track['src'] ); ?>"></amp-audio>
|
178 |
+
</div>
|
179 |
+
<?php endforeach; ?>
|
180 |
+
</amp-carousel>
|
181 |
+
<?php $this->print_tracks( $state_id, $data['tracks'] ); ?>
|
182 |
+
</div>
|
183 |
+
<?php
|
184 |
+
return ob_get_clean();
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Gets an AMP-compliant video playlist.
|
189 |
+
*
|
190 |
+
* This uses similar markup to the native playlist shortcode output.
|
191 |
+
* So the styles from wp-mediaelement.min.css will apply to it.
|
192 |
+
*
|
193 |
+
* @global int $content_width
|
194 |
+
* @param array $data Data.
|
195 |
+
* @return string $video_playlist Markup for the video playlist.
|
196 |
+
*/
|
197 |
+
public function video_playlist( $data ) {
|
198 |
+
global $content_width;
|
199 |
+
if ( ! isset( $data['tracks'], $data['tracks'][0]['src'] ) ) {
|
200 |
+
return '';
|
201 |
+
}
|
202 |
+
self::$playlist_id++;
|
203 |
+
$state_id = 'wpPlaylist' . self::$playlist_id;
|
204 |
+
$amp_state = array(
|
205 |
+
'selectedIndex' => 0,
|
206 |
+
);
|
207 |
+
foreach ( $data['tracks'] as $index => $track ) {
|
208 |
+
$amp_state[ $index ] = array(
|
209 |
+
'videoUrl' => $track['src'],
|
210 |
+
'thumb' => isset( $track['thumb']['src'] ) ? $track['thumb']['src'] : '',
|
211 |
+
);
|
212 |
+
}
|
213 |
+
|
214 |
+
$dimensions = isset( $data['tracks'][0]['dimensions']['resized'] ) ? $data['tracks'][0]['dimensions']['resized'] : null;
|
215 |
+
$width = isset( $dimensions['width'] ) ? $dimensions['width'] : $content_width;
|
216 |
+
$height = isset( $dimensions['height'] ) ? $dimensions['height'] : null;
|
217 |
+
$src_bound = sprintf( '%s[%s.selectedIndex].videoUrl', $state_id, $state_id );
|
218 |
+
|
219 |
+
$this->enqueue_styles();
|
220 |
+
ob_start();
|
221 |
+
?>
|
222 |
+
<div class="wp-playlist wp-video-playlist wp-playlist-light">
|
223 |
+
<amp-state id="<?php echo esc_attr( $state_id ); ?>">
|
224 |
+
<script type="application/json"><?php echo wp_json_encode( $amp_state ); // WPCS: XSS ok. ?></script>
|
225 |
+
</amp-state>
|
226 |
+
<amp-video id="amp-video" src="<?php echo esc_url( $data['tracks'][0]['src'] ); ?>" [src]="<?php echo esc_attr( $src_bound ); ?>" width="<?php echo esc_attr( $width ); ?>" height="<?php echo esc_attr( $height ); ?>" controls></amp-video>
|
227 |
+
<?php $this->print_tracks( $state_id, $data['tracks'] ); ?>
|
228 |
+
</div>
|
229 |
+
<?php
|
230 |
+
return ob_get_clean(); // WPCS: XSS ok.
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Gets the thumbnail image dimensions, including height and width.
|
235 |
+
*
|
236 |
+
* If the width is higher than the maximum width,
|
237 |
+
* reduces it to the maximum width.
|
238 |
+
* And it proportionally reduces the height.
|
239 |
+
*
|
240 |
+
* @param array $track The data for the track.
|
241 |
+
* @return array {
|
242 |
+
* Dimensions.
|
243 |
+
*
|
244 |
+
* @type int $height Image height.
|
245 |
+
* @type int $width Image width.
|
246 |
+
* }
|
247 |
+
*/
|
248 |
+
public function get_thumb_dimensions( $track ) {
|
249 |
+
$original_height = isset( $track['thumb']['height'] ) ? intval( $track['thumb']['height'] ) : self::DEFAULT_THUMB_HEIGHT;
|
250 |
+
$original_width = isset( $track['thumb']['width'] ) ? intval( $track['thumb']['width'] ) : self::DEFAULT_THUMB_WIDTH;
|
251 |
+
if ( $original_width > self::THUMB_MAX_WIDTH ) {
|
252 |
+
$ratio = $original_width / self::THUMB_MAX_WIDTH;
|
253 |
+
$height = intval( $original_height / $ratio );
|
254 |
+
} else {
|
255 |
+
$height = $original_height;
|
256 |
+
}
|
257 |
+
$width = min( self::THUMB_MAX_WIDTH, $original_width );
|
258 |
+
return compact( 'height', 'width' );
|
259 |
+
}
|
260 |
+
|
261 |
+
/**
|
262 |
+
* Outputs the playlist tracks, based on the type of playlist.
|
263 |
+
*
|
264 |
+
* These typically appear below the player.
|
265 |
+
* Clicking a track triggers the player to appear with its src.
|
266 |
+
*
|
267 |
+
* @param string $state_id The ID of the container.
|
268 |
+
* @param array $tracks Tracks.
|
269 |
+
* @return void
|
270 |
+
*/
|
271 |
+
public function print_tracks( $state_id, $tracks ) {
|
272 |
+
?>
|
273 |
+
<div class="wp-playlist-tracks">
|
274 |
+
<?php foreach ( $tracks as $index => $track ) : ?>
|
275 |
+
<?php
|
276 |
+
$on = 'tap:AMP.setState(' . wp_json_encode( array( $state_id => array( 'selectedIndex' => $index ) ) ) . ')';
|
277 |
+
$initial_class = 0 === $index ? 'wp-playlist-item wp-playlist-playing' : 'wp-playlist-item';
|
278 |
+
$bound_class = sprintf( '%d == %s.selectedIndex ? "wp-playlist-item wp-playlist-playing" : "wp-playlist-item"', $index, $state_id );
|
279 |
+
?>
|
280 |
+
<div class="<?php echo esc_attr( $initial_class ); ?>" [class]="<?php echo esc_attr( $bound_class ); ?>" >
|
281 |
+
<a class="wp-playlist-caption" on="<?php echo esc_attr( $on ); ?>">
|
282 |
+
<?php echo esc_html( strval( $index + 1 ) . '.' ); ?> <span class="wp-playlist-item-title"><?php echo esc_html( $this->get_title( $track ) ); ?></span>
|
283 |
+
</a>
|
284 |
+
<?php if ( isset( $track['meta']['length_formatted'] ) ) : ?>
|
285 |
+
<div class="wp-playlist-item-length"><?php echo esc_html( $track['meta']['length_formatted'] ); ?></div>
|
286 |
+
<?php endif; ?>
|
287 |
+
</div>
|
288 |
+
<?php endforeach; ?>
|
289 |
+
</div>
|
290 |
+
<?php
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* Gets the data for the playlist.
|
295 |
+
*
|
296 |
+
* @see wp_playlist_shortcode()
|
297 |
+
* @param array $attr The shortcode attributes.
|
298 |
+
* @return array $data The data for the playlist.
|
299 |
+
*/
|
300 |
+
public function get_data( $attr ) {
|
301 |
+
$markup = wp_playlist_shortcode( $attr );
|
302 |
+
preg_match( self::PLAYLIST_REGEX, $markup, $matches );
|
303 |
+
if ( empty( $matches[1] ) ) {
|
304 |
+
return array();
|
305 |
+
}
|
306 |
+
return json_decode( $matches[1], true );
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* Gets the title for the track.
|
311 |
+
*
|
312 |
+
* @param array $track The track data.
|
313 |
+
* @return string $title The title of the track.
|
314 |
+
*/
|
315 |
+
public function get_title( $track ) {
|
316 |
+
if ( ! empty( $track['caption'] ) ) {
|
317 |
+
return $track['caption'];
|
318 |
+
} elseif ( ! empty( $track['title'] ) ) {
|
319 |
+
return $track['title'];
|
320 |
+
}
|
321 |
+
return '';
|
322 |
+
}
|
323 |
+
|
324 |
+
}
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Reddit_Embed_Handler
|
4 |
+
*
|
5 |
+
* @package AMP
|
6 |
+
* @since 0.7
|
7 |
+
*/
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class AMP_Reddit_Embed_Handler
|
11 |
+
*/
|
12 |
+
class AMP_Reddit_Embed_Handler extends AMP_Base_Embed_Handler {
|
13 |
+
/**
|
14 |
+
* Regex matched to produce output amp-reddit.
|
15 |
+
*
|
16 |
+
* @const string
|
17 |
+
*/
|
18 |
+
const URL_PATTERN = '#https?://(www\.)?reddit\.com/r/[^/]+/comments/.*#i';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Register embed.
|
22 |
+
*/
|
23 |
+
public function register_embed() {
|
24 |
+
wp_embed_register_handler( 'amp-reddit', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Unregister embed.
|
29 |
+
*/
|
30 |
+
public function unregister_embed() {
|
31 |
+
wp_embed_unregister_handler( 'amp-reddit', -1 );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Embed found with matching URL callback.
|
36 |
+
*
|
37 |
+
* @param array $matches URL regex matches.
|
38 |
+
* @param array $attr Additional parameters.
|
39 |
+
* @param array $url URL.
|
40 |
+
* @return string Embed.
|
41 |
+
*/
|
42 |
+
public function oembed( $matches, $attr, $url ) {
|
43 |
+
return $this->render( array( 'url' => $url ) );
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Output the Reddit amp element.
|
48 |
+
*
|
49 |
+
* @param array $args parameters used for output.
|
50 |
+
* @return string Rendered content.
|
51 |
+
*/
|
52 |
+
public function render( $args ) {
|
53 |
+
$args = wp_parse_args( $args, array(
|
54 |
+
'url' => false,
|
55 |
+
) );
|
56 |
+
|
57 |
+
if ( empty( $args['url'] ) ) {
|
58 |
+
return '';
|
59 |
+
}
|
60 |
+
|
61 |
+
// @todo Sizing is not yet correct. See <https://github.com/ampproject/amphtml/issues/11869>.
|
62 |
+
return AMP_HTML_Utils::build_tag(
|
63 |
+
'amp-reddit',
|
64 |
+
array(
|
65 |
+
'layout' => 'responsive',
|
66 |
+
'data-embedtype' => 'post',
|
67 |
+
'width' => '100',
|
68 |
+
'height' => '100',
|
69 |
+
'data-src' => $args['url'],
|
70 |
+
)
|
71 |
+
);
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
@@ -17,21 +17,7 @@ class AMP_SoundCloud_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
17 |
*
|
18 |
* @var int
|
19 |
*/
|
20 |
-
protected $DEFAULT_HEIGHT = 200;
|
21 |
-
|
22 |
-
/**
|
23 |
-
* Script slug.
|
24 |
-
*
|
25 |
-
* @var string
|
26 |
-
*/
|
27 |
-
private static $script_slug = 'amp-soundcloud';
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Script source.
|
31 |
-
*
|
32 |
-
* @var string
|
33 |
-
*/
|
34 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-soundcloud-0.1.js';
|
35 |
|
36 |
/**
|
37 |
* Register embed.
|
@@ -49,19 +35,6 @@ class AMP_SoundCloud_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
49 |
remove_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10 );
|
50 |
}
|
51 |
|
52 |
-
/**
|
53 |
-
* Get scripts needed by component.
|
54 |
-
*
|
55 |
-
* @return array Scripts.
|
56 |
-
*/
|
57 |
-
public function get_scripts() {
|
58 |
-
if ( ! $this->did_convert_elements ) {
|
59 |
-
return array();
|
60 |
-
}
|
61 |
-
|
62 |
-
return array( self::$script_slug => self::$script_src );
|
63 |
-
}
|
64 |
-
|
65 |
/**
|
66 |
* Render oEmbed.
|
67 |
*
|
17 |
*
|
18 |
* @var int
|
19 |
*/
|
20 |
+
protected $DEFAULT_HEIGHT = 200; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.MemberNotSnakeCase
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
/**
|
23 |
* Register embed.
|
35 |
remove_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10 );
|
36 |
}
|
37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
/**
|
39 |
* Render oEmbed.
|
40 |
*
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Tumblr_Embed_Handler
|
4 |
+
*
|
5 |
+
* @package AMP
|
6 |
+
* @since 0.7
|
7 |
+
*/
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class AMP_Tumblr_Embed_Handler
|
11 |
+
*/
|
12 |
+
class AMP_Tumblr_Embed_Handler extends AMP_Base_Embed_Handler {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Register embed.
|
16 |
+
*/
|
17 |
+
public function register_embed() {
|
18 |
+
add_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10, 2 );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Unregister embed.
|
23 |
+
*/
|
24 |
+
public function unregister_embed() {
|
25 |
+
remove_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10 );
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Filter oEmbed HTML for Tumblr to prepare it for AMP.
|
30 |
+
*
|
31 |
+
* @param string $cache Cache for oEmbed.
|
32 |
+
* @param string $url Embed URL.
|
33 |
+
* @return string Embed.
|
34 |
+
*/
|
35 |
+
public function filter_embed_oembed_html( $cache, $url ) {
|
36 |
+
$parsed_url = wp_parse_url( $url );
|
37 |
+
if ( false === strpos( $parsed_url['host'], 'tumblr.com' ) ) {
|
38 |
+
return $cache;
|
39 |
+
}
|
40 |
+
|
41 |
+
// @todo The iframe will not get sized properly.
|
42 |
+
if ( preg_match( '#data-href="(?P<href>https://embed.tumblr.com/embed/post/\w+/\w+)"#', $cache, $matches ) ) {
|
43 |
+
$cache = AMP_HTML_Utils::build_tag(
|
44 |
+
'amp-iframe',
|
45 |
+
array(
|
46 |
+
'width' => $this->args['width'],
|
47 |
+
'height' => $this->args['height'],
|
48 |
+
'layout' => 'responsive',
|
49 |
+
'sandbox' => 'allow-scripts allow-popups', // The allow-scripts is needed to allow the iframe to render; allow-popups needed to allow clicking.
|
50 |
+
'src' => $matches['href'],
|
51 |
+
),
|
52 |
+
sprintf( '<a placeholder href="%s">Tumblr</a>', $url )
|
53 |
+
);
|
54 |
+
}
|
55 |
+
|
56 |
+
return $cache;
|
57 |
+
}
|
58 |
+
|
59 |
+
}
|
60 |
+
|
@@ -13,9 +13,6 @@
|
|
13 |
class AMP_Twitter_Embed_Handler extends AMP_Base_Embed_Handler {
|
14 |
const URL_PATTERN = '#http(s|):\/\/twitter\.com(\/\#\!\/|\/)([a-zA-Z0-9_]{1,20})\/status(es)*\/(\d+)#i';
|
15 |
|
16 |
-
private static $script_slug = 'amp-twitter';
|
17 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-twitter-0.1.js';
|
18 |
-
|
19 |
public function register_embed() {
|
20 |
add_shortcode( 'tweet', array( $this, 'shortcode' ) );
|
21 |
wp_embed_register_handler( 'amp-twitter', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
@@ -26,14 +23,6 @@ class AMP_Twitter_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
26 |
wp_embed_unregister_handler( 'amp-twitter', -1 );
|
27 |
}
|
28 |
|
29 |
-
public function get_scripts() {
|
30 |
-
if ( ! $this->did_convert_elements ) {
|
31 |
-
return array();
|
32 |
-
}
|
33 |
-
|
34 |
-
return array( self::$script_slug => self::$script_src );
|
35 |
-
}
|
36 |
-
|
37 |
function shortcode( $attr ) {
|
38 |
$attr = wp_parse_args( $attr, array(
|
39 |
'tweet' => false,
|
13 |
class AMP_Twitter_Embed_Handler extends AMP_Base_Embed_Handler {
|
14 |
const URL_PATTERN = '#http(s|):\/\/twitter\.com(\/\#\!\/|\/)([a-zA-Z0-9_]{1,20})\/status(es)*\/(\d+)#i';
|
15 |
|
|
|
|
|
|
|
16 |
public function register_embed() {
|
17 |
add_shortcode( 'tweet', array( $this, 'shortcode' ) );
|
18 |
wp_embed_register_handler( 'amp-twitter', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
23 |
wp_embed_unregister_handler( 'amp-twitter', -1 );
|
24 |
}
|
25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
function shortcode( $attr ) {
|
27 |
$attr = wp_parse_args( $attr, array(
|
28 |
'tweet' => false,
|
@@ -19,9 +19,6 @@ class AMP_Vimeo_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
19 |
protected $DEFAULT_WIDTH = 600;
|
20 |
protected $DEFAULT_HEIGHT = 338;
|
21 |
|
22 |
-
private static $script_slug = 'amp-vimeo';
|
23 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-vimeo-0.1.js';
|
24 |
-
|
25 |
function __construct( $args = array() ) {
|
26 |
parent::__construct( $args );
|
27 |
|
@@ -35,6 +32,7 @@ class AMP_Vimeo_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
35 |
function register_embed() {
|
36 |
wp_embed_register_handler( 'amp-vimeo', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
37 |
add_shortcode( 'vimeo', array( $this, 'shortcode' ) );
|
|
|
38 |
}
|
39 |
|
40 |
public function unregister_embed() {
|
@@ -42,14 +40,6 @@ class AMP_Vimeo_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
42 |
remove_shortcode( 'vimeo' );
|
43 |
}
|
44 |
|
45 |
-
public function get_scripts() {
|
46 |
-
if ( ! $this->did_convert_elements ) {
|
47 |
-
return array();
|
48 |
-
}
|
49 |
-
|
50 |
-
return array( self::$script_slug => self::$script_src );
|
51 |
-
}
|
52 |
-
|
53 |
public function shortcode( $attr ) {
|
54 |
$video_id = false;
|
55 |
|
@@ -118,4 +108,27 @@ class AMP_Vimeo_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
118 |
|
119 |
return $video_id;
|
120 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
}
|
19 |
protected $DEFAULT_WIDTH = 600;
|
20 |
protected $DEFAULT_HEIGHT = 338;
|
21 |
|
|
|
|
|
|
|
22 |
function __construct( $args = array() ) {
|
23 |
parent::__construct( $args );
|
24 |
|
32 |
function register_embed() {
|
33 |
wp_embed_register_handler( 'amp-vimeo', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
34 |
add_shortcode( 'vimeo', array( $this, 'shortcode' ) );
|
35 |
+
add_filter( 'wp_video_shortcode_override', array( $this, 'video_override' ), 10, 2 );
|
36 |
}
|
37 |
|
38 |
public function unregister_embed() {
|
40 |
remove_shortcode( 'vimeo' );
|
41 |
}
|
42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
public function shortcode( $attr ) {
|
44 |
$video_id = false;
|
45 |
|
108 |
|
109 |
return $video_id;
|
110 |
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Override the output of Vimeo videos.
|
114 |
+
*
|
115 |
+
* This overrides the value in wp_video_shortcode().
|
116 |
+
* The pattern matching is copied from WP_Widget_Media_Video::render().
|
117 |
+
*
|
118 |
+
* @param string $html Empty variable to be replaced with shortcode markup.
|
119 |
+
* @param array $attr The shortcode attributes.
|
120 |
+
* @return string|null $markup The markup to output.
|
121 |
+
*/
|
122 |
+
public function video_override( $html, $attr ) {
|
123 |
+
if ( ! isset( $attr['src'] ) ) {
|
124 |
+
return $html;
|
125 |
+
}
|
126 |
+
$src = $attr['src'];
|
127 |
+
$vimeo_pattern = '#^https?://(.+\.)?vimeo\.com/.*#';
|
128 |
+
if ( 1 === preg_match( $vimeo_pattern, $src ) ) {
|
129 |
+
return $this->shortcode( array( $src ) );
|
130 |
+
}
|
131 |
+
return $html;
|
132 |
+
}
|
133 |
+
|
134 |
}
|
@@ -14,9 +14,6 @@ class AMP_Vine_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
14 |
protected $DEFAULT_WIDTH = 400;
|
15 |
protected $DEFAULT_HEIGHT = 400;
|
16 |
|
17 |
-
private static $script_slug = 'amp-vine';
|
18 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-vine-0.1.js';
|
19 |
-
|
20 |
public function register_embed() {
|
21 |
wp_embed_register_handler( 'amp-vine', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
22 |
}
|
@@ -25,14 +22,6 @@ class AMP_Vine_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
25 |
wp_embed_unregister_handler( 'amp-vine', -1 );
|
26 |
}
|
27 |
|
28 |
-
public function get_scripts() {
|
29 |
-
if ( ! $this->did_convert_elements ) {
|
30 |
-
return array();
|
31 |
-
}
|
32 |
-
|
33 |
-
return array( self::$script_slug => self::$script_src );
|
34 |
-
}
|
35 |
-
|
36 |
public function oembed( $matches, $attr, $url, $rawattr ) {
|
37 |
return $this->render( array( 'url' => $url, 'vine_id' => end( $matches ) ) );
|
38 |
}
|
14 |
protected $DEFAULT_WIDTH = 400;
|
15 |
protected $DEFAULT_HEIGHT = 400;
|
16 |
|
|
|
|
|
|
|
17 |
public function register_embed() {
|
18 |
wp_embed_register_handler( 'amp-vine', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
19 |
}
|
22 |
wp_embed_unregister_handler( 'amp-vine', -1 );
|
23 |
}
|
24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
public function oembed( $matches, $attr, $url, $rawattr ) {
|
26 |
return $this->render( array( 'url' => $url, 'vine_id' => end( $matches ) ) );
|
27 |
}
|
@@ -19,15 +19,12 @@ class AMP_YouTube_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
19 |
protected $DEFAULT_WIDTH = 600;
|
20 |
protected $DEFAULT_HEIGHT = 338;
|
21 |
|
22 |
-
private static $script_slug = 'amp-youtube';
|
23 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-youtube-0.1.js';
|
24 |
-
|
25 |
function __construct( $args = array() ) {
|
26 |
parent::__construct( $args );
|
27 |
|
28 |
if ( isset( $this->args['content_max_width'] ) ) {
|
29 |
$max_width = $this->args['content_max_width'];
|
30 |
-
$this->args['width']
|
31 |
$this->args['height'] = round( $max_width * self::RATIO );
|
32 |
}
|
33 |
}
|
@@ -35,6 +32,7 @@ class AMP_YouTube_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
35 |
function register_embed() {
|
36 |
wp_embed_register_handler( 'amp-youtube', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
37 |
add_shortcode( 'youtube', array( $this, 'shortcode' ) );
|
|
|
38 |
}
|
39 |
|
40 |
public function unregister_embed() {
|
@@ -42,14 +40,6 @@ class AMP_YouTube_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
42 |
remove_shortcode( 'youtube' );
|
43 |
}
|
44 |
|
45 |
-
public function get_scripts() {
|
46 |
-
if ( ! $this->did_convert_elements ) {
|
47 |
-
return array();
|
48 |
-
}
|
49 |
-
|
50 |
-
return array( self::$script_slug => self::$script_src );
|
51 |
-
}
|
52 |
-
|
53 |
public function shortcode( $attr ) {
|
54 |
$url = false;
|
55 |
$video_id = false;
|
@@ -136,4 +126,27 @@ class AMP_YouTube_Embed_Handler extends AMP_Base_Embed_Handler {
|
|
136 |
|
137 |
return $value;
|
138 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
139 |
}
|
19 |
protected $DEFAULT_WIDTH = 600;
|
20 |
protected $DEFAULT_HEIGHT = 338;
|
21 |
|
|
|
|
|
|
|
22 |
function __construct( $args = array() ) {
|
23 |
parent::__construct( $args );
|
24 |
|
25 |
if ( isset( $this->args['content_max_width'] ) ) {
|
26 |
$max_width = $this->args['content_max_width'];
|
27 |
+
$this->args['width'] = $max_width;
|
28 |
$this->args['height'] = round( $max_width * self::RATIO );
|
29 |
}
|
30 |
}
|
32 |
function register_embed() {
|
33 |
wp_embed_register_handler( 'amp-youtube', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
|
34 |
add_shortcode( 'youtube', array( $this, 'shortcode' ) );
|
35 |
+
add_filter( 'wp_video_shortcode_override', array( $this, 'video_override' ), 10, 2 );
|
36 |
}
|
37 |
|
38 |
public function unregister_embed() {
|
40 |
remove_shortcode( 'youtube' );
|
41 |
}
|
42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
public function shortcode( $attr ) {
|
44 |
$url = false;
|
45 |
$video_id = false;
|
126 |
|
127 |
return $value;
|
128 |
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Override the output of YouTube videos.
|
132 |
+
*
|
133 |
+
* This overrides the value in wp_video_shortcode().
|
134 |
+
* The pattern matching is copied from WP_Widget_Media_Video::render().
|
135 |
+
*
|
136 |
+
* @param string $html Empty variable to be replaced with shortcode markup.
|
137 |
+
* @param array $attr The shortcode attributes.
|
138 |
+
* @return string|null $markup The markup to output.
|
139 |
+
*/
|
140 |
+
public function video_override( $html, $attr ) {
|
141 |
+
if ( ! isset( $attr['src'] ) ) {
|
142 |
+
return $html;
|
143 |
+
}
|
144 |
+
$src = $attr['src'];
|
145 |
+
$youtube_pattern = '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#';
|
146 |
+
if ( 1 === preg_match( $youtube_pattern, $src ) ) {
|
147 |
+
return $this->shortcode( array( $src ) );
|
148 |
+
}
|
149 |
+
return $html;
|
150 |
+
}
|
151 |
+
|
152 |
}
|
@@ -47,7 +47,7 @@ class AMP_Options_Manager {
|
|
47 |
sort( $old_post_types );
|
48 |
sort( $new_post_types );
|
49 |
if ( $old_post_types !== $new_post_types ) {
|
50 |
-
flush_rewrite_rules();
|
51 |
}
|
52 |
}
|
53 |
|
@@ -171,7 +171,7 @@ class AMP_Options_Manager {
|
|
171 |
continue;
|
172 |
}
|
173 |
|
174 |
-
$post_type_supported = post_type_supports( $post_type->name,
|
175 |
$is_support_elected = in_array( $post_type->name, $supported_types, true );
|
176 |
|
177 |
$error = null;
|
47 |
sort( $old_post_types );
|
48 |
sort( $new_post_types );
|
49 |
if ( $old_post_types !== $new_post_types ) {
|
50 |
+
flush_rewrite_rules( false );
|
51 |
}
|
52 |
}
|
53 |
|
171 |
continue;
|
172 |
}
|
173 |
|
174 |
+
$post_type_supported = post_type_supports( $post_type->name, amp_get_slug() );
|
175 |
$is_support_elected = in_array( $post_type->name, $supported_types, true );
|
176 |
|
177 |
$error = null;
|
@@ -22,7 +22,7 @@ class AMP_Options_Menu {
|
|
22 |
*/
|
23 |
public function init() {
|
24 |
add_action( 'admin_post_amp_analytics_options', 'AMP_Options_Manager::handle_analytics_submit' );
|
25 |
-
add_action( 'admin_menu', array( $this, 'add_menu_items' ) );
|
26 |
}
|
27 |
|
28 |
/**
|
@@ -84,7 +84,7 @@ class AMP_Options_Menu {
|
|
84 |
<?php foreach ( array_map( 'get_post_type_object', AMP_Post_Type_Support::get_eligible_post_types() ) as $post_type ) : ?>
|
85 |
<?php
|
86 |
$element_id = AMP_Options_Manager::OPTION_NAME . "-supported_post_types-{$post_type->name}";
|
87 |
-
$is_builtin = in_array( $post_type->name, $builtin_support, true );
|
88 |
?>
|
89 |
<?php if ( $is_builtin ) : ?>
|
90 |
<input type="hidden" name="<?php echo esc_attr( $element_name ); ?>" value="<?php echo esc_attr( $post_type->name ); ?>">
|
@@ -94,7 +94,7 @@ class AMP_Options_Menu {
|
|
94 |
id="<?php echo esc_attr( $element_id ); ?>"
|
95 |
name="<?php echo esc_attr( $element_name ); ?>"
|
96 |
value="<?php echo esc_attr( $post_type->name ); ?>"
|
97 |
-
<?php checked( true, post_type_supports( $post_type->name,
|
98 |
<?php disabled( $is_builtin ); ?>
|
99 |
>
|
100 |
<label for="<?php echo esc_attr( $element_id ); ?>">
|
@@ -102,7 +102,15 @@ class AMP_Options_Menu {
|
|
102 |
</label>
|
103 |
<br>
|
104 |
<?php endforeach; ?>
|
105 |
-
<p class="description"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
</fieldset>
|
107 |
<?php
|
108 |
}
|
@@ -124,7 +132,9 @@ class AMP_Options_Menu {
|
|
124 |
<?php
|
125 |
settings_fields( AMP_Options_Manager::OPTION_NAME );
|
126 |
do_settings_sections( AMP_Options_Manager::OPTION_NAME );
|
127 |
-
|
|
|
|
|
128 |
?>
|
129 |
</form>
|
130 |
</div>
|
22 |
*/
|
23 |
public function init() {
|
24 |
add_action( 'admin_post_amp_analytics_options', 'AMP_Options_Manager::handle_analytics_submit' );
|
25 |
+
add_action( 'admin_menu', array( $this, 'add_menu_items' ), 9 );
|
26 |
}
|
27 |
|
28 |
/**
|
84 |
<?php foreach ( array_map( 'get_post_type_object', AMP_Post_Type_Support::get_eligible_post_types() ) as $post_type ) : ?>
|
85 |
<?php
|
86 |
$element_id = AMP_Options_Manager::OPTION_NAME . "-supported_post_types-{$post_type->name}";
|
87 |
+
$is_builtin = amp_is_canonical() || in_array( $post_type->name, $builtin_support, true );
|
88 |
?>
|
89 |
<?php if ( $is_builtin ) : ?>
|
90 |
<input type="hidden" name="<?php echo esc_attr( $element_name ); ?>" value="<?php echo esc_attr( $post_type->name ); ?>">
|
94 |
id="<?php echo esc_attr( $element_id ); ?>"
|
95 |
name="<?php echo esc_attr( $element_name ); ?>"
|
96 |
value="<?php echo esc_attr( $post_type->name ); ?>"
|
97 |
+
<?php checked( true, amp_is_canonical() || post_type_supports( $post_type->name, amp_get_slug() ) ); ?>
|
98 |
<?php disabled( $is_builtin ); ?>
|
99 |
>
|
100 |
<label for="<?php echo esc_attr( $element_id ); ?>">
|
102 |
</label>
|
103 |
<br>
|
104 |
<?php endforeach; ?>
|
105 |
+
<p class="description">
|
106 |
+
<?php
|
107 |
+
if ( ! amp_is_canonical() ) :
|
108 |
+
esc_html_e( 'Enable/disable AMP post type(s) support', 'amp' );
|
109 |
+
else :
|
110 |
+
esc_html_e( 'Canonical AMP is enabled in your theme, so all post types will render.', 'amp' );
|
111 |
+
endif;
|
112 |
+
?>
|
113 |
+
</p>
|
114 |
</fieldset>
|
115 |
<?php
|
116 |
}
|
132 |
<?php
|
133 |
settings_fields( AMP_Options_Manager::OPTION_NAME );
|
134 |
do_settings_sections( AMP_Options_Manager::OPTION_NAME );
|
135 |
+
if ( ! amp_is_canonical() ) {
|
136 |
+
submit_button();
|
137 |
+
}
|
138 |
?>
|
139 |
</form>
|
140 |
</div>
|
@@ -8,11 +8,13 @@
|
|
8 |
* Note: This file only contains tags that are relevant to the `body` of
|
9 |
* an AMP page. To include additional elements modify the variable
|
10 |
* `mandatory_parent_blacklist` in the amp_wp_build.py script.
|
|
|
|
|
11 |
*/
|
12 |
class AMP_Allowed_Tags_Generated {
|
13 |
|
14 |
-
private static $spec_file_revision =
|
15 |
-
private static $minimum_validator_revision_required =
|
16 |
|
17 |
private static $allowed_tags = array(
|
18 |
'a' => array(
|
@@ -23,31 +25,36 @@ class AMP_Allowed_Tags_Generated {
|
|
23 |
'download' => array(),
|
24 |
'href' => array(
|
25 |
'blacklisted_value_regex' => '__amp_source_origin',
|
26 |
-
'
|
27 |
-
|
28 |
-
|
29 |
-
'
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
|
46 |
),
|
47 |
),
|
48 |
'hreflang' => array(),
|
49 |
'media' => array(),
|
50 |
'name' => array(),
|
|
|
51 |
'rel' => array(
|
52 |
'blacklisted_value_regex' => '(^|\\s)(components|dns-prefetch|import|manifest|preconnect|prefetch|preload|prerender|serviceworker|stylesheet|subresource|)(\\s|$)',
|
53 |
),
|
@@ -61,38 +68,26 @@ class AMP_Allowed_Tags_Generated {
|
|
61 |
),
|
62 |
),
|
63 |
'tag_spec' => array(
|
64 |
-
'html_format' => array(
|
65 |
-
'amp',
|
66 |
-
'amp4ads',
|
67 |
-
),
|
68 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#links',
|
69 |
),
|
70 |
-
|
71 |
),
|
72 |
),
|
73 |
'abbr' => array(
|
74 |
array(
|
75 |
'attr_spec_list' => array(),
|
76 |
'tag_spec' => array(),
|
77 |
-
|
78 |
),
|
79 |
),
|
80 |
'acronym' => array(
|
81 |
array(
|
82 |
'attr_spec_list' => array(),
|
83 |
-
'tag_spec' => array(
|
84 |
-
'html_format' => array(
|
85 |
-
'amp',
|
86 |
-
),
|
87 |
-
),
|
88 |
-
|
89 |
),
|
90 |
),
|
91 |
'address' => array(
|
92 |
array(
|
93 |
'attr_spec_list' => array(),
|
94 |
'tag_spec' => array(),
|
95 |
-
|
96 |
),
|
97 |
),
|
98 |
'amp-3q-player' => array(
|
@@ -110,11 +105,10 @@ class AMP_Allowed_Tags_Generated {
|
|
110 |
),
|
111 |
),
|
112 |
'tag_spec' => array(
|
113 |
-
'
|
114 |
-
|
115 |
),
|
116 |
),
|
117 |
-
|
118 |
),
|
119 |
),
|
120 |
'amp-accordion' => array(
|
@@ -123,11 +117,16 @@ class AMP_Allowed_Tags_Generated {
|
|
123 |
'disable-session-states' => array(
|
124 |
'value' => '',
|
125 |
),
|
|
|
|
|
|
|
126 |
),
|
127 |
'tag_spec' => array(
|
|
|
|
|
|
|
128 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-accordion',
|
129 |
),
|
130 |
-
|
131 |
),
|
132 |
),
|
133 |
'amp-ad' => array(
|
@@ -139,44 +138,55 @@ class AMP_Allowed_Tags_Generated {
|
|
139 |
'noloading' => array(
|
140 |
'value' => '',
|
141 |
),
|
|
|
142 |
'src' => array(
|
143 |
'blacklisted_value_regex' => '__amp_source_origin',
|
144 |
-
'
|
145 |
-
|
146 |
-
'
|
|
|
|
|
147 |
),
|
148 |
),
|
|
|
149 |
'type' => array(
|
150 |
'mandatory' => true,
|
151 |
),
|
152 |
),
|
153 |
'tag_spec' => array(
|
|
|
|
|
|
|
154 |
'disallowed_ancestor' => array(
|
155 |
-
|
156 |
),
|
157 |
-
'
|
158 |
-
|
159 |
),
|
160 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad',
|
161 |
),
|
162 |
-
|
163 |
),
|
164 |
array(
|
165 |
'attr_spec_list' => array(
|
166 |
'alt' => array(),
|
167 |
'data-multi-size' => array(
|
|
|
168 |
'mandatory' => true,
|
|
|
169 |
),
|
170 |
'json' => array(),
|
171 |
'media' => array(),
|
172 |
'noloading' => array(
|
173 |
'value' => '',
|
174 |
),
|
|
|
175 |
'src' => array(
|
176 |
'blacklisted_value_regex' => '__amp_source_origin',
|
177 |
-
'
|
178 |
-
|
179 |
-
'
|
|
|
|
|
180 |
),
|
181 |
),
|
182 |
'type' => array(
|
@@ -184,40 +194,64 @@ class AMP_Allowed_Tags_Generated {
|
|
184 |
),
|
185 |
),
|
186 |
'tag_spec' => array(
|
|
|
|
|
|
|
187 |
'disallowed_ancestor' => array(
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
),
|
194 |
-
'
|
195 |
-
|
196 |
),
|
197 |
'spec_name' => 'amp-ad with data-multi-size attribute',
|
198 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad',
|
199 |
),
|
200 |
-
|
201 |
),
|
202 |
-
),
|
203 |
-
'amp-ad-exit' => array(
|
204 |
array(
|
205 |
'attr_spec_list' => array(
|
206 |
-
'
|
|
|
|
|
207 |
'mandatory' => true,
|
|
|
208 |
),
|
|
|
209 |
'media' => array(),
|
210 |
'noloading' => array(
|
211 |
'value' => '',
|
212 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
),
|
214 |
'tag_spec' => array(
|
215 |
-
'
|
216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
),
|
218 |
-
'
|
|
|
219 |
),
|
220 |
-
|
221 |
),
|
222 |
),
|
223 |
'amp-analytics' => array(
|
@@ -225,22 +259,22 @@ class AMP_Allowed_Tags_Generated {
|
|
225 |
'attr_spec_list' => array(
|
226 |
'config' => array(
|
227 |
'blacklisted_value_regex' => '__amp_source_origin',
|
228 |
-
'
|
229 |
-
|
230 |
-
|
231 |
-
'
|
|
|
|
|
232 |
),
|
233 |
),
|
234 |
'type' => array(),
|
235 |
),
|
236 |
'tag_spec' => array(
|
237 |
-
'
|
238 |
-
|
239 |
-
'amp4ads',
|
240 |
),
|
241 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-analytics',
|
242 |
),
|
243 |
-
|
244 |
),
|
245 |
),
|
246 |
'amp-anim' => array(
|
@@ -259,22 +293,22 @@ class AMP_Allowed_Tags_Generated {
|
|
259 |
),
|
260 |
'blacklisted_value_regex' => '__amp_source_origin',
|
261 |
'mandatory' => true,
|
262 |
-
'
|
263 |
-
|
264 |
-
'
|
265 |
-
|
266 |
-
|
|
|
|
|
267 |
),
|
268 |
),
|
269 |
),
|
270 |
'tag_spec' => array(
|
271 |
-
'
|
272 |
-
|
273 |
-
'amp4ads',
|
274 |
),
|
275 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-anim',
|
276 |
),
|
277 |
-
|
278 |
),
|
279 |
),
|
280 |
'amp-animation' => array(
|
@@ -289,22 +323,20 @@ class AMP_Allowed_Tags_Generated {
|
|
289 |
),
|
290 |
),
|
291 |
'tag_spec' => array(
|
292 |
-
'
|
293 |
-
|
294 |
-
'amp4ads',
|
295 |
),
|
296 |
),
|
297 |
-
|
298 |
),
|
299 |
),
|
300 |
'amp-apester-media' => array(
|
301 |
array(
|
302 |
'attr_spec_list' => array(
|
303 |
'data-apester-channel-token' => array(
|
304 |
-
'value_regex' => '[0-9a-
|
305 |
),
|
306 |
'data-apester-media-id' => array(
|
307 |
-
'value_regex' => '[0-9a-
|
308 |
),
|
309 |
'media' => array(),
|
310 |
'noloading' => array(
|
@@ -312,12 +344,11 @@ class AMP_Allowed_Tags_Generated {
|
|
312 |
),
|
313 |
),
|
314 |
'tag_spec' => array(
|
315 |
-
'
|
316 |
-
|
317 |
),
|
318 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-apester-media',
|
319 |
),
|
320 |
-
|
321 |
),
|
322 |
),
|
323 |
'amp-app-banner' => array(
|
@@ -332,14 +363,13 @@ class AMP_Allowed_Tags_Generated {
|
|
332 |
),
|
333 |
),
|
334 |
'tag_spec' => array(
|
335 |
-
'html_format' => array(
|
336 |
-
'amp',
|
337 |
-
),
|
338 |
'mandatory_parent' => 'body',
|
|
|
|
|
|
|
339 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-app-banner',
|
340 |
'unique' => true,
|
341 |
),
|
342 |
-
|
343 |
),
|
344 |
),
|
345 |
'amp-audio' => array(
|
@@ -363,24 +393,28 @@ class AMP_Allowed_Tags_Generated {
|
|
363 |
'noloading' => array(
|
364 |
'value' => '',
|
365 |
),
|
|
|
|
|
|
|
366 |
'src' => array(
|
367 |
'blacklisted_value_regex' => '__amp_source_origin',
|
368 |
-
'
|
369 |
-
|
370 |
-
'
|
|
|
|
|
371 |
),
|
372 |
),
|
373 |
),
|
374 |
'tag_spec' => array(
|
375 |
'disallowed_ancestor' => array(
|
376 |
-
|
377 |
),
|
378 |
-
'
|
379 |
-
|
380 |
),
|
381 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
382 |
),
|
383 |
-
|
384 |
),
|
385 |
array(
|
386 |
'attr_spec_list' => array(
|
@@ -405,76 +439,114 @@ class AMP_Allowed_Tags_Generated {
|
|
405 |
),
|
406 |
'src' => array(
|
407 |
'blacklisted_value_regex' => '__amp_source_origin',
|
408 |
-
'
|
409 |
-
|
410 |
-
'
|
|
|
|
|
411 |
),
|
412 |
),
|
413 |
),
|
414 |
'tag_spec' => array(
|
415 |
-
'html_format' => array(
|
416 |
-
'amp',
|
417 |
-
),
|
418 |
'mandatory_ancestor' => 'amp-story',
|
|
|
|
|
|
|
419 |
'spec_name' => 'amp-story >> amp-audio',
|
420 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
421 |
),
|
422 |
-
|
423 |
),
|
|
|
|
|
424 |
array(
|
425 |
'attr_spec_list' => array(
|
426 |
-
'album' => array(),
|
427 |
-
'artist' => array(),
|
428 |
-
'artwork' => array(),
|
429 |
-
'controls' => array(),
|
430 |
-
'controlslist' => array(),
|
431 |
-
'loop' => array(
|
432 |
-
'value' => '',
|
433 |
-
),
|
434 |
'media' => array(),
|
435 |
-
'muted' => array(
|
436 |
-
'value' => '',
|
437 |
-
),
|
438 |
'noloading' => array(
|
439 |
'value' => '',
|
440 |
),
|
441 |
-
'
|
442 |
-
'
|
443 |
-
'allow_relative' => true,
|
444 |
-
'allowed_protocol' => array(
|
445 |
-
'https',
|
446 |
-
),
|
447 |
),
|
448 |
),
|
449 |
'tag_spec' => array(
|
450 |
-
'
|
451 |
-
|
|
|
452 |
),
|
453 |
-
'
|
454 |
-
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
455 |
),
|
456 |
-
|
457 |
),
|
458 |
),
|
459 |
-
'amp-
|
460 |
array(
|
461 |
'attr_spec_list' => array(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
462 |
'media' => array(),
|
463 |
'noloading' => array(
|
464 |
'value' => '',
|
465 |
),
|
466 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
467 |
'mandatory' => true,
|
468 |
),
|
469 |
),
|
470 |
'tag_spec' => array(
|
471 |
-
'
|
472 |
-
|
473 |
),
|
474 |
-
'
|
475 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
476 |
),
|
477 |
-
|
478 |
),
|
479 |
),
|
480 |
'amp-brid-player' => array(
|
@@ -504,12 +576,11 @@ class AMP_Allowed_Tags_Generated {
|
|
504 |
),
|
505 |
),
|
506 |
'tag_spec' => array(
|
507 |
-
'
|
508 |
-
|
509 |
),
|
510 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-brid-player',
|
511 |
),
|
512 |
-
|
513 |
),
|
514 |
),
|
515 |
'amp-brightcove' => array(
|
@@ -530,12 +601,32 @@ class AMP_Allowed_Tags_Generated {
|
|
530 |
),
|
531 |
),
|
532 |
'tag_spec' => array(
|
533 |
-
'
|
534 |
-
|
535 |
),
|
536 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-brightcove',
|
537 |
),
|
538 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
539 |
),
|
540 |
),
|
541 |
'amp-call-tracking' => array(
|
@@ -544,9 +635,11 @@ class AMP_Allowed_Tags_Generated {
|
|
544 |
'config' => array(
|
545 |
'blacklisted_value_regex' => '__amp_source_origin',
|
546 |
'mandatory' => true,
|
547 |
-
'
|
548 |
-
|
549 |
-
'
|
|
|
|
|
550 |
),
|
551 |
),
|
552 |
'media' => array(),
|
@@ -555,12 +648,11 @@ class AMP_Allowed_Tags_Generated {
|
|
555 |
),
|
556 |
),
|
557 |
'tag_spec' => array(
|
558 |
-
'
|
559 |
-
|
560 |
),
|
561 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-call-tracking',
|
562 |
),
|
563 |
-
|
564 |
),
|
565 |
),
|
566 |
'amp-carousel' => array(
|
@@ -580,6 +672,13 @@ class AMP_Allowed_Tags_Generated {
|
|
580 |
'dots' => array(
|
581 |
'value' => '',
|
582 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
583 |
'loop' => array(
|
584 |
'value' => '',
|
585 |
),
|
@@ -592,9 +691,27 @@ class AMP_Allowed_Tags_Generated {
|
|
592 |
),
|
593 |
),
|
594 |
'tag_spec' => array(
|
|
|
|
|
|
|
595 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-carousel',
|
596 |
),
|
597 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
598 |
),
|
599 |
),
|
600 |
'amp-dailymotion' => array(
|
@@ -632,116 +749,375 @@ class AMP_Allowed_Tags_Generated {
|
|
632 |
),
|
633 |
),
|
634 |
'tag_spec' => array(
|
635 |
-
'
|
636 |
-
|
637 |
),
|
638 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-dailymotion',
|
639 |
),
|
640 |
-
|
641 |
),
|
642 |
),
|
643 |
-
'amp-
|
644 |
array(
|
645 |
'attr_spec_list' => array(
|
646 |
-
'
|
647 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
648 |
'media' => array(),
|
|
|
|
|
|
|
|
|
|
|
649 |
'noloading' => array(
|
650 |
'value' => '',
|
651 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
652 |
'src' => array(
|
653 |
'blacklisted_value_regex' => '__amp_source_origin',
|
654 |
-
'
|
655 |
-
|
656 |
-
'
|
|
|
|
|
657 |
),
|
658 |
),
|
659 |
'type' => array(
|
660 |
-
'
|
661 |
),
|
|
|
662 |
),
|
663 |
'tag_spec' => array(
|
664 |
-
'
|
665 |
-
|
666 |
),
|
667 |
-
'
|
668 |
-
'amp',
|
669 |
-
),
|
670 |
-
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad',
|
671 |
),
|
672 |
-
|
673 |
),
|
674 |
array(
|
675 |
'attr_spec_list' => array(
|
676 |
-
'
|
677 |
-
'data-multi-size' => array(
|
678 |
-
'mandatory' => true,
|
679 |
'value' => '',
|
680 |
),
|
681 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
682 |
'media' => array(),
|
|
|
|
|
|
|
|
|
|
|
|
|
683 |
'noloading' => array(
|
684 |
'value' => '',
|
685 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
686 |
'src' => array(
|
687 |
'blacklisted_value_regex' => '__amp_source_origin',
|
688 |
-
'
|
689 |
-
|
690 |
-
'
|
|
|
|
|
691 |
),
|
692 |
),
|
693 |
'type' => array(
|
694 |
-
'
|
695 |
),
|
|
|
696 |
),
|
697 |
'tag_spec' => array(
|
698 |
-
'
|
699 |
-
|
700 |
-
'amp-carousel',
|
701 |
-
'amp-fx-flying-carpet',
|
702 |
-
'amp-lightbox',
|
703 |
-
'amp-sticky-ad',
|
704 |
-
),
|
705 |
-
'html_format' => array(
|
706 |
-
'amp',
|
707 |
-
),
|
708 |
-
'spec_name' => 'amp-embed with data-multi-size attribute',
|
709 |
-
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad',
|
710 |
-
),
|
711 |
-
|
712 |
-
),
|
713 |
-
),
|
714 |
-
'amp-experiment' => array(
|
715 |
-
array(
|
716 |
-
'attr_spec_list' => array(),
|
717 |
-
'tag_spec' => array(
|
718 |
-
'html_format' => array(
|
719 |
-
'amp',
|
720 |
),
|
721 |
-
'
|
722 |
-
'unique' => true,
|
723 |
),
|
724 |
-
|
725 |
),
|
726 |
-
),
|
727 |
-
'amp-facebook' => array(
|
728 |
array(
|
729 |
'attr_spec_list' => array(
|
730 |
-
'
|
731 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
732 |
),
|
|
|
|
|
|
|
733 |
'media' => array(),
|
|
|
|
|
|
|
|
|
|
|
734 |
'noloading' => array(
|
735 |
'value' => '',
|
736 |
),
|
737 |
-
|
738 |
-
|
739 |
-
'html_format' => array(
|
740 |
-
'amp',
|
741 |
),
|
742 |
-
|
743 |
-
|
744 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
745 |
),
|
746 |
'amp-facebook-comments' => array(
|
747 |
array(
|
@@ -755,11 +1131,10 @@ class AMP_Allowed_Tags_Generated {
|
|
755 |
),
|
756 |
),
|
757 |
'tag_spec' => array(
|
758 |
-
'
|
759 |
-
|
760 |
),
|
761 |
),
|
762 |
-
|
763 |
),
|
764 |
),
|
765 |
'amp-facebook-like' => array(
|
@@ -767,10 +1142,12 @@ class AMP_Allowed_Tags_Generated {
|
|
767 |
'attr_spec_list' => array(
|
768 |
'data-href' => array(
|
769 |
'mandatory' => true,
|
770 |
-
'
|
771 |
-
|
772 |
-
'
|
773 |
-
|
|
|
|
|
774 |
),
|
775 |
),
|
776 |
'media' => array(),
|
@@ -779,11 +1156,35 @@ class AMP_Allowed_Tags_Generated {
|
|
779 |
),
|
780 |
),
|
781 |
'tag_spec' => array(
|
782 |
-
'
|
783 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
784 |
),
|
785 |
),
|
786 |
-
|
787 |
),
|
788 |
),
|
789 |
'amp-fit-text' => array(
|
@@ -796,8 +1197,11 @@ class AMP_Allowed_Tags_Generated {
|
|
796 |
'value' => '',
|
797 |
),
|
798 |
),
|
799 |
-
'tag_spec' => array(
|
800 |
-
|
|
|
|
|
|
|
801 |
),
|
802 |
),
|
803 |
'amp-font' => array(
|
@@ -822,12 +1226,10 @@ class AMP_Allowed_Tags_Generated {
|
|
822 |
),
|
823 |
),
|
824 |
'tag_spec' => array(
|
825 |
-
'
|
826 |
-
|
827 |
-
'amp4ads',
|
828 |
),
|
829 |
),
|
830 |
-
|
831 |
),
|
832 |
),
|
833 |
'amp-fx-flying-carpet' => array(
|
@@ -842,11 +1244,10 @@ class AMP_Allowed_Tags_Generated {
|
|
842 |
),
|
843 |
),
|
844 |
'tag_spec' => array(
|
845 |
-
'
|
846 |
-
|
847 |
),
|
848 |
),
|
849 |
-
|
850 |
),
|
851 |
),
|
852 |
'amp-gfycat' => array(
|
@@ -864,12 +1265,11 @@ class AMP_Allowed_Tags_Generated {
|
|
864 |
),
|
865 |
),
|
866 |
'tag_spec' => array(
|
867 |
-
'
|
868 |
-
|
869 |
),
|
870 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-gfycat',
|
871 |
),
|
872 |
-
|
873 |
),
|
874 |
),
|
875 |
'amp-gist' => array(
|
@@ -884,31 +1284,11 @@ class AMP_Allowed_Tags_Generated {
|
|
884 |
),
|
885 |
),
|
886 |
'tag_spec' => array(
|
887 |
-
'
|
888 |
-
|
889 |
),
|
890 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-gist',
|
891 |
),
|
892 |
-
|
893 |
-
),
|
894 |
-
),
|
895 |
-
'amp-gwd-animation' => array(
|
896 |
-
array(
|
897 |
-
'attr_spec_list' => array(
|
898 |
-
'media' => array(),
|
899 |
-
'noloading' => array(
|
900 |
-
'value' => '',
|
901 |
-
),
|
902 |
-
'timeline-event-prefix' => array(
|
903 |
-
'value' => '',
|
904 |
-
),
|
905 |
-
),
|
906 |
-
'tag_spec' => array(
|
907 |
-
'html_format' => array(
|
908 |
-
'amp4ads',
|
909 |
-
),
|
910 |
-
),
|
911 |
-
|
912 |
),
|
913 |
),
|
914 |
'amp-hulu' => array(
|
@@ -923,21 +1303,18 @@ class AMP_Allowed_Tags_Generated {
|
|
923 |
),
|
924 |
),
|
925 |
'tag_spec' => array(
|
926 |
-
'
|
927 |
-
|
928 |
),
|
929 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-hulu',
|
930 |
),
|
931 |
-
|
932 |
),
|
933 |
),
|
934 |
'amp-iframe' => array(
|
935 |
array(
|
936 |
'attr_spec_list' => array(
|
937 |
'[src]' => array(),
|
938 |
-
'allow' => array(
|
939 |
-
'value_regex' => 'geolocation|fullscreen|payment|transparency',
|
940 |
-
),
|
941 |
'allowfullscreen' => array(
|
942 |
'value' => '',
|
943 |
),
|
@@ -964,37 +1341,45 @@ class AMP_Allowed_Tags_Generated {
|
|
964 |
),
|
965 |
'src' => array(
|
966 |
'blacklisted_value_regex' => '__amp_source_origin',
|
967 |
-
'
|
968 |
-
|
969 |
-
'
|
970 |
-
|
|
|
|
|
971 |
),
|
972 |
),
|
973 |
'srcdoc' => array(),
|
974 |
),
|
975 |
'tag_spec' => array(
|
976 |
-
'
|
977 |
-
|
978 |
),
|
979 |
),
|
980 |
-
|
981 |
),
|
982 |
),
|
983 |
'amp-ima-video' => array(
|
984 |
array(
|
985 |
'attr_spec_list' => array(
|
|
|
|
|
|
|
986 |
'data-src' => array(
|
987 |
'blacklisted_value_regex' => '__amp_source_origin',
|
988 |
-
'
|
989 |
-
|
990 |
-
'
|
|
|
|
|
991 |
),
|
992 |
),
|
993 |
'data-tag' => array(
|
994 |
'mandatory' => true,
|
995 |
-
'
|
996 |
-
|
997 |
-
'
|
|
|
|
|
998 |
),
|
999 |
),
|
1000 |
'media' => array(),
|
@@ -1003,12 +1388,11 @@ class AMP_Allowed_Tags_Generated {
|
|
1003 |
),
|
1004 |
),
|
1005 |
'tag_spec' => array(
|
1006 |
-
'
|
1007 |
-
|
1008 |
),
|
1009 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ima-video',
|
1010 |
),
|
1011 |
-
|
1012 |
),
|
1013 |
),
|
1014 |
'amp-image-lightbox' => array(
|
@@ -1021,11 +1405,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1021 |
),
|
1022 |
),
|
1023 |
'tag_spec' => array(
|
1024 |
-
'
|
1025 |
-
|
1026 |
),
|
1027 |
),
|
1028 |
-
|
1029 |
),
|
1030 |
),
|
1031 |
'amp-img' => array(
|
@@ -1037,6 +1420,13 @@ class AMP_Allowed_Tags_Generated {
|
|
1037 |
'[srcset]' => array(),
|
1038 |
'alt' => array(),
|
1039 |
'attribution' => array(),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1040 |
'media' => array(),
|
1041 |
'noloading' => array(
|
1042 |
'value' => '',
|
@@ -1048,22 +1438,19 @@ class AMP_Allowed_Tags_Generated {
|
|
1048 |
),
|
1049 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1050 |
'mandatory' => true,
|
1051 |
-
'
|
1052 |
-
|
1053 |
-
'
|
1054 |
-
|
1055 |
-
|
|
|
|
|
1056 |
),
|
1057 |
),
|
1058 |
),
|
1059 |
'tag_spec' => array(
|
1060 |
-
'html_format' => array(
|
1061 |
-
'amp',
|
1062 |
-
'amp4ads',
|
1063 |
-
),
|
1064 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-img',
|
1065 |
),
|
1066 |
-
|
1067 |
),
|
1068 |
),
|
1069 |
'amp-imgur' => array(
|
@@ -1078,11 +1465,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1078 |
),
|
1079 |
),
|
1080 |
'tag_spec' => array(
|
1081 |
-
'
|
1082 |
-
|
1083 |
),
|
1084 |
),
|
1085 |
-
|
1086 |
),
|
1087 |
),
|
1088 |
'amp-instagram' => array(
|
@@ -1098,11 +1484,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1098 |
),
|
1099 |
),
|
1100 |
'tag_spec' => array(
|
1101 |
-
'
|
1102 |
-
|
1103 |
),
|
1104 |
),
|
1105 |
-
|
1106 |
),
|
1107 |
),
|
1108 |
'amp-install-serviceworker' => array(
|
@@ -1110,26 +1495,29 @@ class AMP_Allowed_Tags_Generated {
|
|
1110 |
'attr_spec_list' => array(
|
1111 |
'data-iframe-src' => array(
|
1112 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1113 |
-
'
|
1114 |
-
|
1115 |
-
'
|
|
|
|
|
1116 |
),
|
1117 |
),
|
1118 |
'src' => array(
|
1119 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1120 |
'mandatory' => true,
|
1121 |
-
'
|
1122 |
-
|
1123 |
-
'
|
|
|
|
|
1124 |
),
|
1125 |
),
|
1126 |
),
|
1127 |
'tag_spec' => array(
|
1128 |
-
'
|
1129 |
-
|
1130 |
),
|
1131 |
),
|
1132 |
-
|
1133 |
),
|
1134 |
),
|
1135 |
'amp-izlesene' => array(
|
@@ -1145,11 +1533,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1145 |
),
|
1146 |
),
|
1147 |
'tag_spec' => array(
|
1148 |
-
'
|
1149 |
-
|
1150 |
),
|
1151 |
),
|
1152 |
-
|
1153 |
),
|
1154 |
),
|
1155 |
'amp-jwplayer' => array(
|
@@ -1167,11 +1554,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1167 |
),
|
1168 |
),
|
1169 |
'tag_spec' => array(
|
1170 |
-
'
|
1171 |
-
|
1172 |
),
|
1173 |
),
|
1174 |
-
|
1175 |
),
|
1176 |
),
|
1177 |
'amp-kaltura-player' => array(
|
@@ -1186,11 +1572,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1186 |
),
|
1187 |
),
|
1188 |
'tag_spec' => array(
|
1189 |
-
'
|
1190 |
-
|
1191 |
),
|
1192 |
),
|
1193 |
-
|
1194 |
),
|
1195 |
),
|
1196 |
'amp-layout' => array(
|
@@ -1202,13 +1587,8 @@ class AMP_Allowed_Tags_Generated {
|
|
1202 |
),
|
1203 |
),
|
1204 |
'tag_spec' => array(
|
1205 |
-
'html_format' => array(
|
1206 |
-
'amp',
|
1207 |
-
'amp4ads',
|
1208 |
-
),
|
1209 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-layout',
|
1210 |
),
|
1211 |
-
|
1212 |
),
|
1213 |
),
|
1214 |
'amp-lightbox' => array(
|
@@ -1223,11 +1603,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1223 |
'scrollable' => array(),
|
1224 |
),
|
1225 |
'tag_spec' => array(
|
1226 |
-
'
|
1227 |
-
|
1228 |
),
|
1229 |
),
|
1230 |
-
|
1231 |
),
|
1232 |
),
|
1233 |
'amp-list' => array(
|
@@ -1242,23 +1621,58 @@ class AMP_Allowed_Tags_Generated {
|
|
1242 |
'noloading' => array(
|
1243 |
'value' => '',
|
1244 |
),
|
|
|
|
|
|
|
1245 |
'single-item' => array(),
|
1246 |
'src' => array(
|
1247 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1248 |
'mandatory' => true,
|
1249 |
-
'
|
1250 |
-
|
1251 |
-
'
|
|
|
|
|
1252 |
),
|
1253 |
),
|
1254 |
'template' => array(),
|
1255 |
),
|
1256 |
'tag_spec' => array(
|
1257 |
-
'
|
1258 |
-
|
1259 |
),
|
1260 |
),
|
1261 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1262 |
),
|
1263 |
),
|
1264 |
'amp-live-list' => array(
|
@@ -1277,22 +1691,43 @@ class AMP_Allowed_Tags_Generated {
|
|
1277 |
'id' => array(
|
1278 |
'mandatory' => true,
|
1279 |
),
|
|
|
|
|
|
|
1280 |
),
|
1281 |
'tag_spec' => array(
|
1282 |
-
'
|
1283 |
-
|
1284 |
),
|
1285 |
),
|
1286 |
-
|
1287 |
),
|
1288 |
),
|
1289 |
-
'amp-
|
1290 |
array(
|
1291 |
'attr_spec_list' => array(
|
1292 |
-
'data-
|
1293 |
'mandatory' => true,
|
1294 |
),
|
1295 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1296 |
'mandatory' => true,
|
1297 |
'value_regex' => '[^=/?:]+',
|
1298 |
),
|
@@ -1300,13 +1735,14 @@ class AMP_Allowed_Tags_Generated {
|
|
1300 |
'value_regex' => 'api|static',
|
1301 |
),
|
1302 |
'data-origin' => array(
|
1303 |
-
'
|
1304 |
-
|
1305 |
-
'
|
1306 |
-
|
|
|
|
|
1307 |
),
|
1308 |
),
|
1309 |
-
'data-seek-to' => array(),
|
1310 |
'data-streamtype' => array(
|
1311 |
'value_regex' => 'album|audio|live|playlist|playlist-marked|video',
|
1312 |
),
|
@@ -1316,11 +1752,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1316 |
),
|
1317 |
),
|
1318 |
'tag_spec' => array(
|
1319 |
-
'
|
1320 |
-
|
1321 |
),
|
1322 |
),
|
1323 |
-
|
1324 |
),
|
1325 |
),
|
1326 |
'amp-o2-player' => array(
|
@@ -1338,11 +1773,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1338 |
),
|
1339 |
),
|
1340 |
'tag_spec' => array(
|
1341 |
-
'
|
1342 |
-
|
1343 |
),
|
1344 |
),
|
1345 |
-
|
1346 |
),
|
1347 |
),
|
1348 |
'amp-ooyala-player' => array(
|
@@ -1363,16 +1797,16 @@ class AMP_Allowed_Tags_Generated {
|
|
1363 |
),
|
1364 |
),
|
1365 |
'tag_spec' => array(
|
1366 |
-
'
|
1367 |
-
|
1368 |
),
|
1369 |
),
|
1370 |
-
|
1371 |
),
|
1372 |
),
|
1373 |
'amp-pinterest' => array(
|
1374 |
array(
|
1375 |
'attr_spec_list' => array(
|
|
|
1376 |
'data-do' => array(
|
1377 |
'mandatory' => true,
|
1378 |
),
|
@@ -1382,17 +1816,17 @@ class AMP_Allowed_Tags_Generated {
|
|
1382 |
),
|
1383 |
),
|
1384 |
'tag_spec' => array(
|
1385 |
-
'
|
1386 |
-
|
1387 |
),
|
1388 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-pinterest',
|
1389 |
),
|
1390 |
-
|
1391 |
),
|
1392 |
),
|
1393 |
'amp-pixel' => array(
|
1394 |
array(
|
1395 |
'attr_spec_list' => array(
|
|
|
1396 |
'media' => array(),
|
1397 |
'noloading' => array(
|
1398 |
'value' => '',
|
@@ -1403,21 +1837,18 @@ class AMP_Allowed_Tags_Generated {
|
|
1403 |
'src' => array(
|
1404 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1405 |
'mandatory' => true,
|
1406 |
-
'
|
1407 |
-
|
1408 |
-
|
1409 |
-
'
|
|
|
|
|
1410 |
),
|
1411 |
),
|
1412 |
),
|
1413 |
'tag_spec' => array(
|
1414 |
-
'html_format' => array(
|
1415 |
-
'amp',
|
1416 |
-
'amp4ads',
|
1417 |
-
),
|
1418 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-pixel',
|
1419 |
),
|
1420 |
-
|
1421 |
),
|
1422 |
),
|
1423 |
'amp-playbuzz' => array(
|
@@ -1440,11 +1871,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1440 |
'src' => array(),
|
1441 |
),
|
1442 |
'tag_spec' => array(
|
1443 |
-
'
|
1444 |
-
|
1445 |
),
|
1446 |
),
|
1447 |
-
|
1448 |
),
|
1449 |
),
|
1450 |
'amp-position-observer' => array(
|
@@ -1463,12 +1893,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1463 |
),
|
1464 |
),
|
1465 |
'tag_spec' => array(
|
1466 |
-
'
|
1467 |
-
|
1468 |
-
'amp4ads',
|
1469 |
),
|
1470 |
),
|
1471 |
-
|
1472 |
),
|
1473 |
),
|
1474 |
'amp-reach-player' => array(
|
@@ -1484,11 +1912,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1484 |
),
|
1485 |
),
|
1486 |
'tag_spec' => array(
|
1487 |
-
'
|
1488 |
-
|
1489 |
),
|
1490 |
),
|
1491 |
-
|
1492 |
),
|
1493 |
),
|
1494 |
'amp-reddit' => array(
|
@@ -1513,16 +1940,36 @@ class AMP_Allowed_Tags_Generated {
|
|
1513 |
),
|
1514 |
),
|
1515 |
'tag_spec' => array(
|
1516 |
-
'
|
1517 |
-
|
1518 |
),
|
1519 |
),
|
1520 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1521 |
),
|
1522 |
),
|
1523 |
'amp-selector' => array(
|
1524 |
array(
|
1525 |
'attr_spec_list' => array(
|
|
|
1526 |
'[selected]' => array(),
|
1527 |
'disabled' => array(
|
1528 |
'value' => '',
|
@@ -1536,7 +1983,7 @@ class AMP_Allowed_Tags_Generated {
|
|
1536 |
'value' => '',
|
1537 |
),
|
1538 |
'name' => array(
|
1539 |
-
'blacklisted_value_regex' => '(^|\\s)(__amp_\\
|
1540 |
),
|
1541 |
'noloading' => array(
|
1542 |
'value' => '',
|
@@ -1544,13 +1991,12 @@ class AMP_Allowed_Tags_Generated {
|
|
1544 |
),
|
1545 |
'tag_spec' => array(
|
1546 |
'disallowed_ancestor' => array(
|
1547 |
-
|
1548 |
),
|
1549 |
-
'
|
1550 |
-
|
1551 |
),
|
1552 |
),
|
1553 |
-
|
1554 |
),
|
1555 |
),
|
1556 |
'amp-sidebar' => array(
|
@@ -1565,12 +2011,11 @@ class AMP_Allowed_Tags_Generated {
|
|
1565 |
),
|
1566 |
),
|
1567 |
'tag_spec' => array(
|
1568 |
-
'html_format' => array(
|
1569 |
-
'amp',
|
1570 |
-
),
|
1571 |
'mandatory_parent' => 'body',
|
|
|
|
|
|
|
1572 |
),
|
1573 |
-
|
1574 |
),
|
1575 |
),
|
1576 |
'amp-social-share' => array(
|
@@ -1578,24 +2023,26 @@ class AMP_Allowed_Tags_Generated {
|
|
1578 |
'attr_spec_list' => array(
|
1579 |
'data-share-endpoint' => array(
|
1580 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1581 |
-
'
|
1582 |
-
|
1583 |
-
'
|
1584 |
-
|
1585 |
-
|
1586 |
-
|
1587 |
-
|
1588 |
-
|
1589 |
-
|
1590 |
-
|
1591 |
-
|
1592 |
-
|
1593 |
-
|
1594 |
-
|
1595 |
-
|
1596 |
-
|
1597 |
-
|
1598 |
-
|
|
|
|
|
1599 |
),
|
1600 |
),
|
1601 |
'media' => array(),
|
@@ -1607,12 +2054,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1607 |
),
|
1608 |
),
|
1609 |
'tag_spec' => array(
|
1610 |
-
'
|
1611 |
-
|
1612 |
-
'amp4ads',
|
1613 |
),
|
1614 |
),
|
1615 |
-
|
1616 |
),
|
1617 |
),
|
1618 |
'amp-soundcloud' => array(
|
@@ -1625,7 +2070,7 @@ class AMP_Allowed_Tags_Generated {
|
|
1625 |
'value_regex' => '[0-9]+',
|
1626 |
),
|
1627 |
'data-secret-token' => array(
|
1628 |
-
'value_regex' => '[
|
1629 |
),
|
1630 |
'data-trackid' => array(
|
1631 |
'value_regex' => '[0-9]+',
|
@@ -1639,11 +2084,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1639 |
),
|
1640 |
),
|
1641 |
'tag_spec' => array(
|
1642 |
-
'
|
1643 |
-
|
1644 |
),
|
1645 |
),
|
1646 |
-
|
1647 |
),
|
1648 |
),
|
1649 |
'amp-springboard-player' => array(
|
@@ -1676,11 +2120,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1676 |
),
|
1677 |
),
|
1678 |
'tag_spec' => array(
|
1679 |
-
'
|
1680 |
-
|
1681 |
),
|
1682 |
),
|
1683 |
-
|
1684 |
),
|
1685 |
),
|
1686 |
'amp-state' => array(
|
@@ -1691,22 +2134,24 @@ class AMP_Allowed_Tags_Generated {
|
|
1691 |
'id' => array(
|
1692 |
'mandatory' => true,
|
1693 |
),
|
|
|
1694 |
'src' => array(
|
1695 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1696 |
-
'
|
1697 |
-
|
1698 |
-
'
|
|
|
|
|
1699 |
),
|
1700 |
),
|
1701 |
),
|
1702 |
'tag_spec' => array(
|
1703 |
-
'
|
1704 |
-
|
1705 |
),
|
1706 |
'spec_name' => 'amp-state',
|
1707 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-bind',
|
1708 |
),
|
1709 |
-
|
1710 |
),
|
1711 |
),
|
1712 |
'amp-sticky-ad' => array(
|
@@ -1719,29 +2164,32 @@ class AMP_Allowed_Tags_Generated {
|
|
1719 |
),
|
1720 |
'tag_spec' => array(
|
1721 |
'disallowed_ancestor' => array(
|
1722 |
-
|
1723 |
),
|
1724 |
-
'
|
1725 |
-
|
1726 |
),
|
1727 |
'unique' => true,
|
1728 |
),
|
1729 |
-
|
1730 |
),
|
1731 |
),
|
1732 |
'amp-story' => array(
|
1733 |
array(
|
1734 |
'attr_spec_list' => array(
|
1735 |
'background-audio' => array(
|
1736 |
-
'
|
1737 |
-
'
|
1738 |
-
|
|
|
|
|
1739 |
),
|
1740 |
),
|
1741 |
'bookend-config-src' => array(
|
1742 |
-
'
|
1743 |
-
'
|
1744 |
-
|
|
|
|
|
1745 |
),
|
1746 |
),
|
1747 |
'standalone' => array(
|
@@ -1750,12 +2198,32 @@ class AMP_Allowed_Tags_Generated {
|
|
1750 |
),
|
1751 |
),
|
1752 |
'tag_spec' => array(
|
1753 |
-
'html_format' => array(
|
1754 |
-
'amp',
|
1755 |
-
),
|
1756 |
'mandatory_parent' => 'body',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1757 |
),
|
1758 |
-
|
1759 |
),
|
1760 |
),
|
1761 |
'amp-story-grid-layer' => array(
|
@@ -1767,12 +2235,8 @@ class AMP_Allowed_Tags_Generated {
|
|
1767 |
),
|
1768 |
),
|
1769 |
'tag_spec' => array(
|
1770 |
-
'html_format' => array(
|
1771 |
-
'amp',
|
1772 |
-
),
|
1773 |
'mandatory_ancestor' => 'amp-story-page',
|
1774 |
),
|
1775 |
-
|
1776 |
),
|
1777 |
),
|
1778 |
'amp-story-page' => array(
|
@@ -1780,9 +2244,11 @@ class AMP_Allowed_Tags_Generated {
|
|
1780 |
'attr_spec_list' => array(
|
1781 |
'auto-advance-after' => array(),
|
1782 |
'background-audio' => array(
|
1783 |
-
'
|
1784 |
-
'
|
1785 |
-
|
|
|
|
|
1786 |
),
|
1787 |
),
|
1788 |
'id' => array(
|
@@ -1790,12 +2256,11 @@ class AMP_Allowed_Tags_Generated {
|
|
1790 |
),
|
1791 |
),
|
1792 |
'tag_spec' => array(
|
1793 |
-
'html_format' => array(
|
1794 |
-
'amp',
|
1795 |
-
),
|
1796 |
'mandatory_parent' => 'amp-story',
|
|
|
|
|
|
|
1797 |
),
|
1798 |
-
|
1799 |
),
|
1800 |
),
|
1801 |
'amp-timeago' => array(
|
@@ -1806,7 +2271,7 @@ class AMP_Allowed_Tags_Generated {
|
|
1806 |
),
|
1807 |
'datetime' => array(
|
1808 |
'mandatory' => true,
|
1809 |
-
'value_regex' => '\\d{4}-[01]\\d-[0-3]\\
|
1810 |
),
|
1811 |
'locale' => array(),
|
1812 |
'media' => array(),
|
@@ -1815,12 +2280,11 @@ class AMP_Allowed_Tags_Generated {
|
|
1815 |
),
|
1816 |
),
|
1817 |
'tag_spec' => array(
|
1818 |
-
'
|
1819 |
-
|
1820 |
),
|
1821 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-timeago',
|
1822 |
),
|
1823 |
-
|
1824 |
),
|
1825 |
),
|
1826 |
'amp-twitter' => array(
|
@@ -1835,28 +2299,31 @@ class AMP_Allowed_Tags_Generated {
|
|
1835 |
),
|
1836 |
),
|
1837 |
'tag_spec' => array(
|
1838 |
-
'
|
1839 |
-
|
1840 |
),
|
1841 |
),
|
1842 |
-
|
1843 |
),
|
1844 |
),
|
1845 |
'amp-user-notification' => array(
|
1846 |
array(
|
1847 |
'attr_spec_list' => array(
|
1848 |
'data-dismiss-href' => array(
|
1849 |
-
'
|
1850 |
-
|
1851 |
-
|
1852 |
-
'
|
|
|
|
|
1853 |
),
|
1854 |
),
|
1855 |
'data-show-if-href' => array(
|
1856 |
-
'
|
1857 |
-
|
1858 |
-
|
1859 |
-
'
|
|
|
|
|
1860 |
),
|
1861 |
),
|
1862 |
'enctype' => array(
|
@@ -1868,11 +2335,10 @@ class AMP_Allowed_Tags_Generated {
|
|
1868 |
),
|
1869 |
),
|
1870 |
'tag_spec' => array(
|
1871 |
-
'
|
1872 |
-
|
1873 |
),
|
1874 |
),
|
1875 |
-
|
1876 |
),
|
1877 |
),
|
1878 |
'amp-video' => array(
|
@@ -1906,6 +2372,13 @@ class AMP_Allowed_Tags_Generated {
|
|
1906 |
'disableremoteplayback' => array(
|
1907 |
'value' => '',
|
1908 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1909 |
'loop' => array(
|
1910 |
'value' => '',
|
1911 |
),
|
@@ -1923,23 +2396,23 @@ class AMP_Allowed_Tags_Generated {
|
|
1923 |
),
|
1924 |
'src' => array(
|
1925 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1926 |
-
'
|
1927 |
-
|
1928 |
-
'
|
|
|
|
|
1929 |
),
|
1930 |
),
|
1931 |
),
|
1932 |
'tag_spec' => array(
|
1933 |
-
'
|
1934 |
-
|
1935 |
),
|
1936 |
-
'
|
1937 |
-
|
1938 |
-
'amp4ads',
|
1939 |
),
|
1940 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
|
1941 |
),
|
1942 |
-
|
1943 |
),
|
1944 |
array(
|
1945 |
'attr_spec_list' => array(
|
@@ -1990,22 +2463,22 @@ class AMP_Allowed_Tags_Generated {
|
|
1990 |
),
|
1991 |
'src' => array(
|
1992 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1993 |
-
'
|
1994 |
-
|
1995 |
-
'
|
|
|
|
|
1996 |
),
|
1997 |
),
|
1998 |
),
|
1999 |
'tag_spec' => array(
|
2000 |
-
'html_format' => array(
|
2001 |
-
'amp',
|
2002 |
-
'amp4ads',
|
2003 |
-
),
|
2004 |
'mandatory_ancestor' => 'amp-story',
|
|
|
|
|
|
|
2005 |
'spec_name' => 'amp-story >> amp-video',
|
2006 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
|
2007 |
),
|
2008 |
-
|
2009 |
),
|
2010 |
),
|
2011 |
'amp-vimeo' => array(
|
@@ -2021,11 +2494,10 @@ class AMP_Allowed_Tags_Generated {
|
|
2021 |
),
|
2022 |
),
|
2023 |
'tag_spec' => array(
|
2024 |
-
'
|
2025 |
-
|
2026 |
),
|
2027 |
),
|
2028 |
-
|
2029 |
),
|
2030 |
),
|
2031 |
'amp-vine' => array(
|
@@ -2040,11 +2512,10 @@ class AMP_Allowed_Tags_Generated {
|
|
2040 |
),
|
2041 |
),
|
2042 |
'tag_spec' => array(
|
2043 |
-
'
|
2044 |
-
|
2045 |
),
|
2046 |
),
|
2047 |
-
|
2048 |
),
|
2049 |
),
|
2050 |
'amp-vk' => array(
|
@@ -2059,11 +2530,10 @@ class AMP_Allowed_Tags_Generated {
|
|
2059 |
),
|
2060 |
),
|
2061 |
'tag_spec' => array(
|
2062 |
-
'
|
2063 |
-
|
2064 |
),
|
2065 |
),
|
2066 |
-
|
2067 |
),
|
2068 |
),
|
2069 |
'amp-web-push' => array(
|
@@ -2071,9 +2541,11 @@ class AMP_Allowed_Tags_Generated {
|
|
2071 |
'attr_spec_list' => array(
|
2072 |
'helper-iframe-url' => array(
|
2073 |
'mandatory' => true,
|
2074 |
-
'
|
2075 |
-
|
2076 |
-
'
|
|
|
|
|
2077 |
),
|
2078 |
),
|
2079 |
'id' => array(
|
@@ -2086,26 +2558,29 @@ class AMP_Allowed_Tags_Generated {
|
|
2086 |
),
|
2087 |
'permission-dialog-url' => array(
|
2088 |
'mandatory' => true,
|
2089 |
-
'
|
2090 |
-
|
2091 |
-
'
|
|
|
|
|
2092 |
),
|
2093 |
),
|
2094 |
'service-worker-url' => array(
|
2095 |
'mandatory' => true,
|
2096 |
-
'
|
2097 |
-
|
2098 |
-
'
|
|
|
|
|
2099 |
),
|
2100 |
),
|
2101 |
),
|
2102 |
'tag_spec' => array(
|
2103 |
-
'
|
2104 |
-
|
2105 |
),
|
2106 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-web-push',
|
2107 |
),
|
2108 |
-
|
2109 |
),
|
2110 |
),
|
2111 |
'amp-web-push-widget' => array(
|
@@ -2121,12 +2596,30 @@ class AMP_Allowed_Tags_Generated {
|
|
2121 |
),
|
2122 |
),
|
2123 |
'tag_spec' => array(
|
2124 |
-
'
|
2125 |
-
|
2126 |
),
|
2127 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-web-push',
|
2128 |
),
|
2129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2130 |
),
|
2131 |
),
|
2132 |
'amp-youtube' => array(
|
@@ -2137,31 +2630,41 @@ class AMP_Allowed_Tags_Generated {
|
|
2137 |
'credentials' => array(
|
2138 |
'value_regex_casei' => '(include|omit)',
|
2139 |
),
|
|
|
|
|
|
|
2140 |
'data-videoid' => array(
|
2141 |
-
'mandatory' => true,
|
2142 |
'value_regex' => '[^=/?:]+',
|
2143 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2144 |
'media' => array(),
|
2145 |
'noloading' => array(
|
2146 |
'value' => '',
|
2147 |
),
|
2148 |
),
|
2149 |
-
'tag_spec' => array(
|
2150 |
-
|
|
|
|
|
|
|
2151 |
),
|
2152 |
),
|
2153 |
'article' => array(
|
2154 |
array(
|
2155 |
'attr_spec_list' => array(),
|
2156 |
'tag_spec' => array(),
|
2157 |
-
|
2158 |
),
|
2159 |
),
|
2160 |
'aside' => array(
|
2161 |
array(
|
2162 |
'attr_spec_list' => array(),
|
2163 |
'tag_spec' => array(),
|
2164 |
-
|
2165 |
),
|
2166 |
),
|
2167 |
'audio' => array(
|
@@ -2174,36 +2677,48 @@ class AMP_Allowed_Tags_Generated {
|
|
2174 |
'preload' => array(),
|
2175 |
'src' => array(
|
2176 |
'blacklisted_value_regex' => '__amp_source_origin',
|
2177 |
-
'
|
2178 |
-
|
2179 |
-
'
|
2180 |
-
|
|
|
|
|
2181 |
),
|
2182 |
),
|
2183 |
),
|
2184 |
'tag_spec' => array(
|
2185 |
-
'html_format' => array(
|
2186 |
-
'amp',
|
2187 |
-
),
|
2188 |
'mandatory_ancestor' => 'noscript',
|
2189 |
'mandatory_ancestor_suggested_alternative' => 'amp-audio',
|
2190 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
2191 |
),
|
2192 |
-
|
2193 |
),
|
2194 |
),
|
2195 |
'b' => array(
|
2196 |
array(
|
2197 |
'attr_spec_list' => array(),
|
2198 |
'tag_spec' => array(),
|
2199 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2200 |
),
|
2201 |
),
|
2202 |
'bdi' => array(
|
2203 |
array(
|
2204 |
'attr_spec_list' => array(),
|
2205 |
'tag_spec' => array(),
|
2206 |
-
|
2207 |
),
|
2208 |
),
|
2209 |
'bdo' => array(
|
@@ -2212,18 +2727,12 @@ class AMP_Allowed_Tags_Generated {
|
|
2212 |
'dir' => array(),
|
2213 |
),
|
2214 |
'tag_spec' => array(),
|
2215 |
-
|
2216 |
),
|
2217 |
),
|
2218 |
'big' => array(
|
2219 |
array(
|
2220 |
'attr_spec_list' => array(),
|
2221 |
-
'tag_spec' => array(
|
2222 |
-
'html_format' => array(
|
2223 |
-
'amp',
|
2224 |
-
),
|
2225 |
-
),
|
2226 |
-
|
2227 |
),
|
2228 |
),
|
2229 |
'blockquote' => array(
|
@@ -2232,16 +2741,17 @@ class AMP_Allowed_Tags_Generated {
|
|
2232 |
'align' => array(),
|
2233 |
'cite' => array(
|
2234 |
'blacklisted_value_regex' => '__amp_source_origin',
|
2235 |
-
'
|
2236 |
-
|
2237 |
-
|
2238 |
-
'
|
2239 |
-
|
|
|
|
|
2240 |
),
|
2241 |
),
|
2242 |
),
|
2243 |
'tag_spec' => array(),
|
2244 |
-
|
2245 |
),
|
2246 |
),
|
2247 |
'body' => array(
|
@@ -2253,14 +2763,12 @@ class AMP_Allowed_Tags_Generated {
|
|
2253 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#required-markup',
|
2254 |
'unique' => true,
|
2255 |
),
|
2256 |
-
|
2257 |
),
|
2258 |
),
|
2259 |
'br' => array(
|
2260 |
array(
|
2261 |
'attr_spec_list' => array(),
|
2262 |
'tag_spec' => array(),
|
2263 |
-
|
2264 |
),
|
2265 |
),
|
2266 |
'button' => array(
|
@@ -2273,7 +2781,7 @@ class AMP_Allowed_Tags_Generated {
|
|
2273 |
'value' => '',
|
2274 |
),
|
2275 |
'name' => array(
|
2276 |
-
'blacklisted_value_regex' => '(^|\\s)(__amp_\\
|
2277 |
),
|
2278 |
'role' => array(),
|
2279 |
'tabindex' => array(),
|
@@ -2281,12 +2789,11 @@ class AMP_Allowed_Tags_Generated {
|
|
2281 |
'value' => array(),
|
2282 |
),
|
2283 |
'tag_spec' => array(),
|
2284 |
-
|
2285 |
),
|
2286 |
array(
|
2287 |
'attr_spec_list' => array(
|
2288 |
'name' => array(
|
2289 |
-
'blacklisted_value_regex' => '(^|\\s)(__amp_\\
|
2290 |
),
|
2291 |
'open-button' => array(
|
2292 |
'value' => '',
|
@@ -2297,32 +2804,21 @@ class AMP_Allowed_Tags_Generated {
|
|
2297 |
'value' => array(),
|
2298 |
),
|
2299 |
'tag_spec' => array(
|
2300 |
-
'html_format' => array(
|
2301 |
-
'amp',
|
2302 |
-
'amp4ads',
|
2303 |
-
),
|
2304 |
'mandatory_ancestor' => 'amp-app-banner',
|
2305 |
'spec_name' => 'amp-app-banner button[open-button]',
|
2306 |
),
|
2307 |
-
|
2308 |
),
|
2309 |
),
|
2310 |
'caption' => array(
|
2311 |
array(
|
2312 |
'attr_spec_list' => array(),
|
2313 |
'tag_spec' => array(),
|
2314 |
-
|
2315 |
),
|
2316 |
),
|
2317 |
'center' => array(
|
2318 |
array(
|
2319 |
'attr_spec_list' => array(),
|
2320 |
-
'tag_spec' => array(
|
2321 |
-
'html_format' => array(
|
2322 |
-
'amp',
|
2323 |
-
),
|
2324 |
-
),
|
2325 |
-
|
2326 |
),
|
2327 |
),
|
2328 |
'circle' => array(
|
@@ -2406,21 +2902,15 @@ class AMP_Allowed_Tags_Generated {
|
|
2406 |
'xmlns:xlink' => array(),
|
2407 |
),
|
2408 |
'tag_spec' => array(
|
2409 |
-
'html_format' => array(
|
2410 |
-
'amp',
|
2411 |
-
'amp4ads',
|
2412 |
-
),
|
2413 |
'mandatory_ancestor' => 'svg',
|
2414 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
2415 |
),
|
2416 |
-
|
2417 |
),
|
2418 |
),
|
2419 |
'cite' => array(
|
2420 |
array(
|
2421 |
'attr_spec_list' => array(),
|
2422 |
'tag_spec' => array(),
|
2423 |
-
|
2424 |
),
|
2425 |
),
|
2426 |
'clippath' => array(
|
@@ -2501,21 +2991,15 @@ class AMP_Allowed_Tags_Generated {
|
|
2501 |
'xmlns:xlink' => array(),
|
2502 |
),
|
2503 |
'tag_spec' => array(
|
2504 |
-
'html_format' => array(
|
2505 |
-
'amp',
|
2506 |
-
'amp4ads',
|
2507 |
-
),
|
2508 |
'mandatory_ancestor' => 'svg',
|
2509 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
2510 |
),
|
2511 |
-
|
2512 |
),
|
2513 |
),
|
2514 |
'code' => array(
|
2515 |
array(
|
2516 |
'attr_spec_list' => array(),
|
2517 |
'tag_spec' => array(),
|
2518 |
-
|
2519 |
),
|
2520 |
),
|
2521 |
'col' => array(
|
@@ -2524,7 +3008,6 @@ class AMP_Allowed_Tags_Generated {
|
|
2524 |
'span' => array(),
|
2525 |
),
|
2526 |
'tag_spec' => array(),
|
2527 |
-
|
2528 |
),
|
2529 |
),
|
2530 |
'colgroup' => array(
|
@@ -2533,14 +3016,12 @@ class AMP_Allowed_Tags_Generated {
|
|
2533 |
'span' => array(),
|
2534 |
),
|
2535 |
'tag_spec' => array(),
|
2536 |
-
|
2537 |
),
|
2538 |
),
|
2539 |
'data' => array(
|
2540 |
array(
|
2541 |
'attr_spec_list' => array(),
|
2542 |
'tag_spec' => array(),
|
2543 |
-
|
2544 |
),
|
2545 |
),
|
2546 |
'datalist' => array(
|
@@ -2549,14 +3030,12 @@ class AMP_Allowed_Tags_Generated {
|
|
2549 |
'tag_spec' => array(
|
2550 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
2551 |
),
|
2552 |
-
|
2553 |
),
|
2554 |
),
|
2555 |
'dd' => array(
|
2556 |
array(
|
2557 |
'attr_spec_list' => array(),
|
2558 |
'tag_spec' => array(),
|
2559 |
-
|
2560 |
),
|
2561 |
),
|
2562 |
'defs' => array(
|
@@ -2636,14 +3115,9 @@ class AMP_Allowed_Tags_Generated {
|
|
2636 |
'xmlns:xlink' => array(),
|
2637 |
),
|
2638 |
'tag_spec' => array(
|
2639 |
-
'html_format' => array(
|
2640 |
-
'amp',
|
2641 |
-
'amp4ads',
|
2642 |
-
),
|
2643 |
'mandatory_ancestor' => 'svg',
|
2644 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
2645 |
),
|
2646 |
-
|
2647 |
),
|
2648 |
),
|
2649 |
'del' => array(
|
@@ -2651,17 +3125,18 @@ class AMP_Allowed_Tags_Generated {
|
|
2651 |
'attr_spec_list' => array(
|
2652 |
'cite' => array(
|
2653 |
'blacklisted_value_regex' => '__amp_source_origin',
|
2654 |
-
'
|
2655 |
-
|
2656 |
-
|
2657 |
-
'
|
2658 |
-
|
|
|
|
|
2659 |
),
|
2660 |
),
|
2661 |
'datetime' => array(),
|
2662 |
),
|
2663 |
'tag_spec' => array(),
|
2664 |
-
|
2665 |
),
|
2666 |
),
|
2667 |
'desc' => array(
|
@@ -2676,32 +3151,21 @@ class AMP_Allowed_Tags_Generated {
|
|
2676 |
'xmlns:xlink' => array(),
|
2677 |
),
|
2678 |
'tag_spec' => array(
|
2679 |
-
'html_format' => array(
|
2680 |
-
'amp',
|
2681 |
-
'amp4ads',
|
2682 |
-
),
|
2683 |
'mandatory_ancestor' => 'svg',
|
2684 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
2685 |
),
|
2686 |
-
|
2687 |
),
|
2688 |
),
|
2689 |
'dfn' => array(
|
2690 |
array(
|
2691 |
'attr_spec_list' => array(),
|
2692 |
'tag_spec' => array(),
|
2693 |
-
|
2694 |
),
|
2695 |
),
|
2696 |
'dir' => array(
|
2697 |
array(
|
2698 |
'attr_spec_list' => array(),
|
2699 |
-
'tag_spec' => array(
|
2700 |
-
'html_format' => array(
|
2701 |
-
'amp',
|
2702 |
-
),
|
2703 |
-
),
|
2704 |
-
|
2705 |
),
|
2706 |
),
|
2707 |
'div' => array(
|
@@ -2710,20 +3174,19 @@ class AMP_Allowed_Tags_Generated {
|
|
2710 |
'align' => array(),
|
2711 |
),
|
2712 |
'tag_spec' => array(),
|
2713 |
-
|
2714 |
),
|
2715 |
array(
|
2716 |
'attr_spec_list' => array(
|
2717 |
'align' => array(),
|
2718 |
'submitting' => array(
|
|
|
2719 |
'mandatory' => true,
|
2720 |
),
|
2721 |
),
|
2722 |
'tag_spec' => array(
|
2723 |
'mandatory_parent' => 'form',
|
2724 |
-
'spec_name' => '
|
2725 |
),
|
2726 |
-
|
2727 |
),
|
2728 |
array(
|
2729 |
'attr_spec_list' => array(
|
@@ -2734,9 +3197,23 @@ class AMP_Allowed_Tags_Generated {
|
|
2734 |
),
|
2735 |
'tag_spec' => array(
|
2736 |
'mandatory_parent' => 'form',
|
2737 |
-
'spec_name' => '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2738 |
),
|
2739 |
-
|
2740 |
),
|
2741 |
array(
|
2742 |
'attr_spec_list' => array(
|
@@ -2747,23 +3224,35 @@ class AMP_Allowed_Tags_Generated {
|
|
2747 |
),
|
2748 |
'tag_spec' => array(
|
2749 |
'mandatory_parent' => 'form',
|
2750 |
-
'spec_name' => '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2751 |
),
|
2752 |
-
|
2753 |
),
|
2754 |
),
|
2755 |
'dl' => array(
|
2756 |
array(
|
2757 |
'attr_spec_list' => array(),
|
2758 |
'tag_spec' => array(),
|
2759 |
-
|
2760 |
),
|
2761 |
),
|
2762 |
'dt' => array(
|
2763 |
array(
|
2764 |
'attr_spec_list' => array(),
|
2765 |
'tag_spec' => array(),
|
2766 |
-
|
2767 |
),
|
2768 |
),
|
2769 |
'ellipse' => array(
|
@@ -2848,21 +3337,15 @@ class AMP_Allowed_Tags_Generated {
|
|
2848 |
'xmlns:xlink' => array(),
|
2849 |
),
|
2850 |
'tag_spec' => array(
|
2851 |
-
'html_format' => array(
|
2852 |
-
'amp',
|
2853 |
-
'amp4ads',
|
2854 |
-
),
|
2855 |
'mandatory_ancestor' => 'svg',
|
2856 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
2857 |
),
|
2858 |
-
|
2859 |
),
|
2860 |
),
|
2861 |
'em' => array(
|
2862 |
array(
|
2863 |
'attr_spec_list' => array(),
|
2864 |
'tag_spec' => array(),
|
2865 |
-
|
2866 |
),
|
2867 |
),
|
2868 |
'fecolormatrix' => array(
|
@@ -2945,14 +3428,9 @@ class AMP_Allowed_Tags_Generated {
|
|
2945 |
'y' => array(),
|
2946 |
),
|
2947 |
'tag_spec' => array(
|
2948 |
-
'html_format' => array(
|
2949 |
-
'amp',
|
2950 |
-
'amp4ads',
|
2951 |
-
),
|
2952 |
'mandatory_ancestor' => 'svg',
|
2953 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
2954 |
),
|
2955 |
-
|
2956 |
),
|
2957 |
),
|
2958 |
'fecomposite' => array(
|
@@ -3039,14 +3517,9 @@ class AMP_Allowed_Tags_Generated {
|
|
3039 |
'y' => array(),
|
3040 |
),
|
3041 |
'tag_spec' => array(
|
3042 |
-
'html_format' => array(
|
3043 |
-
'amp',
|
3044 |
-
'amp4ads',
|
3045 |
-
),
|
3046 |
'mandatory_ancestor' => 'svg',
|
3047 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3048 |
),
|
3049 |
-
|
3050 |
),
|
3051 |
),
|
3052 |
'feflood' => array(
|
@@ -3126,14 +3599,9 @@ class AMP_Allowed_Tags_Generated {
|
|
3126 |
'y' => array(),
|
3127 |
),
|
3128 |
'tag_spec' => array(
|
3129 |
-
'html_format' => array(
|
3130 |
-
'amp',
|
3131 |
-
'amp4ads',
|
3132 |
-
),
|
3133 |
'mandatory_ancestor' => 'svg',
|
3134 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3135 |
),
|
3136 |
-
|
3137 |
),
|
3138 |
),
|
3139 |
'fegaussianblur' => array(
|
@@ -3216,14 +3684,9 @@ class AMP_Allowed_Tags_Generated {
|
|
3216 |
'y' => array(),
|
3217 |
),
|
3218 |
'tag_spec' => array(
|
3219 |
-
'html_format' => array(
|
3220 |
-
'amp',
|
3221 |
-
'amp4ads',
|
3222 |
-
),
|
3223 |
'mandatory_ancestor' => 'svg',
|
3224 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3225 |
),
|
3226 |
-
|
3227 |
),
|
3228 |
),
|
3229 |
'femerge' => array(
|
@@ -3303,14 +3766,9 @@ class AMP_Allowed_Tags_Generated {
|
|
3303 |
'y' => array(),
|
3304 |
),
|
3305 |
'tag_spec' => array(
|
3306 |
-
'html_format' => array(
|
3307 |
-
'amp',
|
3308 |
-
'amp4ads',
|
3309 |
-
),
|
3310 |
'mandatory_ancestor' => 'svg',
|
3311 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3312 |
),
|
3313 |
-
|
3314 |
),
|
3315 |
),
|
3316 |
'femergenode' => array(
|
@@ -3326,14 +3784,9 @@ class AMP_Allowed_Tags_Generated {
|
|
3326 |
'xmlns:xlink' => array(),
|
3327 |
),
|
3328 |
'tag_spec' => array(
|
3329 |
-
'html_format' => array(
|
3330 |
-
'amp',
|
3331 |
-
'amp4ads',
|
3332 |
-
),
|
3333 |
'mandatory_ancestor' => 'svg',
|
3334 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3335 |
),
|
3336 |
-
|
3337 |
),
|
3338 |
),
|
3339 |
'feoffset' => array(
|
@@ -3416,14 +3869,9 @@ class AMP_Allowed_Tags_Generated {
|
|
3416 |
'y' => array(),
|
3417 |
),
|
3418 |
'tag_spec' => array(
|
3419 |
-
'html_format' => array(
|
3420 |
-
'amp',
|
3421 |
-
'amp4ads',
|
3422 |
-
),
|
3423 |
'mandatory_ancestor' => 'svg',
|
3424 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3425 |
),
|
3426 |
-
|
3427 |
),
|
3428 |
),
|
3429 |
'fieldset' => array(
|
@@ -3432,25 +3880,22 @@ class AMP_Allowed_Tags_Generated {
|
|
3432 |
'[disabled]' => array(),
|
3433 |
'disabled' => array(),
|
3434 |
'name' => array(
|
3435 |
-
'blacklisted_value_regex' => '(^|\\s)(__amp_\\
|
3436 |
),
|
3437 |
),
|
3438 |
'tag_spec' => array(),
|
3439 |
-
|
3440 |
),
|
3441 |
),
|
3442 |
'figcaption' => array(
|
3443 |
array(
|
3444 |
'attr_spec_list' => array(),
|
3445 |
'tag_spec' => array(),
|
3446 |
-
|
3447 |
),
|
3448 |
),
|
3449 |
'figure' => array(
|
3450 |
array(
|
3451 |
'attr_spec_list' => array(),
|
3452 |
'tag_spec' => array(),
|
3453 |
-
|
3454 |
),
|
3455 |
),
|
3456 |
'filter' => array(
|
@@ -3532,11 +3977,13 @@ class AMP_Allowed_Tags_Generated {
|
|
3532 |
'alternative_names' => array(
|
3533 |
'href',
|
3534 |
),
|
3535 |
-
'
|
3536 |
-
|
3537 |
-
|
3538 |
-
'
|
3539 |
-
|
|
|
|
|
3540 |
),
|
3541 |
),
|
3542 |
'xlink:role' => array(),
|
@@ -3550,21 +3997,15 @@ class AMP_Allowed_Tags_Generated {
|
|
3550 |
'y' => array(),
|
3551 |
),
|
3552 |
'tag_spec' => array(
|
3553 |
-
'html_format' => array(
|
3554 |
-
'amp',
|
3555 |
-
'amp4ads',
|
3556 |
-
),
|
3557 |
'mandatory_ancestor' => 'svg',
|
3558 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3559 |
),
|
3560 |
-
|
3561 |
),
|
3562 |
),
|
3563 |
'footer' => array(
|
3564 |
array(
|
3565 |
'attr_spec_list' => array(),
|
3566 |
'tag_spec' => array(),
|
3567 |
-
|
3568 |
),
|
3569 |
),
|
3570 |
'foreignobject' => array(
|
@@ -3648,14 +4089,9 @@ class AMP_Allowed_Tags_Generated {
|
|
3648 |
'y' => array(),
|
3649 |
),
|
3650 |
'tag_spec' => array(
|
3651 |
-
'html_format' => array(
|
3652 |
-
'amp',
|
3653 |
-
'amp4ads',
|
3654 |
-
),
|
3655 |
'mandatory_ancestor' => 'svg',
|
3656 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3657 |
),
|
3658 |
-
|
3659 |
),
|
3660 |
),
|
3661 |
'form' => array(
|
@@ -3666,16 +4102,20 @@ class AMP_Allowed_Tags_Generated {
|
|
3666 |
'action' => array(
|
3667 |
'blacklisted_value_regex' => '__amp_source_origin',
|
3668 |
'mandatory' => true,
|
3669 |
-
'
|
3670 |
-
|
3671 |
-
'
|
|
|
|
|
3672 |
),
|
3673 |
),
|
3674 |
'action-xhr' => array(
|
3675 |
'blacklisted_value_regex' => '__amp_source_origin',
|
3676 |
-
'
|
3677 |
-
|
3678 |
-
'
|
|
|
|
|
3679 |
),
|
3680 |
),
|
3681 |
'autocomplete' => array(),
|
@@ -3694,23 +4134,23 @@ class AMP_Allowed_Tags_Generated {
|
|
3694 |
),
|
3695 |
'verify-xhr' => array(
|
3696 |
'blacklisted_value_regex' => '__amp_source_origin',
|
3697 |
-
'
|
3698 |
-
|
3699 |
-
'
|
|
|
|
|
3700 |
),
|
3701 |
),
|
3702 |
),
|
3703 |
'tag_spec' => array(
|
3704 |
'disallowed_ancestor' => array(
|
3705 |
-
|
3706 |
),
|
3707 |
-
'
|
3708 |
-
|
3709 |
-
'amp4ads',
|
3710 |
),
|
3711 |
-
'spec_name' => '
|
3712 |
),
|
3713 |
-
|
3714 |
),
|
3715 |
array(
|
3716 |
'attr_spec_list' => array(
|
@@ -3719,9 +4159,11 @@ class AMP_Allowed_Tags_Generated {
|
|
3719 |
'action-xhr' => array(
|
3720 |
'blacklisted_value_regex' => '__amp_source_origin',
|
3721 |
'mandatory' => true,
|
3722 |
-
'
|
3723 |
-
|
3724 |
-
'
|
|
|
|
|
3725 |
),
|
3726 |
),
|
3727 |
'autocomplete' => array(),
|
@@ -3730,6 +4172,7 @@ class AMP_Allowed_Tags_Generated {
|
|
3730 |
),
|
3731 |
'enctype' => array(),
|
3732 |
'method' => array(
|
|
|
3733 |
'mandatory' => true,
|
3734 |
'value_casei' => 'post',
|
3735 |
),
|
@@ -3741,23 +4184,23 @@ class AMP_Allowed_Tags_Generated {
|
|
3741 |
),
|
3742 |
'verify-xhr' => array(
|
3743 |
'blacklisted_value_regex' => '__amp_source_origin',
|
3744 |
-
'
|
3745 |
-
|
3746 |
-
'
|
|
|
|
|
3747 |
),
|
3748 |
),
|
3749 |
),
|
3750 |
'tag_spec' => array(
|
3751 |
'disallowed_ancestor' => array(
|
3752 |
-
|
3753 |
),
|
3754 |
-
'
|
3755 |
-
|
3756 |
-
'amp4ads',
|
3757 |
),
|
3758 |
-
'spec_name' => '
|
3759 |
),
|
3760 |
-
|
3761 |
),
|
3762 |
),
|
3763 |
'g' => array(
|
@@ -3837,14 +4280,9 @@ class AMP_Allowed_Tags_Generated {
|
|
3837 |
'xmlns:xlink' => array(),
|
3838 |
),
|
3839 |
'tag_spec' => array(
|
3840 |
-
'html_format' => array(
|
3841 |
-
'amp',
|
3842 |
-
'amp4ads',
|
3843 |
-
),
|
3844 |
'mandatory_ancestor' => 'svg',
|
3845 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3846 |
),
|
3847 |
-
|
3848 |
),
|
3849 |
),
|
3850 |
'glyph' => array(
|
@@ -3928,14 +4366,9 @@ class AMP_Allowed_Tags_Generated {
|
|
3928 |
'xmlns:xlink' => array(),
|
3929 |
),
|
3930 |
'tag_spec' => array(
|
3931 |
-
'html_format' => array(
|
3932 |
-
'amp',
|
3933 |
-
'amp4ads',
|
3934 |
-
),
|
3935 |
'mandatory_ancestor' => 'svg',
|
3936 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3937 |
),
|
3938 |
-
|
3939 |
),
|
3940 |
),
|
3941 |
'glyphref' => array(
|
@@ -4015,11 +4448,13 @@ class AMP_Allowed_Tags_Generated {
|
|
4015 |
'alternative_names' => array(
|
4016 |
'href',
|
4017 |
),
|
4018 |
-
'
|
4019 |
-
|
4020 |
-
|
4021 |
-
'
|
4022 |
-
|
|
|
|
|
4023 |
),
|
4024 |
),
|
4025 |
'xlink:role' => array(),
|
@@ -4033,14 +4468,9 @@ class AMP_Allowed_Tags_Generated {
|
|
4033 |
'y' => array(),
|
4034 |
),
|
4035 |
'tag_spec' => array(
|
4036 |
-
'html_format' => array(
|
4037 |
-
'amp',
|
4038 |
-
'amp4ads',
|
4039 |
-
),
|
4040 |
'mandatory_ancestor' => 'svg',
|
4041 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4042 |
),
|
4043 |
-
|
4044 |
),
|
4045 |
),
|
4046 |
'h1' => array(
|
@@ -4049,7 +4479,6 @@ class AMP_Allowed_Tags_Generated {
|
|
4049 |
'align' => array(),
|
4050 |
),
|
4051 |
'tag_spec' => array(),
|
4052 |
-
|
4053 |
),
|
4054 |
),
|
4055 |
'h2' => array(
|
@@ -4058,7 +4487,6 @@ class AMP_Allowed_Tags_Generated {
|
|
4058 |
'align' => array(),
|
4059 |
),
|
4060 |
'tag_spec' => array(),
|
4061 |
-
|
4062 |
),
|
4063 |
),
|
4064 |
'h3' => array(
|
@@ -4067,7 +4495,6 @@ class AMP_Allowed_Tags_Generated {
|
|
4067 |
'align' => array(),
|
4068 |
),
|
4069 |
'tag_spec' => array(),
|
4070 |
-
|
4071 |
),
|
4072 |
),
|
4073 |
'h4' => array(
|
@@ -4076,7 +4503,6 @@ class AMP_Allowed_Tags_Generated {
|
|
4076 |
'align' => array(),
|
4077 |
),
|
4078 |
'tag_spec' => array(),
|
4079 |
-
|
4080 |
),
|
4081 |
),
|
4082 |
'h5' => array(
|
@@ -4085,7 +4511,6 @@ class AMP_Allowed_Tags_Generated {
|
|
4085 |
'align' => array(),
|
4086 |
),
|
4087 |
'tag_spec' => array(),
|
4088 |
-
|
4089 |
),
|
4090 |
),
|
4091 |
'h6' => array(
|
@@ -4094,25 +4519,29 @@ class AMP_Allowed_Tags_Generated {
|
|
4094 |
'align' => array(),
|
4095 |
),
|
4096 |
'tag_spec' => array(),
|
4097 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4098 |
),
|
4099 |
),
|
4100 |
'header' => array(
|
4101 |
array(
|
4102 |
'attr_spec_list' => array(),
|
4103 |
'tag_spec' => array(),
|
4104 |
-
|
4105 |
),
|
4106 |
),
|
4107 |
'hgroup' => array(
|
4108 |
array(
|
4109 |
'attr_spec_list' => array(),
|
4110 |
-
'tag_spec' => array(
|
4111 |
-
'html_format' => array(
|
4112 |
-
'amp',
|
4113 |
-
),
|
4114 |
-
),
|
4115 |
-
|
4116 |
),
|
4117 |
),
|
4118 |
'hkern' => array(
|
@@ -4132,28 +4561,41 @@ class AMP_Allowed_Tags_Generated {
|
|
4132 |
'xmlns:xlink' => array(),
|
4133 |
),
|
4134 |
'tag_spec' => array(
|
4135 |
-
'html_format' => array(
|
4136 |
-
'amp',
|
4137 |
-
'amp4ads',
|
4138 |
-
),
|
4139 |
'mandatory_ancestor' => 'svg',
|
4140 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4141 |
),
|
4142 |
-
|
4143 |
),
|
4144 |
),
|
4145 |
'hr' => array(
|
4146 |
array(
|
4147 |
'attr_spec_list' => array(),
|
4148 |
'tag_spec' => array(),
|
4149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4150 |
),
|
4151 |
),
|
4152 |
'i' => array(
|
4153 |
array(
|
4154 |
'attr_spec_list' => array(),
|
4155 |
'tag_spec' => array(),
|
4156 |
-
|
4157 |
),
|
4158 |
),
|
4159 |
'iframe' => array(
|
@@ -4174,24 +4616,22 @@ class AMP_Allowed_Tags_Generated {
|
|
4174 |
),
|
4175 |
'src' => array(
|
4176 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4177 |
-
'
|
4178 |
-
|
4179 |
-
'
|
4180 |
-
|
|
|
|
|
4181 |
),
|
4182 |
),
|
4183 |
'srcdoc' => array(),
|
4184 |
'width' => array(),
|
4185 |
),
|
4186 |
'tag_spec' => array(
|
4187 |
-
'html_format' => array(
|
4188 |
-
'amp',
|
4189 |
-
),
|
4190 |
'mandatory_ancestor' => 'noscript',
|
4191 |
'mandatory_ancestor_suggested_alternative' => 'amp-iframe',
|
4192 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-iframe',
|
4193 |
),
|
4194 |
-
|
4195 |
),
|
4196 |
),
|
4197 |
'image' => array(
|
@@ -4276,12 +4716,14 @@ class AMP_Allowed_Tags_Generated {
|
|
4276 |
'href',
|
4277 |
),
|
4278 |
'blacklisted_value_regex' => '(^|\\s)data:image\\/svg\\+xml',
|
4279 |
-
'
|
4280 |
-
|
4281 |
-
|
4282 |
-
'
|
4283 |
-
|
4284 |
-
|
|
|
|
|
4285 |
),
|
4286 |
),
|
4287 |
'xlink:role' => array(),
|
@@ -4295,14 +4737,9 @@ class AMP_Allowed_Tags_Generated {
|
|
4295 |
'y' => array(),
|
4296 |
),
|
4297 |
'tag_spec' => array(
|
4298 |
-
'html_format' => array(
|
4299 |
-
'amp',
|
4300 |
-
'amp4ads',
|
4301 |
-
),
|
4302 |
'mandatory_ancestor' => 'svg',
|
4303 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4304 |
),
|
4305 |
-
|
4306 |
),
|
4307 |
),
|
4308 |
'img' => array(
|
@@ -4314,10 +4751,12 @@ class AMP_Allowed_Tags_Generated {
|
|
4314 |
'ismap' => array(),
|
4315 |
'longdesc' => array(
|
4316 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4317 |
-
'
|
4318 |
-
|
4319 |
-
'
|
4320 |
-
|
|
|
|
|
4321 |
),
|
4322 |
),
|
4323 |
'src' => array(
|
@@ -4326,23 +4765,21 @@ class AMP_Allowed_Tags_Generated {
|
|
4326 |
),
|
4327 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4328 |
'mandatory' => true,
|
4329 |
-
'
|
4330 |
-
|
4331 |
-
'
|
4332 |
-
|
|
|
|
|
4333 |
),
|
4334 |
),
|
4335 |
'width' => array(),
|
4336 |
),
|
4337 |
'tag_spec' => array(
|
4338 |
-
'html_format' => array(
|
4339 |
-
'amp',
|
4340 |
-
),
|
4341 |
'mandatory_ancestor' => 'noscript',
|
4342 |
'mandatory_ancestor_suggested_alternative' => 'amp-img',
|
4343 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-img',
|
4344 |
),
|
4345 |
-
|
4346 |
),
|
4347 |
),
|
4348 |
'input' => array(
|
@@ -4386,7 +4823,7 @@ class AMP_Allowed_Tags_Generated {
|
|
4386 |
'minlength' => array(),
|
4387 |
'multiple' => array(),
|
4388 |
'name' => array(
|
4389 |
-
'blacklisted_value_regex' => '(^|\\s)(__amp_\\
|
4390 |
),
|
4391 |
'pattern' => array(),
|
4392 |
'placeholder' => array(),
|
@@ -4406,7 +4843,6 @@ class AMP_Allowed_Tags_Generated {
|
|
4406 |
'tag_spec' => array(
|
4407 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
4408 |
),
|
4409 |
-
|
4410 |
),
|
4411 |
),
|
4412 |
'ins' => array(
|
@@ -4414,24 +4850,24 @@ class AMP_Allowed_Tags_Generated {
|
|
4414 |
'attr_spec_list' => array(
|
4415 |
'cite' => array(
|
4416 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4417 |
-
'
|
4418 |
-
|
4419 |
-
|
4420 |
-
'
|
4421 |
-
|
|
|
|
|
4422 |
),
|
4423 |
),
|
4424 |
'datetime' => array(),
|
4425 |
),
|
4426 |
'tag_spec' => array(),
|
4427 |
-
|
4428 |
),
|
4429 |
),
|
4430 |
'kbd' => array(
|
4431 |
array(
|
4432 |
'attr_spec_list' => array(),
|
4433 |
'tag_spec' => array(),
|
4434 |
-
|
4435 |
),
|
4436 |
),
|
4437 |
'label' => array(
|
@@ -4442,14 +4878,12 @@ class AMP_Allowed_Tags_Generated {
|
|
4442 |
'tag_spec' => array(
|
4443 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
4444 |
),
|
4445 |
-
|
4446 |
),
|
4447 |
),
|
4448 |
'legend' => array(
|
4449 |
array(
|
4450 |
'attr_spec_list' => array(),
|
4451 |
'tag_spec' => array(),
|
4452 |
-
|
4453 |
),
|
4454 |
),
|
4455 |
'li' => array(
|
@@ -4460,7 +4894,6 @@ class AMP_Allowed_Tags_Generated {
|
|
4460 |
),
|
4461 |
),
|
4462 |
'tag_spec' => array(),
|
4463 |
-
|
4464 |
),
|
4465 |
),
|
4466 |
'line' => array(
|
@@ -4545,14 +4978,9 @@ class AMP_Allowed_Tags_Generated {
|
|
4545 |
'y2' => array(),
|
4546 |
),
|
4547 |
'tag_spec' => array(
|
4548 |
-
'html_format' => array(
|
4549 |
-
'amp',
|
4550 |
-
'amp4ads',
|
4551 |
-
),
|
4552 |
'mandatory_ancestor' => 'svg',
|
4553 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4554 |
),
|
4555 |
-
|
4556 |
),
|
4557 |
),
|
4558 |
'lineargradient' => array(
|
@@ -4633,11 +5061,13 @@ class AMP_Allowed_Tags_Generated {
|
|
4633 |
'alternative_names' => array(
|
4634 |
'href',
|
4635 |
),
|
4636 |
-
'
|
4637 |
-
|
4638 |
-
|
4639 |
-
'
|
4640 |
-
|
|
|
|
|
4641 |
),
|
4642 |
),
|
4643 |
'xlink:role' => array(),
|
@@ -4652,14 +5082,9 @@ class AMP_Allowed_Tags_Generated {
|
|
4652 |
'y2' => array(),
|
4653 |
),
|
4654 |
'tag_spec' => array(
|
4655 |
-
'html_format' => array(
|
4656 |
-
'amp',
|
4657 |
-
'amp4ads',
|
4658 |
-
),
|
4659 |
'mandatory_ancestor' => 'svg',
|
4660 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4661 |
),
|
4662 |
-
|
4663 |
),
|
4664 |
),
|
4665 |
'link' => array(
|
@@ -4669,11 +5094,12 @@ class AMP_Allowed_Tags_Generated {
|
|
4669 |
'value_casei' => 'utf-8',
|
4670 |
),
|
4671 |
'color' => array(),
|
|
|
4672 |
'href' => array(),
|
4673 |
'hreflang' => array(),
|
4674 |
'media' => array(),
|
4675 |
'rel' => array(
|
4676 |
-
'blacklisted_value_regex' => '(^|\\s)(canonical|components|import|manifest|serviceworker|stylesheet|subresource|)(\\s|$)',
|
4677 |
'mandatory' => true,
|
4678 |
),
|
4679 |
'sizes' => array(),
|
@@ -4682,16 +5108,11 @@ class AMP_Allowed_Tags_Generated {
|
|
4682 |
),
|
4683 |
'tag_spec' => array(
|
4684 |
'disallowed_ancestor' => array(
|
4685 |
-
|
4686 |
-
),
|
4687 |
-
'html_format' => array(
|
4688 |
-
'amp',
|
4689 |
-
'amp4ads',
|
4690 |
),
|
4691 |
'spec_name' => 'link rel=',
|
4692 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
4693 |
),
|
4694 |
-
|
4695 |
),
|
4696 |
array(
|
4697 |
'attr_spec_list' => array(
|
@@ -4699,28 +5120,123 @@ class AMP_Allowed_Tags_Generated {
|
|
4699 |
'value_casei' => 'utf-8',
|
4700 |
),
|
4701 |
'color' => array(),
|
|
|
4702 |
'href' => array(
|
|
|
4703 |
'mandatory' => true,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4704 |
),
|
4705 |
'hreflang' => array(),
|
4706 |
-
'
|
|
|
|
|
4707 |
'mandatory' => true,
|
4708 |
-
'value_casei' => '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4709 |
),
|
|
|
4710 |
'media' => array(),
|
|
|
|
|
|
|
|
|
|
|
4711 |
'sizes' => array(),
|
4712 |
'target' => array(),
|
4713 |
'type' => array(),
|
4714 |
),
|
4715 |
'tag_spec' => array(
|
4716 |
-
'
|
4717 |
-
|
4718 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4719 |
),
|
4720 |
-
'spec_name' => 'link
|
4721 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
4722 |
),
|
4723 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4724 |
),
|
4725 |
array(
|
4726 |
'attr_spec_list' => array(
|
@@ -4728,12 +5244,15 @@ class AMP_Allowed_Tags_Generated {
|
|
4728 |
'value_casei' => 'utf-8',
|
4729 |
),
|
4730 |
'color' => array(),
|
|
|
4731 |
'href' => array(
|
4732 |
'mandatory' => true,
|
4733 |
),
|
4734 |
'hreflang' => array(),
|
4735 |
'itemprop' => array(
|
|
|
4736 |
'mandatory' => true,
|
|
|
4737 |
),
|
4738 |
'media' => array(),
|
4739 |
'sizes' => array(),
|
@@ -4741,14 +5260,33 @@ class AMP_Allowed_Tags_Generated {
|
|
4741 |
'type' => array(),
|
4742 |
),
|
4743 |
'tag_spec' => array(
|
4744 |
-
'
|
4745 |
-
|
4746 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4747 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
4748 |
'spec_name' => 'link itemprop=',
|
4749 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
4750 |
),
|
4751 |
-
|
4752 |
),
|
4753 |
array(
|
4754 |
'attr_spec_list' => array(
|
@@ -4756,6 +5294,7 @@ class AMP_Allowed_Tags_Generated {
|
|
4756 |
'value_casei' => 'utf-8',
|
4757 |
),
|
4758 |
'color' => array(),
|
|
|
4759 |
'href' => array(
|
4760 |
'mandatory' => true,
|
4761 |
),
|
@@ -4769,39 +5308,27 @@ class AMP_Allowed_Tags_Generated {
|
|
4769 |
'type' => array(),
|
4770 |
),
|
4771 |
'tag_spec' => array(
|
4772 |
-
'html_format' => array(
|
4773 |
-
'amp',
|
4774 |
-
'amp4ads',
|
4775 |
-
),
|
4776 |
'spec_name' => 'link property=',
|
4777 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
4778 |
),
|
4779 |
-
|
4780 |
),
|
4781 |
),
|
4782 |
'listing' => array(
|
4783 |
array(
|
4784 |
'attr_spec_list' => array(),
|
4785 |
-
'tag_spec' => array(
|
4786 |
-
'html_format' => array(
|
4787 |
-
'amp',
|
4788 |
-
),
|
4789 |
-
),
|
4790 |
-
|
4791 |
),
|
4792 |
),
|
4793 |
'main' => array(
|
4794 |
array(
|
4795 |
'attr_spec_list' => array(),
|
4796 |
'tag_spec' => array(),
|
4797 |
-
|
4798 |
),
|
4799 |
),
|
4800 |
'mark' => array(
|
4801 |
array(
|
4802 |
'attr_spec_list' => array(),
|
4803 |
'tag_spec' => array(),
|
4804 |
-
|
4805 |
),
|
4806 |
),
|
4807 |
'marker' => array(
|
@@ -4886,14 +5413,9 @@ class AMP_Allowed_Tags_Generated {
|
|
4886 |
'xmlns:xlink' => array(),
|
4887 |
),
|
4888 |
'tag_spec' => array(
|
4889 |
-
'html_format' => array(
|
4890 |
-
'amp',
|
4891 |
-
'amp4ads',
|
4892 |
-
),
|
4893 |
'mandatory_ancestor' => 'svg',
|
4894 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4895 |
),
|
4896 |
-
|
4897 |
),
|
4898 |
),
|
4899 |
'mask' => array(
|
@@ -4978,61 +5500,218 @@ class AMP_Allowed_Tags_Generated {
|
|
4978 |
'y' => array(),
|
4979 |
),
|
4980 |
'tag_spec' => array(
|
4981 |
-
'html_format' => array(
|
4982 |
-
'amp',
|
4983 |
-
'amp4ads',
|
4984 |
-
),
|
4985 |
'mandatory_ancestor' => 'svg',
|
4986 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4987 |
),
|
4988 |
-
|
4989 |
),
|
4990 |
),
|
4991 |
'meta' => array(
|
4992 |
array(
|
4993 |
'attr_spec_list' => array(
|
4994 |
-
'
|
4995 |
-
'
|
4996 |
-
'chrome' => array(
|
4997 |
-
'value',
|
4998 |
-
),
|
4999 |
-
'ie' => array(
|
5000 |
-
'value',
|
5001 |
-
),
|
5002 |
-
),
|
5003 |
-
'http-equiv' => array(
|
5004 |
'mandatory' => true,
|
5005 |
-
'value_casei' => '
|
5006 |
),
|
5007 |
),
|
5008 |
'tag_spec' => array(
|
5009 |
-
'
|
5010 |
-
|
5011 |
-
|
5012 |
-
|
5013 |
-
'
|
5014 |
-
'spec_name' => 'meta http-equiv=x-ua-compatible',
|
5015 |
-
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5016 |
),
|
5017 |
-
|
5018 |
),
|
5019 |
array(
|
5020 |
'attr_spec_list' => array(
|
5021 |
-
'content' => array(
|
5022 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5023 |
'name' => array(
|
5024 |
-
'
|
|
|
|
|
5025 |
),
|
5026 |
-
'property' => array(),
|
5027 |
),
|
5028 |
'tag_spec' => array(
|
5029 |
-
'
|
5030 |
-
|
5031 |
-
|
5032 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5033 |
'spec_name' => 'meta name= and content=',
|
5034 |
),
|
5035 |
-
|
5036 |
),
|
5037 |
array(
|
5038 |
'attr_spec_list' => array(
|
@@ -5041,20 +5720,16 @@ class AMP_Allowed_Tags_Generated {
|
|
5041 |
'value_casei' => 'text/html; charset=utf-8',
|
5042 |
),
|
5043 |
'http-equiv' => array(
|
|
|
5044 |
'mandatory' => true,
|
5045 |
'value_casei' => 'content-type',
|
5046 |
),
|
5047 |
),
|
5048 |
'tag_spec' => array(
|
5049 |
-
'html_format' => array(
|
5050 |
-
'amp',
|
5051 |
-
'amp4ads',
|
5052 |
-
),
|
5053 |
'mandatory_ancestor' => 'head',
|
5054 |
-
'spec_name' => 'meta http-equiv=
|
5055 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5056 |
),
|
5057 |
-
|
5058 |
),
|
5059 |
array(
|
5060 |
'attr_spec_list' => array(
|
@@ -5062,20 +5737,16 @@ class AMP_Allowed_Tags_Generated {
|
|
5062 |
'mandatory' => true,
|
5063 |
),
|
5064 |
'http-equiv' => array(
|
|
|
5065 |
'mandatory' => true,
|
5066 |
'value_casei' => 'content-language',
|
5067 |
),
|
5068 |
),
|
5069 |
'tag_spec' => array(
|
5070 |
-
'html_format' => array(
|
5071 |
-
'amp',
|
5072 |
-
'amp4ads',
|
5073 |
-
),
|
5074 |
'mandatory_ancestor' => 'head',
|
5075 |
'spec_name' => 'meta http-equiv=content-language',
|
5076 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5077 |
),
|
5078 |
-
|
5079 |
),
|
5080 |
array(
|
5081 |
'attr_spec_list' => array(
|
@@ -5083,20 +5754,16 @@ class AMP_Allowed_Tags_Generated {
|
|
5083 |
'mandatory' => true,
|
5084 |
),
|
5085 |
'http-equiv' => array(
|
|
|
5086 |
'mandatory' => true,
|
5087 |
'value_casei' => 'pics-label',
|
5088 |
),
|
5089 |
),
|
5090 |
'tag_spec' => array(
|
5091 |
-
'html_format' => array(
|
5092 |
-
'amp',
|
5093 |
-
'amp4ads',
|
5094 |
-
),
|
5095 |
'mandatory_ancestor' => 'head',
|
5096 |
'spec_name' => 'meta http-equiv=pics-label',
|
5097 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5098 |
),
|
5099 |
-
|
5100 |
),
|
5101 |
array(
|
5102 |
'attr_spec_list' => array(
|
@@ -5104,20 +5771,16 @@ class AMP_Allowed_Tags_Generated {
|
|
5104 |
'mandatory' => true,
|
5105 |
),
|
5106 |
'http-equiv' => array(
|
|
|
5107 |
'mandatory' => true,
|
5108 |
'value_casei' => 'imagetoolbar',
|
5109 |
),
|
5110 |
),
|
5111 |
'tag_spec' => array(
|
5112 |
-
'html_format' => array(
|
5113 |
-
'amp',
|
5114 |
-
'amp4ads',
|
5115 |
-
),
|
5116 |
'mandatory_ancestor' => 'head',
|
5117 |
'spec_name' => 'meta http-equiv=imagetoolbar',
|
5118 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5119 |
),
|
5120 |
-
|
5121 |
),
|
5122 |
array(
|
5123 |
'attr_spec_list' => array(
|
@@ -5126,20 +5789,16 @@ class AMP_Allowed_Tags_Generated {
|
|
5126 |
'value_casei' => 'text/css',
|
5127 |
),
|
5128 |
'http-equiv' => array(
|
|
|
5129 |
'mandatory' => true,
|
5130 |
'value_casei' => 'content-style-type',
|
5131 |
),
|
5132 |
),
|
5133 |
'tag_spec' => array(
|
5134 |
-
'html_format' => array(
|
5135 |
-
'amp',
|
5136 |
-
'amp4ads',
|
5137 |
-
),
|
5138 |
'mandatory_ancestor' => 'head',
|
5139 |
-
'spec_name' => 'meta http-equiv=
|
5140 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5141 |
),
|
5142 |
-
|
5143 |
),
|
5144 |
array(
|
5145 |
'attr_spec_list' => array(
|
@@ -5148,20 +5807,16 @@ class AMP_Allowed_Tags_Generated {
|
|
5148 |
'value_casei' => 'text/javascript',
|
5149 |
),
|
5150 |
'http-equiv' => array(
|
|
|
5151 |
'mandatory' => true,
|
5152 |
'value_casei' => 'content-script-type',
|
5153 |
),
|
5154 |
),
|
5155 |
'tag_spec' => array(
|
5156 |
-
'html_format' => array(
|
5157 |
-
'amp',
|
5158 |
-
'amp4ads',
|
5159 |
-
),
|
5160 |
'mandatory_ancestor' => 'head',
|
5161 |
-
'spec_name' => 'meta http-equiv=
|
5162 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5163 |
),
|
5164 |
-
|
5165 |
),
|
5166 |
array(
|
5167 |
'attr_spec_list' => array(
|
@@ -5169,20 +5824,16 @@ class AMP_Allowed_Tags_Generated {
|
|
5169 |
'mandatory' => true,
|
5170 |
),
|
5171 |
'http-equiv' => array(
|
|
|
5172 |
'mandatory' => true,
|
5173 |
'value_casei' => 'origin-trial',
|
5174 |
),
|
5175 |
),
|
5176 |
'tag_spec' => array(
|
5177 |
-
'html_format' => array(
|
5178 |
-
'amp',
|
5179 |
-
'amp4ads',
|
5180 |
-
),
|
5181 |
'mandatory_ancestor' => 'head',
|
5182 |
'spec_name' => 'meta http-equiv=origin-trial',
|
5183 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5184 |
),
|
5185 |
-
|
5186 |
),
|
5187 |
array(
|
5188 |
'attr_spec_list' => array(
|
@@ -5190,20 +5841,67 @@ class AMP_Allowed_Tags_Generated {
|
|
5190 |
'mandatory' => true,
|
5191 |
),
|
5192 |
'http-equiv' => array(
|
|
|
5193 |
'mandatory' => true,
|
5194 |
'value_casei' => 'resource-type',
|
5195 |
),
|
5196 |
),
|
5197 |
'tag_spec' => array(
|
5198 |
-
'html_format' => array(
|
5199 |
-
'amp',
|
5200 |
-
'amp4ads',
|
5201 |
-
),
|
5202 |
'mandatory_ancestor' => 'head',
|
5203 |
'spec_name' => 'meta http-equiv=resource-type',
|
5204 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5205 |
),
|
5206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5207 |
),
|
5208 |
),
|
5209 |
'metadata' => array(
|
@@ -5218,14 +5916,9 @@ class AMP_Allowed_Tags_Generated {
|
|
5218 |
'xmlns:xlink' => array(),
|
5219 |
),
|
5220 |
'tag_spec' => array(
|
5221 |
-
'html_format' => array(
|
5222 |
-
'amp',
|
5223 |
-
'amp4ads',
|
5224 |
-
),
|
5225 |
'mandatory_ancestor' => 'svg',
|
5226 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
5227 |
),
|
5228 |
-
|
5229 |
),
|
5230 |
),
|
5231 |
'meter' => array(
|
@@ -5239,29 +5932,23 @@ class AMP_Allowed_Tags_Generated {
|
|
5239 |
'value' => array(),
|
5240 |
),
|
5241 |
'tag_spec' => array(),
|
5242 |
-
|
5243 |
),
|
5244 |
),
|
5245 |
'multicol' => array(
|
5246 |
array(
|
5247 |
'attr_spec_list' => array(),
|
5248 |
-
'tag_spec' => array(
|
5249 |
-
'html_format' => array(
|
5250 |
-
'amp',
|
5251 |
-
),
|
5252 |
-
),
|
5253 |
-
|
5254 |
),
|
5255 |
),
|
5256 |
'nav' => array(
|
5257 |
array(
|
5258 |
'attr_spec_list' => array(),
|
5259 |
'tag_spec' => array(),
|
5260 |
-
|
5261 |
),
|
5262 |
array(
|
5263 |
'attr_spec_list' => array(
|
5264 |
'toolbar' => array(
|
|
|
5265 |
'mandatory' => true,
|
5266 |
),
|
5267 |
'toolbar-target' => array(
|
@@ -5269,61 +5956,48 @@ class AMP_Allowed_Tags_Generated {
|
|
5269 |
),
|
5270 |
),
|
5271 |
'tag_spec' => array(
|
5272 |
-
'html_format' => array(
|
5273 |
-
'amp',
|
5274 |
-
),
|
5275 |
'mandatory_parent' => 'amp-sidebar',
|
5276 |
'spec_name' => 'amp-sidebar > nav',
|
5277 |
),
|
5278 |
-
|
5279 |
),
|
5280 |
),
|
5281 |
'nextid' => array(
|
5282 |
array(
|
5283 |
'attr_spec_list' => array(),
|
5284 |
-
'tag_spec' => array(
|
5285 |
-
'html_format' => array(
|
5286 |
-
'amp',
|
5287 |
-
),
|
5288 |
-
),
|
5289 |
-
|
5290 |
),
|
5291 |
),
|
5292 |
'nobr' => array(
|
5293 |
array(
|
5294 |
'attr_spec_list' => array(),
|
5295 |
-
'tag_spec' => array(
|
5296 |
-
'html_format' => array(
|
5297 |
-
'amp',
|
5298 |
-
),
|
5299 |
-
),
|
5300 |
-
|
5301 |
),
|
5302 |
),
|
5303 |
'noscript' => array(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5304 |
array(
|
5305 |
'attr_spec_list' => array(),
|
5306 |
'tag_spec' => array(
|
5307 |
'disallowed_ancestor' => array(
|
5308 |
-
|
5309 |
-
),
|
5310 |
-
'html_format' => array(
|
5311 |
-
'amp',
|
5312 |
),
|
5313 |
'mandatory_ancestor' => 'body',
|
5314 |
),
|
5315 |
-
|
5316 |
),
|
5317 |
),
|
5318 |
'o:p' => array(
|
5319 |
array(
|
5320 |
'attr_spec_list' => array(),
|
5321 |
-
'tag_spec' => array(
|
5322 |
-
'html_format' => array(
|
5323 |
-
'amp',
|
5324 |
-
),
|
5325 |
-
),
|
5326 |
-
|
5327 |
),
|
5328 |
),
|
5329 |
'ol' => array(
|
@@ -5336,11 +6010,10 @@ class AMP_Allowed_Tags_Generated {
|
|
5336 |
'value_regex' => '[0-9]*',
|
5337 |
),
|
5338 |
'type' => array(
|
5339 |
-
'value_regex' => '[
|
5340 |
),
|
5341 |
),
|
5342 |
'tag_spec' => array(),
|
5343 |
-
|
5344 |
),
|
5345 |
),
|
5346 |
'optgroup' => array(
|
@@ -5355,7 +6028,6 @@ class AMP_Allowed_Tags_Generated {
|
|
5355 |
'mandatory_parent' => 'select',
|
5356 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
5357 |
),
|
5358 |
-
|
5359 |
),
|
5360 |
),
|
5361 |
'option' => array(
|
@@ -5373,7 +6045,6 @@ class AMP_Allowed_Tags_Generated {
|
|
5373 |
'tag_spec' => array(
|
5374 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
5375 |
),
|
5376 |
-
|
5377 |
),
|
5378 |
),
|
5379 |
'output' => array(
|
@@ -5382,11 +6053,10 @@ class AMP_Allowed_Tags_Generated {
|
|
5382 |
'for' => array(),
|
5383 |
'form' => array(),
|
5384 |
'name' => array(
|
5385 |
-
'blacklisted_value_regex' => '(^|\\s)(__amp_\\
|
5386 |
),
|
5387 |
),
|
5388 |
'tag_spec' => array(),
|
5389 |
-
|
5390 |
),
|
5391 |
),
|
5392 |
'p' => array(
|
@@ -5395,7 +6065,6 @@ class AMP_Allowed_Tags_Generated {
|
|
5395 |
'align' => array(),
|
5396 |
),
|
5397 |
'tag_spec' => array(),
|
5398 |
-
|
5399 |
),
|
5400 |
),
|
5401 |
'path' => array(
|
@@ -5478,14 +6147,9 @@ class AMP_Allowed_Tags_Generated {
|
|
5478 |
'xmlns:xlink' => array(),
|
5479 |
),
|
5480 |
'tag_spec' => array(
|
5481 |
-
'html_format' => array(
|
5482 |
-
'amp',
|
5483 |
-
'amp4ads',
|
5484 |
-
),
|
5485 |
'mandatory_ancestor' => 'svg',
|
5486 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
5487 |
),
|
5488 |
-
|
5489 |
),
|
5490 |
),
|
5491 |
'pattern' => array(
|
@@ -5572,11 +6236,13 @@ class AMP_Allowed_Tags_Generated {
|
|
5572 |
'alternative_names' => array(
|
5573 |
'href',
|
5574 |
),
|
5575 |
-
'
|
5576 |
-
|
5577 |
-
|
5578 |
-
'
|
5579 |
-
|
|
|
|
|
5580 |
),
|
5581 |
),
|
5582 |
'xlink:role' => array(),
|
@@ -5590,14 +6256,9 @@ class AMP_Allowed_Tags_Generated {
|
|
5590 |
'y' => array(),
|
5591 |
),
|
5592 |
'tag_spec' => array(
|
5593 |
-
'html_format' => array(
|
5594 |
-
'amp',
|
5595 |
-
'amp4ads',
|
5596 |
-
),
|
5597 |
'mandatory_ancestor' => 'svg',
|
5598 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
5599 |
),
|
5600 |
-
|
5601 |
),
|
5602 |
),
|
5603 |
'polygon' => array(
|
@@ -5679,14 +6340,9 @@ class AMP_Allowed_Tags_Generated {
|
|
5679 |
'xmlns:xlink' => array(),
|
5680 |
),
|
5681 |
'tag_spec' => array(
|
5682 |
-
'html_format' => array(
|
5683 |
-
'amp',
|
5684 |
-
'amp4ads',
|
5685 |
-
),
|
5686 |
'mandatory_ancestor' => 'svg',
|
5687 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
5688 |
),
|
5689 |
-
|
5690 |
),
|
5691 |
),
|
5692 |
'polyline' => array(
|
@@ -5768,21 +6424,15 @@ class AMP_Allowed_Tags_Generated {
|
|
5768 |
'xmlns:xlink' => array(),
|
5769 |
),
|
5770 |
'tag_spec' => array(
|
5771 |
-
'html_format' => array(
|
5772 |
-
'amp',
|
5773 |
-
'amp4ads',
|
5774 |
-
),
|
5775 |
'mandatory_ancestor' => 'svg',
|
5776 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
5777 |
),
|
5778 |
-
|
5779 |
),
|
5780 |
),
|
5781 |
'pre' => array(
|
5782 |
array(
|
5783 |
'attr_spec_list' => array(),
|
5784 |
'tag_spec' => array(),
|
5785 |
-
|
5786 |
),
|
5787 |
),
|
5788 |
'progress' => array(
|
@@ -5792,7 +6442,6 @@ class AMP_Allowed_Tags_Generated {
|
|
5792 |
'value' => array(),
|
5793 |
),
|
5794 |
'tag_spec' => array(),
|
5795 |
-
|
5796 |
),
|
5797 |
),
|
5798 |
'q' => array(
|
@@ -5800,16 +6449,17 @@ class AMP_Allowed_Tags_Generated {
|
|
5800 |
'attr_spec_list' => array(
|
5801 |
'cite' => array(
|
5802 |
'blacklisted_value_regex' => '__amp_source_origin',
|
5803 |
-
'
|
5804 |
-
|
5805 |
-
|
5806 |
-
'
|
5807 |
-
|
|
|
|
|
5808 |
),
|
5809 |
),
|
5810 |
),
|
5811 |
'tag_spec' => array(),
|
5812 |
-
|
5813 |
),
|
5814 |
),
|
5815 |
'radialgradient' => array(
|
@@ -5894,11 +6544,13 @@ class AMP_Allowed_Tags_Generated {
|
|
5894 |
'alternative_names' => array(
|
5895 |
'href',
|
5896 |
),
|
5897 |
-
'
|
5898 |
-
|
5899 |
-
|
5900 |
-
'
|
5901 |
-
|
|
|
|
|
5902 |
),
|
5903 |
),
|
5904 |
'xlink:role' => array(),
|
@@ -5911,21 +6563,15 @@ class AMP_Allowed_Tags_Generated {
|
|
5911 |
'xmlns:xlink' => array(),
|
5912 |
),
|
5913 |
'tag_spec' => array(
|
5914 |
-
'html_format' => array(
|
5915 |
-
'amp',
|
5916 |
-
'amp4ads',
|
5917 |
-
),
|
5918 |
'mandatory_ancestor' => 'svg',
|
5919 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
5920 |
),
|
5921 |
-
|
5922 |
),
|
5923 |
),
|
5924 |
'rb' => array(
|
5925 |
array(
|
5926 |
'attr_spec_list' => array(),
|
5927 |
'tag_spec' => array(),
|
5928 |
-
|
5929 |
),
|
5930 |
),
|
5931 |
'rect' => array(
|
@@ -6012,174 +6658,2201 @@ class AMP_Allowed_Tags_Generated {
|
|
6012 |
'y' => array(),
|
6013 |
),
|
6014 |
'tag_spec' => array(
|
6015 |
-
'html_format' => array(
|
6016 |
-
'amp',
|
6017 |
-
'amp4ads',
|
6018 |
-
),
|
6019 |
'mandatory_ancestor' => 'svg',
|
6020 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6021 |
),
|
6022 |
-
|
6023 |
),
|
6024 |
),
|
6025 |
'rp' => array(
|
6026 |
array(
|
6027 |
'attr_spec_list' => array(),
|
6028 |
'tag_spec' => array(),
|
6029 |
-
|
6030 |
),
|
6031 |
),
|
6032 |
'rt' => array(
|
6033 |
array(
|
6034 |
'attr_spec_list' => array(),
|
6035 |
'tag_spec' => array(),
|
6036 |
-
|
6037 |
),
|
6038 |
),
|
6039 |
'rtc' => array(
|
6040 |
array(
|
6041 |
'attr_spec_list' => array(),
|
6042 |
'tag_spec' => array(),
|
6043 |
-
|
6044 |
),
|
6045 |
),
|
6046 |
'ruby' => array(
|
6047 |
array(
|
6048 |
'attr_spec_list' => array(),
|
6049 |
'tag_spec' => array(),
|
6050 |
-
|
6051 |
),
|
6052 |
),
|
6053 |
's' => array(
|
6054 |
array(
|
6055 |
'attr_spec_list' => array(),
|
6056 |
'tag_spec' => array(),
|
6057 |
-
|
6058 |
),
|
6059 |
),
|
6060 |
'samp' => array(
|
6061 |
array(
|
6062 |
'attr_spec_list' => array(),
|
6063 |
'tag_spec' => array(),
|
6064 |
-
|
6065 |
),
|
6066 |
),
|
6067 |
'script' => array(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6068 |
array(
|
6069 |
'attr_spec_list' => array(
|
6070 |
'nonce' => array(),
|
6071 |
'type' => array(
|
|
|
6072 |
'mandatory' => true,
|
6073 |
'value_casei' => 'application/ld+json',
|
6074 |
),
|
6075 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
6076 |
'tag_spec' => array(
|
6077 |
'spec_name' => 'script type=application/ld+json',
|
6078 |
),
|
6079 |
-
|
6080 |
),
|
6081 |
array(
|
6082 |
'attr_spec_list' => array(
|
|
|
|
|
|
|
|
|
|
|
|
|
6083 |
'type' => array(
|
6084 |
'mandatory' => true,
|
6085 |
'value_casei' => 'application/json',
|
6086 |
),
|
6087 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
6088 |
'tag_spec' => array(
|
6089 |
-
'
|
6090 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6091 |
),
|
6092 |
-
'mandatory_parent' => 'amp-ima-video',
|
6093 |
-
'spec_name' => 'amp-ima-video > script[type=application/json]',
|
6094 |
),
|
6095 |
-
|
6096 |
),
|
6097 |
array(
|
6098 |
'attr_spec_list' => array(
|
|
|
|
|
|
|
|
|
6099 |
'nonce' => array(),
|
6100 |
'type' => array(
|
6101 |
-
'
|
6102 |
-
'value' => 'application/json',
|
6103 |
),
|
6104 |
),
|
6105 |
'tag_spec' => array(
|
6106 |
-
'
|
6107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
6108 |
),
|
6109 |
-
'mandatory_parent' => 'amp-ad-exit',
|
6110 |
-
'spec_name' => 'amp-ad-exit configuration json',
|
6111 |
-
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad-exit',
|
6112 |
),
|
6113 |
-
|
6114 |
),
|
6115 |
array(
|
6116 |
'attr_spec_list' => array(
|
|
|
|
|
|
|
|
|
6117 |
'nonce' => array(),
|
6118 |
'type' => array(
|
6119 |
-
'
|
6120 |
-
'value_casei' => 'application/json',
|
6121 |
),
|
6122 |
),
|
6123 |
'tag_spec' => array(
|
6124 |
-
'
|
6125 |
-
|
6126 |
-
'
|
|
|
|
|
|
|
6127 |
),
|
6128 |
-
'mandatory_parent' => 'amp-analytics',
|
6129 |
-
'spec_name' => 'amp-analytics extension .json script',
|
6130 |
-
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-analytics',
|
6131 |
),
|
6132 |
-
|
6133 |
),
|
6134 |
array(
|
6135 |
'attr_spec_list' => array(
|
|
|
|
|
|
|
|
|
6136 |
'nonce' => array(),
|
6137 |
'type' => array(
|
6138 |
-
'
|
6139 |
-
'value_casei' => 'application/json',
|
6140 |
),
|
6141 |
),
|
6142 |
'tag_spec' => array(
|
6143 |
-
'
|
6144 |
-
|
|
|
|
|
|
|
|
|
|
|
6145 |
),
|
6146 |
-
|
6147 |
),
|
6148 |
array(
|
6149 |
'attr_spec_list' => array(
|
|
|
|
|
|
|
|
|
6150 |
'nonce' => array(),
|
6151 |
'type' => array(
|
6152 |
-
'
|
6153 |
-
'value_casei' => 'application/json',
|
6154 |
),
|
6155 |
),
|
6156 |
'tag_spec' => array(
|
6157 |
-
'
|
6158 |
-
|
|
|
|
|
|
|
|
|
6159 |
),
|
6160 |
-
'mandatory_parent' => 'amp-state',
|
6161 |
-
'spec_name' => 'amp-bind extension .json script',
|
6162 |
-
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-bind',
|
6163 |
),
|
6164 |
-
|
6165 |
),
|
6166 |
array(
|
6167 |
'attr_spec_list' => array(
|
|
|
|
|
|
|
|
|
6168 |
'nonce' => array(),
|
6169 |
'type' => array(
|
6170 |
-
'
|
6171 |
-
'value_casei' => 'application/json',
|
6172 |
),
|
6173 |
),
|
6174 |
'tag_spec' => array(
|
6175 |
-
'
|
6176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
6177 |
),
|
6178 |
-
'mandatory_parent' => 'amp-experiment',
|
6179 |
-
'spec_name' => 'amp-experiment extension .json script',
|
6180 |
-
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-experiment',
|
6181 |
),
|
6182 |
-
|
6183 |
),
|
6184 |
),
|
6185 |
'section' => array(
|
@@ -6187,10 +8860,9 @@ class AMP_Allowed_Tags_Generated {
|
|
6187 |
'attr_spec_list' => array(),
|
6188 |
'tag_spec' => array(
|
6189 |
'disallowed_ancestor' => array(
|
6190 |
-
|
6191 |
),
|
6192 |
),
|
6193 |
-
|
6194 |
),
|
6195 |
array(
|
6196 |
'attr_spec_list' => array(
|
@@ -6202,7 +8874,6 @@ class AMP_Allowed_Tags_Generated {
|
|
6202 |
'mandatory_parent' => 'amp-accordion',
|
6203 |
'spec_name' => 'amp-accordion > section',
|
6204 |
),
|
6205 |
-
|
6206 |
),
|
6207 |
),
|
6208 |
'select' => array(
|
@@ -6217,7 +8888,7 @@ class AMP_Allowed_Tags_Generated {
|
|
6217 |
'disabled' => array(),
|
6218 |
'multiple' => array(),
|
6219 |
'name' => array(
|
6220 |
-
'blacklisted_value_regex' => '(^|\\s)(__amp_\\
|
6221 |
),
|
6222 |
'required' => array(),
|
6223 |
'size' => array(),
|
@@ -6225,7 +8896,6 @@ class AMP_Allowed_Tags_Generated {
|
|
6225 |
'tag_spec' => array(
|
6226 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
6227 |
),
|
6228 |
-
|
6229 |
),
|
6230 |
),
|
6231 |
'slot' => array(
|
@@ -6233,19 +8903,13 @@ class AMP_Allowed_Tags_Generated {
|
|
6233 |
'attr_spec_list' => array(
|
6234 |
'name' => array(),
|
6235 |
),
|
6236 |
-
'tag_spec' => array(
|
6237 |
-
'html_format' => array(
|
6238 |
-
'amp',
|
6239 |
-
),
|
6240 |
-
),
|
6241 |
-
|
6242 |
),
|
6243 |
),
|
6244 |
'small' => array(
|
6245 |
array(
|
6246 |
'attr_spec_list' => array(),
|
6247 |
'tag_spec' => array(),
|
6248 |
-
|
6249 |
),
|
6250 |
),
|
6251 |
'solidcolor' => array(
|
@@ -6322,14 +8986,9 @@ class AMP_Allowed_Tags_Generated {
|
|
6322 |
'xmlns:xlink' => array(),
|
6323 |
),
|
6324 |
'tag_spec' => array(
|
6325 |
-
'html_format' => array(
|
6326 |
-
'amp',
|
6327 |
-
'amp4ads',
|
6328 |
-
),
|
6329 |
'mandatory_ancestor' => 'svg',
|
6330 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6331 |
),
|
6332 |
-
|
6333 |
),
|
6334 |
),
|
6335 |
'source' => array(
|
@@ -6340,23 +8999,20 @@ class AMP_Allowed_Tags_Generated {
|
|
6340 |
'media' => array(),
|
6341 |
'src' => array(
|
6342 |
'blacklisted_value_regex' => '__amp_source_origin',
|
6343 |
-
'
|
6344 |
-
|
6345 |
-
'
|
|
|
|
|
6346 |
),
|
6347 |
),
|
6348 |
'type' => array(),
|
6349 |
),
|
6350 |
'tag_spec' => array(
|
6351 |
-
'html_format' => array(
|
6352 |
-
'amp',
|
6353 |
-
'amp4ads',
|
6354 |
-
),
|
6355 |
'mandatory_parent' => 'amp-video',
|
6356 |
'spec_name' => 'amp-video > source',
|
6357 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
|
6358 |
),
|
6359 |
-
|
6360 |
),
|
6361 |
array(
|
6362 |
'attr_spec_list' => array(
|
@@ -6365,23 +9021,20 @@ class AMP_Allowed_Tags_Generated {
|
|
6365 |
'media' => array(),
|
6366 |
'src' => array(
|
6367 |
'blacklisted_value_regex' => '__amp_source_origin',
|
6368 |
-
'
|
6369 |
-
|
6370 |
-
'
|
|
|
|
|
6371 |
),
|
6372 |
),
|
6373 |
'type' => array(),
|
6374 |
),
|
6375 |
'tag_spec' => array(
|
6376 |
-
'html_format' => array(
|
6377 |
-
'amp',
|
6378 |
-
'amp4ads',
|
6379 |
-
),
|
6380 |
'mandatory_parent' => 'amp-audio',
|
6381 |
'spec_name' => 'amp-audio > source',
|
6382 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
6383 |
),
|
6384 |
-
|
6385 |
),
|
6386 |
array(
|
6387 |
'attr_spec_list' => array(
|
@@ -6389,9 +9042,11 @@ class AMP_Allowed_Tags_Generated {
|
|
6389 |
'src' => array(
|
6390 |
'blacklisted_value_regex' => '__amp_source_origin',
|
6391 |
'mandatory' => true,
|
6392 |
-
'
|
6393 |
-
|
6394 |
-
'
|
|
|
|
|
6395 |
),
|
6396 |
),
|
6397 |
'type' => array(
|
@@ -6399,15 +9054,10 @@ class AMP_Allowed_Tags_Generated {
|
|
6399 |
),
|
6400 |
),
|
6401 |
'tag_spec' => array(
|
6402 |
-
'html_format' => array(
|
6403 |
-
'amp',
|
6404 |
-
'amp4ads',
|
6405 |
-
),
|
6406 |
'mandatory_parent' => 'audio',
|
6407 |
'spec_name' => 'audio > source',
|
6408 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
6409 |
),
|
6410 |
-
|
6411 |
),
|
6412 |
array(
|
6413 |
'attr_spec_list' => array(
|
@@ -6415,9 +9065,11 @@ class AMP_Allowed_Tags_Generated {
|
|
6415 |
'src' => array(
|
6416 |
'blacklisted_value_regex' => '__amp_source_origin',
|
6417 |
'mandatory' => true,
|
6418 |
-
'
|
6419 |
-
|
6420 |
-
'
|
|
|
|
|
6421 |
),
|
6422 |
),
|
6423 |
'type' => array(
|
@@ -6425,15 +9077,10 @@ class AMP_Allowed_Tags_Generated {
|
|
6425 |
),
|
6426 |
),
|
6427 |
'tag_spec' => array(
|
6428 |
-
'html_format' => array(
|
6429 |
-
'amp',
|
6430 |
-
'amp4ads',
|
6431 |
-
),
|
6432 |
'mandatory_parent' => 'video',
|
6433 |
'spec_name' => 'video > source',
|
6434 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
|
6435 |
),
|
6436 |
-
|
6437 |
),
|
6438 |
array(
|
6439 |
'attr_spec_list' => array(
|
@@ -6442,40 +9089,34 @@ class AMP_Allowed_Tags_Generated {
|
|
6442 |
'media' => array(),
|
6443 |
'src' => array(
|
6444 |
'blacklisted_value_regex' => '__amp_source_origin',
|
6445 |
-
'
|
6446 |
-
|
6447 |
-
'
|
|
|
|
|
6448 |
),
|
6449 |
),
|
6450 |
'type' => array(),
|
6451 |
),
|
6452 |
'tag_spec' => array(
|
6453 |
-
'html_format' => array(
|
6454 |
-
'amp',
|
6455 |
-
'amp4ads',
|
6456 |
-
),
|
6457 |
'mandatory_parent' => 'amp-ima-video',
|
|
|
|
|
|
|
6458 |
'spec_name' => 'amp-ima-video > source',
|
6459 |
),
|
6460 |
-
|
6461 |
),
|
6462 |
),
|
6463 |
'spacer' => array(
|
6464 |
array(
|
6465 |
'attr_spec_list' => array(),
|
6466 |
-
'tag_spec' => array(
|
6467 |
-
'html_format' => array(
|
6468 |
-
'amp',
|
6469 |
-
),
|
6470 |
-
),
|
6471 |
-
|
6472 |
),
|
6473 |
),
|
6474 |
'span' => array(
|
6475 |
array(
|
6476 |
'attr_spec_list' => array(),
|
6477 |
'tag_spec' => array(),
|
6478 |
-
|
6479 |
),
|
6480 |
),
|
6481 |
'stop' => array(
|
@@ -6489,15 +9130,10 @@ class AMP_Allowed_Tags_Generated {
|
|
6489 |
),
|
6490 |
),
|
6491 |
'tag_spec' => array(
|
6492 |
-
'html_format' => array(
|
6493 |
-
'amp',
|
6494 |
-
'amp4ads',
|
6495 |
-
),
|
6496 |
'mandatory_ancestor' => 'lineargradient',
|
6497 |
'spec_name' => 'lineargradient > stop',
|
6498 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6499 |
),
|
6500 |
-
|
6501 |
),
|
6502 |
array(
|
6503 |
'attr_spec_list' => array(
|
@@ -6509,48 +9145,84 @@ class AMP_Allowed_Tags_Generated {
|
|
6509 |
),
|
6510 |
),
|
6511 |
'tag_spec' => array(
|
6512 |
-
'html_format' => array(
|
6513 |
-
'amp',
|
6514 |
-
'amp4ads',
|
6515 |
-
),
|
6516 |
'mandatory_ancestor' => 'radialgradient',
|
6517 |
'spec_name' => 'radialgradient > stop',
|
6518 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6519 |
),
|
6520 |
-
|
6521 |
),
|
6522 |
),
|
6523 |
'strike' => array(
|
6524 |
array(
|
6525 |
'attr_spec_list' => array(),
|
6526 |
-
'tag_spec' => array(
|
6527 |
-
'html_format' => array(
|
6528 |
-
'amp',
|
6529 |
-
),
|
6530 |
-
),
|
6531 |
-
|
6532 |
),
|
6533 |
),
|
6534 |
'strong' => array(
|
6535 |
array(
|
6536 |
'attr_spec_list' => array(),
|
6537 |
'tag_spec' => array(),
|
6538 |
-
|
6539 |
),
|
6540 |
),
|
6541 |
'style' => array(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6542 |
array(
|
6543 |
'attr_spec_list' => array(
|
6544 |
'amp-boilerplate' => array(
|
|
|
6545 |
'mandatory' => true,
|
6546 |
'value' => '',
|
6547 |
),
|
6548 |
'nonce' => array(),
|
6549 |
),
|
|
|
|
|
|
|
6550 |
'tag_spec' => array(
|
6551 |
-
'
|
6552 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6553 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
6554 |
'mandatory_alternatives' => 'noscript > style[amp-boilerplate]',
|
6555 |
'mandatory_ancestor' => 'head',
|
6556 |
'mandatory_parent' => 'noscript',
|
@@ -6558,39 +9230,36 @@ class AMP_Allowed_Tags_Generated {
|
|
6558 |
'spec_url' => 'https://github.com/ampproject/amphtml/blob/master/spec/amp-boilerplate.md',
|
6559 |
'unique' => true,
|
6560 |
),
|
6561 |
-
|
6562 |
),
|
6563 |
array(
|
6564 |
'attr_spec_list' => array(
|
6565 |
'amp-keyframes' => array(
|
|
|
6566 |
'mandatory' => true,
|
6567 |
'value' => '',
|
6568 |
),
|
6569 |
),
|
|
|
|
|
|
|
|
|
6570 |
'tag_spec' => array(
|
6571 |
-
'html_format' => array(
|
6572 |
-
'amp',
|
6573 |
-
'amp4ads',
|
6574 |
-
),
|
6575 |
'mandatory_parent' => 'body',
|
6576 |
'spec_name' => 'style[amp-keyframes]',
|
6577 |
'unique' => true,
|
6578 |
),
|
6579 |
-
|
6580 |
),
|
6581 |
),
|
6582 |
'sub' => array(
|
6583 |
array(
|
6584 |
'attr_spec_list' => array(),
|
6585 |
'tag_spec' => array(),
|
6586 |
-
|
6587 |
),
|
6588 |
),
|
6589 |
'sup' => array(
|
6590 |
array(
|
6591 |
'attr_spec_list' => array(),
|
6592 |
'tag_spec' => array(),
|
6593 |
-
|
6594 |
),
|
6595 |
),
|
6596 |
'svg' => array(
|
@@ -6678,13 +9347,8 @@ class AMP_Allowed_Tags_Generated {
|
|
6678 |
'zoomandpan' => array(),
|
6679 |
),
|
6680 |
'tag_spec' => array(
|
6681 |
-
'html_format' => array(
|
6682 |
-
'amp',
|
6683 |
-
'amp4ads',
|
6684 |
-
),
|
6685 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6686 |
),
|
6687 |
-
|
6688 |
),
|
6689 |
),
|
6690 |
'switch' => array(
|
@@ -6762,14 +9426,9 @@ class AMP_Allowed_Tags_Generated {
|
|
6762 |
'xmlns:xlink' => array(),
|
6763 |
),
|
6764 |
'tag_spec' => array(
|
6765 |
-
'html_format' => array(
|
6766 |
-
'amp',
|
6767 |
-
'amp4ads',
|
6768 |
-
),
|
6769 |
'mandatory_ancestor' => 'svg',
|
6770 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6771 |
),
|
6772 |
-
|
6773 |
),
|
6774 |
),
|
6775 |
'symbol' => array(
|
@@ -6847,14 +9506,9 @@ class AMP_Allowed_Tags_Generated {
|
|
6847 |
'xmlns:xlink' => array(),
|
6848 |
),
|
6849 |
'tag_spec' => array(
|
6850 |
-
'html_format' => array(
|
6851 |
-
'amp',
|
6852 |
-
'amp4ads',
|
6853 |
-
),
|
6854 |
'mandatory_ancestor' => 'svg',
|
6855 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6856 |
),
|
6857 |
-
|
6858 |
),
|
6859 |
),
|
6860 |
'table' => array(
|
@@ -6871,14 +9525,12 @@ class AMP_Allowed_Tags_Generated {
|
|
6871 |
'width' => array(),
|
6872 |
),
|
6873 |
'tag_spec' => array(),
|
6874 |
-
|
6875 |
),
|
6876 |
),
|
6877 |
'tbody' => array(
|
6878 |
array(
|
6879 |
'attr_spec_list' => array(),
|
6880 |
'tag_spec' => array(),
|
6881 |
-
|
6882 |
),
|
6883 |
),
|
6884 |
'td' => array(
|
@@ -6894,7 +9546,6 @@ class AMP_Allowed_Tags_Generated {
|
|
6894 |
'width' => array(),
|
6895 |
),
|
6896 |
'tag_spec' => array(),
|
6897 |
-
|
6898 |
),
|
6899 |
),
|
6900 |
'template' => array(
|
@@ -6907,10 +9558,30 @@ class AMP_Allowed_Tags_Generated {
|
|
6907 |
),
|
6908 |
'tag_spec' => array(
|
6909 |
'disallowed_ancestor' => array(
|
6910 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
6911 |
),
|
6912 |
),
|
6913 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6914 |
),
|
6915 |
),
|
6916 |
'text' => array(
|
@@ -6997,14 +9668,9 @@ class AMP_Allowed_Tags_Generated {
|
|
6997 |
'y' => array(),
|
6998 |
),
|
6999 |
'tag_spec' => array(
|
7000 |
-
'html_format' => array(
|
7001 |
-
'amp',
|
7002 |
-
'amp4ads',
|
7003 |
-
),
|
7004 |
'mandatory_ancestor' => 'svg',
|
7005 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
7006 |
),
|
7007 |
-
|
7008 |
),
|
7009 |
),
|
7010 |
'textarea' => array(
|
@@ -7032,7 +9698,7 @@ class AMP_Allowed_Tags_Generated {
|
|
7032 |
'maxlength' => array(),
|
7033 |
'minlength' => array(),
|
7034 |
'name' => array(
|
7035 |
-
'blacklisted_value_regex' => '(^|\\s)(__amp_\\
|
7036 |
),
|
7037 |
'placeholder' => array(),
|
7038 |
'readonly' => array(),
|
@@ -7047,7 +9713,6 @@ class AMP_Allowed_Tags_Generated {
|
|
7047 |
'tag_spec' => array(
|
7048 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
7049 |
),
|
7050 |
-
|
7051 |
),
|
7052 |
),
|
7053 |
'textpath' => array(
|
@@ -7129,11 +9794,13 @@ class AMP_Allowed_Tags_Generated {
|
|
7129 |
'alternative_names' => array(
|
7130 |
'href',
|
7131 |
),
|
7132 |
-
'
|
7133 |
-
|
7134 |
-
|
7135 |
-
'
|
7136 |
-
|
|
|
|
|
7137 |
),
|
7138 |
),
|
7139 |
'xlink:role' => array(),
|
@@ -7146,21 +9813,15 @@ class AMP_Allowed_Tags_Generated {
|
|
7146 |
'xmlns:xlink' => array(),
|
7147 |
),
|
7148 |
'tag_spec' => array(
|
7149 |
-
'html_format' => array(
|
7150 |
-
'amp',
|
7151 |
-
'amp4ads',
|
7152 |
-
),
|
7153 |
'mandatory_ancestor' => 'svg',
|
7154 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
7155 |
),
|
7156 |
-
|
7157 |
),
|
7158 |
),
|
7159 |
'tfoot' => array(
|
7160 |
array(
|
7161 |
'attr_spec_list' => array(),
|
7162 |
'tag_spec' => array(),
|
7163 |
-
|
7164 |
),
|
7165 |
),
|
7166 |
'th' => array(
|
@@ -7179,14 +9840,12 @@ class AMP_Allowed_Tags_Generated {
|
|
7179 |
'width' => array(),
|
7180 |
),
|
7181 |
'tag_spec' => array(),
|
7182 |
-
|
7183 |
),
|
7184 |
),
|
7185 |
'thead' => array(
|
7186 |
array(
|
7187 |
'attr_spec_list' => array(),
|
7188 |
'tag_spec' => array(),
|
7189 |
-
|
7190 |
),
|
7191 |
),
|
7192 |
'time' => array(
|
@@ -7195,7 +9854,6 @@ class AMP_Allowed_Tags_Generated {
|
|
7195 |
'datetime' => array(),
|
7196 |
),
|
7197 |
'tag_spec' => array(),
|
7198 |
-
|
7199 |
),
|
7200 |
),
|
7201 |
'title' => array(
|
@@ -7204,7 +9862,6 @@ class AMP_Allowed_Tags_Generated {
|
|
7204 |
'tag_spec' => array(
|
7205 |
'spec_name' => 'title',
|
7206 |
),
|
7207 |
-
|
7208 |
),
|
7209 |
array(
|
7210 |
'attr_spec_list' => array(
|
@@ -7217,15 +9874,10 @@ class AMP_Allowed_Tags_Generated {
|
|
7217 |
'xmlns:xlink' => array(),
|
7218 |
),
|
7219 |
'tag_spec' => array(
|
7220 |
-
'html_format' => array(
|
7221 |
-
'amp',
|
7222 |
-
'amp4ads',
|
7223 |
-
),
|
7224 |
'mandatory_ancestor' => 'svg',
|
7225 |
'spec_name' => 'svg title',
|
7226 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
7227 |
),
|
7228 |
-
|
7229 |
),
|
7230 |
),
|
7231 |
'tr' => array(
|
@@ -7237,7 +9889,6 @@ class AMP_Allowed_Tags_Generated {
|
|
7237 |
'valign' => array(),
|
7238 |
),
|
7239 |
'tag_spec' => array(),
|
7240 |
-
|
7241 |
),
|
7242 |
),
|
7243 |
'track' => array(
|
@@ -7253,22 +9904,19 @@ class AMP_Allowed_Tags_Generated {
|
|
7253 |
'src' => array(
|
7254 |
'blacklisted_value_regex' => '__amp_source_origin',
|
7255 |
'mandatory' => true,
|
7256 |
-
'
|
7257 |
-
|
7258 |
-
'
|
|
|
|
|
7259 |
),
|
7260 |
),
|
7261 |
'srclang' => array(),
|
7262 |
),
|
7263 |
'tag_spec' => array(
|
7264 |
-
'html_format' => array(
|
7265 |
-
'amp',
|
7266 |
-
'amp4ads',
|
7267 |
-
),
|
7268 |
'mandatory_parent' => 'audio',
|
7269 |
'spec_name' => 'audio > track',
|
7270 |
),
|
7271 |
-
|
7272 |
),
|
7273 |
array(
|
7274 |
'attr_spec_list' => array(
|
@@ -7283,9 +9931,11 @@ class AMP_Allowed_Tags_Generated {
|
|
7283 |
'src' => array(
|
7284 |
'blacklisted_value_regex' => '__amp_source_origin',
|
7285 |
'mandatory' => true,
|
7286 |
-
'
|
7287 |
-
|
7288 |
-
'
|
|
|
|
|
7289 |
),
|
7290 |
),
|
7291 |
'srclang' => array(
|
@@ -7293,14 +9943,9 @@ class AMP_Allowed_Tags_Generated {
|
|
7293 |
),
|
7294 |
),
|
7295 |
'tag_spec' => array(
|
7296 |
-
'html_format' => array(
|
7297 |
-
'amp',
|
7298 |
-
'amp4ads',
|
7299 |
-
),
|
7300 |
'mandatory_parent' => 'audio',
|
7301 |
'spec_name' => 'audio > track[kind=subtitles]',
|
7302 |
),
|
7303 |
-
|
7304 |
),
|
7305 |
array(
|
7306 |
'attr_spec_list' => array(
|
@@ -7314,22 +9959,19 @@ class AMP_Allowed_Tags_Generated {
|
|
7314 |
'src' => array(
|
7315 |
'blacklisted_value_regex' => '__amp_source_origin',
|
7316 |
'mandatory' => true,
|
7317 |
-
'
|
7318 |
-
|
7319 |
-
'
|
|
|
|
|
7320 |
),
|
7321 |
),
|
7322 |
'srclang' => array(),
|
7323 |
),
|
7324 |
'tag_spec' => array(
|
7325 |
-
'html_format' => array(
|
7326 |
-
'amp',
|
7327 |
-
'amp4ads',
|
7328 |
-
),
|
7329 |
'mandatory_parent' => 'video',
|
7330 |
'spec_name' => 'video > track',
|
7331 |
),
|
7332 |
-
|
7333 |
),
|
7334 |
array(
|
7335 |
'attr_spec_list' => array(
|
@@ -7344,9 +9986,11 @@ class AMP_Allowed_Tags_Generated {
|
|
7344 |
'src' => array(
|
7345 |
'blacklisted_value_regex' => '__amp_source_origin',
|
7346 |
'mandatory' => true,
|
7347 |
-
'
|
7348 |
-
|
7349 |
-
'
|
|
|
|
|
7350 |
),
|
7351 |
),
|
7352 |
'srclang' => array(
|
@@ -7354,14 +9998,9 @@ class AMP_Allowed_Tags_Generated {
|
|
7354 |
),
|
7355 |
),
|
7356 |
'tag_spec' => array(
|
7357 |
-
'html_format' => array(
|
7358 |
-
'amp',
|
7359 |
-
'amp4ads',
|
7360 |
-
),
|
7361 |
'mandatory_parent' => 'video',
|
7362 |
'spec_name' => 'video > track[kind=subtitles]',
|
7363 |
),
|
7364 |
-
|
7365 |
),
|
7366 |
array(
|
7367 |
'attr_spec_list' => array(
|
@@ -7378,22 +10017,19 @@ class AMP_Allowed_Tags_Generated {
|
|
7378 |
'src' => array(
|
7379 |
'blacklisted_value_regex' => '__amp_source_origin',
|
7380 |
'mandatory' => true,
|
7381 |
-
'
|
7382 |
-
|
7383 |
-
'
|
|
|
|
|
7384 |
),
|
7385 |
),
|
7386 |
'srclang' => array(),
|
7387 |
),
|
7388 |
'tag_spec' => array(
|
7389 |
-
'html_format' => array(
|
7390 |
-
'amp',
|
7391 |
-
'amp4ads',
|
7392 |
-
),
|
7393 |
'mandatory_parent' => 'amp-audio',
|
7394 |
'spec_name' => 'amp-audio > track',
|
7395 |
),
|
7396 |
-
|
7397 |
),
|
7398 |
array(
|
7399 |
'attr_spec_list' => array(
|
@@ -7411,9 +10047,11 @@ class AMP_Allowed_Tags_Generated {
|
|
7411 |
'src' => array(
|
7412 |
'blacklisted_value_regex' => '__amp_source_origin',
|
7413 |
'mandatory' => true,
|
7414 |
-
'
|
7415 |
-
|
7416 |
-
'
|
|
|
|
|
7417 |
),
|
7418 |
),
|
7419 |
'srclang' => array(
|
@@ -7421,14 +10059,9 @@ class AMP_Allowed_Tags_Generated {
|
|
7421 |
),
|
7422 |
),
|
7423 |
'tag_spec' => array(
|
7424 |
-
'html_format' => array(
|
7425 |
-
'amp',
|
7426 |
-
'amp4ads',
|
7427 |
-
),
|
7428 |
'mandatory_parent' => 'amp-audio',
|
7429 |
'spec_name' => 'amp-audio > track[kind=subtitles]',
|
7430 |
),
|
7431 |
-
|
7432 |
),
|
7433 |
array(
|
7434 |
'attr_spec_list' => array(
|
@@ -7445,22 +10078,19 @@ class AMP_Allowed_Tags_Generated {
|
|
7445 |
'src' => array(
|
7446 |
'blacklisted_value_regex' => '__amp_source_origin',
|
7447 |
'mandatory' => true,
|
7448 |
-
'
|
7449 |
-
|
7450 |
-
'
|
|
|
|
|
7451 |
),
|
7452 |
),
|
7453 |
'srclang' => array(),
|
7454 |
),
|
7455 |
'tag_spec' => array(
|
7456 |
-
'html_format' => array(
|
7457 |
-
'amp',
|
7458 |
-
'amp4ads',
|
7459 |
-
),
|
7460 |
'mandatory_parent' => 'amp-video',
|
7461 |
'spec_name' => 'amp-video > track',
|
7462 |
),
|
7463 |
-
|
7464 |
),
|
7465 |
array(
|
7466 |
'attr_spec_list' => array(
|
@@ -7478,9 +10108,11 @@ class AMP_Allowed_Tags_Generated {
|
|
7478 |
'src' => array(
|
7479 |
'blacklisted_value_regex' => '__amp_source_origin',
|
7480 |
'mandatory' => true,
|
7481 |
-
'
|
7482 |
-
|
7483 |
-
'
|
|
|
|
|
7484 |
),
|
7485 |
),
|
7486 |
'srclang' => array(
|
@@ -7488,14 +10120,9 @@ class AMP_Allowed_Tags_Generated {
|
|
7488 |
),
|
7489 |
),
|
7490 |
'tag_spec' => array(
|
7491 |
-
'html_format' => array(
|
7492 |
-
'amp',
|
7493 |
-
'amp4ads',
|
7494 |
-
),
|
7495 |
'mandatory_parent' => 'amp-video',
|
7496 |
'spec_name' => 'amp-video > track[kind=subtitles]',
|
7497 |
),
|
7498 |
-
|
7499 |
),
|
7500 |
array(
|
7501 |
'attr_spec_list' => array(
|
@@ -7513,9 +10140,11 @@ class AMP_Allowed_Tags_Generated {
|
|
7513 |
'src' => array(
|
7514 |
'blacklisted_value_regex' => '__amp_source_origin',
|
7515 |
'mandatory' => true,
|
7516 |
-
'
|
7517 |
-
|
7518 |
-
'
|
|
|
|
|
7519 |
),
|
7520 |
),
|
7521 |
'srclang' => array(
|
@@ -7523,15 +10152,10 @@ class AMP_Allowed_Tags_Generated {
|
|
7523 |
),
|
7524 |
),
|
7525 |
'tag_spec' => array(
|
7526 |
-
'html_format' => array(
|
7527 |
-
'amp',
|
7528 |
-
'amp4ads',
|
7529 |
-
),
|
7530 |
'mandatory_parent' => 'amp-ima-video',
|
7531 |
'spec_name' => 'amp-ima-video > track[kind=subtitles]',
|
7532 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ima-video',
|
7533 |
),
|
7534 |
-
|
7535 |
),
|
7536 |
),
|
7537 |
'tref' => array(
|
@@ -7610,11 +10234,13 @@ class AMP_Allowed_Tags_Generated {
|
|
7610 |
'alternative_names' => array(
|
7611 |
'href',
|
7612 |
),
|
7613 |
-
'
|
7614 |
-
|
7615 |
-
|
7616 |
-
'
|
7617 |
-
|
|
|
|
|
7618 |
),
|
7619 |
),
|
7620 |
'xlink:role' => array(),
|
@@ -7627,14 +10253,9 @@ class AMP_Allowed_Tags_Generated {
|
|
7627 |
'xmlns:xlink' => array(),
|
7628 |
),
|
7629 |
'tag_spec' => array(
|
7630 |
-
'html_format' => array(
|
7631 |
-
'amp',
|
7632 |
-
'amp4ads',
|
7633 |
-
),
|
7634 |
'mandatory_ancestor' => 'svg',
|
7635 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
7636 |
),
|
7637 |
-
|
7638 |
),
|
7639 |
),
|
7640 |
'tspan' => array(
|
@@ -7720,39 +10341,27 @@ class AMP_Allowed_Tags_Generated {
|
|
7720 |
'y' => array(),
|
7721 |
),
|
7722 |
'tag_spec' => array(
|
7723 |
-
'html_format' => array(
|
7724 |
-
'amp',
|
7725 |
-
'amp4ads',
|
7726 |
-
),
|
7727 |
'mandatory_ancestor' => 'svg',
|
7728 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
7729 |
),
|
7730 |
-
|
7731 |
),
|
7732 |
),
|
7733 |
'tt' => array(
|
7734 |
array(
|
7735 |
'attr_spec_list' => array(),
|
7736 |
-
'tag_spec' => array(
|
7737 |
-
'html_format' => array(
|
7738 |
-
'amp',
|
7739 |
-
),
|
7740 |
-
),
|
7741 |
-
|
7742 |
),
|
7743 |
),
|
7744 |
'u' => array(
|
7745 |
array(
|
7746 |
'attr_spec_list' => array(),
|
7747 |
'tag_spec' => array(),
|
7748 |
-
|
7749 |
),
|
7750 |
),
|
7751 |
'ul' => array(
|
7752 |
array(
|
7753 |
'attr_spec_list' => array(),
|
7754 |
'tag_spec' => array(),
|
7755 |
-
|
7756 |
),
|
7757 |
),
|
7758 |
'use' => array(
|
@@ -7835,11 +10444,13 @@ class AMP_Allowed_Tags_Generated {
|
|
7835 |
'alternative_names' => array(
|
7836 |
'href',
|
7837 |
),
|
7838 |
-
'
|
7839 |
-
|
7840 |
-
|
7841 |
-
'
|
7842 |
-
|
|
|
|
|
7843 |
),
|
7844 |
),
|
7845 |
'xlink:role' => array(),
|
@@ -7853,21 +10464,15 @@ class AMP_Allowed_Tags_Generated {
|
|
7853 |
'y' => array(),
|
7854 |
),
|
7855 |
'tag_spec' => array(
|
7856 |
-
'html_format' => array(
|
7857 |
-
'amp',
|
7858 |
-
'amp4ads',
|
7859 |
-
),
|
7860 |
'mandatory_ancestor' => 'svg',
|
7861 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
7862 |
),
|
7863 |
-
|
7864 |
),
|
7865 |
),
|
7866 |
'var' => array(
|
7867 |
array(
|
7868 |
'attr_spec_list' => array(),
|
7869 |
'tag_spec' => array(),
|
7870 |
-
|
7871 |
),
|
7872 |
),
|
7873 |
'video' => array(
|
@@ -7883,23 +10488,21 @@ class AMP_Allowed_Tags_Generated {
|
|
7883 |
'preload' => array(),
|
7884 |
'src' => array(
|
7885 |
'blacklisted_value_regex' => '__amp_source_origin',
|
7886 |
-
'
|
7887 |
-
|
7888 |
-
'
|
7889 |
-
|
|
|
|
|
7890 |
),
|
7891 |
),
|
7892 |
'width' => array(),
|
7893 |
),
|
7894 |
'tag_spec' => array(
|
7895 |
-
'html_format' => array(
|
7896 |
-
'amp',
|
7897 |
-
),
|
7898 |
'mandatory_ancestor' => 'noscript',
|
7899 |
'mandatory_ancestor_suggested_alternative' => 'amp-video',
|
7900 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
|
7901 |
),
|
7902 |
-
|
7903 |
),
|
7904 |
),
|
7905 |
'view' => array(
|
@@ -7919,14 +10522,9 @@ class AMP_Allowed_Tags_Generated {
|
|
7919 |
'zoomandpan' => array(),
|
7920 |
),
|
7921 |
'tag_spec' => array(
|
7922 |
-
'html_format' => array(
|
7923 |
-
'amp',
|
7924 |
-
'amp4ads',
|
7925 |
-
),
|
7926 |
'mandatory_ancestor' => 'svg',
|
7927 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
7928 |
),
|
7929 |
-
|
7930 |
),
|
7931 |
),
|
7932 |
'vkern' => array(
|
@@ -7946,32 +10544,21 @@ class AMP_Allowed_Tags_Generated {
|
|
7946 |
'xmlns:xlink' => array(),
|
7947 |
),
|
7948 |
'tag_spec' => array(
|
7949 |
-
'html_format' => array(
|
7950 |
-
'amp',
|
7951 |
-
'amp4ads',
|
7952 |
-
),
|
7953 |
'mandatory_ancestor' => 'svg',
|
7954 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
7955 |
),
|
7956 |
-
|
7957 |
),
|
7958 |
),
|
7959 |
'wbr' => array(
|
7960 |
array(
|
7961 |
'attr_spec_list' => array(),
|
7962 |
'tag_spec' => array(),
|
7963 |
-
|
7964 |
),
|
7965 |
),
|
7966 |
'xmp' => array(
|
7967 |
array(
|
7968 |
'attr_spec_list' => array(),
|
7969 |
-
'tag_spec' => array(
|
7970 |
-
'html_format' => array(
|
7971 |
-
'amp',
|
7972 |
-
),
|
7973 |
-
),
|
7974 |
-
|
7975 |
),
|
7976 |
),
|
7977 |
);
|
@@ -7984,7 +10571,6 @@ class AMP_Allowed_Tags_Generated {
|
|
7984 |
'layout' => array(),
|
7985 |
'sizes' => array(),
|
7986 |
'width' => array(),
|
7987 |
-
|
7988 |
);
|
7989 |
|
7990 |
|
@@ -8040,6 +10626,9 @@ class AMP_Allowed_Tags_Generated {
|
|
8040 |
'amp-access-show' => array(),
|
8041 |
'amp-access-style' => array(),
|
8042 |
'amp-access-template' => array(),
|
|
|
|
|
|
|
8043 |
'aria-activedescendant' => array(),
|
8044 |
'aria-atomic' => array(),
|
8045 |
'aria-autocomplete' => array(),
|
@@ -8077,21 +10666,21 @@ class AMP_Allowed_Tags_Generated {
|
|
8077 |
'aria-valuenow' => array(),
|
8078 |
'aria-valuetext' => array(),
|
8079 |
'class' => array(
|
8080 |
-
|
8081 |
),
|
8082 |
'content' => array(),
|
8083 |
'datatype' => array(),
|
8084 |
'dir' => array(),
|
8085 |
'draggable' => array(),
|
8086 |
'fallback' => array(
|
8087 |
-
|
8088 |
),
|
8089 |
'hidden' => array(
|
8090 |
-
|
8091 |
),
|
8092 |
'i-amp-access-id' => array(),
|
8093 |
'id' => array(
|
8094 |
-
|
8095 |
),
|
8096 |
'inlist' => array(),
|
8097 |
'itemid' => array(),
|
@@ -8104,40 +10693,84 @@ class AMP_Allowed_Tags_Generated {
|
|
8104 |
'on' => array(),
|
8105 |
'overflow' => array(),
|
8106 |
'placeholder' => array(
|
8107 |
-
|
8108 |
),
|
8109 |
'prefix' => array(),
|
8110 |
'property' => array(),
|
8111 |
'rel' => array(
|
8112 |
-
|
8113 |
),
|
8114 |
'resource' => array(),
|
8115 |
'rev' => array(),
|
8116 |
'role' => array(),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8117 |
'tabindex' => array(),
|
8118 |
'title' => array(),
|
8119 |
'translate' => array(),
|
8120 |
'typeof' => array(),
|
8121 |
'validation-for' => array(),
|
8122 |
'visible-when-invalid' => array(
|
8123 |
-
|
8124 |
),
|
8125 |
'vocab' => array(),
|
8126 |
-
|
8127 |
);
|
8128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8129 |
public static function get_allowed_tags() {
|
8130 |
return self::$allowed_tags;
|
8131 |
}
|
8132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8133 |
public static function get_allowed_attributes() {
|
8134 |
return self::$globally_allowed_attrs;
|
8135 |
}
|
8136 |
|
|
|
|
|
|
|
|
|
|
|
|
|
8137 |
public static function get_layout_attributes() {
|
8138 |
return self::$layout_allowed_attrs;
|
8139 |
}
|
8140 |
|
8141 |
}
|
8142 |
-
|
8143 |
-
?>
|
8 |
* Note: This file only contains tags that are relevant to the `body` of
|
9 |
* an AMP page. To include additional elements modify the variable
|
10 |
* `mandatory_parent_blacklist` in the amp_wp_build.py script.
|
11 |
+
*
|
12 |
+
* phpcs:ignoreFile
|
13 |
*/
|
14 |
class AMP_Allowed_Tags_Generated {
|
15 |
|
16 |
+
private static $spec_file_revision = 595;
|
17 |
+
private static $minimum_validator_revision_required = 322;
|
18 |
|
19 |
private static $allowed_tags = array(
|
20 |
'a' => array(
|
25 |
'download' => array(),
|
26 |
'href' => array(
|
27 |
'blacklisted_value_regex' => '__amp_source_origin',
|
28 |
+
'value_url' => array(
|
29 |
+
'allow_empty' => true,
|
30 |
+
'allow_relative' => true,
|
31 |
+
'allowed_protocol' => array(
|
32 |
+
'ftp',
|
33 |
+
'geo',
|
34 |
+
'http',
|
35 |
+
'https',
|
36 |
+
'mailto',
|
37 |
+
'maps',
|
38 |
+
'bbmi',
|
39 |
+
'fb-messenger',
|
40 |
+
'intent',
|
41 |
+
'line',
|
42 |
+
'skype',
|
43 |
+
'sms',
|
44 |
+
'snapchat',
|
45 |
+
'tel',
|
46 |
+
'tg',
|
47 |
+
'threema',
|
48 |
+
'twitter',
|
49 |
+
'viber',
|
50 |
+
'whatsapp',
|
51 |
+
),
|
52 |
),
|
53 |
),
|
54 |
'hreflang' => array(),
|
55 |
'media' => array(),
|
56 |
'name' => array(),
|
57 |
+
'referrerpolicy' => array(),
|
58 |
'rel' => array(
|
59 |
'blacklisted_value_regex' => '(^|\\s)(components|dns-prefetch|import|manifest|preconnect|prefetch|preload|prerender|serviceworker|stylesheet|subresource|)(\\s|$)',
|
60 |
),
|
68 |
),
|
69 |
),
|
70 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
71 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#links',
|
72 |
),
|
|
|
73 |
),
|
74 |
),
|
75 |
'abbr' => array(
|
76 |
array(
|
77 |
'attr_spec_list' => array(),
|
78 |
'tag_spec' => array(),
|
|
|
79 |
),
|
80 |
),
|
81 |
'acronym' => array(
|
82 |
array(
|
83 |
'attr_spec_list' => array(),
|
84 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
85 |
),
|
86 |
),
|
87 |
'address' => array(
|
88 |
array(
|
89 |
'attr_spec_list' => array(),
|
90 |
'tag_spec' => array(),
|
|
|
91 |
),
|
92 |
),
|
93 |
'amp-3q-player' => array(
|
105 |
),
|
106 |
),
|
107 |
'tag_spec' => array(
|
108 |
+
'requires_extension' => array(
|
109 |
+
'amp-3q-player',
|
110 |
),
|
111 |
),
|
|
|
112 |
),
|
113 |
),
|
114 |
'amp-accordion' => array(
|
117 |
'disable-session-states' => array(
|
118 |
'value' => '',
|
119 |
),
|
120 |
+
'expand-single-section' => array(
|
121 |
+
'value' => '',
|
122 |
+
),
|
123 |
),
|
124 |
'tag_spec' => array(
|
125 |
+
'requires_extension' => array(
|
126 |
+
'amp-accordion',
|
127 |
+
),
|
128 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-accordion',
|
129 |
),
|
|
|
130 |
),
|
131 |
),
|
132 |
'amp-ad' => array(
|
138 |
'noloading' => array(
|
139 |
'value' => '',
|
140 |
),
|
141 |
+
'rtc-config' => array(),
|
142 |
'src' => array(
|
143 |
'blacklisted_value_regex' => '__amp_source_origin',
|
144 |
+
'value_url' => array(
|
145 |
+
'allow_relative' => true,
|
146 |
+
'allowed_protocol' => array(
|
147 |
+
'https',
|
148 |
+
),
|
149 |
),
|
150 |
),
|
151 |
+
'template' => array(),
|
152 |
'type' => array(
|
153 |
'mandatory' => true,
|
154 |
),
|
155 |
),
|
156 |
'tag_spec' => array(
|
157 |
+
'also_requires_tag_warning' => array(
|
158 |
+
'amp-ad extension .js script',
|
159 |
+
),
|
160 |
'disallowed_ancestor' => array(
|
161 |
+
'amp-app-banner',
|
162 |
),
|
163 |
+
'requires_extension' => array(
|
164 |
+
'amp-ad',
|
165 |
),
|
166 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad',
|
167 |
),
|
|
|
168 |
),
|
169 |
array(
|
170 |
'attr_spec_list' => array(
|
171 |
'alt' => array(),
|
172 |
'data-multi-size' => array(
|
173 |
+
'dispatch_key' => 2,
|
174 |
'mandatory' => true,
|
175 |
+
'value' => '',
|
176 |
),
|
177 |
'json' => array(),
|
178 |
'media' => array(),
|
179 |
'noloading' => array(
|
180 |
'value' => '',
|
181 |
),
|
182 |
+
'rtc-config' => array(),
|
183 |
'src' => array(
|
184 |
'blacklisted_value_regex' => '__amp_source_origin',
|
185 |
+
'value_url' => array(
|
186 |
+
'allow_relative' => true,
|
187 |
+
'allowed_protocol' => array(
|
188 |
+
'https',
|
189 |
+
),
|
190 |
),
|
191 |
),
|
192 |
'type' => array(
|
194 |
),
|
195 |
),
|
196 |
'tag_spec' => array(
|
197 |
+
'also_requires_tag_warning' => array(
|
198 |
+
'amp-ad extension .js script',
|
199 |
+
),
|
200 |
'disallowed_ancestor' => array(
|
201 |
+
'amp-app-banner',
|
202 |
+
'amp-carousel',
|
203 |
+
'amp-fx-flying-carpet',
|
204 |
+
'amp-lightbox',
|
205 |
+
'amp-sticky-ad',
|
206 |
),
|
207 |
+
'requires_extension' => array(
|
208 |
+
'amp-ad',
|
209 |
),
|
210 |
'spec_name' => 'amp-ad with data-multi-size attribute',
|
211 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad',
|
212 |
),
|
|
|
213 |
),
|
|
|
|
|
214 |
array(
|
215 |
'attr_spec_list' => array(
|
216 |
+
'alt' => array(),
|
217 |
+
'data-enable-refresh' => array(
|
218 |
+
'dispatch_key' => 2,
|
219 |
'mandatory' => true,
|
220 |
+
'value' => '',
|
221 |
),
|
222 |
+
'json' => array(),
|
223 |
'media' => array(),
|
224 |
'noloading' => array(
|
225 |
'value' => '',
|
226 |
),
|
227 |
+
'src' => array(
|
228 |
+
'blacklisted_value_regex' => '__amp_source_origin',
|
229 |
+
'value_url' => array(
|
230 |
+
'allow_relative' => true,
|
231 |
+
'allowed_protocol' => array(
|
232 |
+
'https',
|
233 |
+
),
|
234 |
+
),
|
235 |
+
),
|
236 |
+
'type' => array(
|
237 |
+
'mandatory' => true,
|
238 |
+
),
|
239 |
),
|
240 |
'tag_spec' => array(
|
241 |
+
'also_requires_tag_warning' => array(
|
242 |
+
'amp-ad extension .js script',
|
243 |
+
),
|
244 |
+
'disallowed_ancestor' => array(
|
245 |
+
'amp-app-banner',
|
246 |
+
'amp-fx-flying-carpet',
|
247 |
+
'amp-lightbox',
|
248 |
+
),
|
249 |
+
'requires_extension' => array(
|
250 |
+
'amp-ad',
|
251 |
),
|
252 |
+
'spec_name' => 'amp-ad with data-enable-refresh attribute',
|
253 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad',
|
254 |
),
|
|
|
255 |
),
|
256 |
),
|
257 |
'amp-analytics' => array(
|
259 |
'attr_spec_list' => array(
|
260 |
'config' => array(
|
261 |
'blacklisted_value_regex' => '__amp_source_origin',
|
262 |
+
'value_url' => array(
|
263 |
+
'allow_empty' => true,
|
264 |
+
'allow_relative' => true,
|
265 |
+
'allowed_protocol' => array(
|
266 |
+
'https',
|
267 |
+
),
|
268 |
),
|
269 |
),
|
270 |
'type' => array(),
|
271 |
),
|
272 |
'tag_spec' => array(
|
273 |
+
'requires_extension' => array(
|
274 |
+
'amp-analytics',
|
|
|
275 |
),
|
276 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-analytics',
|
277 |
),
|
|
|
278 |
),
|
279 |
),
|
280 |
'amp-anim' => array(
|
293 |
),
|
294 |
'blacklisted_value_regex' => '__amp_source_origin',
|
295 |
'mandatory' => true,
|
296 |
+
'value_url' => array(
|
297 |
+
'allow_relative' => true,
|
298 |
+
'allowed_protocol' => array(
|
299 |
+
'data',
|
300 |
+
'http',
|
301 |
+
'https',
|
302 |
+
),
|
303 |
),
|
304 |
),
|
305 |
),
|
306 |
'tag_spec' => array(
|
307 |
+
'requires_extension' => array(
|
308 |
+
'amp-anim',
|
|
|
309 |
),
|
310 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-anim',
|
311 |
),
|
|
|
312 |
),
|
313 |
),
|
314 |
'amp-animation' => array(
|
323 |
),
|
324 |
),
|
325 |
'tag_spec' => array(
|
326 |
+
'requires_extension' => array(
|
327 |
+
'amp-animation',
|
|
|
328 |
),
|
329 |
),
|
|
|
330 |
),
|
331 |
),
|
332 |
'amp-apester-media' => array(
|
333 |
array(
|
334 |
'attr_spec_list' => array(
|
335 |
'data-apester-channel-token' => array(
|
336 |
+
'value_regex' => '[0-9a-zA-Z]+',
|
337 |
),
|
338 |
'data-apester-media-id' => array(
|
339 |
+
'value_regex' => '[0-9a-zA-Z]+',
|
340 |
),
|
341 |
'media' => array(),
|
342 |
'noloading' => array(
|
344 |
),
|
345 |
),
|
346 |
'tag_spec' => array(
|
347 |
+
'requires_extension' => array(
|
348 |
+
'amp-apester-media',
|
349 |
),
|
350 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-apester-media',
|
351 |
),
|
|
|
352 |
),
|
353 |
),
|
354 |
'amp-app-banner' => array(
|
363 |
),
|
364 |
),
|
365 |
'tag_spec' => array(
|
|
|
|
|
|
|
366 |
'mandatory_parent' => 'body',
|
367 |
+
'requires_extension' => array(
|
368 |
+
'amp-app-banner',
|
369 |
+
),
|
370 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-app-banner',
|
371 |
'unique' => true,
|
372 |
),
|
|
|
373 |
),
|
374 |
),
|
375 |
'amp-audio' => array(
|
393 |
'noloading' => array(
|
394 |
'value' => '',
|
395 |
),
|
396 |
+
'preload' => array(
|
397 |
+
'value_regex_casei' => '(auto|metadata|none|)',
|
398 |
+
),
|
399 |
'src' => array(
|
400 |
'blacklisted_value_regex' => '__amp_source_origin',
|
401 |
+
'value_url' => array(
|
402 |
+
'allow_relative' => true,
|
403 |
+
'allowed_protocol' => array(
|
404 |
+
'https',
|
405 |
+
),
|
406 |
),
|
407 |
),
|
408 |
),
|
409 |
'tag_spec' => array(
|
410 |
'disallowed_ancestor' => array(
|
411 |
+
'amp-story',
|
412 |
),
|
413 |
+
'requires_extension' => array(
|
414 |
+
'amp-audio',
|
415 |
),
|
416 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
417 |
),
|
|
|
418 |
),
|
419 |
array(
|
420 |
'attr_spec_list' => array(
|
439 |
),
|
440 |
'src' => array(
|
441 |
'blacklisted_value_regex' => '__amp_source_origin',
|
442 |
+
'value_url' => array(
|
443 |
+
'allow_relative' => true,
|
444 |
+
'allowed_protocol' => array(
|
445 |
+
'https',
|
446 |
+
),
|
447 |
),
|
448 |
),
|
449 |
),
|
450 |
'tag_spec' => array(
|
|
|
|
|
|
|
451 |
'mandatory_ancestor' => 'amp-story',
|
452 |
+
'requires_extension' => array(
|
453 |
+
'amp-audio',
|
454 |
+
),
|
455 |
'spec_name' => 'amp-story >> amp-audio',
|
456 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
457 |
),
|
|
|
458 |
),
|
459 |
+
),
|
460 |
+
'amp-auto-ads' => array(
|
461 |
array(
|
462 |
'attr_spec_list' => array(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
463 |
'media' => array(),
|
|
|
|
|
|
|
464 |
'noloading' => array(
|
465 |
'value' => '',
|
466 |
),
|
467 |
+
'type' => array(
|
468 |
+
'mandatory' => true,
|
|
|
|
|
|
|
|
|
469 |
),
|
470 |
),
|
471 |
'tag_spec' => array(
|
472 |
+
'mandatory_parent' => 'body',
|
473 |
+
'requires_extension' => array(
|
474 |
+
'amp-auto-ads',
|
475 |
),
|
476 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-auto-ads',
|
|
|
477 |
),
|
|
|
478 |
),
|
479 |
),
|
480 |
+
'amp-beopinion' => array(
|
481 |
array(
|
482 |
'attr_spec_list' => array(
|
483 |
+
'data-account' => array(
|
484 |
+
'mandatory' => true,
|
485 |
+
'value_regex_casei' => '[0-9a-f]{24}',
|
486 |
+
),
|
487 |
+
'data-content' => array(
|
488 |
+
'value_regex_casei' => '[0-9a-f]{24}',
|
489 |
+
),
|
490 |
+
'data-my-content' => array(
|
491 |
+
'value_regex' => '0|1',
|
492 |
+
),
|
493 |
+
'data-name' => array(),
|
494 |
'media' => array(),
|
495 |
'noloading' => array(
|
496 |
'value' => '',
|
497 |
),
|
498 |
+
),
|
499 |
+
'tag_spec' => array(
|
500 |
+
'requires_extension' => array(
|
501 |
+
'amp-beopinion',
|
502 |
+
),
|
503 |
+
),
|
504 |
+
),
|
505 |
+
),
|
506 |
+
'amp-bind-macro' => array(
|
507 |
+
array(
|
508 |
+
'attr_spec_list' => array(
|
509 |
+
'arguments' => array(),
|
510 |
+
'expression' => array(
|
511 |
+
'mandatory' => true,
|
512 |
+
),
|
513 |
+
'id' => array(
|
514 |
'mandatory' => true,
|
515 |
),
|
516 |
),
|
517 |
'tag_spec' => array(
|
518 |
+
'requires_extension' => array(
|
519 |
+
'amp-bind',
|
520 |
),
|
521 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-bind',
|
522 |
+
),
|
523 |
+
),
|
524 |
+
),
|
525 |
+
'amp-bodymovin-animation' => array(
|
526 |
+
array(
|
527 |
+
'attr_spec_list' => array(
|
528 |
+
'loop' => array(
|
529 |
+
'value_regex_casei' => '(false|number|true)',
|
530 |
+
),
|
531 |
+
'noautoplay' => array(
|
532 |
+
'value' => '',
|
533 |
+
),
|
534 |
+
'src' => array(
|
535 |
+
'mandatory' => true,
|
536 |
+
'value_url' => array(
|
537 |
+
'allow_relative' => false,
|
538 |
+
'allowed_protocol' => array(
|
539 |
+
'https',
|
540 |
+
),
|
541 |
+
),
|
542 |
+
),
|
543 |
+
),
|
544 |
+
'tag_spec' => array(
|
545 |
+
'requires_extension' => array(
|
546 |
+
'amp-bodymovin-animation',
|
547 |
+
),
|
548 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-bodymovin-animation',
|
549 |
),
|
|
|
550 |
),
|
551 |
),
|
552 |
'amp-brid-player' => array(
|
576 |
),
|
577 |
),
|
578 |
'tag_spec' => array(
|
579 |
+
'requires_extension' => array(
|
580 |
+
'amp-brid-player',
|
581 |
),
|
582 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-brid-player',
|
583 |
),
|
|
|
584 |
),
|
585 |
),
|
586 |
'amp-brightcove' => array(
|
601 |
),
|
602 |
),
|
603 |
'tag_spec' => array(
|
604 |
+
'requires_extension' => array(
|
605 |
+
'amp-brightcove',
|
606 |
),
|
607 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-brightcove',
|
608 |
),
|
609 |
+
),
|
610 |
+
),
|
611 |
+
'amp-byside-content' => array(
|
612 |
+
array(
|
613 |
+
'attr_spec_list' => array(
|
614 |
+
'data-label' => array(
|
615 |
+
'mandatory' => true,
|
616 |
+
),
|
617 |
+
'data-webcare-id' => array(
|
618 |
+
'mandatory' => true,
|
619 |
+
),
|
620 |
+
'media' => array(),
|
621 |
+
'noloading' => array(
|
622 |
+
'value' => '',
|
623 |
+
),
|
624 |
+
),
|
625 |
+
'tag_spec' => array(
|
626 |
+
'requires_extension' => array(
|
627 |
+
'amp-byside-content',
|
628 |
+
),
|
629 |
+
),
|
630 |
),
|
631 |
),
|
632 |
'amp-call-tracking' => array(
|
635 |
'config' => array(
|
636 |
'blacklisted_value_regex' => '__amp_source_origin',
|
637 |
'mandatory' => true,
|
638 |
+
'value_url' => array(
|
639 |
+
'allow_relative' => false,
|
640 |
+
'allowed_protocol' => array(
|
641 |
+
'https',
|
642 |
+
),
|
643 |
),
|
644 |
),
|
645 |
'media' => array(),
|
648 |
),
|
649 |
),
|
650 |
'tag_spec' => array(
|
651 |
+
'requires_extension' => array(
|
652 |
+
'amp-call-tracking',
|
653 |
),
|
654 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-call-tracking',
|
655 |
),
|
|
|
656 |
),
|
657 |
),
|
658 |
'amp-carousel' => array(
|
672 |
'dots' => array(
|
673 |
'value' => '',
|
674 |
),
|
675 |
+
'lightbox' => array(),
|
676 |
+
'lightbox-exclude' => array(
|
677 |
+
'value' => '',
|
678 |
+
),
|
679 |
+
'lightbox-thumbnail-id' => array(
|
680 |
+
'value_regex_casei' => '^[a-z][a-z\\d_-]*',
|
681 |
+
),
|
682 |
'loop' => array(
|
683 |
'value' => '',
|
684 |
),
|
691 |
),
|
692 |
),
|
693 |
'tag_spec' => array(
|
694 |
+
'requires_extension' => array(
|
695 |
+
'amp-carousel',
|
696 |
+
),
|
697 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-carousel',
|
698 |
),
|
699 |
+
),
|
700 |
+
),
|
701 |
+
'amp-consent' => array(
|
702 |
+
array(
|
703 |
+
'attr_spec_list' => array(
|
704 |
+
'media' => array(),
|
705 |
+
'noloading' => array(
|
706 |
+
'value' => '',
|
707 |
+
),
|
708 |
+
),
|
709 |
+
'tag_spec' => array(
|
710 |
+
'requires_extension' => array(
|
711 |
+
'amp-consent',
|
712 |
+
),
|
713 |
+
'unique' => true,
|
714 |
+
),
|
715 |
),
|
716 |
),
|
717 |
'amp-dailymotion' => array(
|
749 |
),
|
750 |
),
|
751 |
'tag_spec' => array(
|
752 |
+
'requires_extension' => array(
|
753 |
+
'amp-dailymotion',
|
754 |
),
|
755 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-dailymotion',
|
756 |
),
|
|
|
757 |
),
|
758 |
),
|
759 |
+
'amp-date-picker' => array(
|
760 |
array(
|
761 |
'attr_spec_list' => array(
|
762 |
+
'allow-blocked-ranges' => array(
|
763 |
+
'value' => '',
|
764 |
+
),
|
765 |
+
'blocked' => array(),
|
766 |
+
'day-size' => array(
|
767 |
+
'value_regex' => '[0-9]+',
|
768 |
+
),
|
769 |
+
'first-day-of-week' => array(
|
770 |
+
'value_regex' => '[0-6]',
|
771 |
+
),
|
772 |
+
'format' => array(),
|
773 |
+
'fullscreen' => array(
|
774 |
+
'value' => '',
|
775 |
+
),
|
776 |
+
'highlighted' => array(),
|
777 |
+
'input-selector' => array(),
|
778 |
+
'locale' => array(),
|
779 |
+
'max' => array(),
|
780 |
'media' => array(),
|
781 |
+
'min' => array(),
|
782 |
+
'mode' => array(
|
783 |
+
'value_casei' => 'static',
|
784 |
+
),
|
785 |
+
'month-format' => array(),
|
786 |
'noloading' => array(
|
787 |
'value' => '',
|
788 |
),
|
789 |
+
'number-of-months' => array(
|
790 |
+
'value_regex' => '[0-9]+',
|
791 |
+
),
|
792 |
+
'open-after-clear' => array(
|
793 |
+
'value' => '',
|
794 |
+
),
|
795 |
+
'open-after-select' => array(
|
796 |
+
'value' => '',
|
797 |
+
),
|
798 |
'src' => array(
|
799 |
'blacklisted_value_regex' => '__amp_source_origin',
|
800 |
+
'value_url' => array(
|
801 |
+
'allow_relative' => true,
|
802 |
+
'allowed_protocol' => array(
|
803 |
+
'https',
|
804 |
+
),
|
805 |
),
|
806 |
),
|
807 |
'type' => array(
|
808 |
+
'value_casei' => 'single',
|
809 |
),
|
810 |
+
'week-day-format' => array(),
|
811 |
),
|
812 |
'tag_spec' => array(
|
813 |
+
'requires_extension' => array(
|
814 |
+
'amp-date-picker',
|
815 |
),
|
816 |
+
'spec_name' => 'amp-date-picker[type=single][mode=static]',
|
|
|
|
|
|
|
817 |
),
|
|
|
818 |
),
|
819 |
array(
|
820 |
'attr_spec_list' => array(
|
821 |
+
'allow-blocked-ranges' => array(
|
|
|
|
|
822 |
'value' => '',
|
823 |
),
|
824 |
+
'blocked' => array(),
|
825 |
+
'day-size' => array(
|
826 |
+
'value_regex' => '[0-9]+',
|
827 |
+
),
|
828 |
+
'first-day-of-week' => array(
|
829 |
+
'value_regex' => '[0-6]',
|
830 |
+
),
|
831 |
+
'format' => array(),
|
832 |
+
'highlighted' => array(),
|
833 |
+
'input-selector' => array(),
|
834 |
+
'locale' => array(),
|
835 |
+
'max' => array(),
|
836 |
'media' => array(),
|
837 |
+
'min' => array(),
|
838 |
+
'mode' => array(
|
839 |
+
'mandatory' => true,
|
840 |
+
'value_casei' => 'overlay',
|
841 |
+
),
|
842 |
+
'month-format' => array(),
|
843 |
'noloading' => array(
|
844 |
'value' => '',
|
845 |
),
|
846 |
+
'number-of-months' => array(
|
847 |
+
'value_regex' => '[0-9]+',
|
848 |
+
),
|
849 |
+
'open-after-clear' => array(
|
850 |
+
'value' => '',
|
851 |
+
),
|
852 |
+
'open-after-select' => array(
|
853 |
+
'value' => '',
|
854 |
+
),
|
855 |
'src' => array(
|
856 |
'blacklisted_value_regex' => '__amp_source_origin',
|
857 |
+
'value_url' => array(
|
858 |
+
'allow_relative' => true,
|
859 |
+
'allowed_protocol' => array(
|
860 |
+
'https',
|
861 |
+
),
|
862 |
),
|
863 |
),
|
864 |
'type' => array(
|
865 |
+
'value_casei' => 'single',
|
866 |
),
|
867 |
+
'week-day-format' => array(),
|
868 |
),
|
869 |
'tag_spec' => array(
|
870 |
+
'requires_extension' => array(
|
871 |
+
'amp-date-picker',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
872 |
),
|
873 |
+
'spec_name' => 'amp-date-picker[type=single][mode=overlay]',
|
|
|
874 |
),
|
|
|
875 |
),
|
|
|
|
|
876 |
array(
|
877 |
'attr_spec_list' => array(
|
878 |
+
'allow-blocked-ranges' => array(
|
879 |
+
'value' => '',
|
880 |
+
),
|
881 |
+
'blocked' => array(),
|
882 |
+
'day-size' => array(
|
883 |
+
'value_regex' => '[0-9]+',
|
884 |
+
),
|
885 |
+
'end-input-selector' => array(),
|
886 |
+
'first-day-of-week' => array(
|
887 |
+
'value_regex' => '[0-6]',
|
888 |
+
),
|
889 |
+
'format' => array(),
|
890 |
+
'fullscreen' => array(
|
891 |
+
'value' => '',
|
892 |
),
|
893 |
+
'highlighted' => array(),
|
894 |
+
'locale' => array(),
|
895 |
+
'max' => array(),
|
896 |
'media' => array(),
|
897 |
+
'min' => array(),
|
898 |
+
'mode' => array(
|
899 |
+
'value_casei' => 'static',
|
900 |
+
),
|
901 |
+
'month-format' => array(),
|
902 |
'noloading' => array(
|
903 |
'value' => '',
|
904 |
),
|
905 |
+
'number-of-months' => array(
|
906 |
+
'value_regex' => '[0-9]+',
|
|
|
|
|
907 |
),
|
908 |
+
'open-after-clear' => array(
|
909 |
+
'value' => '',
|
910 |
+
),
|
911 |
+
'open-after-select' => array(
|
912 |
+
'value' => '',
|
913 |
+
),
|
914 |
+
'src' => array(
|
915 |
+
'blacklisted_value_regex' => '__amp_source_origin',
|
916 |
+
'value_url' => array(
|
917 |
+
'allow_relative' => true,
|
918 |
+
'allowed_protocol' => array(
|
919 |
+
'https',
|
920 |
+
),
|
921 |
+
),
|
922 |
+
),
|
923 |
+
'start-input-selector' => array(),
|
924 |
+
'type' => array(
|
925 |
+
'mandatory' => true,
|
926 |
+
'value_casei' => 'range',
|
927 |
+
),
|
928 |
+
'week-day-format' => array(),
|
929 |
+
),
|
930 |
+
'tag_spec' => array(
|
931 |
+
'requires_extension' => array(
|
932 |
+
'amp-date-picker',
|
933 |
+
),
|
934 |
+
'spec_name' => 'amp-date-picker[type=range][mode=static]',
|
935 |
+
),
|
936 |
+
),
|
937 |
+
array(
|
938 |
+
'attr_spec_list' => array(
|
939 |
+
'allow-blocked-ranges' => array(
|
940 |
+
'value' => '',
|
941 |
+
),
|
942 |
+
'blocked' => array(),
|
943 |
+
'day-size' => array(
|
944 |
+
'value_regex' => '[0-9]+',
|
945 |
+
),
|
946 |
+
'end-input-selector' => array(),
|
947 |
+
'first-day-of-week' => array(
|
948 |
+
'value_regex' => '[0-6]',
|
949 |
+
),
|
950 |
+
'format' => array(),
|
951 |
+
'highlighted' => array(),
|
952 |
+
'locale' => array(),
|
953 |
+
'max' => array(),
|
954 |
+
'media' => array(),
|
955 |
+
'min' => array(),
|
956 |
+
'mode' => array(
|
957 |
+
'mandatory' => true,
|
958 |
+
'value_casei' => 'overlay',
|
959 |
+
),
|
960 |
+
'month-format' => array(),
|
961 |
+
'noloading' => array(
|
962 |
+
'value' => '',
|
963 |
+
),
|
964 |
+
'number-of-months' => array(
|
965 |
+
'value_regex' => '[0-9]+',
|
966 |
+
),
|
967 |
+
'open-after-clear' => array(
|
968 |
+
'value' => '',
|
969 |
+
),
|
970 |
+
'open-after-select' => array(
|
971 |
+
'value' => '',
|
972 |
+
),
|
973 |
+
'src' => array(
|
974 |
+
'blacklisted_value_regex' => '__amp_source_origin',
|
975 |
+
'value_url' => array(
|
976 |
+
'allow_relative' => true,
|
977 |
+
'allowed_protocol' => array(
|
978 |
+
'https',
|
979 |
+
),
|
980 |
+
),
|
981 |
+
),
|
982 |
+
'start-input-selector' => array(),
|
983 |
+
'type' => array(
|
984 |
+
'mandatory' => true,
|
985 |
+
'value_casei' => 'range',
|
986 |
+
),
|
987 |
+
'week-day-format' => array(),
|
988 |
+
),
|
989 |
+
'tag_spec' => array(
|
990 |
+
'requires_extension' => array(
|
991 |
+
'amp-date-picker',
|
992 |
+
),
|
993 |
+
'spec_name' => 'amp-date-picker[type=range][mode=overlay]',
|
994 |
+
),
|
995 |
+
),
|
996 |
+
),
|
997 |
+
'amp-document-recommendations' => array(
|
998 |
+
array(
|
999 |
+
'attr_spec_list' => array(),
|
1000 |
+
'tag_spec' => array(
|
1001 |
+
'mandatory_parent' => 'body',
|
1002 |
+
'requires_extension' => array(
|
1003 |
+
'amp-document-recommendations',
|
1004 |
+
),
|
1005 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-document-recommendations',
|
1006 |
+
),
|
1007 |
+
),
|
1008 |
+
),
|
1009 |
+
'amp-embed' => array(
|
1010 |
+
array(
|
1011 |
+
'attr_spec_list' => array(
|
1012 |
+
'alt' => array(),
|
1013 |
+
'json' => array(),
|
1014 |
+
'media' => array(),
|
1015 |
+
'noloading' => array(
|
1016 |
+
'value' => '',
|
1017 |
+
),
|
1018 |
+
'rtc-config' => array(),
|
1019 |
+
'src' => array(
|
1020 |
+
'blacklisted_value_regex' => '__amp_source_origin',
|
1021 |
+
'value_url' => array(
|
1022 |
+
'allow_relative' => true,
|
1023 |
+
'allowed_protocol' => array(
|
1024 |
+
'https',
|
1025 |
+
),
|
1026 |
+
),
|
1027 |
+
),
|
1028 |
+
'template' => array(),
|
1029 |
+
'type' => array(
|
1030 |
+
'mandatory' => true,
|
1031 |
+
),
|
1032 |
+
),
|
1033 |
+
'tag_spec' => array(
|
1034 |
+
'also_requires_tag_warning' => array(
|
1035 |
+
'amp-ad extension .js script',
|
1036 |
+
),
|
1037 |
+
'disallowed_ancestor' => array(
|
1038 |
+
'amp-app-banner',
|
1039 |
+
),
|
1040 |
+
'requires_extension' => array(
|
1041 |
+
'amp-ad',
|
1042 |
+
),
|
1043 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad',
|
1044 |
+
),
|
1045 |
+
),
|
1046 |
+
array(
|
1047 |
+
'attr_spec_list' => array(
|
1048 |
+
'alt' => array(),
|
1049 |
+
'data-multi-size' => array(
|
1050 |
+
'dispatch_key' => 2,
|
1051 |
+
'mandatory' => true,
|
1052 |
+
'value' => '',
|
1053 |
+
),
|
1054 |
+
'json' => array(),
|
1055 |
+
'media' => array(),
|
1056 |
+
'noloading' => array(
|
1057 |
+
'value' => '',
|
1058 |
+
),
|
1059 |
+
'rtc-config' => array(),
|
1060 |
+
'src' => array(
|
1061 |
+
'blacklisted_value_regex' => '__amp_source_origin',
|
1062 |
+
'value_url' => array(
|
1063 |
+
'allow_relative' => true,
|
1064 |
+
'allowed_protocol' => array(
|
1065 |
+
'https',
|
1066 |
+
),
|
1067 |
+
),
|
1068 |
+
),
|
1069 |
+
'type' => array(
|
1070 |
+
'mandatory' => true,
|
1071 |
+
),
|
1072 |
+
),
|
1073 |
+
'tag_spec' => array(
|
1074 |
+
'also_requires_tag_warning' => array(
|
1075 |
+
'amp-ad extension .js script',
|
1076 |
+
),
|
1077 |
+
'disallowed_ancestor' => array(
|
1078 |
+
'amp-app-banner',
|
1079 |
+
'amp-carousel',
|
1080 |
+
'amp-fx-flying-carpet',
|
1081 |
+
'amp-lightbox',
|
1082 |
+
'amp-sticky-ad',
|
1083 |
+
),
|
1084 |
+
'requires_extension' => array(
|
1085 |
+
'amp-ad',
|
1086 |
+
),
|
1087 |
+
'spec_name' => 'amp-embed with data-multi-size attribute',
|
1088 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad',
|
1089 |
+
),
|
1090 |
+
),
|
1091 |
+
),
|
1092 |
+
'amp-experiment' => array(
|
1093 |
+
array(
|
1094 |
+
'attr_spec_list' => array(),
|
1095 |
+
'tag_spec' => array(
|
1096 |
+
'requires_extension' => array(
|
1097 |
+
'amp-experiment',
|
1098 |
+
),
|
1099 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-experiment',
|
1100 |
+
'unique' => true,
|
1101 |
+
),
|
1102 |
+
),
|
1103 |
+
),
|
1104 |
+
'amp-facebook' => array(
|
1105 |
+
array(
|
1106 |
+
'attr_spec_list' => array(
|
1107 |
+
'data-href' => array(
|
1108 |
+
'mandatory' => true,
|
1109 |
+
),
|
1110 |
+
'media' => array(),
|
1111 |
+
'noloading' => array(
|
1112 |
+
'value' => '',
|
1113 |
+
),
|
1114 |
+
),
|
1115 |
+
'tag_spec' => array(
|
1116 |
+
'requires_extension' => array(
|
1117 |
+
'amp-facebook',
|
1118 |
+
),
|
1119 |
+
),
|
1120 |
+
),
|
1121 |
),
|
1122 |
'amp-facebook-comments' => array(
|
1123 |
array(
|
1131 |
),
|
1132 |
),
|
1133 |
'tag_spec' => array(
|
1134 |
+
'requires_extension' => array(
|
1135 |
+
'amp-facebook-comments',
|
1136 |
),
|
1137 |
),
|
|
|
1138 |
),
|
1139 |
),
|
1140 |
'amp-facebook-like' => array(
|
1142 |
'attr_spec_list' => array(
|
1143 |
'data-href' => array(
|
1144 |
'mandatory' => true,
|
1145 |
+
'value_url' => array(
|
1146 |
+
'allow_relative' => false,
|
1147 |
+
'allowed_protocol' => array(
|
1148 |
+
'http',
|
1149 |
+
'https',
|
1150 |
+
),
|
1151 |
),
|
1152 |
),
|
1153 |
'media' => array(),
|
1156 |
),
|
1157 |
),
|
1158 |
'tag_spec' => array(
|
1159 |
+
'requires_extension' => array(
|
1160 |
+
'amp-facebook-like',
|
1161 |
+
),
|
1162 |
+
),
|
1163 |
+
),
|
1164 |
+
),
|
1165 |
+
'amp-facebook-page' => array(
|
1166 |
+
array(
|
1167 |
+
'attr_spec_list' => array(
|
1168 |
+
'data-href' => array(
|
1169 |
+
'mandatory' => true,
|
1170 |
+
'value_url' => array(
|
1171 |
+
'allow_relative' => false,
|
1172 |
+
'allowed_protocol' => array(
|
1173 |
+
'http',
|
1174 |
+
'https',
|
1175 |
+
),
|
1176 |
+
),
|
1177 |
+
),
|
1178 |
+
'media' => array(),
|
1179 |
+
'noloading' => array(
|
1180 |
+
'value' => '',
|
1181 |
+
),
|
1182 |
+
),
|
1183 |
+
'tag_spec' => array(
|
1184 |
+
'requires_extension' => array(
|
1185 |
+
'amp-facebook-page',
|
1186 |
),
|
1187 |
),
|
|
|
1188 |
),
|
1189 |
),
|
1190 |
'amp-fit-text' => array(
|
1197 |
'value' => '',
|
1198 |
),
|
1199 |
),
|
1200 |
+
'tag_spec' => array(
|
1201 |
+
'requires_extension' => array(
|
1202 |
+
'amp-fit-text',
|
1203 |
+
),
|
1204 |
+
),
|
1205 |
),
|
1206 |
),
|
1207 |
'amp-font' => array(
|
1226 |
),
|
1227 |
),
|
1228 |
'tag_spec' => array(
|
1229 |
+
'requires_extension' => array(
|
1230 |
+
'amp-font',
|
|
|
1231 |
),
|
1232 |
),
|
|
|
1233 |
),
|
1234 |
),
|
1235 |
'amp-fx-flying-carpet' => array(
|
1244 |
),
|
1245 |
),
|
1246 |
'tag_spec' => array(
|
1247 |
+
'requires_extension' => array(
|
1248 |
+
'amp-fx-flying-carpet',
|
1249 |
),
|
1250 |
),
|
|
|
1251 |
),
|
1252 |
),
|
1253 |
'amp-gfycat' => array(
|
1265 |
),
|
1266 |
),
|
1267 |
'tag_spec' => array(
|
1268 |
+
'requires_extension' => array(
|
1269 |
+
'amp-gfycat',
|
1270 |
),
|
1271 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-gfycat',
|
1272 |
),
|
|
|
1273 |
),
|
1274 |
),
|
1275 |
'amp-gist' => array(
|
1284 |
),
|
1285 |
),
|
1286 |
'tag_spec' => array(
|
1287 |
+
'requires_extension' => array(
|
1288 |
+
'amp-gist',
|
1289 |
),
|
1290 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-gist',
|
1291 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1292 |
),
|
1293 |
),
|
1294 |
'amp-hulu' => array(
|
1303 |
),
|
1304 |
),
|
1305 |
'tag_spec' => array(
|
1306 |
+
'requires_extension' => array(
|
1307 |
+
'amp-hulu',
|
1308 |
),
|
1309 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-hulu',
|
1310 |
),
|
|
|
1311 |
),
|
1312 |
),
|
1313 |
'amp-iframe' => array(
|
1314 |
array(
|
1315 |
'attr_spec_list' => array(
|
1316 |
'[src]' => array(),
|
1317 |
+
'allow' => array(),
|
|
|
|
|
1318 |
'allowfullscreen' => array(
|
1319 |
'value' => '',
|
1320 |
),
|
1341 |
),
|
1342 |
'src' => array(
|
1343 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1344 |
+
'value_url' => array(
|
1345 |
+
'allow_relative' => true,
|
1346 |
+
'allowed_protocol' => array(
|
1347 |
+
'data',
|
1348 |
+
'https',
|
1349 |
+
),
|
1350 |
),
|
1351 |
),
|
1352 |
'srcdoc' => array(),
|
1353 |
),
|
1354 |
'tag_spec' => array(
|
1355 |
+
'requires_extension' => array(
|
1356 |
+
'amp-iframe',
|
1357 |
),
|
1358 |
),
|
|
|
1359 |
),
|
1360 |
),
|
1361 |
'amp-ima-video' => array(
|
1362 |
array(
|
1363 |
'attr_spec_list' => array(
|
1364 |
+
'autoplay' => array(
|
1365 |
+
'value' => '',
|
1366 |
+
),
|
1367 |
'data-src' => array(
|
1368 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1369 |
+
'value_url' => array(
|
1370 |
+
'allow_relative' => true,
|
1371 |
+
'allowed_protocol' => array(
|
1372 |
+
'https',
|
1373 |
+
),
|
1374 |
),
|
1375 |
),
|
1376 |
'data-tag' => array(
|
1377 |
'mandatory' => true,
|
1378 |
+
'value_url' => array(
|
1379 |
+
'allow_relative' => true,
|
1380 |
+
'allowed_protocol' => array(
|
1381 |
+
'https',
|
1382 |
+
),
|
1383 |
),
|
1384 |
),
|
1385 |
'media' => array(),
|
1388 |
),
|
1389 |
),
|
1390 |
'tag_spec' => array(
|
1391 |
+
'requires_extension' => array(
|
1392 |
+
'amp-ima-video',
|
1393 |
),
|
1394 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ima-video',
|
1395 |
),
|
|
|
1396 |
),
|
1397 |
),
|
1398 |
'amp-image-lightbox' => array(
|
1405 |
),
|
1406 |
),
|
1407 |
'tag_spec' => array(
|
1408 |
+
'requires_extension' => array(
|
1409 |
+
'amp-image-lightbox',
|
1410 |
),
|
1411 |
),
|
|
|
1412 |
),
|
1413 |
),
|
1414 |
'amp-img' => array(
|
1420 |
'[srcset]' => array(),
|
1421 |
'alt' => array(),
|
1422 |
'attribution' => array(),
|
1423 |
+
'lightbox' => array(),
|
1424 |
+
'lightbox-exclude' => array(
|
1425 |
+
'value' => '',
|
1426 |
+
),
|
1427 |
+
'lightbox-thumbnail-id' => array(
|
1428 |
+
'value_regex_casei' => '^[a-z][a-z\\d_-]*',
|
1429 |
+
),
|
1430 |
'media' => array(),
|
1431 |
'noloading' => array(
|
1432 |
'value' => '',
|
1438 |
),
|
1439 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1440 |
'mandatory' => true,
|
1441 |
+
'value_url' => array(
|
1442 |
+
'allow_relative' => true,
|
1443 |
+
'allowed_protocol' => array(
|
1444 |
+
'data',
|
1445 |
+
'http',
|
1446 |
+
'https',
|
1447 |
+
),
|
1448 |
),
|
1449 |
),
|
1450 |
),
|
1451 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
1452 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-img',
|
1453 |
),
|
|
|
1454 |
),
|
1455 |
),
|
1456 |
'amp-imgur' => array(
|
1465 |
),
|
1466 |
),
|
1467 |
'tag_spec' => array(
|
1468 |
+
'requires_extension' => array(
|
1469 |
+
'amp-imgur',
|
1470 |
),
|
1471 |
),
|
|
|
1472 |
),
|
1473 |
),
|
1474 |
'amp-instagram' => array(
|
1484 |
),
|
1485 |
),
|
1486 |
'tag_spec' => array(
|
1487 |
+
'requires_extension' => array(
|
1488 |
+
'amp-instagram',
|
1489 |
),
|
1490 |
),
|
|
|
1491 |
),
|
1492 |
),
|
1493 |
'amp-install-serviceworker' => array(
|
1495 |
'attr_spec_list' => array(
|
1496 |
'data-iframe-src' => array(
|
1497 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1498 |
+
'value_url' => array(
|
1499 |
+
'allow_relative' => true,
|
1500 |
+
'allowed_protocol' => array(
|
1501 |
+
'https',
|
1502 |
+
),
|
1503 |
),
|
1504 |
),
|
1505 |
'src' => array(
|
1506 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1507 |
'mandatory' => true,
|
1508 |
+
'value_url' => array(
|
1509 |
+
'allow_relative' => true,
|
1510 |
+
'allowed_protocol' => array(
|
1511 |
+
'https',
|
1512 |
+
),
|
1513 |
),
|
1514 |
),
|
1515 |
),
|
1516 |
'tag_spec' => array(
|
1517 |
+
'requires_extension' => array(
|
1518 |
+
'amp-install-serviceworker',
|
1519 |
),
|
1520 |
),
|
|
|
1521 |
),
|
1522 |
),
|
1523 |
'amp-izlesene' => array(
|
1533 |
),
|
1534 |
),
|
1535 |
'tag_spec' => array(
|
1536 |
+
'requires_extension' => array(
|
1537 |
+
'amp-izlesene',
|
1538 |
),
|
1539 |
),
|
|
|
1540 |
),
|
1541 |
),
|
1542 |
'amp-jwplayer' => array(
|
1554 |
),
|
1555 |
),
|
1556 |
'tag_spec' => array(
|
1557 |
+
'requires_extension' => array(
|
1558 |
+
'amp-jwplayer',
|
1559 |
),
|
1560 |
),
|
|
|
1561 |
),
|
1562 |
),
|
1563 |
'amp-kaltura-player' => array(
|
1572 |
),
|
1573 |
),
|
1574 |
'tag_spec' => array(
|
1575 |
+
'requires_extension' => array(
|
1576 |
+
'amp-kaltura-player',
|
1577 |
),
|
1578 |
),
|
|
|
1579 |
),
|
1580 |
),
|
1581 |
'amp-layout' => array(
|
1587 |
),
|
1588 |
),
|
1589 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
1590 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-layout',
|
1591 |
),
|
|
|
1592 |
),
|
1593 |
),
|
1594 |
'amp-lightbox' => array(
|
1603 |
'scrollable' => array(),
|
1604 |
),
|
1605 |
'tag_spec' => array(
|
1606 |
+
'requires_extension' => array(
|
1607 |
+
'amp-lightbox',
|
1608 |
),
|
1609 |
),
|
|
|
1610 |
),
|
1611 |
),
|
1612 |
'amp-list' => array(
|
1621 |
'noloading' => array(
|
1622 |
'value' => '',
|
1623 |
),
|
1624 |
+
'reset-on-refresh' => array(
|
1625 |
+
'value' => '',
|
1626 |
+
),
|
1627 |
'single-item' => array(),
|
1628 |
'src' => array(
|
1629 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1630 |
'mandatory' => true,
|
1631 |
+
'value_url' => array(
|
1632 |
+
'allow_relative' => true,
|
1633 |
+
'allowed_protocol' => array(
|
1634 |
+
'https',
|
1635 |
+
),
|
1636 |
),
|
1637 |
),
|
1638 |
'template' => array(),
|
1639 |
),
|
1640 |
'tag_spec' => array(
|
1641 |
+
'requires_extension' => array(
|
1642 |
+
'amp-list',
|
1643 |
),
|
1644 |
),
|
1645 |
+
),
|
1646 |
+
array(
|
1647 |
+
'attr_spec_list' => array(
|
1648 |
+
'[src]' => array(
|
1649 |
+
'mandatory' => true,
|
1650 |
+
),
|
1651 |
+
'credentials' => array(),
|
1652 |
+
'items' => array(),
|
1653 |
+
'max-items' => array(),
|
1654 |
+
'media' => array(),
|
1655 |
+
'noloading' => array(
|
1656 |
+
'value' => '',
|
1657 |
+
),
|
1658 |
+
'single-item' => array(),
|
1659 |
+
'src' => array(
|
1660 |
+
'blacklisted_value_regex' => '__amp_source_origin',
|
1661 |
+
'value_url' => array(
|
1662 |
+
'allow_relative' => true,
|
1663 |
+
'allowed_protocol' => array(
|
1664 |
+
'https',
|
1665 |
+
),
|
1666 |
+
),
|
1667 |
+
),
|
1668 |
+
'template' => array(),
|
1669 |
+
),
|
1670 |
+
'tag_spec' => array(
|
1671 |
+
'requires_extension' => array(
|
1672 |
+
'amp-list',
|
1673 |
+
),
|
1674 |
+
'spec_name' => 'AMP-LIST [SRC]',
|
1675 |
+
),
|
1676 |
),
|
1677 |
),
|
1678 |
'amp-live-list' => array(
|
1691 |
'id' => array(
|
1692 |
'mandatory' => true,
|
1693 |
),
|
1694 |
+
'sort' => array(
|
1695 |
+
'value_regex' => 'ascending',
|
1696 |
+
),
|
1697 |
),
|
1698 |
'tag_spec' => array(
|
1699 |
+
'requires_extension' => array(
|
1700 |
+
'amp-live-list',
|
1701 |
),
|
1702 |
),
|
|
|
1703 |
),
|
1704 |
),
|
1705 |
+
'amp-mathml' => array(
|
1706 |
array(
|
1707 |
'attr_spec_list' => array(
|
1708 |
+
'data-formula' => array(
|
1709 |
'mandatory' => true,
|
1710 |
),
|
1711 |
+
'inline' => array(),
|
1712 |
+
'media' => array(),
|
1713 |
+
'noloading' => array(
|
1714 |
+
'value' => '',
|
1715 |
+
),
|
1716 |
+
),
|
1717 |
+
'tag_spec' => array(
|
1718 |
+
'requires_extension' => array(
|
1719 |
+
'amp-mathml',
|
1720 |
+
),
|
1721 |
+
),
|
1722 |
+
),
|
1723 |
+
),
|
1724 |
+
'amp-nexxtv-player' => array(
|
1725 |
+
array(
|
1726 |
+
'attr_spec_list' => array(
|
1727 |
+
'data-client' => array(
|
1728 |
+
'mandatory' => true,
|
1729 |
+
),
|
1730 |
+
'data-mediaid' => array(
|
1731 |
'mandatory' => true,
|
1732 |
'value_regex' => '[^=/?:]+',
|
1733 |
),
|
1735 |
'value_regex' => 'api|static',
|
1736 |
),
|
1737 |
'data-origin' => array(
|
1738 |
+
'value_url' => array(
|
1739 |
+
'allow_empty' => true,
|
1740 |
+
'allowed_protocol' => array(
|
1741 |
+
'https',
|
1742 |
+
'http',
|
1743 |
+
),
|
1744 |
),
|
1745 |
),
|
|
|
1746 |
'data-streamtype' => array(
|
1747 |
'value_regex' => 'album|audio|live|playlist|playlist-marked|video',
|
1748 |
),
|
1752 |
),
|
1753 |
),
|
1754 |
'tag_spec' => array(
|
1755 |
+
'requires_extension' => array(
|
1756 |
+
'amp-nexxtv-player',
|
1757 |
),
|
1758 |
),
|
|
|
1759 |
),
|
1760 |
),
|
1761 |
'amp-o2-player' => array(
|
1773 |
),
|
1774 |
),
|
1775 |
'tag_spec' => array(
|
1776 |
+
'requires_extension' => array(
|
1777 |
+
'amp-o2-player',
|
1778 |
),
|
1779 |
),
|
|
|
1780 |
),
|
1781 |
),
|
1782 |
'amp-ooyala-player' => array(
|
1797 |
),
|
1798 |
),
|
1799 |
'tag_spec' => array(
|
1800 |
+
'requires_extension' => array(
|
1801 |
+
'amp-ooyala-player',
|
1802 |
),
|
1803 |
),
|
|
|
1804 |
),
|
1805 |
),
|
1806 |
'amp-pinterest' => array(
|
1807 |
array(
|
1808 |
'attr_spec_list' => array(
|
1809 |
+
'alt' => array(),
|
1810 |
'data-do' => array(
|
1811 |
'mandatory' => true,
|
1812 |
),
|
1816 |
),
|
1817 |
),
|
1818 |
'tag_spec' => array(
|
1819 |
+
'requires_extension' => array(
|
1820 |
+
'amp-pinterest',
|
1821 |
),
|
1822 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-pinterest',
|
1823 |
),
|
|
|
1824 |
),
|
1825 |
),
|
1826 |
'amp-pixel' => array(
|
1827 |
array(
|
1828 |
'attr_spec_list' => array(
|
1829 |
+
'allow-ssr-img' => array(),
|
1830 |
'media' => array(),
|
1831 |
'noloading' => array(
|
1832 |
'value' => '',
|
1837 |
'src' => array(
|
1838 |
'blacklisted_value_regex' => '__amp_source_origin',
|
1839 |
'mandatory' => true,
|
1840 |
+
'value_url' => array(
|
1841 |
+
'allow_empty' => true,
|
1842 |
+
'allow_relative' => true,
|
1843 |
+
'allowed_protocol' => array(
|
1844 |
+
'https',
|
1845 |
+
),
|
1846 |
),
|
1847 |
),
|
1848 |
),
|
1849 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
1850 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-pixel',
|
1851 |
),
|
|
|
1852 |
),
|
1853 |
),
|
1854 |
'amp-playbuzz' => array(
|
1871 |
'src' => array(),
|
1872 |
),
|
1873 |
'tag_spec' => array(
|
1874 |
+
'requires_extension' => array(
|
1875 |
+
'amp-playbuzz',
|
1876 |
),
|
1877 |
),
|
|
|
1878 |
),
|
1879 |
),
|
1880 |
'amp-position-observer' => array(
|
1893 |
),
|
1894 |
),
|
1895 |
'tag_spec' => array(
|
1896 |
+
'requires_extension' => array(
|
1897 |
+
'amp-position-observer',
|
|
|
1898 |
),
|
1899 |
),
|
|
|
1900 |
),
|
1901 |
),
|
1902 |
'amp-reach-player' => array(
|
1912 |
),
|
1913 |
),
|
1914 |
'tag_spec' => array(
|
1915 |
+
'requires_extension' => array(
|
1916 |
+
'amp-reach-player',
|
1917 |
),
|
1918 |
),
|
|
|
1919 |
),
|
1920 |
),
|
1921 |
'amp-reddit' => array(
|
1940 |
),
|
1941 |
),
|
1942 |
'tag_spec' => array(
|
1943 |
+
'requires_extension' => array(
|
1944 |
+
'amp-reddit',
|
1945 |
),
|
1946 |
),
|
1947 |
+
),
|
1948 |
+
),
|
1949 |
+
'amp-riddle-quiz' => array(
|
1950 |
+
array(
|
1951 |
+
'attr_spec_list' => array(
|
1952 |
+
'data-riddle-id' => array(
|
1953 |
+
'mandatory' => true,
|
1954 |
+
'value_regex' => '[0-9]+',
|
1955 |
+
),
|
1956 |
+
'media' => array(),
|
1957 |
+
'noloading' => array(
|
1958 |
+
'value' => '',
|
1959 |
+
),
|
1960 |
+
),
|
1961 |
+
'tag_spec' => array(
|
1962 |
+
'requires_extension' => array(
|
1963 |
+
'amp-riddle-quiz',
|
1964 |
+
),
|
1965 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-riddle-quiz',
|
1966 |
+
),
|
1967 |
),
|
1968 |
),
|
1969 |
'amp-selector' => array(
|
1970 |
array(
|
1971 |
'attr_spec_list' => array(
|
1972 |
+
'[disabled]' => array(),
|
1973 |
'[selected]' => array(),
|
1974 |
'disabled' => array(
|
1975 |
'value' => '',
|
1983 |
'value' => '',
|
1984 |
),
|
1985 |
'name' => array(
|
1986 |
+
'blacklisted_value_regex' => '(^|\\s)(__amp_\\S*|__count__|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|__noSuchMethod__|__parent__|__proto__|__AMP_\\S*|\\$p|\\$proxy|acceptCharset|addEventListener|appendChild|assignedSlot|attachShadow|baseURI|checkValidity|childElementCount|childNodes|classList|className|clientHeight|clientLeft|clientTop|clientWidth|compareDocumentPosition|computedName|computedRole|contentEditable|createShadowRoot|enqueAction|firstChild|firstElementChild|getAnimations|getAttribute|getAttributeNS|getAttributeNode|getAttributeNodeNS|getBoundingClientRect|getClientRects|getDestinationInsertionPoints|getElementsByClassName|getElementsByTagName|getElementsByTagNameNS|getRootNode|hasAttribute|hasAttributeNS|hasAttributes|hasChildNodes|hasPointerCapture|innerHTML|innerText|inputMode|insertAdjacentElement|insertAdjacentHTML|insertAdjacentText|isContentEditable|isDefaultNamespace|isEqualNode|isSameNode|lastChild|lastElementChild|lookupNamespaceURI|namespaceURI|nextElementSibling|nextSibling|nodeName|nodeType|nodeValue|offsetHeight|offsetLeft|offsetParent|offsetTop|offsetWidth|outerHTML|outerText|ownerDocument|parentElement|parentNode|previousElementSibling|previousSibling|querySelector|querySelectorAll|releasePointerCapture|removeAttribute|removeAttributeNS|removeAttributeNode|removeChild|removeEventListener|replaceChild|reportValidity|requestPointerLock|scrollHeight|scrollIntoView|scrollIntoViewIfNeeded|scrollLeft|scrollWidth|setAttribute|setAttributeNS|setAttributeNode|setAttributeNodeNS|setPointerCapture|shadowRoot|styleMap|tabIndex|tagName|textContent|toString|valueOf|(webkit|ms|moz|o)dropzone|(webkit|moz|ms|o)MatchesSelector|(webkit|moz|ms|o)RequestFullScreen|(webkit|moz|ms|o)RequestFullscreen)(\\s|$)',
|
1987 |
),
|
1988 |
'noloading' => array(
|
1989 |
'value' => '',
|
1991 |
),
|
1992 |
'tag_spec' => array(
|
1993 |
'disallowed_ancestor' => array(
|
1994 |
+
'amp-selector',
|
1995 |
),
|
1996 |
+
'requires_extension' => array(
|
1997 |
+
'amp-selector',
|
1998 |
),
|
1999 |
),
|
|
|
2000 |
),
|
2001 |
),
|
2002 |
'amp-sidebar' => array(
|
2011 |
),
|
2012 |
),
|
2013 |
'tag_spec' => array(
|
|
|
|
|
|
|
2014 |
'mandatory_parent' => 'body',
|
2015 |
+
'requires_extension' => array(
|
2016 |
+
'amp-sidebar',
|
2017 |
+
),
|
2018 |
),
|
|
|
2019 |
),
|
2020 |
),
|
2021 |
'amp-social-share' => array(
|
2023 |
'attr_spec_list' => array(
|
2024 |
'data-share-endpoint' => array(
|
2025 |
'blacklisted_value_regex' => '__amp_source_origin',
|
2026 |
+
'value_url' => array(
|
2027 |
+
'allow_relative' => false,
|
2028 |
+
'allowed_protocol' => array(
|
2029 |
+
'ftp',
|
2030 |
+
'http',
|
2031 |
+
'https',
|
2032 |
+
'mailto',
|
2033 |
+
'bbmi',
|
2034 |
+
'fb-messenger',
|
2035 |
+
'intent',
|
2036 |
+
'line',
|
2037 |
+
'skype',
|
2038 |
+
'sms',
|
2039 |
+
'snapchat',
|
2040 |
+
'tel',
|
2041 |
+
'tg',
|
2042 |
+
'threema',
|
2043 |
+
'viber',
|
2044 |
+
'whatsapp',
|
2045 |
+
),
|
2046 |
),
|
2047 |
),
|
2048 |
'media' => array(),
|
2054 |
),
|
2055 |
),
|
2056 |
'tag_spec' => array(
|
2057 |
+
'requires_extension' => array(
|
2058 |
+
'amp-social-share',
|
|
|
2059 |
),
|
2060 |
),
|
|
|
2061 |
),
|
2062 |
),
|
2063 |
'amp-soundcloud' => array(
|
2070 |
'value_regex' => '[0-9]+',
|
2071 |
),
|
2072 |
'data-secret-token' => array(
|
2073 |
+
'value_regex' => '[A-Za-z0-9_-]+',
|
2074 |
),
|
2075 |
'data-trackid' => array(
|
2076 |
'value_regex' => '[0-9]+',
|
2084 |
),
|
2085 |
),
|
2086 |
'tag_spec' => array(
|
2087 |
+
'requires_extension' => array(
|
2088 |
+
'amp-soundcloud',
|
2089 |
),
|
2090 |
),
|
|
|
2091 |
),
|
2092 |
),
|
2093 |
'amp-springboard-player' => array(
|
2120 |
),
|
2121 |
),
|
2122 |
'tag_spec' => array(
|
2123 |
+
'requires_extension' => array(
|
2124 |
+
'amp-springboard-player',
|
2125 |
),
|
2126 |
),
|
|
|
2127 |
),
|
2128 |
),
|
2129 |
'amp-state' => array(
|
2134 |
'id' => array(
|
2135 |
'mandatory' => true,
|
2136 |
),
|
2137 |
+
'overridable' => array(),
|
2138 |
'src' => array(
|
2139 |
'blacklisted_value_regex' => '__amp_source_origin',
|
2140 |
+
'value_url' => array(
|
2141 |
+
'allow_relative' => true,
|
2142 |
+
'allowed_protocol' => array(
|
2143 |
+
'https',
|
2144 |
+
),
|
2145 |
),
|
2146 |
),
|
2147 |
),
|
2148 |
'tag_spec' => array(
|
2149 |
+
'requires_extension' => array(
|
2150 |
+
'amp-bind',
|
2151 |
),
|
2152 |
'spec_name' => 'amp-state',
|
2153 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-bind',
|
2154 |
),
|
|
|
2155 |
),
|
2156 |
),
|
2157 |
'amp-sticky-ad' => array(
|
2164 |
),
|
2165 |
'tag_spec' => array(
|
2166 |
'disallowed_ancestor' => array(
|
2167 |
+
'amp-app-banner',
|
2168 |
),
|
2169 |
+
'requires_extension' => array(
|
2170 |
+
'amp-sticky-ad',
|
2171 |
),
|
2172 |
'unique' => true,
|
2173 |
),
|
|
|
2174 |
),
|
2175 |
),
|
2176 |
'amp-story' => array(
|
2177 |
array(
|
2178 |
'attr_spec_list' => array(
|
2179 |
'background-audio' => array(
|
2180 |
+
'value_url' => array(
|
2181 |
+
'allowed_protocol' => array(
|
2182 |
+
'http',
|
2183 |
+
'https',
|
2184 |
+
),
|
2185 |
),
|
2186 |
),
|
2187 |
'bookend-config-src' => array(
|
2188 |
+
'value_url' => array(
|
2189 |
+
'allowed_protocol' => array(
|
2190 |
+
'http',
|
2191 |
+
'https',
|
2192 |
+
),
|
2193 |
),
|
2194 |
),
|
2195 |
'standalone' => array(
|
2198 |
),
|
2199 |
),
|
2200 |
'tag_spec' => array(
|
|
|
|
|
|
|
2201 |
'mandatory_parent' => 'body',
|
2202 |
+
'requires_extension' => array(
|
2203 |
+
'amp-story',
|
2204 |
+
),
|
2205 |
+
),
|
2206 |
+
),
|
2207 |
+
),
|
2208 |
+
'amp-story-auto-ads' => array(
|
2209 |
+
array(
|
2210 |
+
'attr_spec_list' => array(),
|
2211 |
+
'tag_spec' => array(
|
2212 |
+
'mandatory_parent' => 'amp-story',
|
2213 |
+
'requires_extension' => array(
|
2214 |
+
'amp-story-auto-ads',
|
2215 |
+
),
|
2216 |
+
'spec_url' => 'https://github.com/ampproject/amphtml/blob/master/extensions/amp-story/amp-story-auto-ads.md',
|
2217 |
+
'unique' => true,
|
2218 |
+
),
|
2219 |
+
),
|
2220 |
+
),
|
2221 |
+
'amp-story-cta-layer' => array(
|
2222 |
+
array(
|
2223 |
+
'attr_spec_list' => array(),
|
2224 |
+
'tag_spec' => array(
|
2225 |
+
'mandatory_ancestor' => 'amp-story-page',
|
2226 |
),
|
|
|
2227 |
),
|
2228 |
),
|
2229 |
'amp-story-grid-layer' => array(
|
2235 |
),
|
2236 |
),
|
2237 |
'tag_spec' => array(
|
|
|
|
|
|
|
2238 |
'mandatory_ancestor' => 'amp-story-page',
|
2239 |
),
|
|
|
2240 |
),
|
2241 |
),
|
2242 |
'amp-story-page' => array(
|
2244 |
'attr_spec_list' => array(
|
2245 |
'auto-advance-after' => array(),
|
2246 |
'background-audio' => array(
|
2247 |
+
'value_url' => array(
|
2248 |
+
'allowed_protocol' => array(
|
2249 |
+
'http',
|
2250 |
+
'https',
|
2251 |
+
),
|
2252 |
),
|
2253 |
),
|
2254 |
'id' => array(
|
2256 |
),
|
2257 |
),
|
2258 |
'tag_spec' => array(
|
|
|
|
|
|
|
2259 |
'mandatory_parent' => 'amp-story',
|
2260 |
+
'requires_extension' => array(
|
2261 |
+
'amp-story',
|
2262 |
+
),
|
2263 |
),
|
|
|
2264 |
),
|
2265 |
),
|
2266 |
'amp-timeago' => array(
|
2271 |
),
|
2272 |
'datetime' => array(
|
2273 |
'mandatory' => true,
|
2274 |
+
'value_regex' => '\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d(:[0-5]\\d(\\.\\d+)?)?(Z|[+-][0-1][0-9]:[0-5][0-9])',
|
2275 |
),
|
2276 |
'locale' => array(),
|
2277 |
'media' => array(),
|
2280 |
),
|
2281 |
),
|
2282 |
'tag_spec' => array(
|
2283 |
+
'requires_extension' => array(
|
2284 |
+
'amp-timeago',
|
2285 |
),
|
2286 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-timeago',
|
2287 |
),
|
|
|
2288 |
),
|
2289 |
),
|
2290 |
'amp-twitter' => array(
|
2299 |
),
|
2300 |
),
|
2301 |
'tag_spec' => array(
|
2302 |
+
'requires_extension' => array(
|
2303 |
+
'amp-twitter',
|
2304 |
),
|
2305 |
),
|
|
|
2306 |
),
|
2307 |
),
|
2308 |
'amp-user-notification' => array(
|
2309 |
array(
|
2310 |
'attr_spec_list' => array(
|
2311 |
'data-dismiss-href' => array(
|
2312 |
+
'value_url' => array(
|
2313 |
+
'allow_empty' => false,
|
2314 |
+
'allow_relative' => false,
|
2315 |
+
'allowed_protocol' => array(
|
2316 |
+
'https',
|
2317 |
+
),
|
2318 |
),
|
2319 |
),
|
2320 |
'data-show-if-href' => array(
|
2321 |
+
'value_url' => array(
|
2322 |
+
'allow_empty' => false,
|
2323 |
+
'allow_relative' => false,
|
2324 |
+
'allowed_protocol' => array(
|
2325 |
+
'https',
|
2326 |
+
),
|
2327 |
),
|
2328 |
),
|
2329 |
'enctype' => array(
|
2335 |
),
|
2336 |
),
|
2337 |
'tag_spec' => array(
|
2338 |
+
'requires_extension' => array(
|
2339 |
+
'amp-user-notification',
|
2340 |
),
|
2341 |
),
|
|
|
2342 |
),
|
2343 |
),
|
2344 |
'amp-video' => array(
|
2372 |
'disableremoteplayback' => array(
|
2373 |
'value' => '',
|
2374 |
),
|
2375 |
+
'lightbox' => array(),
|
2376 |
+
'lightbox-exclude' => array(
|
2377 |
+
'value' => '',
|
2378 |
+
),
|
2379 |
+
'lightbox-thumbnail-id' => array(
|
2380 |
+
'value_regex_casei' => '^[a-z][a-z\\d_-]*',
|
2381 |
+
),
|
2382 |
'loop' => array(
|
2383 |
'value' => '',
|
2384 |
),
|
2396 |
),
|
2397 |
'src' => array(
|
2398 |
'blacklisted_value_regex' => '__amp_source_origin',
|
2399 |
+
'value_url' => array(
|
2400 |
+
'allow_relative' => true,
|
2401 |
+
'allowed_protocol' => array(
|
2402 |
+
'https',
|
2403 |
+
),
|
2404 |
),
|
2405 |
),
|
2406 |
),
|
2407 |
'tag_spec' => array(
|
2408 |
+
'also_requires_tag_warning' => array(
|
2409 |
+
'amp-video extension .js script',
|
2410 |
),
|
2411 |
+
'disallowed_ancestor' => array(
|
2412 |
+
'amp-story',
|
|
|
2413 |
),
|
2414 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
|
2415 |
),
|
|
|
2416 |
),
|
2417 |
array(
|
2418 |
'attr_spec_list' => array(
|
2463 |
),
|
2464 |
'src' => array(
|
2465 |
'blacklisted_value_regex' => '__amp_source_origin',
|
2466 |
+
'value_url' => array(
|
2467 |
+
'allow_relative' => true,
|
2468 |
+
'allowed_protocol' => array(
|
2469 |
+
'https',
|
2470 |
+
),
|
2471 |
),
|
2472 |
),
|
2473 |
),
|
2474 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
2475 |
'mandatory_ancestor' => 'amp-story',
|
2476 |
+
'requires_extension' => array(
|
2477 |
+
'amp-video',
|
2478 |
+
),
|
2479 |
'spec_name' => 'amp-story >> amp-video',
|
2480 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
|
2481 |
),
|
|
|
2482 |
),
|
2483 |
),
|
2484 |
'amp-vimeo' => array(
|
2494 |
),
|
2495 |
),
|
2496 |
'tag_spec' => array(
|
2497 |
+
'requires_extension' => array(
|
2498 |
+
'amp-vimeo',
|
2499 |
),
|
2500 |
),
|
|
|
2501 |
),
|
2502 |
),
|
2503 |
'amp-vine' => array(
|
2512 |
),
|
2513 |
),
|
2514 |
'tag_spec' => array(
|
2515 |
+
'requires_extension' => array(
|
2516 |
+
'amp-vine',
|
2517 |
),
|
2518 |
),
|
|
|
2519 |
),
|
2520 |
),
|
2521 |
'amp-vk' => array(
|
2530 |
),
|
2531 |
),
|
2532 |
'tag_spec' => array(
|
2533 |
+
'requires_extension' => array(
|
2534 |
+
'amp-vk',
|
2535 |
),
|
2536 |
),
|
|
|
2537 |
),
|
2538 |
),
|
2539 |
'amp-web-push' => array(
|
2541 |
'attr_spec_list' => array(
|
2542 |
'helper-iframe-url' => array(
|
2543 |
'mandatory' => true,
|
2544 |
+
'value_url' => array(
|
2545 |
+
'allow_relative' => false,
|
2546 |
+
'allowed_protocol' => array(
|
2547 |
+
'https',
|
2548 |
+
),
|
2549 |
),
|
2550 |
),
|
2551 |
'id' => array(
|
2558 |
),
|
2559 |
'permission-dialog-url' => array(
|
2560 |
'mandatory' => true,
|
2561 |
+
'value_url' => array(
|
2562 |
+
'allow_relative' => false,
|
2563 |
+
'allowed_protocol' => array(
|
2564 |
+
'https',
|
2565 |
+
),
|
2566 |
),
|
2567 |
),
|
2568 |
'service-worker-url' => array(
|
2569 |
'mandatory' => true,
|
2570 |
+
'value_url' => array(
|
2571 |
+
'allow_relative' => false,
|
2572 |
+
'allowed_protocol' => array(
|
2573 |
+
'https',
|
2574 |
+
),
|
2575 |
),
|
2576 |
),
|
2577 |
),
|
2578 |
'tag_spec' => array(
|
2579 |
+
'requires_extension' => array(
|
2580 |
+
'amp-web-push',
|
2581 |
),
|
2582 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-web-push',
|
2583 |
),
|
|
|
2584 |
),
|
2585 |
),
|
2586 |
'amp-web-push-widget' => array(
|
2596 |
),
|
2597 |
),
|
2598 |
'tag_spec' => array(
|
2599 |
+
'requires_extension' => array(
|
2600 |
+
'amp-web-push',
|
2601 |
),
|
2602 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-web-push',
|
2603 |
),
|
2604 |
+
),
|
2605 |
+
),
|
2606 |
+
'amp-wistia-player' => array(
|
2607 |
+
array(
|
2608 |
+
'attr_spec_list' => array(
|
2609 |
+
'data-media-hashed-id' => array(
|
2610 |
+
'mandatory' => true,
|
2611 |
+
'value_regex' => '[0-9a-zA-Z]+',
|
2612 |
+
),
|
2613 |
+
'media' => array(),
|
2614 |
+
'noloading' => array(
|
2615 |
+
'value' => '',
|
2616 |
+
),
|
2617 |
+
),
|
2618 |
+
'tag_spec' => array(
|
2619 |
+
'requires_extension' => array(
|
2620 |
+
'amp-wistia-player',
|
2621 |
+
),
|
2622 |
+
),
|
2623 |
),
|
2624 |
),
|
2625 |
'amp-youtube' => array(
|
2630 |
'credentials' => array(
|
2631 |
'value_regex_casei' => '(include|omit)',
|
2632 |
),
|
2633 |
+
'data-live-channelid' => array(
|
2634 |
+
'value_regex' => '[^=/?:]+',
|
2635 |
+
),
|
2636 |
'data-videoid' => array(
|
|
|
2637 |
'value_regex' => '[^=/?:]+',
|
2638 |
),
|
2639 |
+
'lightbox' => array(),
|
2640 |
+
'lightbox-exclude' => array(
|
2641 |
+
'value' => '',
|
2642 |
+
),
|
2643 |
+
'lightbox-thumbnail-id' => array(
|
2644 |
+
'value_regex_casei' => '^[a-z][a-z\\d_-]*',
|
2645 |
+
),
|
2646 |
'media' => array(),
|
2647 |
'noloading' => array(
|
2648 |
'value' => '',
|
2649 |
),
|
2650 |
),
|
2651 |
+
'tag_spec' => array(
|
2652 |
+
'requires_extension' => array(
|
2653 |
+
'amp-youtube',
|
2654 |
+
),
|
2655 |
+
),
|
2656 |
),
|
2657 |
),
|
2658 |
'article' => array(
|
2659 |
array(
|
2660 |
'attr_spec_list' => array(),
|
2661 |
'tag_spec' => array(),
|
|
|
2662 |
),
|
2663 |
),
|
2664 |
'aside' => array(
|
2665 |
array(
|
2666 |
'attr_spec_list' => array(),
|
2667 |
'tag_spec' => array(),
|
|
|
2668 |
),
|
2669 |
),
|
2670 |
'audio' => array(
|
2677 |
'preload' => array(),
|
2678 |
'src' => array(
|
2679 |
'blacklisted_value_regex' => '__amp_source_origin',
|
2680 |
+
'value_url' => array(
|
2681 |
+
'allow_relative' => false,
|
2682 |
+
'allowed_protocol' => array(
|
2683 |
+
'data',
|
2684 |
+
'https',
|
2685 |
+
),
|
2686 |
),
|
2687 |
),
|
2688 |
),
|
2689 |
'tag_spec' => array(
|
|
|
|
|
|
|
2690 |
'mandatory_ancestor' => 'noscript',
|
2691 |
'mandatory_ancestor_suggested_alternative' => 'amp-audio',
|
2692 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
2693 |
),
|
|
|
2694 |
),
|
2695 |
),
|
2696 |
'b' => array(
|
2697 |
array(
|
2698 |
'attr_spec_list' => array(),
|
2699 |
'tag_spec' => array(),
|
2700 |
+
),
|
2701 |
+
),
|
2702 |
+
'base' => array(
|
2703 |
+
array(
|
2704 |
+
'attr_spec_list' => array(
|
2705 |
+
'href' => array(
|
2706 |
+
'value' => '/',
|
2707 |
+
),
|
2708 |
+
'target' => array(
|
2709 |
+
'value_regex_casei' => '(_blank|_self|_top)',
|
2710 |
+
),
|
2711 |
+
),
|
2712 |
+
'tag_spec' => array(
|
2713 |
+
'mandatory_parent' => 'head',
|
2714 |
+
'unique' => true,
|
2715 |
+
),
|
2716 |
),
|
2717 |
),
|
2718 |
'bdi' => array(
|
2719 |
array(
|
2720 |
'attr_spec_list' => array(),
|
2721 |
'tag_spec' => array(),
|
|
|
2722 |
),
|
2723 |
),
|
2724 |
'bdo' => array(
|
2727 |
'dir' => array(),
|
2728 |
),
|
2729 |
'tag_spec' => array(),
|
|
|
2730 |
),
|
2731 |
),
|
2732 |
'big' => array(
|
2733 |
array(
|
2734 |
'attr_spec_list' => array(),
|
2735 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
2736 |
),
|
2737 |
),
|
2738 |
'blockquote' => array(
|
2741 |
'align' => array(),
|
2742 |
'cite' => array(
|
2743 |
'blacklisted_value_regex' => '__amp_source_origin',
|
2744 |
+
'value_url' => array(
|
2745 |
+
'allow_empty' => true,
|
2746 |
+
'allow_relative' => true,
|
2747 |
+
'allowed_protocol' => array(
|
2748 |
+
'http',
|
2749 |
+
'https',
|
2750 |
+
),
|
2751 |
),
|
2752 |
),
|
2753 |
),
|
2754 |
'tag_spec' => array(),
|
|
|
2755 |
),
|
2756 |
),
|
2757 |
'body' => array(
|
2763 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#required-markup',
|
2764 |
'unique' => true,
|
2765 |
),
|
|
|
2766 |
),
|
2767 |
),
|
2768 |
'br' => array(
|
2769 |
array(
|
2770 |
'attr_spec_list' => array(),
|
2771 |
'tag_spec' => array(),
|
|
|
2772 |
),
|
2773 |
),
|
2774 |
'button' => array(
|
2781 |
'value' => '',
|
2782 |
),
|
2783 |
'name' => array(
|
2784 |
+
'blacklisted_value_regex' => '(^|\\s)(__amp_\\S*|__count__|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|__noSuchMethod__|__parent__|__proto__|__AMP_\\S*|\\$p|\\$proxy|acceptCharset|addEventListener|appendChild|assignedSlot|attachShadow|baseURI|checkValidity|childElementCount|childNodes|classList|className|clientHeight|clientLeft|clientTop|clientWidth|compareDocumentPosition|computedName|computedRole|contentEditable|createShadowRoot|enqueAction|firstChild|firstElementChild|getAnimations|getAttribute|getAttributeNS|getAttributeNode|getAttributeNodeNS|getBoundingClientRect|getClientRects|getDestinationInsertionPoints|getElementsByClassName|getElementsByTagName|getElementsByTagNameNS|getRootNode|hasAttribute|hasAttributeNS|hasAttributes|hasChildNodes|hasPointerCapture|innerHTML|innerText|inputMode|insertAdjacentElement|insertAdjacentHTML|insertAdjacentText|isContentEditable|isDefaultNamespace|isEqualNode|isSameNode|lastChild|lastElementChild|lookupNamespaceURI|namespaceURI|nextElementSibling|nextSibling|nodeName|nodeType|nodeValue|offsetHeight|offsetLeft|offsetParent|offsetTop|offsetWidth|outerHTML|outerText|ownerDocument|parentElement|parentNode|previousElementSibling|previousSibling|querySelector|querySelectorAll|releasePointerCapture|removeAttribute|removeAttributeNS|removeAttributeNode|removeChild|removeEventListener|replaceChild|reportValidity|requestPointerLock|scrollHeight|scrollIntoView|scrollIntoViewIfNeeded|scrollLeft|scrollWidth|setAttribute|setAttributeNS|setAttributeNode|setAttributeNodeNS|setPointerCapture|shadowRoot|styleMap|tabIndex|tagName|textContent|toString|valueOf|(webkit|ms|moz|o)dropzone|(webkit|moz|ms|o)MatchesSelector|(webkit|moz|ms|o)RequestFullScreen|(webkit|moz|ms|o)RequestFullscreen)(\\s|$)',
|
2785 |
),
|
2786 |
'role' => array(),
|
2787 |
'tabindex' => array(),
|
2789 |
'value' => array(),
|
2790 |
),
|
2791 |
'tag_spec' => array(),
|
|
|
2792 |
),
|
2793 |
array(
|
2794 |
'attr_spec_list' => array(
|
2795 |
'name' => array(
|
2796 |
+
'blacklisted_value_regex' => '(^|\\s)(__amp_\\S*|__count__|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|__noSuchMethod__|__parent__|__proto__|__AMP_\\S*|\\$p|\\$proxy|acceptCharset|addEventListener|appendChild|assignedSlot|attachShadow|baseURI|checkValidity|childElementCount|childNodes|classList|className|clientHeight|clientLeft|clientTop|clientWidth|compareDocumentPosition|computedName|computedRole|contentEditable|createShadowRoot|enqueAction|firstChild|firstElementChild|getAnimations|getAttribute|getAttributeNS|getAttributeNode|getAttributeNodeNS|getBoundingClientRect|getClientRects|getDestinationInsertionPoints|getElementsByClassName|getElementsByTagName|getElementsByTagNameNS|getRootNode|hasAttribute|hasAttributeNS|hasAttributes|hasChildNodes|hasPointerCapture|innerHTML|innerText|inputMode|insertAdjacentElement|insertAdjacentHTML|insertAdjacentText|isContentEditable|isDefaultNamespace|isEqualNode|isSameNode|lastChild|lastElementChild|lookupNamespaceURI|namespaceURI|nextElementSibling|nextSibling|nodeName|nodeType|nodeValue|offsetHeight|offsetLeft|offsetParent|offsetTop|offsetWidth|outerHTML|outerText|ownerDocument|parentElement|parentNode|previousElementSibling|previousSibling|querySelector|querySelectorAll|releasePointerCapture|removeAttribute|removeAttributeNS|removeAttributeNode|removeChild|removeEventListener|replaceChild|reportValidity|requestPointerLock|scrollHeight|scrollIntoView|scrollIntoViewIfNeeded|scrollLeft|scrollWidth|setAttribute|setAttributeNS|setAttributeNode|setAttributeNodeNS|setPointerCapture|shadowRoot|styleMap|tabIndex|tagName|textContent|toString|valueOf|(webkit|ms|moz|o)dropzone|(webkit|moz|ms|o)MatchesSelector|(webkit|moz|ms|o)RequestFullScreen|(webkit|moz|ms|o)RequestFullscreen)(\\s|$)',
|
2797 |
),
|
2798 |
'open-button' => array(
|
2799 |
'value' => '',
|
2804 |
'value' => array(),
|
2805 |
),
|
2806 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
2807 |
'mandatory_ancestor' => 'amp-app-banner',
|
2808 |
'spec_name' => 'amp-app-banner button[open-button]',
|
2809 |
),
|
|
|
2810 |
),
|
2811 |
),
|
2812 |
'caption' => array(
|
2813 |
array(
|
2814 |
'attr_spec_list' => array(),
|
2815 |
'tag_spec' => array(),
|
|
|
2816 |
),
|
2817 |
),
|
2818 |
'center' => array(
|
2819 |
array(
|
2820 |
'attr_spec_list' => array(),
|
2821 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
2822 |
),
|
2823 |
),
|
2824 |
'circle' => array(
|
2902 |
'xmlns:xlink' => array(),
|
2903 |
),
|
2904 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
2905 |
'mandatory_ancestor' => 'svg',
|
2906 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
2907 |
),
|
|
|
2908 |
),
|
2909 |
),
|
2910 |
'cite' => array(
|
2911 |
array(
|
2912 |
'attr_spec_list' => array(),
|
2913 |
'tag_spec' => array(),
|
|
|
2914 |
),
|
2915 |
),
|
2916 |
'clippath' => array(
|
2991 |
'xmlns:xlink' => array(),
|
2992 |
),
|
2993 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
2994 |
'mandatory_ancestor' => 'svg',
|
2995 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
2996 |
),
|
|
|
2997 |
),
|
2998 |
),
|
2999 |
'code' => array(
|
3000 |
array(
|
3001 |
'attr_spec_list' => array(),
|
3002 |
'tag_spec' => array(),
|
|
|
3003 |
),
|
3004 |
),
|
3005 |
'col' => array(
|
3008 |
'span' => array(),
|
3009 |
),
|
3010 |
'tag_spec' => array(),
|
|
|
3011 |
),
|
3012 |
),
|
3013 |
'colgroup' => array(
|
3016 |
'span' => array(),
|
3017 |
),
|
3018 |
'tag_spec' => array(),
|
|
|
3019 |
),
|
3020 |
),
|
3021 |
'data' => array(
|
3022 |
array(
|
3023 |
'attr_spec_list' => array(),
|
3024 |
'tag_spec' => array(),
|
|
|
3025 |
),
|
3026 |
),
|
3027 |
'datalist' => array(
|
3030 |
'tag_spec' => array(
|
3031 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
3032 |
),
|
|
|
3033 |
),
|
3034 |
),
|
3035 |
'dd' => array(
|
3036 |
array(
|
3037 |
'attr_spec_list' => array(),
|
3038 |
'tag_spec' => array(),
|
|
|
3039 |
),
|
3040 |
),
|
3041 |
'defs' => array(
|
3115 |
'xmlns:xlink' => array(),
|
3116 |
),
|
3117 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
3118 |
'mandatory_ancestor' => 'svg',
|
3119 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3120 |
),
|
|
|
3121 |
),
|
3122 |
),
|
3123 |
'del' => array(
|
3125 |
'attr_spec_list' => array(
|
3126 |
'cite' => array(
|
3127 |
'blacklisted_value_regex' => '__amp_source_origin',
|
3128 |
+
'value_url' => array(
|
3129 |
+
'allow_empty' => true,
|
3130 |
+
'allow_relative' => true,
|
3131 |
+
'allowed_protocol' => array(
|
3132 |
+
'http',
|
3133 |
+
'https',
|
3134 |
+
),
|
3135 |
),
|
3136 |
),
|
3137 |
'datetime' => array(),
|
3138 |
),
|
3139 |
'tag_spec' => array(),
|
|
|
3140 |
),
|
3141 |
),
|
3142 |
'desc' => array(
|
3151 |
'xmlns:xlink' => array(),
|
3152 |
),
|
3153 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
3154 |
'mandatory_ancestor' => 'svg',
|
3155 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3156 |
),
|
|
|
3157 |
),
|
3158 |
),
|
3159 |
'dfn' => array(
|
3160 |
array(
|
3161 |
'attr_spec_list' => array(),
|
3162 |
'tag_spec' => array(),
|
|
|
3163 |
),
|
3164 |
),
|
3165 |
'dir' => array(
|
3166 |
array(
|
3167 |
'attr_spec_list' => array(),
|
3168 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
3169 |
),
|
3170 |
),
|
3171 |
'div' => array(
|
3174 |
'align' => array(),
|
3175 |
),
|
3176 |
'tag_spec' => array(),
|
|
|
3177 |
),
|
3178 |
array(
|
3179 |
'attr_spec_list' => array(
|
3180 |
'align' => array(),
|
3181 |
'submitting' => array(
|
3182 |
+
'dispatch_key' => 1,
|
3183 |
'mandatory' => true,
|
3184 |
),
|
3185 |
),
|
3186 |
'tag_spec' => array(
|
3187 |
'mandatory_parent' => 'form',
|
3188 |
+
'spec_name' => 'FORM > DIV [submitting]',
|
3189 |
),
|
|
|
3190 |
),
|
3191 |
array(
|
3192 |
'attr_spec_list' => array(
|
3197 |
),
|
3198 |
'tag_spec' => array(
|
3199 |
'mandatory_parent' => 'form',
|
3200 |
+
'spec_name' => 'FORM > DIV [submit-success]',
|
3201 |
+
),
|
3202 |
+
),
|
3203 |
+
array(
|
3204 |
+
'attr_spec_list' => array(
|
3205 |
+
'align' => array(),
|
3206 |
+
'submit-success' => array(
|
3207 |
+
'mandatory' => true,
|
3208 |
+
),
|
3209 |
+
'template' => array(
|
3210 |
+
'mandatory' => true,
|
3211 |
+
),
|
3212 |
+
),
|
3213 |
+
'tag_spec' => array(
|
3214 |
+
'mandatory_parent' => 'form',
|
3215 |
+
'spec_name' => 'FORM > DIV [submit-success][template]',
|
3216 |
),
|
|
|
3217 |
),
|
3218 |
array(
|
3219 |
'attr_spec_list' => array(
|
3224 |
),
|
3225 |
'tag_spec' => array(
|
3226 |
'mandatory_parent' => 'form',
|
3227 |
+
'spec_name' => 'FORM > DIV [submit-error]',
|
3228 |
+
),
|
3229 |
+
),
|
3230 |
+
array(
|
3231 |
+
'attr_spec_list' => array(
|
3232 |
+
'align' => array(),
|
3233 |
+
'submit-error' => array(
|
3234 |
+
'mandatory' => true,
|
3235 |
+
),
|
3236 |
+
'template' => array(
|
3237 |
+
'mandatory' => true,
|
3238 |
+
),
|
3239 |
+
),
|
3240 |
+
'tag_spec' => array(
|
3241 |
+
'mandatory_parent' => 'form',
|
3242 |
+
'spec_name' => 'FORM > DIV [submit-error][template]',
|
3243 |
),
|
|
|
3244 |
),
|
3245 |
),
|
3246 |
'dl' => array(
|
3247 |
array(
|
3248 |
'attr_spec_list' => array(),
|
3249 |
'tag_spec' => array(),
|
|
|
3250 |
),
|
3251 |
),
|
3252 |
'dt' => array(
|
3253 |
array(
|
3254 |
'attr_spec_list' => array(),
|
3255 |
'tag_spec' => array(),
|
|
|
3256 |
),
|
3257 |
),
|
3258 |
'ellipse' => array(
|
3337 |
'xmlns:xlink' => array(),
|
3338 |
),
|
3339 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
3340 |
'mandatory_ancestor' => 'svg',
|
3341 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3342 |
),
|
|
|
3343 |
),
|
3344 |
),
|
3345 |
'em' => array(
|
3346 |
array(
|
3347 |
'attr_spec_list' => array(),
|
3348 |
'tag_spec' => array(),
|
|
|
3349 |
),
|
3350 |
),
|
3351 |
'fecolormatrix' => array(
|
3428 |
'y' => array(),
|
3429 |
),
|
3430 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
3431 |
'mandatory_ancestor' => 'svg',
|
3432 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3433 |
),
|
|
|
3434 |
),
|
3435 |
),
|
3436 |
'fecomposite' => array(
|
3517 |
'y' => array(),
|
3518 |
),
|
3519 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
3520 |
'mandatory_ancestor' => 'svg',
|
3521 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3522 |
),
|
|
|
3523 |
),
|
3524 |
),
|
3525 |
'feflood' => array(
|
3599 |
'y' => array(),
|
3600 |
),
|
3601 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
3602 |
'mandatory_ancestor' => 'svg',
|
3603 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3604 |
),
|
|
|
3605 |
),
|
3606 |
),
|
3607 |
'fegaussianblur' => array(
|
3684 |
'y' => array(),
|
3685 |
),
|
3686 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
3687 |
'mandatory_ancestor' => 'svg',
|
3688 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3689 |
),
|
|
|
3690 |
),
|
3691 |
),
|
3692 |
'femerge' => array(
|
3766 |
'y' => array(),
|
3767 |
),
|
3768 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
3769 |
'mandatory_ancestor' => 'svg',
|
3770 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3771 |
),
|
|
|
3772 |
),
|
3773 |
),
|
3774 |
'femergenode' => array(
|
3784 |
'xmlns:xlink' => array(),
|
3785 |
),
|
3786 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
3787 |
'mandatory_ancestor' => 'svg',
|
3788 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3789 |
),
|
|
|
3790 |
),
|
3791 |
),
|
3792 |
'feoffset' => array(
|
3869 |
'y' => array(),
|
3870 |
),
|
3871 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
3872 |
'mandatory_ancestor' => 'svg',
|
3873 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
3874 |
),
|
|
|
3875 |
),
|
3876 |
),
|
3877 |
'fieldset' => array(
|
3880 |
'[disabled]' => array(),
|
3881 |
'disabled' => array(),
|
3882 |
'name' => array(
|
3883 |
+
'blacklisted_value_regex' => '(^|\\s)(__amp_\\S*|__count__|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|__noSuchMethod__|__parent__|__proto__|__AMP_\\S*|\\$p|\\$proxy|acceptCharset|addEventListener|appendChild|assignedSlot|attachShadow|baseURI|checkValidity|childElementCount|childNodes|classList|className|clientHeight|clientLeft|clientTop|clientWidth|compareDocumentPosition|computedName|computedRole|contentEditable|createShadowRoot|enqueAction|firstChild|firstElementChild|getAnimations|getAttribute|getAttributeNS|getAttributeNode|getAttributeNodeNS|getBoundingClientRect|getClientRects|getDestinationInsertionPoints|getElementsByClassName|getElementsByTagName|getElementsByTagNameNS|getRootNode|hasAttribute|hasAttributeNS|hasAttributes|hasChildNodes|hasPointerCapture|innerHTML|innerText|inputMode|insertAdjacentElement|insertAdjacentHTML|insertAdjacentText|isContentEditable|isDefaultNamespace|isEqualNode|isSameNode|lastChild|lastElementChild|lookupNamespaceURI|namespaceURI|nextElementSibling|nextSibling|nodeName|nodeType|nodeValue|offsetHeight|offsetLeft|offsetParent|offsetTop|offsetWidth|outerHTML|outerText|ownerDocument|parentElement|parentNode|previousElementSibling|previousSibling|querySelector|querySelectorAll|releasePointerCapture|removeAttribute|removeAttributeNS|removeAttributeNode|removeChild|removeEventListener|replaceChild|reportValidity|requestPointerLock|scrollHeight|scrollIntoView|scrollIntoViewIfNeeded|scrollLeft|scrollWidth|setAttribute|setAttributeNS|setAttributeNode|setAttributeNodeNS|setPointerCapture|shadowRoot|styleMap|tabIndex|tagName|textContent|toString|valueOf|(webkit|ms|moz|o)dropzone|(webkit|moz|ms|o)MatchesSelector|(webkit|moz|ms|o)RequestFullScreen|(webkit|moz|ms|o)RequestFullscreen)(\\s|$)',
|
3884 |
),
|
3885 |
),
|
3886 |
'tag_spec' => array(),
|
|
|
3887 |
),
|
3888 |
),
|
3889 |
'figcaption' => array(
|
3890 |
array(
|
3891 |
'attr_spec_list' => array(),
|
3892 |
'tag_spec' => array(),
|
|
|
3893 |
),
|
3894 |
),
|
3895 |
'figure' => array(
|
3896 |
array(
|
3897 |
'attr_spec_list' => array(),
|
3898 |
'tag_spec' => array(),
|
|
|
3899 |
),
|
3900 |
),
|
3901 |
'filter' => array(
|
3977 |
'alternative_names' => array(
|
3978 |
'href',
|
3979 |
),
|
3980 |
+
'value_url' => array(
|
3981 |
+
'allow_empty' => false,
|
3982 |
+
'allow_relative' => true,
|
3983 |
+
'allowed_protocol' => array(
|
3984 |
+
'http',
|
3985 |
+
'https',
|
3986 |
+
),
|
3987 |
),
|
3988 |
),
|
3989 |
'xlink:role' => array(),
|
3997 |
'y' => array(),
|
3998 |
),
|
3999 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
4000 |
'mandatory_ancestor' => 'svg',
|
4001 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4002 |
),
|
|
|
4003 |
),
|
4004 |
),
|
4005 |
'footer' => array(
|
4006 |
array(
|
4007 |
'attr_spec_list' => array(),
|
4008 |
'tag_spec' => array(),
|
|
|
4009 |
),
|
4010 |
),
|
4011 |
'foreignobject' => array(
|
4089 |
'y' => array(),
|
4090 |
),
|
4091 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
4092 |
'mandatory_ancestor' => 'svg',
|
4093 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4094 |
),
|
|
|
4095 |
),
|
4096 |
),
|
4097 |
'form' => array(
|
4102 |
'action' => array(
|
4103 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4104 |
'mandatory' => true,
|
4105 |
+
'value_url' => array(
|
4106 |
+
'allow_relative' => true,
|
4107 |
+
'allowed_protocol' => array(
|
4108 |
+
'https',
|
4109 |
+
),
|
4110 |
),
|
4111 |
),
|
4112 |
'action-xhr' => array(
|
4113 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4114 |
+
'value_url' => array(
|
4115 |
+
'allow_relative' => true,
|
4116 |
+
'allowed_protocol' => array(
|
4117 |
+
'https',
|
4118 |
+
),
|
4119 |
),
|
4120 |
),
|
4121 |
'autocomplete' => array(),
|
4134 |
),
|
4135 |
'verify-xhr' => array(
|
4136 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4137 |
+
'value_url' => array(
|
4138 |
+
'allow_relative' => true,
|
4139 |
+
'allowed_protocol' => array(
|
4140 |
+
'https',
|
4141 |
+
),
|
4142 |
),
|
4143 |
),
|
4144 |
),
|
4145 |
'tag_spec' => array(
|
4146 |
'disallowed_ancestor' => array(
|
4147 |
+
'amp-app-banner',
|
4148 |
),
|
4149 |
+
'requires_extension' => array(
|
4150 |
+
'amp-form',
|
|
|
4151 |
),
|
4152 |
+
'spec_name' => 'FORM [method=GET]',
|
4153 |
),
|
|
|
4154 |
),
|
4155 |
array(
|
4156 |
'attr_spec_list' => array(
|
4159 |
'action-xhr' => array(
|
4160 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4161 |
'mandatory' => true,
|
4162 |
+
'value_url' => array(
|
4163 |
+
'allow_relative' => true,
|
4164 |
+
'allowed_protocol' => array(
|
4165 |
+
'https',
|
4166 |
+
),
|
4167 |
),
|
4168 |
),
|
4169 |
'autocomplete' => array(),
|
4172 |
),
|
4173 |
'enctype' => array(),
|
4174 |
'method' => array(
|
4175 |
+
'dispatch_key' => 2,
|
4176 |
'mandatory' => true,
|
4177 |
'value_casei' => 'post',
|
4178 |
),
|
4184 |
),
|
4185 |
'verify-xhr' => array(
|
4186 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4187 |
+
'value_url' => array(
|
4188 |
+
'allow_relative' => true,
|
4189 |
+
'allowed_protocol' => array(
|
4190 |
+
'https',
|
4191 |
+
),
|
4192 |
),
|
4193 |
),
|
4194 |
),
|
4195 |
'tag_spec' => array(
|
4196 |
'disallowed_ancestor' => array(
|
4197 |
+
'amp-app-banner',
|
4198 |
),
|
4199 |
+
'requires_extension' => array(
|
4200 |
+
'amp-form',
|
|
|
4201 |
),
|
4202 |
+
'spec_name' => 'FORM [method=POST]',
|
4203 |
),
|
|
|
4204 |
),
|
4205 |
),
|
4206 |
'g' => array(
|
4280 |
'xmlns:xlink' => array(),
|
4281 |
),
|
4282 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
4283 |
'mandatory_ancestor' => 'svg',
|
4284 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4285 |
),
|
|
|
4286 |
),
|
4287 |
),
|
4288 |
'glyph' => array(
|
4366 |
'xmlns:xlink' => array(),
|
4367 |
),
|
4368 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
4369 |
'mandatory_ancestor' => 'svg',
|
4370 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4371 |
),
|
|
|
4372 |
),
|
4373 |
),
|
4374 |
'glyphref' => array(
|
4448 |
'alternative_names' => array(
|
4449 |
'href',
|
4450 |
),
|
4451 |
+
'value_url' => array(
|
4452 |
+
'allow_empty' => false,
|
4453 |
+
'allow_relative' => true,
|
4454 |
+
'allowed_protocol' => array(
|
4455 |
+
'http',
|
4456 |
+
'https',
|
4457 |
+
),
|
4458 |
),
|
4459 |
),
|
4460 |
'xlink:role' => array(),
|
4468 |
'y' => array(),
|
4469 |
),
|
4470 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
4471 |
'mandatory_ancestor' => 'svg',
|
4472 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4473 |
),
|
|
|
4474 |
),
|
4475 |
),
|
4476 |
'h1' => array(
|
4479 |
'align' => array(),
|
4480 |
),
|
4481 |
'tag_spec' => array(),
|
|
|
4482 |
),
|
4483 |
),
|
4484 |
'h2' => array(
|
4487 |
'align' => array(),
|
4488 |
),
|
4489 |
'tag_spec' => array(),
|
|
|
4490 |
),
|
4491 |
),
|
4492 |
'h3' => array(
|
4495 |
'align' => array(),
|
4496 |
),
|
4497 |
'tag_spec' => array(),
|
|
|
4498 |
),
|
4499 |
),
|
4500 |
'h4' => array(
|
4503 |
'align' => array(),
|
4504 |
),
|
4505 |
'tag_spec' => array(),
|
|
|
4506 |
),
|
4507 |
),
|
4508 |
'h5' => array(
|
4511 |
'align' => array(),
|
4512 |
),
|
4513 |
'tag_spec' => array(),
|
|
|
4514 |
),
|
4515 |
),
|
4516 |
'h6' => array(
|
4519 |
'align' => array(),
|
4520 |
),
|
4521 |
'tag_spec' => array(),
|
4522 |
+
),
|
4523 |
+
),
|
4524 |
+
'head' => array(
|
4525 |
+
array(
|
4526 |
+
'attr_spec_list' => array(),
|
4527 |
+
'tag_spec' => array(
|
4528 |
+
'mandatory' => true,
|
4529 |
+
'mandatory_parent' => 'html',
|
4530 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#required-markup',
|
4531 |
+
'unique' => true,
|
4532 |
+
),
|
4533 |
),
|
4534 |
),
|
4535 |
'header' => array(
|
4536 |
array(
|
4537 |
'attr_spec_list' => array(),
|
4538 |
'tag_spec' => array(),
|
|
|
4539 |
),
|
4540 |
),
|
4541 |
'hgroup' => array(
|
4542 |
array(
|
4543 |
'attr_spec_list' => array(),
|
4544 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
4545 |
),
|
4546 |
),
|
4547 |
'hkern' => array(
|
4561 |
'xmlns:xlink' => array(),
|
4562 |
),
|
4563 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
4564 |
'mandatory_ancestor' => 'svg',
|
4565 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4566 |
),
|
|
|
4567 |
),
|
4568 |
),
|
4569 |
'hr' => array(
|
4570 |
array(
|
4571 |
'attr_spec_list' => array(),
|
4572 |
'tag_spec' => array(),
|
4573 |
+
),
|
4574 |
+
),
|
4575 |
+
'html' => array(
|
4576 |
+
array(
|
4577 |
+
'attr_spec_list' => array(
|
4578 |
+
'\\u26a1' => array(
|
4579 |
+
'alternative_names' => array(
|
4580 |
+
'amp',
|
4581 |
+
),
|
4582 |
+
'mandatory' => true,
|
4583 |
+
'value' => '',
|
4584 |
+
),
|
4585 |
+
),
|
4586 |
+
'tag_spec' => array(
|
4587 |
+
'mandatory' => true,
|
4588 |
+
'mandatory_parent' => '!doctype',
|
4589 |
+
'spec_name' => 'html \\u26a1 for top-level html',
|
4590 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#required-markup',
|
4591 |
+
'unique' => true,
|
4592 |
+
),
|
4593 |
),
|
4594 |
),
|
4595 |
'i' => array(
|
4596 |
array(
|
4597 |
'attr_spec_list' => array(),
|
4598 |
'tag_spec' => array(),
|
|
|
4599 |
),
|
4600 |
),
|
4601 |
'iframe' => array(
|
4616 |
),
|
4617 |
'src' => array(
|
4618 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4619 |
+
'value_url' => array(
|
4620 |
+
'allow_relative' => false,
|
4621 |
+
'allowed_protocol' => array(
|
4622 |
+
'data',
|
4623 |
+
'https',
|
4624 |
+
),
|
4625 |
),
|
4626 |
),
|
4627 |
'srcdoc' => array(),
|
4628 |
'width' => array(),
|
4629 |
),
|
4630 |
'tag_spec' => array(
|
|
|
|
|
|
|
4631 |
'mandatory_ancestor' => 'noscript',
|
4632 |
'mandatory_ancestor_suggested_alternative' => 'amp-iframe',
|
4633 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-iframe',
|
4634 |
),
|
|
|
4635 |
),
|
4636 |
),
|
4637 |
'image' => array(
|
4716 |
'href',
|
4717 |
),
|
4718 |
'blacklisted_value_regex' => '(^|\\s)data:image\\/svg\\+xml',
|
4719 |
+
'value_url' => array(
|
4720 |
+
'allow_empty' => false,
|
4721 |
+
'allow_relative' => true,
|
4722 |
+
'allowed_protocol' => array(
|
4723 |
+
'data',
|
4724 |
+
'http',
|
4725 |
+
'https',
|
4726 |
+
),
|
4727 |
),
|
4728 |
),
|
4729 |
'xlink:role' => array(),
|
4737 |
'y' => array(),
|
4738 |
),
|
4739 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
4740 |
'mandatory_ancestor' => 'svg',
|
4741 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4742 |
),
|
|
|
4743 |
),
|
4744 |
),
|
4745 |
'img' => array(
|
4751 |
'ismap' => array(),
|
4752 |
'longdesc' => array(
|
4753 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4754 |
+
'value_url' => array(
|
4755 |
+
'allow_relative' => true,
|
4756 |
+
'allowed_protocol' => array(
|
4757 |
+
'http',
|
4758 |
+
'https',
|
4759 |
+
),
|
4760 |
),
|
4761 |
),
|
4762 |
'src' => array(
|
4765 |
),
|
4766 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4767 |
'mandatory' => true,
|
4768 |
+
'value_url' => array(
|
4769 |
+
'allow_relative' => true,
|
4770 |
+
'allowed_protocol' => array(
|
4771 |
+
'data',
|
4772 |
+
'https',
|
4773 |
+
),
|
4774 |
),
|
4775 |
),
|
4776 |
'width' => array(),
|
4777 |
),
|
4778 |
'tag_spec' => array(
|
|
|
|
|
|
|
4779 |
'mandatory_ancestor' => 'noscript',
|
4780 |
'mandatory_ancestor_suggested_alternative' => 'amp-img',
|
4781 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-img',
|
4782 |
),
|
|
|
4783 |
),
|
4784 |
),
|
4785 |
'input' => array(
|
4823 |
'minlength' => array(),
|
4824 |
'multiple' => array(),
|
4825 |
'name' => array(
|
4826 |
+
'blacklisted_value_regex' => '(^|\\s)(__amp_\\S*|__count__|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|__noSuchMethod__|__parent__|__proto__|__AMP_\\S*|\\$p|\\$proxy|acceptCharset|addEventListener|appendChild|assignedSlot|attachShadow|baseURI|checkValidity|childElementCount|childNodes|classList|className|clientHeight|clientLeft|clientTop|clientWidth|compareDocumentPosition|computedName|computedRole|contentEditable|createShadowRoot|enqueAction|firstChild|firstElementChild|getAnimations|getAttribute|getAttributeNS|getAttributeNode|getAttributeNodeNS|getBoundingClientRect|getClientRects|getDestinationInsertionPoints|getElementsByClassName|getElementsByTagName|getElementsByTagNameNS|getRootNode|hasAttribute|hasAttributeNS|hasAttributes|hasChildNodes|hasPointerCapture|innerHTML|innerText|inputMode|insertAdjacentElement|insertAdjacentHTML|insertAdjacentText|isContentEditable|isDefaultNamespace|isEqualNode|isSameNode|lastChild|lastElementChild|lookupNamespaceURI|namespaceURI|nextElementSibling|nextSibling|nodeName|nodeType|nodeValue|offsetHeight|offsetLeft|offsetParent|offsetTop|offsetWidth|outerHTML|outerText|ownerDocument|parentElement|parentNode|previousElementSibling|previousSibling|querySelector|querySelectorAll|releasePointerCapture|removeAttribute|removeAttributeNS|removeAttributeNode|removeChild|removeEventListener|replaceChild|reportValidity|requestPointerLock|scrollHeight|scrollIntoView|scrollIntoViewIfNeeded|scrollLeft|scrollWidth|setAttribute|setAttributeNS|setAttributeNode|setAttributeNodeNS|setPointerCapture|shadowRoot|styleMap|tabIndex|tagName|textContent|toString|valueOf|(webkit|ms|moz|o)dropzone|(webkit|moz|ms|o)MatchesSelector|(webkit|moz|ms|o)RequestFullScreen|(webkit|moz|ms|o)RequestFullscreen)(\\s|$)',
|
4827 |
),
|
4828 |
'pattern' => array(),
|
4829 |
'placeholder' => array(),
|
4843 |
'tag_spec' => array(
|
4844 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
4845 |
),
|
|
|
4846 |
),
|
4847 |
),
|
4848 |
'ins' => array(
|
4850 |
'attr_spec_list' => array(
|
4851 |
'cite' => array(
|
4852 |
'blacklisted_value_regex' => '__amp_source_origin',
|
4853 |
+
'value_url' => array(
|
4854 |
+
'allow_empty' => true,
|
4855 |
+
'allow_relative' => true,
|
4856 |
+
'allowed_protocol' => array(
|
4857 |
+
'http',
|
4858 |
+
'https',
|
4859 |
+
),
|
4860 |
),
|
4861 |
),
|
4862 |
'datetime' => array(),
|
4863 |
),
|
4864 |
'tag_spec' => array(),
|
|
|
4865 |
),
|
4866 |
),
|
4867 |
'kbd' => array(
|
4868 |
array(
|
4869 |
'attr_spec_list' => array(),
|
4870 |
'tag_spec' => array(),
|
|
|
4871 |
),
|
4872 |
),
|
4873 |
'label' => array(
|
4878 |
'tag_spec' => array(
|
4879 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
4880 |
),
|
|
|
4881 |
),
|
4882 |
),
|
4883 |
'legend' => array(
|
4884 |
array(
|
4885 |
'attr_spec_list' => array(),
|
4886 |
'tag_spec' => array(),
|
|
|
4887 |
),
|
4888 |
),
|
4889 |
'li' => array(
|
4894 |
),
|
4895 |
),
|
4896 |
'tag_spec' => array(),
|
|
|
4897 |
),
|
4898 |
),
|
4899 |
'line' => array(
|
4978 |
'y2' => array(),
|
4979 |
),
|
4980 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
4981 |
'mandatory_ancestor' => 'svg',
|
4982 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
4983 |
),
|
|
|
4984 |
),
|
4985 |
),
|
4986 |
'lineargradient' => array(
|
5061 |
'alternative_names' => array(
|
5062 |
'href',
|
5063 |
),
|
5064 |
+
'value_url' => array(
|
5065 |
+
'allow_empty' => false,
|
5066 |
+
'allow_relative' => true,
|
5067 |
+
'allowed_protocol' => array(
|
5068 |
+
'http',
|
5069 |
+
'https',
|
5070 |
+
),
|
5071 |
),
|
5072 |
),
|
5073 |
'xlink:role' => array(),
|
5082 |
'y2' => array(),
|
5083 |
),
|
5084 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5085 |
'mandatory_ancestor' => 'svg',
|
5086 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
5087 |
),
|
|
|
5088 |
),
|
5089 |
),
|
5090 |
'link' => array(
|
5094 |
'value_casei' => 'utf-8',
|
5095 |
),
|
5096 |
'color' => array(),
|
5097 |
+
'crossorigin' => array(),
|
5098 |
'href' => array(),
|
5099 |
'hreflang' => array(),
|
5100 |
'media' => array(),
|
5101 |
'rel' => array(
|
5102 |
+
'blacklisted_value_regex' => '(^|\\s)(canonical|components|import|manifest|preload|serviceworker|stylesheet|subresource|)(\\s|$)',
|
5103 |
'mandatory' => true,
|
5104 |
),
|
5105 |
'sizes' => array(),
|
5108 |
),
|
5109 |
'tag_spec' => array(
|
5110 |
'disallowed_ancestor' => array(
|
5111 |
+
'template',
|
|
|
|
|
|
|
|
|
5112 |
),
|
5113 |
'spec_name' => 'link rel=',
|
5114 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5115 |
),
|
|
|
5116 |
),
|
5117 |
array(
|
5118 |
'attr_spec_list' => array(
|
5120 |
'value_casei' => 'utf-8',
|
5121 |
),
|
5122 |
'color' => array(),
|
5123 |
+
'crossorigin' => array(),
|
5124 |
'href' => array(
|
5125 |
+
'blacklisted_value_regex' => '__amp_source_origin',
|
5126 |
'mandatory' => true,
|
5127 |
+
'value_url' => array(
|
5128 |
+
'allow_relative' => true,
|
5129 |
+
'allowed_protocol' => array(
|
5130 |
+
'http',
|
5131 |
+
'https',
|
5132 |
+
),
|
5133 |
+
),
|
5134 |
),
|
5135 |
'hreflang' => array(),
|
5136 |
+
'media' => array(),
|
5137 |
+
'rel' => array(
|
5138 |
+
'dispatch_key' => 2,
|
5139 |
'mandatory' => true,
|
5140 |
+
'value_casei' => 'canonical',
|
5141 |
+
),
|
5142 |
+
'sizes' => array(),
|
5143 |
+
'target' => array(),
|
5144 |
+
'type' => array(),
|
5145 |
+
),
|
5146 |
+
'tag_spec' => array(
|
5147 |
+
'mandatory' => true,
|
5148 |
+
'mandatory_parent' => 'head',
|
5149 |
+
'spec_name' => 'link rel=canonical',
|
5150 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#required-markup',
|
5151 |
+
'unique' => true,
|
5152 |
+
),
|
5153 |
+
),
|
5154 |
+
array(
|
5155 |
+
'attr_spec_list' => array(
|
5156 |
+
'charset' => array(
|
5157 |
+
'value_casei' => 'utf-8',
|
5158 |
+
),
|
5159 |
+
'color' => array(),
|
5160 |
+
'crossorigin' => array(),
|
5161 |
+
'href' => array(
|
5162 |
+
'blacklisted_value_regex' => '__amp_source_origin',
|
5163 |
+
'mandatory' => true,
|
5164 |
+
'value_url' => array(
|
5165 |
+
'allow_relative' => true,
|
5166 |
+
'allowed_protocol' => array(
|
5167 |
+
'https',
|
5168 |
+
),
|
5169 |
+
),
|
5170 |
),
|
5171 |
+
'hreflang' => array(),
|
5172 |
'media' => array(),
|
5173 |
+
'rel' => array(
|
5174 |
+
'dispatch_key' => 2,
|
5175 |
+
'mandatory' => true,
|
5176 |
+
'value_casei' => 'manifest',
|
5177 |
+
),
|
5178 |
'sizes' => array(),
|
5179 |
'target' => array(),
|
5180 |
'type' => array(),
|
5181 |
),
|
5182 |
'tag_spec' => array(
|
5183 |
+
'mandatory_parent' => 'head',
|
5184 |
+
'spec_name' => 'link rel=manifest',
|
5185 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5186 |
+
),
|
5187 |
+
),
|
5188 |
+
array(
|
5189 |
+
'attr_spec_list' => array(
|
5190 |
+
'as' => array(),
|
5191 |
+
'charset' => array(
|
5192 |
+
'value_casei' => 'utf-8',
|
5193 |
+
),
|
5194 |
+
'color' => array(),
|
5195 |
+
'crossorigin' => array(),
|
5196 |
+
'href' => array(),
|
5197 |
+
'hreflang' => array(),
|
5198 |
+
'media' => array(),
|
5199 |
+
'rel' => array(
|
5200 |
+
'dispatch_key' => 2,
|
5201 |
+
'mandatory' => true,
|
5202 |
+
'value_casei' => 'preload',
|
5203 |
+
),
|
5204 |
+
'sizes' => array(),
|
5205 |
+
'target' => array(),
|
5206 |
+
'type' => array(),
|
5207 |
+
),
|
5208 |
+
'tag_spec' => array(
|
5209 |
+
'disallowed_ancestor' => array(
|
5210 |
+
'template',
|
5211 |
),
|
5212 |
+
'spec_name' => 'link rel=preload',
|
5213 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5214 |
),
|
5215 |
+
),
|
5216 |
+
array(
|
5217 |
+
'attr_spec_list' => array(
|
5218 |
+
'async' => array(),
|
5219 |
+
'crossorigin' => array(),
|
5220 |
+
'href' => array(
|
5221 |
+
'mandatory' => true,
|
5222 |
+
'value_regex' => 'https://cdn\\.materialdesignicons\\.com/([0-9]+\\.?)+/css/materialdesignicons\\.min\\.css|https://cloud\\.typography\\.com/[0-9]*/[0-9]*/css/fonts\\.css|https://fast\\.fonts\\.net/.*|https://fonts\\.googleapis\\.com/css\\?.*|https://fonts\\.googleapis\\.com/icon\\?.*|https://fonts\\.googleapis\\.com/earlyaccess/.*\\.css|https://maxcdn\\.bootstrapcdn\\.com/font-awesome/([0-9]+\\.?)+/css/font-awesome\\.min\\.css(\\?.*)?|https://use\\.fontawesome\\.com/releases/v([0-9]+\\.?)+/css/(all|brands|solids|fontawesome)\\.css|https://use\\.typekit\\.net/[\\w\\p{L}\\p{N}_]+\\.css',
|
5223 |
+
),
|
5224 |
+
'integrity' => array(),
|
5225 |
+
'media' => array(),
|
5226 |
+
'rel' => array(
|
5227 |
+
'dispatch_key' => 2,
|
5228 |
+
'mandatory' => true,
|
5229 |
+
'value_casei' => 'stylesheet',
|
5230 |
+
),
|
5231 |
+
'type' => array(
|
5232 |
+
'value_casei' => 'text/css',
|
5233 |
+
),
|
5234 |
+
),
|
5235 |
+
'tag_spec' => array(
|
5236 |
+
'mandatory_parent' => 'head',
|
5237 |
+
'spec_name' => 'link rel=stylesheet for fonts',
|
5238 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#custom-fonts',
|
5239 |
+
),
|
5240 |
),
|
5241 |
array(
|
5242 |
'attr_spec_list' => array(
|
5244 |
'value_casei' => 'utf-8',
|
5245 |
),
|
5246 |
'color' => array(),
|
5247 |
+
'crossorigin' => array(),
|
5248 |
'href' => array(
|
5249 |
'mandatory' => true,
|
5250 |
),
|
5251 |
'hreflang' => array(),
|
5252 |
'itemprop' => array(
|
5253 |
+
'dispatch_key' => 2,
|
5254 |
'mandatory' => true,
|
5255 |
+
'value_casei' => 'sameas',
|
5256 |
),
|
5257 |
'media' => array(),
|
5258 |
'sizes' => array(),
|
5260 |
'type' => array(),
|
5261 |
),
|
5262 |
'tag_spec' => array(
|
5263 |
+
'spec_name' => 'link itemprop=sameAs',
|
5264 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5265 |
+
),
|
5266 |
+
),
|
5267 |
+
array(
|
5268 |
+
'attr_spec_list' => array(
|
5269 |
+
'charset' => array(
|
5270 |
+
'value_casei' => 'utf-8',
|
5271 |
+
),
|
5272 |
+
'color' => array(),
|
5273 |
+
'crossorigin' => array(),
|
5274 |
+
'href' => array(
|
5275 |
+
'mandatory' => true,
|
5276 |
+
),
|
5277 |
+
'hreflang' => array(),
|
5278 |
+
'itemprop' => array(
|
5279 |
+
'mandatory' => true,
|
5280 |
),
|
5281 |
+
'media' => array(),
|
5282 |
+
'sizes' => array(),
|
5283 |
+
'target' => array(),
|
5284 |
+
'type' => array(),
|
5285 |
+
),
|
5286 |
+
'tag_spec' => array(
|
5287 |
'spec_name' => 'link itemprop=',
|
5288 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5289 |
),
|
|
|
5290 |
),
|
5291 |
array(
|
5292 |
'attr_spec_list' => array(
|
5294 |
'value_casei' => 'utf-8',
|
5295 |
),
|
5296 |
'color' => array(),
|
5297 |
+
'crossorigin' => array(),
|
5298 |
'href' => array(
|
5299 |
'mandatory' => true,
|
5300 |
),
|
5308 |
'type' => array(),
|
5309 |
),
|
5310 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5311 |
'spec_name' => 'link property=',
|
5312 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5313 |
),
|
|
|
5314 |
),
|
5315 |
),
|
5316 |
'listing' => array(
|
5317 |
array(
|
5318 |
'attr_spec_list' => array(),
|
5319 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
5320 |
),
|
5321 |
),
|
5322 |
'main' => array(
|
5323 |
array(
|
5324 |
'attr_spec_list' => array(),
|
5325 |
'tag_spec' => array(),
|
|
|
5326 |
),
|
5327 |
),
|
5328 |
'mark' => array(
|
5329 |
array(
|
5330 |
'attr_spec_list' => array(),
|
5331 |
'tag_spec' => array(),
|
|
|
5332 |
),
|
5333 |
),
|
5334 |
'marker' => array(
|
5413 |
'xmlns:xlink' => array(),
|
5414 |
),
|
5415 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5416 |
'mandatory_ancestor' => 'svg',
|
5417 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
5418 |
),
|
|
|
5419 |
),
|
5420 |
),
|
5421 |
'mask' => array(
|
5500 |
'y' => array(),
|
5501 |
),
|
5502 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5503 |
'mandatory_ancestor' => 'svg',
|
5504 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
5505 |
),
|
|
|
5506 |
),
|
5507 |
),
|
5508 |
'meta' => array(
|
5509 |
array(
|
5510 |
'attr_spec_list' => array(
|
5511 |
+
'charset' => array(
|
5512 |
+
'dispatch_key' => 1,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5513 |
'mandatory' => true,
|
5514 |
+
'value_casei' => 'utf-8',
|
5515 |
),
|
5516 |
),
|
5517 |
'tag_spec' => array(
|
5518 |
+
'mandatory' => true,
|
5519 |
+
'mandatory_parent' => 'head',
|
5520 |
+
'spec_name' => 'meta charset=utf-8',
|
5521 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#required-markup',
|
5522 |
+
'unique' => true,
|
|
|
|
|
5523 |
),
|
|
|
5524 |
),
|
5525 |
array(
|
5526 |
'attr_spec_list' => array(
|
5527 |
+
'content' => array(
|
5528 |
+
'mandatory' => true,
|
5529 |
+
'value_properties' => array(
|
5530 |
+
'height' => array(),
|
5531 |
+
'initial-scale' => array(),
|
5532 |
+
'maximum-scale' => array(),
|
5533 |
+
'minimum-scale' => array(
|
5534 |
+
'mandatory' => true,
|
5535 |
+
'value_double' => 1.0,
|
5536 |
+
),
|
5537 |
+
'shrink-to-fit' => array(),
|
5538 |
+
'user-scalable' => array(),
|
5539 |
+
'viewport-fit' => array(),
|
5540 |
+
'width' => array(
|
5541 |
+
'mandatory' => true,
|
5542 |
+
'value' => 'device-width',
|
5543 |
+
),
|
5544 |
+
),
|
5545 |
+
),
|
5546 |
'name' => array(
|
5547 |
+
'dispatch_key' => 2,
|
5548 |
+
'mandatory' => true,
|
5549 |
+
'value' => 'viewport',
|
5550 |
),
|
|
|
5551 |
),
|
5552 |
'tag_spec' => array(
|
5553 |
+
'mandatory' => true,
|
5554 |
+
'mandatory_parent' => 'head',
|
5555 |
+
'spec_name' => 'meta name=viewport',
|
5556 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#required-markup',
|
5557 |
+
'unique' => true,
|
5558 |
+
),
|
5559 |
+
),
|
5560 |
+
array(
|
5561 |
+
'attr_spec_list' => array(
|
5562 |
+
'content' => array(
|
5563 |
+
'mandatory' => true,
|
5564 |
+
'value_properties' => array(
|
5565 |
+
'chrome' => array(
|
5566 |
+
'value' => '1',
|
5567 |
+
),
|
5568 |
+
'ie' => array(
|
5569 |
+
'value' => 'edge',
|
5570 |
+
),
|
5571 |
+
),
|
5572 |
+
),
|
5573 |
+
'http-equiv' => array(
|
5574 |
+
'dispatch_key' => 2,
|
5575 |
+
'mandatory' => true,
|
5576 |
+
'value_casei' => 'x-ua-compatible',
|
5577 |
+
),
|
5578 |
+
),
|
5579 |
+
'tag_spec' => array(
|
5580 |
+
'mandatory_ancestor' => 'head',
|
5581 |
+
'spec_name' => 'meta http-equiv=X-UA-Compatible',
|
5582 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5583 |
+
),
|
5584 |
+
),
|
5585 |
+
array(
|
5586 |
+
'attr_spec_list' => array(
|
5587 |
+
'content' => array(
|
5588 |
+
'mandatory' => true,
|
5589 |
+
'value_regex' => '.*app-id=.*',
|
5590 |
+
),
|
5591 |
+
'name' => array(
|
5592 |
+
'dispatch_key' => 2,
|
5593 |
+
'mandatory' => true,
|
5594 |
+
'value_casei' => 'apple-itunes-app',
|
5595 |
+
),
|
5596 |
+
),
|
5597 |
+
'tag_spec' => array(
|
5598 |
+
'mandatory_parent' => 'head',
|
5599 |
+
'spec_name' => 'meta name=apple-itunes-app',
|
5600 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5601 |
+
),
|
5602 |
+
),
|
5603 |
+
array(
|
5604 |
+
'attr_spec_list' => array(
|
5605 |
+
'content' => array(
|
5606 |
+
'mandatory' => true,
|
5607 |
+
),
|
5608 |
+
'name' => array(
|
5609 |
+
'dispatch_key' => 2,
|
5610 |
+
'mandatory' => true,
|
5611 |
+
'value_casei' => 'amp-experiments-opt-in',
|
5612 |
+
),
|
5613 |
+
),
|
5614 |
+
'tag_spec' => array(
|
5615 |
+
'mandatory_parent' => 'head',
|
5616 |
+
'spec_name' => 'meta name=amp-experiments-opt-in',
|
5617 |
+
),
|
5618 |
+
),
|
5619 |
+
array(
|
5620 |
+
'attr_spec_list' => array(
|
5621 |
+
'content' => array(
|
5622 |
+
'mandatory' => true,
|
5623 |
+
'value_url' => array(
|
5624 |
+
'allowed_protocol' => array(
|
5625 |
+
'https',
|
5626 |
+
),
|
5627 |
+
),
|
5628 |
+
),
|
5629 |
+
'name' => array(
|
5630 |
+
'dispatch_key' => 2,
|
5631 |
+
'mandatory' => true,
|
5632 |
+
'value_casei' => 'amp-3p-iframe-src',
|
5633 |
+
),
|
5634 |
+
),
|
5635 |
+
'tag_spec' => array(
|
5636 |
+
'mandatory_parent' => 'head',
|
5637 |
+
'spec_name' => 'meta name=amp-3p-iframe-src',
|
5638 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ad',
|
5639 |
+
),
|
5640 |
+
),
|
5641 |
+
array(
|
5642 |
+
'attr_spec_list' => array(
|
5643 |
+
'content' => array(
|
5644 |
+
'mandatory' => true,
|
5645 |
+
),
|
5646 |
+
'name' => array(
|
5647 |
+
'dispatch_key' => 2,
|
5648 |
+
'mandatory' => true,
|
5649 |
+
'value_casei' => 'amp-experiment-token',
|
5650 |
+
),
|
5651 |
+
),
|
5652 |
+
'tag_spec' => array(
|
5653 |
+
'mandatory_parent' => 'head',
|
5654 |
+
'spec_name' => 'meta name=amp-experiment-token',
|
5655 |
+
),
|
5656 |
+
),
|
5657 |
+
array(
|
5658 |
+
'attr_spec_list' => array(
|
5659 |
+
'content' => array(
|
5660 |
+
'mandatory' => true,
|
5661 |
+
),
|
5662 |
+
'name' => array(
|
5663 |
+
'dispatch_key' => 2,
|
5664 |
+
'mandatory' => true,
|
5665 |
+
'value_casei' => 'amp-link-variable-allowed-origin',
|
5666 |
+
),
|
5667 |
+
),
|
5668 |
+
'tag_spec' => array(
|
5669 |
+
'mandatory_parent' => 'head',
|
5670 |
+
'spec_name' => 'meta name=amp-link-variable-allowed-origin',
|
5671 |
+
),
|
5672 |
+
),
|
5673 |
+
array(
|
5674 |
+
'attr_spec_list' => array(
|
5675 |
+
'content' => array(
|
5676 |
+
'mandatory' => true,
|
5677 |
+
),
|
5678 |
+
'name' => array(
|
5679 |
+
'dispatch_key' => 2,
|
5680 |
+
'mandatory' => true,
|
5681 |
+
'value_casei' => 'amp-google-client-id-api',
|
5682 |
+
),
|
5683 |
+
),
|
5684 |
+
'tag_spec' => array(
|
5685 |
+
'mandatory_parent' => 'head',
|
5686 |
+
'spec_name' => 'meta name=amp-google-clientid-id-api',
|
5687 |
+
),
|
5688 |
+
),
|
5689 |
+
array(
|
5690 |
+
'attr_spec_list' => array(
|
5691 |
+
'name' => array(
|
5692 |
+
'dispatch_key' => 2,
|
5693 |
+
'mandatory' => true,
|
5694 |
+
'value_casei' => 'amp-ad-doubleclick-sra',
|
5695 |
+
),
|
5696 |
+
),
|
5697 |
+
'tag_spec' => array(
|
5698 |
+
'mandatory_parent' => 'head',
|
5699 |
+
'spec_name' => 'meta name=amp-ad-doubleclick-sra',
|
5700 |
+
),
|
5701 |
+
),
|
5702 |
+
array(
|
5703 |
+
'attr_spec_list' => array(
|
5704 |
+
'content' => array(),
|
5705 |
+
'itemprop' => array(),
|
5706 |
+
'name' => array(
|
5707 |
+
'blacklisted_value_regex' => '(^|\\s)(amp-.*|amp4ads-.*|apple-itunes-app|content-disposition|revisit-after|viewport)(\\s|$)',
|
5708 |
+
),
|
5709 |
+
'property' => array(),
|
5710 |
+
'scheme' => array(),
|
5711 |
+
),
|
5712 |
+
'tag_spec' => array(
|
5713 |
'spec_name' => 'meta name= and content=',
|
5714 |
),
|
|
|
5715 |
),
|
5716 |
array(
|
5717 |
'attr_spec_list' => array(
|
5720 |
'value_casei' => 'text/html; charset=utf-8',
|
5721 |
),
|
5722 |
'http-equiv' => array(
|
5723 |
+
'dispatch_key' => 2,
|
5724 |
'mandatory' => true,
|
5725 |
'value_casei' => 'content-type',
|
5726 |
),
|
5727 |
),
|
5728 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5729 |
'mandatory_ancestor' => 'head',
|
5730 |
+
'spec_name' => 'meta http-equiv=Content-Type',
|
5731 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5732 |
),
|
|
|
5733 |
),
|
5734 |
array(
|
5735 |
'attr_spec_list' => array(
|
5737 |
'mandatory' => true,
|
5738 |
),
|
5739 |
'http-equiv' => array(
|
5740 |
+
'dispatch_key' => 2,
|
5741 |
'mandatory' => true,
|
5742 |
'value_casei' => 'content-language',
|
5743 |
),
|
5744 |
),
|
5745 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5746 |
'mandatory_ancestor' => 'head',
|
5747 |
'spec_name' => 'meta http-equiv=content-language',
|
5748 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5749 |
),
|
|
|
5750 |
),
|
5751 |
array(
|
5752 |
'attr_spec_list' => array(
|
5754 |
'mandatory' => true,
|
5755 |
),
|
5756 |
'http-equiv' => array(
|
5757 |
+
'dispatch_key' => 2,
|
5758 |
'mandatory' => true,
|
5759 |
'value_casei' => 'pics-label',
|
5760 |
),
|
5761 |
),
|
5762 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5763 |
'mandatory_ancestor' => 'head',
|
5764 |
'spec_name' => 'meta http-equiv=pics-label',
|
5765 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5766 |
),
|
|
|
5767 |
),
|
5768 |
array(
|
5769 |
'attr_spec_list' => array(
|
5771 |
'mandatory' => true,
|
5772 |
),
|
5773 |
'http-equiv' => array(
|
5774 |
+
'dispatch_key' => 2,
|
5775 |
'mandatory' => true,
|
5776 |
'value_casei' => 'imagetoolbar',
|
5777 |
),
|
5778 |
),
|
5779 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5780 |
'mandatory_ancestor' => 'head',
|
5781 |
'spec_name' => 'meta http-equiv=imagetoolbar',
|
5782 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5783 |
),
|
|
|
5784 |
),
|
5785 |
array(
|
5786 |
'attr_spec_list' => array(
|
5789 |
'value_casei' => 'text/css',
|
5790 |
),
|
5791 |
'http-equiv' => array(
|
5792 |
+
'dispatch_key' => 2,
|
5793 |
'mandatory' => true,
|
5794 |
'value_casei' => 'content-style-type',
|
5795 |
),
|
5796 |
),
|
5797 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5798 |
'mandatory_ancestor' => 'head',
|
5799 |
+
'spec_name' => 'meta http-equiv=Content-Style-Type',
|
5800 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5801 |
),
|
|
|
5802 |
),
|
5803 |
array(
|
5804 |
'attr_spec_list' => array(
|
5807 |
'value_casei' => 'text/javascript',
|
5808 |
),
|
5809 |
'http-equiv' => array(
|
5810 |
+
'dispatch_key' => 2,
|
5811 |
'mandatory' => true,
|
5812 |
'value_casei' => 'content-script-type',
|
5813 |
),
|
5814 |
),
|
5815 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5816 |
'mandatory_ancestor' => 'head',
|
5817 |
+
'spec_name' => 'meta http-equiv=Content-Script-Type',
|
5818 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5819 |
),
|
|
|
5820 |
),
|
5821 |
array(
|
5822 |
'attr_spec_list' => array(
|
5824 |
'mandatory' => true,
|
5825 |
),
|
5826 |
'http-equiv' => array(
|
5827 |
+
'dispatch_key' => 2,
|
5828 |
'mandatory' => true,
|
5829 |
'value_casei' => 'origin-trial',
|
5830 |
),
|
5831 |
),
|
5832 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5833 |
'mandatory_ancestor' => 'head',
|
5834 |
'spec_name' => 'meta http-equiv=origin-trial',
|
5835 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5836 |
),
|
|
|
5837 |
),
|
5838 |
array(
|
5839 |
'attr_spec_list' => array(
|
5841 |
'mandatory' => true,
|
5842 |
),
|
5843 |
'http-equiv' => array(
|
5844 |
+
'dispatch_key' => 2,
|
5845 |
'mandatory' => true,
|
5846 |
'value_casei' => 'resource-type',
|
5847 |
),
|
5848 |
),
|
5849 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5850 |
'mandatory_ancestor' => 'head',
|
5851 |
'spec_name' => 'meta http-equiv=resource-type',
|
5852 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5853 |
),
|
5854 |
+
),
|
5855 |
+
array(
|
5856 |
+
'attr_spec_list' => array(
|
5857 |
+
'content' => array(
|
5858 |
+
'mandatory' => true,
|
5859 |
+
'value_regex_casei' => '(off|on)',
|
5860 |
+
),
|
5861 |
+
'http-equiv' => array(
|
5862 |
+
'dispatch_key' => 2,
|
5863 |
+
'mandatory' => true,
|
5864 |
+
'value_casei' => 'x-dns-prefetch-control',
|
5865 |
+
),
|
5866 |
+
),
|
5867 |
+
'tag_spec' => array(
|
5868 |
+
'mandatory_ancestor' => 'head',
|
5869 |
+
'spec_name' => 'meta http-equiv=x-dns-prefetch-control',
|
5870 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#html-tags',
|
5871 |
+
),
|
5872 |
+
),
|
5873 |
+
array(
|
5874 |
+
'attr_spec_list' => array(
|
5875 |
+
'content' => array(
|
5876 |
+
'mandatory' => true,
|
5877 |
+
),
|
5878 |
+
'name' => array(
|
5879 |
+
'dispatch_key' => 2,
|
5880 |
+
'mandatory' => true,
|
5881 |
+
'value_casei' => 'amp-ad-enable-refresh',
|
5882 |
+
),
|
5883 |
+
),
|
5884 |
+
'tag_spec' => array(
|
5885 |
+
'mandatory_ancestor' => 'head',
|
5886 |
+
'spec_name' => 'meta name=amp-ad-enable-refresh',
|
5887 |
+
),
|
5888 |
+
),
|
5889 |
+
array(
|
5890 |
+
'attr_spec_list' => array(
|
5891 |
+
'content' => array(
|
5892 |
+
'mandatory' => true,
|
5893 |
+
),
|
5894 |
+
'name' => array(
|
5895 |
+
'dispatch_key' => 2,
|
5896 |
+
'mandatory' => true,
|
5897 |
+
'value_casei' => 'amp-to-amp-navigation',
|
5898 |
+
),
|
5899 |
+
),
|
5900 |
+
'tag_spec' => array(
|
5901 |
+
'mandatory_parent' => 'head',
|
5902 |
+
'spec_name' => 'meta name=amp-to-amp-navigation',
|
5903 |
+
'unique' => true,
|
5904 |
+
),
|
5905 |
),
|
5906 |
),
|
5907 |
'metadata' => array(
|
5916 |
'xmlns:xlink' => array(),
|
5917 |
),
|
5918 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
5919 |
'mandatory_ancestor' => 'svg',
|
5920 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
5921 |
),
|
|
|
5922 |
),
|
5923 |
),
|
5924 |
'meter' => array(
|
5932 |
'value' => array(),
|
5933 |
),
|
5934 |
'tag_spec' => array(),
|
|
|
5935 |
),
|
5936 |
),
|
5937 |
'multicol' => array(
|
5938 |
array(
|
5939 |
'attr_spec_list' => array(),
|
5940 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
5941 |
),
|
5942 |
),
|
5943 |
'nav' => array(
|
5944 |
array(
|
5945 |
'attr_spec_list' => array(),
|
5946 |
'tag_spec' => array(),
|
|
|
5947 |
),
|
5948 |
array(
|
5949 |
'attr_spec_list' => array(
|
5950 |
'toolbar' => array(
|
5951 |
+
'dispatch_key' => 1,
|
5952 |
'mandatory' => true,
|
5953 |
),
|
5954 |
'toolbar-target' => array(
|
5956 |
),
|
5957 |
),
|
5958 |
'tag_spec' => array(
|
|
|
|
|
|
|
5959 |
'mandatory_parent' => 'amp-sidebar',
|
5960 |
'spec_name' => 'amp-sidebar > nav',
|
5961 |
),
|
|
|
5962 |
),
|
5963 |
),
|
5964 |
'nextid' => array(
|
5965 |
array(
|
5966 |
'attr_spec_list' => array(),
|
5967 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
5968 |
),
|
5969 |
),
|
5970 |
'nobr' => array(
|
5971 |
array(
|
5972 |
'attr_spec_list' => array(),
|
5973 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
5974 |
),
|
5975 |
),
|
5976 |
'noscript' => array(
|
5977 |
+
array(
|
5978 |
+
'attr_spec_list' => array(),
|
5979 |
+
'tag_spec' => array(
|
5980 |
+
'mandatory' => true,
|
5981 |
+
'mandatory_parent' => 'head',
|
5982 |
+
'spec_name' => 'noscript enclosure for boilerplate',
|
5983 |
+
'spec_url' => 'https://github.com/ampproject/amphtml/blob/master/spec/amp-boilerplate.md',
|
5984 |
+
'unique' => true,
|
5985 |
+
),
|
5986 |
+
),
|
5987 |
array(
|
5988 |
'attr_spec_list' => array(),
|
5989 |
'tag_spec' => array(
|
5990 |
'disallowed_ancestor' => array(
|
5991 |
+
'noscript',
|
|
|
|
|
|
|
5992 |
),
|
5993 |
'mandatory_ancestor' => 'body',
|
5994 |
),
|
|
|
5995 |
),
|
5996 |
),
|
5997 |
'o:p' => array(
|
5998 |
array(
|
5999 |
'attr_spec_list' => array(),
|
6000 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
6001 |
),
|
6002 |
),
|
6003 |
'ol' => array(
|
6010 |
'value_regex' => '[0-9]*',
|
6011 |
),
|
6012 |
'type' => array(
|
6013 |
+
'value_regex' => '[1AaIi]',
|
6014 |
),
|
6015 |
),
|
6016 |
'tag_spec' => array(),
|
|
|
6017 |
),
|
6018 |
),
|
6019 |
'optgroup' => array(
|
6028 |
'mandatory_parent' => 'select',
|
6029 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
6030 |
),
|
|
|
6031 |
),
|
6032 |
),
|
6033 |
'option' => array(
|
6045 |
'tag_spec' => array(
|
6046 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
6047 |
),
|
|
|
6048 |
),
|
6049 |
),
|
6050 |
'output' => array(
|
6053 |
'for' => array(),
|
6054 |
'form' => array(),
|
6055 |
'name' => array(
|
6056 |
+
'blacklisted_value_regex' => '(^|\\s)(__amp_\\S*|__count__|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|__noSuchMethod__|__parent__|__proto__|__AMP_\\S*|\\$p|\\$proxy|acceptCharset|addEventListener|appendChild|assignedSlot|attachShadow|baseURI|checkValidity|childElementCount|childNodes|classList|className|clientHeight|clientLeft|clientTop|clientWidth|compareDocumentPosition|computedName|computedRole|contentEditable|createShadowRoot|enqueAction|firstChild|firstElementChild|getAnimations|getAttribute|getAttributeNS|getAttributeNode|getAttributeNodeNS|getBoundingClientRect|getClientRects|getDestinationInsertionPoints|getElementsByClassName|getElementsByTagName|getElementsByTagNameNS|getRootNode|hasAttribute|hasAttributeNS|hasAttributes|hasChildNodes|hasPointerCapture|innerHTML|innerText|inputMode|insertAdjacentElement|insertAdjacentHTML|insertAdjacentText|isContentEditable|isDefaultNamespace|isEqualNode|isSameNode|lastChild|lastElementChild|lookupNamespaceURI|namespaceURI|nextElementSibling|nextSibling|nodeName|nodeType|nodeValue|offsetHeight|offsetLeft|offsetParent|offsetTop|offsetWidth|outerHTML|outerText|ownerDocument|parentElement|parentNode|previousElementSibling|previousSibling|querySelector|querySelectorAll|releasePointerCapture|removeAttribute|removeAttributeNS|removeAttributeNode|removeChild|removeEventListener|replaceChild|reportValidity|requestPointerLock|scrollHeight|scrollIntoView|scrollIntoViewIfNeeded|scrollLeft|scrollWidth|setAttribute|setAttributeNS|setAttributeNode|setAttributeNodeNS|setPointerCapture|shadowRoot|styleMap|tabIndex|tagName|textContent|toString|valueOf|(webkit|ms|moz|o)dropzone|(webkit|moz|ms|o)MatchesSelector|(webkit|moz|ms|o)RequestFullScreen|(webkit|moz|ms|o)RequestFullscreen)(\\s|$)',
|
6057 |
),
|
6058 |
),
|
6059 |
'tag_spec' => array(),
|
|
|
6060 |
),
|
6061 |
),
|
6062 |
'p' => array(
|
6065 |
'align' => array(),
|
6066 |
),
|
6067 |
'tag_spec' => array(),
|
|
|
6068 |
),
|
6069 |
),
|
6070 |
'path' => array(
|
6147 |
'xmlns:xlink' => array(),
|
6148 |
),
|
6149 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
6150 |
'mandatory_ancestor' => 'svg',
|
6151 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6152 |
),
|
|
|
6153 |
),
|
6154 |
),
|
6155 |
'pattern' => array(
|
6236 |
'alternative_names' => array(
|
6237 |
'href',
|
6238 |
),
|
6239 |
+
'value_url' => array(
|
6240 |
+
'allow_empty' => false,
|
6241 |
+
'allow_relative' => true,
|
6242 |
+
'allowed_protocol' => array(
|
6243 |
+
'http',
|
6244 |
+
'https',
|
6245 |
+
),
|
6246 |
),
|
6247 |
),
|
6248 |
'xlink:role' => array(),
|
6256 |
'y' => array(),
|
6257 |
),
|
6258 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
6259 |
'mandatory_ancestor' => 'svg',
|
6260 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6261 |
),
|
|
|
6262 |
),
|
6263 |
),
|
6264 |
'polygon' => array(
|
6340 |
'xmlns:xlink' => array(),
|
6341 |
),
|
6342 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
6343 |
'mandatory_ancestor' => 'svg',
|
6344 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6345 |
),
|
|
|
6346 |
),
|
6347 |
),
|
6348 |
'polyline' => array(
|
6424 |
'xmlns:xlink' => array(),
|
6425 |
),
|
6426 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
6427 |
'mandatory_ancestor' => 'svg',
|
6428 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6429 |
),
|
|
|
6430 |
),
|
6431 |
),
|
6432 |
'pre' => array(
|
6433 |
array(
|
6434 |
'attr_spec_list' => array(),
|
6435 |
'tag_spec' => array(),
|
|
|
6436 |
),
|
6437 |
),
|
6438 |
'progress' => array(
|
6442 |
'value' => array(),
|
6443 |
),
|
6444 |
'tag_spec' => array(),
|
|
|
6445 |
),
|
6446 |
),
|
6447 |
'q' => array(
|
6449 |
'attr_spec_list' => array(
|
6450 |
'cite' => array(
|
6451 |
'blacklisted_value_regex' => '__amp_source_origin',
|
6452 |
+
'value_url' => array(
|
6453 |
+
'allow_empty' => true,
|
6454 |
+
'allow_relative' => true,
|
6455 |
+
'allowed_protocol' => array(
|
6456 |
+
'http',
|
6457 |
+
'https',
|
6458 |
+
),
|
6459 |
),
|
6460 |
),
|
6461 |
),
|
6462 |
'tag_spec' => array(),
|
|
|
6463 |
),
|
6464 |
),
|
6465 |
'radialgradient' => array(
|
6544 |
'alternative_names' => array(
|
6545 |
'href',
|
6546 |
),
|
6547 |
+
'value_url' => array(
|
6548 |
+
'allow_empty' => false,
|
6549 |
+
'allow_relative' => true,
|
6550 |
+
'allowed_protocol' => array(
|
6551 |
+
'http',
|
6552 |
+
'https',
|
6553 |
+
),
|
6554 |
),
|
6555 |
),
|
6556 |
'xlink:role' => array(),
|
6563 |
'xmlns:xlink' => array(),
|
6564 |
),
|
6565 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
6566 |
'mandatory_ancestor' => 'svg',
|
6567 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6568 |
),
|
|
|
6569 |
),
|
6570 |
),
|
6571 |
'rb' => array(
|
6572 |
array(
|
6573 |
'attr_spec_list' => array(),
|
6574 |
'tag_spec' => array(),
|
|
|
6575 |
),
|
6576 |
),
|
6577 |
'rect' => array(
|
6658 |
'y' => array(),
|
6659 |
),
|
6660 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
6661 |
'mandatory_ancestor' => 'svg',
|
6662 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
6663 |
),
|
|
|
6664 |
),
|
6665 |
),
|
6666 |
'rp' => array(
|
6667 |
array(
|
6668 |
'attr_spec_list' => array(),
|
6669 |
'tag_spec' => array(),
|
|
|
6670 |
),
|
6671 |
),
|
6672 |
'rt' => array(
|
6673 |
array(
|
6674 |
'attr_spec_list' => array(),
|
6675 |
'tag_spec' => array(),
|
|
|
6676 |
),
|
6677 |
),
|
6678 |
'rtc' => array(
|
6679 |
array(
|
6680 |
'attr_spec_list' => array(),
|
6681 |
'tag_spec' => array(),
|
|
|
6682 |
),
|
6683 |
),
|
6684 |
'ruby' => array(
|
6685 |
array(
|
6686 |
'attr_spec_list' => array(),
|
6687 |
'tag_spec' => array(),
|
|
|
6688 |
),
|
6689 |
),
|
6690 |
's' => array(
|
6691 |
array(
|
6692 |
'attr_spec_list' => array(),
|
6693 |
'tag_spec' => array(),
|
|
|
6694 |
),
|
6695 |
),
|
6696 |
'samp' => array(
|
6697 |
array(
|
6698 |
'attr_spec_list' => array(),
|
6699 |
'tag_spec' => array(),
|
|
|
6700 |
),
|
6701 |
),
|
6702 |
'script' => array(
|
6703 |
+
array(
|
6704 |
+
'attr_spec_list' => array(
|
6705 |
+
'async' => array(
|
6706 |
+
'mandatory' => true,
|
6707 |
+
'value' => '',
|
6708 |
+
),
|
6709 |
+
'nonce' => array(),
|
6710 |
+
'src' => array(
|
6711 |
+
'dispatch_key' => 2,
|
6712 |
+
'mandatory' => true,
|
6713 |
+
'value' => 'https://cdn.ampproject.org/v0.js',
|
6714 |
+
),
|
6715 |
+
'type' => array(
|
6716 |
+
'value_casei' => 'text/javascript',
|
6717 |
+
),
|
6718 |
+
),
|
6719 |
+
'cdata' => array(
|
6720 |
+
'blacklisted_cdata_regex' => array(
|
6721 |
+
'error_message' => 'contents',
|
6722 |
+
'regex' => '.',
|
6723 |
+
),
|
6724 |
+
),
|
6725 |
+
'tag_spec' => array(
|
6726 |
+
'mandatory' => true,
|
6727 |
+
'mandatory_parent' => 'head',
|
6728 |
+
'spec_name' => 'amphtml engine v0.js script',
|
6729 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#required-markup',
|
6730 |
+
'unique' => true,
|
6731 |
+
),
|
6732 |
+
),
|
6733 |
array(
|
6734 |
'attr_spec_list' => array(
|
6735 |
'nonce' => array(),
|
6736 |
'type' => array(
|
6737 |
+
'dispatch_key' => 2,
|
6738 |
'mandatory' => true,
|
6739 |
'value_casei' => 'application/ld+json',
|
6740 |
),
|
6741 |
),
|
6742 |
+
'cdata' => array(
|
6743 |
+
'blacklisted_cdata_regex' => array(
|
6744 |
+
'error_message' => 'html comments',
|
6745 |
+
'regex' => '<!--',
|
6746 |
+
),
|
6747 |
+
),
|
6748 |
'tag_spec' => array(
|
6749 |
'spec_name' => 'script type=application/ld+json',
|
6750 |
),
|
|
|
6751 |
),
|
6752 |
array(
|
6753 |
'attr_spec_list' => array(
|
6754 |
+
'id' => array(
|
6755 |
+
'dispatch_key' => 2,
|
6756 |
+
'mandatory' => true,
|
6757 |
+
'value_casei' => 'amp-rtc',
|
6758 |
+
),
|
6759 |
+
'nonce' => array(),
|
6760 |
'type' => array(
|
6761 |
'mandatory' => true,
|
6762 |
'value_casei' => 'application/json',
|
6763 |
),
|
6764 |
),
|
6765 |
+
'cdata' => array(
|
6766 |
+
'blacklisted_cdata_regex' => array(
|
6767 |
+
'error_message' => 'html comments',
|
6768 |
+
'regex' => '<!--',
|
6769 |
+
),
|
6770 |
+
),
|
6771 |
'tag_spec' => array(
|
6772 |
+
'mandatory_parent' => 'head',
|
6773 |
+
'spec_name' => 'script id=amp-rtc',
|
6774 |
+
'unique' => true,
|
6775 |
+
),
|
6776 |
+
),
|
6777 |
+
array(
|
6778 |
+
'attr_spec_list' => array(
|
6779 |
+
'type' => array(
|
6780 |
+
'dispatch_key' => 3,
|
6781 |
+
'mandatory' => true,
|
6782 |
+
'value_casei' => 'application/json',
|
6783 |
+
),
|
6784 |
+
),
|
6785 |
+
'cdata' => array(
|
6786 |
+
'blacklisted_cdata_regex' => array(
|
6787 |
+
'error_message' => 'html comments',
|
6788 |
+
'regex' => '<!--',
|
6789 |
+
),
|
6790 |
+
),
|
6791 |
+
'tag_spec' => array(
|
6792 |
+
'mandatory_parent' => 'amp-ima-video',
|
6793 |
+
'spec_name' => 'amp-ima-video > script[type=application/json]',
|
6794 |
+
),
|
6795 |
+
),
|
6796 |
+
array(
|
6797 |
+
'attr_spec_list' => array(
|
6798 |
+
'async' => array(
|
6799 |
+
'mandatory' => true,
|
6800 |
+
'value' => '',
|
6801 |
+
),
|
6802 |
+
'nonce' => array(),
|
6803 |
+
'type' => array(
|
6804 |
+
'value_casei' => 'text/javascript',
|
6805 |
+
),
|
6806 |
+
),
|
6807 |
+
'tag_spec' => array(
|
6808 |
+
'extension_spec' => array(
|
6809 |
+
'allowed_versions' => array(
|
6810 |
+
'0.1',
|
6811 |
+
'latest',
|
6812 |
+
),
|
6813 |
+
'name' => 'amp-3q-player',
|
6814 |
+
),
|
6815 |
+
),
|
6816 |
+
),
|
6817 |
+
array(
|
6818 |
+
'attr_spec_list' => array(
|
6819 |
+
'async' => array(
|
6820 |
+
'mandatory' => true,
|
6821 |
+
'value' => '',
|
6822 |
+
),
|
6823 |
+
'nonce' => array(),
|
6824 |
+
'type' => array(
|
6825 |
+
'value_casei' => 'text/javascript',
|
6826 |
+
),
|
6827 |
+
),
|
6828 |
+
'tag_spec' => array(
|
6829 |
+
'extension_spec' => array(
|
6830 |
+
'allowed_versions' => array(
|
6831 |
+
'0.1',
|
6832 |
+
'latest',
|
6833 |
+
),
|
6834 |
+
'name' => 'amp-access-laterpay',
|
6835 |
+
'requires_usage' => 3,
|
6836 |
+
),
|
6837 |
+
'requires_extension' => array(
|
6838 |
+
'amp-access',
|
6839 |
+
),
|
6840 |
+
),
|
6841 |
+
),
|
6842 |
+
array(
|
6843 |
+
'attr_spec_list' => array(
|
6844 |
+
'async' => array(
|
6845 |
+
'mandatory' => true,
|
6846 |
+
'value' => '',
|
6847 |
+
),
|
6848 |
+
'nonce' => array(),
|
6849 |
+
'type' => array(
|
6850 |
+
'value_casei' => 'text/javascript',
|
6851 |
+
),
|
6852 |
+
),
|
6853 |
+
'tag_spec' => array(
|
6854 |
+
'extension_spec' => array(
|
6855 |
+
'allowed_versions' => array(
|
6856 |
+
'0.1',
|
6857 |
+
'latest',
|
6858 |
+
),
|
6859 |
+
'name' => 'amp-access-scroll',
|
6860 |
+
'requires_usage' => 3,
|
6861 |
+
),
|
6862 |
+
'requires_extension' => array(
|
6863 |
+
'amp-access',
|
6864 |
+
),
|
6865 |
+
),
|
6866 |
+
),
|
6867 |
+
array(
|
6868 |
+
'attr_spec_list' => array(
|
6869 |
+
'async' => array(
|
6870 |
+
'mandatory' => true,
|
6871 |
+
'value' => '',
|
6872 |
+
),
|
6873 |
+
'nonce' => array(),
|
6874 |
+
'type' => array(
|
6875 |
+
'value_casei' => 'text/javascript',
|
6876 |
+
),
|
6877 |
+
),
|
6878 |
+
'tag_spec' => array(
|
6879 |
+
'extension_spec' => array(
|
6880 |
+
'allowed_versions' => array(
|
6881 |
+
'0.1',
|
6882 |
+
'latest',
|
6883 |
+
),
|
6884 |
+
'deprecated_allow_duplicates' => true,
|
6885 |
+
'name' => 'amp-access',
|
6886 |
+
'requires_usage' => 2,
|
6887 |
+
),
|
6888 |
+
),
|
6889 |
+
),
|
6890 |
+
array(
|
6891 |
+
'attr_spec_list' => array(
|
6892 |
+
'id' => array(
|
6893 |
+
'dispatch_key' => 2,
|
6894 |
+
'mandatory' => true,
|
6895 |
+
'value' => 'amp-access',
|
6896 |
+
),
|
6897 |
+
'nonce' => array(),
|
6898 |
+
'type' => array(
|
6899 |
+
'mandatory' => true,
|
6900 |
+
'value_casei' => 'application/json',
|
6901 |
+
),
|
6902 |
+
),
|
6903 |
+
'cdata' => array(
|
6904 |
+
'blacklisted_cdata_regex' => array(
|
6905 |
+
'error_message' => 'html comments',
|
6906 |
+
'regex' => '<!--',
|
6907 |
+
),
|
6908 |
+
),
|
6909 |
+
'tag_spec' => array(
|
6910 |
+
'mandatory_parent' => 'head',
|
6911 |
+
'requires_extension' => array(
|
6912 |
+
'amp-access',
|
6913 |
+
'amp-analytics',
|
6914 |
+
),
|
6915 |
+
'spec_name' => 'amp-access extension .json script',
|
6916 |
+
'unique' => true,
|
6917 |
+
),
|
6918 |
+
),
|
6919 |
+
array(
|
6920 |
+
'attr_spec_list' => array(
|
6921 |
+
'async' => array(
|
6922 |
+
'mandatory' => true,
|
6923 |
+
'value' => '',
|
6924 |
+
),
|
6925 |
+
'nonce' => array(),
|
6926 |
+
'type' => array(
|
6927 |
+
'value_casei' => 'text/javascript',
|
6928 |
+
),
|
6929 |
+
),
|
6930 |
+
'tag_spec' => array(
|
6931 |
+
'extension_spec' => array(
|
6932 |
+
'allowed_versions' => array(
|
6933 |
+
'0.1',
|
6934 |
+
'latest',
|
6935 |
+
),
|
6936 |
+
'deprecated_allow_duplicates' => true,
|
6937 |
+
'name' => 'amp-accordion',
|
6938 |
+
'requires_usage' => 2,
|
6939 |
+
),
|
6940 |
+
),
|
6941 |
+
),
|
6942 |
+
array(
|
6943 |
+
'attr_spec_list' => array(
|
6944 |
+
'async' => array(
|
6945 |
+
'mandatory' => true,
|
6946 |
+
'value' => '',
|
6947 |
+
),
|
6948 |
+
'nonce' => array(),
|
6949 |
+
'type' => array(
|
6950 |
+
'value_casei' => 'text/javascript',
|
6951 |
+
),
|
6952 |
+
),
|
6953 |
+
'tag_spec' => array(
|
6954 |
+
'extension_spec' => array(
|
6955 |
+
'allowed_versions' => array(
|
6956 |
+
'0.1',
|
6957 |
+
'latest',
|
6958 |
+
),
|
6959 |
+
'deprecated_allow_duplicates' => true,
|
6960 |
+
'name' => 'amp-ad',
|
6961 |
+
'requires_usage' => 2,
|
6962 |
+
),
|
6963 |
+
'spec_name' => 'amp-ad extension .js script',
|
6964 |
+
),
|
6965 |
+
),
|
6966 |
+
array(
|
6967 |
+
'attr_spec_list' => array(
|
6968 |
+
'async' => array(
|
6969 |
+
'mandatory' => true,
|
6970 |
+
'value' => '',
|
6971 |
+
),
|
6972 |
+
'nonce' => array(),
|
6973 |
+
'type' => array(
|
6974 |
+
'value_casei' => 'text/javascript',
|
6975 |
+
),
|
6976 |
+
),
|
6977 |
+
'tag_spec' => array(
|
6978 |
+
'extension_spec' => array(
|
6979 |
+
'allowed_versions' => array(
|
6980 |
+
'0.1',
|
6981 |
+
'latest',
|
6982 |
+
),
|
6983 |
+
'deprecated_allow_duplicates' => true,
|
6984 |
+
'name' => 'amp-analytics',
|
6985 |
+
'requires_usage' => 2,
|
6986 |
+
),
|
6987 |
+
),
|
6988 |
+
),
|
6989 |
+
array(
|
6990 |
+
'attr_spec_list' => array(
|
6991 |
+
'nonce' => array(),
|
6992 |
+
'type' => array(
|
6993 |
+
'dispatch_key' => 3,
|
6994 |
+
'mandatory' => true,
|
6995 |
+
'value_casei' => 'application/json',
|
6996 |
+
),
|
6997 |
+
),
|
6998 |
+
'cdata' => array(
|
6999 |
+
'blacklisted_cdata_regex' => array(
|
7000 |
+
'error_message' => 'html comments',
|
7001 |
+
'regex' => '<!--',
|
7002 |
+
),
|
7003 |
+
),
|
7004 |
+
'tag_spec' => array(
|
7005 |
+
'mandatory_parent' => 'amp-analytics',
|
7006 |
+
'requires_extension' => array(
|
7007 |
+
'amp-analytics',
|
7008 |
+
),
|
7009 |
+
'spec_name' => 'amp-analytics extension .json script',
|
7010 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-analytics',
|
7011 |
+
),
|
7012 |
+
),
|
7013 |
+
array(
|
7014 |
+
'attr_spec_list' => array(
|
7015 |
+
'async' => array(
|
7016 |
+
'mandatory' => true,
|
7017 |
+
'value' => '',
|
7018 |
+
),
|
7019 |
+
'nonce' => array(),
|
7020 |
+
'type' => array(
|
7021 |
+
'value_casei' => 'text/javascript',
|
7022 |
+
),
|
7023 |
+
),
|
7024 |
+
'tag_spec' => array(
|
7025 |
+
'extension_spec' => array(
|
7026 |
+
'allowed_versions' => array(
|
7027 |
+
'0.1',
|
7028 |
+
'latest',
|
7029 |
+
),
|
7030 |
+
'deprecated_allow_duplicates' => true,
|
7031 |
+
'name' => 'amp-anim',
|
7032 |
+
'requires_usage' => 2,
|
7033 |
+
),
|
7034 |
+
),
|
7035 |
+
),
|
7036 |
+
array(
|
7037 |
+
'attr_spec_list' => array(
|
7038 |
+
'async' => array(
|
7039 |
+
'mandatory' => true,
|
7040 |
+
'value' => '',
|
7041 |
+
),
|
7042 |
+
'nonce' => array(),
|
7043 |
+
'type' => array(
|
7044 |
+
'value_casei' => 'text/javascript',
|
7045 |
+
),
|
7046 |
+
),
|
7047 |
+
'tag_spec' => array(
|
7048 |
+
'extension_spec' => array(
|
7049 |
+
'allowed_versions' => array(
|
7050 |
+
'0.1',
|
7051 |
+
'latest',
|
7052 |
+
),
|
7053 |
+
'name' => 'amp-animation',
|
7054 |
+
),
|
7055 |
+
),
|
7056 |
+
),
|
7057 |
+
array(
|
7058 |
+
'attr_spec_list' => array(
|
7059 |
+
'nonce' => array(),
|
7060 |
+
'type' => array(
|
7061 |
+
'dispatch_key' => 3,
|
7062 |
+
'mandatory' => true,
|
7063 |
+
'value_casei' => 'application/json',
|
7064 |
+
),
|
7065 |
+
),
|
7066 |
+
'cdata' => array(
|
7067 |
+
'blacklisted_cdata_regex' => array(
|
7068 |
+
'error_message' => 'html comments',
|
7069 |
+
'regex' => '<!--',
|
7070 |
+
),
|
7071 |
+
),
|
7072 |
+
'tag_spec' => array(
|
7073 |
+
'mandatory_parent' => 'amp-animation',
|
7074 |
+
'requires_extension' => array(
|
7075 |
+
'amp-animation',
|
7076 |
+
),
|
7077 |
+
'spec_name' => 'amp-animation extension .json script',
|
7078 |
+
),
|
7079 |
+
),
|
7080 |
+
array(
|
7081 |
+
'attr_spec_list' => array(
|
7082 |
+
'async' => array(
|
7083 |
+
'mandatory' => true,
|
7084 |
+
'value' => '',
|
7085 |
+
),
|
7086 |
+
'nonce' => array(),
|
7087 |
+
'type' => array(
|
7088 |
+
'value_casei' => 'text/javascript',
|
7089 |
+
),
|
7090 |
+
),
|
7091 |
+
'tag_spec' => array(
|
7092 |
+
'extension_spec' => array(
|
7093 |
+
'allowed_versions' => array(
|
7094 |
+
'0.1',
|
7095 |
+
'latest',
|
7096 |
+
),
|
7097 |
+
'deprecated_allow_duplicates' => true,
|
7098 |
+
'name' => 'amp-apester-media',
|
7099 |
+
'requires_usage' => 2,
|
7100 |
+
),
|
7101 |
+
),
|
7102 |
+
),
|
7103 |
+
array(
|
7104 |
+
'attr_spec_list' => array(
|
7105 |
+
'async' => array(
|
7106 |
+
'mandatory' => true,
|
7107 |
+
'value' => '',
|
7108 |
+
),
|
7109 |
+
'nonce' => array(),
|
7110 |
+
'type' => array(
|
7111 |
+
'value_casei' => 'text/javascript',
|
7112 |
+
),
|
7113 |
+
),
|
7114 |
+
'tag_spec' => array(
|
7115 |
+
'extension_spec' => array(
|
7116 |
+
'allowed_versions' => array(
|
7117 |
+
'0.1',
|
7118 |
+
'latest',
|
7119 |
+
),
|
7120 |
+
'deprecated_allow_duplicates' => true,
|
7121 |
+
'name' => 'amp-app-banner',
|
7122 |
+
),
|
7123 |
+
),
|
7124 |
+
),
|
7125 |
+
array(
|
7126 |
+
'attr_spec_list' => array(
|
7127 |
+
'async' => array(
|
7128 |
+
'mandatory' => true,
|
7129 |
+
'value' => '',
|
7130 |
+
),
|
7131 |
+
'nonce' => array(),
|
7132 |
+
'type' => array(
|
7133 |
+
'value_casei' => 'text/javascript',
|
7134 |
+
),
|
7135 |
+
),
|
7136 |
+
'tag_spec' => array(
|
7137 |
+
'extension_spec' => array(
|
7138 |
+
'allowed_versions' => array(
|
7139 |
+
'0.1',
|
7140 |
+
'latest',
|
7141 |
+
),
|
7142 |
+
'deprecated_allow_duplicates' => true,
|
7143 |
+
'name' => 'amp-audio',
|
7144 |
+
'requires_usage' => 2,
|
7145 |
+
),
|
7146 |
+
),
|
7147 |
+
),
|
7148 |
+
array(
|
7149 |
+
'attr_spec_list' => array(
|
7150 |
+
'async' => array(
|
7151 |
+
'mandatory' => true,
|
7152 |
+
'value' => '',
|
7153 |
+
),
|
7154 |
+
'nonce' => array(),
|
7155 |
+
'type' => array(
|
7156 |
+
'value_casei' => 'text/javascript',
|
7157 |
+
),
|
7158 |
+
),
|
7159 |
+
'tag_spec' => array(
|
7160 |
+
'extension_spec' => array(
|
7161 |
+
'allowed_versions' => array(
|
7162 |
+
'0.1',
|
7163 |
+
'latest',
|
7164 |
+
),
|
7165 |
+
'name' => 'amp-auto-ads',
|
7166 |
+
),
|
7167 |
+
),
|
7168 |
+
),
|
7169 |
+
array(
|
7170 |
+
'attr_spec_list' => array(
|
7171 |
+
'async' => array(
|
7172 |
+
'mandatory' => true,
|
7173 |
+
'value' => '',
|
7174 |
+
),
|
7175 |
+
'nonce' => array(),
|
7176 |
+
'type' => array(
|
7177 |
+
'value_casei' => 'text/javascript',
|
7178 |
+
),
|
7179 |
+
),
|
7180 |
+
'tag_spec' => array(
|
7181 |
+
'extension_spec' => array(
|
7182 |
+
'allowed_versions' => array(
|
7183 |
+
'0.1',
|
7184 |
+
'latest',
|
7185 |
+
),
|
7186 |
+
'name' => 'amp-beopinion',
|
7187 |
+
),
|
7188 |
+
),
|
7189 |
+
),
|
7190 |
+
array(
|
7191 |
+
'attr_spec_list' => array(
|
7192 |
+
'async' => array(
|
7193 |
+
'mandatory' => true,
|
7194 |
+
'value' => '',
|
7195 |
+
),
|
7196 |
+
'nonce' => array(),
|
7197 |
+
'type' => array(
|
7198 |
+
'value_casei' => 'text/javascript',
|
7199 |
+
),
|
7200 |
+
),
|
7201 |
+
'tag_spec' => array(
|
7202 |
+
'extension_spec' => array(
|
7203 |
+
'allowed_versions' => array(
|
7204 |
+
'0.1',
|
7205 |
+
'latest',
|
7206 |
+
),
|
7207 |
+
'name' => 'amp-bind',
|
7208 |
+
'requires_usage' => 3,
|
7209 |
+
),
|
7210 |
+
),
|
7211 |
+
),
|
7212 |
+
array(
|
7213 |
+
'attr_spec_list' => array(
|
7214 |
+
'nonce' => array(),
|
7215 |
+
'type' => array(
|
7216 |
+
'dispatch_key' => 3,
|
7217 |
+
'mandatory' => true,
|
7218 |
+
'value_casei' => 'application/json',
|
7219 |
+
),
|
7220 |
+
),
|
7221 |
+
'cdata' => array(
|
7222 |
+
'blacklisted_cdata_regex' => array(
|
7223 |
+
'error_message' => 'html comments',
|
7224 |
+
'regex' => '<!--',
|
7225 |
+
),
|
7226 |
+
),
|
7227 |
+
'tag_spec' => array(
|
7228 |
+
'mandatory_parent' => 'amp-state',
|
7229 |
+
'requires_extension' => array(
|
7230 |
+
'amp-bind',
|
7231 |
+
),
|
7232 |
+
'spec_name' => 'amp-bind extension .json script',
|
7233 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-bind',
|
7234 |
+
),
|
7235 |
+
),
|
7236 |
+
array(
|
7237 |
+
'attr_spec_list' => array(
|
7238 |
+
'async' => array(
|
7239 |
+
'mandatory' => true,
|
7240 |
+
'value' => '',
|
7241 |
+
),
|
7242 |
+
'nonce' => array(),
|
7243 |
+
'type' => array(
|
7244 |
+
'value_casei' => 'text/javascript',
|
7245 |
+
),
|
7246 |
+
),
|
7247 |
+
'tag_spec' => array(
|
7248 |
+
'extension_spec' => array(
|
7249 |
+
'allowed_versions' => array(
|
7250 |
+
'0.1',
|
7251 |
+
'latest',
|
7252 |
+
),
|
7253 |
+
'name' => 'amp-bodymovin-animation',
|
7254 |
+
),
|
7255 |
+
),
|
7256 |
+
),
|
7257 |
+
array(
|
7258 |
+
'attr_spec_list' => array(
|
7259 |
+
'async' => array(
|
7260 |
+
'mandatory' => true,
|
7261 |
+
'value' => '',
|
7262 |
+
),
|
7263 |
+
'nonce' => array(),
|
7264 |
+
'type' => array(
|
7265 |
+
'value_casei' => 'text/javascript',
|
7266 |
+
),
|
7267 |
+
),
|
7268 |
+
'tag_spec' => array(
|
7269 |
+
'extension_spec' => array(
|
7270 |
+
'allowed_versions' => array(
|
7271 |
+
'0.1',
|
7272 |
+
'latest',
|
7273 |
+
),
|
7274 |
+
'deprecated_allow_duplicates' => true,
|
7275 |
+
'name' => 'amp-brid-player',
|
7276 |
+
'requires_usage' => 2,
|
7277 |
+
),
|
7278 |
+
),
|
7279 |
+
),
|
7280 |
+
array(
|
7281 |
+
'attr_spec_list' => array(
|
7282 |
+
'async' => array(
|
7283 |
+
'mandatory' => true,
|
7284 |
+
'value' => '',
|
7285 |
+
),
|
7286 |
+
'nonce' => array(),
|
7287 |
+
'type' => array(
|
7288 |
+
'value_casei' => 'text/javascript',
|
7289 |
+
),
|
7290 |
+
),
|
7291 |
+
'tag_spec' => array(
|
7292 |
+
'extension_spec' => array(
|
7293 |
+
'allowed_versions' => array(
|
7294 |
+
'0.1',
|
7295 |
+
'latest',
|
7296 |
+
),
|
7297 |
+
'deprecated_allow_duplicates' => true,
|
7298 |
+
'name' => 'amp-brightcove',
|
7299 |
+
'requires_usage' => 2,
|
7300 |
+
),
|
7301 |
+
),
|
7302 |
+
),
|
7303 |
+
array(
|
7304 |
+
'attr_spec_list' => array(
|
7305 |
+
'async' => array(
|
7306 |
+
'mandatory' => true,
|
7307 |
+
'value' => '',
|
7308 |
+
),
|
7309 |
+
'nonce' => array(),
|
7310 |
+
'type' => array(
|
7311 |
+
'value_casei' => 'text/javascript',
|
7312 |
+
),
|
7313 |
+
),
|
7314 |
+
'tag_spec' => array(
|
7315 |
+
'extension_spec' => array(
|
7316 |
+
'allowed_versions' => array(
|
7317 |
+
'0.1',
|
7318 |
+
'latest',
|
7319 |
+
),
|
7320 |
+
'name' => 'amp-byside-content',
|
7321 |
+
),
|
7322 |
+
),
|
7323 |
+
),
|
7324 |
+
array(
|
7325 |
+
'attr_spec_list' => array(
|
7326 |
+
'async' => array(
|
7327 |
+
'mandatory' => true,
|
7328 |
+
'value' => '',
|
7329 |
+
),
|
7330 |
+
'nonce' => array(),
|
7331 |
+
'type' => array(
|
7332 |
+
'value_casei' => 'text/javascript',
|
7333 |
+
),
|
7334 |
+
),
|
7335 |
+
'tag_spec' => array(
|
7336 |
+
'extension_spec' => array(
|
7337 |
+
'allowed_versions' => array(
|
7338 |
+
'0.1',
|
7339 |
+
'latest',
|
7340 |
+
),
|
7341 |
+
'name' => 'amp-call-tracking',
|
7342 |
+
'requires_usage' => 2,
|
7343 |
+
),
|
7344 |
+
),
|
7345 |
+
),
|
7346 |
+
array(
|
7347 |
+
'attr_spec_list' => array(
|
7348 |
+
'async' => array(
|
7349 |
+
'mandatory' => true,
|
7350 |
+
'value' => '',
|
7351 |
+
),
|
7352 |
+
'nonce' => array(),
|
7353 |
+
'type' => array(
|
7354 |
+
'value_casei' => 'text/javascript',
|
7355 |
+
),
|
7356 |
+
),
|
7357 |
+
'tag_spec' => array(
|
7358 |
+
'extension_spec' => array(
|
7359 |
+
'allowed_versions' => array(
|
7360 |
+
'0.1',
|
7361 |
+
'latest',
|
7362 |
+
),
|
7363 |
+
'deprecated_allow_duplicates' => true,
|
7364 |
+
'name' => 'amp-carousel',
|
7365 |
+
'requires_usage' => 2,
|
7366 |
+
),
|
7367 |
+
),
|
7368 |
+
),
|
7369 |
+
array(
|
7370 |
+
'attr_spec_list' => array(
|
7371 |
+
'async' => array(
|
7372 |
+
'mandatory' => true,
|
7373 |
+
'value' => '',
|
7374 |
+
),
|
7375 |
+
'nonce' => array(),
|
7376 |
+
'type' => array(
|
7377 |
+
'value_casei' => 'text/javascript',
|
7378 |
+
),
|
7379 |
+
),
|
7380 |
+
'tag_spec' => array(
|
7381 |
+
'extension_spec' => array(
|
7382 |
+
'allowed_versions' => array(
|
7383 |
+
'0.1',
|
7384 |
+
'latest',
|
7385 |
+
),
|
7386 |
+
'name' => 'amp-consent',
|
7387 |
+
),
|
7388 |
+
),
|
7389 |
+
),
|
7390 |
+
array(
|
7391 |
+
'attr_spec_list' => array(
|
7392 |
+
'nonce' => array(),
|
7393 |
+
'type' => array(
|
7394 |
+
'dispatch_key' => 3,
|
7395 |
+
'mandatory' => true,
|
7396 |
+
'value_casei' => 'application/json',
|
7397 |
+
),
|
7398 |
+
),
|
7399 |
+
'cdata' => array(
|
7400 |
+
'blacklisted_cdata_regex' => array(
|
7401 |
+
'error_message' => 'html comments',
|
7402 |
+
'regex' => '<!--',
|
7403 |
+
),
|
7404 |
+
),
|
7405 |
+
'tag_spec' => array(
|
7406 |
+
'mandatory_parent' => 'amp-consent',
|
7407 |
+
'requires_extension' => array(
|
7408 |
+
'amp-consent',
|
7409 |
+
),
|
7410 |
+
'spec_name' => 'amp-consent extension .json script',
|
7411 |
+
'unique' => true,
|
7412 |
+
),
|
7413 |
+
),
|
7414 |
+
array(
|
7415 |
+
'attr_spec_list' => array(
|
7416 |
+
'async' => array(
|
7417 |
+
'mandatory' => true,
|
7418 |
+
'value' => '',
|
7419 |
+
),
|
7420 |
+
'nonce' => array(),
|
7421 |
+
'type' => array(
|
7422 |
+
'value_casei' => 'text/javascript',
|
7423 |
+
),
|
7424 |
+
),
|
7425 |
+
'tag_spec' => array(
|
7426 |
+
'extension_spec' => array(
|
7427 |
+
'allowed_versions' => array(
|
7428 |
+
'0.1',
|
7429 |
+
'latest',
|
7430 |
+
),
|
7431 |
+
'deprecated_allow_duplicates' => true,
|
7432 |
+
'name' => 'amp-dailymotion',
|
7433 |
+
'requires_usage' => 2,
|
7434 |
+
),
|
7435 |
+
),
|
7436 |
+
),
|
7437 |
+
array(
|
7438 |
+
'attr_spec_list' => array(
|
7439 |
+
'async' => array(
|
7440 |
+
'mandatory' => true,
|
7441 |
+
'value' => '',
|
7442 |
+
),
|
7443 |
+
'nonce' => array(),
|
7444 |
+
'type' => array(
|
7445 |
+
'value_casei' => 'text/javascript',
|
7446 |
+
),
|
7447 |
+
),
|
7448 |
+
'tag_spec' => array(
|
7449 |
+
'extension_spec' => array(
|
7450 |
+
'allowed_versions' => array(
|
7451 |
+
'0.1',
|
7452 |
+
'latest',
|
7453 |
+
),
|
7454 |
+
'name' => 'amp-date-picker',
|
7455 |
+
),
|
7456 |
+
),
|
7457 |
+
),
|
7458 |
+
array(
|
7459 |
+
'attr_spec_list' => array(),
|
7460 |
+
'tag_spec' => array(
|
7461 |
+
'extension_spec' => array(
|
7462 |
+
'allowed_versions' => array(
|
7463 |
+
'0.1',
|
7464 |
+
'latest',
|
7465 |
+
),
|
7466 |
+
'name' => 'amp-document-recommendations',
|
7467 |
+
),
|
7468 |
+
),
|
7469 |
+
),
|
7470 |
+
array(
|
7471 |
+
'attr_spec_list' => array(
|
7472 |
+
'recommendations' => array(
|
7473 |
+
'dispatch_key' => 3,
|
7474 |
+
'mandatory' => true,
|
7475 |
+
'value_casei' => 'application/json',
|
7476 |
+
),
|
7477 |
+
),
|
7478 |
+
'tag_spec' => array(
|
7479 |
+
'mandatory_parent' => 'amp-document-recommendations',
|
7480 |
+
'requires_extension' => array(
|
7481 |
+
'amp-document-recommendations',
|
7482 |
+
),
|
7483 |
+
'spec_name' => 'amp-document-recommendations extension .json configuration',
|
7484 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-document-recommendations',
|
7485 |
+
),
|
7486 |
+
),
|
7487 |
+
array(
|
7488 |
+
'attr_spec_list' => array(
|
7489 |
+
'async' => array(
|
7490 |
+
'mandatory' => true,
|
7491 |
+
'value' => '',
|
7492 |
+
),
|
7493 |
+
'nonce' => array(),
|
7494 |
+
'type' => array(
|
7495 |
+
'value_casei' => 'text/javascript',
|
7496 |
+
),
|
7497 |
+
),
|
7498 |
+
'tag_spec' => array(
|
7499 |
+
'extension_spec' => array(
|
7500 |
+
'allowed_versions' => array(
|
7501 |
+
'0.1',
|
7502 |
+
'latest',
|
7503 |
+
),
|
7504 |
+
'deprecated_allow_duplicates' => true,
|
7505 |
+
'name' => 'amp-dynamic-css-classes',
|
7506 |
+
'requires_usage' => 3,
|
7507 |
+
),
|
7508 |
+
),
|
7509 |
+
),
|
7510 |
+
array(
|
7511 |
+
'attr_spec_list' => array(
|
7512 |
+
'async' => array(
|
7513 |
+
'mandatory' => true,
|
7514 |
+
'value' => '',
|
7515 |
+
),
|
7516 |
+
'nonce' => array(),
|
7517 |
+
'type' => array(
|
7518 |
+
'value_casei' => 'text/javascript',
|
7519 |
+
),
|
7520 |
+
),
|
7521 |
+
'tag_spec' => array(
|
7522 |
+
'extension_spec' => array(
|
7523 |
+
'allowed_versions' => array(
|
7524 |
+
'0.1',
|
7525 |
+
'latest',
|
7526 |
+
),
|
7527 |
+
'deprecated_allow_duplicates' => true,
|
7528 |
+
'name' => 'amp-experiment',
|
7529 |
+
'requires_usage' => 2,
|
7530 |
+
),
|
7531 |
+
),
|
7532 |
+
),
|
7533 |
+
array(
|
7534 |
+
'attr_spec_list' => array(
|
7535 |
+
'nonce' => array(),
|
7536 |
+
'type' => array(
|
7537 |
+
'dispatch_key' => 3,
|
7538 |
+
'mandatory' => true,
|
7539 |
+
'value_casei' => 'application/json',
|
7540 |
+
),
|
7541 |
+
),
|
7542 |
+
'cdata' => array(
|
7543 |
+
'blacklisted_cdata_regex' => array(
|
7544 |
+
'error_message' => 'html comments',
|
7545 |
+
'regex' => '<!--',
|
7546 |
+
),
|
7547 |
+
),
|
7548 |
+
'tag_spec' => array(
|
7549 |
+
'mandatory_parent' => 'amp-experiment',
|
7550 |
+
'spec_name' => 'amp-experiment extension .json script',
|
7551 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-experiment',
|
7552 |
+
),
|
7553 |
+
),
|
7554 |
+
array(
|
7555 |
+
'attr_spec_list' => array(
|
7556 |
+
'async' => array(
|
7557 |
+
'mandatory' => true,
|
7558 |
+
'value' => '',
|
7559 |
+
),
|
7560 |
+
'nonce' => array(),
|
7561 |
+
'type' => array(
|
7562 |
+
'value_casei' => 'text/javascript',
|
7563 |
+
),
|
7564 |
+
),
|
7565 |
+
'tag_spec' => array(
|
7566 |
+
'extension_spec' => array(
|
7567 |
+
'allowed_versions' => array(
|
7568 |
+
'0.1',
|
7569 |
+
'latest',
|
7570 |
+
),
|
7571 |
+
'name' => 'amp-facebook-comments',
|
7572 |
+
),
|
7573 |
+
),
|
7574 |
+
),
|
7575 |
+
array(
|
7576 |
+
'attr_spec_list' => array(
|
7577 |
+
'async' => array(
|
7578 |
+
'mandatory' => true,
|
7579 |
+
'value' => '',
|
7580 |
+
),
|
7581 |
+
'nonce' => array(),
|
7582 |
+
'type' => array(
|
7583 |
+
'value_casei' => 'text/javascript',
|
7584 |
+
),
|
7585 |
+
),
|
7586 |
+
'tag_spec' => array(
|
7587 |
+
'extension_spec' => array(
|
7588 |
+
'allowed_versions' => array(
|
7589 |
+
'0.1',
|
7590 |
+
'latest',
|
7591 |
+
),
|
7592 |
+
'name' => 'amp-facebook-like',
|
7593 |
+
),
|
7594 |
+
),
|
7595 |
+
),
|
7596 |
+
array(
|
7597 |
+
'attr_spec_list' => array(
|
7598 |
+
'async' => array(
|
7599 |
+
'mandatory' => true,
|
7600 |
+
'value' => '',
|
7601 |
+
),
|
7602 |
+
'nonce' => array(),
|
7603 |
+
'type' => array(
|
7604 |
+
'value_casei' => 'text/javascript',
|
7605 |
+
),
|
7606 |
+
),
|
7607 |
+
'tag_spec' => array(
|
7608 |
+
'extension_spec' => array(
|
7609 |
+
'allowed_versions' => array(
|
7610 |
+
'0.1',
|
7611 |
+
'latest',
|
7612 |
+
),
|
7613 |
+
'name' => 'amp-facebook-page',
|
7614 |
+
),
|
7615 |
+
),
|
7616 |
+
),
|
7617 |
+
array(
|
7618 |
+
'attr_spec_list' => array(
|
7619 |
+
'async' => array(
|
7620 |
+
'mandatory' => true,
|
7621 |
+
'value' => '',
|
7622 |
+
),
|
7623 |
+
'nonce' => array(),
|
7624 |
+
'type' => array(
|
7625 |
+
'value_casei' => 'text/javascript',
|
7626 |
+
),
|
7627 |
+
),
|
7628 |
+
'tag_spec' => array(
|
7629 |
+
'extension_spec' => array(
|
7630 |
+
'allowed_versions' => array(
|
7631 |
+
'0.1',
|
7632 |
+
'latest',
|
7633 |
+
),
|
7634 |
+
'deprecated_allow_duplicates' => true,
|
7635 |
+
'name' => 'amp-facebook',
|
7636 |
+
'requires_usage' => 2,
|
7637 |
+
),
|
7638 |
+
),
|
7639 |
+
),
|
7640 |
+
array(
|
7641 |
+
'attr_spec_list' => array(
|
7642 |
+
'async' => array(
|
7643 |
+
'mandatory' => true,
|
7644 |
+
'value' => '',
|
7645 |
+
),
|
7646 |
+
'nonce' => array(),
|
7647 |
+
'type' => array(
|
7648 |
+
'value_casei' => 'text/javascript',
|
7649 |
+
),
|
7650 |
+
),
|
7651 |
+
'tag_spec' => array(
|
7652 |
+
'extension_spec' => array(
|
7653 |
+
'allowed_versions' => array(
|
7654 |
+
'0.1',
|
7655 |
+
'latest',
|
7656 |
+
),
|
7657 |
+
'deprecated_allow_duplicates' => true,
|
7658 |
+
'name' => 'amp-fit-text',
|
7659 |
+
'requires_usage' => 2,
|
7660 |
+
),
|
7661 |
+
),
|
7662 |
+
),
|
7663 |
+
array(
|
7664 |
+
'attr_spec_list' => array(
|
7665 |
+
'async' => array(
|
7666 |
+
'mandatory' => true,
|
7667 |
+
'value' => '',
|
7668 |
+
),
|
7669 |
+
'nonce' => array(),
|
7670 |
+
'type' => array(
|
7671 |
+
'value_casei' => 'text/javascript',
|
7672 |
+
),
|
7673 |
+
),
|
7674 |
+
'tag_spec' => array(
|
7675 |
+
'extension_spec' => array(
|
7676 |
+
'allowed_versions' => array(
|
7677 |
+
'0.1',
|
7678 |
+
'latest',
|
7679 |
+
),
|
7680 |
+
'deprecated_allow_duplicates' => true,
|
7681 |
+
'name' => 'amp-font',
|
7682 |
+
'requires_usage' => 2,
|
7683 |
+
),
|
7684 |
+
),
|
7685 |
+
),
|
7686 |
+
array(
|
7687 |
+
'attr_spec_list' => array(
|
7688 |
+
'async' => array(
|
7689 |
+
'mandatory' => true,
|
7690 |
+
'value' => '',
|
7691 |
+
),
|
7692 |
+
'nonce' => array(),
|
7693 |
+
'type' => array(
|
7694 |
+
'value_casei' => 'text/javascript',
|
7695 |
+
),
|
7696 |
+
),
|
7697 |
+
'tag_spec' => array(
|
7698 |
+
'extension_spec' => array(
|
7699 |
+
'allowed_versions' => array(
|
7700 |
+
'0.1',
|
7701 |
+
'latest',
|
7702 |
+
),
|
7703 |
+
'deprecated_allow_duplicates' => true,
|
7704 |
+
'name' => 'amp-form',
|
7705 |
+
'requires_usage' => 2,
|
7706 |
+
),
|
7707 |
+
),
|
7708 |
+
),
|
7709 |
+
array(
|
7710 |
+
'attr_spec_list' => array(
|
7711 |
+
'async' => array(
|
7712 |
+
'mandatory' => true,
|
7713 |
+
'value' => '',
|
7714 |
+
),
|
7715 |
+
'nonce' => array(),
|
7716 |
+
'type' => array(
|
7717 |
+
'value_casei' => 'text/javascript',
|
7718 |
+
),
|
7719 |
+
),
|
7720 |
+
'tag_spec' => array(
|
7721 |
+
'extension_spec' => array(
|
7722 |
+
'allowed_versions' => array(
|
7723 |
+
'0.1',
|
7724 |
+
'latest',
|
7725 |
+
),
|
7726 |
+
'name' => 'amp-fx-collection',
|
7727 |
+
'requires_usage' => 3,
|
7728 |
+
),
|
7729 |
+
),
|
7730 |
+
),
|
7731 |
+
array(
|
7732 |
+
'attr_spec_list' => array(
|
7733 |
+
'async' => array(
|
7734 |
+
'mandatory' => true,
|
7735 |
+
'value' => '',
|
7736 |
+
),
|
7737 |
+
'nonce' => array(),
|
7738 |
+
'type' => array(
|
7739 |
+
'value_casei' => 'text/javascript',
|
7740 |
+
),
|
7741 |
+
),
|
7742 |
+
'tag_spec' => array(
|
7743 |
+
'extension_spec' => array(
|
7744 |
+
'allowed_versions' => array(
|
7745 |
+
'0.1',
|
7746 |
+
'latest',
|
7747 |
+
),
|
7748 |
+
'deprecated_allow_duplicates' => true,
|
7749 |
+
'name' => 'amp-fx-flying-carpet',
|
7750 |
+
'requires_usage' => 2,
|
7751 |
+
),
|
7752 |
+
),
|
7753 |
+
),
|
7754 |
+
array(
|
7755 |
+
'attr_spec_list' => array(
|
7756 |
+
'async' => array(
|
7757 |
+
'mandatory' => true,
|
7758 |
+
'value' => '',
|
7759 |
+
),
|
7760 |
+
'nonce' => array(),
|
7761 |
+
'type' => array(
|
7762 |
+
'value_casei' => 'text/javascript',
|
7763 |
+
),
|
7764 |
+
),
|
7765 |
+
'tag_spec' => array(
|
7766 |
+
'extension_spec' => array(
|
7767 |
+
'allowed_versions' => array(
|
7768 |
+
'0.1',
|
7769 |
+
'latest',
|
7770 |
+
),
|
7771 |
+
'deprecated_allow_duplicates' => true,
|
7772 |
+
'name' => 'amp-gfycat',
|
7773 |
+
'requires_usage' => 2,
|
7774 |
+
),
|
7775 |
+
),
|
7776 |
+
),
|
7777 |
+
array(
|
7778 |
+
'attr_spec_list' => array(
|
7779 |
+
'async' => array(
|
7780 |
+
'mandatory' => true,
|
7781 |
+
'value' => '',
|
7782 |
+
),
|
7783 |
+
'nonce' => array(),
|
7784 |
+
'type' => array(
|
7785 |
+
'value_casei' => 'text/javascript',
|
7786 |
+
),
|
7787 |
+
),
|
7788 |
+
'tag_spec' => array(
|
7789 |
+
'extension_spec' => array(
|
7790 |
+
'allowed_versions' => array(
|
7791 |
+
'0.1',
|
7792 |
+
'latest',
|
7793 |
+
),
|
7794 |
+
'name' => 'amp-gist',
|
7795 |
+
),
|
7796 |
+
),
|
7797 |
+
),
|
7798 |
+
array(
|
7799 |
+
'attr_spec_list' => array(
|
7800 |
+
'async' => array(
|
7801 |
+
'mandatory' => true,
|
7802 |
+
'value' => '',
|
7803 |
+
),
|
7804 |
+
'nonce' => array(),
|
7805 |
+
'type' => array(
|
7806 |
+
'value_casei' => 'text/javascript',
|
7807 |
+
),
|
7808 |
+
),
|
7809 |
+
'tag_spec' => array(
|
7810 |
+
'extension_spec' => array(
|
7811 |
+
'allowed_versions' => array(
|
7812 |
+
'0.1',
|
7813 |
+
'latest',
|
7814 |
+
),
|
7815 |
+
'name' => 'amp-hulu',
|
7816 |
+
),
|
7817 |
+
),
|
7818 |
+
),
|
7819 |
+
array(
|
7820 |
+
'attr_spec_list' => array(
|
7821 |
+
'async' => array(
|
7822 |
+
'mandatory' => true,
|
7823 |
+
'value' => '',
|
7824 |
+
),
|
7825 |
+
'nonce' => array(),
|
7826 |
+
'type' => array(
|
7827 |
+
'value_casei' => 'text/javascript',
|
7828 |
+
),
|
7829 |
+
),
|
7830 |
+
'tag_spec' => array(
|
7831 |
+
'extension_spec' => array(
|
7832 |
+
'allowed_versions' => array(
|
7833 |
+
'0.1',
|
7834 |
+
'latest',
|
7835 |
+
),
|
7836 |
+
'deprecated_allow_duplicates' => true,
|
7837 |
+
'name' => 'amp-iframe',
|
7838 |
+
'requires_usage' => 2,
|
7839 |
+
),
|
7840 |
+
),
|
7841 |
+
),
|
7842 |
+
array(
|
7843 |
+
'attr_spec_list' => array(
|
7844 |
+
'async' => array(
|
7845 |
+
'mandatory' => true,
|
7846 |
+
'value' => '',
|
7847 |
+
),
|
7848 |
+
'nonce' => array(),
|
7849 |
+
'type' => array(
|
7850 |
+
'value_casei' => 'text/javascript',
|
7851 |
+
),
|
7852 |
+
),
|
7853 |
+
'tag_spec' => array(
|
7854 |
+
'extension_spec' => array(
|
7855 |
+
'allowed_versions' => array(
|
7856 |
+
'0.1',
|
7857 |
+
'latest',
|
7858 |
+
),
|
7859 |
+
'name' => 'amp-ima-video',
|
7860 |
+
),
|
7861 |
+
),
|
7862 |
+
),
|
7863 |
+
array(
|
7864 |
+
'attr_spec_list' => array(
|
7865 |
+
'async' => array(
|
7866 |
+
'mandatory' => true,
|
7867 |
+
'value' => '',
|
7868 |
+
),
|
7869 |
+
'nonce' => array(),
|
7870 |
+
'type' => array(
|
7871 |
+
'value_casei' => 'text/javascript',
|
7872 |
+
),
|
7873 |
+
),
|
7874 |
+
'tag_spec' => array(
|
7875 |
+
'extension_spec' => array(
|
7876 |
+
'allowed_versions' => array(
|
7877 |
+
'0.1',
|
7878 |
+
'latest',
|
7879 |
+
),
|
7880 |
+
'deprecated_allow_duplicates' => true,
|
7881 |
+
'name' => 'amp-image-lightbox',
|
7882 |
+
'requires_usage' => 2,
|
7883 |
+
),
|
7884 |
+
),
|
7885 |
+
),
|
7886 |
+
array(
|
7887 |
+
'attr_spec_list' => array(
|
7888 |
+
'async' => array(
|
7889 |
+
'mandatory' => true,
|
7890 |
+
'value' => '',
|
7891 |
+
),
|
7892 |
+
'nonce' => array(),
|
7893 |
+
'type' => array(
|
7894 |
+
'value_casei' => 'text/javascript',
|
7895 |
+
),
|
7896 |
+
),
|
7897 |
+
'tag_spec' => array(
|
7898 |
+
'extension_spec' => array(
|
7899 |
+
'allowed_versions' => array(
|
7900 |
+
'0.1',
|
7901 |
+
'latest',
|
7902 |
+
),
|
7903 |
+
'name' => 'amp-imgur',
|
7904 |
+
),
|
7905 |
+
),
|
7906 |
+
),
|
7907 |
+
array(
|
7908 |
+
'attr_spec_list' => array(
|
7909 |
+
'async' => array(
|
7910 |
+
'mandatory' => true,
|
7911 |
+
'value' => '',
|
7912 |
+
),
|
7913 |
+
'nonce' => array(),
|
7914 |
+
'type' => array(
|
7915 |
+
'value_casei' => 'text/javascript',
|
7916 |
+
),
|
7917 |
+
),
|
7918 |
+
'tag_spec' => array(
|
7919 |
+
'extension_spec' => array(
|
7920 |
+
'allowed_versions' => array(
|
7921 |
+
'0.1',
|
7922 |
+
'latest',
|
7923 |
+
),
|
7924 |
+
'deprecated_allow_duplicates' => true,
|
7925 |
+
'name' => 'amp-instagram',
|
7926 |
+
'requires_usage' => 2,
|
7927 |
+
),
|
7928 |
+
),
|
7929 |
+
),
|
7930 |
+
array(
|
7931 |
+
'attr_spec_list' => array(
|
7932 |
+
'async' => array(
|
7933 |
+
'mandatory' => true,
|
7934 |
+
'value' => '',
|
7935 |
+
),
|
7936 |
+
'nonce' => array(),
|
7937 |
+
'type' => array(
|
7938 |
+
'value_casei' => 'text/javascript',
|
7939 |
+
),
|
7940 |
+
),
|
7941 |
+
'tag_spec' => array(
|
7942 |
+
'extension_spec' => array(
|
7943 |
+
'allowed_versions' => array(
|
7944 |
+
'0.1',
|
7945 |
+
'latest',
|
7946 |
+
),
|
7947 |
+
'deprecated_allow_duplicates' => true,
|
7948 |
+
'name' => 'amp-install-serviceworker',
|
7949 |
+
'requires_usage' => 2,
|
7950 |
+
),
|
7951 |
+
),
|
7952 |
+
),
|
7953 |
+
array(
|
7954 |
+
'attr_spec_list' => array(
|
7955 |
+
'async' => array(
|
7956 |
+
'mandatory' => true,
|
7957 |
+
'value' => '',
|
7958 |
+
),
|
7959 |
+
'nonce' => array(),
|
7960 |
+
'type' => array(
|
7961 |
+
'value_casei' => 'text/javascript',
|
7962 |
+
),
|
7963 |
+
),
|
7964 |
+
'tag_spec' => array(
|
7965 |
+
'extension_spec' => array(
|
7966 |
+
'allowed_versions' => array(
|
7967 |
+
'0.1',
|
7968 |
+
'latest',
|
7969 |
+
),
|
7970 |
+
'name' => 'amp-izlesene',
|
7971 |
+
'requires_usage' => 2,
|
7972 |
+
),
|
7973 |
+
),
|
7974 |
+
),
|
7975 |
+
array(
|
7976 |
+
'attr_spec_list' => array(
|
7977 |
+
'async' => array(
|
7978 |
+
'mandatory' => true,
|
7979 |
+
'value' => '',
|
7980 |
+
),
|
7981 |
+
'nonce' => array(),
|
7982 |
+
'type' => array(
|
7983 |
+
'value_casei' => 'text/javascript',
|
7984 |
+
),
|
7985 |
+
),
|
7986 |
+
'tag_spec' => array(
|
7987 |
+
'extension_spec' => array(
|
7988 |
+
'allowed_versions' => array(
|
7989 |
+
'0.1',
|
7990 |
+
'latest',
|
7991 |
+
),
|
7992 |
+
'deprecated_allow_duplicates' => true,
|
7993 |
+
'name' => 'amp-jwplayer',
|
7994 |
+
'requires_usage' => 2,
|
7995 |
+
),
|
7996 |
+
),
|
7997 |
+
),
|
7998 |
+
array(
|
7999 |
+
'attr_spec_list' => array(
|
8000 |
+
'async' => array(
|
8001 |
+
'mandatory' => true,
|
8002 |
+
'value' => '',
|
8003 |
+
),
|
8004 |
+
'nonce' => array(),
|
8005 |
+
'type' => array(
|
8006 |
+
'value_casei' => 'text/javascript',
|
8007 |
+
),
|
8008 |
+
),
|
8009 |
+
'tag_spec' => array(
|
8010 |
+
'extension_spec' => array(
|
8011 |
+
'allowed_versions' => array(
|
8012 |
+
'0.1',
|
8013 |
+
'latest',
|
8014 |
+
),
|
8015 |
+
'deprecated_allow_duplicates' => true,
|
8016 |
+
'name' => 'amp-kaltura-player',
|
8017 |
+
'requires_usage' => 2,
|
8018 |
+
),
|
8019 |
+
),
|
8020 |
+
),
|
8021 |
+
array(
|
8022 |
+
'attr_spec_list' => array(
|
8023 |
+
'async' => array(
|
8024 |
+
'mandatory' => true,
|
8025 |
+
'value' => '',
|
8026 |
+
),
|
8027 |
+
'nonce' => array(),
|
8028 |
+
'type' => array(
|
8029 |
+
'value_casei' => 'text/javascript',
|
8030 |
+
),
|
8031 |
+
),
|
8032 |
+
'tag_spec' => array(
|
8033 |
+
'extension_spec' => array(
|
8034 |
+
'allowed_versions' => array(
|
8035 |
+
'0.1',
|
8036 |
+
'latest',
|
8037 |
+
),
|
8038 |
+
'name' => 'amp-lightbox-gallery',
|
8039 |
+
'requires_usage' => 3,
|
8040 |
+
),
|
8041 |
+
),
|
8042 |
+
),
|
8043 |
+
array(
|
8044 |
+
'attr_spec_list' => array(
|
8045 |
+
'async' => array(
|
8046 |
+
'mandatory' => true,
|
8047 |
+
'value' => '',
|
8048 |
+
),
|
8049 |
+
'nonce' => array(),
|
8050 |
+
'type' => array(
|
8051 |
+
'value_casei' => 'text/javascript',
|
8052 |
+
),
|
8053 |
+
),
|
8054 |
+
'tag_spec' => array(
|
8055 |
+
'extension_spec' => array(
|
8056 |
+
'allowed_versions' => array(
|
8057 |
+
'0.1',
|
8058 |
+
'latest',
|
8059 |
+
),
|
8060 |
+
'deprecated_allow_duplicates' => true,
|
8061 |
+
'name' => 'amp-lightbox',
|
8062 |
+
'requires_usage' => 2,
|
8063 |
+
),
|
8064 |
+
),
|
8065 |
+
),
|
8066 |
+
array(
|
8067 |
+
'attr_spec_list' => array(
|
8068 |
+
'async' => array(
|
8069 |
+
'mandatory' => true,
|
8070 |
+
'value' => '',
|
8071 |
+
),
|
8072 |
+
'nonce' => array(),
|
8073 |
+
'type' => array(
|
8074 |
+
'value_casei' => 'text/javascript',
|
8075 |
+
),
|
8076 |
+
),
|
8077 |
+
'tag_spec' => array(
|
8078 |
+
'extension_spec' => array(
|
8079 |
+
'allowed_versions' => array(
|
8080 |
+
'0.1',
|
8081 |
+
'latest',
|
8082 |
+
),
|
8083 |
+
'deprecated_allow_duplicates' => true,
|
8084 |
+
'name' => 'amp-list',
|
8085 |
+
'requires_usage' => 2,
|
8086 |
+
),
|
8087 |
+
),
|
8088 |
+
),
|
8089 |
+
array(
|
8090 |
+
'attr_spec_list' => array(
|
8091 |
+
'async' => array(
|
8092 |
+
'mandatory' => true,
|
8093 |
+
'value' => '',
|
8094 |
+
),
|
8095 |
+
'nonce' => array(),
|
8096 |
+
'type' => array(
|
8097 |
+
'value_casei' => 'text/javascript',
|
8098 |
+
),
|
8099 |
+
),
|
8100 |
+
'tag_spec' => array(
|
8101 |
+
'extension_spec' => array(
|
8102 |
+
'allowed_versions' => array(
|
8103 |
+
'0.1',
|
8104 |
+
'latest',
|
8105 |
+
),
|
8106 |
+
'name' => 'amp-live-list',
|
8107 |
+
'requires_usage' => 2,
|
8108 |
+
),
|
8109 |
+
'mandatory_parent' => 'head',
|
8110 |
+
'unique_warning' => true,
|
8111 |
+
),
|
8112 |
+
),
|
8113 |
+
array(
|
8114 |
+
'attr_spec_list' => array(
|
8115 |
+
'async' => array(
|
8116 |
+
'mandatory' => true,
|
8117 |
+
'value' => '',
|
8118 |
+
),
|
8119 |
+
'nonce' => array(),
|
8120 |
+
'type' => array(
|
8121 |
+
'value_casei' => 'text/javascript',
|
8122 |
+
),
|
8123 |
+
),
|
8124 |
+
'tag_spec' => array(
|
8125 |
+
'extension_spec' => array(
|
8126 |
+
'allowed_versions' => array(
|
8127 |
+
'0.1',
|
8128 |
+
'latest',
|
8129 |
+
),
|
8130 |
+
'name' => 'amp-mathml',
|
8131 |
+
),
|
8132 |
+
),
|
8133 |
+
),
|
8134 |
+
array(
|
8135 |
+
'attr_spec_list' => array(
|
8136 |
+
'async' => array(
|
8137 |
+
'mandatory' => true,
|
8138 |
+
'value' => '',
|
8139 |
+
),
|
8140 |
+
'nonce' => array(),
|
8141 |
+
'type' => array(
|
8142 |
+
'value_casei' => 'text/javascript',
|
8143 |
+
),
|
8144 |
+
),
|
8145 |
+
'tag_spec' => array(
|
8146 |
+
'extension_spec' => array(
|
8147 |
+
'allowed_versions' => array(
|
8148 |
+
'0.1',
|
8149 |
+
'latest',
|
8150 |
+
),
|
8151 |
+
'deprecated_allow_duplicates' => true,
|
8152 |
+
'is_custom_template' => true,
|
8153 |
+
'name' => 'amp-mustache',
|
8154 |
+
'requires_usage' => 2,
|
8155 |
+
),
|
8156 |
+
),
|
8157 |
+
),
|
8158 |
+
array(
|
8159 |
+
'attr_spec_list' => array(
|
8160 |
+
'async' => array(
|
8161 |
+
'mandatory' => true,
|
8162 |
+
'value' => '',
|
8163 |
+
),
|
8164 |
+
'nonce' => array(),
|
8165 |
+
'type' => array(
|
8166 |
+
'value_casei' => 'text/javascript',
|
8167 |
+
),
|
8168 |
+
),
|
8169 |
+
'tag_spec' => array(
|
8170 |
+
'extension_spec' => array(
|
8171 |
+
'allowed_versions' => array(
|
8172 |
+
'0.1',
|
8173 |
+
'latest',
|
8174 |
+
),
|
8175 |
+
'name' => 'amp-nexxtv-player',
|
8176 |
+
),
|
8177 |
+
),
|
8178 |
+
),
|
8179 |
+
array(
|
8180 |
+
'attr_spec_list' => array(
|
8181 |
+
'async' => array(
|
8182 |
+
'mandatory' => true,
|
8183 |
+
'value' => '',
|
8184 |
+
),
|
8185 |
+
'nonce' => array(),
|
8186 |
+
'type' => array(
|
8187 |
+
'value_casei' => 'text/javascript',
|
8188 |
+
),
|
8189 |
+
),
|
8190 |
+
'tag_spec' => array(
|
8191 |
+
'extension_spec' => array(
|
8192 |
+
'allowed_versions' => array(
|
8193 |
+
'0.1',
|
8194 |
+
'latest',
|
8195 |
+
),
|
8196 |
+
'deprecated_allow_duplicates' => true,
|
8197 |
+
'name' => 'amp-o2-player',
|
8198 |
+
'requires_usage' => 2,
|
8199 |
+
),
|
8200 |
+
),
|
8201 |
+
),
|
8202 |
+
array(
|
8203 |
+
'attr_spec_list' => array(
|
8204 |
+
'async' => array(
|
8205 |
+
'mandatory' => true,
|
8206 |
+
'value' => '',
|
8207 |
+
),
|
8208 |
+
'nonce' => array(),
|
8209 |
+
'type' => array(
|
8210 |
+
'value_casei' => 'text/javascript',
|
8211 |
+
),
|
8212 |
+
),
|
8213 |
+
'tag_spec' => array(
|
8214 |
+
'extension_spec' => array(
|
8215 |
+
'allowed_versions' => array(
|
8216 |
+
'0.1',
|
8217 |
+
'latest',
|
8218 |
+
),
|
8219 |
+
'name' => 'amp-ooyala-player',
|
8220 |
+
),
|
8221 |
+
),
|
8222 |
+
),
|
8223 |
+
array(
|
8224 |
+
'attr_spec_list' => array(
|
8225 |
+
'async' => array(
|
8226 |
+
'mandatory' => true,
|
8227 |
+
'value' => '',
|
8228 |
+
),
|
8229 |
+
'nonce' => array(),
|
8230 |
+
'type' => array(
|
8231 |
+
'value_casei' => 'text/javascript',
|
8232 |
+
),
|
8233 |
+
),
|
8234 |
+
'tag_spec' => array(
|
8235 |
+
'extension_spec' => array(
|
8236 |
+
'allowed_versions' => array(
|
8237 |
+
'0.1',
|
8238 |
+
'latest',
|
8239 |
+
),
|
8240 |
+
'deprecated_allow_duplicates' => true,
|
8241 |
+
'name' => 'amp-pinterest',
|
8242 |
+
'requires_usage' => 2,
|
8243 |
+
),
|
8244 |
+
),
|
8245 |
+
),
|
8246 |
+
array(
|
8247 |
+
'attr_spec_list' => array(
|
8248 |
+
'async' => array(
|
8249 |
+
'mandatory' => true,
|
8250 |
+
'value' => '',
|
8251 |
+
),
|
8252 |
+
'nonce' => array(),
|
8253 |
+
'type' => array(
|
8254 |
+
'value_casei' => 'text/javascript',
|
8255 |
+
),
|
8256 |
+
),
|
8257 |
+
'tag_spec' => array(
|
8258 |
+
'extension_spec' => array(
|
8259 |
+
'allowed_versions' => array(
|
8260 |
+
'0.1',
|
8261 |
+
'latest',
|
8262 |
+
),
|
8263 |
+
'name' => 'amp-playbuzz',
|
8264 |
+
),
|
8265 |
+
),
|
8266 |
+
),
|
8267 |
+
array(
|
8268 |
+
'attr_spec_list' => array(
|
8269 |
+
'async' => array(
|
8270 |
+
'mandatory' => true,
|
8271 |
+
'value' => '',
|
8272 |
+
),
|
8273 |
+
'nonce' => array(),
|
8274 |
+
'type' => array(
|
8275 |
+
'value_casei' => 'text/javascript',
|
8276 |
+
),
|
8277 |
+
),
|
8278 |
+
'tag_spec' => array(
|
8279 |
+
'extension_spec' => array(
|
8280 |
+
'allowed_versions' => array(
|
8281 |
+
'0.1',
|
8282 |
+
'latest',
|
8283 |
+
),
|
8284 |
+
'name' => 'amp-position-observer',
|
8285 |
+
),
|
8286 |
+
),
|
8287 |
+
),
|
8288 |
+
array(
|
8289 |
+
'attr_spec_list' => array(
|
8290 |
+
'async' => array(
|
8291 |
+
'mandatory' => true,
|
8292 |
+
'value' => '',
|
8293 |
+
),
|
8294 |
+
'nonce' => array(),
|
8295 |
+
'type' => array(
|
8296 |
+
'value_casei' => 'text/javascript',
|
8297 |
+
),
|
8298 |
+
),
|
8299 |
+
'tag_spec' => array(
|
8300 |
+
'extension_spec' => array(
|
8301 |
+
'allowed_versions' => array(
|
8302 |
+
'0.1',
|
8303 |
+
'latest',
|
8304 |
+
),
|
8305 |
+
'deprecated_allow_duplicates' => true,
|
8306 |
+
'name' => 'amp-reach-player',
|
8307 |
+
'requires_usage' => 2,
|
8308 |
+
),
|
8309 |
+
),
|
8310 |
+
),
|
8311 |
+
array(
|
8312 |
+
'attr_spec_list' => array(
|
8313 |
+
'async' => array(
|
8314 |
+
'mandatory' => true,
|
8315 |
+
'value' => '',
|
8316 |
+
),
|
8317 |
+
'nonce' => array(),
|
8318 |
+
'type' => array(
|
8319 |
+
'value_casei' => 'text/javascript',
|
8320 |
+
),
|
8321 |
+
),
|
8322 |
+
'tag_spec' => array(
|
8323 |
+
'extension_spec' => array(
|
8324 |
+
'allowed_versions' => array(
|
8325 |
+
'0.1',
|
8326 |
+
'latest',
|
8327 |
+
),
|
8328 |
+
'deprecated_allow_duplicates' => true,
|
8329 |
+
'name' => 'amp-reddit',
|
8330 |
+
),
|
8331 |
+
),
|
8332 |
+
),
|
8333 |
+
array(
|
8334 |
+
'attr_spec_list' => array(
|
8335 |
+
'async' => array(
|
8336 |
+
'mandatory' => true,
|
8337 |
+
'value' => '',
|
8338 |
+
),
|
8339 |
+
'nonce' => array(),
|
8340 |
+
'type' => array(
|
8341 |
+
'value_casei' => 'text/javascript',
|
8342 |
+
),
|
8343 |
+
),
|
8344 |
+
'tag_spec' => array(
|
8345 |
+
'extension_spec' => array(
|
8346 |
+
'allowed_versions' => array(
|
8347 |
+
'0.1',
|
8348 |
+
'latest',
|
8349 |
+
),
|
8350 |
+
'name' => 'amp-riddle-quiz',
|
8351 |
+
),
|
8352 |
+
),
|
8353 |
+
),
|
8354 |
+
array(
|
8355 |
+
'attr_spec_list' => array(
|
8356 |
+
'async' => array(
|
8357 |
+
'mandatory' => true,
|
8358 |
+
'value' => '',
|
8359 |
+
),
|
8360 |
+
'nonce' => array(),
|
8361 |
+
'type' => array(
|
8362 |
+
'value_casei' => 'text/javascript',
|
8363 |
+
),
|
8364 |
+
),
|
8365 |
+
'tag_spec' => array(
|
8366 |
+
'extension_spec' => array(
|
8367 |
+
'allowed_versions' => array(
|
8368 |
+
'0.1',
|
8369 |
+
'latest',
|
8370 |
+
),
|
8371 |
+
'name' => 'amp-selector',
|
8372 |
+
'requires_usage' => 2,
|
8373 |
+
),
|
8374 |
+
),
|
8375 |
+
),
|
8376 |
+
array(
|
8377 |
+
'attr_spec_list' => array(
|
8378 |
+
'async' => array(
|
8379 |
+
'mandatory' => true,
|
8380 |
+
'value' => '',
|
8381 |
+
),
|
8382 |
+
'nonce' => array(),
|
8383 |
+
'type' => array(
|
8384 |
+
'value_casei' => 'text/javascript',
|
8385 |
+
),
|
8386 |
+
),
|
8387 |
+
'tag_spec' => array(
|
8388 |
+
'extension_spec' => array(
|
8389 |
+
'allowed_versions' => array(
|
8390 |
+
'0.1',
|
8391 |
+
'latest',
|
8392 |
+
),
|
8393 |
+
'deprecated_allow_duplicates' => true,
|
8394 |
+
'name' => 'amp-sidebar',
|
8395 |
+
'requires_usage' => 2,
|
8396 |
+
),
|
8397 |
+
),
|
8398 |
+
),
|
8399 |
+
array(
|
8400 |
+
'attr_spec_list' => array(
|
8401 |
+
'async' => array(
|
8402 |
+
'mandatory' => true,
|
8403 |
+
'value' => '',
|
8404 |
+
),
|
8405 |
+
'nonce' => array(),
|
8406 |
+
'type' => array(
|
8407 |
+
'value_casei' => 'text/javascript',
|
8408 |
+
),
|
8409 |
+
),
|
8410 |
+
'tag_spec' => array(
|
8411 |
+
'extension_spec' => array(
|
8412 |
+
'allowed_versions' => array(
|
8413 |
+
'0.1',
|
8414 |
+
'latest',
|
8415 |
+
),
|
8416 |
+
'deprecated_allow_duplicates' => true,
|
8417 |
+
'name' => 'amp-social-share',
|
8418 |
+
'requires_usage' => 2,
|
8419 |
+
),
|
8420 |
+
),
|
8421 |
+
),
|
8422 |
+
array(
|
8423 |
+
'attr_spec_list' => array(
|
8424 |
+
'async' => array(
|
8425 |
+
'mandatory' => true,
|
8426 |
+
'value' => '',
|
8427 |
+
),
|
8428 |
+
'nonce' => array(),
|
8429 |
+
'type' => array(
|
8430 |
+
'value_casei' => 'text/javascript',
|
8431 |
+
),
|
8432 |
+
),
|
8433 |
+
'tag_spec' => array(
|
8434 |
+
'extension_spec' => array(
|
8435 |
+
'allowed_versions' => array(
|
8436 |
+
'0.1',
|
8437 |
+
'latest',
|
8438 |
+
),
|
8439 |
+
'deprecated_allow_duplicates' => true,
|
8440 |
+
'name' => 'amp-soundcloud',
|
8441 |
+
'requires_usage' => 2,
|
8442 |
+
),
|
8443 |
+
),
|
8444 |
+
),
|
8445 |
+
array(
|
8446 |
+
'attr_spec_list' => array(
|
8447 |
+
'async' => array(
|
8448 |
+
'mandatory' => true,
|
8449 |
+
'value' => '',
|
8450 |
+
),
|
8451 |
+
'nonce' => array(),
|
8452 |
+
'type' => array(
|
8453 |
+
'value_casei' => 'text/javascript',
|
8454 |
+
),
|
8455 |
+
),
|
8456 |
+
'tag_spec' => array(
|
8457 |
+
'extension_spec' => array(
|
8458 |
+
'allowed_versions' => array(
|
8459 |
+
'0.1',
|
8460 |
+
'latest',
|
8461 |
+
),
|
8462 |
+
'deprecated_allow_duplicates' => true,
|
8463 |
+
'name' => 'amp-springboard-player',
|
8464 |
+
'requires_usage' => 2,
|
8465 |
+
),
|
8466 |
+
),
|
8467 |
+
),
|
8468 |
+
array(
|
8469 |
+
'attr_spec_list' => array(
|
8470 |
+
'async' => array(
|
8471 |
+
'mandatory' => true,
|
8472 |
+
'value' => '',
|
8473 |
+
),
|
8474 |
+
'nonce' => array(),
|
8475 |
+
'type' => array(
|
8476 |
+
'value_casei' => 'text/javascript',
|
8477 |
+
),
|
8478 |
+
),
|
8479 |
+
'tag_spec' => array(
|
8480 |
+
'extension_spec' => array(
|
8481 |
+
'allowed_versions' => array(
|
8482 |
+
'0.1',
|
8483 |
+
'1.0',
|
8484 |
+
'latest',
|
8485 |
+
),
|
8486 |
+
'deprecated_versions' => array(
|
8487 |
+
'0.1',
|
8488 |
+
),
|
8489 |
+
'name' => 'amp-sticky-ad',
|
8490 |
+
'requires_usage' => 2,
|
8491 |
+
),
|
8492 |
+
),
|
8493 |
+
),
|
8494 |
+
array(
|
8495 |
+
'attr_spec_list' => array(
|
8496 |
+
'async' => array(
|
8497 |
+
'mandatory' => true,
|
8498 |
+
'value' => '',
|
8499 |
+
),
|
8500 |
+
'nonce' => array(),
|
8501 |
+
'type' => array(
|
8502 |
+
'value_casei' => 'text/javascript',
|
8503 |
+
),
|
8504 |
+
),
|
8505 |
+
'tag_spec' => array(
|
8506 |
+
'extension_spec' => array(
|
8507 |
+
'allowed_versions' => array(
|
8508 |
+
'0.1',
|
8509 |
+
'latest',
|
8510 |
+
),
|
8511 |
+
'name' => 'amp-story-auto-ads',
|
8512 |
+
),
|
8513 |
+
),
|
8514 |
+
),
|
8515 |
+
array(
|
8516 |
+
'attr_spec_list' => array(
|
8517 |
+
'nonce' => array(),
|
8518 |
+
'type' => array(
|
8519 |
+
'dispatch_key' => 3,
|
8520 |
+
'mandatory' => true,
|
8521 |
+
'value_casei' => 'application/json',
|
8522 |
+
),
|
8523 |
+
),
|
8524 |
+
'cdata' => array(
|
8525 |
+
'blacklisted_cdata_regex' => array(
|
8526 |
+
'error_message' => 'html comments',
|
8527 |
+
'regex' => '<!--',
|
8528 |
+
),
|
8529 |
+
),
|
8530 |
+
'tag_spec' => array(
|
8531 |
+
'mandatory_parent' => 'amp-story-auto-ads',
|
8532 |
+
'requires_extension' => array(
|
8533 |
+
'amp-story-auto-ads',
|
8534 |
+
),
|
8535 |
+
'spec_name' => 'amp-story-auto-ads config script',
|
8536 |
+
'spec_url' => 'https://github.com/ampproject/amphtml/blob/master/extensions/amp-story/amp-story-auto-ads.md',
|
8537 |
+
),
|
8538 |
+
),
|
8539 |
+
array(
|
8540 |
+
'attr_spec_list' => array(
|
8541 |
+
'async' => array(
|
8542 |
+
'mandatory' => true,
|
8543 |
+
'value' => '',
|
8544 |
+
),
|
8545 |
+
'nonce' => array(),
|
8546 |
+
'type' => array(
|
8547 |
+
'value_casei' => 'text/javascript',
|
8548 |
+
),
|
8549 |
+
),
|
8550 |
+
'tag_spec' => array(
|
8551 |
+
'extension_spec' => array(
|
8552 |
+
'allowed_versions' => array(
|
8553 |
+
'0.1',
|
8554 |
+
'latest',
|
8555 |
+
),
|
8556 |
+
'name' => 'amp-story',
|
8557 |
+
),
|
8558 |
+
),
|
8559 |
+
),
|
8560 |
+
array(
|
8561 |
+
'attr_spec_list' => array(
|
8562 |
+
'async' => array(
|
8563 |
+
'mandatory' => true,
|
8564 |
+
'value' => '',
|
8565 |
+
),
|
8566 |
+
'nonce' => array(),
|
8567 |
+
'type' => array(
|
8568 |
+
'value_casei' => 'text/javascript',
|
8569 |
+
),
|
8570 |
+
),
|
8571 |
+
'tag_spec' => array(
|
8572 |
+
'extension_spec' => array(
|
8573 |
+
'allowed_versions' => array(
|
8574 |
+
'0.1',
|
8575 |
+
'latest',
|
8576 |
+
),
|
8577 |
+
'name' => 'amp-subscriptions',
|
8578 |
+
'requires_usage' => 3,
|
8579 |
+
),
|
8580 |
+
),
|
8581 |
+
),
|
8582 |
+
array(
|
8583 |
+
'attr_spec_list' => array(
|
8584 |
+
'id' => array(
|
8585 |
+
'dispatch_key' => 2,
|
8586 |
+
'mandatory' => true,
|
8587 |
+
'value' => 'amp-subscriptions',
|
8588 |
+
),
|
8589 |
+
'nonce' => array(),
|
8590 |
+
'type' => array(
|
8591 |
+
'mandatory' => true,
|
8592 |
+
'value_casei' => 'application/json',
|
8593 |
+
),
|
8594 |
+
),
|
8595 |
+
'cdata' => array(
|
8596 |
+
'blacklisted_cdata_regex' => array(
|
8597 |
+
'error_message' => 'html comments',
|
8598 |
+
'regex' => '<!--',
|
8599 |
+
),
|
8600 |
+
),
|
8601 |
+
'tag_spec' => array(
|
8602 |
+
'mandatory_parent' => 'head',
|
8603 |
+
'requires_extension' => array(
|
8604 |
+
'amp-subscriptions',
|
8605 |
+
),
|
8606 |
+
'spec_name' => 'amp-subscriptions extension .json script',
|
8607 |
+
'unique' => true,
|
8608 |
+
),
|
8609 |
+
),
|
8610 |
+
array(
|
8611 |
+
'attr_spec_list' => array(
|
8612 |
+
'async' => array(
|
8613 |
+
'mandatory' => true,
|
8614 |
+
'value' => '',
|
8615 |
+
),
|
8616 |
+
'nonce' => array(),
|
8617 |
+
'type' => array(
|
8618 |
+
'value_casei' => 'text/javascript',
|
8619 |
+
),
|
8620 |
+
),
|
8621 |
+
'tag_spec' => array(
|
8622 |
+
'extension_spec' => array(
|
8623 |
+
'allowed_versions' => array(
|
8624 |
+
'0.1',
|
8625 |
+
'latest',
|
8626 |
+
),
|
8627 |
+
'name' => 'amp-subscriptions-google',
|
8628 |
+
'requires_usage' => 3,
|
8629 |
+
),
|
8630 |
+
'requires_extension' => array(
|
8631 |
+
'amp-subscriptions',
|
8632 |
+
),
|
8633 |
+
),
|
8634 |
+
),
|
8635 |
+
array(
|
8636 |
+
'attr_spec_list' => array(
|
8637 |
+
'async' => array(
|
8638 |
+
'mandatory' => true,
|
8639 |
+
'value' => '',
|
8640 |
+
),
|
8641 |
+
'nonce' => array(),
|
8642 |
+
'type' => array(
|
8643 |
+
'value_casei' => 'text/javascript',
|
8644 |
+
),
|
8645 |
+
),
|
8646 |
+
'tag_spec' => array(
|
8647 |
+
'extension_spec' => array(
|
8648 |
+
'allowed_versions' => array(
|
8649 |
+
'0.1',
|
8650 |
+
'latest',
|
8651 |
+
),
|
8652 |
+
'name' => 'amp-timeago',
|
8653 |
+
),
|
8654 |
+
),
|
8655 |
+
),
|
8656 |
+
array(
|
8657 |
+
'attr_spec_list' => array(
|
8658 |
+
'async' => array(
|
8659 |
+
'mandatory' => true,
|
8660 |
+
'value' => '',
|
8661 |
+
),
|
8662 |
+
'nonce' => array(),
|
8663 |
+
'type' => array(
|
8664 |
+
'value_casei' => 'text/javascript',
|
8665 |
+
),
|
8666 |
+
),
|
8667 |
+
'tag_spec' => array(
|
8668 |
+
'extension_spec' => array(
|
8669 |
+
'allowed_versions' => array(
|
8670 |
+
'0.1',
|
8671 |
+
'latest',
|
8672 |
+
),
|
8673 |
+
'deprecated_allow_duplicates' => true,
|
8674 |
+
'name' => 'amp-twitter',
|
8675 |
+
'requires_usage' => 2,
|
8676 |
+
),
|
8677 |
+
),
|
8678 |
+
),
|
8679 |
+
array(
|
8680 |
+
'attr_spec_list' => array(
|
8681 |
+
'async' => array(
|
8682 |
+
'mandatory' => true,
|
8683 |
+
'value' => '',
|
8684 |
+
),
|
8685 |
+
'nonce' => array(),
|
8686 |
+
'type' => array(
|
8687 |
+
'value_casei' => 'text/javascript',
|
8688 |
+
),
|
8689 |
+
),
|
8690 |
+
'tag_spec' => array(
|
8691 |
+
'extension_spec' => array(
|
8692 |
+
'allowed_versions' => array(
|
8693 |
+
'0.1',
|
8694 |
+
'latest',
|
8695 |
+
),
|
8696 |
+
'deprecated_allow_duplicates' => true,
|
8697 |
+
'name' => 'amp-user-notification',
|
8698 |
+
'requires_usage' => 2,
|
8699 |
+
),
|
8700 |
+
),
|
8701 |
+
),
|
8702 |
+
array(
|
8703 |
+
'attr_spec_list' => array(
|
8704 |
+
'async' => array(
|
8705 |
+
'mandatory' => true,
|
8706 |
+
'value' => '',
|
8707 |
+
),
|
8708 |
+
'nonce' => array(),
|
8709 |
+
'type' => array(
|
8710 |
+
'value_casei' => 'text/javascript',
|
8711 |
+
),
|
8712 |
+
),
|
8713 |
+
'tag_spec' => array(
|
8714 |
+
'extension_spec' => array(
|
8715 |
+
'allowed_versions' => array(
|
8716 |
+
'0.1',
|
8717 |
+
'latest',
|
8718 |
+
),
|
8719 |
+
'name' => 'amp-video',
|
8720 |
+
'requires_usage' => 3,
|
8721 |
+
),
|
8722 |
+
'spec_name' => 'amp-video extension .js script',
|
8723 |
+
),
|
8724 |
+
),
|
8725 |
+
array(
|
8726 |
+
'attr_spec_list' => array(
|
8727 |
+
'async' => array(
|
8728 |
+
'mandatory' => true,
|
8729 |
+
'value' => '',
|
8730 |
+
),
|
8731 |
+
'nonce' => array(),
|
8732 |
+
'type' => array(
|
8733 |
+
'value_casei' => 'text/javascript',
|
8734 |
+
),
|
8735 |
+
),
|
8736 |
+
'tag_spec' => array(
|
8737 |
+
'extension_spec' => array(
|
8738 |
+
'allowed_versions' => array(
|
8739 |
+
'0.1',
|
8740 |
+
'latest',
|
8741 |
+
),
|
8742 |
+
'deprecated_allow_duplicates' => true,
|
8743 |
+
'name' => 'amp-vimeo',
|
8744 |
+
'requires_usage' => 2,
|
8745 |
),
|
|
|
|
|
8746 |
),
|
|
|
8747 |
),
|
8748 |
array(
|
8749 |
'attr_spec_list' => array(
|
8750 |
+
'async' => array(
|
8751 |
+
'mandatory' => true,
|
8752 |
+
'value' => '',
|
8753 |
+
),
|
8754 |
'nonce' => array(),
|
8755 |
'type' => array(
|
8756 |
+
'value_casei' => 'text/javascript',
|
|
|
8757 |
),
|
8758 |
),
|
8759 |
'tag_spec' => array(
|
8760 |
+
'extension_spec' => array(
|
8761 |
+
'allowed_versions' => array(
|
8762 |
+
'0.1',
|
8763 |
+
'latest',
|
8764 |
+
),
|
8765 |
+
'deprecated_allow_duplicates' => true,
|
8766 |
+
'name' => 'amp-vine',
|
8767 |
+
'requires_usage' => 2,
|
8768 |
),
|
|
|
|
|
|
|
8769 |
),
|
|
|
8770 |
),
|
8771 |
array(
|
8772 |
'attr_spec_list' => array(
|
8773 |
+
'async' => array(
|
8774 |
+
'mandatory' => true,
|
8775 |
+
'value' => '',
|
8776 |
+
),
|
8777 |
'nonce' => array(),
|
8778 |
'type' => array(
|
8779 |
+
'value_casei' => 'text/javascript',
|
|
|
8780 |
),
|
8781 |
),
|
8782 |
'tag_spec' => array(
|
8783 |
+
'extension_spec' => array(
|
8784 |
+
'allowed_versions' => array(
|
8785 |
+
'0.1',
|
8786 |
+
'latest',
|
8787 |
+
),
|
8788 |
+
'name' => 'amp-vk',
|
8789 |
),
|
|
|
|
|
|
|
8790 |
),
|
|
|
8791 |
),
|
8792 |
array(
|
8793 |
'attr_spec_list' => array(
|
8794 |
+
'async' => array(
|
8795 |
+
'mandatory' => true,
|
8796 |
+
'value' => '',
|
8797 |
+
),
|
8798 |
'nonce' => array(),
|
8799 |
'type' => array(
|
8800 |
+
'value_casei' => 'text/javascript',
|
|
|
8801 |
),
|
8802 |
),
|
8803 |
'tag_spec' => array(
|
8804 |
+
'extension_spec' => array(
|
8805 |
+
'allowed_versions' => array(
|
8806 |
+
'0.1',
|
8807 |
+
'latest',
|
8808 |
+
),
|
8809 |
+
'name' => 'amp-web-push',
|
8810 |
+
),
|
8811 |
),
|
|
|
8812 |
),
|
8813 |
array(
|
8814 |
'attr_spec_list' => array(
|
8815 |
+
'async' => array(
|
8816 |
+
'mandatory' => true,
|
8817 |
+
'value' => '',
|
8818 |
+
),
|
8819 |
'nonce' => array(),
|
8820 |
'type' => array(
|
8821 |
+
'value_casei' => 'text/javascript',
|
|
|
8822 |
),
|
8823 |
),
|
8824 |
'tag_spec' => array(
|
8825 |
+
'extension_spec' => array(
|
8826 |
+
'allowed_versions' => array(
|
8827 |
+
'0.1',
|
8828 |
+
'latest',
|
8829 |
+
),
|
8830 |
+
'name' => 'amp-wistia-player',
|
8831 |
),
|
|
|
|
|
|
|
8832 |
),
|
|
|
8833 |
),
|
8834 |
array(
|
8835 |
'attr_spec_list' => array(
|
8836 |
+
'async' => array(
|
8837 |
+
'mandatory' => true,
|
8838 |
+
'value' => '',
|
8839 |
+
),
|
8840 |
'nonce' => array(),
|
8841 |
'type' => array(
|
8842 |
+
'value_casei' => 'text/javascript',
|
|
|
8843 |
),
|
8844 |
),
|
8845 |
'tag_spec' => array(
|
8846 |
+
'extension_spec' => array(
|
8847 |
+
'allowed_versions' => array(
|
8848 |
+
'0.1',
|
8849 |
+
'latest',
|
8850 |
+
),
|
8851 |
+
'deprecated_allow_duplicates' => true,
|
8852 |
+
'name' => 'amp-youtube',
|
8853 |
+
'requires_usage' => 2,
|
8854 |
),
|
|
|
|
|
|
|
8855 |
),
|
|
|
8856 |
),
|
8857 |
),
|
8858 |
'section' => array(
|
8860 |
'attr_spec_list' => array(),
|
8861 |
'tag_spec' => array(
|
8862 |
'disallowed_ancestor' => array(
|
8863 |
+
'amp-accordion',
|
8864 |
),
|
8865 |
),
|
|
|
8866 |
),
|
8867 |
array(
|
8868 |
'attr_spec_list' => array(
|
8874 |
'mandatory_parent' => 'amp-accordion',
|
8875 |
'spec_name' => 'amp-accordion > section',
|
8876 |
),
|
|
|
8877 |
),
|
8878 |
),
|
8879 |
'select' => array(
|
8888 |
'disabled' => array(),
|
8889 |
'multiple' => array(),
|
8890 |
'name' => array(
|
8891 |
+
'blacklisted_value_regex' => '(^|\\s)(__amp_\\S*|__count__|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|__noSuchMethod__|__parent__|__proto__|__AMP_\\S*|\\$p|\\$proxy|acceptCharset|addEventListener|appendChild|assignedSlot|attachShadow|baseURI|checkValidity|childElementCount|childNodes|classList|className|clientHeight|clientLeft|clientTop|clientWidth|compareDocumentPosition|computedName|computedRole|contentEditable|createShadowRoot|enqueAction|firstChild|firstElementChild|getAnimations|getAttribute|getAttributeNS|getAttributeNode|getAttributeNodeNS|getBoundingClientRect|getClientRects|getDestinationInsertionPoints|getElementsByClassName|getElementsByTagName|getElementsByTagNameNS|getRootNode|hasAttribute|hasAttributeNS|hasAttributes|hasChildNodes|hasPointerCapture|innerHTML|innerText|inputMode|insertAdjacentElement|insertAdjacentHTML|insertAdjacentText|isContentEditable|isDefaultNamespace|isEqualNode|isSameNode|lastChild|lastElementChild|lookupNamespaceURI|namespaceURI|nextElementSibling|nextSibling|nodeName|nodeType|nodeValue|offsetHeight|offsetLeft|offsetParent|offsetTop|offsetWidth|outerHTML|outerText|ownerDocument|parentElement|parentNode|previousElementSibling|previousSibling|querySelector|querySelectorAll|releasePointerCapture|removeAttribute|removeAttributeNS|removeAttributeNode|removeChild|removeEventListener|replaceChild|reportValidity|requestPointerLock|scrollHeight|scrollIntoView|scrollIntoViewIfNeeded|scrollLeft|scrollWidth|setAttribute|setAttributeNS|setAttributeNode|setAttributeNodeNS|setPointerCapture|shadowRoot|styleMap|tabIndex|tagName|textContent|toString|valueOf|(webkit|ms|moz|o)dropzone|(webkit|moz|ms|o)MatchesSelector|(webkit|moz|ms|o)RequestFullScreen|(webkit|moz|ms|o)RequestFullscreen)(\\s|$)',
|
8892 |
),
|
8893 |
'required' => array(),
|
8894 |
'size' => array(),
|
8896 |
'tag_spec' => array(
|
8897 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
8898 |
),
|
|
|
8899 |
),
|
8900 |
),
|
8901 |
'slot' => array(
|
8903 |
'attr_spec_list' => array(
|
8904 |
'name' => array(),
|
8905 |
),
|
8906 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
8907 |
),
|
8908 |
),
|
8909 |
'small' => array(
|
8910 |
array(
|
8911 |
'attr_spec_list' => array(),
|
8912 |
'tag_spec' => array(),
|
|
|
8913 |
),
|
8914 |
),
|
8915 |
'solidcolor' => array(
|
8986 |
'xmlns:xlink' => array(),
|
8987 |
),
|
8988 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
8989 |
'mandatory_ancestor' => 'svg',
|
8990 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
8991 |
),
|
|
|
8992 |
),
|
8993 |
),
|
8994 |
'source' => array(
|
8999 |
'media' => array(),
|
9000 |
'src' => array(
|
9001 |
'blacklisted_value_regex' => '__amp_source_origin',
|
9002 |
+
'value_url' => array(
|
9003 |
+
'allow_relative' => true,
|
9004 |
+
'allowed_protocol' => array(
|
9005 |
+
'https',
|
9006 |
+
),
|
9007 |
),
|
9008 |
),
|
9009 |
'type' => array(),
|
9010 |
),
|
9011 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9012 |
'mandatory_parent' => 'amp-video',
|
9013 |
'spec_name' => 'amp-video > source',
|
9014 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
|
9015 |
),
|
|
|
9016 |
),
|
9017 |
array(
|
9018 |
'attr_spec_list' => array(
|
9021 |
'media' => array(),
|
9022 |
'src' => array(
|
9023 |
'blacklisted_value_regex' => '__amp_source_origin',
|
9024 |
+
'value_url' => array(
|
9025 |
+
'allow_relative' => true,
|
9026 |
+
'allowed_protocol' => array(
|
9027 |
+
'https',
|
9028 |
+
),
|
9029 |
),
|
9030 |
),
|
9031 |
'type' => array(),
|
9032 |
),
|
9033 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9034 |
'mandatory_parent' => 'amp-audio',
|
9035 |
'spec_name' => 'amp-audio > source',
|
9036 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
9037 |
),
|
|
|
9038 |
),
|
9039 |
array(
|
9040 |
'attr_spec_list' => array(
|
9042 |
'src' => array(
|
9043 |
'blacklisted_value_regex' => '__amp_source_origin',
|
9044 |
'mandatory' => true,
|
9045 |
+
'value_url' => array(
|
9046 |
+
'allow_relative' => true,
|
9047 |
+
'allowed_protocol' => array(
|
9048 |
+
'https',
|
9049 |
+
),
|
9050 |
),
|
9051 |
),
|
9052 |
'type' => array(
|
9054 |
),
|
9055 |
),
|
9056 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9057 |
'mandatory_parent' => 'audio',
|
9058 |
'spec_name' => 'audio > source',
|
9059 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-audio',
|
9060 |
),
|
|
|
9061 |
),
|
9062 |
array(
|
9063 |
'attr_spec_list' => array(
|
9065 |
'src' => array(
|
9066 |
'blacklisted_value_regex' => '__amp_source_origin',
|
9067 |
'mandatory' => true,
|
9068 |
+
'value_url' => array(
|
9069 |
+
'allow_relative' => true,
|
9070 |
+
'allowed_protocol' => array(
|
9071 |
+
'https',
|
9072 |
+
),
|
9073 |
),
|
9074 |
),
|
9075 |
'type' => array(
|
9077 |
),
|
9078 |
),
|
9079 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9080 |
'mandatory_parent' => 'video',
|
9081 |
'spec_name' => 'video > source',
|
9082 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
|
9083 |
),
|
|
|
9084 |
),
|
9085 |
array(
|
9086 |
'attr_spec_list' => array(
|
9089 |
'media' => array(),
|
9090 |
'src' => array(
|
9091 |
'blacklisted_value_regex' => '__amp_source_origin',
|
9092 |
+
'value_url' => array(
|
9093 |
+
'allow_relative' => true,
|
9094 |
+
'allowed_protocol' => array(
|
9095 |
+
'https',
|
9096 |
+
),
|
9097 |
),
|
9098 |
),
|
9099 |
'type' => array(),
|
9100 |
),
|
9101 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9102 |
'mandatory_parent' => 'amp-ima-video',
|
9103 |
+
'requires_extension' => array(
|
9104 |
+
'amp-ima-video',
|
9105 |
+
),
|
9106 |
'spec_name' => 'amp-ima-video > source',
|
9107 |
),
|
|
|
9108 |
),
|
9109 |
),
|
9110 |
'spacer' => array(
|
9111 |
array(
|
9112 |
'attr_spec_list' => array(),
|
9113 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
9114 |
),
|
9115 |
),
|
9116 |
'span' => array(
|
9117 |
array(
|
9118 |
'attr_spec_list' => array(),
|
9119 |
'tag_spec' => array(),
|
|
|
9120 |
),
|
9121 |
),
|
9122 |
'stop' => array(
|
9130 |
),
|
9131 |
),
|
9132 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9133 |
'mandatory_ancestor' => 'lineargradient',
|
9134 |
'spec_name' => 'lineargradient > stop',
|
9135 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
9136 |
),
|
|
|
9137 |
),
|
9138 |
array(
|
9139 |
'attr_spec_list' => array(
|
9145 |
),
|
9146 |
),
|
9147 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9148 |
'mandatory_ancestor' => 'radialgradient',
|
9149 |
'spec_name' => 'radialgradient > stop',
|
9150 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
9151 |
),
|
|
|
9152 |
),
|
9153 |
),
|
9154 |
'strike' => array(
|
9155 |
array(
|
9156 |
'attr_spec_list' => array(),
|
9157 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
9158 |
),
|
9159 |
),
|
9160 |
'strong' => array(
|
9161 |
array(
|
9162 |
'attr_spec_list' => array(),
|
9163 |
'tag_spec' => array(),
|
|
|
9164 |
),
|
9165 |
),
|
9166 |
'style' => array(
|
9167 |
+
array(
|
9168 |
+
'attr_spec_list' => array(
|
9169 |
+
'amp-custom' => array(
|
9170 |
+
'mandatory' => true,
|
9171 |
+
'value' => '',
|
9172 |
+
),
|
9173 |
+
'nonce' => array(),
|
9174 |
+
'type' => array(
|
9175 |
+
'value_casei' => 'text/css',
|
9176 |
+
),
|
9177 |
+
),
|
9178 |
+
'cdata' => array(
|
9179 |
+
'blacklisted_cdata_regex' => array(
|
9180 |
+
'error_message' => 'CSS !important',
|
9181 |
+
'regex' => '!important',
|
9182 |
+
),
|
9183 |
+
'max_bytes' => 50000,
|
9184 |
+
'max_bytes_spec_url' => 'https://www.ampproject.org/docs/reference/spec#maximum-size',
|
9185 |
+
),
|
9186 |
+
'tag_spec' => array(
|
9187 |
+
'mandatory_parent' => 'head',
|
9188 |
+
'spec_name' => 'style amp-custom',
|
9189 |
+
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#stylesheets',
|
9190 |
+
'unique' => true,
|
9191 |
+
),
|
9192 |
+
),
|
9193 |
array(
|
9194 |
'attr_spec_list' => array(
|
9195 |
'amp-boilerplate' => array(
|
9196 |
+
'dispatch_key' => 3,
|
9197 |
'mandatory' => true,
|
9198 |
'value' => '',
|
9199 |
),
|
9200 |
'nonce' => array(),
|
9201 |
),
|
9202 |
+
'cdata' => array(
|
9203 |
+
'cdata_regex' => '\\s*body{-webkit-animation:-amp-start\\s+8s\\s+steps\\(1,end\\)\\s+0s\\s+1\\s+normal\\s+both;-moz-animation:-amp-start\\s+8s\\s+steps\\(1,end\\)\\s+0s\\s+1\\s+normal\\s+both;-ms-animation:-amp-start\\s+8s\\s+steps\\(1,end\\)\\s+0s\\s+1\\s+normal\\s+both;animation:-amp-start\\s+8s\\s+steps\\(1,end\\)\\s+0s\\s+1\\s+normal\\s+both}@-webkit-keyframes\\s+-amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes\\s+-amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes\\s+-amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes\\s+-amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes\\s+-amp-start{from{visibility:hidden}to{visibility:visible}}\\s*',
|
9204 |
+
),
|
9205 |
'tag_spec' => array(
|
9206 |
+
'mandatory_alternatives' => 'head > style[amp-boilerplate]',
|
9207 |
+
'mandatory_parent' => 'head',
|
9208 |
+
'spec_name' => 'head > style[amp-boilerplate]',
|
9209 |
+
'spec_url' => 'https://github.com/ampproject/amphtml/blob/master/spec/amp-boilerplate.md',
|
9210 |
+
'unique' => true,
|
9211 |
+
),
|
9212 |
+
),
|
9213 |
+
array(
|
9214 |
+
'attr_spec_list' => array(
|
9215 |
+
'amp-boilerplate' => array(
|
9216 |
+
'dispatch_key' => 3,
|
9217 |
+
'mandatory' => true,
|
9218 |
+
'value' => '',
|
9219 |
),
|
9220 |
+
'nonce' => array(),
|
9221 |
+
),
|
9222 |
+
'cdata' => array(
|
9223 |
+
'cdata_regex' => '\\s*body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}\\s*',
|
9224 |
+
),
|
9225 |
+
'tag_spec' => array(
|
9226 |
'mandatory_alternatives' => 'noscript > style[amp-boilerplate]',
|
9227 |
'mandatory_ancestor' => 'head',
|
9228 |
'mandatory_parent' => 'noscript',
|
9230 |
'spec_url' => 'https://github.com/ampproject/amphtml/blob/master/spec/amp-boilerplate.md',
|
9231 |
'unique' => true,
|
9232 |
),
|
|
|
9233 |
),
|
9234 |
array(
|
9235 |
'attr_spec_list' => array(
|
9236 |
'amp-keyframes' => array(
|
9237 |
+
'dispatch_key' => 1,
|
9238 |
'mandatory' => true,
|
9239 |
'value' => '',
|
9240 |
),
|
9241 |
),
|
9242 |
+
'cdata' => array(
|
9243 |
+
'max_bytes' => 500000,
|
9244 |
+
'max_bytes_spec_url' => 'https://www.ampproject.org/docs/reference/spec#keyframes-stylesheet',
|
9245 |
+
),
|
9246 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9247 |
'mandatory_parent' => 'body',
|
9248 |
'spec_name' => 'style[amp-keyframes]',
|
9249 |
'unique' => true,
|
9250 |
),
|
|
|
9251 |
),
|
9252 |
),
|
9253 |
'sub' => array(
|
9254 |
array(
|
9255 |
'attr_spec_list' => array(),
|
9256 |
'tag_spec' => array(),
|
|
|
9257 |
),
|
9258 |
),
|
9259 |
'sup' => array(
|
9260 |
array(
|
9261 |
'attr_spec_list' => array(),
|
9262 |
'tag_spec' => array(),
|
|
|
9263 |
),
|
9264 |
),
|
9265 |
'svg' => array(
|
9347 |
'zoomandpan' => array(),
|
9348 |
),
|
9349 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9350 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
9351 |
),
|
|
|
9352 |
),
|
9353 |
),
|
9354 |
'switch' => array(
|
9426 |
'xmlns:xlink' => array(),
|
9427 |
),
|
9428 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9429 |
'mandatory_ancestor' => 'svg',
|
9430 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
9431 |
),
|
|
|
9432 |
),
|
9433 |
),
|
9434 |
'symbol' => array(
|
9506 |
'xmlns:xlink' => array(),
|
9507 |
),
|
9508 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9509 |
'mandatory_ancestor' => 'svg',
|
9510 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
9511 |
),
|
|
|
9512 |
),
|
9513 |
),
|
9514 |
'table' => array(
|
9525 |
'width' => array(),
|
9526 |
),
|
9527 |
'tag_spec' => array(),
|
|
|
9528 |
),
|
9529 |
),
|
9530 |
'tbody' => array(
|
9531 |
array(
|
9532 |
'attr_spec_list' => array(),
|
9533 |
'tag_spec' => array(),
|
|
|
9534 |
),
|
9535 |
),
|
9536 |
'td' => array(
|
9546 |
'width' => array(),
|
9547 |
),
|
9548 |
'tag_spec' => array(),
|
|
|
9549 |
),
|
9550 |
),
|
9551 |
'template' => array(
|
9558 |
),
|
9559 |
'tag_spec' => array(
|
9560 |
'disallowed_ancestor' => array(
|
9561 |
+
'template',
|
9562 |
+
'amp-story-auto-ads',
|
9563 |
+
'form > div [submit-success][template]',
|
9564 |
+
'form > div [submit-error][template]',
|
9565 |
+
),
|
9566 |
+
'requires_extension' => array(
|
9567 |
+
'amp-mustache',
|
9568 |
),
|
9569 |
),
|
9570 |
+
),
|
9571 |
+
array(
|
9572 |
+
'attr_spec_list' => array(
|
9573 |
+
'type' => array(
|
9574 |
+
'mandatory' => true,
|
9575 |
+
'value' => 'amp-mustache',
|
9576 |
+
),
|
9577 |
+
),
|
9578 |
+
'tag_spec' => array(
|
9579 |
+
'mandatory_parent' => 'amp-story-auto-ads',
|
9580 |
+
'requires_extension' => array(
|
9581 |
+
'amp-mustache',
|
9582 |
+
),
|
9583 |
+
'spec_name' => 'amp-story-auto-ads > template',
|
9584 |
+
),
|
9585 |
),
|
9586 |
),
|
9587 |
'text' => array(
|
9668 |
'y' => array(),
|
9669 |
),
|
9670 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9671 |
'mandatory_ancestor' => 'svg',
|
9672 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
9673 |
),
|
|
|
9674 |
),
|
9675 |
),
|
9676 |
'textarea' => array(
|
9698 |
'maxlength' => array(),
|
9699 |
'minlength' => array(),
|
9700 |
'name' => array(
|
9701 |
+
'blacklisted_value_regex' => '(^|\\s)(__amp_\\S*|__count__|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|__noSuchMethod__|__parent__|__proto__|__AMP_\\S*|\\$p|\\$proxy|acceptCharset|addEventListener|appendChild|assignedSlot|attachShadow|baseURI|checkValidity|childElementCount|childNodes|classList|className|clientHeight|clientLeft|clientTop|clientWidth|compareDocumentPosition|computedName|computedRole|contentEditable|createShadowRoot|enqueAction|firstChild|firstElementChild|getAnimations|getAttribute|getAttributeNS|getAttributeNode|getAttributeNodeNS|getBoundingClientRect|getClientRects|getDestinationInsertionPoints|getElementsByClassName|getElementsByTagName|getElementsByTagNameNS|getRootNode|hasAttribute|hasAttributeNS|hasAttributes|hasChildNodes|hasPointerCapture|innerHTML|innerText|inputMode|insertAdjacentElement|insertAdjacentHTML|insertAdjacentText|isContentEditable|isDefaultNamespace|isEqualNode|isSameNode|lastChild|lastElementChild|lookupNamespaceURI|namespaceURI|nextElementSibling|nextSibling|nodeName|nodeType|nodeValue|offsetHeight|offsetLeft|offsetParent|offsetTop|offsetWidth|outerHTML|outerText|ownerDocument|parentElement|parentNode|previousElementSibling|previousSibling|querySelector|querySelectorAll|releasePointerCapture|removeAttribute|removeAttributeNS|removeAttributeNode|removeChild|removeEventListener|replaceChild|reportValidity|requestPointerLock|scrollHeight|scrollIntoView|scrollIntoViewIfNeeded|scrollLeft|scrollWidth|setAttribute|setAttributeNS|setAttributeNode|setAttributeNodeNS|setPointerCapture|shadowRoot|styleMap|tabIndex|tagName|textContent|toString|valueOf|(webkit|ms|moz|o)dropzone|(webkit|moz|ms|o)MatchesSelector|(webkit|moz|ms|o)RequestFullScreen|(webkit|moz|ms|o)RequestFullscreen)(\\s|$)',
|
9702 |
),
|
9703 |
'placeholder' => array(),
|
9704 |
'readonly' => array(),
|
9713 |
'tag_spec' => array(
|
9714 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
|
9715 |
),
|
|
|
9716 |
),
|
9717 |
),
|
9718 |
'textpath' => array(
|
9794 |
'alternative_names' => array(
|
9795 |
'href',
|
9796 |
),
|
9797 |
+
'value_url' => array(
|
9798 |
+
'allow_empty' => false,
|
9799 |
+
'allow_relative' => true,
|
9800 |
+
'allowed_protocol' => array(
|
9801 |
+
'http',
|
9802 |
+
'https',
|
9803 |
+
),
|
9804 |
),
|
9805 |
),
|
9806 |
'xlink:role' => array(),
|
9813 |
'xmlns:xlink' => array(),
|
9814 |
),
|
9815 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9816 |
'mandatory_ancestor' => 'svg',
|
9817 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
9818 |
),
|
|
|
9819 |
),
|
9820 |
),
|
9821 |
'tfoot' => array(
|
9822 |
array(
|
9823 |
'attr_spec_list' => array(),
|
9824 |
'tag_spec' => array(),
|
|
|
9825 |
),
|
9826 |
),
|
9827 |
'th' => array(
|
9840 |
'width' => array(),
|
9841 |
),
|
9842 |
'tag_spec' => array(),
|
|
|
9843 |
),
|
9844 |
),
|
9845 |
'thead' => array(
|
9846 |
array(
|
9847 |
'attr_spec_list' => array(),
|
9848 |
'tag_spec' => array(),
|
|
|
9849 |
),
|
9850 |
),
|
9851 |
'time' => array(
|
9854 |
'datetime' => array(),
|
9855 |
),
|
9856 |
'tag_spec' => array(),
|
|
|
9857 |
),
|
9858 |
),
|
9859 |
'title' => array(
|
9862 |
'tag_spec' => array(
|
9863 |
'spec_name' => 'title',
|
9864 |
),
|
|
|
9865 |
),
|
9866 |
array(
|
9867 |
'attr_spec_list' => array(
|
9874 |
'xmlns:xlink' => array(),
|
9875 |
),
|
9876 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9877 |
'mandatory_ancestor' => 'svg',
|
9878 |
'spec_name' => 'svg title',
|
9879 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
9880 |
),
|
|
|
9881 |
),
|
9882 |
),
|
9883 |
'tr' => array(
|
9889 |
'valign' => array(),
|
9890 |
),
|
9891 |
'tag_spec' => array(),
|
|
|
9892 |
),
|
9893 |
),
|
9894 |
'track' => array(
|
9904 |
'src' => array(
|
9905 |
'blacklisted_value_regex' => '__amp_source_origin',
|
9906 |
'mandatory' => true,
|
9907 |
+
'value_url' => array(
|
9908 |
+
'allow_relative' => false,
|
9909 |
+
'allowed_protocol' => array(
|
9910 |
+
'https',
|
9911 |
+
),
|
9912 |
),
|
9913 |
),
|
9914 |
'srclang' => array(),
|
9915 |
),
|
9916 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9917 |
'mandatory_parent' => 'audio',
|
9918 |
'spec_name' => 'audio > track',
|
9919 |
),
|
|
|
9920 |
),
|
9921 |
array(
|
9922 |
'attr_spec_list' => array(
|
9931 |
'src' => array(
|
9932 |
'blacklisted_value_regex' => '__amp_source_origin',
|
9933 |
'mandatory' => true,
|
9934 |
+
'value_url' => array(
|
9935 |
+
'allow_relative' => false,
|
9936 |
+
'allowed_protocol' => array(
|
9937 |
+
'https',
|
9938 |
+
),
|
9939 |
),
|
9940 |
),
|
9941 |
'srclang' => array(
|
9943 |
),
|
9944 |
),
|
9945 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9946 |
'mandatory_parent' => 'audio',
|
9947 |
'spec_name' => 'audio > track[kind=subtitles]',
|
9948 |
),
|
|
|
9949 |
),
|
9950 |
array(
|
9951 |
'attr_spec_list' => array(
|
9959 |
'src' => array(
|
9960 |
'blacklisted_value_regex' => '__amp_source_origin',
|
9961 |
'mandatory' => true,
|
9962 |
+
'value_url' => array(
|
9963 |
+
'allow_relative' => false,
|
9964 |
+
'allowed_protocol' => array(
|
9965 |
+
'https',
|
9966 |
+
),
|
9967 |
),
|
9968 |
),
|
9969 |
'srclang' => array(),
|
9970 |
),
|
9971 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
9972 |
'mandatory_parent' => 'video',
|
9973 |
'spec_name' => 'video > track',
|
9974 |
),
|
|
|
9975 |
),
|
9976 |
array(
|
9977 |
'attr_spec_list' => array(
|
9986 |
'src' => array(
|
9987 |
'blacklisted_value_regex' => '__amp_source_origin',
|
9988 |
'mandatory' => true,
|
9989 |
+
'value_url' => array(
|
9990 |
+
'allow_relative' => false,
|
9991 |
+
'allowed_protocol' => array(
|
9992 |
+
'https',
|
9993 |
+
),
|
9994 |
),
|
9995 |
),
|
9996 |
'srclang' => array(
|
9998 |
),
|
9999 |
),
|
10000 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10001 |
'mandatory_parent' => 'video',
|
10002 |
'spec_name' => 'video > track[kind=subtitles]',
|
10003 |
),
|
|
|
10004 |
),
|
10005 |
array(
|
10006 |
'attr_spec_list' => array(
|
10017 |
'src' => array(
|
10018 |
'blacklisted_value_regex' => '__amp_source_origin',
|
10019 |
'mandatory' => true,
|
10020 |
+
'value_url' => array(
|
10021 |
+
'allow_relative' => false,
|
10022 |
+
'allowed_protocol' => array(
|
10023 |
+
'https',
|
10024 |
+
),
|
10025 |
),
|
10026 |
),
|
10027 |
'srclang' => array(),
|
10028 |
),
|
10029 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10030 |
'mandatory_parent' => 'amp-audio',
|
10031 |
'spec_name' => 'amp-audio > track',
|
10032 |
),
|
|
|
10033 |
),
|
10034 |
array(
|
10035 |
'attr_spec_list' => array(
|
10047 |
'src' => array(
|
10048 |
'blacklisted_value_regex' => '__amp_source_origin',
|
10049 |
'mandatory' => true,
|
10050 |
+
'value_url' => array(
|
10051 |
+
'allow_relative' => false,
|
10052 |
+
'allowed_protocol' => array(
|
10053 |
+
'https',
|
10054 |
+
),
|
10055 |
),
|
10056 |
),
|
10057 |
'srclang' => array(
|
10059 |
),
|
10060 |
),
|
10061 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10062 |
'mandatory_parent' => 'amp-audio',
|
10063 |
'spec_name' => 'amp-audio > track[kind=subtitles]',
|
10064 |
),
|
|
|
10065 |
),
|
10066 |
array(
|
10067 |
'attr_spec_list' => array(
|
10078 |
'src' => array(
|
10079 |
'blacklisted_value_regex' => '__amp_source_origin',
|
10080 |
'mandatory' => true,
|
10081 |
+
'value_url' => array(
|
10082 |
+
'allow_relative' => false,
|
10083 |
+
'allowed_protocol' => array(
|
10084 |
+
'https',
|
10085 |
+
),
|
10086 |
),
|
10087 |
),
|
10088 |
'srclang' => array(),
|
10089 |
),
|
10090 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10091 |
'mandatory_parent' => 'amp-video',
|
10092 |
'spec_name' => 'amp-video > track',
|
10093 |
),
|
|
|
10094 |
),
|
10095 |
array(
|
10096 |
'attr_spec_list' => array(
|
10108 |
'src' => array(
|
10109 |
'blacklisted_value_regex' => '__amp_source_origin',
|
10110 |
'mandatory' => true,
|
10111 |
+
'value_url' => array(
|
10112 |
+
'allow_relative' => false,
|
10113 |
+
'allowed_protocol' => array(
|
10114 |
+
'https',
|
10115 |
+
),
|
10116 |
),
|
10117 |
),
|
10118 |
'srclang' => array(
|
10120 |
),
|
10121 |
),
|
10122 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10123 |
'mandatory_parent' => 'amp-video',
|
10124 |
'spec_name' => 'amp-video > track[kind=subtitles]',
|
10125 |
),
|
|
|
10126 |
),
|
10127 |
array(
|
10128 |
'attr_spec_list' => array(
|
10140 |
'src' => array(
|
10141 |
'blacklisted_value_regex' => '__amp_source_origin',
|
10142 |
'mandatory' => true,
|
10143 |
+
'value_url' => array(
|
10144 |
+
'allow_relative' => false,
|
10145 |
+
'allowed_protocol' => array(
|
10146 |
+
'https',
|
10147 |
+
),
|
10148 |
),
|
10149 |
),
|
10150 |
'srclang' => array(
|
10152 |
),
|
10153 |
),
|
10154 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10155 |
'mandatory_parent' => 'amp-ima-video',
|
10156 |
'spec_name' => 'amp-ima-video > track[kind=subtitles]',
|
10157 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ima-video',
|
10158 |
),
|
|
|
10159 |
),
|
10160 |
),
|
10161 |
'tref' => array(
|
10234 |
'alternative_names' => array(
|
10235 |
'href',
|
10236 |
),
|
10237 |
+
'value_url' => array(
|
10238 |
+
'allow_empty' => false,
|
10239 |
+
'allow_relative' => true,
|
10240 |
+
'allowed_protocol' => array(
|
10241 |
+
'http',
|
10242 |
+
'https',
|
10243 |
+
),
|
10244 |
),
|
10245 |
),
|
10246 |
'xlink:role' => array(),
|
10253 |
'xmlns:xlink' => array(),
|
10254 |
),
|
10255 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10256 |
'mandatory_ancestor' => 'svg',
|
10257 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
10258 |
),
|
|
|
10259 |
),
|
10260 |
),
|
10261 |
'tspan' => array(
|
10341 |
'y' => array(),
|
10342 |
),
|
10343 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10344 |
'mandatory_ancestor' => 'svg',
|
10345 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
10346 |
),
|
|
|
10347 |
),
|
10348 |
),
|
10349 |
'tt' => array(
|
10350 |
array(
|
10351 |
'attr_spec_list' => array(),
|
10352 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
10353 |
),
|
10354 |
),
|
10355 |
'u' => array(
|
10356 |
array(
|
10357 |
'attr_spec_list' => array(),
|
10358 |
'tag_spec' => array(),
|
|
|
10359 |
),
|
10360 |
),
|
10361 |
'ul' => array(
|
10362 |
array(
|
10363 |
'attr_spec_list' => array(),
|
10364 |
'tag_spec' => array(),
|
|
|
10365 |
),
|
10366 |
),
|
10367 |
'use' => array(
|
10444 |
'alternative_names' => array(
|
10445 |
'href',
|
10446 |
),
|
10447 |
+
'value_url' => array(
|
10448 |
+
'allow_empty' => false,
|
10449 |
+
'allow_relative' => true,
|
10450 |
+
'allowed_protocol' => array(
|
10451 |
+
'http',
|
10452 |
+
'https',
|
10453 |
+
),
|
10454 |
),
|
10455 |
),
|
10456 |
'xlink:role' => array(),
|
10464 |
'y' => array(),
|
10465 |
),
|
10466 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10467 |
'mandatory_ancestor' => 'svg',
|
10468 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
10469 |
),
|
|
|
10470 |
),
|
10471 |
),
|
10472 |
'var' => array(
|
10473 |
array(
|
10474 |
'attr_spec_list' => array(),
|
10475 |
'tag_spec' => array(),
|
|
|
10476 |
),
|
10477 |
),
|
10478 |
'video' => array(
|
10488 |
'preload' => array(),
|
10489 |
'src' => array(
|
10490 |
'blacklisted_value_regex' => '__amp_source_origin',
|
10491 |
+
'value_url' => array(
|
10492 |
+
'allow_relative' => false,
|
10493 |
+
'allowed_protocol' => array(
|
10494 |
+
'data',
|
10495 |
+
'https',
|
10496 |
+
),
|
10497 |
),
|
10498 |
),
|
10499 |
'width' => array(),
|
10500 |
),
|
10501 |
'tag_spec' => array(
|
|
|
|
|
|
|
10502 |
'mandatory_ancestor' => 'noscript',
|
10503 |
'mandatory_ancestor_suggested_alternative' => 'amp-video',
|
10504 |
'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
|
10505 |
),
|
|
|
10506 |
),
|
10507 |
),
|
10508 |
'view' => array(
|
10522 |
'zoomandpan' => array(),
|
10523 |
),
|
10524 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10525 |
'mandatory_ancestor' => 'svg',
|
10526 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
10527 |
),
|
|
|
10528 |
),
|
10529 |
),
|
10530 |
'vkern' => array(
|
10544 |
'xmlns:xlink' => array(),
|
10545 |
),
|
10546 |
'tag_spec' => array(
|
|
|
|
|
|
|
|
|
10547 |
'mandatory_ancestor' => 'svg',
|
10548 |
'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
|
10549 |
),
|
|
|
10550 |
),
|
10551 |
),
|
10552 |
'wbr' => array(
|
10553 |
array(
|
10554 |
'attr_spec_list' => array(),
|
10555 |
'tag_spec' => array(),
|
|
|
10556 |
),
|
10557 |
),
|
10558 |
'xmp' => array(
|
10559 |
array(
|
10560 |
'attr_spec_list' => array(),
|
10561 |
+
'tag_spec' => array(),
|
|
|
|
|
|
|
|
|
|
|
10562 |
),
|
10563 |
),
|
10564 |
);
|
10571 |
'layout' => array(),
|
10572 |
'sizes' => array(),
|
10573 |
'width' => array(),
|
|
|
10574 |
);
|
10575 |
|
10576 |
|
10626 |
'amp-access-show' => array(),
|
10627 |
'amp-access-style' => array(),
|
10628 |
'amp-access-template' => array(),
|
10629 |
+
'amp-fx' => array(
|
10630 |
+
'value_casei' => 'parallax',
|
10631 |
+
),
|
10632 |
'aria-activedescendant' => array(),
|
10633 |
'aria-atomic' => array(),
|
10634 |
'aria-autocomplete' => array(),
|
10666 |
'aria-valuenow' => array(),
|
10667 |
'aria-valuetext' => array(),
|
10668 |
'class' => array(
|
10669 |
+
'blacklisted_value_regex' => '(^|\\W)i-amphtml-',
|
10670 |
),
|
10671 |
'content' => array(),
|
10672 |
'datatype' => array(),
|
10673 |
'dir' => array(),
|
10674 |
'draggable' => array(),
|
10675 |
'fallback' => array(
|
10676 |
+
'value' => '',
|
10677 |
),
|
10678 |
'hidden' => array(
|
10679 |
+
'value' => '',
|
10680 |
),
|
10681 |
'i-amp-access-id' => array(),
|
10682 |
'id' => array(
|
10683 |
+
'blacklisted_value_regex' => '(^|\\s)(__amp_\\S*|__count__|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|__noSuchMethod__|__parent__|__proto__|__AMP_\\S*|\\$p|\\$proxy|acceptCharset|addEventListener|appendChild|assignedSlot|attachShadow|AMP|baseURI|checkValidity|childElementCount|childNodes|classList|className|clientHeight|clientLeft|clientTop|clientWidth|compareDocumentPosition|computedName|computedRole|contentEditable|createShadowRoot|enqueAction|firstChild|firstElementChild|getAnimations|getAttribute|getAttributeNS|getAttributeNode|getAttributeNodeNS|getBoundingClientRect|getClientRects|getDestinationInsertionPoints|getElementsByClassName|getElementsByTagName|getElementsByTagNameNS|getRootNode|hasAttribute|hasAttributeNS|hasAttributes|hasChildNodes|hasPointerCapture|i-amphtml-\\S*|innerHTML|innerText|inputMode|insertAdjacentElement|insertAdjacentHTML|insertAdjacentText|isContentEditable|isDefaultNamespace|isEqualNode|isSameNode|lastChild|lastElementChild|lookupNamespaceURI|namespaceURI|nextElementSibling|nextSibling|nodeName|nodeType|nodeValue|offsetHeight|offsetLeft|offsetParent|offsetTop|offsetWidth|outerHTML|outerText|ownerDocument|parentElement|parentNode|previousElementSibling|previousSibling|querySelector|querySelectorAll|releasePointerCapture|removeAttribute|removeAttributeNS|removeAttributeNode|removeChild|removeEventListener|replaceChild|reportValidity|requestPointerLock|scrollHeight|scrollIntoView|scrollIntoViewIfNeeded|scrollLeft|scrollWidth|setAttribute|setAttributeNS|setAttributeNode|setAttributeNodeNS|setPointerCapture|shadowRoot|styleMap|tabIndex|tagName|textContent|toString|valueOf|(webkit|ms|moz|o)dropzone|(webkit|moz|ms|o)MatchesSelector|(webkit|moz|ms|o)RequestFullScreen|(webkit|moz|ms|o)RequestFullscreen)(\\s|$)',
|
10684 |
),
|
10685 |
'inlist' => array(),
|
10686 |
'itemid' => array(),
|
10693 |
'on' => array(),
|
10694 |
'overflow' => array(),
|
10695 |
'placeholder' => array(
|
10696 |
+
'value' => '',
|
10697 |
),
|
10698 |
'prefix' => array(),
|
10699 |
'property' => array(),
|
10700 |
'rel' => array(
|
10701 |
+
'blacklisted_value_regex' => '(^|\\s)(canonical|components|dns-prefetch|import|manifest|preconnect|preload|prerender|serviceworker|stylesheet|subresource)(\\s|$)',
|
10702 |
),
|
10703 |
'resource' => array(),
|
10704 |
'rev' => array(),
|
10705 |
'role' => array(),
|
10706 |
+
'subscriptions-action' => array(),
|
10707 |
+
'subscriptions-actions' => array(
|
10708 |
+
'value' => '',
|
10709 |
+
),
|
10710 |
+
'subscriptions-dialog' => array(
|
10711 |
+
'value' => '',
|
10712 |
+
),
|
10713 |
+
'subscriptions-display' => array(),
|
10714 |
+
'subscriptions-section' => array(
|
10715 |
+
'value_regex_casei' => '(actions|content|content-not-granted)',
|
10716 |
+
),
|
10717 |
+
'subscriptions-service' => array(),
|
10718 |
'tabindex' => array(),
|
10719 |
'title' => array(),
|
10720 |
'translate' => array(),
|
10721 |
'typeof' => array(),
|
10722 |
'validation-for' => array(),
|
10723 |
'visible-when-invalid' => array(
|
10724 |
+
'value_regex' => '(badInput|customError|patternMismatch|rangeOverflow|rangeUnderflow|stepMismatch|tooLong|typeMismatch|valueMissing)',
|
10725 |
),
|
10726 |
'vocab' => array(),
|
|
|
10727 |
);
|
10728 |
|
10729 |
+
|
10730 |
+
/**
|
10731 |
+
* Get allowed tags.
|
10732 |
+
*
|
10733 |
+
* @since 0.5
|
10734 |
+
* @return array Allowed tags.
|
10735 |
+
*/
|
10736 |
public static function get_allowed_tags() {
|
10737 |
return self::$allowed_tags;
|
10738 |
}
|
10739 |
|
10740 |
+
/**
|
10741 |
+
* Get allowed tag.
|
10742 |
+
*
|
10743 |
+
* Get the rules for a single tag so that the entire data structure needn't be passed around.
|
10744 |
+
*
|
10745 |
+
* @since 0.7
|
10746 |
+
* @param string $node_name Tag name.
|
10747 |
+
* @return array|null Allowed tag, or null if the tag does not exist.
|
10748 |
+
*/
|
10749 |
+
public static function get_allowed_tag( $node_name ) {
|
10750 |
+
if ( isset( self::$allowed_tags[ $node_name ] ) ) {
|
10751 |
+
return self::$allowed_tags[ $node_name ];
|
10752 |
+
}
|
10753 |
+
return null;
|
10754 |
+
}
|
10755 |
+
|
10756 |
+
/**
|
10757 |
+
* Get list of globally-allowed attributes.
|
10758 |
+
*
|
10759 |
+
* @since 0.5
|
10760 |
+
* @return array Allowed tag.
|
10761 |
+
*/
|
10762 |
public static function get_allowed_attributes() {
|
10763 |
return self::$globally_allowed_attrs;
|
10764 |
}
|
10765 |
|
10766 |
+
/**
|
10767 |
+
* Get layout attributes.
|
10768 |
+
*
|
10769 |
+
* @since 0.5
|
10770 |
+
* @return array Allowed tag.
|
10771 |
+
*/
|
10772 |
public static function get_layout_attributes() {
|
10773 |
return self::$layout_allowed_attrs;
|
10774 |
}
|
10775 |
|
10776 |
}
|
|
|
|
@@ -20,42 +20,6 @@ class AMP_Audio_Sanitizer extends AMP_Base_Sanitizer {
|
|
20 |
*/
|
21 |
public static $tag = 'audio';
|
22 |
|
23 |
-
/**
|
24 |
-
* Script slug.
|
25 |
-
*
|
26 |
-
* @var string AMP HTML audio tag to use in place of HTML's 'audio' tag.
|
27 |
-
*
|
28 |
-
* @since 0.2
|
29 |
-
*/
|
30 |
-
private static $script_slug = 'amp-audio';
|
31 |
-
|
32 |
-
/**
|
33 |
-
* Script src.
|
34 |
-
*
|
35 |
-
* @var string URL to AMP Project's Audio element javascript file found at cdn.ampproject.org
|
36 |
-
*
|
37 |
-
* @since 0.2
|
38 |
-
*/
|
39 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-audio-0.1.js';
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Return one element array containing AMP HTML audio tag and respective Javascript URL
|
43 |
-
*
|
44 |
-
* HTML tags and Javascript URLs found at cdn.ampproject.org
|
45 |
-
*
|
46 |
-
* @since 0.2
|
47 |
-
*
|
48 |
-
* @return string[] Returns AMP HTML audio tag as array key and Javascript URL as array value,
|
49 |
-
* respectively. Will return an empty array if sanitization has yet to be run
|
50 |
-
* or if it did not find any HTML audio elements to convert to AMP equivalents.
|
51 |
-
*/
|
52 |
-
public function get_scripts() {
|
53 |
-
if ( ! $this->did_convert_elements ) {
|
54 |
-
return array();
|
55 |
-
}
|
56 |
-
return array( self::$script_slug => self::$script_src );
|
57 |
-
}
|
58 |
-
|
59 |
/**
|
60 |
* Sanitize the <audio> elements from the HTML contained in this instance's DOMDocument.
|
61 |
*
|
@@ -117,7 +81,7 @@ class AMP_Audio_Sanitizer extends AMP_Base_Sanitizer {
|
|
117 |
* @see: https://github.com/ampproject/amphtml/issues/2261
|
118 |
*/
|
119 |
if ( 0 === $new_node->childNodes->length && empty( $new_attributes['src'] ) ) {
|
120 |
-
$
|
121 |
} else {
|
122 |
$node->parentNode->replaceChild( $new_node, $node );
|
123 |
}
|
20 |
*/
|
21 |
public static $tag = 'audio';
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
/**
|
24 |
* Sanitize the <audio> elements from the HTML contained in this instance's DOMDocument.
|
25 |
*
|
81 |
* @see: https://github.com/ampproject/amphtml/issues/2261
|
82 |
*/
|
83 |
if ( 0 === $new_node->childNodes->length && empty( $new_attributes['src'] ) ) {
|
84 |
+
$this->remove_invalid_child( $node );
|
85 |
} else {
|
86 |
$node->parentNode->replaceChild( $new_node, $node );
|
87 |
}
|
@@ -43,17 +43,26 @@ abstract class AMP_Base_Sanitizer {
|
|
43 |
* @var array {
|
44 |
* @type int $content_max_width
|
45 |
* @type bool $add_placeholder
|
|
|
46 |
* @type bool $require_https_src
|
47 |
* @type string[] $amp_allowed_tags
|
48 |
* @type string[] $amp_globally_allowed_attributes
|
49 |
* @type string[] $amp_layout_allowed_attributes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
* }
|
51 |
*/
|
52 |
protected $args;
|
53 |
|
54 |
/**
|
55 |
* Flag to be set in child class' sanitize() method indicating if the
|
56 |
-
* HTML contained in the DOMDocument has been
|
57 |
*
|
58 |
* @since 0.2
|
59 |
*
|
@@ -61,13 +70,20 @@ abstract class AMP_Base_Sanitizer {
|
|
61 |
*/
|
62 |
protected $did_convert_elements = false;
|
63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
/**
|
65 |
* AMP_Base_Sanitizer constructor.
|
66 |
*
|
67 |
* @since 0.2
|
68 |
*
|
69 |
* @param DOMDocument $dom Represents the HTML document to sanitize.
|
70 |
-
* @param array $args
|
71 |
* Args.
|
72 |
*
|
73 |
* @type int $content_max_width
|
@@ -81,6 +97,12 @@ abstract class AMP_Base_Sanitizer {
|
|
81 |
public function __construct( $dom, $args = array() ) {
|
82 |
$this->dom = $dom;
|
83 |
$this->args = array_merge( $this->DEFAULT_ARGS, $args );
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
}
|
85 |
|
86 |
/**
|
@@ -96,7 +118,9 @@ abstract class AMP_Base_Sanitizer {
|
|
96 |
*
|
97 |
* @since 0.2
|
98 |
*
|
99 |
-
* @return string[]
|
|
|
|
|
100 |
*/
|
101 |
public function get_scripts() {
|
102 |
return array();
|
@@ -107,19 +131,39 @@ abstract class AMP_Base_Sanitizer {
|
|
107 |
*
|
108 |
* @since 0.4
|
109 |
*
|
110 |
-
* @return
|
111 |
*/
|
112 |
public function get_styles() {
|
113 |
return array();
|
114 |
}
|
115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
/**
|
117 |
* Get HTML body as DOMElement from DOMDocument received by the constructor.
|
118 |
*
|
119 |
-
* @
|
|
|
120 |
*/
|
121 |
protected function get_body_node() {
|
122 |
-
|
|
|
123 |
}
|
124 |
|
125 |
/**
|
@@ -131,12 +175,15 @@ abstract class AMP_Base_Sanitizer {
|
|
131 |
* @return float|int|string Returns a numeric dimension value, or an empty string.
|
132 |
*/
|
133 |
public function sanitize_dimension( $value, $dimension ) {
|
134 |
-
|
|
|
|
|
135 |
return '';
|
136 |
}
|
137 |
|
138 |
-
|
139 |
-
|
|
|
140 |
}
|
141 |
|
142 |
if ( AMP_String_Utils::endswith( $value, 'px' ) ) {
|
@@ -154,7 +201,7 @@ abstract class AMP_Base_Sanitizer {
|
|
154 |
}
|
155 |
|
156 |
/**
|
157 |
-
*
|
158 |
*
|
159 |
* @param string[] $attributes {
|
160 |
* Attributes.
|
@@ -167,12 +214,11 @@ abstract class AMP_Base_Sanitizer {
|
|
167 |
* }
|
168 |
* @return string[]
|
169 |
*/
|
170 |
-
public function
|
171 |
if ( empty( $attributes['height'] ) ) {
|
172 |
unset( $attributes['width'] );
|
173 |
$attributes['height'] = self::FALLBACK_HEIGHT;
|
174 |
}
|
175 |
-
|
176 |
if ( empty( $attributes['width'] ) ) {
|
177 |
$attributes['layout'] = 'fixed-height';
|
178 |
}
|
@@ -180,42 +226,6 @@ abstract class AMP_Base_Sanitizer {
|
|
180 |
return $attributes;
|
181 |
}
|
182 |
|
183 |
-
/**
|
184 |
-
* This is our workaround to enforce max sizing with layout=responsive.
|
185 |
-
*
|
186 |
-
* We want elements to not grow beyond their width and shrink to fill the screen on viewports smaller than their width.
|
187 |
-
*
|
188 |
-
* See https://github.com/ampproject/amphtml/issues/1280#issuecomment-171533526
|
189 |
-
* See https://github.com/Automattic/amp-wp/issues/101
|
190 |
-
*
|
191 |
-
* @param string[] $attributes {
|
192 |
-
* Attributes.
|
193 |
-
*
|
194 |
-
* @type int $height
|
195 |
-
* @type int $width
|
196 |
-
* @type string $sizes
|
197 |
-
* @type string $class
|
198 |
-
* @type string $layout
|
199 |
-
* }
|
200 |
-
* @return string[]
|
201 |
-
*/
|
202 |
-
public function enforce_sizes_attribute( $attributes ) {
|
203 |
-
if ( ! isset( $attributes['width'], $attributes['height'] ) ) {
|
204 |
-
return $attributes;
|
205 |
-
}
|
206 |
-
|
207 |
-
$max_width = $attributes['width'];
|
208 |
-
if ( isset( $this->args['content_max_width'] ) && $max_width >= $this->args['content_max_width'] ) {
|
209 |
-
$max_width = $this->args['content_max_width'];
|
210 |
-
}
|
211 |
-
|
212 |
-
$attributes['sizes'] = sprintf( '(min-width: %1$dpx) %1$dpx, 100vw', absint( $max_width ) );
|
213 |
-
|
214 |
-
$this->add_or_append_attribute( $attributes, 'class', 'amp-wp-enforced-sizes' );
|
215 |
-
|
216 |
-
return $attributes;
|
217 |
-
}
|
218 |
-
|
219 |
/**
|
220 |
* Adds or appends key and value to list of attributes
|
221 |
*
|
@@ -268,4 +278,68 @@ abstract class AMP_Base_Sanitizer {
|
|
268 |
|
269 |
return $src;
|
270 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
}
|
43 |
* @var array {
|
44 |
* @type int $content_max_width
|
45 |
* @type bool $add_placeholder
|
46 |
+
* @type bool $use_document_element
|
47 |
* @type bool $require_https_src
|
48 |
* @type string[] $amp_allowed_tags
|
49 |
* @type string[] $amp_globally_allowed_attributes
|
50 |
* @type string[] $amp_layout_allowed_attributes
|
51 |
+
* @type array $amp_allowed_tags
|
52 |
+
* @type array $amp_globally_allowed_attributes
|
53 |
+
* @type array $amp_layout_allowed_attributes
|
54 |
+
* @type array $amp_bind_placeholder_prefix
|
55 |
+
* @type bool $allow_dirty_styles
|
56 |
+
* @type bool $allow_dirty_scripts
|
57 |
+
* @type bool $disable_invalid_removal
|
58 |
+
* @type callable $remove_invalid_callback
|
59 |
* }
|
60 |
*/
|
61 |
protected $args;
|
62 |
|
63 |
/**
|
64 |
* Flag to be set in child class' sanitize() method indicating if the
|
65 |
+
* HTML contained in the DOMDocument has been sanitized yet or not.
|
66 |
*
|
67 |
* @since 0.2
|
68 |
*
|
70 |
*/
|
71 |
protected $did_convert_elements = false;
|
72 |
|
73 |
+
/**
|
74 |
+
* The root element used for sanitization. Either html or body.
|
75 |
+
*
|
76 |
+
* @var DOMElement
|
77 |
+
*/
|
78 |
+
protected $root_element;
|
79 |
+
|
80 |
/**
|
81 |
* AMP_Base_Sanitizer constructor.
|
82 |
*
|
83 |
* @since 0.2
|
84 |
*
|
85 |
* @param DOMDocument $dom Represents the HTML document to sanitize.
|
86 |
+
* @param array $args {
|
87 |
* Args.
|
88 |
*
|
89 |
* @type int $content_max_width
|
97 |
public function __construct( $dom, $args = array() ) {
|
98 |
$this->dom = $dom;
|
99 |
$this->args = array_merge( $this->DEFAULT_ARGS, $args );
|
100 |
+
|
101 |
+
if ( ! empty( $this->args['use_document_element'] ) ) {
|
102 |
+
$this->root_element = $this->dom->documentElement;
|
103 |
+
} else {
|
104 |
+
$this->root_element = $this->dom->getElementsByTagName( 'body' )->item( 0 );
|
105 |
+
}
|
106 |
}
|
107 |
|
108 |
/**
|
118 |
*
|
119 |
* @since 0.2
|
120 |
*
|
121 |
+
* @return string[] Returns component name as array key and JavaScript URL as array value,
|
122 |
+
* respectively. Will return an empty array if sanitization has yet to be run
|
123 |
+
* or if it did not find any HTML elements to convert to AMP equivalents.
|
124 |
*/
|
125 |
public function get_scripts() {
|
126 |
return array();
|
131 |
*
|
132 |
* @since 0.4
|
133 |
*
|
134 |
+
* @return array[][] Mapping of CSS selectors to arrays of properties.
|
135 |
*/
|
136 |
public function get_styles() {
|
137 |
return array();
|
138 |
}
|
139 |
|
140 |
+
/**
|
141 |
+
* Get stylesheets.
|
142 |
+
*
|
143 |
+
* @since 0.7
|
144 |
+
* @returns array Values are the CSS stylesheets. Keys are MD5 hashes of the stylesheets.
|
145 |
+
*/
|
146 |
+
public function get_stylesheets() {
|
147 |
+
$stylesheets = array();
|
148 |
+
|
149 |
+
foreach ( $this->get_styles() as $selector => $properties ) {
|
150 |
+
$stylesheet = sprintf( '%s { %s }', $selector, join( '; ', $properties ) . ';' );
|
151 |
+
|
152 |
+
$stylesheets[ md5( $stylesheet ) ] = $stylesheet;
|
153 |
+
}
|
154 |
+
|
155 |
+
return $stylesheets;
|
156 |
+
}
|
157 |
+
|
158 |
/**
|
159 |
* Get HTML body as DOMElement from DOMDocument received by the constructor.
|
160 |
*
|
161 |
+
* @deprecated Just reference $root_element instead.
|
162 |
+
* @return DOMElement The body or html element.
|
163 |
*/
|
164 |
protected function get_body_node() {
|
165 |
+
_deprecated_function( __METHOD__, 'AMP_Base_Sanitizer::$root_element', '0.7' );
|
166 |
+
return $this->root_element;
|
167 |
}
|
168 |
|
169 |
/**
|
175 |
* @return float|int|string Returns a numeric dimension value, or an empty string.
|
176 |
*/
|
177 |
public function sanitize_dimension( $value, $dimension ) {
|
178 |
+
|
179 |
+
// Allows 0 to be used as valid dimension.
|
180 |
+
if ( null === $value ) {
|
181 |
return '';
|
182 |
}
|
183 |
|
184 |
+
// Accepts both integers and floats & prevents negative values.
|
185 |
+
if ( is_numeric( $value ) ) {
|
186 |
+
return max( 0, floatval( $value ) );
|
187 |
}
|
188 |
|
189 |
if ( AMP_String_Utils::endswith( $value, 'px' ) ) {
|
201 |
}
|
202 |
|
203 |
/**
|
204 |
+
* Sets the layout, and possibly the 'height' and 'width' attributes.
|
205 |
*
|
206 |
* @param string[] $attributes {
|
207 |
* Attributes.
|
214 |
* }
|
215 |
* @return string[]
|
216 |
*/
|
217 |
+
public function set_layout( $attributes ) {
|
218 |
if ( empty( $attributes['height'] ) ) {
|
219 |
unset( $attributes['width'] );
|
220 |
$attributes['height'] = self::FALLBACK_HEIGHT;
|
221 |
}
|
|
|
222 |
if ( empty( $attributes['width'] ) ) {
|
223 |
$attributes['layout'] = 'fixed-height';
|
224 |
}
|
226 |
return $attributes;
|
227 |
}
|
228 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
229 |
/**
|
230 |
* Adds or appends key and value to list of attributes
|
231 |
*
|
278 |
|
279 |
return $src;
|
280 |
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Removes an invalid child of a node.
|
284 |
+
*
|
285 |
+
* Also, calls the mutation callback for it.
|
286 |
+
* This tracks all the nodes that were removed.
|
287 |
+
*
|
288 |
+
* @since 0.7
|
289 |
+
*
|
290 |
+
* @param DOMNode|DOMElement $node The node to remove.
|
291 |
+
* @param array $args Additional args to pass to validation error callback.
|
292 |
+
*
|
293 |
+
* @return void
|
294 |
+
*/
|
295 |
+
public function remove_invalid_child( $node, $args = array() ) {
|
296 |
+
if ( isset( $this->args['validation_error_callback'] ) ) {
|
297 |
+
call_user_func( $this->args['validation_error_callback'],
|
298 |
+
array_merge( compact( 'node' ), $args )
|
299 |
+
);
|
300 |
+
}
|
301 |
+
if ( empty( $this->args['disable_invalid_removal'] ) ) {
|
302 |
+
$node->parentNode->removeChild( $node );
|
303 |
+
}
|
304 |
+
}
|
305 |
+
|
306 |
+
/**
|
307 |
+
* Removes an invalid attribute of a node.
|
308 |
+
*
|
309 |
+
* Also, calls the mutation callback for it.
|
310 |
+
* This tracks all the attributes that were removed.
|
311 |
+
*
|
312 |
+
* @since 0.7
|
313 |
+
*
|
314 |
+
* @param DOMElement $element The node for which to remove the attribute.
|
315 |
+
* @param DOMAttr|string $attribute The attribute to remove from the element.
|
316 |
+
* @param array $args Additional args to pass to validation error callback.
|
317 |
+
* @return void
|
318 |
+
*/
|
319 |
+
public function remove_invalid_attribute( $element, $attribute, $args = array() ) {
|
320 |
+
if ( isset( $this->args['validation_error_callback'] ) ) {
|
321 |
+
if ( is_string( $attribute ) ) {
|
322 |
+
$attribute = $element->getAttributeNode( $attribute );
|
323 |
+
}
|
324 |
+
if ( $attribute ) {
|
325 |
+
call_user_func( $this->args['validation_error_callback'],
|
326 |
+
array_merge(
|
327 |
+
array(
|
328 |
+
'node' => $attribute,
|
329 |
+
),
|
330 |
+
$args
|
331 |
+
)
|
332 |
+
);
|
333 |
+
if ( empty( $this->args['disable_invalid_removal'] ) ) {
|
334 |
+
$element->removeAttributeNode( $attribute );
|
335 |
+
}
|
336 |
+
}
|
337 |
+
} elseif ( empty( $this->args['disable_invalid_removal'] ) ) {
|
338 |
+
if ( is_string( $attribute ) ) {
|
339 |
+
$element->removeAttribute( $attribute );
|
340 |
+
} else {
|
341 |
+
$element->removeAttributeNode( $attribute );
|
342 |
+
}
|
343 |
+
}
|
344 |
+
}
|
345 |
}
|
@@ -32,11 +32,13 @@ class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
|
|
32 |
* Sanitize.
|
33 |
*/
|
34 |
public function sanitize() {
|
|
|
|
|
35 |
$blacklisted_tags = $this->get_blacklisted_tags();
|
36 |
$blacklisted_attributes = $this->get_blacklisted_attributes();
|
37 |
$blacklisted_protocols = $this->get_blacklisted_protocols();
|
38 |
|
39 |
-
$body = $this->
|
40 |
$this->strip_tags( $body, $blacklisted_tags );
|
41 |
$this->strip_attributes_recursive( $body, $blacklisted_attributes, $blacklisted_protocols );
|
42 |
}
|
@@ -71,13 +73,13 @@ class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
|
|
71 |
$attribute = $node->attributes->item( $i );
|
72 |
$attribute_name = strtolower( $attribute->name );
|
73 |
if ( in_array( $attribute_name, $bad_attributes, true ) ) {
|
74 |
-
$
|
75 |
continue;
|
76 |
}
|
77 |
|
78 |
// The on* attributes (like onclick) are a special case.
|
79 |
if ( 0 === stripos( $attribute_name, 'on' ) && 'on' !== $attribute_name ) {
|
80 |
-
$
|
81 |
continue;
|
82 |
} elseif ( 'a' === $node_name ) {
|
83 |
$this->sanitize_a_attribute( $node, $attribute );
|
@@ -110,10 +112,10 @@ class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
|
|
110 |
for ( $i = $length - 1; $i >= 0; $i-- ) {
|
111 |
$element = $elements->item( $i );
|
112 |
$parent_node = $element->parentNode;
|
113 |
-
$
|
114 |
|
115 |
if ( 'body' !== $parent_node->nodeName && AMP_DOM_Utils::is_node_empty( $parent_node ) ) {
|
116 |
-
$
|
117 |
}
|
118 |
}
|
119 |
}
|
@@ -132,13 +134,13 @@ class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
|
|
132 |
$old_value = $attribute->value;
|
133 |
$new_value = trim( preg_replace( self::PATTERN_REL_WP_ATTACHMENT, '', $old_value ) );
|
134 |
if ( empty( $new_value ) ) {
|
135 |
-
$
|
136 |
} elseif ( $old_value !== $new_value ) {
|
137 |
$node->setAttribute( $attribute_name, $new_value );
|
138 |
}
|
139 |
} elseif ( 'rev' === $attribute_name ) {
|
140 |
// rev removed from HTML5 spec, which was used by Jetpack Markdown.
|
141 |
-
$
|
142 |
} elseif ( 'target' === $attribute_name ) {
|
143 |
// _blank is the only allowed value and it must be lowercase.
|
144 |
// replace _new with _blank and others should simply be removed.
|
@@ -148,7 +150,7 @@ class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
|
|
148 |
$node->setAttribute( $attribute_name, '_blank' );
|
149 |
} else {
|
150 |
// Only _blank is allowed.
|
151 |
-
$
|
152 |
}
|
153 |
}
|
154 |
}
|
@@ -217,7 +219,7 @@ class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
|
|
217 |
|
218 |
// Remove the node from the parent, if defined.
|
219 |
if ( $node->parentNode ) {
|
220 |
-
$
|
221 |
}
|
222 |
}
|
223 |
|
32 |
* Sanitize.
|
33 |
*/
|
34 |
public function sanitize() {
|
35 |
+
_deprecated_function( __METHOD__, '0.7', 'AMP_Tag_And_Attribute_Sanitizer::sanitize' );
|
36 |
+
|
37 |
$blacklisted_tags = $this->get_blacklisted_tags();
|
38 |
$blacklisted_attributes = $this->get_blacklisted_attributes();
|
39 |
$blacklisted_protocols = $this->get_blacklisted_protocols();
|
40 |
|
41 |
+
$body = $this->root_element;
|
42 |
$this->strip_tags( $body, $blacklisted_tags );
|
43 |
$this->strip_attributes_recursive( $body, $blacklisted_attributes, $blacklisted_protocols );
|
44 |
}
|
73 |
$attribute = $node->attributes->item( $i );
|
74 |
$attribute_name = strtolower( $attribute->name );
|
75 |
if ( in_array( $attribute_name, $bad_attributes, true ) ) {
|
76 |
+
$this->remove_invalid_attribute( $node, $attribute_name );
|
77 |
continue;
|
78 |
}
|
79 |
|
80 |
// The on* attributes (like onclick) are a special case.
|
81 |
if ( 0 === stripos( $attribute_name, 'on' ) && 'on' !== $attribute_name ) {
|
82 |
+
$this->remove_invalid_attribute( $node, $attribute_name );
|
83 |
continue;
|
84 |
} elseif ( 'a' === $node_name ) {
|
85 |
$this->sanitize_a_attribute( $node, $attribute );
|
112 |
for ( $i = $length - 1; $i >= 0; $i-- ) {
|
113 |
$element = $elements->item( $i );
|
114 |
$parent_node = $element->parentNode;
|
115 |
+
$this->remove_invalid_child( $element );
|
116 |
|
117 |
if ( 'body' !== $parent_node->nodeName && AMP_DOM_Utils::is_node_empty( $parent_node ) ) {
|
118 |
+
$this->remove_invalid_child( $parent_node );
|
119 |
}
|
120 |
}
|
121 |
}
|
134 |
$old_value = $attribute->value;
|
135 |
$new_value = trim( preg_replace( self::PATTERN_REL_WP_ATTACHMENT, '', $old_value ) );
|
136 |
if ( empty( $new_value ) ) {
|
137 |
+
$this->remove_invalid_attribute( $node, $attribute_name );
|
138 |
} elseif ( $old_value !== $new_value ) {
|
139 |
$node->setAttribute( $attribute_name, $new_value );
|
140 |
}
|
141 |
} elseif ( 'rev' === $attribute_name ) {
|
142 |
// rev removed from HTML5 spec, which was used by Jetpack Markdown.
|
143 |
+
$this->remove_invalid_attribute( $node, $attribute_name );
|
144 |
} elseif ( 'target' === $attribute_name ) {
|
145 |
// _blank is the only allowed value and it must be lowercase.
|
146 |
// replace _new with _blank and others should simply be removed.
|
150 |
$node->setAttribute( $attribute_name, '_blank' );
|
151 |
} else {
|
152 |
// Only _blank is allowed.
|
153 |
+
$this->remove_invalid_attribute( $node, $attribute_name );
|
154 |
}
|
155 |
}
|
156 |
}
|
219 |
|
220 |
// Remove the node from the parent, if defined.
|
221 |
if ( $node->parentNode ) {
|
222 |
+
$this->remove_invalid_child( $node );
|
223 |
}
|
224 |
}
|
225 |
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Comments_Sanitizer.
|
4 |
+
*
|
5 |
+
* @package AMP
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class AMP_Comments_Sanitizer
|
10 |
+
*
|
11 |
+
* Strips and corrects attributes in forms.
|
12 |
+
*/
|
13 |
+
class AMP_Comments_Sanitizer extends AMP_Base_Sanitizer {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Pre-process the comment form and comment list for AMP.
|
17 |
+
*
|
18 |
+
* @since 0.7
|
19 |
+
*/
|
20 |
+
public function sanitize() {
|
21 |
+
|
22 |
+
foreach ( $this->dom->getElementsByTagName( 'form' ) as $comment_form ) {
|
23 |
+
/**
|
24 |
+
* Comment form.
|
25 |
+
*
|
26 |
+
* @var DOMElement $comment_form
|
27 |
+
*/
|
28 |
+
$action = $comment_form->getAttribute( 'action-xhr' );
|
29 |
+
if ( ! $action ) {
|
30 |
+
$action = $comment_form->getAttribute( 'action' );
|
31 |
+
}
|
32 |
+
$action_path = wp_parse_url( $action, PHP_URL_PATH );
|
33 |
+
if ( preg_match( '#/wp-comments-post\.php$#', $action_path ) ) {
|
34 |
+
$this->process_comment_form( $comment_form );
|
35 |
+
}
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Comment form.
|
41 |
+
*
|
42 |
+
* @param DOMElement $comment_form Comment form.
|
43 |
+
*/
|
44 |
+
protected function process_comment_form( $comment_form ) {
|
45 |
+
/**
|
46 |
+
* Element.
|
47 |
+
*
|
48 |
+
* @var DOMElement $element
|
49 |
+
*/
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Named input elements.
|
53 |
+
*
|
54 |
+
* @var DOMElement[][] $form_fields
|
55 |
+
*/
|
56 |
+
$form_fields = array();
|
57 |
+
foreach ( $comment_form->getElementsByTagName( 'input' ) as $element ) {
|
58 |
+
$name = $element->getAttribute( 'name' );
|
59 |
+
if ( $name ) {
|
60 |
+
$form_fields[ $name ][] = $element;
|
61 |
+
}
|
62 |
+
}
|
63 |
+
foreach ( $comment_form->getElementsByTagName( 'textarea' ) as $element ) {
|
64 |
+
$name = $element->getAttribute( 'name' );
|
65 |
+
if ( $name ) {
|
66 |
+
$form_fields[ $name ][] = $element;
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
if ( empty( $form_fields['comment_post_ID'] ) ) {
|
71 |
+
return;
|
72 |
+
}
|
73 |
+
$post_id = (int) $form_fields['comment_post_ID'][0]->getAttribute( 'value' );
|
74 |
+
$state_id = AMP_Theme_Support::get_comment_form_state_id( $post_id );
|
75 |
+
|
76 |
+
$form_state = array(
|
77 |
+
'values' => array(),
|
78 |
+
'submitting' => false,
|
79 |
+
'replyToName' => '',
|
80 |
+
);
|
81 |
+
|
82 |
+
if ( ! empty( $form_fields['comment_parent'] ) ) {
|
83 |
+
$comment_id = (int) $form_fields['comment_parent'][0]->getAttribute( 'value' );
|
84 |
+
if ( $comment_id ) {
|
85 |
+
$reply_comment = get_comment( $comment_id );
|
86 |
+
if ( $reply_comment ) {
|
87 |
+
$form_state['replyToName'] = $reply_comment->comment_author;
|
88 |
+
}
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
$amp_bind_attr_format = AMP_DOM_Utils::get_amp_bind_placeholder_prefix() . '%s';
|
93 |
+
foreach ( $form_fields as $name => $form_field ) {
|
94 |
+
foreach ( $form_field as $element ) {
|
95 |
+
|
96 |
+
// @todo Radio and checkbox inputs are not supported yet.
|
97 |
+
if ( in_array( strtolower( $element->getAttribute( 'type' ) ), array( 'checkbox', 'radio' ), true ) ) {
|
98 |
+
continue;
|
99 |
+
}
|
100 |
+
|
101 |
+
$element->setAttribute( sprintf( $amp_bind_attr_format, 'disabled' ), "$state_id.submitting" );
|
102 |
+
|
103 |
+
if ( 'textarea' === strtolower( $element->nodeName ) ) {
|
104 |
+
$form_state['values'][ $name ] = $element->textContent;
|
105 |
+
$element->setAttribute( sprintf( $amp_bind_attr_format, 'text' ), "$state_id.values.$name" );
|
106 |
+
} else {
|
107 |
+
$form_state['values'][ $name ] = $element->hasAttribute( 'value' ) ? $element->getAttribute( 'value' ) : '';
|
108 |
+
$element->setAttribute( sprintf( $amp_bind_attr_format, 'value' ), "$state_id.values.$name" );
|
109 |
+
}
|
110 |
+
|
111 |
+
// Update the state in response to changing the input.
|
112 |
+
$element->setAttribute( 'on', sprintf(
|
113 |
+
'change:AMP.setState( { %s: { values: { %s: event.value } } } )',
|
114 |
+
$state_id,
|
115 |
+
wp_json_encode( $name )
|
116 |
+
) );
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
// Add amp-state to the document.
|
121 |
+
$amp_state = $this->dom->createElement( 'amp-state' );
|
122 |
+
$amp_state->setAttribute( 'id', $state_id );
|
123 |
+
$script = $this->dom->createElement( 'script' );
|
124 |
+
$script->setAttribute( 'type', 'application/json' );
|
125 |
+
$amp_state->appendChild( $script );
|
126 |
+
$script->appendChild( $this->dom->createTextNode( wp_json_encode( $form_state ) ) );
|
127 |
+
$comment_form->insertBefore( $amp_state, $comment_form->firstChild );
|
128 |
+
|
129 |
+
// Update state when submitting form.
|
130 |
+
$form_reset_state = $form_state;
|
131 |
+
unset(
|
132 |
+
$form_reset_state['values']['author'],
|
133 |
+
$form_reset_state['values']['email'],
|
134 |
+
$form_reset_state['values']['url']
|
135 |
+
);
|
136 |
+
$on = array(
|
137 |
+
// Disable the form when submitting.
|
138 |
+
sprintf(
|
139 |
+
'submit:AMP.setState( { %s: { submitting: true } } )',
|
140 |
+
wp_json_encode( $state_id )
|
141 |
+
),
|
142 |
+
// Re-enable the form fields when the submission fails.
|
143 |
+
sprintf(
|
144 |
+
'submit-error:AMP.setState( { %s: { submitting: false } } )',
|
145 |
+
wp_json_encode( $state_id )
|
146 |
+
),
|
147 |
+
// Reset the form to its initial state (with enabled form fields), except for the author, email, and url.
|
148 |
+
sprintf(
|
149 |
+
'submit-success:AMP.setState( { %s: %s } )',
|
150 |
+
$state_id,
|
151 |
+
wp_json_encode( $form_reset_state )
|
152 |
+
),
|
153 |
+
);
|
154 |
+
$comment_form->setAttribute( 'on', implode( ';', $on ) );
|
155 |
+
}
|
156 |
+
}
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Form_Sanitizer.
|
4 |
+
*
|
5 |
+
* @package AMP
|
6 |
+
* @since 0.7
|
7 |
+
*/
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class AMP_Form_Sanitizer
|
11 |
+
*
|
12 |
+
* Strips and corrects attributes in forms.
|
13 |
+
*
|
14 |
+
* @since 0.7
|
15 |
+
*/
|
16 |
+
class AMP_Form_Sanitizer extends AMP_Base_Sanitizer {
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Tag.
|
20 |
+
*
|
21 |
+
* @var string HTML <form> tag to identify and process.
|
22 |
+
*
|
23 |
+
* @since 0.7
|
24 |
+
*/
|
25 |
+
public static $tag = 'form';
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Sanitize the <form> elements from the HTML contained in this instance's DOMDocument.
|
29 |
+
*
|
30 |
+
* @link https://www.ampproject.org/docs/reference/components/amp-form
|
31 |
+
* @since 0.7
|
32 |
+
*/
|
33 |
+
public function sanitize() {
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Node list.
|
37 |
+
*
|
38 |
+
* @var DOMNodeList $node
|
39 |
+
*/
|
40 |
+
$nodes = $this->dom->getElementsByTagName( self::$tag );
|
41 |
+
$num_nodes = $nodes->length;
|
42 |
+
|
43 |
+
if ( 0 === $num_nodes ) {
|
44 |
+
return;
|
45 |
+
}
|
46 |
+
|
47 |
+
for ( $i = $num_nodes - 1; $i >= 0; $i-- ) {
|
48 |
+
$node = $nodes->item( $i );
|
49 |
+
if ( ! $node instanceof DOMElement ) {
|
50 |
+
continue;
|
51 |
+
}
|
52 |
+
|
53 |
+
// In HTML, the default method is 'get'.
|
54 |
+
$method = 'get';
|
55 |
+
if ( $node->getAttribute( 'method' ) ) {
|
56 |
+
$method = strtolower( $node->getAttribute( 'method' ) );
|
57 |
+
} else {
|
58 |
+
$node->setAttribute( 'method', $method );
|
59 |
+
}
|
60 |
+
|
61 |
+
/*
|
62 |
+
* In HTML, the default action is just the current URL that the page is served from.
|
63 |
+
* The action "specifies a server endpoint to handle the form input. The value must be an
|
64 |
+
* https URL and must not be a link to a CDN".
|
65 |
+
*/
|
66 |
+
if ( ! $node->getAttribute( 'action' ) ) {
|
67 |
+
$action_url = esc_url_raw( '//' . $_SERVER['HTTP_HOST'] . wp_unslash( $_SERVER['REQUEST_URI'] ) ); // WPCS: ignore. input var okay, sanitization ok.
|
68 |
+
} else {
|
69 |
+
$action_url = $node->getAttribute( 'action' );
|
70 |
+
}
|
71 |
+
$xhr_action = $node->getAttribute( 'action-xhr' );
|
72 |
+
|
73 |
+
// Make HTTP URLs protocol-less, since HTTPS is required for forms.
|
74 |
+
if ( 'http://' === strtolower( substr( $action_url, 0, 7 ) ) ) {
|
75 |
+
$action_url = substr( $action_url, 5 );
|
76 |
+
}
|
77 |
+
|
78 |
+
/*
|
79 |
+
* "For GET submissions, provide at least one of action or action-xhr".
|
80 |
+
* "This attribute is required for method=GET. For method=POST, the
|
81 |
+
* action attribute is invalid, use action-xhr instead".
|
82 |
+
*/
|
83 |
+
if ( 'get' === $method ) {
|
84 |
+
if ( $action_url !== $node->getAttribute( 'action' ) ) {
|
85 |
+
$node->setAttribute( 'action', $action_url );
|
86 |
+
}
|
87 |
+
} elseif ( 'post' === $method ) {
|
88 |
+
$node->removeAttribute( 'action' );
|
89 |
+
if ( ! $xhr_action ) {
|
90 |
+
// record that action was converted tp action-xhr.
|
91 |
+
$action_url = add_query_arg( '_wp_amp_action_xhr_converted', 1, $action_url );
|
92 |
+
$node->setAttribute( 'action-xhr', $action_url );
|
93 |
+
// Append error handler if not found.
|
94 |
+
$this->ensure_submit_error_element( $node );
|
95 |
+
} elseif ( 'http://' === substr( $xhr_action, 0, 7 ) ) {
|
96 |
+
$node->setAttribute( 'action-xhr', substr( $xhr_action, 5 ) );
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
+
/*
|
101 |
+
* The target "indicates where to display the form response after submitting the form.
|
102 |
+
* The value must be _blank or _top". The _self and _parent values are treated
|
103 |
+
* as synonymous with _top, and anything else is treated like _blank.
|
104 |
+
*/
|
105 |
+
$target = $node->getAttribute( 'target' );
|
106 |
+
if ( '_top' !== $target ) {
|
107 |
+
if ( ! $target || in_array( $target, array( '_self', '_parent' ), true ) ) {
|
108 |
+
$node->setAttribute( 'target', '_top' );
|
109 |
+
} elseif ( '_blank' !== $target ) {
|
110 |
+
$node->setAttribute( 'target', '_blank' );
|
111 |
+
}
|
112 |
+
}
|
113 |
+
}
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Checks if the form has an error handler else create one if not.
|
118 |
+
*
|
119 |
+
* @link https://www.ampproject.org/docs/reference/components/amp-form#success/error-response-rendering
|
120 |
+
* @since 0.7
|
121 |
+
*
|
122 |
+
* @param DOMElement $form The form node to check.
|
123 |
+
*/
|
124 |
+
public function ensure_submit_error_element( $form ) {
|
125 |
+
$templates = $form->getElementsByTagName( 'template' );
|
126 |
+
for ( $i = $templates->length - 1; $i >= 0; $i-- ) {
|
127 |
+
if ( $templates->item( $i )->parentNode->hasAttribute( 'submit-error' ) ) {
|
128 |
+
return; // Found error template, do nothing.
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
$div = $this->dom->createElement( 'div' );
|
133 |
+
$template = $this->dom->createElement( 'template' );
|
134 |
+
$mustache = $this->dom->createTextNode( '{{{error}}}' );
|
135 |
+
$div->setAttribute( 'submit-error', '' );
|
136 |
+
$template->setAttribute( 'type', 'amp-mustache' );
|
137 |
+
$template->appendChild( $mustache );
|
138 |
+
$div->appendChild( $template );
|
139 |
+
$form->appendChild( $div );
|
140 |
+
}
|
141 |
+
}
|
@@ -39,24 +39,6 @@ class AMP_Iframe_Sanitizer extends AMP_Base_Sanitizer {
|
|
39 |
*/
|
40 |
public static $tag = 'iframe';
|
41 |
|
42 |
-
/**
|
43 |
-
* Script slug.
|
44 |
-
*
|
45 |
-
* @var string AMP HTML tag to use in place of HTML's <iframe> tag.
|
46 |
-
*
|
47 |
-
* @since 0.2
|
48 |
-
*/
|
49 |
-
private static $script_slug = 'amp-iframe';
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Script src.
|
53 |
-
*
|
54 |
-
* @var string URL to AMP Project's IFrame element's JavaScript file found at cdn.ampproject.org
|
55 |
-
*
|
56 |
-
* @since 0.2
|
57 |
-
*/
|
58 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-iframe-0.1.js';
|
59 |
-
|
60 |
/**
|
61 |
* Default args.
|
62 |
*
|
@@ -66,24 +48,6 @@ class AMP_Iframe_Sanitizer extends AMP_Base_Sanitizer {
|
|
66 |
'add_placeholder' => false,
|
67 |
);
|
68 |
|
69 |
-
/**
|
70 |
-
* Return one element array containing AMP HTML iframe tag and respective Javascript URL
|
71 |
-
*
|
72 |
-
* HTML tags and Javascript URLs found at cdn.ampproject.org
|
73 |
-
*
|
74 |
-
* @since 0.2
|
75 |
-
*
|
76 |
-
* @return string[] Returns AMP HTML iframe tag as array key and Javascript URL as array value,
|
77 |
-
* respectively. Will return an empty array if sanitization has yet to be run
|
78 |
-
* or if it did not find any HTML iframe elements to convert to AMP equivalents.
|
79 |
-
*/
|
80 |
-
public function get_scripts() {
|
81 |
-
if ( ! $this->did_convert_elements ) {
|
82 |
-
return array();
|
83 |
-
}
|
84 |
-
return array( self::$script_slug => self::$script_src );
|
85 |
-
}
|
86 |
-
|
87 |
/**
|
88 |
* Sanitize the <iframe> elements from the HTML contained in this instance's DOMDocument.
|
89 |
*
|
@@ -110,14 +74,16 @@ class AMP_Iframe_Sanitizer extends AMP_Base_Sanitizer {
|
|
110 |
* @see: https://github.com/ampproject/amphtml/issues/2261
|
111 |
*/
|
112 |
if ( empty( $new_attributes['src'] ) ) {
|
113 |
-
$
|
114 |
continue;
|
115 |
}
|
116 |
|
117 |
$this->did_convert_elements = true;
|
118 |
-
|
119 |
-
$new_attributes
|
120 |
-
|
|
|
|
|
121 |
|
122 |
$new_node = AMP_DOM_Utils::create_node( $this->dom, 'amp-iframe', $new_attributes );
|
123 |
|
@@ -228,4 +194,5 @@ class AMP_Iframe_Sanitizer extends AMP_Base_Sanitizer {
|
|
228 |
|
229 |
return $placeholder_node;
|
230 |
}
|
|
|
231 |
}
|
39 |
*/
|
40 |
public static $tag = 'iframe';
|
41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
/**
|
43 |
* Default args.
|
44 |
*
|
48 |
'add_placeholder' => false,
|
49 |
);
|
50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
/**
|
52 |
* Sanitize the <iframe> elements from the HTML contained in this instance's DOMDocument.
|
53 |
*
|
74 |
* @see: https://github.com/ampproject/amphtml/issues/2261
|
75 |
*/
|
76 |
if ( empty( $new_attributes['src'] ) ) {
|
77 |
+
$this->remove_invalid_child( $node );
|
78 |
continue;
|
79 |
}
|
80 |
|
81 |
$this->did_convert_elements = true;
|
82 |
+
$new_attributes = $this->set_layout( $new_attributes );
|
83 |
+
if ( empty( $new_attributes['layout'] ) && ! empty( $new_attributes['width'] ) && ! empty( $new_attributes['height'] ) ) {
|
84 |
+
$new_attributes['layout'] = 'intrinsic';
|
85 |
+
$this->add_or_append_attribute( $new_attributes, 'class', 'amp-wp-enforced-sizes' );
|
86 |
+
}
|
87 |
|
88 |
$new_node = AMP_DOM_Utils::create_node( $this->dom, 'amp-iframe', $new_attributes );
|
89 |
|
194 |
|
195 |
return $placeholder_node;
|
196 |
}
|
197 |
+
|
198 |
}
|
@@ -46,43 +46,6 @@ class AMP_Img_Sanitizer extends AMP_Base_Sanitizer {
|
|
46 |
*/
|
47 |
private static $anim_extension = '.gif';
|
48 |
|
49 |
-
/**
|
50 |
-
* Script slug.
|
51 |
-
*
|
52 |
-
* @var string AMP HTML tag to use in place of HTML's <img> tag.
|
53 |
-
*
|
54 |
-
* @since 0.2
|
55 |
-
*/
|
56 |
-
private static $script_slug = 'amp-anim';
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Script src.
|
60 |
-
*
|
61 |
-
* @var string URL to AMP Project's Image element's JavaScript file found at cdn.ampproject.org
|
62 |
-
*
|
63 |
-
* @since 0.2
|
64 |
-
*/
|
65 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-anim-0.1.js';
|
66 |
-
|
67 |
-
/**
|
68 |
-
* Return one element array containing AMP HTML image tag and respective Javascript URL
|
69 |
-
*
|
70 |
-
* HTML tags and Javascript URLs found at cdn.ampproject.org
|
71 |
-
*
|
72 |
-
* @since 0.2
|
73 |
-
*
|
74 |
-
* @return string[] Returns AMP HTML image tag as array key and Javascript URL
|
75 |
-
* as array value, respectively. Will return an empty array
|
76 |
-
* if sanitization has yet to be run or if it did not find any
|
77 |
-
* HTML image elements to convert to AMP equivalents.
|
78 |
-
*/
|
79 |
-
public function get_scripts() {
|
80 |
-
if ( ! $this->did_convert_elements ) {
|
81 |
-
return array();
|
82 |
-
}
|
83 |
-
return array( self::$script_slug => self::$script_src );
|
84 |
-
}
|
85 |
-
|
86 |
/**
|
87 |
* Sanitize the <img> elements from the HTML contained in this instance's DOMDocument.
|
88 |
*
|
@@ -110,13 +73,13 @@ class AMP_Img_Sanitizer extends AMP_Base_Sanitizer {
|
|
110 |
continue;
|
111 |
}
|
112 |
|
113 |
-
if ( ! $node->hasAttribute( 'src' ) || '' === $node->getAttribute( 'src' ) ) {
|
114 |
-
$
|
115 |
continue;
|
116 |
}
|
117 |
|
118 |
// Determine which images need their dimensions determined/extracted.
|
119 |
-
if (
|
120 |
$need_dimensions[ $node->getAttribute( 'src' ) ][] = $node;
|
121 |
} else {
|
122 |
$this->adjust_and_replace_node( $node );
|
@@ -156,7 +119,6 @@ class AMP_Img_Sanitizer extends AMP_Base_Sanitizer {
|
|
156 |
case 'alt':
|
157 |
case 'class':
|
158 |
case 'srcset':
|
159 |
-
case 'sizes':
|
160 |
case 'on':
|
161 |
case 'attribution':
|
162 |
$out[ $name ] = $value;
|
@@ -167,6 +129,10 @@ class AMP_Img_Sanitizer extends AMP_Base_Sanitizer {
|
|
167 |
$out[ $name ] = $this->sanitize_dimension( $value, $name );
|
168 |
break;
|
169 |
|
|
|
|
|
|
|
|
|
170 |
default:
|
171 |
break;
|
172 |
}
|
@@ -191,18 +157,30 @@ class AMP_Img_Sanitizer extends AMP_Base_Sanitizer {
|
|
191 |
if ( ! $node instanceof DOMElement ) {
|
192 |
continue;
|
193 |
}
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
$node->setAttribute( 'height', $dimensions['height'] );
|
199 |
-
} else {
|
200 |
-
$width = isset( $this->args['content_max_width'] ) ? $this->args['content_max_width'] : self::FALLBACK_WIDTH;
|
201 |
$height = self::FALLBACK_HEIGHT;
|
|
|
202 |
$node->setAttribute( 'width', $width );
|
203 |
$node->setAttribute( 'height', $height );
|
204 |
$class = $node->hasAttribute( 'class' ) ? $node->getAttribute( 'class' ) . ' amp-wp-unknown-size' : 'amp-wp-unknown-size';
|
205 |
$node->setAttribute( 'class', $class );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
206 |
}
|
207 |
}
|
208 |
}
|
@@ -229,7 +207,10 @@ class AMP_Img_Sanitizer extends AMP_Base_Sanitizer {
|
|
229 |
private function adjust_and_replace_node( $node ) {
|
230 |
$old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
|
231 |
$new_attributes = $this->filter_attributes( $old_attributes );
|
232 |
-
$
|
|
|
|
|
|
|
233 |
if ( $this->is_gif_url( $new_attributes['src'] ) ) {
|
234 |
$this->did_convert_elements = true;
|
235 |
|
@@ -238,6 +219,7 @@ class AMP_Img_Sanitizer extends AMP_Base_Sanitizer {
|
|
238 |
$new_tag = 'amp-img';
|
239 |
}
|
240 |
$new_node = AMP_DOM_Utils::create_node( $this->dom, $new_tag, $new_attributes );
|
|
|
241 |
$node->parentNode->replaceChild( $new_node, $node );
|
242 |
}
|
243 |
|
@@ -255,4 +237,43 @@ class AMP_Img_Sanitizer extends AMP_Base_Sanitizer {
|
|
255 |
$path = AMP_WP_Utils::parse_url( $url, PHP_URL_PATH );
|
256 |
return substr( $path, -strlen( $ext ) ) === $ext;
|
257 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
}
|
46 |
*/
|
47 |
private static $anim_extension = '.gif';
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
/**
|
50 |
* Sanitize the <img> elements from the HTML contained in this instance's DOMDocument.
|
51 |
*
|
73 |
continue;
|
74 |
}
|
75 |
|
76 |
+
if ( ! $node->hasAttribute( 'src' ) || '' === trim( $node->getAttribute( 'src' ) ) ) {
|
77 |
+
$this->remove_invalid_child( $node );
|
78 |
continue;
|
79 |
}
|
80 |
|
81 |
// Determine which images need their dimensions determined/extracted.
|
82 |
+
if ( ! is_numeric( $node->getAttribute( 'width' ) ) || ! is_numeric( $node->getAttribute( 'height' ) ) ) {
|
83 |
$need_dimensions[ $node->getAttribute( 'src' ) ][] = $node;
|
84 |
} else {
|
85 |
$this->adjust_and_replace_node( $node );
|
119 |
case 'alt':
|
120 |
case 'class':
|
121 |
case 'srcset':
|
|
|
122 |
case 'on':
|
123 |
case 'attribution':
|
124 |
$out[ $name ] = $value;
|
129 |
$out[ $name ] = $this->sanitize_dimension( $value, $name );
|
130 |
break;
|
131 |
|
132 |
+
case 'data-amp-layout':
|
133 |
+
$out['layout'] = $value;
|
134 |
+
break;
|
135 |
+
|
136 |
default:
|
137 |
break;
|
138 |
}
|
157 |
if ( ! $node instanceof DOMElement ) {
|
158 |
continue;
|
159 |
}
|
160 |
+
if (
|
161 |
+
! is_numeric( $node->getAttribute( 'width' ) ) &&
|
162 |
+
! is_numeric( $node->getAttribute( 'height' ) )
|
163 |
+
) {
|
|
|
|
|
|
|
164 |
$height = self::FALLBACK_HEIGHT;
|
165 |
+
$width = self::FALLBACK_WIDTH;
|
166 |
$node->setAttribute( 'width', $width );
|
167 |
$node->setAttribute( 'height', $height );
|
168 |
$class = $node->hasAttribute( 'class' ) ? $node->getAttribute( 'class' ) . ' amp-wp-unknown-size' : 'amp-wp-unknown-size';
|
169 |
$node->setAttribute( 'class', $class );
|
170 |
+
} elseif (
|
171 |
+
! is_numeric( $node->getAttribute( 'height' ) )
|
172 |
+
) {
|
173 |
+
$height = self::FALLBACK_HEIGHT;
|
174 |
+
$node->setAttribute( 'height', $height );
|
175 |
+
$class = $node->hasAttribute( 'class' ) ? $node->getAttribute( 'class' ) . ' amp-wp-unknown-size amp-wp-unknown-height' : 'amp-wp-unknown-size amp-wp-unknown-height';
|
176 |
+
$node->setAttribute( 'class', $class );
|
177 |
+
} elseif (
|
178 |
+
! is_numeric( $node->getAttribute( 'width' ) )
|
179 |
+
) {
|
180 |
+
$width = self::FALLBACK_WIDTH;
|
181 |
+
$node->setAttribute( 'width', $width );
|
182 |
+
$class = $node->hasAttribute( 'class' ) ? $node->getAttribute( 'class' ) . ' amp-wp-unknown-size amp-wp-unknown-width' : 'amp-wp-unknown-size amp-wp-unknown-width';
|
183 |
+
$node->setAttribute( 'class', $class );
|
184 |
}
|
185 |
}
|
186 |
}
|
207 |
private function adjust_and_replace_node( $node ) {
|
208 |
$old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
|
209 |
$new_attributes = $this->filter_attributes( $old_attributes );
|
210 |
+
$this->add_or_append_attribute( $new_attributes, 'class', 'amp-wp-enforced-sizes' );
|
211 |
+
if ( empty( $new_attributes['layout'] ) && ! empty( $new_attributes['height'] ) && ! empty( $new_attributes['width'] ) ) {
|
212 |
+
$new_attributes['layout'] = 'intrinsic';
|
213 |
+
}
|
214 |
if ( $this->is_gif_url( $new_attributes['src'] ) ) {
|
215 |
$this->did_convert_elements = true;
|
216 |
|
219 |
$new_tag = 'amp-img';
|
220 |
}
|
221 |
$new_node = AMP_DOM_Utils::create_node( $this->dom, $new_tag, $new_attributes );
|
222 |
+
$new_node = $this->handle_centering( $new_node );
|
223 |
$node->parentNode->replaceChild( $new_node, $node );
|
224 |
}
|
225 |
|
237 |
$path = AMP_WP_Utils::parse_url( $url, PHP_URL_PATH );
|
238 |
return substr( $path, -strlen( $ext ) ) === $ext;
|
239 |
}
|
240 |
+
|
241 |
+
/**
|
242 |
+
* Handles an issue with the aligncenter class.
|
243 |
+
*
|
244 |
+
* If the <amp-img> has the class aligncenter, this strips the class and wraps it in a <figure> to center the image.
|
245 |
+
* There was an issue where the aligncenter class overrode the "display: inline-block" rule of AMP's layout="intrinsic" attribute.
|
246 |
+
* So this strips that class, and instead wraps the image in a <figure> to center it.
|
247 |
+
*
|
248 |
+
* @since 0.7
|
249 |
+
* @see https://github.com/Automattic/amp-wp/issues/1104
|
250 |
+
*
|
251 |
+
* @param DOMElement $node The <amp-img> node.
|
252 |
+
* @return DOMElement $node The <amp-img> node, possibly wrapped in a <figure>.
|
253 |
+
*/
|
254 |
+
public function handle_centering( $node ) {
|
255 |
+
$align_class = 'aligncenter';
|
256 |
+
$classes = $node->getAttribute( 'class' );
|
257 |
+
$width = $node->getAttribute( 'width' );
|
258 |
+
|
259 |
+
// If this doesn't have a width attribute, centering it in the <figure> wrapper won't work.
|
260 |
+
if ( empty( $width ) || ! in_array( $align_class, preg_split( '/\s+/', trim( $classes ) ), true ) ) {
|
261 |
+
return $node;
|
262 |
+
}
|
263 |
+
|
264 |
+
// Strip the class, and wrap the <amp-img> in a <figure>.
|
265 |
+
$classes = trim( str_replace( $align_class, '', $classes ) );
|
266 |
+
$node->setAttribute( 'class', $classes );
|
267 |
+
$figure = AMP_DOM_Utils::create_node(
|
268 |
+
$this->dom,
|
269 |
+
'figure',
|
270 |
+
array(
|
271 |
+
'class' => $align_class,
|
272 |
+
'style' => "max-width: {$width}px;",
|
273 |
+
)
|
274 |
+
);
|
275 |
+
$figure->appendChild( $node );
|
276 |
+
|
277 |
+
return $figure;
|
278 |
+
}
|
279 |
}
|
@@ -31,24 +31,6 @@ class AMP_Playbuzz_Sanitizer extends AMP_Base_Sanitizer {
|
|
31 |
*/
|
32 |
public static $pb_class = 'pb_feed';
|
33 |
|
34 |
-
/**
|
35 |
-
* Script slug.
|
36 |
-
*
|
37 |
-
* @var string AMP HTML audio tag to use in place of HTML's 'audio' tag.
|
38 |
-
*
|
39 |
-
* @since 0.2
|
40 |
-
*/
|
41 |
-
private static $script_slug = 'amp-playbuzz';
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Script src.
|
45 |
-
*
|
46 |
-
* @var string URL to AMP Project's Playbuzz element javascript file found at cdn.ampproject.org
|
47 |
-
*
|
48 |
-
* @since 0.2
|
49 |
-
*/
|
50 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-playbuzz-0.1.js';
|
51 |
-
|
52 |
/**
|
53 |
* Hardcoded height to set for Playbuzz elements.
|
54 |
*
|
@@ -58,25 +40,6 @@ class AMP_Playbuzz_Sanitizer extends AMP_Base_Sanitizer {
|
|
58 |
*/
|
59 |
private static $height = '500';
|
60 |
|
61 |
-
/**
|
62 |
-
* Return one element array containing AMP HTML audio tag and respective Javascript URL
|
63 |
-
*
|
64 |
-
* HTML tags and Javascript URLs found at cdn.ampproject.org
|
65 |
-
*
|
66 |
-
* @since 0.2
|
67 |
-
*
|
68 |
-
* @return string[] Returns AMP Playbuzz tag as array key and Javascript URL as array value,
|
69 |
-
* respectively. Will return an empty array if sanitization has yet to be run
|
70 |
-
* or if it did not find any HTML Playbuzz elements to convert to AMP equivalents.
|
71 |
-
*/
|
72 |
-
public function get_scripts() {
|
73 |
-
if ( ! $this->did_convert_elements ) {
|
74 |
-
return array();
|
75 |
-
}
|
76 |
-
return array( self::$script_slug => self::$script_src );
|
77 |
-
}
|
78 |
-
|
79 |
-
|
80 |
/**
|
81 |
* Sanitize the Playbuzz elements from the HTML contained in this instance's DOMDocument.
|
82 |
*
|
@@ -106,7 +69,7 @@ class AMP_Playbuzz_Sanitizer extends AMP_Base_Sanitizer {
|
|
106 |
continue;
|
107 |
}
|
108 |
|
109 |
-
$new_node = AMP_DOM_Utils::create_node( $this->dom,
|
110 |
|
111 |
$node->parentNode->replaceChild( $new_node, $node );
|
112 |
|
31 |
*/
|
32 |
public static $pb_class = 'pb_feed';
|
33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
/**
|
35 |
* Hardcoded height to set for Playbuzz elements.
|
36 |
*
|
40 |
*/
|
41 |
private static $height = '500';
|
42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
/**
|
44 |
* Sanitize the Playbuzz elements from the HTML contained in this instance's DOMDocument.
|
45 |
*
|
69 |
continue;
|
70 |
}
|
71 |
|
72 |
+
$new_node = AMP_DOM_Utils::create_node( $this->dom, 'amp-playbuzz', $new_attributes );
|
73 |
|
74 |
$node->parentNode->replaceChild( $new_node, $node );
|
75 |
|
@@ -17,13 +17,16 @@ abstract class AMP_Rule_Spec {
|
|
17 |
*/
|
18 |
const ATTR_SPEC_LIST = 'attr_spec_list';
|
19 |
const TAG_SPEC = 'tag_spec';
|
|
|
20 |
|
21 |
/**
|
22 |
-
* AMP attr_spec value check results
|
|
|
|
|
23 |
*/
|
24 |
-
const PASS =
|
25 |
-
const FAIL =
|
26 |
-
const NOT_APPLICABLE =
|
27 |
|
28 |
/**
|
29 |
* HTML Element Tag rule names
|
@@ -46,6 +49,8 @@ abstract class AMP_Rule_Spec {
|
|
46 |
const VALUE_CASEI = 'value_casei';
|
47 |
const VALUE_REGEX = 'value_regex';
|
48 |
const VALUE_REGEX_CASEI = 'value_regex_casei';
|
|
|
|
|
49 |
|
50 |
/**
|
51 |
* If a node type listed here is invalid, it and it's subtree will be
|
@@ -77,7 +82,60 @@ abstract class AMP_Rule_Spec {
|
|
77 |
*/
|
78 |
public static $whitelisted_attr_regex = array(
|
79 |
'@^data-[a-zA-Z][\\w:.-]*$@uis',
|
80 |
-
'(update|item|pagination)', // Allowed for live reference points.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
);
|
82 |
|
83 |
/**
|
@@ -92,16 +150,5 @@ abstract class AMP_Rule_Spec {
|
|
92 |
'attr_spec_list' => array(),
|
93 |
'tag_spec' => array(),
|
94 |
),
|
95 |
-
|
96 |
-
// Needed for some tags such as analytics.
|
97 |
-
'script' => array(
|
98 |
-
'attr_spec_list' => array(
|
99 |
-
'type' => array(
|
100 |
-
'mandatory' => true,
|
101 |
-
'value_casei' => 'application/json',
|
102 |
-
),
|
103 |
-
),
|
104 |
-
'tag_spec' => array(),
|
105 |
-
),
|
106 |
);
|
107 |
}
|
17 |
*/
|
18 |
const ATTR_SPEC_LIST = 'attr_spec_list';
|
19 |
const TAG_SPEC = 'tag_spec';
|
20 |
+
const CDATA = 'cdata';
|
21 |
|
22 |
/**
|
23 |
+
* AMP attr_spec value check results.
|
24 |
+
*
|
25 |
+
* In 0.7 these changed from strings to integers to speed up comparisons.
|
26 |
*/
|
27 |
+
const PASS = 1;
|
28 |
+
const FAIL = 0;
|
29 |
+
const NOT_APPLICABLE = -1;
|
30 |
|
31 |
/**
|
32 |
* HTML Element Tag rule names
|
49 |
const VALUE_CASEI = 'value_casei';
|
50 |
const VALUE_REGEX = 'value_regex';
|
51 |
const VALUE_REGEX_CASEI = 'value_regex_casei';
|
52 |
+
const VALUE_PROPERTIES = 'value_properties';
|
53 |
+
const VALUE_URL = 'value_url';
|
54 |
|
55 |
/**
|
56 |
* If a node type listed here is invalid, it and it's subtree will be
|
82 |
*/
|
83 |
public static $whitelisted_attr_regex = array(
|
84 |
'@^data-[a-zA-Z][\\w:.-]*$@uis',
|
85 |
+
'(update|item|pagination|option|selected|disabled)', // Allowed for live reference points.
|
86 |
+
);
|
87 |
+
|
88 |
+
/**
|
89 |
+
* List of boolean attributes.
|
90 |
+
*
|
91 |
+
* @since 0.7
|
92 |
+
* @var array
|
93 |
+
*/
|
94 |
+
public static $boolean_attributes = array(
|
95 |
+
'allowfullscreen',
|
96 |
+
'async',
|
97 |
+
'autofocus',
|
98 |
+
'autoplay',
|
99 |
+
'checked',
|
100 |
+
'compact',
|
101 |
+
'controls',
|
102 |
+
'declare',
|
103 |
+
'default',
|
104 |
+
'defaultchecked',
|
105 |
+
'defaultmuted',
|
106 |
+
'defaultselected',
|
107 |
+
'defer',
|
108 |
+
'disabled',
|
109 |
+
'draggable',
|
110 |
+
'enabled',
|
111 |
+
'formnovalidate',
|
112 |
+
'hidden',
|
113 |
+
'indeterminate',
|
114 |
+
'inert',
|
115 |
+
'ismap',
|
116 |
+
'itemscope',
|
117 |
+
'loop',
|
118 |
+
'multiple',
|
119 |
+
'muted',
|
120 |
+
'nohref',
|
121 |
+
'noresize',
|
122 |
+
'noshade',
|
123 |
+
'novalidate',
|
124 |
+
'nowrap',
|
125 |
+
'open',
|
126 |
+
'pauseonexit',
|
127 |
+
'readonly',
|
128 |
+
'required',
|
129 |
+
'reversed',
|
130 |
+
'scoped',
|
131 |
+
'seamless',
|
132 |
+
'selected',
|
133 |
+
'sortable',
|
134 |
+
'spellcheck',
|
135 |
+
'translate',
|
136 |
+
'truespeed',
|
137 |
+
'typemustmatch',
|
138 |
+
'visible',
|
139 |
);
|
140 |
|
141 |
/**
|
150 |
'attr_spec_list' => array(),
|
151 |
'tag_spec' => array(),
|
152 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
);
|
154 |
}
|
@@ -15,18 +15,127 @@ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer {
|
|
15 |
/**
|
16 |
* Styles.
|
17 |
*
|
18 |
-
*
|
19 |
*
|
20 |
* @since 0.4
|
|
|
21 |
*/
|
22 |
private $styles = array();
|
23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
/**
|
25 |
* Get list of CSS styles in HTML content of DOMDocument ($this->dom).
|
26 |
*
|
27 |
* @since 0.4
|
28 |
*
|
29 |
-
* @return
|
30 |
*/
|
31 |
public function get_styles() {
|
32 |
if ( ! $this->did_convert_elements ) {
|
@@ -35,57 +144,338 @@ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer {
|
|
35 |
return $this->styles;
|
36 |
}
|
37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
/**
|
39 |
* Sanitize CSS styles within the HTML contained in this instance's DOMDocument.
|
40 |
*
|
41 |
* @since 0.4
|
42 |
*/
|
43 |
public function sanitize() {
|
44 |
-
$
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
$this->did_convert_elements = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
}
|
48 |
|
49 |
/**
|
50 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
*
|
52 |
* Collects the CSS styles from within the HTML contained in this instance's DOMDocument.
|
53 |
*
|
54 |
* @see Retrieve array of styles using $this->get_styles() after calling this method.
|
55 |
*
|
56 |
* @since 0.4
|
|
|
57 |
*
|
58 |
* @note Uses recursion to traverse down the tree of DOMDocument nodes.
|
59 |
*
|
60 |
-
* @param
|
61 |
*/
|
62 |
-
private function
|
63 |
-
|
|
|
64 |
return;
|
65 |
}
|
|
|
66 |
|
67 |
-
|
68 |
-
$style = $node->getAttribute( 'style' );
|
69 |
-
$class = $node->getAttribute( 'class' );
|
70 |
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
$class_name = $this->generate_class_name( $style );
|
75 |
-
$new_class = trim( $class . ' ' . $class_name );
|
76 |
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
|
|
|
|
|
|
|
|
81 |
}
|
82 |
-
}
|
83 |
|
84 |
-
|
85 |
-
|
86 |
-
$child_node = $node->childNodes->item( $i );
|
87 |
-
$this->collect_styles_recursive( $child_node );
|
88 |
}
|
|
|
89 |
}
|
90 |
|
91 |
/**
|
@@ -93,26 +483,20 @@ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer {
|
|
93 |
*
|
94 |
* @since 0.4
|
95 |
*
|
96 |
-
* @param string $
|
97 |
-
* @return array
|
98 |
*/
|
99 |
-
private function process_style( $
|
100 |
|
101 |
-
|
102 |
-
|
103 |
-
*/
|
104 |
-
$string = safecss_filter_attr( esc_html( $string ) );
|
105 |
-
|
106 |
-
if ( ! $string ) {
|
107 |
-
return array();
|
108 |
-
}
|
109 |
|
110 |
/*
|
111 |
-
* safecss returns a string but we want individual rules.
|
112 |
* Use preg_split to break up rules by `;` but only if the
|
113 |
* semi-colon is not inside parens (like a data-encoded image).
|
114 |
*/
|
115 |
-
$styles =
|
|
|
116 |
|
117 |
// Normalize the order of the styles.
|
118 |
sort( $styles );
|
@@ -121,12 +505,12 @@ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer {
|
|
121 |
|
122 |
// Normalize whitespace and filter rules.
|
123 |
foreach ( $styles as $index => $rule ) {
|
124 |
-
$
|
125 |
-
if ( 2 !== count( $
|
126 |
continue;
|
127 |
}
|
128 |
|
129 |
-
list( $property, $value ) = $this->filter_style( $
|
130 |
if ( empty( $property ) || empty( $value ) ) {
|
131 |
continue;
|
132 |
}
|
@@ -151,13 +535,13 @@ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer {
|
|
151 |
* @return array
|
152 |
*/
|
153 |
private function filter_style( $property, $value ) {
|
154 |
-
|
155 |
-
/**
|
156 |
* Remove overflow if value is `auto` or `scroll`; not allowed in AMP
|
157 |
*
|
|
|
158 |
* @see https://www.ampproject.org/docs/reference/spec.html#properties
|
159 |
*/
|
160 |
-
if ( preg_match( '#^overflow
|
161 |
return array( false, false );
|
162 |
}
|
163 |
|
@@ -165,8 +549,10 @@ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer {
|
|
165 |
$property = 'max-width';
|
166 |
}
|
167 |
|
168 |
-
|
169 |
* Remove `!important`; not allowed in AMP
|
|
|
|
|
170 |
*/
|
171 |
if ( false !== strpos( $value, 'important' ) ) {
|
172 |
$value = preg_replace( '/\s*\!\s*important$/', '', $value );
|
15 |
/**
|
16 |
* Styles.
|
17 |
*
|
18 |
+
* List of CSS styles in HTML content of DOMDocument ($this->dom).
|
19 |
*
|
20 |
* @since 0.4
|
21 |
+
* @var array[]
|
22 |
*/
|
23 |
private $styles = array();
|
24 |
|
25 |
+
/**
|
26 |
+
* Stylesheets.
|
27 |
+
*
|
28 |
+
* Values are the CSS stylesheets. Keys are MD5 hashes of the stylesheets
|
29 |
+
*
|
30 |
+
* @since 0.7
|
31 |
+
* @var string[]
|
32 |
+
*/
|
33 |
+
private $stylesheets = array();
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Maximum number of bytes allowed for a keyframes style.
|
37 |
+
*
|
38 |
+
* @since 0.7
|
39 |
+
* @var int
|
40 |
+
*/
|
41 |
+
private $keyframes_max_size;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Maximum number of bytes allowed for a AMP Custom style.
|
45 |
+
*
|
46 |
+
* @since 0.7
|
47 |
+
* @var int
|
48 |
+
*/
|
49 |
+
private $custom_max_size;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Current CSS size.
|
53 |
+
*
|
54 |
+
* Sum of CSS located in $styles and $stylesheets.
|
55 |
+
*
|
56 |
+
* @var int
|
57 |
+
*/
|
58 |
+
private $current_custom_size = 0;
|
59 |
+
|
60 |
+
/**
|
61 |
+
* The style[amp-custom] element.
|
62 |
+
*
|
63 |
+
* @var DOMElement
|
64 |
+
*/
|
65 |
+
private $amp_custom_style_element;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Regex for allowed font stylesheet URL.
|
69 |
+
*
|
70 |
+
* @var string
|
71 |
+
*/
|
72 |
+
private $allowed_font_src_regex;
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Base URL for styles.
|
76 |
+
*
|
77 |
+
* Full URL with trailing slash.
|
78 |
+
*
|
79 |
+
* @var string
|
80 |
+
*/
|
81 |
+
private $base_url;
|
82 |
+
|
83 |
+
/**
|
84 |
+
* URL of the content directory.
|
85 |
+
*
|
86 |
+
* @var string
|
87 |
+
*/
|
88 |
+
private $content_url;
|
89 |
+
|
90 |
+
/**
|
91 |
+
* AMP_Base_Sanitizer constructor.
|
92 |
+
*
|
93 |
+
* @since 0.7
|
94 |
+
*
|
95 |
+
* @param DOMDocument $dom Represents the HTML document to sanitize.
|
96 |
+
* @param array $args Args.
|
97 |
+
*/
|
98 |
+
public function __construct( DOMDocument $dom, array $args = array() ) {
|
99 |
+
parent::__construct( $dom, $args );
|
100 |
+
|
101 |
+
$spec_name = 'style[amp-keyframes]';
|
102 |
+
foreach ( AMP_Allowed_Tags_Generated::get_allowed_tag( 'style' ) as $spec_rule ) {
|
103 |
+
if ( isset( $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) && $spec_name === $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) {
|
104 |
+
$this->keyframes_max_size = $spec_rule[ AMP_Rule_Spec::CDATA ]['max_bytes'];
|
105 |
+
break;
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
$spec_name = 'style amp-custom';
|
110 |
+
foreach ( AMP_Allowed_Tags_Generated::get_allowed_tag( 'style' ) as $spec_rule ) {
|
111 |
+
if ( isset( $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) && $spec_name === $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) {
|
112 |
+
$this->custom_max_size = $spec_rule[ AMP_Rule_Spec::CDATA ]['max_bytes'];
|
113 |
+
break;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
$spec_name = 'link rel=stylesheet for fonts'; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
|
118 |
+
foreach ( AMP_Allowed_Tags_Generated::get_allowed_tag( 'link' ) as $spec_rule ) {
|
119 |
+
if ( isset( $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) && $spec_name === $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) {
|
120 |
+
$this->allowed_font_src_regex = '@^(' . $spec_rule[ AMP_Rule_Spec::ATTR_SPEC_LIST ]['href']['value_regex'] . ')$@';
|
121 |
+
break;
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
$guessurl = site_url();
|
126 |
+
if ( ! $guessurl ) {
|
127 |
+
$guessurl = wp_guess_url();
|
128 |
+
}
|
129 |
+
$this->base_url = $guessurl;
|
130 |
+
$this->content_url = WP_CONTENT_URL;
|
131 |
+
}
|
132 |
+
|
133 |
/**
|
134 |
* Get list of CSS styles in HTML content of DOMDocument ($this->dom).
|
135 |
*
|
136 |
* @since 0.4
|
137 |
*
|
138 |
+
* @return array[] Mapping CSS selectors to array of properties, or mapping of keys starting with 'stylesheet:' with value being the stylesheet.
|
139 |
*/
|
140 |
public function get_styles() {
|
141 |
if ( ! $this->did_convert_elements ) {
|
144 |
return $this->styles;
|
145 |
}
|
146 |
|
147 |
+
/**
|
148 |
+
* Get stylesheets.
|
149 |
+
*
|
150 |
+
* @since 0.7
|
151 |
+
* @returns array Values are the CSS stylesheets. Keys are MD5 hashes of the stylesheets.
|
152 |
+
*/
|
153 |
+
public function get_stylesheets() {
|
154 |
+
return array_merge( $this->stylesheets, parent::get_stylesheets() );
|
155 |
+
}
|
156 |
+
|
157 |
/**
|
158 |
* Sanitize CSS styles within the HTML contained in this instance's DOMDocument.
|
159 |
*
|
160 |
* @since 0.4
|
161 |
*/
|
162 |
public function sanitize() {
|
163 |
+
$elements = array();
|
164 |
+
|
165 |
+
// Do nothing if inline styles are allowed.
|
166 |
+
if ( ! empty( $this->args['allow_dirty_styles'] ) ) {
|
167 |
+
return;
|
168 |
+
}
|
169 |
+
|
170 |
+
/*
|
171 |
+
* Note that xpath is used to query the DOM so that the link and style elements will be
|
172 |
+
* in document order. DOMNode::compareDocumentPosition() is not yet implemented.
|
173 |
+
*/
|
174 |
+
$xpath = new DOMXPath( $this->dom );
|
175 |
+
|
176 |
+
$lower_case = 'translate( %s, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz" )'; // In XPath 2.0 this is lower-case().
|
177 |
+
$predicates = array(
|
178 |
+
sprintf( '( self::style and not( @amp-boilerplate ) and ( not( @type ) or %s = "text/css" ) )', sprintf( $lower_case, '@type' ) ),
|
179 |
+
sprintf( '( self::link and @href and %s = "stylesheet" )', sprintf( $lower_case, '@rel' ) ),
|
180 |
+
);
|
181 |
+
|
182 |
+
foreach ( $xpath->query( '//*[ ' . implode( ' or ', $predicates ) . ' ]' ) as $element ) {
|
183 |
+
$elements[] = $element;
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Element.
|
188 |
+
*
|
189 |
+
* @var DOMElement $element
|
190 |
+
*/
|
191 |
+
foreach ( $elements as $element ) {
|
192 |
+
$node_name = strtolower( $element->nodeName );
|
193 |
+
if ( 'style' === $node_name ) {
|
194 |
+
$this->process_style_element( $element );
|
195 |
+
} elseif ( 'link' === $node_name ) {
|
196 |
+
$this->process_link_element( $element );
|
197 |
+
}
|
198 |
+
}
|
199 |
+
|
200 |
+
$elements = array();
|
201 |
+
foreach ( $xpath->query( '//*[ @style ]' ) as $element ) {
|
202 |
+
$elements[] = $element;
|
203 |
+
}
|
204 |
+
foreach ( $elements as $element ) {
|
205 |
+
$this->collect_inline_styles( $element );
|
206 |
+
}
|
207 |
$this->did_convert_elements = true;
|
208 |
+
|
209 |
+
// Now make sure the amp-custom style is in the DOM and populated, if we're working with the document element.
|
210 |
+
if ( ! empty( $this->args['use_document_element'] ) ) {
|
211 |
+
if ( ! $this->amp_custom_style_element ) {
|
212 |
+
$this->amp_custom_style_element = $this->dom->createElement( 'style' );
|
213 |
+
$this->amp_custom_style_element->setAttribute( 'amp-custom', '' );
|
214 |
+
$head = $this->dom->getElementsByTagName( 'head' )->item( 0 );
|
215 |
+
if ( ! $head ) {
|
216 |
+
$head = $this->dom->createElement( 'head' );
|
217 |
+
$this->dom->documentElement->insertBefore( $head, $this->dom->documentElement->firstChild );
|
218 |
+
}
|
219 |
+
$head->appendChild( $this->amp_custom_style_element );
|
220 |
+
}
|
221 |
+
|
222 |
+
$css = implode( '', $this->get_stylesheets() );
|
223 |
+
|
224 |
+
/*
|
225 |
+
* Let the style[amp-custom] be populated with the concatenated CSS.
|
226 |
+
* !important: Updating the contents of this style element by setting textContent is not
|
227 |
+
* reliable across PHP/libxml versions, so this is why the children are removed and the
|
228 |
+
* text node is then explicitly added containing the CSS.
|
229 |
+
*/
|
230 |
+
while ( $this->amp_custom_style_element->firstChild ) {
|
231 |
+
$this->amp_custom_style_element->removeChild( $this->amp_custom_style_element->firstChild );
|
232 |
+
}
|
233 |
+
$this->amp_custom_style_element->appendChild( $this->dom->createTextNode( $css ) );
|
234 |
+
}
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Generates an enqueued style's fully-qualified file path.
|
239 |
+
*
|
240 |
+
* @since 0.7
|
241 |
+
* @see WP_Styles::_css_href()
|
242 |
+
*
|
243 |
+
* @param string $src The source URL of the enqueued style.
|
244 |
+
* @return string|WP_Error Style's absolute validated filesystem path, or WP_Error when error.
|
245 |
+
*/
|
246 |
+
public function get_validated_css_file_path( $src ) {
|
247 |
+
$needs_base_url = (
|
248 |
+
! is_bool( $src )
|
249 |
+
&&
|
250 |
+
! preg_match( '|^(https?:)?//|', $src )
|
251 |
+
&&
|
252 |
+
! ( $this->content_url && 0 === strpos( $src, $this->content_url ) )
|
253 |
+
);
|
254 |
+
if ( $needs_base_url ) {
|
255 |
+
$src = $this->base_url . $src;
|
256 |
+
}
|
257 |
+
|
258 |
+
// Strip query and fragment from URL.
|
259 |
+
$src = preg_replace( ':[\?#].*$:', '', $src );
|
260 |
+
|
261 |
+
if ( ! preg_match( '/\.(css|less|scss|sass)$/i', $src ) ) {
|
262 |
+
/* translators: %s is stylesheet URL */
|
263 |
+
return new WP_Error( 'amp_css_bad_file_extension', sprintf( __( 'Skipped stylesheet which does not have recognized CSS file extension (%s).', 'amp' ), $src ) );
|
264 |
+
}
|
265 |
+
|
266 |
+
$includes_url = includes_url( '/' );
|
267 |
+
$content_url = content_url( '/' );
|
268 |
+
$admin_url = get_admin_url( null, '/' );
|
269 |
+
$css_path = null;
|
270 |
+
if ( 0 === strpos( $src, $content_url ) ) {
|
271 |
+
$css_path = WP_CONTENT_DIR . substr( $src, strlen( $content_url ) - 1 );
|
272 |
+
} elseif ( 0 === strpos( $src, $includes_url ) ) {
|
273 |
+
$css_path = ABSPATH . WPINC . substr( $src, strlen( $includes_url ) - 1 );
|
274 |
+
} elseif ( 0 === strpos( $src, $admin_url ) ) {
|
275 |
+
$css_path = ABSPATH . 'wp-admin' . substr( $src, strlen( $admin_url ) - 1 );
|
276 |
+
}
|
277 |
+
|
278 |
+
if ( ! $css_path || false !== strpos( '../', $css_path ) || 0 !== validate_file( $css_path ) || ! file_exists( $css_path ) ) {
|
279 |
+
/* translators: %s is stylesheet URL */
|
280 |
+
return new WP_Error( 'amp_css_path_not_found', sprintf( __( 'Unable to locate filesystem path for stylesheet %s.', 'amp' ), $src ) );
|
281 |
+
}
|
282 |
+
|
283 |
+
return $css_path;
|
284 |
}
|
285 |
|
286 |
/**
|
287 |
+
* Process style element.
|
288 |
+
*
|
289 |
+
* @param DOMElement $element Style element.
|
290 |
+
*/
|
291 |
+
private function process_style_element( DOMElement $element ) {
|
292 |
+
if ( $element->hasAttribute( 'amp-keyframes' ) ) {
|
293 |
+
$validity = $this->validate_amp_keyframe( $element );
|
294 |
+
if ( is_wp_error( $validity ) ) {
|
295 |
+
$this->remove_invalid_child( $element, array(
|
296 |
+
'message' => $validity->get_error_message(),
|
297 |
+
) );
|
298 |
+
}
|
299 |
+
return;
|
300 |
+
}
|
301 |
+
|
302 |
+
$rules = trim( $element->textContent );
|
303 |
+
$rules = $this->remove_illegal_css( $rules, $element );
|
304 |
+
|
305 |
+
// Remove if surpasses max size.
|
306 |
+
$length = strlen( $rules );
|
307 |
+
if ( $this->current_custom_size + $length > $this->custom_max_size ) {
|
308 |
+
$this->remove_invalid_child( $element, array(
|
309 |
+
'message' => __( 'Too much CSS enqueued.', 'amp' ),
|
310 |
+
) );
|
311 |
+
return;
|
312 |
+
}
|
313 |
+
|
314 |
+
$this->stylesheets[ md5( $rules ) ] = $rules;
|
315 |
+
$this->current_custom_size += $length;
|
316 |
+
|
317 |
+
if ( $element->hasAttribute( 'amp-custom' ) ) {
|
318 |
+
if ( ! $this->amp_custom_style_element ) {
|
319 |
+
$this->amp_custom_style_element = $element;
|
320 |
+
} else {
|
321 |
+
$element->parentNode->removeChild( $element ); // There can only be one. #highlander.
|
322 |
+
}
|
323 |
+
} else {
|
324 |
+
|
325 |
+
// Remove from DOM since we'll be adding it to amp-custom.
|
326 |
+
$element->parentNode->removeChild( $element );
|
327 |
+
}
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Process link element.
|
332 |
+
*
|
333 |
+
* @param DOMElement $element Link element.
|
334 |
+
*/
|
335 |
+
private function process_link_element( DOMElement $element ) {
|
336 |
+
$href = $element->getAttribute( 'href' );
|
337 |
+
|
338 |
+
// Allow font URLs.
|
339 |
+
if ( $this->allowed_font_src_regex && preg_match( $this->allowed_font_src_regex, $href ) ) {
|
340 |
+
return;
|
341 |
+
}
|
342 |
+
|
343 |
+
$css_file_path = $this->get_validated_css_file_path( $href );
|
344 |
+
if ( is_wp_error( $css_file_path ) ) {
|
345 |
+
$this->remove_invalid_child( $element, array(
|
346 |
+
'message' => $css_file_path->get_error_message(),
|
347 |
+
) );
|
348 |
+
return;
|
349 |
+
}
|
350 |
+
|
351 |
+
// Load the CSS from the filesystem.
|
352 |
+
$rules = "\n/* $href */\n";
|
353 |
+
$rules .= file_get_contents( $css_file_path ); // phpcs:ignore -- It's a local filesystem path not a remote request.
|
354 |
+
|
355 |
+
$rules = $this->remove_illegal_css( $rules, $element );
|
356 |
+
|
357 |
+
$media = $element->getAttribute( 'media' );
|
358 |
+
if ( $media && 'all' !== $media ) {
|
359 |
+
$rules = sprintf( '@media %s { %s }', $media, $rules );
|
360 |
+
}
|
361 |
+
|
362 |
+
// Remove if surpasses max size.
|
363 |
+
$length = strlen( $rules );
|
364 |
+
if ( $this->current_custom_size + $length > $this->custom_max_size ) {
|
365 |
+
$this->remove_invalid_child( $element, array(
|
366 |
+
'message' => __( 'Too much CSS enqueued.', 'amp' ),
|
367 |
+
) );
|
368 |
+
return;
|
369 |
+
}
|
370 |
+
|
371 |
+
$this->current_custom_size += $length;
|
372 |
+
$this->stylesheets[ $href ] = $rules;
|
373 |
+
|
374 |
+
// Remove now that styles have been processed.
|
375 |
+
$element->parentNode->removeChild( $element );
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* Remove illegal CSS from the stylesheet.
|
380 |
+
*
|
381 |
+
* @since 0.7
|
382 |
+
*
|
383 |
+
* @todo This needs proper CSS parser and to take an alternative approach to removing !important by extracting
|
384 |
+
* the rule into a separate style rule with a very specific selector.
|
385 |
+
* @param string $stylesheet Stylesheet.
|
386 |
+
* @param DOMElement $element Element where the stylesheet came from.
|
387 |
+
* @return string Scrubbed stylesheet.
|
388 |
+
*/
|
389 |
+
private function remove_illegal_css( $stylesheet, $element ) {
|
390 |
+
$stylesheet = preg_replace( '/\s*!important/', '', $stylesheet, -1, $important_count ); // Note this has to also replace inside comments to be valid.
|
391 |
+
if ( $important_count > 0 && ! empty( $this->args['validation_error_callback'] ) ) {
|
392 |
+
call_user_func( $this->args['validation_error_callback'], array(
|
393 |
+
'code' => 'css_important_removed',
|
394 |
+
'node' => $element,
|
395 |
+
) );
|
396 |
+
}
|
397 |
+
$stylesheet = preg_replace( '/overflow(-[xy])?\s*:\s*(auto|scroll)\s*;?\s*/', '', $stylesheet, -1, $overlow_count );
|
398 |
+
if ( $overlow_count > 0 && ! empty( $this->args['validation_error_callback'] ) ) {
|
399 |
+
call_user_func( $this->args['validation_error_callback'], array(
|
400 |
+
'code' => 'css_overflow_property_removed',
|
401 |
+
'node' => $element,
|
402 |
+
) );
|
403 |
+
}
|
404 |
+
return $stylesheet;
|
405 |
+
}
|
406 |
+
|
407 |
+
/**
|
408 |
+
* Validate amp-keyframe style.
|
409 |
+
*
|
410 |
+
* @since 0.7
|
411 |
+
* @link https://github.com/ampproject/amphtml/blob/b685a0780a7f59313666225478b2b79b463bcd0b/validator/validator-main.protoascii#L1002-L1043
|
412 |
+
*
|
413 |
+
* @param DOMElement $style Style element.
|
414 |
+
* @return true|WP_Error Validity.
|
415 |
+
*/
|
416 |
+
private function validate_amp_keyframe( $style ) {
|
417 |
+
if ( 'body' !== $style->parentNode->nodeName ) {
|
418 |
+
return new WP_Error( 'mandatory_body_child', __( 'amp-keyframes is not child of body element.', 'amp' ) );
|
419 |
+
}
|
420 |
+
|
421 |
+
if ( $this->keyframes_max_size && strlen( $style->textContent ) > $this->keyframes_max_size ) {
|
422 |
+
return new WP_Error( 'max_bytes', __( 'amp-keyframes is too large', 'amp' ) );
|
423 |
+
}
|
424 |
+
|
425 |
+
// This logic could be in AMP_Tag_And_Attribute_Sanitizer, but since it only applies to amp-keyframes it seems unnecessary.
|
426 |
+
$next_sibling = $style->nextSibling;
|
427 |
+
while ( $next_sibling ) {
|
428 |
+
if ( $next_sibling instanceof DOMElement ) {
|
429 |
+
return new WP_Error( 'mandatory_last_child', __( 'amp-keyframes is not last element in body.', 'amp' ) );
|
430 |
+
}
|
431 |
+
$next_sibling = $next_sibling->nextSibling;
|
432 |
+
}
|
433 |
+
|
434 |
+
// @todo Also add validation of the CSS spec itself.
|
435 |
+
return true;
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* Collect and store all CSS style attributes.
|
440 |
*
|
441 |
* Collects the CSS styles from within the HTML contained in this instance's DOMDocument.
|
442 |
*
|
443 |
* @see Retrieve array of styles using $this->get_styles() after calling this method.
|
444 |
*
|
445 |
* @since 0.4
|
446 |
+
* @since 0.7 Modified to use element passed by XPath query.
|
447 |
*
|
448 |
* @note Uses recursion to traverse down the tree of DOMDocument nodes.
|
449 |
*
|
450 |
+
* @param DOMElement $element Node.
|
451 |
*/
|
452 |
+
private function collect_inline_styles( $element ) {
|
453 |
+
$value = $element->getAttribute( 'style' );
|
454 |
+
if ( ! $value ) {
|
455 |
return;
|
456 |
}
|
457 |
+
$class = $element->getAttribute( 'class' );
|
458 |
|
459 |
+
$properties = $this->process_style( $value );
|
|
|
|
|
460 |
|
461 |
+
if ( ! empty( $properties ) ) {
|
462 |
+
$class_name = $this->generate_class_name( $properties );
|
463 |
+
$new_class = trim( $class . ' ' . $class_name );
|
|
|
|
|
464 |
|
465 |
+
$selector = '.' . $class_name;
|
466 |
+
$length = strlen( sprintf( '%s { %s }', $selector, join( '; ', $properties ) . ';' ) );
|
467 |
+
|
468 |
+
if ( $this->current_custom_size + $length > $this->custom_max_size ) {
|
469 |
+
$this->remove_invalid_attribute( $element, 'style', array(
|
470 |
+
'message' => __( 'Too much CSS.', 'amp' ),
|
471 |
+
) );
|
472 |
+
return;
|
473 |
}
|
|
|
474 |
|
475 |
+
$element->setAttribute( 'class', $new_class );
|
476 |
+
$this->styles[ $selector ] = $properties;
|
|
|
|
|
477 |
}
|
478 |
+
$element->removeAttribute( 'style' );
|
479 |
}
|
480 |
|
481 |
/**
|
483 |
*
|
484 |
* @since 0.4
|
485 |
*
|
486 |
+
* @param string $css Style string.
|
487 |
+
* @return array Style properties.
|
488 |
*/
|
489 |
+
private function process_style( $css ) {
|
490 |
|
491 |
+
// Normalize whitespace.
|
492 |
+
$css = str_replace( array( "\n", "\r", "\t" ), '', $css );
|
|
|
|
|
|
|
|
|
|
|
|
|
493 |
|
494 |
/*
|
|
|
495 |
* Use preg_split to break up rules by `;` but only if the
|
496 |
* semi-colon is not inside parens (like a data-encoded image).
|
497 |
*/
|
498 |
+
$styles = preg_split( '/\s*;\s*(?![^(]*\))/', trim( $css, '; ' ) );
|
499 |
+
$styles = array_filter( $styles );
|
500 |
|
501 |
// Normalize the order of the styles.
|
502 |
sort( $styles );
|
505 |
|
506 |
// Normalize whitespace and filter rules.
|
507 |
foreach ( $styles as $index => $rule ) {
|
508 |
+
$tuple = preg_split( '/\s*:\s*/', $rule, 2 );
|
509 |
+
if ( 2 !== count( $tuple ) ) {
|
510 |
continue;
|
511 |
}
|
512 |
|
513 |
+
list( $property, $value ) = $this->filter_style( $tuple[0], $tuple[1] );
|
514 |
if ( empty( $property ) || empty( $value ) ) {
|
515 |
continue;
|
516 |
}
|
535 |
* @return array
|
536 |
*/
|
537 |
private function filter_style( $property, $value ) {
|
538 |
+
/*
|
|
|
539 |
* Remove overflow if value is `auto` or `scroll`; not allowed in AMP
|
540 |
*
|
541 |
+
* @todo This removal needs to be reported.
|
542 |
* @see https://www.ampproject.org/docs/reference/spec.html#properties
|
543 |
*/
|
544 |
+
if ( preg_match( '#^overflow(-[xy])?$#i', $property ) && preg_match( '#^(auto|scroll)$#i', $value ) ) {
|
545 |
return array( false, false );
|
546 |
}
|
547 |
|
549 |
$property = 'max-width';
|
550 |
}
|
551 |
|
552 |
+
/*
|
553 |
* Remove `!important`; not allowed in AMP
|
554 |
+
*
|
555 |
+
* @todo This removal needs to be reported.
|
556 |
*/
|
557 |
if ( false !== strpos( $value, 'important' ) ) {
|
558 |
$value = preg_replace( '/\s*\!\s*important$/', '', $value );
|
@@ -17,7 +17,6 @@
|
|
17 |
* @todo Need to check the following items that are not yet checked by this sanitizer:
|
18 |
*
|
19 |
* - `also_requires_attr` - if one attribute is present, this requires another.
|
20 |
-
* - `CdataSpec` - CDATA is not validated or sanitized.
|
21 |
* - `ChildTagSpec` - Places restrictions on the number and type of child tags.
|
22 |
* - `if_value_regex` - if one attribute value matches, this places a restriction
|
23 |
* on another attribute/value.
|
@@ -53,6 +52,14 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
53 |
*/
|
54 |
protected $layout_allowed_attributes;
|
55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
/**
|
57 |
* Stack.
|
58 |
*
|
@@ -71,6 +78,13 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
71 |
*/
|
72 |
protected $DEFAULT_ARGS = array();
|
73 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
/**
|
75 |
* AMP_Tag_And_Attribute_Sanitizer constructor.
|
76 |
*
|
@@ -81,40 +95,152 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
81 |
*/
|
82 |
public function __construct( $dom, $args = array() ) {
|
83 |
$this->DEFAULT_ARGS = array(
|
84 |
-
'amp_allowed_tags'
|
85 |
'amp_globally_allowed_attributes' => AMP_Allowed_Tags_Generated::get_allowed_attributes(),
|
86 |
-
'amp_layout_allowed_attributes'
|
|
|
87 |
);
|
88 |
|
89 |
parent::__construct( $dom, $args );
|
90 |
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
}
|
98 |
|
99 |
/**
|
100 |
-
*
|
101 |
*
|
102 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
*/
|
104 |
-
public function
|
|
|
|
|
105 |
|
106 |
-
|
107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
}
|
|
|
|
|
109 |
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
|
117 |
-
|
|
|
118 |
|
119 |
/**
|
120 |
* This loop traverses through the DOM tree iteratively.
|
@@ -165,6 +291,12 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
165 |
return;
|
166 |
}
|
167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
/*
|
169 |
* Compile a list of rule_specs to validate for this node
|
170 |
* based on tag name of the node.
|
@@ -176,6 +308,27 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
176 |
}
|
177 |
foreach ( $rule_spec_list as $id => $rule_spec ) {
|
178 |
if ( $this->validate_tag_spec_for_node( $node, $rule_spec[ AMP_Rule_Spec::TAG_SPEC ] ) ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
$rule_spec_list_to_validate[ $id ] = $rule_spec;
|
180 |
}
|
181 |
}
|
@@ -188,6 +341,8 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
188 |
|
189 |
// The remaining validations all have to do with attributes.
|
190 |
$attr_spec_list = array();
|
|
|
|
|
191 |
|
192 |
/*
|
193 |
* If we have exactly one rule_spec, use it's attr_spec_list
|
@@ -196,7 +351,10 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
196 |
if ( 1 === count( $rule_spec_list_to_validate ) ) {
|
197 |
$rule_spec = array_pop( $rule_spec_list_to_validate );
|
198 |
$attr_spec_list = $rule_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ];
|
199 |
-
|
|
|
|
|
|
|
200 |
} else {
|
201 |
/*
|
202 |
* If there is more than one valid rule_spec for this node,
|
@@ -213,12 +371,25 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
213 |
$attr_spec_scores[ $spec_id ] = $this->validate_attr_spec_list_for_node( $node, $rule_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ] );
|
214 |
}
|
215 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
216 |
// Get the key(s) to the highest score(s).
|
217 |
$spec_ids_sorted = array_keys( $attr_spec_scores, max( $attr_spec_scores ), true );
|
218 |
|
219 |
// If there is exactly one attr_spec with a max score, use that one.
|
220 |
if ( 1 === count( $spec_ids_sorted ) ) {
|
221 |
$attr_spec_list = $rule_spec_list_to_validate[ $spec_ids_sorted[0] ][ AMP_Rule_Spec::ATTR_SPEC_LIST ];
|
|
|
|
|
|
|
|
|
222 |
} else {
|
223 |
// This should not happen very often, but...
|
224 |
// If we're here, then we're not sure which spec should
|
@@ -227,6 +398,13 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
227 |
$spec_list = isset( $rule_spec_list_to_validate[ $id ][ AMP_Rule_Spec::ATTR_SPEC_LIST ] ) ? $rule_spec_list_to_validate[ $id ][ AMP_Rule_Spec::ATTR_SPEC_LIST ] : null;
|
228 |
if ( ! $this->is_missing_mandatory_attribute( $spec_list, $node ) ) {
|
229 |
$attr_spec_list = array_merge( $attr_spec_list, $spec_list );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
}
|
231 |
}
|
232 |
$first_spec = reset( $rule_spec_list_to_validate );
|
@@ -241,18 +419,68 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
241 |
return;
|
242 |
}
|
243 |
|
244 |
-
// Remove
|
245 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
246 |
|
247 |
-
|
248 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
249 |
}
|
250 |
|
251 |
/**
|
252 |
* Whether a node is missing a mandatory attribute.
|
253 |
*
|
254 |
-
* @param array
|
255 |
-
* @param
|
256 |
* @return boolean $is_missing boolean Whether a required attribute is missing.
|
257 |
*/
|
258 |
public function is_missing_mandatory_attribute( $attr_spec, $node ) {
|
@@ -260,8 +488,22 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
260 |
return false;
|
261 |
}
|
262 |
foreach ( $attr_spec as $attr_name => $attr_spec_rule_value ) {
|
|
|
|
|
|
|
263 |
$is_mandatory = isset( $attr_spec_rule_value[ AMP_Rule_Spec::MANDATORY ] ) ? ( true === $attr_spec_rule_value[ AMP_Rule_Spec::MANDATORY ] ) : false;
|
264 |
-
$attribute_exists =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
265 |
if ( $is_mandatory && ! $attribute_exists ) {
|
266 |
return true;
|
267 |
}
|
@@ -269,6 +511,24 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
269 |
return false;
|
270 |
}
|
271 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
272 |
/**
|
273 |
* Determines is a node is currently valid per its tag specification.
|
274 |
*
|
@@ -279,15 +539,21 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
279 |
*
|
280 |
* @since 0.5
|
281 |
*
|
282 |
-
* @param
|
283 |
-
* @param array
|
284 |
* @return boolean $valid Whether the node's placement is valid.
|
285 |
*/
|
286 |
private function validate_tag_spec_for_node( $node, $tag_spec ) {
|
|
|
287 |
if ( ! empty( $tag_spec[ AMP_Rule_Spec::MANDATORY_PARENT ] ) && ! $this->has_parent( $node, $tag_spec[ AMP_Rule_Spec::MANDATORY_PARENT ] ) ) {
|
288 |
return false;
|
289 |
}
|
290 |
|
|
|
|
|
|
|
|
|
|
|
291 |
if ( ! empty( $tag_spec[ AMP_Rule_Spec::DISALLOWED_ANCESTOR ] ) ) {
|
292 |
foreach ( $tag_spec[ AMP_Rule_Spec::DISALLOWED_ANCESTOR ] as $disallowed_ancestor_node_name ) {
|
293 |
if ( $this->has_ancestor( $node, $disallowed_ancestor_node_name ) ) {
|
@@ -313,14 +579,27 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
313 |
* @param DOMNode $node Node.
|
314 |
* @param array[] $attr_spec_list Attribute Spec list.
|
315 |
*
|
316 |
-
* @return
|
317 |
*/
|
318 |
private function validate_attr_spec_list_for_node( $node, $attr_spec_list ) {
|
319 |
-
|
320 |
-
|
321 |
-
*
|
322 |
*/
|
323 |
if ( ! $node->hasAttributes() ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
324 |
return 0;
|
325 |
}
|
326 |
|
@@ -333,16 +612,15 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
333 |
}
|
334 |
}
|
335 |
|
336 |
-
if ( ! $node instanceof DOMElement ) {
|
337 |
-
/*
|
338 |
-
* A DOMNode is not valid for checks so might
|
339 |
-
* as well bail here is not an DOMElement.
|
340 |
-
*/
|
341 |
-
return 0;
|
342 |
-
}
|
343 |
-
|
344 |
$score = 0;
|
345 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
346 |
/*
|
347 |
* Iterate through each attribute rule in this attr spec list and run
|
348 |
* the series of tests. Each filter is given a `$node`, an `$attr_name`,
|
@@ -351,10 +629,20 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
351 |
*/
|
352 |
foreach ( $attr_spec_list as $attr_name => $attr_spec_rule ) {
|
353 |
|
|
|
|
|
|
|
|
|
|
|
|
|
354 |
// If a mandatory attribute is required, and attribute exists, pass.
|
355 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::MANDATORY ] ) ) {
|
356 |
-
|
|
|
|
|
357 |
$score++;
|
|
|
|
|
358 |
}
|
359 |
}
|
360 |
|
@@ -363,8 +651,11 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
363 |
* Given attribute's value must exactly equal value of the rule to pass.
|
364 |
*/
|
365 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE ] ) ) {
|
366 |
-
|
|
|
367 |
$score++;
|
|
|
|
|
368 |
}
|
369 |
}
|
370 |
|
@@ -374,8 +665,11 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
374 |
* specified by the value of rule to pass.
|
375 |
*/
|
376 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX ] ) ) {
|
377 |
-
|
|
|
378 |
$score++;
|
|
|
|
|
379 |
}
|
380 |
}
|
381 |
|
@@ -385,8 +679,11 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
385 |
* the rule to pass.
|
386 |
*/
|
387 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_CASEI ] ) ) {
|
388 |
-
|
|
|
389 |
$score++;
|
|
|
|
|
390 |
}
|
391 |
}
|
392 |
|
@@ -396,8 +693,11 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
396 |
* pattern specified by the value of the rule to pass.
|
397 |
*/
|
398 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX_CASEI ] ) ) {
|
399 |
-
|
|
|
400 |
$score++;
|
|
|
|
|
401 |
}
|
402 |
}
|
403 |
|
@@ -405,9 +705,25 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
405 |
* If given attribute's value is a URL with a protocol, the protocol must
|
406 |
* be in the array specified by the rule's value to pass.
|
407 |
*/
|
408 |
-
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALLOWED_PROTOCOL ] ) ) {
|
409 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
410 |
$score++;
|
|
|
|
|
411 |
}
|
412 |
}
|
413 |
|
@@ -415,9 +731,12 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
415 |
* If the given attribute's value is *not* a relative path, and the rule's
|
416 |
* value is `false`, then pass.
|
417 |
*/
|
418 |
-
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALLOW_RELATIVE ] ) ) {
|
419 |
-
|
|
|
420 |
$score++;
|
|
|
|
|
421 |
}
|
422 |
}
|
423 |
|
@@ -425,9 +744,12 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
425 |
* If the given attribute's value exists, is non-empty and the rule's value
|
426 |
* is false, then pass.
|
427 |
*/
|
428 |
-
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALLOW_EMPTY ] ) ) {
|
429 |
-
|
|
|
430 |
$score++;
|
|
|
|
|
431 |
}
|
432 |
}
|
433 |
|
@@ -436,8 +758,11 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
436 |
* of domains in the value of the rule, then pass.
|
437 |
*/
|
438 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::DISALLOWED_DOMAIN ] ) ) {
|
439 |
-
|
|
|
440 |
$score++;
|
|
|
|
|
441 |
}
|
442 |
}
|
443 |
|
@@ -446,10 +771,28 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
446 |
* by the rule's value, then pass.
|
447 |
*/
|
448 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::BLACKLISTED_VALUE_REGEX ] ) ) {
|
449 |
-
|
|
|
450 |
$score++;
|
|
|
|
|
451 |
}
|
452 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
453 |
}
|
454 |
|
455 |
return $score;
|
@@ -460,15 +803,16 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
460 |
*
|
461 |
* @param DOMNode $node Node.
|
462 |
* @param array[] $attr_spec_list Attribute spec list.
|
|
|
463 |
*/
|
464 |
-
private function
|
465 |
|
466 |
if ( ! $node instanceof DOMElement ) {
|
467 |
/**
|
468 |
* If $node is only a DOMNode and not a DOMElement we can't
|
469 |
* remove an attribute from it anyway. So bail out now.
|
470 |
*/
|
471 |
-
return;
|
472 |
}
|
473 |
|
474 |
/*
|
@@ -479,34 +823,11 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
479 |
$attrs_to_remove = array();
|
480 |
foreach ( $node->attributes as $attr_name => $attr_node ) {
|
481 |
if ( ! $this->is_amp_allowed_attribute( $attr_name, $attr_spec_list ) ) {
|
482 |
-
|
483 |
-
* This attribute is not allowed for this node; plan to remove it.
|
484 |
-
*/
|
485 |
-
$attrs_to_remove[] = $attr_name;
|
486 |
}
|
487 |
}
|
488 |
|
489 |
-
|
490 |
-
/*
|
491 |
-
* Ensure we are not removing attributes listed as an alternate
|
492 |
-
* or allowed attributes, e.g. 'srcset' is an alternate for 'src'.
|
493 |
-
*/
|
494 |
-
foreach ( $attr_spec_list as $attr_name => $attr_spec_rule_value ) {
|
495 |
-
if ( isset( $attr_spec_rule_value[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
|
496 |
-
foreach ( $attr_spec_rule_value[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_name ) {
|
497 |
-
$alt_name_keys = array_keys( $attrs_to_remove, $alternative_name, true );
|
498 |
-
if ( ! empty( $alt_name_keys ) ) {
|
499 |
-
unset( $attrs_to_remove[ $alt_name_keys[0] ] );
|
500 |
-
}
|
501 |
-
}
|
502 |
-
}
|
503 |
-
}
|
504 |
-
|
505 |
-
// Remove the disallowed attributes.
|
506 |
-
foreach ( $attrs_to_remove as $attr_name ) {
|
507 |
-
$node->removeAttribute( $attr_name );
|
508 |
-
}
|
509 |
-
}
|
510 |
}
|
511 |
|
512 |
/**
|
@@ -514,23 +835,29 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
514 |
*
|
515 |
* Allowed values are found $this->globally_allowed_attributes and in parameter $attr_spec_list
|
516 |
*
|
517 |
-
* @param DOMNode $node
|
518 |
-
* @param array[][] $attr_spec_list
|
|
|
|
|
519 |
*/
|
520 |
-
private function sanitize_disallowed_attribute_values_in_node( $node, $attr_spec_list ) {
|
521 |
|
522 |
if ( ! $node instanceof DOMElement ) {
|
523 |
/*
|
524 |
* If $node is only a DOMNode and not a DOMElement we can't
|
525 |
* remove an attribute from it anyway. So bail out now.
|
526 |
*/
|
527 |
-
return;
|
528 |
}
|
529 |
|
530 |
-
$this->delegated_sanitize_disallowed_attribute_values_in_node(
|
531 |
-
|
532 |
-
|
533 |
-
|
|
|
|
|
|
|
|
|
534 |
}
|
535 |
|
536 |
/**
|
@@ -538,10 +865,12 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
538 |
*
|
539 |
* @see $this->sanitize_disallowed_attribute_values_in_node() which delegates to this method
|
540 |
*
|
541 |
-
* @param DOMElement $node
|
542 |
-
* @param array[][] $attr_spec_list
|
|
|
|
|
543 |
*/
|
544 |
-
private function delegated_sanitize_disallowed_attribute_values_in_node( $node, $attr_spec_list ) {
|
545 |
$attrs_to_remove = array();
|
546 |
|
547 |
foreach ( $attr_spec_list as $attr_name => $attr_val ) {
|
@@ -554,7 +883,7 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
554 |
|
555 |
foreach ( $node->attributes as $attr_name => $attr_node ) {
|
556 |
|
557 |
-
if ( ! isset( $attr_spec_list[ $attr_name ] ) ) {
|
558 |
continue;
|
559 |
}
|
560 |
|
@@ -568,18 +897,21 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
568 |
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_value_casei( $node, $attr_name, $attr_spec_rule ) ) {
|
569 |
$should_remove_node = true;
|
570 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX ] ) &&
|
571 |
-
|
572 |
$should_remove_node = true;
|
573 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX_CASEI ] ) &&
|
574 |
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_value_regex_casei( $node, $attr_name, $attr_spec_rule ) ) {
|
575 |
$should_remove_node = true;
|
576 |
-
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALLOWED_PROTOCOL ] ) &&
|
577 |
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_allowed_protocol( $node, $attr_name, $attr_spec_rule ) ) {
|
578 |
$should_remove_node = true;
|
579 |
-
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::
|
|
|
|
|
|
|
580 |
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_disallowed_relative( $node, $attr_name, $attr_spec_rule ) ) {
|
581 |
$should_remove_node = true;
|
582 |
-
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALLOW_EMPTY ] ) &&
|
583 |
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_disallowed_empty( $node, $attr_name, $attr_spec_rule ) ) {
|
584 |
$should_remove_node = true;
|
585 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::DISALLOWED_DOMAIN ] ) &&
|
@@ -598,22 +930,24 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
598 |
|
599 |
if ( $is_mandatory ) {
|
600 |
$this->remove_node( $node );
|
601 |
-
return;
|
602 |
}
|
603 |
|
604 |
-
$attrs_to_remove[] = $
|
605 |
}
|
606 |
}
|
607 |
|
608 |
// Remove the disallowed values.
|
609 |
-
foreach ( $attrs_to_remove as $
|
610 |
-
if ( isset( $attr_spec_list[ $
|
611 |
-
( true === $attr_spec_list[ $
|
612 |
-
$
|
613 |
} else {
|
614 |
-
$
|
615 |
}
|
616 |
}
|
|
|
|
|
617 |
}
|
618 |
|
619 |
/**
|
@@ -656,7 +990,7 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
656 |
*
|
657 |
* @param DOMElement $node Node.
|
658 |
* @param string $attr_name Attribute name.
|
659 |
-
* @param array
|
660 |
*
|
661 |
* @return string:
|
662 |
* - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
|
@@ -667,7 +1001,7 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
667 |
private function check_attr_spec_rule_value( $node, $attr_name, $attr_spec_rule ) {
|
668 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE ] ) ) {
|
669 |
if ( $node->hasAttribute( $attr_name ) ) {
|
670 |
-
if ( $node->getAttribute( $attr_name )
|
671 |
return AMP_Rule_Spec::PASS;
|
672 |
} else {
|
673 |
return AMP_Rule_Spec::FAIL;
|
@@ -675,7 +1009,7 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
675 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
|
676 |
foreach ( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_name ) {
|
677 |
if ( $node->hasAttribute( $alternative_name ) ) {
|
678 |
-
if ( $node->getAttribute( $alternative_name )
|
679 |
return AMP_Rule_Spec::PASS;
|
680 |
} else {
|
681 |
return AMP_Rule_Spec::FAIL;
|
@@ -687,6 +1021,33 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
687 |
return AMP_Rule_Spec::NOT_APPLICABLE;
|
688 |
}
|
689 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
690 |
/**
|
691 |
* Check if attribute has a value rule determine if its value matches ignoring case.
|
692 |
*
|
@@ -792,6 +1153,47 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
792 |
return AMP_Rule_Spec::NOT_APPLICABLE;
|
793 |
}
|
794 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
795 |
/**
|
796 |
* Check if attribute has a protocol value rule determine if it matches.
|
797 |
*
|
@@ -806,11 +1208,9 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
806 |
* is no rule for this attribute.
|
807 |
*/
|
808 |
private function check_attr_spec_rule_allowed_protocol( $node, $attr_name, $attr_spec_rule ) {
|
809 |
-
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALLOWED_PROTOCOL ] ) ) {
|
810 |
if ( $node->hasAttribute( $attr_name ) ) {
|
811 |
-
$
|
812 |
-
$attr_value = preg_replace( '/\s*,\s*/', ',', $attr_value );
|
813 |
-
$urls_to_test = explode( ',', $attr_value );
|
814 |
foreach ( $urls_to_test as $url ) {
|
815 |
/*
|
816 |
* This seems to be an acceptable check since the AMP validator
|
@@ -818,7 +1218,7 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
818 |
*/
|
819 |
$url_scheme = AMP_WP_Utils::parse_url( $url, PHP_URL_SCHEME );
|
820 |
if ( $url_scheme ) {
|
821 |
-
if ( ! in_array( strtolower( $url_scheme ), $attr_spec_rule[ AMP_Rule_Spec::ALLOWED_PROTOCOL ], true ) ) {
|
822 |
return AMP_Rule_Spec::FAIL;
|
823 |
}
|
824 |
}
|
@@ -827,9 +1227,7 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
827 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
|
828 |
foreach ( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_name ) {
|
829 |
if ( $node->hasAttribute( $alternative_name ) ) {
|
830 |
-
$
|
831 |
-
$attr_value = preg_replace( '/\s*,\s*/', ',', $attr_value );
|
832 |
-
$urls_to_test = explode( ',', $attr_value );
|
833 |
foreach ( $urls_to_test as $url ) {
|
834 |
/*
|
835 |
* This seems to be an acceptable check since the AMP validator
|
@@ -837,7 +1235,7 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
837 |
*/
|
838 |
$url_scheme = AMP_WP_Utils::parse_url( $url, PHP_URL_SCHEME );
|
839 |
if ( $url_scheme ) {
|
840 |
-
if ( ! in_array( strtolower( $url_scheme ), $attr_spec_rule[ AMP_Rule_Spec::ALLOWED_PROTOCOL ], true ) ) {
|
841 |
return AMP_Rule_Spec::FAIL;
|
842 |
}
|
843 |
}
|
@@ -864,11 +1262,9 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
864 |
* is no rule for this attribute.
|
865 |
*/
|
866 |
private function check_attr_spec_rule_disallowed_relative( $node, $attr_name, $attr_spec_rule ) {
|
867 |
-
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALLOW_RELATIVE ] ) && ! ( $attr_spec_rule[ AMP_Rule_Spec::ALLOW_RELATIVE ] ) ) {
|
868 |
if ( $node->hasAttribute( $attr_name ) ) {
|
869 |
-
$
|
870 |
-
$attr_value = preg_replace( '/\s*,\s*/', ',', $attr_value );
|
871 |
-
$urls_to_test = explode( ',', $attr_value );
|
872 |
foreach ( $urls_to_test as $url ) {
|
873 |
$parsed_url = AMP_WP_Utils::parse_url( $url );
|
874 |
|
@@ -887,9 +1283,7 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
887 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
|
888 |
foreach ( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_name ) {
|
889 |
if ( $node->hasAttribute( $alternative_name ) ) {
|
890 |
-
$
|
891 |
-
$attr_value = preg_replace( '/\s*,\s*/', ',', $attr_value );
|
892 |
-
$urls_to_test = explode( ',', $attr_value );
|
893 |
foreach ( $urls_to_test as $url ) {
|
894 |
$parsed_url = AMP_WP_Utils::parse_url( $url );
|
895 |
if ( empty( $parsed_url['scheme'] ) ) {
|
@@ -918,7 +1312,7 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
918 |
* is no rule for this attribute.
|
919 |
*/
|
920 |
private function check_attr_spec_rule_disallowed_empty( $node, $attr_name, $attr_spec_rule ) {
|
921 |
-
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALLOW_EMPTY ] ) && ! ( $attr_spec_rule[ AMP_Rule_Spec::ALLOW_EMPTY ] ) && $node->hasAttribute( $attr_name ) ) {
|
922 |
$attr_value = $node->getAttribute( $attr_name );
|
923 |
if ( empty( $attr_value ) ) {
|
924 |
return AMP_Rule_Spec::FAIL;
|
@@ -1000,6 +1394,67 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
1000 |
return AMP_Rule_Spec::NOT_APPLICABLE;
|
1001 |
}
|
1002 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1003 |
/**
|
1004 |
* Determine if the supplied attribute name is allowed for AMP.
|
1005 |
*
|
@@ -1019,6 +1474,16 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
1019 |
}
|
1020 |
}
|
1021 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1022 |
return false;
|
1023 |
}
|
1024 |
|
@@ -1066,6 +1531,7 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
1066 |
*
|
1067 |
* @since 0.5
|
1068 |
*
|
|
|
1069 |
* @param DOMNode $node Node.
|
1070 |
* @param string $ancestor_tag_name Ancestor tag name.
|
1071 |
* @return bool Return true if given node has any ancestor with the give name, false otherwise.
|
@@ -1151,9 +1617,9 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
1151 |
*/
|
1152 |
$parent = $node->parentNode;
|
1153 |
if ( $node && $parent ) {
|
1154 |
-
$
|
1155 |
}
|
1156 |
-
while ( $parent && ! $parent->hasChildNodes() &&
|
1157 |
$node = $parent;
|
1158 |
$parent = $parent->parentNode;
|
1159 |
if ( $parent ) {
|
@@ -1162,4 +1628,3 @@ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
|
|
1162 |
}
|
1163 |
}
|
1164 |
}
|
1165 |
-
|
17 |
* @todo Need to check the following items that are not yet checked by this sanitizer:
|
18 |
*
|
19 |
* - `also_requires_attr` - if one attribute is present, this requires another.
|
|
|
20 |
* - `ChildTagSpec` - Places restrictions on the number and type of child tags.
|
21 |
* - `if_value_regex` - if one attribute value matches, this places a restriction
|
22 |
* on another attribute/value.
|
52 |
*/
|
53 |
protected $layout_allowed_attributes;
|
54 |
|
55 |
+
/**
|
56 |
+
* Mapping of alternative names back to their primary names.
|
57 |
+
*
|
58 |
+
* @since 0.7
|
59 |
+
* @var array
|
60 |
+
*/
|
61 |
+
protected $rev_alternate_attr_name_lookup = array();
|
62 |
+
|
63 |
/**
|
64 |
* Stack.
|
65 |
*
|
78 |
*/
|
79 |
protected $DEFAULT_ARGS = array();
|
80 |
|
81 |
+
/**
|
82 |
+
* AMP script components that are discovered being required through sanitization.
|
83 |
+
*
|
84 |
+
* @var string[]
|
85 |
+
*/
|
86 |
+
protected $script_components = array();
|
87 |
+
|
88 |
/**
|
89 |
* AMP_Tag_And_Attribute_Sanitizer constructor.
|
90 |
*
|
95 |
*/
|
96 |
public function __construct( $dom, $args = array() ) {
|
97 |
$this->DEFAULT_ARGS = array(
|
98 |
+
'amp_allowed_tags' => AMP_Allowed_Tags_Generated::get_allowed_tags(),
|
99 |
'amp_globally_allowed_attributes' => AMP_Allowed_Tags_Generated::get_allowed_attributes(),
|
100 |
+
'amp_layout_allowed_attributes' => AMP_Allowed_Tags_Generated::get_layout_attributes(),
|
101 |
+
'amp_bind_placeholder_prefix' => AMP_DOM_Utils::get_amp_bind_placeholder_prefix(),
|
102 |
);
|
103 |
|
104 |
parent::__construct( $dom, $args );
|
105 |
|
106 |
+
if ( ! empty( $this->args['allow_dirty_styles'] ) ) {
|
107 |
+
|
108 |
+
// Allow style attribute on all elements.
|
109 |
+
$this->args['amp_globally_allowed_attributes']['style'] = array();
|
110 |
+
|
111 |
+
// Allow style elements.
|
112 |
+
$this->args['amp_allowed_tags']['style'][] = array(
|
113 |
+
'attr_spec_list' => array(
|
114 |
+
'type' => array(
|
115 |
+
'value_casei' => 'text/css',
|
116 |
+
),
|
117 |
+
),
|
118 |
+
'cdata' => array(),
|
119 |
+
'tag_spec' => array(
|
120 |
+
'spec_name' => 'style for Customizer preview',
|
121 |
+
),
|
122 |
+
);
|
123 |
+
|
124 |
+
// Allow stylesheet links.
|
125 |
+
$this->args['amp_allowed_tags']['link'][] = array(
|
126 |
+
'attr_spec_list' => array(
|
127 |
+
'async' => array(),
|
128 |
+
'crossorigin' => array(),
|
129 |
+
'href' => array(
|
130 |
+
'mandatory' => true,
|
131 |
+
),
|
132 |
+
'integrity' => array(),
|
133 |
+
'media' => array(),
|
134 |
+
'rel' => array(
|
135 |
+
'dispatch_key' => 2,
|
136 |
+
'mandatory' => true,
|
137 |
+
'value_casei' => 'stylesheet',
|
138 |
+
),
|
139 |
+
'type' => array(
|
140 |
+
'value_casei' => 'text/css',
|
141 |
+
),
|
142 |
+
),
|
143 |
+
'tag_spec' => array(
|
144 |
+
'spec_name' => 'link rel=stylesheet for Customizer preview', // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
|
145 |
+
),
|
146 |
+
);
|
147 |
+
}
|
148 |
+
|
149 |
+
// Allow scripts if requested.
|
150 |
+
if ( ! empty( $this->args['allow_dirty_scripts'] ) ) {
|
151 |
+
$this->args['amp_allowed_tags']['script'][] = array(
|
152 |
+
'attr_spec_list' => array(
|
153 |
+
'type' => array(),
|
154 |
+
'src' => array(),
|
155 |
+
'async' => array(),
|
156 |
+
'defer' => array(),
|
157 |
+
),
|
158 |
+
'cdata' => array(),
|
159 |
+
'tag_spec' => array(
|
160 |
+
'spec_name' => 'scripts for Customizer preview',
|
161 |
+
),
|
162 |
+
);
|
163 |
+
}
|
164 |
+
|
165 |
+
// Prepare whitelists.
|
166 |
+
$this->allowed_tags = $this->args['amp_allowed_tags'];
|
167 |
+
foreach ( AMP_Rule_Spec::$additional_allowed_tags as $tag_name => $tag_rule_spec ) {
|
168 |
+
$this->allowed_tags[ $tag_name ][] = $tag_rule_spec;
|
169 |
+
}
|
170 |
+
|
171 |
+
// @todo Do the same for body when !use_document_element?
|
172 |
+
if ( ! empty( $this->args['use_document_element'] ) ) {
|
173 |
+
foreach ( $this->allowed_tags['html'] as &$rule_spec ) {
|
174 |
+
unset( $rule_spec[ AMP_Rule_Spec::TAG_SPEC ][ AMP_Rule_Spec::MANDATORY_PARENT ] );
|
175 |
+
}
|
176 |
+
}
|
177 |
+
|
178 |
+
foreach ( $this->allowed_tags as &$tag_specs ) {
|
179 |
+
foreach ( $tag_specs as &$tag_spec ) {
|
180 |
+
if ( isset( $tag_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ] ) ) {
|
181 |
+
$tag_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ] = $this->process_alternate_names( $tag_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ] );
|
182 |
+
}
|
183 |
+
}
|
184 |
+
}
|
185 |
+
$this->globally_allowed_attributes = $this->process_alternate_names( $this->args['amp_globally_allowed_attributes'] );
|
186 |
+
$this->layout_allowed_attributes = $this->process_alternate_names( $this->args['amp_layout_allowed_attributes'] );
|
187 |
}
|
188 |
|
189 |
/**
|
190 |
+
* Return array of values that would be valid as an HTML `script` element.
|
191 |
*
|
192 |
+
* Array keys are AMP element names and array values are their respective
|
193 |
+
* Javascript URLs from https://cdn.ampproject.org
|
194 |
+
*
|
195 |
+
* @since 0.7
|
196 |
+
* @see amp_register_default_scripts()
|
197 |
+
*
|
198 |
+
* @return array() Returns component name as array key and true as value (or JavaScript URL string),
|
199 |
+
* respectively. When true then the default component script URL will be used.
|
200 |
+
* Will return an empty array if sanitization has yet to be run
|
201 |
+
* or if it did not find any HTML elements to convert to AMP equivalents.
|
202 |
*/
|
203 |
+
public function get_scripts() {
|
204 |
+
return array_fill_keys( $this->script_components, true );
|
205 |
+
}
|
206 |
|
207 |
+
/**
|
208 |
+
* Process alternative names in attribute spec list.
|
209 |
+
*
|
210 |
+
* @since 0.7
|
211 |
+
*
|
212 |
+
* @param array $attr_spec_list Attribute spec list.
|
213 |
+
* @return array Modified attribute spec list.
|
214 |
+
*/
|
215 |
+
private function process_alternate_names( $attr_spec_list ) {
|
216 |
+
foreach ( $attr_spec_list as $attr_name => &$attr_spec ) {
|
217 |
+
if ( '[' === $attr_name[0] ) {
|
218 |
+
$placeholder_attr_name = $this->args['amp_bind_placeholder_prefix'] . trim( $attr_name, '[]' );
|
219 |
+
if ( ! isset( $attr_spec[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
|
220 |
+
$attr_spec[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] = array();
|
221 |
+
}
|
222 |
+
$attr_spec[ AMP_Rule_Spec::ALTERNATIVE_NAMES ][] = $placeholder_attr_name;
|
223 |
+
}
|
224 |
+
|
225 |
+
// Save all alternative names in lookup to improve performance.
|
226 |
+
if ( isset( $attr_spec[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
|
227 |
+
foreach ( $attr_spec[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_name ) {
|
228 |
+
$this->rev_alternate_attr_name_lookup[ $alternative_name ] = $attr_name;
|
229 |
+
}
|
230 |
+
}
|
231 |
}
|
232 |
+
return $attr_spec_list;
|
233 |
+
}
|
234 |
|
235 |
+
/**
|
236 |
+
* Sanitize the <video> elements from the HTML contained in this instance's DOMDocument.
|
237 |
+
*
|
238 |
+
* @since 0.5
|
239 |
+
*/
|
240 |
+
public function sanitize() {
|
241 |
|
242 |
+
// Add root of content to the stack.
|
243 |
+
$this->stack[] = $this->root_element;
|
244 |
|
245 |
/**
|
246 |
* This loop traverses through the DOM tree iteratively.
|
291 |
return;
|
292 |
}
|
293 |
|
294 |
+
/**
|
295 |
+
* Node is now an element.
|
296 |
+
*
|
297 |
+
* @var DOMElement $node
|
298 |
+
*/
|
299 |
+
|
300 |
/*
|
301 |
* Compile a list of rule_specs to validate for this node
|
302 |
* based on tag name of the node.
|
308 |
}
|
309 |
foreach ( $rule_spec_list as $id => $rule_spec ) {
|
310 |
if ( $this->validate_tag_spec_for_node( $node, $rule_spec[ AMP_Rule_Spec::TAG_SPEC ] ) ) {
|
311 |
+
|
312 |
+
// Expand extension_spec into a set of attr_spec_list.
|
313 |
+
if ( isset( $rule_spec[ AMP_Rule_Spec::TAG_SPEC ]['extension_spec'] ) ) {
|
314 |
+
$extension_spec = $rule_spec[ AMP_Rule_Spec::TAG_SPEC ]['extension_spec'];
|
315 |
+
$custom_attr = 'amp-mustache' === $extension_spec['name'] ? 'custom-template' : 'custom-element';
|
316 |
+
|
317 |
+
$rule_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ][ $custom_attr ] = array(
|
318 |
+
AMP_Rule_Spec::VALUE => $extension_spec['name'],
|
319 |
+
AMP_Rule_Spec::MANDATORY => true,
|
320 |
+
);
|
321 |
+
|
322 |
+
$rule_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ]['src'] = array(
|
323 |
+
AMP_Rule_Spec::VALUE_REGEX => implode( '', array(
|
324 |
+
'^',
|
325 |
+
preg_quote( 'https://cdn.ampproject.org/v0/' . $extension_spec['name'] . '-' ),
|
326 |
+
'(' . implode( '|', $extension_spec['allowed_versions'] ) . ')',
|
327 |
+
'\.js$',
|
328 |
+
) ),
|
329 |
+
);
|
330 |
+
}
|
331 |
+
|
332 |
$rule_spec_list_to_validate[ $id ] = $rule_spec;
|
333 |
}
|
334 |
}
|
341 |
|
342 |
// The remaining validations all have to do with attributes.
|
343 |
$attr_spec_list = array();
|
344 |
+
$tag_spec = array();
|
345 |
+
$cdata = array();
|
346 |
|
347 |
/*
|
348 |
* If we have exactly one rule_spec, use it's attr_spec_list
|
351 |
if ( 1 === count( $rule_spec_list_to_validate ) ) {
|
352 |
$rule_spec = array_pop( $rule_spec_list_to_validate );
|
353 |
$attr_spec_list = $rule_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ];
|
354 |
+
$tag_spec = $rule_spec[ AMP_Rule_Spec::TAG_SPEC ];
|
355 |
+
if ( isset( $rule_spec[ AMP_Rule_Spec::CDATA ] ) ) {
|
356 |
+
$cdata = $rule_spec[ AMP_Rule_Spec::CDATA ];
|
357 |
+
}
|
358 |
} else {
|
359 |
/*
|
360 |
* If there is more than one valid rule_spec for this node,
|
371 |
$attr_spec_scores[ $spec_id ] = $this->validate_attr_spec_list_for_node( $node, $rule_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ] );
|
372 |
}
|
373 |
|
374 |
+
// Remove all spec lists that didn't match.
|
375 |
+
$attr_spec_scores = array_filter( $attr_spec_scores );
|
376 |
+
|
377 |
+
// If no attribute spec lists match, then the element must be removed.
|
378 |
+
if ( empty( $attr_spec_scores ) ) {
|
379 |
+
$this->remove_node( $node );
|
380 |
+
return;
|
381 |
+
}
|
382 |
+
|
383 |
// Get the key(s) to the highest score(s).
|
384 |
$spec_ids_sorted = array_keys( $attr_spec_scores, max( $attr_spec_scores ), true );
|
385 |
|
386 |
// If there is exactly one attr_spec with a max score, use that one.
|
387 |
if ( 1 === count( $spec_ids_sorted ) ) {
|
388 |
$attr_spec_list = $rule_spec_list_to_validate[ $spec_ids_sorted[0] ][ AMP_Rule_Spec::ATTR_SPEC_LIST ];
|
389 |
+
$tag_spec = $rule_spec_list_to_validate[ $spec_ids_sorted[0] ][ AMP_Rule_Spec::TAG_SPEC ];
|
390 |
+
if ( isset( $rule_spec_list_to_validate[ $spec_ids_sorted[0] ][ AMP_Rule_Spec::CDATA ] ) ) {
|
391 |
+
$cdata = $rule_spec_list_to_validate[ $spec_ids_sorted[0] ][ AMP_Rule_Spec::CDATA ];
|
392 |
+
}
|
393 |
} else {
|
394 |
// This should not happen very often, but...
|
395 |
// If we're here, then we're not sure which spec should
|
398 |
$spec_list = isset( $rule_spec_list_to_validate[ $id ][ AMP_Rule_Spec::ATTR_SPEC_LIST ] ) ? $rule_spec_list_to_validate[ $id ][ AMP_Rule_Spec::ATTR_SPEC_LIST ] : null;
|
399 |
if ( ! $this->is_missing_mandatory_attribute( $spec_list, $node ) ) {
|
400 |
$attr_spec_list = array_merge( $attr_spec_list, $spec_list );
|
401 |
+
$tag_spec = array_merge(
|
402 |
+
$tag_spec,
|
403 |
+
$rule_spec_list_to_validate[ $id ][ AMP_Rule_Spec::TAG_SPEC ]
|
404 |
+
);
|
405 |
+
if ( isset( $rule_spec_list_to_validate[ $id ][ AMP_Rule_Spec::CDATA ] ) ) {
|
406 |
+
$cdata = array_merge( $cdata, $rule_spec_list_to_validate[ $id ][ AMP_Rule_Spec::CDATA ] );
|
407 |
+
}
|
408 |
}
|
409 |
}
|
410 |
$first_spec = reset( $rule_spec_list_to_validate );
|
419 |
return;
|
420 |
}
|
421 |
|
422 |
+
// Remove element if it has illegal CDATA.
|
423 |
+
if ( ! empty( $cdata ) && $node instanceof DOMElement ) {
|
424 |
+
$validity = $this->validate_cdata_for_node( $node, $cdata );
|
425 |
+
if ( is_wp_error( $validity ) ) {
|
426 |
+
$this->remove_node( $node );
|
427 |
+
return;
|
428 |
+
}
|
429 |
+
}
|
430 |
+
|
431 |
+
$merged_attr_spec_list = array_merge(
|
432 |
+
$this->globally_allowed_attributes,
|
433 |
+
$attr_spec_list
|
434 |
+
);
|
435 |
+
|
436 |
+
// Identify any remaining disallowed attributes.
|
437 |
+
$disallowed_attributes = $this->get_disallowed_attributes_in_node( $node, $merged_attr_spec_list );
|
438 |
+
|
439 |
+
// Identify attribute values that don't conform to the attr_spec.
|
440 |
+
$disallowed_attributes = $this->sanitize_disallowed_attribute_values_in_node( $node, $merged_attr_spec_list, $disallowed_attributes );
|
441 |
+
|
442 |
+
// If $disallowed_attributes is false then the entire element should be removed.
|
443 |
+
if ( false === $disallowed_attributes ) {
|
444 |
+
$this->remove_node( $node );
|
445 |
+
return;
|
446 |
+
}
|
447 |
+
|
448 |
+
// Remove all invalid attributes.
|
449 |
+
foreach ( $disallowed_attributes as $disallowed_attribute ) {
|
450 |
+
$this->remove_invalid_attribute( $node, $disallowed_attribute );
|
451 |
+
}
|
452 |
+
|
453 |
+
// Add required AMP component scripts if the element is still in the document.
|
454 |
+
if ( $node->parentNode ) {
|
455 |
+
if ( ! empty( $tag_spec['also_requires_tag_warning'] ) ) {
|
456 |
+
$this->script_components[] = strtok( $tag_spec['also_requires_tag_warning'][0], ' ' );
|
457 |
+
}
|
458 |
+
if ( ! empty( $tag_spec['requires_extension'] ) ) {
|
459 |
+
$this->script_components = array_merge( $this->script_components, $tag_spec['requires_extension'] );
|
460 |
+
}
|
461 |
|
462 |
+
// Check if element needs amp-bind component.
|
463 |
+
if ( $node instanceof DOMElement && ! in_array( 'amp-bind', $this->script_components, true ) ) {
|
464 |
+
foreach ( $node->attributes as $name => $value ) {
|
465 |
+
$is_bind_attribute = (
|
466 |
+
'[' === $name[0]
|
467 |
+
||
|
468 |
+
( isset( $this->rev_alternate_attr_name_lookup[ $name ] ) && '[' === $this->rev_alternate_attr_name_lookup[ $name ][0] )
|
469 |
+
);
|
470 |
+
if ( $is_bind_attribute ) {
|
471 |
+
$this->script_components[] = 'amp-bind';
|
472 |
+
break;
|
473 |
+
}
|
474 |
+
}
|
475 |
+
}
|
476 |
+
}
|
477 |
}
|
478 |
|
479 |
/**
|
480 |
* Whether a node is missing a mandatory attribute.
|
481 |
*
|
482 |
+
* @param array $attr_spec The attribute specification.
|
483 |
+
* @param DOMElement $node The DOMElement of the node to check.
|
484 |
* @return boolean $is_missing boolean Whether a required attribute is missing.
|
485 |
*/
|
486 |
public function is_missing_mandatory_attribute( $attr_spec, $node ) {
|
488 |
return false;
|
489 |
}
|
490 |
foreach ( $attr_spec as $attr_name => $attr_spec_rule_value ) {
|
491 |
+
if ( '\u' === substr( $attr_name, 0, 2 ) ) {
|
492 |
+
$attr_name = html_entity_decode( '&#x' . substr( $attr_name, 2 ) . ';' ); // Probably ⚡.
|
493 |
+
}
|
494 |
$is_mandatory = isset( $attr_spec_rule_value[ AMP_Rule_Spec::MANDATORY ] ) ? ( true === $attr_spec_rule_value[ AMP_Rule_Spec::MANDATORY ] ) : false;
|
495 |
+
$attribute_exists = false;
|
496 |
+
if ( method_exists( $node, 'hasAttribute' ) ) {
|
497 |
+
$attribute_exists = $node->hasAttribute( $attr_name );
|
498 |
+
if ( ! $attribute_exists && ! empty( $attr_spec_rule_value[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
|
499 |
+
foreach ( $attr_spec_rule_value[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_attr_name ) {
|
500 |
+
if ( $node->hasAttribute( $alternative_attr_name ) ) {
|
501 |
+
$attribute_exists = true;
|
502 |
+
break;
|
503 |
+
}
|
504 |
+
}
|
505 |
+
}
|
506 |
+
}
|
507 |
if ( $is_mandatory && ! $attribute_exists ) {
|
508 |
return true;
|
509 |
}
|
511 |
return false;
|
512 |
}
|
513 |
|
514 |
+
/**
|
515 |
+
* Validate element for its CDATA.
|
516 |
+
*
|
517 |
+
* @since 0.7
|
518 |
+
*
|
519 |
+
* @param DOMElement $element Element.
|
520 |
+
* @param array $cdata_spec CDATA.
|
521 |
+
* @return true|WP_Error True when valid or error when invalid.
|
522 |
+
*/
|
523 |
+
private function validate_cdata_for_node( $element, $cdata_spec ) {
|
524 |
+
if ( isset( $cdata_spec['blacklisted_cdata_regex'] ) ) {
|
525 |
+
if ( preg_match( '@' . $cdata_spec['blacklisted_cdata_regex']['regex'] . '@u', $element->textContent ) ) {
|
526 |
+
return new WP_Error( $cdata_spec['blacklisted_cdata_regex']['error_message'] );
|
527 |
+
}
|
528 |
+
}
|
529 |
+
return true;
|
530 |
+
}
|
531 |
+
|
532 |
/**
|
533 |
* Determines is a node is currently valid per its tag specification.
|
534 |
*
|
539 |
*
|
540 |
* @since 0.5
|
541 |
*
|
542 |
+
* @param DOMNode $node The node to validate.
|
543 |
+
* @param array $tag_spec The specification.
|
544 |
* @return boolean $valid Whether the node's placement is valid.
|
545 |
*/
|
546 |
private function validate_tag_spec_for_node( $node, $tag_spec ) {
|
547 |
+
|
548 |
if ( ! empty( $tag_spec[ AMP_Rule_Spec::MANDATORY_PARENT ] ) && ! $this->has_parent( $node, $tag_spec[ AMP_Rule_Spec::MANDATORY_PARENT ] ) ) {
|
549 |
return false;
|
550 |
}
|
551 |
|
552 |
+
// Extension scripts must be in the head.
|
553 |
+
if ( isset( $tag_spec['extension_spec'] ) && ! $this->has_parent( $node, 'head' ) ) {
|
554 |
+
return false;
|
555 |
+
}
|
556 |
+
|
557 |
if ( ! empty( $tag_spec[ AMP_Rule_Spec::DISALLOWED_ANCESTOR ] ) ) {
|
558 |
foreach ( $tag_spec[ AMP_Rule_Spec::DISALLOWED_ANCESTOR ] as $disallowed_ancestor_node_name ) {
|
559 |
if ( $this->has_ancestor( $node, $disallowed_ancestor_node_name ) ) {
|
579 |
* @param DOMNode $node Node.
|
580 |
* @param array[] $attr_spec_list Attribute Spec list.
|
581 |
*
|
582 |
+
* @return float Number of times the attribute spec list matched. If there was a mismatch, then 0 is returned. 0.5 is returned if there is an implicit match.
|
583 |
*/
|
584 |
private function validate_attr_spec_list_for_node( $node, $attr_spec_list ) {
|
585 |
+
/*
|
586 |
+
* If node has no attributes there is no point in continuing, but if none of attributes
|
587 |
+
* in the spec list are mandatory, then we give this a score.
|
588 |
*/
|
589 |
if ( ! $node->hasAttributes() ) {
|
590 |
+
foreach ( $attr_spec_list as $attr_name => $attr_spec_rule ) {
|
591 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::MANDATORY ] ) ) {
|
592 |
+
return 0;
|
593 |
+
}
|
594 |
+
}
|
595 |
+
return 0.5;
|
596 |
+
}
|
597 |
+
|
598 |
+
if ( ! $node instanceof DOMElement ) {
|
599 |
+
/*
|
600 |
+
* A DOMNode is not valid for checks so might
|
601 |
+
* as well bail here is not an DOMElement.
|
602 |
+
*/
|
603 |
return 0;
|
604 |
}
|
605 |
|
612 |
}
|
613 |
}
|
614 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
615 |
$score = 0;
|
616 |
|
617 |
+
/*
|
618 |
+
* Keep track of how many of the attribute spec rules are mandatory,
|
619 |
+
* because if none are mandatory, then we will let this rule have a
|
620 |
+
* score since all the invalid attributes can just be removed.
|
621 |
+
*/
|
622 |
+
$mandatory_count = 0;
|
623 |
+
|
624 |
/*
|
625 |
* Iterate through each attribute rule in this attr spec list and run
|
626 |
* the series of tests. Each filter is given a `$node`, an `$attr_name`,
|
629 |
*/
|
630 |
foreach ( $attr_spec_list as $attr_name => $attr_spec_rule ) {
|
631 |
|
632 |
+
// If attr spec rule is empty, then it allows anything.
|
633 |
+
if ( empty( $attr_spec_rule ) && $node->hasAttribute( $attr_name ) ) {
|
634 |
+
$score++;
|
635 |
+
continue;
|
636 |
+
}
|
637 |
+
|
638 |
// If a mandatory attribute is required, and attribute exists, pass.
|
639 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::MANDATORY ] ) ) {
|
640 |
+
$mandatory_count++;
|
641 |
+
$result = $this->check_attr_spec_rule_mandatory( $node, $attr_name, $attr_spec_rule );
|
642 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
643 |
$score++;
|
644 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
645 |
+
return 0;
|
646 |
}
|
647 |
}
|
648 |
|
651 |
* Given attribute's value must exactly equal value of the rule to pass.
|
652 |
*/
|
653 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE ] ) ) {
|
654 |
+
$result = $this->check_attr_spec_rule_value( $node, $attr_name, $attr_spec_rule );
|
655 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
656 |
$score++;
|
657 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
658 |
+
return 0;
|
659 |
}
|
660 |
}
|
661 |
|
665 |
* specified by the value of rule to pass.
|
666 |
*/
|
667 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX ] ) ) {
|
668 |
+
$result = $this->check_attr_spec_rule_value_regex( $node, $attr_name, $attr_spec_rule );
|
669 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
670 |
$score++;
|
671 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
672 |
+
return 0;
|
673 |
}
|
674 |
}
|
675 |
|
679 |
* the rule to pass.
|
680 |
*/
|
681 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_CASEI ] ) ) {
|
682 |
+
$result = $this->check_attr_spec_rule_value_casei( $node, $attr_name, $attr_spec_rule );
|
683 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
684 |
$score++;
|
685 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
686 |
+
return 0;
|
687 |
}
|
688 |
}
|
689 |
|
693 |
* pattern specified by the value of the rule to pass.
|
694 |
*/
|
695 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX_CASEI ] ) ) {
|
696 |
+
$result = $this->check_attr_spec_rule_value_regex_casei( $node, $attr_name, $attr_spec_rule );
|
697 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
698 |
$score++;
|
699 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
700 |
+
return 0;
|
701 |
}
|
702 |
}
|
703 |
|
705 |
* If given attribute's value is a URL with a protocol, the protocol must
|
706 |
* be in the array specified by the rule's value to pass.
|
707 |
*/
|
708 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOWED_PROTOCOL ] ) ) {
|
709 |
+
$result = $this->check_attr_spec_rule_allowed_protocol( $node, $attr_name, $attr_spec_rule );
|
710 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
711 |
+
$score++;
|
712 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
713 |
+
return 0;
|
714 |
+
}
|
715 |
+
}
|
716 |
+
|
717 |
+
/*
|
718 |
+
* If given attribute's value is a URL with a host, the host must
|
719 |
+
* be valid
|
720 |
+
*/
|
721 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ] ) ) {
|
722 |
+
$result = $this->check_attr_spec_rule_valid_url( $node, $attr_name, $attr_spec_rule );
|
723 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
724 |
$score++;
|
725 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
726 |
+
return 0;
|
727 |
}
|
728 |
}
|
729 |
|
731 |
* If the given attribute's value is *not* a relative path, and the rule's
|
732 |
* value is `false`, then pass.
|
733 |
*/
|
734 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOW_RELATIVE ] ) ) {
|
735 |
+
$result = $this->check_attr_spec_rule_disallowed_relative( $node, $attr_name, $attr_spec_rule );
|
736 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
737 |
$score++;
|
738 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
739 |
+
return 0;
|
740 |
}
|
741 |
}
|
742 |
|
744 |
* If the given attribute's value exists, is non-empty and the rule's value
|
745 |
* is false, then pass.
|
746 |
*/
|
747 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOW_EMPTY ] ) ) {
|
748 |
+
$result = $this->check_attr_spec_rule_disallowed_empty( $node, $attr_name, $attr_spec_rule );
|
749 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
750 |
$score++;
|
751 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
752 |
+
return 0;
|
753 |
}
|
754 |
}
|
755 |
|
758 |
* of domains in the value of the rule, then pass.
|
759 |
*/
|
760 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::DISALLOWED_DOMAIN ] ) ) {
|
761 |
+
$result = $this->check_attr_spec_rule_disallowed_domain( $node, $attr_name, $attr_spec_rule );
|
762 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
763 |
$score++;
|
764 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
765 |
+
return 0;
|
766 |
}
|
767 |
}
|
768 |
|
771 |
* by the rule's value, then pass.
|
772 |
*/
|
773 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::BLACKLISTED_VALUE_REGEX ] ) ) {
|
774 |
+
$result = $this->check_attr_spec_rule_blacklisted_value_regex( $node, $attr_name, $attr_spec_rule );
|
775 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
776 |
$score++;
|
777 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
778 |
+
return 0;
|
779 |
}
|
780 |
}
|
781 |
+
|
782 |
+
// If the attribute's value exists and it matches the value properties spec.
|
783 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_PROPERTIES ] ) && $node->hasAttribute( $attr_name ) ) {
|
784 |
+
$result = $this->check_attr_spec_rule_value_properties( $node, $attr_name, $attr_spec_rule );
|
785 |
+
if ( AMP_Rule_Spec::PASS === $result ) {
|
786 |
+
$score++;
|
787 |
+
} elseif ( AMP_Rule_Spec::FAIL === $result ) {
|
788 |
+
return 0;
|
789 |
+
}
|
790 |
+
}
|
791 |
+
}
|
792 |
+
|
793 |
+
// Give the spec a score if it doesn't have any mandatory attributes.
|
794 |
+
if ( 0 === $mandatory_count && 0 === $score ) {
|
795 |
+
$score = 0.5;
|
796 |
}
|
797 |
|
798 |
return $score;
|
803 |
*
|
804 |
* @param DOMNode $node Node.
|
805 |
* @param array[] $attr_spec_list Attribute spec list.
|
806 |
+
* @return DOMAttr[] Attributes to remove.
|
807 |
*/
|
808 |
+
private function get_disallowed_attributes_in_node( $node, $attr_spec_list ) {
|
809 |
|
810 |
if ( ! $node instanceof DOMElement ) {
|
811 |
/**
|
812 |
* If $node is only a DOMNode and not a DOMElement we can't
|
813 |
* remove an attribute from it anyway. So bail out now.
|
814 |
*/
|
815 |
+
return array();
|
816 |
}
|
817 |
|
818 |
/*
|
823 |
$attrs_to_remove = array();
|
824 |
foreach ( $node->attributes as $attr_name => $attr_node ) {
|
825 |
if ( ! $this->is_amp_allowed_attribute( $attr_name, $attr_spec_list ) ) {
|
826 |
+
$attrs_to_remove[] = $attr_node;
|
|
|
|
|
|
|
827 |
}
|
828 |
}
|
829 |
|
830 |
+
return $attrs_to_remove;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
831 |
}
|
832 |
|
833 |
/**
|
835 |
*
|
836 |
* Allowed values are found $this->globally_allowed_attributes and in parameter $attr_spec_list
|
837 |
*
|
838 |
+
* @param DOMNode $node Node.
|
839 |
+
* @param array[][] $attr_spec_list Attribute spec list.
|
840 |
+
* @param DOMAttr[] $attributes_pending_removal Attributes pending removal.
|
841 |
+
* @return DOMAttr[]|false Attributes to remove, or false if the element itself should be removed.
|
842 |
*/
|
843 |
+
private function sanitize_disallowed_attribute_values_in_node( $node, $attr_spec_list, $attributes_pending_removal ) {
|
844 |
|
845 |
if ( ! $node instanceof DOMElement ) {
|
846 |
/*
|
847 |
* If $node is only a DOMNode and not a DOMElement we can't
|
848 |
* remove an attribute from it anyway. So bail out now.
|
849 |
*/
|
850 |
+
return $attributes_pending_removal;
|
851 |
}
|
852 |
|
853 |
+
return $this->delegated_sanitize_disallowed_attribute_values_in_node(
|
854 |
+
$node,
|
855 |
+
array_merge(
|
856 |
+
$this->globally_allowed_attributes,
|
857 |
+
$attr_spec_list
|
858 |
+
),
|
859 |
+
$attributes_pending_removal
|
860 |
+
);
|
861 |
}
|
862 |
|
863 |
/**
|
865 |
*
|
866 |
* @see $this->sanitize_disallowed_attribute_values_in_node() which delegates to this method
|
867 |
*
|
868 |
+
* @param DOMElement $node Node.
|
869 |
+
* @param array[][] $attr_spec_list Attribute spec list.
|
870 |
+
* @param DOMAttr[] $attributes_pending_removal Attributes pending removal.
|
871 |
+
* @return DOMAttr[]|false Attributes to remove, or false if the element itself should be removed.
|
872 |
*/
|
873 |
+
private function delegated_sanitize_disallowed_attribute_values_in_node( $node, $attr_spec_list, $attributes_pending_removal ) {
|
874 |
$attrs_to_remove = array();
|
875 |
|
876 |
foreach ( $attr_spec_list as $attr_name => $attr_val ) {
|
883 |
|
884 |
foreach ( $node->attributes as $attr_name => $attr_node ) {
|
885 |
|
886 |
+
if ( ! isset( $attr_spec_list[ $attr_name ] ) || in_array( $attr_node, $attributes_pending_removal, true ) ) {
|
887 |
continue;
|
888 |
}
|
889 |
|
897 |
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_value_casei( $node, $attr_name, $attr_spec_rule ) ) {
|
898 |
$should_remove_node = true;
|
899 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX ] ) &&
|
900 |
+
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_value_regex( $node, $attr_name, $attr_spec_rule ) ) {
|
901 |
$should_remove_node = true;
|
902 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX_CASEI ] ) &&
|
903 |
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_value_regex_casei( $node, $attr_name, $attr_spec_rule ) ) {
|
904 |
$should_remove_node = true;
|
905 |
+
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOWED_PROTOCOL ] ) &&
|
906 |
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_allowed_protocol( $node, $attr_name, $attr_spec_rule ) ) {
|
907 |
$should_remove_node = true;
|
908 |
+
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ] ) &&
|
909 |
+
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_valid_url( $node, $attr_name, $attr_spec_rule ) ) {
|
910 |
+
$should_remove_node = true;
|
911 |
+
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOW_RELATIVE ] ) &&
|
912 |
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_disallowed_relative( $node, $attr_name, $attr_spec_rule ) ) {
|
913 |
$should_remove_node = true;
|
914 |
+
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOW_EMPTY ] ) &&
|
915 |
AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_disallowed_empty( $node, $attr_name, $attr_spec_rule ) ) {
|
916 |
$should_remove_node = true;
|
917 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::DISALLOWED_DOMAIN ] ) &&
|
930 |
|
931 |
if ( $is_mandatory ) {
|
932 |
$this->remove_node( $node );
|
933 |
+
return false;
|
934 |
}
|
935 |
|
936 |
+
$attrs_to_remove[] = $attr_node;
|
937 |
}
|
938 |
}
|
939 |
|
940 |
// Remove the disallowed values.
|
941 |
+
foreach ( $attrs_to_remove as $attr_node ) {
|
942 |
+
if ( isset( $attr_spec_list[ $attr_node->nodeName ][ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOW_EMPTY ] ) &&
|
943 |
+
( true === $attr_spec_list[ $attr_node->nodeName ][ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOW_EMPTY ] ) ) {
|
944 |
+
$attr_node->nodeValue = '';
|
945 |
} else {
|
946 |
+
$attributes_pending_removal[] = $attr_node;
|
947 |
}
|
948 |
}
|
949 |
+
|
950 |
+
return $attributes_pending_removal;
|
951 |
}
|
952 |
|
953 |
/**
|
990 |
*
|
991 |
* @param DOMElement $node Node.
|
992 |
* @param string $attr_name Attribute name.
|
993 |
+
* @param array $attr_spec_rule Attribute spec rule.
|
994 |
*
|
995 |
* @return string:
|
996 |
* - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
|
1001 |
private function check_attr_spec_rule_value( $node, $attr_name, $attr_spec_rule ) {
|
1002 |
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE ] ) ) {
|
1003 |
if ( $node->hasAttribute( $attr_name ) ) {
|
1004 |
+
if ( $this->check_matching_attribute_value( $attr_name, $node->getAttribute( $attr_name ), $attr_spec_rule[ AMP_Rule_Spec::VALUE ] ) ) {
|
1005 |
return AMP_Rule_Spec::PASS;
|
1006 |
} else {
|
1007 |
return AMP_Rule_Spec::FAIL;
|
1009 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
|
1010 |
foreach ( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_name ) {
|
1011 |
if ( $node->hasAttribute( $alternative_name ) ) {
|
1012 |
+
if ( $this->check_matching_attribute_value( $attr_name, $node->getAttribute( $alternative_name ), $attr_spec_rule[ AMP_Rule_Spec::VALUE ] ) ) {
|
1013 |
return AMP_Rule_Spec::PASS;
|
1014 |
} else {
|
1015 |
return AMP_Rule_Spec::FAIL;
|
1021 |
return AMP_Rule_Spec::NOT_APPLICABLE;
|
1022 |
}
|
1023 |
|
1024 |
+
/**
|
1025 |
+
* Check that an attribute's value matches is given spec value.
|
1026 |
+
*
|
1027 |
+
* This takes into account boolean attributes where value can match name (e.g. selected="selected").
|
1028 |
+
*
|
1029 |
+
* @since 0.7.0
|
1030 |
+
*
|
1031 |
+
* @param string $attr_name Attribute name.
|
1032 |
+
* @param string $attr_value Attribute value.
|
1033 |
+
* @param string $spec_value Attribute spec value.
|
1034 |
+
* @return bool Is value valid.
|
1035 |
+
*/
|
1036 |
+
private function check_matching_attribute_value( $attr_name, $attr_value, $spec_value ) {
|
1037 |
+
if ( $spec_value === $attr_value ) {
|
1038 |
+
return true;
|
1039 |
+
}
|
1040 |
+
|
1041 |
+
// Check for boolean attribute.
|
1042 |
+
return (
|
1043 |
+
'' === $spec_value
|
1044 |
+
&&
|
1045 |
+
in_array( $attr_name, AMP_Rule_Spec::$boolean_attributes, true )
|
1046 |
+
&&
|
1047 |
+
strtolower( $attr_value ) === strtolower( $attr_name )
|
1048 |
+
);
|
1049 |
+
}
|
1050 |
+
|
1051 |
/**
|
1052 |
* Check if attribute has a value rule determine if its value matches ignoring case.
|
1053 |
*
|
1153 |
return AMP_Rule_Spec::NOT_APPLICABLE;
|
1154 |
}
|
1155 |
|
1156 |
+
/**
|
1157 |
+
* Check if attribute has a valid host value
|
1158 |
+
*
|
1159 |
+
* @since 0.7
|
1160 |
+
*
|
1161 |
+
* @param DOMElement $node Node.
|
1162 |
+
* @param string $attr_name Attribute name.
|
1163 |
+
* @param array[]|string[] $attr_spec_rule Attribute spec rule.
|
1164 |
+
*
|
1165 |
+
* @return string:
|
1166 |
+
* - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
|
1167 |
+
* - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
|
1168 |
+
* - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
|
1169 |
+
* is no rule for this attribute.
|
1170 |
+
*/
|
1171 |
+
private function check_attr_spec_rule_valid_url( $node, $attr_name, $attr_spec_rule ) {
|
1172 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ] ) ) {
|
1173 |
+
if ( $node->hasAttribute( $attr_name ) ) {
|
1174 |
+
$urls_to_test = preg_split( '/\s*,\s*/', $node->getAttribute( $attr_name ) );
|
1175 |
+
foreach ( $urls_to_test as $url ) {
|
1176 |
+
$url = urldecode( $url );
|
1177 |
+
// Check if the host contains invalid chars.
|
1178 |
+
$url_host = wp_parse_url( $url, PHP_URL_HOST );
|
1179 |
+
if ( $url_host && preg_match( '/[!"#$%&\'()*+,\/:;<=>?@[\]^`{|}~\s]/i', $url_host ) ) {
|
1180 |
+
return AMP_Rule_Spec::FAIL;
|
1181 |
+
}
|
1182 |
+
|
1183 |
+
// Check if the protocol contains invalid chars.
|
1184 |
+
$dots_pos = strpos( $url, ':' );
|
1185 |
+
if ( false !== $dots_pos && preg_match( '/[!"#$%&\'()*+,\/:;<=>?@[\]^`{|}~\s]/i', substr( $url, 0, $dots_pos ) ) ) {
|
1186 |
+
return AMP_Rule_Spec::FAIL;
|
1187 |
+
}
|
1188 |
+
}
|
1189 |
+
|
1190 |
+
return AMP_Rule_Spec::PASS;
|
1191 |
+
}
|
1192 |
+
}
|
1193 |
+
|
1194 |
+
return AMP_Rule_Spec::NOT_APPLICABLE;
|
1195 |
+
}
|
1196 |
+
|
1197 |
/**
|
1198 |
* Check if attribute has a protocol value rule determine if it matches.
|
1199 |
*
|
1208 |
* is no rule for this attribute.
|
1209 |
*/
|
1210 |
private function check_attr_spec_rule_allowed_protocol( $node, $attr_name, $attr_spec_rule ) {
|
1211 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOWED_PROTOCOL ] ) ) {
|
1212 |
if ( $node->hasAttribute( $attr_name ) ) {
|
1213 |
+
$urls_to_test = preg_split( '/\s*,\s*/', $node->getAttribute( $attr_name ) );
|
|
|
|
|
1214 |
foreach ( $urls_to_test as $url ) {
|
1215 |
/*
|
1216 |
* This seems to be an acceptable check since the AMP validator
|
1218 |
*/
|
1219 |
$url_scheme = AMP_WP_Utils::parse_url( $url, PHP_URL_SCHEME );
|
1220 |
if ( $url_scheme ) {
|
1221 |
+
if ( ! in_array( strtolower( $url_scheme ), $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOWED_PROTOCOL ], true ) ) {
|
1222 |
return AMP_Rule_Spec::FAIL;
|
1223 |
}
|
1224 |
}
|
1227 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
|
1228 |
foreach ( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_name ) {
|
1229 |
if ( $node->hasAttribute( $alternative_name ) ) {
|
1230 |
+
$urls_to_test = preg_split( '/\s*,\s*/', $node->getAttribute( $alternative_name ) );
|
|
|
|
|
1231 |
foreach ( $urls_to_test as $url ) {
|
1232 |
/*
|
1233 |
* This seems to be an acceptable check since the AMP validator
|
1235 |
*/
|
1236 |
$url_scheme = AMP_WP_Utils::parse_url( $url, PHP_URL_SCHEME );
|
1237 |
if ( $url_scheme ) {
|
1238 |
+
if ( ! in_array( strtolower( $url_scheme ), $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOWED_PROTOCOL ], true ) ) {
|
1239 |
return AMP_Rule_Spec::FAIL;
|
1240 |
}
|
1241 |
}
|
1262 |
* is no rule for this attribute.
|
1263 |
*/
|
1264 |
private function check_attr_spec_rule_disallowed_relative( $node, $attr_name, $attr_spec_rule ) {
|
1265 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOW_RELATIVE ] ) && ! ( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOW_RELATIVE ] ) ) {
|
1266 |
if ( $node->hasAttribute( $attr_name ) ) {
|
1267 |
+
$urls_to_test = preg_split( '/\s*,\s*/', $node->getAttribute( $attr_name ) );
|
|
|
|
|
1268 |
foreach ( $urls_to_test as $url ) {
|
1269 |
$parsed_url = AMP_WP_Utils::parse_url( $url );
|
1270 |
|
1283 |
} elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
|
1284 |
foreach ( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_name ) {
|
1285 |
if ( $node->hasAttribute( $alternative_name ) ) {
|
1286 |
+
$urls_to_test = preg_split( '/\s*,\s*/', $node->getAttribute( $alternative_name ) );
|
|
|
|
|
1287 |
foreach ( $urls_to_test as $url ) {
|
1288 |
$parsed_url = AMP_WP_Utils::parse_url( $url );
|
1289 |
if ( empty( $parsed_url['scheme'] ) ) {
|
1312 |
* is no rule for this attribute.
|
1313 |
*/
|
1314 |
private function check_attr_spec_rule_disallowed_empty( $node, $attr_name, $attr_spec_rule ) {
|
1315 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOW_EMPTY ] ) && ! ( $attr_spec_rule[ AMP_Rule_Spec::VALUE_URL ][ AMP_Rule_Spec::ALLOW_EMPTY ] ) && $node->hasAttribute( $attr_name ) ) {
|
1316 |
$attr_value = $node->getAttribute( $attr_name );
|
1317 |
if ( empty( $attr_value ) ) {
|
1318 |
return AMP_Rule_Spec::FAIL;
|
1394 |
return AMP_Rule_Spec::NOT_APPLICABLE;
|
1395 |
}
|
1396 |
|
1397 |
+
/**
|
1398 |
+
* Check if attribute has valid properties.
|
1399 |
+
*
|
1400 |
+
* @since 0.7
|
1401 |
+
*
|
1402 |
+
* @param DOMElement $node Node.
|
1403 |
+
* @param string $attr_name Attribute name.
|
1404 |
+
* @param array[]|string[] $attr_spec_rule Attribute spec rule.
|
1405 |
+
*
|
1406 |
+
* @return string:
|
1407 |
+
* - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
|
1408 |
+
* - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
|
1409 |
+
* - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
|
1410 |
+
* is no rule for this attribute.
|
1411 |
+
*/
|
1412 |
+
private function check_attr_spec_rule_value_properties( $node, $attr_name, $attr_spec_rule ) {
|
1413 |
+
if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_PROPERTIES ] ) && $node->hasAttribute( $attr_name ) ) {
|
1414 |
+
$properties = array();
|
1415 |
+
foreach ( explode( ',', $node->getAttribute( $attr_name ) ) as $pair ) {
|
1416 |
+
$pair_parts = explode( '=', $pair, 2 );
|
1417 |
+
if ( 2 !== count( $pair_parts ) ) {
|
1418 |
+
return 0;
|
1419 |
+
}
|
1420 |
+
$properties[ strtolower( $pair_parts[0] ) ] = $pair_parts[1];
|
1421 |
+
}
|
1422 |
+
|
1423 |
+
// Fail if there are unrecognized properties.
|
1424 |
+
if ( count( array_diff( array_keys( $properties ), array_keys( $attr_spec_rule[ AMP_Rule_Spec::VALUE_PROPERTIES ] ) ) ) > 0 ) {
|
1425 |
+
return AMP_Rule_Spec::FAIL;
|
1426 |
+
}
|
1427 |
+
|
1428 |
+
foreach ( $attr_spec_rule[ AMP_Rule_Spec::VALUE_PROPERTIES ] as $prop_name => $property_spec ) {
|
1429 |
+
|
1430 |
+
// Mandatory property is missing.
|
1431 |
+
if ( ! empty( $property_spec['mandatory'] ) && ! isset( $properties[ $prop_name ] ) ) {
|
1432 |
+
return AMP_Rule_Spec::FAIL;
|
1433 |
+
}
|
1434 |
+
|
1435 |
+
if ( ! isset( $properties[ $prop_name ] ) ) {
|
1436 |
+
continue;
|
1437 |
+
}
|
1438 |
+
|
1439 |
+
$prop_value = $properties[ $prop_name ];
|
1440 |
+
|
1441 |
+
// Required value is absent, so fail.
|
1442 |
+
$required_value = null;
|
1443 |
+
if ( isset( $property_spec['value'] ) ) {
|
1444 |
+
$required_value = $property_spec['value'];
|
1445 |
+
} elseif ( isset( $property_spec['value_double'] ) ) {
|
1446 |
+
$required_value = $property_spec['value_double'];
|
1447 |
+
$prop_value = (double) $prop_value;
|
1448 |
+
}
|
1449 |
+
if ( isset( $required_value ) && $prop_value !== $required_value ) {
|
1450 |
+
return AMP_Rule_Spec::FAIL;
|
1451 |
+
}
|
1452 |
+
}
|
1453 |
+
return AMP_Rule_Spec::PASS;
|
1454 |
+
}
|
1455 |
+
return AMP_Rule_Spec::NOT_APPLICABLE;
|
1456 |
+
}
|
1457 |
+
|
1458 |
/**
|
1459 |
* Determine if the supplied attribute name is allowed for AMP.
|
1460 |
*
|
1474 |
}
|
1475 |
}
|
1476 |
}
|
1477 |
+
|
1478 |
+
$is_allowed_alt_name_attr = (
|
1479 |
+
isset( $this->rev_alternate_attr_name_lookup[ $attr_name ] )
|
1480 |
+
&&
|
1481 |
+
isset( $attr_spec_list[ $this->rev_alternate_attr_name_lookup[ $attr_name ] ] )
|
1482 |
+
);
|
1483 |
+
if ( $is_allowed_alt_name_attr ) {
|
1484 |
+
return true;
|
1485 |
+
}
|
1486 |
+
|
1487 |
return false;
|
1488 |
}
|
1489 |
|
1531 |
*
|
1532 |
* @since 0.5
|
1533 |
*
|
1534 |
+
* @todo The $ancestor_tag_name here is not sufficient as it is not just a tag name but an entire selector that is used.
|
1535 |
* @param DOMNode $node Node.
|
1536 |
* @param string $ancestor_tag_name Ancestor tag name.
|
1537 |
* @return bool Return true if given node has any ancestor with the give name, false otherwise.
|
1617 |
*/
|
1618 |
$parent = $node->parentNode;
|
1619 |
if ( $node && $parent ) {
|
1620 |
+
$this->remove_invalid_child( $node );
|
1621 |
}
|
1622 |
+
while ( $parent && ! $parent->hasChildNodes() && $this->root_element !== $parent ) {
|
1623 |
$node = $parent;
|
1624 |
$parent = $parent->parentNode;
|
1625 |
if ( $parent ) {
|
1628 |
}
|
1629 |
}
|
1630 |
}
|
|
@@ -32,42 +32,6 @@ class AMP_Video_Sanitizer extends AMP_Base_Sanitizer {
|
|
32 |
*/
|
33 |
public static $tag = 'video';
|
34 |
|
35 |
-
/**
|
36 |
-
* Script tag.
|
37 |
-
*
|
38 |
-
* @var string AMP HTML tag to use in place of HTML's <video> tag.
|
39 |
-
*
|
40 |
-
* @since 0.2
|
41 |
-
*/
|
42 |
-
private static $script_slug = 'amp-video';
|
43 |
-
|
44 |
-
/**
|
45 |
-
* Script src.
|
46 |
-
*
|
47 |
-
* @var string URL to AMP Project's Video element's JavaScript file found at cdn.ampproject.org
|
48 |
-
*
|
49 |
-
* @since 0.2
|
50 |
-
*/
|
51 |
-
private static $script_src = 'https://cdn.ampproject.org/v0/amp-video-0.1.js';
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Return one element array containing AMP HTML video tag and respective Javascript URL
|
55 |
-
*
|
56 |
-
* HTML tags and Javascript URLs found at cdn.ampproject.org
|
57 |
-
*
|
58 |
-
* @since 0.2
|
59 |
-
*
|
60 |
-
* @return string[] Returns AMP HTML video tag as array key and Javascript URL as array value,
|
61 |
-
* respectively. Will return an empty array if sanitization has yet to be run
|
62 |
-
* or if it did not find any HTML video elements to convert to AMP equivalents.
|
63 |
-
*/
|
64 |
-
public function get_scripts() {
|
65 |
-
if ( ! $this->did_convert_elements ) {
|
66 |
-
return array();
|
67 |
-
}
|
68 |
-
return array( self::$script_slug => self::$script_src );
|
69 |
-
}
|
70 |
-
|
71 |
/**
|
72 |
* Sanitize the <video> elements from the HTML contained in this instance's DOMDocument.
|
73 |
*
|
@@ -85,9 +49,10 @@ class AMP_Video_Sanitizer extends AMP_Base_Sanitizer {
|
|
85 |
$old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
|
86 |
|
87 |
$new_attributes = $this->filter_attributes( $old_attributes );
|
88 |
-
|
89 |
-
$new_attributes
|
90 |
-
|
|
|
91 |
|
92 |
$new_node = AMP_DOM_Utils::create_node( $this->dom, 'amp-video', $new_attributes );
|
93 |
|
@@ -129,7 +94,7 @@ class AMP_Video_Sanitizer extends AMP_Base_Sanitizer {
|
|
129 |
* See: https://github.com/ampproject/amphtml/issues/2261
|
130 |
*/
|
131 |
if ( 0 === $new_node->childNodes->length && empty( $new_attributes['src'] ) ) {
|
132 |
-
$
|
133 |
} else {
|
134 |
$node->parentNode->replaceChild( $new_node, $node );
|
135 |
}
|
32 |
*/
|
33 |
public static $tag = 'video';
|
34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
/**
|
36 |
* Sanitize the <video> elements from the HTML contained in this instance's DOMDocument.
|
37 |
*
|
49 |
$old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
|
50 |
|
51 |
$new_attributes = $this->filter_attributes( $old_attributes );
|
52 |
+
$new_attributes = $this->set_layout( $new_attributes );
|
53 |
+
if ( empty( $new_attributes['layout'] ) && ! empty( $new_attributes['width'] ) && ! empty( $new_attributes['height'] ) ) {
|
54 |
+
$new_attributes['layout'] = 'responsive';
|
55 |
+
}
|
56 |
|
57 |
$new_node = AMP_DOM_Utils::create_node( $this->dom, 'amp-video', $new_attributes );
|
58 |
|
94 |
* See: https://github.com/ampproject/amphtml/issues/2261
|
95 |
*/
|
96 |
if ( 0 === $new_node->childNodes->length && empty( $new_attributes['src'] ) ) {
|
97 |
+
$this->remove_invalid_child( $node );
|
98 |
} else {
|
99 |
$node->parentNode->replaceChild( $new_node, $node );
|
100 |
}
|
@@ -7,31 +7,74 @@
|
|
7 |
|
8 |
/**
|
9 |
* Class AMP_Content_Sanitizer
|
|
|
|
|
10 |
*/
|
11 |
class AMP_Content_Sanitizer {
|
12 |
|
13 |
/**
|
14 |
-
* Sanitize.
|
|
|
|
|
|
|
15 |
*
|
16 |
-
* @param string $content
|
17 |
* @param string[] $sanitizer_classes Sanitizer classes.
|
18 |
* @param array $global_args Global args.
|
19 |
-
*
|
20 |
-
* @return array
|
21 |
*/
|
22 |
public static function sanitize( $content, array $sanitizer_classes, $global_args = array() ) {
|
23 |
-
$
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
if ( ! class_exists( $sanitizer_class ) ) {
|
29 |
/* translators: %s is sanitizer class */
|
30 |
_doing_it_wrong( __METHOD__, sprintf( esc_html__( 'Sanitizer (%s) class does not exist', 'amp' ), esc_html( $sanitizer_class ) ), '0.4.1' );
|
31 |
continue;
|
32 |
}
|
33 |
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
if ( ! is_subclass_of( $sanitizer, 'AMP_Base_Sanitizer' ) ) {
|
37 |
/* translators: %s is sanitizer class */
|
@@ -42,12 +85,14 @@ class AMP_Content_Sanitizer {
|
|
42 |
$sanitizer->sanitize();
|
43 |
|
44 |
$scripts = array_merge( $scripts, $sanitizer->get_scripts() );
|
45 |
-
|
|
|
|
|
|
|
|
|
46 |
}
|
47 |
|
48 |
-
|
49 |
-
|
50 |
-
return array( $sanitized_content, $scripts, $styles );
|
51 |
}
|
52 |
}
|
53 |
|
7 |
|
8 |
/**
|
9 |
* Class AMP_Content_Sanitizer
|
10 |
+
*
|
11 |
+
* @since 0.4.1
|
12 |
*/
|
13 |
class AMP_Content_Sanitizer {
|
14 |
|
15 |
/**
|
16 |
+
* Sanitize _content_.
|
17 |
+
*
|
18 |
+
* @since 0.4.1
|
19 |
+
* @since 0.7 Passing return_styles=false in $global_args causes stylesheets to be returned instead of styles.
|
20 |
*
|
21 |
+
* @param string $content HTML content string or DOM document.
|
22 |
* @param string[] $sanitizer_classes Sanitizer classes.
|
23 |
* @param array $global_args Global args.
|
24 |
+
* @return array Tuple containing sanitized HTML, scripts array, and styles array (or stylesheets, if return_styles=false is passed in $global_args).
|
|
|
25 |
*/
|
26 |
public static function sanitize( $content, array $sanitizer_classes, $global_args = array() ) {
|
27 |
+
$dom = AMP_DOM_Utils::get_dom_from_content( $content );
|
28 |
+
|
29 |
+
// For back-compat.
|
30 |
+
if ( ! isset( $global_args['return_styles'] ) ) {
|
31 |
+
$global_args['return_styles'] = true;
|
32 |
+
}
|
33 |
+
|
34 |
+
$results = self::sanitize_document( $dom, $sanitizer_classes, $global_args );
|
35 |
+
return array(
|
36 |
+
AMP_DOM_Utils::get_content_from_dom( $dom ),
|
37 |
+
$results['scripts'],
|
38 |
+
empty( $global_args['return_styles'] ) ? $results['stylesheets'] : $results['styles'],
|
39 |
+
);
|
40 |
+
}
|
41 |
|
42 |
+
/**
|
43 |
+
* Sanitize document.
|
44 |
+
*
|
45 |
+
* @since 0.7
|
46 |
+
*
|
47 |
+
* @param DOMDocument $dom HTML document.
|
48 |
+
* @param string[] $sanitizer_classes Sanitizer classes.
|
49 |
+
* @param array $args Global args passed into sanitizers.
|
50 |
+
* @return array {
|
51 |
+
* Scripts and stylesheets needed by sanitizers.
|
52 |
+
*
|
53 |
+
* @type array $scripts Scripts.
|
54 |
+
* @type array $stylesheets Stylesheets. If $args['return_styles'] is empty.
|
55 |
+
* @type array $styles Styles. If $args['return_styles'] is not empty. For legacy purposes.
|
56 |
+
* }
|
57 |
+
*/
|
58 |
+
public static function sanitize_document( &$dom, $sanitizer_classes, $args ) {
|
59 |
+
$scripts = array();
|
60 |
+
$stylesheets = array();
|
61 |
+
$styles = array();
|
62 |
+
|
63 |
+
$return_styles = ! empty( $args['return_styles'] );
|
64 |
+
unset( $args['return_styles'] );
|
65 |
+
foreach ( $sanitizer_classes as $sanitizer_class => $sanitizer_args ) {
|
66 |
if ( ! class_exists( $sanitizer_class ) ) {
|
67 |
/* translators: %s is sanitizer class */
|
68 |
_doing_it_wrong( __METHOD__, sprintf( esc_html__( 'Sanitizer (%s) class does not exist', 'amp' ), esc_html( $sanitizer_class ) ), '0.4.1' );
|
69 |
continue;
|
70 |
}
|
71 |
|
72 |
+
/**
|
73 |
+
* Sanitizer.
|
74 |
+
*
|
75 |
+
* @type AMP_Base_Sanitizer $sanitizer
|
76 |
+
*/
|
77 |
+
$sanitizer = new $sanitizer_class( $dom, array_merge( $args, $sanitizer_args ) );
|
78 |
|
79 |
if ( ! is_subclass_of( $sanitizer, 'AMP_Base_Sanitizer' ) ) {
|
80 |
/* translators: %s is sanitizer class */
|
85 |
$sanitizer->sanitize();
|
86 |
|
87 |
$scripts = array_merge( $scripts, $sanitizer->get_scripts() );
|
88 |
+
if ( $return_styles ) {
|
89 |
+
$styles = array_merge( $styles, $sanitizer->get_styles() );
|
90 |
+
} else {
|
91 |
+
$stylesheets = array_merge( $stylesheets, $sanitizer->get_stylesheets() );
|
92 |
+
}
|
93 |
}
|
94 |
|
95 |
+
return compact( 'scripts', 'styles', 'stylesheets' );
|
|
|
|
|
96 |
}
|
97 |
}
|
98 |
|
@@ -137,17 +137,7 @@ class AMP_Post_Template {
|
|
137 |
|
138 |
'post_amp_styles' => array(),
|
139 |
|
140 |
-
|
141 |
-
* Add amp-analytics tags.
|
142 |
-
*
|
143 |
-
* This filter allows you to easily insert any amp-analytics tags without needing much heavy lifting.
|
144 |
-
*
|
145 |
-
* @since 0.4
|
146 |
-
*
|
147 |
-
* @param array $analytics An associative array of the analytics entries we want to output. Each array entry must have a unique key, and the value should be an array with the following keys: `type`, `attributes`, `script_data`. See readme for more details.
|
148 |
-
* @param WP_Post $post The current post.
|
149 |
-
*/
|
150 |
-
'amp_analytics' => apply_filters( 'amp_post_template_analytics', array(), $this->post ),
|
151 |
);
|
152 |
|
153 |
$this->build_post_content();
|
@@ -287,42 +277,6 @@ class AMP_Post_Template {
|
|
287 |
)
|
288 |
);
|
289 |
|
290 |
-
$metadata = array(
|
291 |
-
'@context' => 'http://schema.org',
|
292 |
-
'@type' => is_page() ? 'WebPage' : 'BlogPosting',
|
293 |
-
'mainEntityOfPage' => $this->get( 'canonical_url' ),
|
294 |
-
'publisher' => array(
|
295 |
-
'@type' => 'Organization',
|
296 |
-
'name' => $this->get( 'blog_name' ),
|
297 |
-
),
|
298 |
-
'headline' => $post_title,
|
299 |
-
'datePublished' => date( 'c', $post_publish_timestamp ),
|
300 |
-
'dateModified' => date( 'c', $post_modified_timestamp ),
|
301 |
-
);
|
302 |
-
if ( $post_author ) {
|
303 |
-
$metadata['author'] = array(
|
304 |
-
'@type' => 'Person',
|
305 |
-
'name' => html_entity_decode( $post_author->display_name, ENT_QUOTES, get_bloginfo( 'charset' ) ),
|
306 |
-
);
|
307 |
-
}
|
308 |
-
|
309 |
-
$site_icon_url = $this->get( 'site_icon_url' );
|
310 |
-
if ( $site_icon_url ) {
|
311 |
-
$metadata['publisher']['logo'] = array(
|
312 |
-
'@type' => 'ImageObject',
|
313 |
-
'url' => $site_icon_url,
|
314 |
-
'height' => self::SITE_ICON_SIZE,
|
315 |
-
'width' => self::SITE_ICON_SIZE,
|
316 |
-
);
|
317 |
-
}
|
318 |
-
|
319 |
-
$image_metadata = $this->get_post_image_metadata();
|
320 |
-
if ( $image_metadata ) {
|
321 |
-
$metadata['image'] = $image_metadata;
|
322 |
-
}
|
323 |
-
|
324 |
-
$this->add_data_by_key( 'metadata', apply_filters( 'amp_post_template_metadata', $metadata, $this->post ) );
|
325 |
-
|
326 |
$this->build_post_featured_image();
|
327 |
$this->build_post_commments_data();
|
328 |
}
|
@@ -362,34 +316,8 @@ class AMP_Post_Template {
|
|
362 |
private function build_post_content() {
|
363 |
$amp_content = new AMP_Content(
|
364 |
$this->post->post_content,
|
365 |
-
|
366 |
-
|
367 |
-
'AMP_Twitter_Embed_Handler' => array(),
|
368 |
-
'AMP_YouTube_Embed_Handler' => array(),
|
369 |
-
'AMP_DailyMotion_Embed_Handler' => array(),
|
370 |
-
'AMP_Vimeo_Embed_Handler' => array(),
|
371 |
-
'AMP_SoundCloud_Embed_Handler' => array(),
|
372 |
-
'AMP_Instagram_Embed_Handler' => array(),
|
373 |
-
'AMP_Vine_Embed_Handler' => array(),
|
374 |
-
'AMP_Facebook_Embed_Handler' => array(),
|
375 |
-
'AMP_Pinterest_Embed_Handler' => array(),
|
376 |
-
'AMP_Gallery_Embed_Handler' => array(),
|
377 |
-
'WPCOM_AMP_Polldaddy_Embed' => array(),
|
378 |
-
), $this->post
|
379 |
-
),
|
380 |
-
apply_filters(
|
381 |
-
'amp_content_sanitizers', array(
|
382 |
-
'AMP_Style_Sanitizer' => array(),
|
383 |
-
'AMP_Img_Sanitizer' => array(),
|
384 |
-
'AMP_Video_Sanitizer' => array(),
|
385 |
-
'AMP_Audio_Sanitizer' => array(),
|
386 |
-
'AMP_Playbuzz_Sanitizer' => array(),
|
387 |
-
'AMP_Iframe_Sanitizer' => array(
|
388 |
-
'add_placeholder' => true,
|
389 |
-
),
|
390 |
-
'AMP_Tag_And_Attribute_Sanitizer' => array(), // Note: This whitelist sanitizer must come at the end to clean up any remaining issues the other sanitizers didn't catch.
|
391 |
-
), $this->post
|
392 |
-
),
|
393 |
array(
|
394 |
'content_max_width' => $this->get( 'content_max_width' ),
|
395 |
)
|
@@ -427,7 +355,7 @@ class AMP_Post_Template {
|
|
427 |
|
428 |
list( $sanitized_html, $featured_scripts, $featured_styles ) = AMP_Content_Sanitizer::sanitize(
|
429 |
$featured_html,
|
430 |
-
|
431 |
array(
|
432 |
'content_max_width' => $this->get( 'content_max_width' ),
|
433 |
)
|
@@ -472,54 +400,6 @@ class AMP_Post_Template {
|
|
472 |
$this->add_data_by_key( 'customizer_settings', apply_filters( 'amp_post_template_customizer_settings', $settings, $this->post ) );
|
473 |
}
|
474 |
|
475 |
-
/**
|
476 |
-
* Grabs featured image or the first attached image for the post
|
477 |
-
*
|
478 |
-
* TODO: move to a utils class?
|
479 |
-
*/
|
480 |
-
private function get_post_image_metadata() {
|
481 |
-
$post_image_meta = null;
|
482 |
-
$post_image_id = false;
|
483 |
-
|
484 |
-
if ( has_post_thumbnail( $this->ID ) ) {
|
485 |
-
$post_image_id = get_post_thumbnail_id( $this->ID );
|
486 |
-
} else {
|
487 |
-
$attached_image_ids = get_posts(
|
488 |
-
array(
|
489 |
-
'post_parent' => $this->ID,
|
490 |
-
'post_type' => 'attachment',
|
491 |
-
'post_mime_type' => 'image',
|
492 |
-
'posts_per_page' => 1,
|
493 |
-
'orderby' => 'menu_order',
|
494 |
-
'order' => 'ASC',
|
495 |
-
'fields' => 'ids',
|
496 |
-
'suppress_filters' => false,
|
497 |
-
)
|
498 |
-
);
|
499 |
-
|
500 |
-
if ( ! empty( $attached_image_ids ) ) {
|
501 |
-
$post_image_id = array_shift( $attached_image_ids );
|
502 |
-
}
|
503 |
-
}
|
504 |
-
|
505 |
-
if ( ! $post_image_id ) {
|
506 |
-
return false;
|
507 |
-
}
|
508 |
-
|
509 |
-
$post_image_src = wp_get_attachment_image_src( $post_image_id, 'full' );
|
510 |
-
|
511 |
-
if ( is_array( $post_image_src ) ) {
|
512 |
-
$post_image_meta = array(
|
513 |
-
'@type' => 'ImageObject',
|
514 |
-
'url' => $post_image_src[0],
|
515 |
-
'width' => $post_image_src[1],
|
516 |
-
'height' => $post_image_src[2],
|
517 |
-
);
|
518 |
-
}
|
519 |
-
|
520 |
-
return $post_image_meta;
|
521 |
-
}
|
522 |
-
|
523 |
/**
|
524 |
* Build HTML tag attributes.
|
525 |
*/
|
137 |
|
138 |
'post_amp_styles' => array(),
|
139 |
|
140 |
+
'amp_analytics' => amp_add_custom_analytics(),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
);
|
142 |
|
143 |
$this->build_post_content();
|
277 |
)
|
278 |
);
|
279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
$this->build_post_featured_image();
|
281 |
$this->build_post_commments_data();
|
282 |
}
|
316 |
private function build_post_content() {
|
317 |
$amp_content = new AMP_Content(
|
318 |
$this->post->post_content,
|
319 |
+
amp_get_content_embed_handlers( $this->post ),
|
320 |
+
amp_get_content_sanitizers( $this->post ),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
321 |
array(
|
322 |
'content_max_width' => $this->get( 'content_max_width' ),
|
323 |
)
|
355 |
|
356 |
list( $sanitized_html, $featured_scripts, $featured_styles ) = AMP_Content_Sanitizer::sanitize(
|
357 |
$featured_html,
|
358 |
+
amp_get_content_sanitizers( $this->post ),
|
359 |
array(
|
360 |
'content_max_width' => $this->get( 'content_max_width' ),
|
361 |
)
|
400 |
$this->add_data_by_key( 'customizer_settings', apply_filters( 'amp_post_template_customizer_settings', $settings, $this->post ) );
|
401 |
}
|
402 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
403 |
/**
|
404 |
* Build HTML tag attributes.
|
405 |
*/
|
@@ -13,29 +13,125 @@
|
|
13 |
class AMP_DOM_Utils {
|
14 |
|
15 |
/**
|
16 |
-
*
|
17 |
*
|
18 |
-
*
|
19 |
*
|
20 |
-
* @since 0.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
*
|
22 |
-
* @
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
*
|
|
|
|
|
|
|
|
|
24 |
* @return DOMDocument|false Returns DOMDocument, or false if conversion failed.
|
25 |
*/
|
26 |
-
public static function
|
27 |
$libxml_previous_state = libxml_use_internal_errors( true );
|
28 |
|
29 |
$dom = new DOMDocument();
|
30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
/*
|
32 |
* Wrap in dummy tags, since XML needs one parent node.
|
33 |
* It also makes it easier to loop through nodes.
|
34 |
* We can later use this to extract our nodes.
|
35 |
-
* Add
|
36 |
-
* See: http://php.net/manual/en/domdocument.loadhtml.php#78243
|
37 |
*/
|
38 |
-
$result = $dom->loadHTML(
|
39 |
|
40 |
libxml_clear_errors();
|
41 |
libxml_use_internal_errors( $libxml_previous_state );
|
@@ -44,24 +140,207 @@ class AMP_DOM_Utils {
|
|
44 |
return false;
|
45 |
}
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
return $dom;
|
48 |
}
|
49 |
|
50 |
/**
|
51 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
*
|
53 |
-
* @
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
*
|
55 |
* @since 0.2
|
56 |
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
* @param DOMDocument $dom Represents an HTML document from which to extract HTML content.
|
58 |
*
|
59 |
-
* @return string Returns the HTML content represented in the DOMDocument
|
60 |
*/
|
61 |
public static function get_content_from_dom( $dom ) {
|
62 |
|
63 |
/**
|
64 |
* We only want children of the body tag, since we have a subset of HTML.
|
|
|
|
|
65 |
*/
|
66 |
$body = $dom->getElementsByTagName( 'body' )->item( 0 );
|
67 |
|
@@ -85,12 +364,13 @@ class AMP_DOM_Utils {
|
|
85 |
/**
|
86 |
* Return valid HTML content extracted from the DOMNode passed as a parameter.
|
87 |
*
|
88 |
-
* @see Called by function get_content_from_dom()
|
89 |
-
*
|
90 |
* @since 0.6
|
|
|
|
|
|
|
91 |
*
|
92 |
* @param DOMDocument $dom Represents an HTML document.
|
93 |
-
* @param
|
94 |
* @return string Returns the HTML content represented in the DOMNode
|
95 |
*/
|
96 |
public static function get_content_from_dom_node( $dom, $node ) {
|
@@ -102,39 +382,49 @@ class AMP_DOM_Utils {
|
|
102 |
*/
|
103 |
static $self_closing_tags_regex;
|
104 |
|
105 |
-
/*
|
106 |
-
* Most AMP elements need closing tags. To force them, we cannot use
|
107 |
-
* saveHTML (node support is 5.3+) and LIBXML_NOEMPTYTAG results in
|
108 |
-
* issues with self-closing tags like `br` and `hr`. So, we're manually
|
109 |
-
* forcing closing tags.
|
110 |
-
*/
|
111 |
-
self::recursive_force_closing_tags( $dom, $node );
|
112 |
-
|
113 |
/*
|
114 |
* Cache this regex so we don't have to recreate it every call.
|
115 |
*/
|
116 |
if ( ! isset( $self_closing_tags_regex ) ) {
|
117 |
-
$self_closing_tags = implode( '|', self
|
118 |
-
$self_closing_tags_regex = "
|
119 |
}
|
120 |
|
121 |
-
$html = $dom->
|
122 |
|
123 |
// Whitespace just causes unit tests to fail... so whitespace begone.
|
124 |
if ( '' === trim( $html ) ) {
|
125 |
return '';
|
126 |
}
|
127 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
/*
|
129 |
* Travis w/PHP 7.1 generates <br></br> and <hr></hr> vs. <br/> and <hr/>, respectively.
|
130 |
* Travis w/PHP 7.x generates <source ...></source> vs. <source ... />. Etc.
|
131 |
* Seems like LIBXML_NOEMPTYTAG was passed, but as you can see it was not.
|
132 |
* This does not happen in my (@mikeschinkel) local testing, btw.
|
133 |
*/
|
134 |
-
$html = preg_replace( $self_closing_tags_regex, '
|
135 |
|
136 |
return $html;
|
137 |
-
|
138 |
}
|
139 |
|
140 |
/**
|
@@ -210,12 +500,14 @@ class AMP_DOM_Utils {
|
|
210 |
* Forces HTML element closing tags given a DOMDocument and optional DOMElement
|
211 |
*
|
212 |
* @since 0.2
|
|
|
213 |
*
|
214 |
* @param DOMDocument $dom Represents HTML document on which to force closing tags.
|
215 |
* @param DOMElement $node Represents HTML element to start closing tags on.
|
216 |
* If not passed, defaults to first child of body.
|
217 |
*/
|
218 |
public static function recursive_force_closing_tags( $dom, $node = null ) {
|
|
|
219 |
|
220 |
if ( is_null( $node ) ) {
|
221 |
$node = $dom->getElementsByTagName( 'body' )->item( 0 );
|
@@ -257,48 +549,6 @@ class AMP_DOM_Utils {
|
|
257 |
* @return bool Returns true if a valid self-closing tag, false if not.
|
258 |
*/
|
259 |
private static function is_self_closing_tag( $tag ) {
|
260 |
-
return in_array( $tag, self
|
261 |
-
}
|
262 |
-
|
263 |
-
/**
|
264 |
-
* Returns array of self closing tags
|
265 |
-
*
|
266 |
-
* @since 0.6
|
267 |
-
*
|
268 |
-
* @return string[]
|
269 |
-
*/
|
270 |
-
private static function get_self_closing_tags() {
|
271 |
-
/*
|
272 |
-
* As this function is called a lot the static var
|
273 |
-
* prevents having to re-create the array every time.
|
274 |
-
*/
|
275 |
-
static $self_closing_tags;
|
276 |
-
if ( ! isset( $self_closing_tags ) ) {
|
277 |
-
/*
|
278 |
-
* https://www.w3.org/TR/html5/syntax.html#serializing-html-fragments
|
279 |
-
* Not all are valid AMP, but we include them for completeness.
|
280 |
-
*/
|
281 |
-
$self_closing_tags = array(
|
282 |
-
'area',
|
283 |
-
'base',
|
284 |
-
'basefont',
|
285 |
-
'bgsound',
|
286 |
-
'br',
|
287 |
-
'col',
|
288 |
-
'embed',
|
289 |
-
'frame',
|
290 |
-
'hr',
|
291 |
-
'img',
|
292 |
-
'input',
|
293 |
-
'keygen',
|
294 |
-
'link',
|
295 |
-
'meta',
|
296 |
-
'param',
|
297 |
-
'source',
|
298 |
-
'track',
|
299 |
-
'wbr',
|
300 |
-
);
|
301 |
-
}
|
302 |
-
return $self_closing_tags;
|
303 |
}
|
304 |
}
|
13 |
class AMP_DOM_Utils {
|
14 |
|
15 |
/**
|
16 |
+
* HTML elements that are self-closing.
|
17 |
*
|
18 |
+
* Not all are valid AMP, but we include them for completeness.
|
19 |
*
|
20 |
+
* @since 0.7
|
21 |
+
* @link https://www.w3.org/TR/html5/syntax.html#serializing-html-fragments
|
22 |
+
* @var array
|
23 |
+
*/
|
24 |
+
private static $self_closing_tags = array(
|
25 |
+
'area',
|
26 |
+
'base',
|
27 |
+
'basefont',
|
28 |
+
'bgsound',
|
29 |
+
'br',
|
30 |
+
'col',
|
31 |
+
'embed',
|
32 |
+
'frame',
|
33 |
+
'hr',
|
34 |
+
'img',
|
35 |
+
'input',
|
36 |
+
'keygen',
|
37 |
+
'link',
|
38 |
+
'meta',
|
39 |
+
'param',
|
40 |
+
'source',
|
41 |
+
'track',
|
42 |
+
'wbr',
|
43 |
+
);
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Stored noscript/comment replacements for libxml<2.8.
|
47 |
*
|
48 |
+
* @since 0.7
|
49 |
+
* @var array
|
50 |
+
*/
|
51 |
+
public static $noscript_placeholder_comments = array();
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Return a valid DOMDocument representing HTML document passed as a parameter.
|
55 |
*
|
56 |
+
* @since 0.7
|
57 |
+
* @see AMP_DOM_Utils::get_content_from_dom_node()
|
58 |
+
*
|
59 |
+
* @param string $document Valid HTML document to be represented by a DOMDocument.
|
60 |
* @return DOMDocument|false Returns DOMDocument, or false if conversion failed.
|
61 |
*/
|
62 |
+
public static function get_dom( $document ) {
|
63 |
$libxml_previous_state = libxml_use_internal_errors( true );
|
64 |
|
65 |
$dom = new DOMDocument();
|
66 |
|
67 |
+
// @todo In the future consider an AMP_DOMDocument subclass that does this automatically. See <https://github.com/Automattic/amp-wp/pull/895/files#r163825513>.
|
68 |
+
$document = self::convert_amp_bind_attributes( $document );
|
69 |
+
|
70 |
+
/*
|
71 |
+
* Prevent amp-mustache syntax from getting URL-encoded in attributes when saveHTML is done.
|
72 |
+
* While this is applying to the entire document, it only really matters inside of <template>
|
73 |
+
* elements, since URL-encoding of curly braces in href attributes would not normally matter.
|
74 |
+
* But when this is done inside of a <template> then it breaks Mustache. Since Mustache
|
75 |
+
* is logic-less and curly braces are not unsafe for HTML, we can do a global replacement.
|
76 |
+
* The replacement is done on the entire HTML document instead of just inside of the <template>
|
77 |
+
* elements since it is faster and wouldn't change the outcome.
|
78 |
+
*/
|
79 |
+
$placeholders = self::get_mustache_tag_placeholders();
|
80 |
+
$document = str_replace(
|
81 |
+
array_keys( $placeholders ),
|
82 |
+
array_values( $placeholders ),
|
83 |
+
$document
|
84 |
+
);
|
85 |
+
|
86 |
+
// Force all self-closing tags to have closing tags since DOMDocument isn't fully aware.
|
87 |
+
$document = preg_replace(
|
88 |
+
'#<(' . implode( '|', self::$self_closing_tags ) . ')[^>]*>(?!</\1>)#',
|
89 |
+
'$0</$1>',
|
90 |
+
$document
|
91 |
+
);
|
92 |
+
|
93 |
+
// Deal with bugs in older versions of libxml.
|
94 |
+
$added_back_compat_meta_content_type = false;
|
95 |
+
if ( version_compare( LIBXML_DOTTED_VERSION, '2.8', '<' ) ) {
|
96 |
+
/*
|
97 |
+
* Replace noscript elements with placeholders since libxml<2.8 can parse them incorrectly.
|
98 |
+
* When appearing in the head element, a noscript can cause the head to close prematurely
|
99 |
+
* and the noscript gets moved to the body and anything after it which was in the head.
|
100 |
+
* See <https://stackoverflow.com/questions/39013102/why-does-noscript-move-into-body-tag-instead-of-head-tag>.
|
101 |
+
*/
|
102 |
+
$document = preg_replace_callback(
|
103 |
+
'#<noscript[^>]*>.*?</noscript>#si',
|
104 |
+
function( $matches ) {
|
105 |
+
$placeholder = sprintf( '<!--noscript:%s-->', (string) wp_rand() );
|
106 |
+
AMP_DOM_Utils::$noscript_placeholder_comments[ $placeholder ] = $matches[0];
|
107 |
+
return $placeholder;
|
108 |
+
},
|
109 |
+
$document
|
110 |
+
);
|
111 |
+
|
112 |
+
/*
|
113 |
+
* Add a pre-HTML5-style declaration of the encoding since libxml<2.8 doesn't recognize
|
114 |
+
* HTML5's meta charset. See <https://bugzilla.gnome.org/show_bug.cgi?id=655218>.
|
115 |
+
*/
|
116 |
+
$document = preg_replace(
|
117 |
+
'#(?=<meta\s+charset=["\']?([a-z0-9_-]+))#i',
|
118 |
+
'<meta http-equiv="Content-Type" content="text/html; charset=$1" id="meta-http-equiv-content-type">',
|
119 |
+
$document,
|
120 |
+
1,
|
121 |
+
$count
|
122 |
+
);
|
123 |
+
if ( 1 === $count ) {
|
124 |
+
$added_back_compat_meta_content_type = true;
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
/*
|
129 |
* Wrap in dummy tags, since XML needs one parent node.
|
130 |
* It also makes it easier to loop through nodes.
|
131 |
* We can later use this to extract our nodes.
|
132 |
+
* Add charset so loadHTML does not have problems parsing it.
|
|
|
133 |
*/
|
134 |
+
$result = $dom->loadHTML( $document );
|
135 |
|
136 |
libxml_clear_errors();
|
137 |
libxml_use_internal_errors( $libxml_previous_state );
|
140 |
return false;
|
141 |
}
|
142 |
|
143 |
+
// Remove pre-HTML5-style encoding declaration if added above.
|
144 |
+
if ( $added_back_compat_meta_content_type ) {
|
145 |
+
$meta_http_equiv_element = $dom->getElementById( 'meta-http-equiv-content-type' );
|
146 |
+
if ( $meta_http_equiv_element ) {
|
147 |
+
$meta_http_equiv_element->parentNode->removeChild( $meta_http_equiv_element );
|
148 |
+
}
|
149 |
+
}
|
150 |
+
|
151 |
return $dom;
|
152 |
}
|
153 |
|
154 |
/**
|
155 |
+
* Get attribute prefix for converted amp-bind attributes.
|
156 |
+
*
|
157 |
+
* This contains a random string to prevent HTML content containing this data- attribute
|
158 |
+
* originally from being mutated to contain an amp-bind attribute when attributes are restored.
|
159 |
+
*
|
160 |
+
* @since 0.7
|
161 |
+
* @see \AMP_DOM_Utils::convert_amp_bind_attributes()
|
162 |
+
* @see \AMP_DOM_Utils::restore_amp_bind_attributes()
|
163 |
+
* @link https://www.ampproject.org/docs/reference/components/amp-bind
|
164 |
+
*
|
165 |
+
* @return string HTML5 data-* attribute name prefix for AMP binding attributes.
|
166 |
+
*/
|
167 |
+
public static function get_amp_bind_placeholder_prefix() {
|
168 |
+
static $attribute_prefix;
|
169 |
+
if ( ! isset( $attribute_prefix ) ) {
|
170 |
+
$attribute_prefix = sprintf( 'amp-binding-%s-', md5( wp_rand() ) );
|
171 |
+
}
|
172 |
+
return $attribute_prefix;
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Get amp-mustache tag/placeholder mappings.
|
177 |
+
*
|
178 |
+
* @since 0.7
|
179 |
+
* @see \wpdb::placeholder_escape()
|
180 |
+
*
|
181 |
+
* @return array Mapping of mustache tag token to its placeholder.
|
182 |
+
*/
|
183 |
+
private static function get_mustache_tag_placeholders() {
|
184 |
+
static $placeholders;
|
185 |
+
if ( ! isset( $placeholders ) ) {
|
186 |
+
$salt = wp_rand();
|
187 |
+
|
188 |
+
// Note: The order of these tokens is important, as it determines the order of the order of the replacements.
|
189 |
+
$tokens = array(
|
190 |
+
'{{{',
|
191 |
+
'}}}',
|
192 |
+
'{{#',
|
193 |
+
'{{^',
|
194 |
+
'{{/',
|
195 |
+
'{{/',
|
196 |
+
'{{',
|
197 |
+
'}}',
|
198 |
+
);
|
199 |
+
$placeholders = array();
|
200 |
+
foreach ( $tokens as $token ) {
|
201 |
+
$placeholders[ $token ] = '_amp_mustache_' . md5( $salt . $token );
|
202 |
+
}
|
203 |
+
}
|
204 |
+
return $placeholders;
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Replace AMP binding attributes with something that libxml can parse (as HTML5 data-* attributes).
|
209 |
+
*
|
210 |
+
* This is necessary because attributes in square brackets are not understood in PHP and
|
211 |
+
* get dropped with an error raised:
|
212 |
+
* > Warning: DOMDocument::loadHTML(): error parsing attribute name
|
213 |
+
* This is a reciprocal function of AMP_DOM_Utils::restore_amp_bind_attributes().
|
214 |
+
*
|
215 |
+
* @since 0.7
|
216 |
+
* @see \AMP_DOM_Utils::convert_amp_bind_attributes()
|
217 |
+
* @link https://www.ampproject.org/docs/reference/components/amp-bind
|
218 |
+
*
|
219 |
+
* @param string $html HTML containing amp-bind attributes.
|
220 |
+
* @return string HTML with AMP binding attributes replaced with HTML5 data-* attributes.
|
221 |
+
*/
|
222 |
+
public static function convert_amp_bind_attributes( $html ) {
|
223 |
+
$amp_bind_attr_prefix = self::get_amp_bind_placeholder_prefix();
|
224 |
+
|
225 |
+
// Pattern for HTML attribute accounting for binding attr name, boolean attribute, single/double-quoted attribute value, and unquoted attribute values.
|
226 |
+
$attr_regex = '#^\s+(?P<name>\[?[a-zA-Z0-9_\-]+\]?)(?P<value>=(?:"[^"]*"|\'[^\']*\'|[^\'"\s]+))?#';
|
227 |
+
|
228 |
+
/**
|
229 |
+
* Replace callback.
|
230 |
+
*
|
231 |
+
* @param array $tag_matches Tag matches.
|
232 |
+
* @return string Replacement.
|
233 |
+
*/
|
234 |
+
$replace_callback = function( $tag_matches ) use ( $amp_bind_attr_prefix, $attr_regex ) {
|
235 |
+
$old_attrs = rtrim( $tag_matches['attrs'] );
|
236 |
+
$new_attrs = '';
|
237 |
+
$offset = 0;
|
238 |
+
while ( preg_match( $attr_regex, substr( $old_attrs, $offset ), $attr_matches ) ) {
|
239 |
+
$offset += strlen( $attr_matches[0] );
|
240 |
+
|
241 |
+
if ( '[' === $attr_matches['name'][0] ) {
|
242 |
+
$new_attrs .= ' ' . $amp_bind_attr_prefix . trim( $attr_matches['name'], '[]' );
|
243 |
+
if ( isset( $attr_matches['value'] ) ) {
|
244 |
+
$new_attrs .= $attr_matches['value'];
|
245 |
+
}
|
246 |
+
} else {
|
247 |
+
$new_attrs .= $attr_matches[0];
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
// Bail on parse error which occurs when the regex isn't able to consume the entire $new_attrs string.
|
252 |
+
if ( strlen( $old_attrs ) !== $offset ) {
|
253 |
+
return $tag_matches[0];
|
254 |
+
}
|
255 |
+
|
256 |
+
return '<' . $tag_matches['name'] . $new_attrs . '>';
|
257 |
+
};
|
258 |
+
|
259 |
+
$converted = preg_replace_callback(
|
260 |
+
// Match all start tags that probably contain a binding attribute.
|
261 |
+
'#<(?P<name>[a-zA-Z0-9_\-]+)(?P<attrs>\s[^>]+\]=[^>]+)>#',
|
262 |
+
$replace_callback,
|
263 |
+
$html
|
264 |
+
);
|
265 |
+
|
266 |
+
/**
|
267 |
+
* If the regex engine incurred an error during processing, for example exceeding the backtrack
|
268 |
+
* limit, $converted will be null. In this case we return the originally passed document to allow
|
269 |
+
* DOMDocument to attempt to load it. If the AMP HTML doesn't make use of amp-bind or similar
|
270 |
+
* attributes, then everything should still work.
|
271 |
+
*
|
272 |
+
* See https://github.com/Automattic/amp-wp/issues/993 for additional context on this issue.
|
273 |
+
* See http://php.net/manual/en/pcre.constants.php for additional info on PCRE errors.
|
274 |
+
*/
|
275 |
+
return ( ! is_null( $converted ) ) ? $converted : $html;
|
276 |
+
}
|
277 |
+
|
278 |
+
/**
|
279 |
+
* Convert AMP bind-attributes back to their original syntax.
|
280 |
+
*
|
281 |
+
* This is a reciprocal function of AMP_DOM_Utils::convert_amp_bind_attributes().
|
282 |
*
|
283 |
+
* @since 0.7
|
284 |
+
* @see \AMP_DOM_Utils::convert_amp_bind_attributes()
|
285 |
+
* @link https://www.ampproject.org/docs/reference/components/amp-bind
|
286 |
+
*
|
287 |
+
* @param string $html HTML with amp-bind attributes converted.
|
288 |
+
* @return string HTML with amp-bind attributes restored.
|
289 |
+
*/
|
290 |
+
public static function restore_amp_bind_attributes( $html ) {
|
291 |
+
$html = preg_replace(
|
292 |
+
'#\s' . self::get_amp_bind_placeholder_prefix() . '([a-zA-Z0-9_\-]+)#',
|
293 |
+
' [$1]',
|
294 |
+
$html
|
295 |
+
);
|
296 |
+
return $html;
|
297 |
+
}
|
298 |
+
|
299 |
+
/**
|
300 |
+
* Return a valid DOMDocument representing arbitrary HTML content passed as a parameter.
|
301 |
+
*
|
302 |
+
* @see Reciprocal function get_content_from_dom()
|
303 |
*
|
304 |
* @since 0.2
|
305 |
*
|
306 |
+
* @param string $content Valid HTML content to be represented by a DOMDocument.
|
307 |
+
*
|
308 |
+
* @return DOMDocument|false Returns DOMDocument, or false if conversion failed.
|
309 |
+
*/
|
310 |
+
public static function get_dom_from_content( $content ) {
|
311 |
+
/*
|
312 |
+
* Wrap in dummy tags, since XML needs one parent node.
|
313 |
+
* It also makes it easier to loop through nodes.
|
314 |
+
* We can later use this to extract our nodes.
|
315 |
+
* Add utf-8 charset so loadHTML does not have problems parsing it.
|
316 |
+
* See: http://php.net/manual/en/domdocument.loadhtml.php#78243
|
317 |
+
*/
|
318 |
+
$document = sprintf(
|
319 |
+
'<html><head><meta http-equiv="content-type" content="text/html; charset=%s"></head><body>%s</body></html>',
|
320 |
+
get_bloginfo( 'charset' ),
|
321 |
+
$content
|
322 |
+
);
|
323 |
+
|
324 |
+
return self::get_dom( $document );
|
325 |
+
|
326 |
+
}
|
327 |
+
|
328 |
+
/**
|
329 |
+
* Return valid HTML *body* content extracted from the DOMDocument passed as a parameter.
|
330 |
+
*
|
331 |
+
* @since 0.2
|
332 |
+
* @see AMP_DOM_Utils::get_content_from_dom_node() Reciprocal function.
|
333 |
+
*
|
334 |
* @param DOMDocument $dom Represents an HTML document from which to extract HTML content.
|
335 |
*
|
336 |
+
* @return string Returns the HTML content of the body element represented in the DOMDocument.
|
337 |
*/
|
338 |
public static function get_content_from_dom( $dom ) {
|
339 |
|
340 |
/**
|
341 |
* We only want children of the body tag, since we have a subset of HTML.
|
342 |
+
*
|
343 |
+
* @todo We will want to get the full HTML eventually.
|
344 |
*/
|
345 |
$body = $dom->getElementsByTagName( 'body' )->item( 0 );
|
346 |
|
364 |
/**
|
365 |
* Return valid HTML content extracted from the DOMNode passed as a parameter.
|
366 |
*
|
|
|
|
|
367 |
* @since 0.6
|
368 |
+
* @see AMP_DOM_Utils::get_dom() Where the operations in this method are mirrored.
|
369 |
+
* @see AMP_DOM_Utils::get_content_from_dom() Reciprocal function.
|
370 |
+
* @todo In the future consider an AMP_DOMDocument subclass that does this automatically at saveHTML(). See <https://github.com/Automattic/amp-wp/pull/895/files#r163825513>.
|
371 |
*
|
372 |
* @param DOMDocument $dom Represents an HTML document.
|
373 |
+
* @param DOMElement $node Represents an HTML element of the $dom from which to extract HTML content.
|
374 |
* @return string Returns the HTML content represented in the DOMNode
|
375 |
*/
|
376 |
public static function get_content_from_dom_node( $dom, $node ) {
|
382 |
*/
|
383 |
static $self_closing_tags_regex;
|
384 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
385 |
/*
|
386 |
* Cache this regex so we don't have to recreate it every call.
|
387 |
*/
|
388 |
if ( ! isset( $self_closing_tags_regex ) ) {
|
389 |
+
$self_closing_tags = implode( '|', self::$self_closing_tags );
|
390 |
+
$self_closing_tags_regex = "#</({$self_closing_tags})>#i";
|
391 |
}
|
392 |
|
393 |
+
$html = $dom->saveHTML( $node );
|
394 |
|
395 |
// Whitespace just causes unit tests to fail... so whitespace begone.
|
396 |
if ( '' === trim( $html ) ) {
|
397 |
return '';
|
398 |
}
|
399 |
|
400 |
+
// Restore noscript elements which were temporarily removed to prevent libxml<2.8 parsing problems.
|
401 |
+
if ( version_compare( LIBXML_DOTTED_VERSION, '2.8', '<' ) ) {
|
402 |
+
$html = str_replace(
|
403 |
+
array_keys( self::$noscript_placeholder_comments ),
|
404 |
+
array_values( self::$noscript_placeholder_comments ),
|
405 |
+
$html
|
406 |
+
);
|
407 |
+
}
|
408 |
+
|
409 |
+
$html = self::restore_amp_bind_attributes( $html );
|
410 |
+
|
411 |
+
// Restore amp-mustache placeholders which were replaced to prevent URL-encoded corruption by saveHTML.
|
412 |
+
$placeholders = self::get_mustache_tag_placeholders();
|
413 |
+
$html = str_replace(
|
414 |
+
array_values( $placeholders ),
|
415 |
+
array_keys( $placeholders ),
|
416 |
+
$html
|
417 |
+
);
|
418 |
+
|
419 |
/*
|
420 |
* Travis w/PHP 7.1 generates <br></br> and <hr></hr> vs. <br/> and <hr/>, respectively.
|
421 |
* Travis w/PHP 7.x generates <source ...></source> vs. <source ... />. Etc.
|
422 |
* Seems like LIBXML_NOEMPTYTAG was passed, but as you can see it was not.
|
423 |
* This does not happen in my (@mikeschinkel) local testing, btw.
|
424 |
*/
|
425 |
+
$html = preg_replace( $self_closing_tags_regex, '', $html );
|
426 |
|
427 |
return $html;
|
|
|
428 |
}
|
429 |
|
430 |
/**
|
500 |
* Forces HTML element closing tags given a DOMDocument and optional DOMElement
|
501 |
*
|
502 |
* @since 0.2
|
503 |
+
* @deprecated
|
504 |
*
|
505 |
* @param DOMDocument $dom Represents HTML document on which to force closing tags.
|
506 |
* @param DOMElement $node Represents HTML element to start closing tags on.
|
507 |
* If not passed, defaults to first child of body.
|
508 |
*/
|
509 |
public static function recursive_force_closing_tags( $dom, $node = null ) {
|
510 |
+
_deprecated_function( __METHOD__, '0.7' );
|
511 |
|
512 |
if ( is_null( $node ) ) {
|
513 |
$node = $dom->getElementsByTagName( 'body' )->item( 0 );
|
549 |
* @return bool Returns true if a valid self-closing tag, false if not.
|
550 |
*/
|
551 |
private static function is_self_closing_tag( $tag ) {
|
552 |
+
return in_array( strtolower( $tag ), self::$self_closing_tags, true );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
553 |
}
|
554 |
}
|
@@ -0,0 +1,1848 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Validation_Utils
|
4 |
+
*
|
5 |
+
* @package AMP
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class AMP_Validation_Utils
|
10 |
+
*
|
11 |
+
* @since 0.7
|
12 |
+
*/
|
13 |
+
class AMP_Validation_Utils {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Query var that triggers validation.
|
17 |
+
*
|
18 |
+
* @var string
|
19 |
+
*/
|
20 |
+
const VALIDATE_QUERY_VAR = 'amp_validate';
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Query var that enables validation debug mode, to disable removal of invalid elements/attributes.
|
24 |
+
*
|
25 |
+
* @var string
|
26 |
+
*/
|
27 |
+
const DEBUG_QUERY_VAR = 'amp_debug';
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Query var for cache-busting.
|
31 |
+
*
|
32 |
+
* @var string
|
33 |
+
*/
|
34 |
+
const CACHE_BUST_QUERY_VAR = 'amp_cache_bust';
|
35 |
+
|
36 |
+
/**
|
37 |
+
* The slug of the post type to store AMP errors.
|
38 |
+
*
|
39 |
+
* @var string
|
40 |
+
*/
|
41 |
+
const POST_TYPE_SLUG = 'amp_validation_error';
|
42 |
+
|
43 |
+
/**
|
44 |
+
* The key in the response for the sources that have invalid output.
|
45 |
+
*
|
46 |
+
* @var string
|
47 |
+
*/
|
48 |
+
const SOURCES_INVALID_OUTPUT = 'sources_with_invalid_output';
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Validation code for an invalid element.
|
52 |
+
*
|
53 |
+
* @var string
|
54 |
+
*/
|
55 |
+
const INVALID_ELEMENT_CODE = 'invalid_element';
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Validation code for an invalid attribute.
|
59 |
+
*
|
60 |
+
* @var string
|
61 |
+
*/
|
62 |
+
const INVALID_ATTRIBUTE_CODE = 'invalid_attribute';
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Validation code for when script is enqueued (which is not allowed).
|
66 |
+
*
|
67 |
+
* @var string
|
68 |
+
*/
|
69 |
+
const ENQUEUED_SCRIPT_CODE = 'enqueued_script';
|
70 |
+
|
71 |
+
/**
|
72 |
+
* The meta key for the AMP URL where the error occurred.
|
73 |
+
*
|
74 |
+
* @var string
|
75 |
+
*/
|
76 |
+
const AMP_URL_META = 'amp_url';
|
77 |
+
|
78 |
+
/**
|
79 |
+
* The key for removed elements.
|
80 |
+
*
|
81 |
+
* @var string
|
82 |
+
*/
|
83 |
+
const REMOVED_ELEMENTS = 'removed_elements';
|
84 |
+
|
85 |
+
/**
|
86 |
+
* The key for removed attributes.
|
87 |
+
*
|
88 |
+
* @var string
|
89 |
+
*/
|
90 |
+
const REMOVED_ATTRIBUTES = 'removed_attributes';
|
91 |
+
|
92 |
+
/**
|
93 |
+
* The key for removed sources.
|
94 |
+
*
|
95 |
+
* @var string
|
96 |
+
*/
|
97 |
+
const REMOVED_SOURCES = 'removed_sources';
|
98 |
+
|
99 |
+
/**
|
100 |
+
* The action to recheck URLs for AMP validity.
|
101 |
+
*
|
102 |
+
* @var string
|
103 |
+
*/
|
104 |
+
const RECHECK_ACTION = 'amp_recheck';
|
105 |
+
|
106 |
+
/**
|
107 |
+
* The query arg for whether there are remaining errors after rechecking URLs.
|
108 |
+
*
|
109 |
+
* @var string
|
110 |
+
*/
|
111 |
+
const REMAINING_ERRORS = 'amp_remaining_errors';
|
112 |
+
|
113 |
+
/**
|
114 |
+
* The query arg for the number of URLs tested.
|
115 |
+
*
|
116 |
+
* @var string
|
117 |
+
*/
|
118 |
+
const URLS_TESTED = 'amp_urls_tested';
|
119 |
+
|
120 |
+
/**
|
121 |
+
* The nonce action for rechecking a URL.
|
122 |
+
*
|
123 |
+
* @var string
|
124 |
+
*/
|
125 |
+
const NONCE_ACTION = 'amp_recheck_';
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Transient key to store validation errors when activating a plugin.
|
129 |
+
*
|
130 |
+
* @var string
|
131 |
+
*/
|
132 |
+
const PLUGIN_ACTIVATION_VALIDATION_ERRORS_TRANSIENT_KEY = 'amp_plugin_activation_validation_errors';
|
133 |
+
|
134 |
+
/**
|
135 |
+
* The name of the side meta box on the CPT post.php page.
|
136 |
+
*
|
137 |
+
* @var string
|
138 |
+
*/
|
139 |
+
const STATUS_META_BOX = 'amp_validation_status';
|
140 |
+
|
141 |
+
/**
|
142 |
+
* The name of the side meta box on the CPT post.php page.
|
143 |
+
*
|
144 |
+
* @var string
|
145 |
+
*/
|
146 |
+
const VALIDATION_ERRORS_META_BOX = 'amp_validation_errors';
|
147 |
+
|
148 |
+
/**
|
149 |
+
* The errors encountered when validating.
|
150 |
+
*
|
151 |
+
* @var array[][] {
|
152 |
+
* @type string $code Error code.
|
153 |
+
* @type string $node_name Name of removed node.
|
154 |
+
* @type string $parent_name Name of parent node.
|
155 |
+
* }
|
156 |
+
*/
|
157 |
+
public static $validation_errors = array();
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Sources that enqueue each script.
|
161 |
+
*
|
162 |
+
* @var array
|
163 |
+
*/
|
164 |
+
public static $enqueued_script_sources = array();
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Sources that enqueue each style.
|
168 |
+
*
|
169 |
+
* @var array
|
170 |
+
*/
|
171 |
+
public static $enqueued_style_sources = array();
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Post IDs for posts that have been updated which need to be re-validated.
|
175 |
+
*
|
176 |
+
* @var int[]
|
177 |
+
*/
|
178 |
+
public static $posts_pending_frontend_validation = array();
|
179 |
+
|
180 |
+
/**
|
181 |
+
* Current sources gathered for a given hook currently being run.
|
182 |
+
*
|
183 |
+
* @see AMP_Validation_Utils::wrap_hook_callbacks()
|
184 |
+
* @see AMP_Validation_Utils::decorate_filter_source()
|
185 |
+
* @var array[]
|
186 |
+
*/
|
187 |
+
protected static $current_hook_source_stack = array();
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Hook source stack.
|
191 |
+
*
|
192 |
+
* This has to be public for the sake of PHP 5.3.
|
193 |
+
*
|
194 |
+
* @since 0.7
|
195 |
+
* @var array[]
|
196 |
+
*/
|
197 |
+
public static $hook_source_stack = array();
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Add the actions.
|
201 |
+
*
|
202 |
+
* @return void
|
203 |
+
*/
|
204 |
+
public static function init() {
|
205 |
+
if ( current_theme_supports( 'amp' ) ) {
|
206 |
+
add_action( 'init', array( __CLASS__, 'register_post_type' ) );
|
207 |
+
add_filter( 'dashboard_glance_items', array( __CLASS__, 'filter_dashboard_glance_items' ) );
|
208 |
+
add_action( 'rightnow_end', array( __CLASS__, 'print_dashboard_glance_styles' ) );
|
209 |
+
add_action( 'save_post', array( __CLASS__, 'handle_save_post_prompting_validation' ), 10, 2 );
|
210 |
+
}
|
211 |
+
|
212 |
+
add_action( 'edit_form_top', array( __CLASS__, 'print_edit_form_validation_status' ), 10, 2 );
|
213 |
+
add_action( 'all_admin_notices', array( __CLASS__, 'plugin_notice' ) );
|
214 |
+
add_filter( 'manage_' . self::POST_TYPE_SLUG . '_posts_columns', array( __CLASS__, 'add_post_columns' ) );
|
215 |
+
add_action( 'manage_posts_custom_column', array( __CLASS__, 'output_custom_column' ), 10, 2 );
|
216 |
+
add_filter( 'post_row_actions', array( __CLASS__, 'filter_row_actions' ), 10, 2 );
|
217 |
+
add_filter( 'bulk_actions-edit-' . self::POST_TYPE_SLUG, array( __CLASS__, 'add_bulk_action' ), 10, 2 );
|
218 |
+
add_filter( 'handle_bulk_actions-edit-' . self::POST_TYPE_SLUG, array( __CLASS__, 'handle_bulk_action' ), 10, 3 );
|
219 |
+
add_action( 'admin_notices', array( __CLASS__, 'remaining_error_notice' ) );
|
220 |
+
add_action( 'post_action_' . self::RECHECK_ACTION, array( __CLASS__, 'handle_inline_recheck' ) );
|
221 |
+
add_action( 'admin_menu', array( __CLASS__, 'remove_publish_meta_box' ) );
|
222 |
+
add_action( 'admin_menu', array( __CLASS__, 'add_admin_menu_validation_status_count' ) );
|
223 |
+
add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_boxes' ) );
|
224 |
+
|
225 |
+
// Actions and filters involved in validation.
|
226 |
+
add_action( 'activate_plugin', function() {
|
227 |
+
if ( ! has_action( 'shutdown', array( __CLASS__, 'validate_after_plugin_activation' ) ) ) {
|
228 |
+
add_action( 'shutdown', array( __CLASS__, 'validate_after_plugin_activation' ) ); // Shutdown so all plugins will have been activated.
|
229 |
+
}
|
230 |
+
} );
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Add count of how many validation error posts there are to the admin menu.
|
235 |
+
*/
|
236 |
+
public static function add_admin_menu_validation_status_count() {
|
237 |
+
global $submenu;
|
238 |
+
if ( ! isset( $submenu[ AMP_Options_Manager::OPTION_NAME ] ) ) {
|
239 |
+
return;
|
240 |
+
}
|
241 |
+
$count = wp_count_posts( self::POST_TYPE_SLUG );
|
242 |
+
if ( empty( $count->publish ) ) {
|
243 |
+
return;
|
244 |
+
}
|
245 |
+
foreach ( $submenu[ AMP_Options_Manager::OPTION_NAME ] as &$submenu_item ) {
|
246 |
+
if ( 'edit.php?post_type=' . self::POST_TYPE_SLUG === $submenu_item[2] ) {
|
247 |
+
$submenu_item[0] .= ' <span class="awaiting-mod"><span class="pending-count">' . esc_html( $count->publish ) . '</span></span>';
|
248 |
+
break;
|
249 |
+
}
|
250 |
+
}
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Filter At a Glance items add AMP Validation Errors.
|
255 |
+
*
|
256 |
+
* @param array $items At a glance items.
|
257 |
+
* @return array Items.
|
258 |
+
*/
|
259 |
+
public static function filter_dashboard_glance_items( $items ) {
|
260 |
+
$counts = wp_count_posts( self::POST_TYPE_SLUG );
|
261 |
+
if ( ! empty( $counts->publish ) ) {
|
262 |
+
$items[] = sprintf(
|
263 |
+
'<a class="amp-validation-errors" href="%s">%s</a>',
|
264 |
+
esc_url( admin_url( 'edit.php?post_type=' . self::POST_TYPE_SLUG ) ),
|
265 |
+
esc_html( sprintf(
|
266 |
+
/* translators: %s is the validation error count */
|
267 |
+
_n( '%s AMP Validation Error', '%s AMP Validation Errors', $counts->publish, 'amp' ),
|
268 |
+
$counts->publish
|
269 |
+
) )
|
270 |
+
);
|
271 |
+
}
|
272 |
+
return $items;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Print styles for the At a Glance widget.
|
277 |
+
*/
|
278 |
+
public static function print_dashboard_glance_styles() {
|
279 |
+
?>
|
280 |
+
<style>
|
281 |
+
#dashboard_right_now .amp-validation-errors {
|
282 |
+
color: #a00;
|
283 |
+
}
|
284 |
+
#dashboard_right_now .amp-validation-errors:before {
|
285 |
+
content: "\f534";
|
286 |
+
}
|
287 |
+
#dashboard_right_now .amp-validation-errors:hover {
|
288 |
+
color: #dc3232;
|
289 |
+
border: none;
|
290 |
+
}
|
291 |
+
</style>
|
292 |
+
<?php
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Add hooks for doing validation during preprocessing/sanitizing.
|
297 |
+
*/
|
298 |
+
public static function add_validation_hooks() {
|
299 |
+
self::wrap_widget_callbacks();
|
300 |
+
|
301 |
+
add_action( 'all', array( __CLASS__, 'wrap_hook_callbacks' ) );
|
302 |
+
$wrapped_filters = array( 'the_content', 'the_excerpt' );
|
303 |
+
foreach ( $wrapped_filters as $wrapped_filter ) {
|
304 |
+
add_filter( $wrapped_filter, array( __CLASS__, 'decorate_filter_source' ), PHP_INT_MAX );
|
305 |
+
}
|
306 |
+
|
307 |
+
add_filter( 'do_shortcode_tag', array( __CLASS__, 'decorate_shortcode_source' ), -1, 2 );
|
308 |
+
add_filter( 'amp_content_sanitizers', array( __CLASS__, 'add_validation_callback' ) );
|
309 |
+
}
|
310 |
+
|
311 |
+
/**
|
312 |
+
* Handle save_post action to queue re-validation of the post on the frontend.
|
313 |
+
*
|
314 |
+
* @see AMP_Validation_Utils::validate_queued_posts_on_frontend()
|
315 |
+
* @param int $post_id Post ID.
|
316 |
+
* @param WP_Post $post Post.
|
317 |
+
*/
|
318 |
+
public static function handle_save_post_prompting_validation( $post_id, $post ) {
|
319 |
+
$should_validate_post = (
|
320 |
+
is_post_type_viewable( $post->post_type )
|
321 |
+
&&
|
322 |
+
! wp_is_post_autosave( $post )
|
323 |
+
&&
|
324 |
+
! wp_is_post_revision( $post )
|
325 |
+
);
|
326 |
+
if ( $should_validate_post ) {
|
327 |
+
self::$posts_pending_frontend_validation[] = $post_id;
|
328 |
+
|
329 |
+
// The reason for shutdown is to ensure that all postmeta changes have been saved, including whether AMP is enabled.
|
330 |
+
if ( ! has_action( 'shutdown', array( __CLASS__, 'validate_queued_posts_on_frontend' ) ) ) {
|
331 |
+
add_action( 'shutdown', array( __CLASS__, 'validate_queued_posts_on_frontend' ) );
|
332 |
+
}
|
333 |
+
}
|
334 |
+
}
|
335 |
+
|
336 |
+
/**
|
337 |
+
* Validate the posts pending frontend validation.
|
338 |
+
*
|
339 |
+
* @see AMP_Validation_Utils::handle_save_post_prompting_validation()
|
340 |
+
*/
|
341 |
+
public static function validate_queued_posts_on_frontend() {
|
342 |
+
$posts = array_filter(
|
343 |
+
array_map( 'get_post', self::$posts_pending_frontend_validation ),
|
344 |
+
function( $post ) {
|
345 |
+
return $post && post_supports_amp( $post ) && 'trash' !== $post->post_status;
|
346 |
+
}
|
347 |
+
);
|
348 |
+
|
349 |
+
// @todo Only validate the first and then queue the rest in WP Cron?
|
350 |
+
foreach ( $posts as $post ) {
|
351 |
+
$url = amp_get_permalink( $post->ID );
|
352 |
+
if ( ! $url ) {
|
353 |
+
continue;
|
354 |
+
}
|
355 |
+
|
356 |
+
$validation_errors = self::validate_url( $url );
|
357 |
+
if ( is_wp_error( $validation_errors ) ) {
|
358 |
+
continue;
|
359 |
+
}
|
360 |
+
|
361 |
+
self::store_validation_errors( $validation_errors, $url );
|
362 |
+
}
|
363 |
+
}
|
364 |
+
|
365 |
+
/**
|
366 |
+
* Processes markup, to determine AMP validity.
|
367 |
+
*
|
368 |
+
* Passes $markup through the AMP sanitizers.
|
369 |
+
* Also passes a 'validation_error_callback' to keep track of stripped attributes and nodes.
|
370 |
+
*
|
371 |
+
* @param string $markup The markup to process.
|
372 |
+
* @return string Sanitized markup.
|
373 |
+
*/
|
374 |
+
public static function process_markup( $markup ) {
|
375 |
+
AMP_Theme_Support::register_content_embed_handlers();
|
376 |
+
|
377 |
+
/** This filter is documented in wp-includes/post-template.php */
|
378 |
+
$markup = apply_filters( 'the_content', $markup );
|
379 |
+
$args = array(
|
380 |
+
'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH,
|
381 |
+
'validation_error_callback' => 'AMP_Validation_Utils::add_validation_error',
|
382 |
+
);
|
383 |
+
|
384 |
+
$results = AMP_Content_Sanitizer::sanitize( $markup, amp_get_content_sanitizers(), $args );
|
385 |
+
return $results[0];
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* Whether the user has the required capability.
|
390 |
+
*
|
391 |
+
* Checks for permissions before validating.
|
392 |
+
*
|
393 |
+
* @return boolean $has_cap Whether the current user has the capability.
|
394 |
+
*/
|
395 |
+
public static function has_cap() {
|
396 |
+
return current_user_can( 'edit_posts' );
|
397 |
+
}
|
398 |
+
|
399 |
+
/**
|
400 |
+
* Add validation error.
|
401 |
+
*
|
402 |
+
* @param array $data {
|
403 |
+
* Data.
|
404 |
+
*
|
405 |
+
* @type string $code Error code.
|
406 |
+
* @type DOMElement|DOMNode $node The removed node.
|
407 |
+
* }
|
408 |
+
*/
|
409 |
+
public static function add_validation_error( array $data ) {
|
410 |
+
$node = null;
|
411 |
+
|
412 |
+
if ( isset( $data['node'] ) && $data['node'] instanceof DOMNode ) {
|
413 |
+
$node = $data['node'];
|
414 |
+
unset( $data['node'] );
|
415 |
+
$data['node_name'] = $node->nodeName;
|
416 |
+
$data['sources'] = self::locate_sources( $node );
|
417 |
+
if ( $node->parentNode ) {
|
418 |
+
$data['parent_name'] = $node->parentNode->nodeName;
|
419 |
+
}
|
420 |
+
}
|
421 |
+
|
422 |
+
if ( $node instanceof DOMElement ) {
|
423 |
+
if ( ! isset( $data['code'] ) ) {
|
424 |
+
$data['code'] = self::INVALID_ELEMENT_CODE;
|
425 |
+
}
|
426 |
+
$data['node_attributes'] = array();
|
427 |
+
foreach ( $node->attributes as $attribute ) {
|
428 |
+
$data['node_attributes'][ $attribute->nodeName ] = $attribute->nodeValue;
|
429 |
+
}
|
430 |
+
|
431 |
+
$is_enqueued_link = (
|
432 |
+
'link' === $node->nodeName
|
433 |
+
&&
|
434 |
+
preg_match( '/(?P<handle>.+)-css$/', (string) $node->getAttribute( 'id' ), $matches )
|
435 |
+
&&
|
436 |
+
isset( self::$enqueued_style_sources[ $matches['handle'] ] )
|
437 |
+
);
|
438 |
+
if ( $is_enqueued_link ) {
|
439 |
+
$data['sources'] = self::$enqueued_style_sources[ $matches['handle'] ];
|
440 |
+
}
|
441 |
+
} elseif ( $node instanceof DOMAttr ) {
|
442 |
+
if ( ! isset( $data['code'] ) ) {
|
443 |
+
$data['code'] = self::INVALID_ATTRIBUTE_CODE;
|
444 |
+
}
|
445 |
+
$data['element_attributes'] = array();
|
446 |
+
if ( $node->parentNode && $node->parentNode->hasAttributes() ) {
|
447 |
+
foreach ( $node->parentNode->attributes as $attribute ) {
|
448 |
+
$data['element_attributes'][ $attribute->nodeName ] = $attribute->nodeValue;
|
449 |
+
}
|
450 |
+
}
|
451 |
+
}
|
452 |
+
|
453 |
+
if ( ! isset( $data['code'] ) ) {
|
454 |
+
$data['code'] = 'unknown';
|
455 |
+
}
|
456 |
+
|
457 |
+
self::$validation_errors[] = $data;
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Gets the AMP validation response.
|
462 |
+
*
|
463 |
+
* Returns the current validation errors the sanitizers found in rendering the page.
|
464 |
+
*
|
465 |
+
* @param array $validation_errors Validation errors.
|
466 |
+
* @return array The AMP validity of the markup.
|
467 |
+
*/
|
468 |
+
public static function summarize_validation_errors( $validation_errors ) {
|
469 |
+
$results = array();
|
470 |
+
$removed_elements = array();
|
471 |
+
$removed_attributes = array();
|
472 |
+
$invalid_sources = array();
|
473 |
+
foreach ( $validation_errors as $validation_error ) {
|
474 |
+
$code = isset( $validation_error['code'] ) ? $validation_error['code'] : null;
|
475 |
+
|
476 |
+
if ( self::INVALID_ELEMENT_CODE === $code ) {
|
477 |
+
if ( ! isset( $removed_elements[ $validation_error['node_name'] ] ) ) {
|
478 |
+
$removed_elements[ $validation_error['node_name'] ] = 0;
|
479 |
+
}
|
480 |
+
$removed_elements[ $validation_error['node_name'] ] += 1;
|
481 |
+
} elseif ( self::INVALID_ATTRIBUTE_CODE === $code ) {
|
482 |
+
if ( ! isset( $removed_attributes[ $validation_error['node_name'] ] ) ) {
|
483 |
+
$removed_attributes[ $validation_error['node_name'] ] = 0;
|
484 |
+
}
|
485 |
+
$removed_attributes[ $validation_error['node_name'] ] += 1;
|
486 |
+
}
|
487 |
+
|
488 |
+
if ( ! empty( $validation_error['sources'] ) ) {
|
489 |
+
$source = array_pop( $validation_error['sources'] );
|
490 |
+
|
491 |
+
if ( isset( $source['type'], $source['name'] ) ) {
|
492 |
+
$invalid_sources[ $source['type'] ][] = $source['name'];
|
493 |
+
}
|
494 |
+
}
|
495 |
+
}
|
496 |
+
|
497 |
+
$results = array_merge(
|
498 |
+
array(
|
499 |
+
self::SOURCES_INVALID_OUTPUT => $invalid_sources,
|
500 |
+
),
|
501 |
+
compact(
|
502 |
+
'removed_elements',
|
503 |
+
'removed_attributes'
|
504 |
+
),
|
505 |
+
$results );
|
506 |
+
|
507 |
+
return $results;
|
508 |
+
}
|
509 |
+
|
510 |
+
/**
|
511 |
+
* Reset the stored removed nodes and attributes.
|
512 |
+
*
|
513 |
+
* After testing if the markup is valid,
|
514 |
+
* these static values will remain.
|
515 |
+
* So reset them in case another test is needed.
|
516 |
+
*
|
517 |
+
* @return void
|
518 |
+
*/
|
519 |
+
public static function reset_validation_results() {
|
520 |
+
self::$validation_errors = array();
|
521 |
+
self::$enqueued_style_sources = array();
|
522 |
+
self::$enqueued_script_sources = array();
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* Checks the AMP validity of the post content.
|
527 |
+
*
|
528 |
+
* If it's not valid AMP, it displays an error message above the 'Classic' editor.
|
529 |
+
*
|
530 |
+
* @param WP_Post $post The updated post.
|
531 |
+
* @return void
|
532 |
+
*/
|
533 |
+
public static function print_edit_form_validation_status( $post ) {
|
534 |
+
if ( ! post_supports_amp( $post ) || ! self::has_cap() ) {
|
535 |
+
return;
|
536 |
+
}
|
537 |
+
|
538 |
+
$url = null;
|
539 |
+
$validation_status_post = null;
|
540 |
+
$validation_errors = array();
|
541 |
+
|
542 |
+
// Incorporate frontend validation status if there is a known URL for the post.
|
543 |
+
if ( is_post_type_viewable( $post->post_type ) ) {
|
544 |
+
$url = amp_get_permalink( $post->ID );
|
545 |
+
|
546 |
+
$validation_status_post = self::get_validation_status_post( $url );
|
547 |
+
if ( $validation_status_post ) {
|
548 |
+
$data = json_decode( $validation_status_post->post_content, true );
|
549 |
+
if ( is_array( $data ) ) {
|
550 |
+
$validation_errors = array_merge( $validation_errors, $data );
|
551 |
+
}
|
552 |
+
}
|
553 |
+
}
|
554 |
+
|
555 |
+
// If no results from URL are available, validate post content outside frontend context.
|
556 |
+
if ( empty( $validation_errors ) && post_type_supports( $post->post_type, 'editor' ) ) {
|
557 |
+
self::process_markup( $post->post_content );
|
558 |
+
$validation_errors = array_merge(
|
559 |
+
$validation_errors,
|
560 |
+
self::$validation_errors
|
561 |
+
);
|
562 |
+
self::reset_validation_results();
|
563 |
+
|
564 |
+
// Make sure original post is restored after applying shortcodes which could change it.
|
565 |
+
$GLOBALS['post'] = $post; // WPCS: override ok.
|
566 |
+
setup_postdata( $post );
|
567 |
+
}
|
568 |
+
|
569 |
+
if ( empty( $validation_errors ) ) {
|
570 |
+
return;
|
571 |
+
}
|
572 |
+
|
573 |
+
echo '<div class="notice notice-warning">';
|
574 |
+
echo '<p>';
|
575 |
+
esc_html_e( 'Warning: There is content which fails AMP validation; it will be stripped when served as AMP.', 'amp' );
|
576 |
+
if ( $validation_status_post || $url ) {
|
577 |
+
if ( $validation_status_post ) {
|
578 |
+
echo sprintf(
|
579 |
+
' <a href="%s" target="_blank">%s</a>',
|
580 |
+
esc_url( get_edit_post_link( $validation_status_post ) ),
|
581 |
+
esc_html__( 'Details', 'amp' )
|
582 |
+
);
|
583 |
+
}
|
584 |
+
if ( $url ) {
|
585 |
+
if ( $validation_status_post ) {
|
586 |
+
echo ' | ';
|
587 |
+
}
|
588 |
+
echo sprintf(
|
589 |
+
' <a href="%s" aria-label="%s" target="_blank">%s</a>',
|
590 |
+
esc_url( self::get_debug_url( $url ) ),
|
591 |
+
esc_attr__( 'Validate URL on frontend but without invalid elements/attributes removed', 'amp' ),
|
592 |
+
esc_html__( 'Debug', 'amp' )
|
593 |
+
);
|
594 |
+
}
|
595 |
+
}
|
596 |
+
echo '</p>';
|
597 |
+
|
598 |
+
$results = self::summarize_validation_errors( array_unique( $validation_errors, SORT_REGULAR ) );
|
599 |
+
$removed_sets = array();
|
600 |
+
if ( ! empty( $results[ self::REMOVED_ELEMENTS ] ) && is_array( $results[ self::REMOVED_ELEMENTS ] ) ) {
|
601 |
+
$removed_sets[] = array(
|
602 |
+
'label' => __( 'Invalid elements:', 'amp' ),
|
603 |
+
'names' => array_map( 'sanitize_key', $results[ self::REMOVED_ELEMENTS ] ),
|
604 |
+
);
|
605 |
+
}
|
606 |
+
if ( ! empty( $results[ self::REMOVED_ATTRIBUTES ] ) && is_array( $results[ self::REMOVED_ATTRIBUTES ] ) ) {
|
607 |
+
$removed_sets[] = array(
|
608 |
+
'label' => __( 'Invalid attributes:', 'amp' ),
|
609 |
+
'names' => array_map( 'sanitize_key', $results[ self::REMOVED_ATTRIBUTES ] ),
|
610 |
+
);
|
611 |
+
}
|
612 |
+
// @todo There are other kinds of errors other than REMOVED_ELEMENTS and REMOVED_ATTRIBUTES.
|
613 |
+
foreach ( $removed_sets as $removed_set ) {
|
614 |
+
printf( '<p>%s ', esc_html( $removed_set['label'] ) );
|
615 |
+
self::output_removed_set( $removed_set['names'] );
|
616 |
+
echo '</p>';
|
617 |
+
}
|
618 |
+
|
619 |
+
echo '</div>';
|
620 |
+
}
|
621 |
+
|
622 |
+
/**
|
623 |
+
* Get source start comment.
|
624 |
+
*
|
625 |
+
* @param array $source Source data.
|
626 |
+
* @param bool $is_start Whether the comment is the start or end.
|
627 |
+
* @return string HTML Comment.
|
628 |
+
*/
|
629 |
+
public static function get_source_comment( array $source, $is_start = true ) {
|
630 |
+
unset( $source['reflection'] );
|
631 |
+
return sprintf(
|
632 |
+
'<!--%samp-source-stack %s-->',
|
633 |
+
$is_start ? '' : '/',
|
634 |
+
str_replace( '--', '', wp_json_encode( $source ) )
|
635 |
+
);
|
636 |
+
}
|
637 |
+
|
638 |
+
/**
|
639 |
+
* Parse source comment.
|
640 |
+
*
|
641 |
+
* @param DOMComment $comment Comment.
|
642 |
+
* @return array|null Parsed source or null if not a source comment.
|
643 |
+
*/
|
644 |
+
public static function parse_source_comment( DOMComment $comment ) {
|
645 |
+
if ( ! preg_match( '#^\s*(?P<closing>/)?amp-source-stack\s+(?P<args>{.+})\s*$#s', $comment->nodeValue, $matches ) ) {
|
646 |
+
return null;
|
647 |
+
}
|
648 |
+
|
649 |
+
$source = json_decode( $matches['args'], true );
|
650 |
+
$closing = ! empty( $matches['closing'] );
|
651 |
+
|
652 |
+
return compact( 'source', 'closing' );
|
653 |
+
}
|
654 |
+
|
655 |
+
/**
|
656 |
+
* Walk back tree to find the open sources.
|
657 |
+
*
|
658 |
+
* @param DOMNode $node Node to look for.
|
659 |
+
* @return array[][] {
|
660 |
+
* The data of the removed sources (theme, plugin, or mu-plugin).
|
661 |
+
*
|
662 |
+
* @type string $name The name of the source.
|
663 |
+
* @type string $type The type of the source.
|
664 |
+
* }
|
665 |
+
*/
|
666 |
+
public static function locate_sources( DOMNode $node ) {
|
667 |
+
$xpath = new DOMXPath( $node->ownerDocument );
|
668 |
+
$comments = $xpath->query( 'preceding::comment()[ starts-with( ., "amp-source-stack" ) or starts-with( ., "/amp-source-stack" ) ]', $node );
|
669 |
+
$sources = array();
|
670 |
+
foreach ( $comments as $comment ) {
|
671 |
+
$parsed_comment = self::parse_source_comment( $comment );
|
672 |
+
if ( ! $parsed_comment ) {
|
673 |
+
continue;
|
674 |
+
}
|
675 |
+
if ( $parsed_comment['closing'] ) {
|
676 |
+
array_pop( $sources );
|
677 |
+
} else {
|
678 |
+
$sources[] = $parsed_comment['source'];
|
679 |
+
}
|
680 |
+
}
|
681 |
+
return $sources;
|
682 |
+
}
|
683 |
+
|
684 |
+
/**
|
685 |
+
* Remove source comments.
|
686 |
+
*
|
687 |
+
* @param DOMDocument $dom Document.
|
688 |
+
*/
|
689 |
+
public static function remove_source_comments( $dom ) {
|
690 |
+
$xpath = new DOMXPath( $dom );
|
691 |
+
$comments = array();
|
692 |
+
foreach ( $xpath->query( '//comment()[ starts-with( ., "amp-source-stack" ) or starts-with( ., "/amp-source-stack" ) ]' ) as $comment ) {
|
693 |
+
if ( self::parse_source_comment( $comment ) ) {
|
694 |
+
$comments[] = $comment;
|
695 |
+
}
|
696 |
+
}
|
697 |
+
foreach ( $comments as $comment ) {
|
698 |
+
$comment->parentNode->removeChild( $comment );
|
699 |
+
}
|
700 |
+
}
|
701 |
+
|
702 |
+
/**
|
703 |
+
* Wrap callbacks for registered widgets to keep track of queued assets and the source for anything printed for validation.
|
704 |
+
*
|
705 |
+
* @global array $wp_filter
|
706 |
+
* @return void
|
707 |
+
*/
|
708 |
+
public static function wrap_widget_callbacks() {
|
709 |
+
global $wp_registered_widgets;
|
710 |
+
foreach ( $wp_registered_widgets as $widget_id => &$registered_widget ) {
|
711 |
+
$source = self::get_source( $registered_widget['callback'] );
|
712 |
+
if ( ! $source ) {
|
713 |
+
continue;
|
714 |
+
}
|
715 |
+
$source['widget_id'] = $widget_id;
|
716 |
+
|
717 |
+
$function = $registered_widget['callback'];
|
718 |
+
$accepted_args = 2; // For the $instance and $args arguments.
|
719 |
+
$callback = compact( 'function', 'accepted_args', 'source' );
|
720 |
+
|
721 |
+
$registered_widget['callback'] = self::wrapped_callback( $callback );
|
722 |
+
}
|
723 |
+
}
|
724 |
+
|
725 |
+
/**
|
726 |
+
* Wrap filter/action callback functions for a given hook.
|
727 |
+
*
|
728 |
+
* Wrapped callback functions are reset to their original functions after invocation.
|
729 |
+
* This runs at the 'all' action. The shutdown hook is excluded.
|
730 |
+
*
|
731 |
+
* @global WP_Hook[] $wp_filter
|
732 |
+
* @param string $hook Hook name for action or filter.
|
733 |
+
* @return void
|
734 |
+
*/
|
735 |
+
public static function wrap_hook_callbacks( $hook ) {
|
736 |
+
global $wp_filter;
|
737 |
+
|
738 |
+
if ( ! isset( $wp_filter[ $hook ] ) || 'shutdown' === $hook ) {
|
739 |
+
return;
|
740 |
+
}
|
741 |
+
|
742 |
+
self::$current_hook_source_stack[ $hook ] = array();
|
743 |
+
foreach ( $wp_filter[ $hook ]->callbacks as $priority => &$callbacks ) {
|
744 |
+
foreach ( $callbacks as &$callback ) {
|
745 |
+
$source = self::get_source( $callback['function'] );
|
746 |
+
if ( ! $source ) {
|
747 |
+
continue;
|
748 |
+
}
|
749 |
+
|
750 |
+
$reflection = $source['reflection'];
|
751 |
+
unset( $source['reflection'] ); // Omit from stored source.
|
752 |
+
|
753 |
+
// Add hook to stack for decorate_filter_source to read from.
|
754 |
+
self::$current_hook_source_stack[ $hook ][] = $source;
|
755 |
+
|
756 |
+
/*
|
757 |
+
* A current limitation with wrapping callbacks is that the wrapped function cannot have
|
758 |
+
* any parameters passed by reference. Without this the result is:
|
759 |
+
*
|
760 |
+
* > PHP Warning: Parameter 1 to wp_default_styles() expected to be a reference, value given.
|
761 |
+
*/
|
762 |
+
if ( self::has_parameters_passed_by_reference( $reflection ) ) {
|
763 |
+
continue;
|
764 |
+
}
|
765 |
+
|
766 |
+
$source['hook'] = $hook;
|
767 |
+
$original_function = $callback['function'];
|
768 |
+
$wrapped_callback = self::wrapped_callback( array_merge(
|
769 |
+
$callback,
|
770 |
+
compact( 'priority', 'source', 'hook' )
|
771 |
+
) );
|
772 |
+
|
773 |
+
$callback['function'] = function() use ( &$callback, $wrapped_callback, $original_function ) {
|
774 |
+
$callback['function'] = $original_function; // Restore original.
|
775 |
+
return call_user_func_array( $wrapped_callback, func_get_args() );
|
776 |
+
};
|
777 |
+
}
|
778 |
+
}
|
779 |
+
}
|
780 |
+
|
781 |
+
/**
|
782 |
+
* Determine whether the given reflection method/function has params passed by reference.
|
783 |
+
*
|
784 |
+
* @since 0.7
|
785 |
+
* @param ReflectionFunction|ReflectionMethod $reflection Reflection.
|
786 |
+
* @return bool Whether there are parameters passed by reference.
|
787 |
+
*/
|
788 |
+
protected static function has_parameters_passed_by_reference( $reflection ) {
|
789 |
+
foreach ( $reflection->getParameters() as $parameter ) {
|
790 |
+
if ( $parameter->isPassedByReference() ) {
|
791 |
+
return true;
|
792 |
+
}
|
793 |
+
}
|
794 |
+
return false;
|
795 |
+
}
|
796 |
+
|
797 |
+
/**
|
798 |
+
* Filters the output created by a shortcode callback.
|
799 |
+
*
|
800 |
+
* @since 0.7
|
801 |
+
*
|
802 |
+
* @param string $output Shortcode output.
|
803 |
+
* @param string $tag Shortcode name.
|
804 |
+
* @return string Output.
|
805 |
+
* @global array $shortcode_tags
|
806 |
+
*/
|
807 |
+
public static function decorate_shortcode_source( $output, $tag ) {
|
808 |
+
global $shortcode_tags;
|
809 |
+
if ( ! isset( $shortcode_tags[ $tag ] ) ) {
|
810 |
+
return $output;
|
811 |
+
}
|
812 |
+
$source = self::get_source( $shortcode_tags[ $tag ] );
|
813 |
+
if ( empty( $source ) ) {
|
814 |
+
return $output;
|
815 |
+
}
|
816 |
+
$source['shortcode'] = $tag;
|
817 |
+
|
818 |
+
$output = implode( '', array(
|
819 |
+
self::get_source_comment( $source, true ),
|
820 |
+
$output,
|
821 |
+
self::get_source_comment( $source, false ),
|
822 |
+
) );
|
823 |
+
return $output;
|
824 |
+
}
|
825 |
+
|
826 |
+
/**
|
827 |
+
* Wraps output of a filter to add source stack comments.
|
828 |
+
*
|
829 |
+
* @todo Duplicate with AMP_Validation_Utils::wrap_buffer_with_source_comments()?
|
830 |
+
* @param string $value Value.
|
831 |
+
* @return string Value wrapped in source comments.
|
832 |
+
*/
|
833 |
+
public static function decorate_filter_source( $value ) {
|
834 |
+
|
835 |
+
// Abort if the output is not a string and it doesn't contain any HTML tags.
|
836 |
+
if ( ! is_string( $value ) || ! preg_match( '/<.+?>/s', $value ) ) {
|
837 |
+
return $value;
|
838 |
+
}
|
839 |
+
|
840 |
+
$post = get_post();
|
841 |
+
$source = array(
|
842 |
+
'hook' => current_filter(),
|
843 |
+
'filter' => true,
|
844 |
+
);
|
845 |
+
if ( $post ) {
|
846 |
+
$source['post_id'] = $post->ID;
|
847 |
+
$source['post_type'] = $post->post_type;
|
848 |
+
}
|
849 |
+
if ( isset( self::$current_hook_source_stack[ current_filter() ] ) ) {
|
850 |
+
$sources = self::$current_hook_source_stack[ current_filter() ];
|
851 |
+
array_pop( $sources ); // Remove self.
|
852 |
+
$source['sources'] = $sources;
|
853 |
+
}
|
854 |
+
return implode( '', array(
|
855 |
+
self::get_source_comment( $source, true ),
|
856 |
+
$value,
|
857 |
+
self::get_source_comment( $source, false ),
|
858 |
+
) );
|
859 |
+
}
|
860 |
+
|
861 |
+
/**
|
862 |
+
* Gets the plugin or theme of the callback, if one exists.
|
863 |
+
*
|
864 |
+
* @param string|array $callback The callback for which to get the plugin.
|
865 |
+
* @return array|null {
|
866 |
+
* The source data.
|
867 |
+
*
|
868 |
+
* @type string $type Source type (core, plugin, mu-plugin, or theme).
|
869 |
+
* @type string $name Source name.
|
870 |
+
* @type string $function Normalized function name.
|
871 |
+
* @type ReflectionMethod|ReflectionFunction $reflection
|
872 |
+
* }
|
873 |
+
*/
|
874 |
+
public static function get_source( $callback ) {
|
875 |
+
$reflection = null;
|
876 |
+
$class_name = null; // Because ReflectionMethod::getDeclaringClass() can return a parent class.
|
877 |
+
try {
|
878 |
+
if ( is_string( $callback ) && is_callable( $callback ) ) {
|
879 |
+
// The $callback is a function or static method.
|
880 |
+
$exploded_callback = explode( '::', $callback, 2 );
|
881 |
+
if ( 2 === count( $exploded_callback ) ) {
|
882 |
+
$class_name = $exploded_callback[0];
|
883 |
+
$reflection = new ReflectionMethod( $exploded_callback[0], $exploded_callback[1] );
|
884 |
+
} else {
|
885 |
+
$reflection = new ReflectionFunction( $callback );
|
886 |
+
}
|
887 |
+
} elseif ( is_array( $callback ) && isset( $callback[0], $callback[1] ) && method_exists( $callback[0], $callback[1] ) ) {
|
888 |
+
// The $callback is a method.
|
889 |
+
if ( is_string( $callback[0] ) ) {
|
890 |
+
$class_name = $callback[0];
|
891 |
+
} elseif ( is_object( $callback[0] ) ) {
|
892 |
+
$class_name = get_class( $callback[0] );
|
893 |
+
}
|
894 |
+
$reflection = new ReflectionMethod( $callback[0], $callback[1] );
|
895 |
+
} elseif ( is_object( $callback ) && ( 'Closure' === get_class( $callback ) ) ) {
|
896 |
+
$reflection = new ReflectionFunction( $callback );
|
897 |
+
}
|
898 |
+
} catch ( Exception $e ) {
|
899 |
+
return null;
|
900 |
+
}
|
901 |
+
|
902 |
+
if ( ! $reflection ) {
|
903 |
+
return null;
|
904 |
+
}
|
905 |
+
|
906 |
+
$source = compact( 'reflection' );
|
907 |
+
|
908 |
+
$file = $reflection->getFileName();
|
909 |
+
if ( $file ) {
|
910 |
+
$file = wp_normalize_path( $file );
|
911 |
+
$slug_pattern = '([^/]+)';
|
912 |
+
if ( preg_match( ':' . preg_quote( trailingslashit( wp_normalize_path( WP_PLUGIN_DIR ) ), ':' ) . $slug_pattern . ':s', $file, $matches ) ) {
|
913 |
+
$source['type'] = 'plugin';
|
914 |
+
$source['name'] = $matches[1];
|
915 |
+
} elseif ( preg_match( ':' . preg_quote( trailingslashit( wp_normalize_path( get_theme_root() ) ), ':' ) . $slug_pattern . ':s', $file, $matches ) ) {
|
916 |
+
$source['type'] = 'theme';
|
917 |
+
$source['name'] = $matches[1];
|
918 |
+
} elseif ( preg_match( ':' . preg_quote( trailingslashit( wp_normalize_path( WPMU_PLUGIN_DIR ) ), ':' ) . $slug_pattern . ':s', $file, $matches ) ) {
|
919 |
+
$source['type'] = 'mu-plugin';
|
920 |
+
$source['name'] = $matches[1];
|
921 |
+
} elseif ( preg_match( ':' . preg_quote( trailingslashit( wp_normalize_path( ABSPATH ) ), ':' ) . '(wp-admin|wp-includes)/:s', $file, $matches ) ) {
|
922 |
+
$source['type'] = 'core';
|
923 |
+
$source['name'] = $matches[1];
|
924 |
+
}
|
925 |
+
}
|
926 |
+
|
927 |
+
if ( $class_name ) {
|
928 |
+
$source['function'] = $class_name . '::' . $reflection->getName();
|
929 |
+
} else {
|
930 |
+
$source['function'] = $reflection->getName();
|
931 |
+
}
|
932 |
+
|
933 |
+
return $source;
|
934 |
+
}
|
935 |
+
|
936 |
+
/**
|
937 |
+
* Check whether or not output buffering is currently possible.
|
938 |
+
*
|
939 |
+
* This is to guard against a fatal error: "ob_start(): Cannot use output buffering in output buffering display handlers".
|
940 |
+
*
|
941 |
+
* @return bool Whether output buffering is allowed.
|
942 |
+
*/
|
943 |
+
public static function can_output_buffer() {
|
944 |
+
|
945 |
+
// Output buffering for validation can only be done while overall output buffering is being done for the response.
|
946 |
+
if ( ! AMP_Theme_Support::is_output_buffering() ) {
|
947 |
+
return false;
|
948 |
+
}
|
949 |
+
|
950 |
+
// Abort when in shutdown since output has finished, when we're likely in the overall output buffering display handler.
|
951 |
+
if ( did_action( 'shutdown' ) ) {
|
952 |
+
return false;
|
953 |
+
}
|
954 |
+
|
955 |
+
// Check if any functions in call stack are output buffering display handlers.
|
956 |
+
$called_functions = array();
|
957 |
+
if ( defined( 'DEBUG_BACKTRACE_IGNORE_ARGS' ) ) {
|
958 |
+
$arg = DEBUG_BACKTRACE_IGNORE_ARGS; // phpcs:ignore PHPCompatibility.PHP.NewConstants.debug_backtrace_ignore_argsFound
|
959 |
+
} else {
|
960 |
+
$arg = false;
|
961 |
+
}
|
962 |
+
$backtrace = debug_backtrace( $arg ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace -- Only way to find out if we are in a buffering display handler.
|
963 |
+
foreach ( $backtrace as $call_stack ) {
|
964 |
+
$called_functions[] = '{closure}' === $call_stack['function'] ? 'Closure::__invoke' : $call_stack['function'];
|
965 |
+
}
|
966 |
+
return 0 === count( array_intersect( ob_list_handlers(), $called_functions ) );
|
967 |
+
}
|
968 |
+
|
969 |
+
/**
|
970 |
+
* Wraps a callback in comments if it outputs markup.
|
971 |
+
*
|
972 |
+
* If the sanitizer removes markup,
|
973 |
+
* this indicates which plugin it was from.
|
974 |
+
* The call_user_func_array() logic is mainly copied from WP_Hook:apply_filters().
|
975 |
+
*
|
976 |
+
* @param array $callback {
|
977 |
+
* The callback data.
|
978 |
+
*
|
979 |
+
* @type callable $function
|
980 |
+
* @type int $accepted_args
|
981 |
+
* @type array $source
|
982 |
+
* }
|
983 |
+
* @return closure $wrapped_callback The callback, wrapped in comments.
|
984 |
+
*/
|
985 |
+
public static function wrapped_callback( $callback ) {
|
986 |
+
return function() use ( $callback ) {
|
987 |
+
global $wp_styles, $wp_scripts;
|
988 |
+
|
989 |
+
$function = $callback['function'];
|
990 |
+
$accepted_args = $callback['accepted_args'];
|
991 |
+
$args = func_get_args();
|
992 |
+
|
993 |
+
$before_styles_enqueued = array();
|
994 |
+
if ( isset( $wp_styles ) && isset( $wp_styles->queue ) ) {
|
995 |
+
$before_styles_enqueued = $wp_styles->queue;
|
996 |
+
}
|
997 |
+
$before_scripts_enqueued = array();
|
998 |
+
if ( isset( $wp_scripts ) && isset( $wp_scripts->queue ) ) {
|
999 |
+
$before_scripts_enqueued = $wp_scripts->queue;
|
1000 |
+
}
|
1001 |
+
|
1002 |
+
// Wrap the markup output of (action) hooks in source comments.
|
1003 |
+
AMP_Validation_Utils::$hook_source_stack[] = $callback['source'];
|
1004 |
+
$has_buffer_started = false;
|
1005 |
+
if ( AMP_Validation_Utils::can_output_buffer() ) {
|
1006 |
+
$has_buffer_started = ob_start( array( __CLASS__, 'wrap_buffer_with_source_comments' ) );
|
1007 |
+
}
|
1008 |
+
$result = call_user_func_array( $function, array_slice( $args, 0, intval( $accepted_args ) ) );
|
1009 |
+
if ( $has_buffer_started ) {
|
1010 |
+
ob_end_flush();
|
1011 |
+
}
|
1012 |
+
array_pop( AMP_Validation_Utils::$hook_source_stack );
|
1013 |
+
|
1014 |
+
// Keep track of which source enqueued the styles.
|
1015 |
+
if ( isset( $wp_styles ) && isset( $wp_styles->queue ) ) {
|
1016 |
+
foreach ( array_diff( $wp_styles->queue, $before_styles_enqueued ) as $handle ) {
|
1017 |
+
AMP_Validation_Utils::$enqueued_style_sources[ $handle ][] = $callback['source'];
|
1018 |
+
}
|
1019 |
+
}
|
1020 |
+
|
1021 |
+
// Keep track of which source enqueued the scripts, and immediately report validity .
|
1022 |
+
if ( isset( $wp_scripts ) && isset( $wp_scripts->queue ) ) {
|
1023 |
+
foreach ( array_diff( $wp_scripts->queue, $before_scripts_enqueued ) as $handle ) {
|
1024 |
+
AMP_Validation_Utils::$enqueued_script_sources[ $handle ][] = $callback['source'];
|
1025 |
+
|
1026 |
+
// Flag all scripts not loaded from the AMP CDN as validation errors.
|
1027 |
+
if ( isset( $wp_scripts->registered[ $handle ] ) && 0 !== strpos( $wp_scripts->registered[ $handle ]->src, 'https://cdn.ampproject.org/' ) ) {
|
1028 |
+
self::add_validation_error( array(
|
1029 |
+
'code' => self::ENQUEUED_SCRIPT_CODE,
|
1030 |
+
'handle' => $handle,
|
1031 |
+
'dependency' => $wp_scripts->registered[ $handle ],
|
1032 |
+
'sources' => array(
|
1033 |
+
$callback['source'],
|
1034 |
+
),
|
1035 |
+
) );
|
1036 |
+
}
|
1037 |
+
}
|
1038 |
+
}
|
1039 |
+
|
1040 |
+
return $result;
|
1041 |
+
};
|
1042 |
+
}
|
1043 |
+
|
1044 |
+
/**
|
1045 |
+
* Wrap output buffer with source comments.
|
1046 |
+
*
|
1047 |
+
* A key reason for why this is a method and not a closure is so that
|
1048 |
+
* the can_output_buffer method will be able to identify it by name.
|
1049 |
+
*
|
1050 |
+
* @since 0.7
|
1051 |
+
* @todo Is duplicate of \AMP_Validation_Utils::decorate_filter_source()?
|
1052 |
+
*
|
1053 |
+
* @param string $output Output buffer.
|
1054 |
+
* @return string Output buffer conditionally wrapped with source comments.
|
1055 |
+
*/
|
1056 |
+
public static function wrap_buffer_with_source_comments( $output ) {
|
1057 |
+
if ( empty( self::$hook_source_stack ) ) {
|
1058 |
+
return $output;
|
1059 |
+
}
|
1060 |
+
|
1061 |
+
$source = self::$hook_source_stack[ count( self::$hook_source_stack ) - 1 ];
|
1062 |
+
|
1063 |
+
// Wrap output that contains HTML tags (as opposed to actions that trigger in HTML attributes).
|
1064 |
+
if ( ! empty( $output ) && preg_match( '/<.+?>/s', $output ) ) {
|
1065 |
+
$output = implode( '', array(
|
1066 |
+
self::get_source_comment( $source, true ),
|
1067 |
+
$output,
|
1068 |
+
self::get_source_comment( $source, false ),
|
1069 |
+
) );
|
1070 |
+
}
|
1071 |
+
return $output;
|
1072 |
+
}
|
1073 |
+
|
1074 |
+
/**
|
1075 |
+
* Output a removed set, each wrapped in <code></code>.
|
1076 |
+
*
|
1077 |
+
* @param array[][] $set {
|
1078 |
+
* The removed elements to output.
|
1079 |
+
*
|
1080 |
+
* @type string $name The name of the source.
|
1081 |
+
* @type string $count The number that were invalid.
|
1082 |
+
* }
|
1083 |
+
* @return void
|
1084 |
+
*/
|
1085 |
+
protected static function output_removed_set( $set ) {
|
1086 |
+
$items = array();
|
1087 |
+
foreach ( $set as $name => $count ) {
|
1088 |
+
if ( 1 === intval( $count ) ) {
|
1089 |
+
$items[] = sprintf( '<code>%s</code>', esc_html( $name ) );
|
1090 |
+
} else {
|
1091 |
+
$items[] = sprintf( '<code>%s</code> (%d)', esc_html( $name ), $count );
|
1092 |
+
}
|
1093 |
+
}
|
1094 |
+
echo implode( ', ', $items ); // WPCS: XSS OK.
|
1095 |
+
}
|
1096 |
+
|
1097 |
+
/**
|
1098 |
+
* Whether to validate the front end response.
|
1099 |
+
*
|
1100 |
+
* Either the user has the capability and the query var is present.
|
1101 |
+
*
|
1102 |
+
* @return boolean Whether to validate.
|
1103 |
+
*/
|
1104 |
+
public static function should_validate_response() {
|
1105 |
+
return self::has_cap() && isset( $_GET[ self::VALIDATE_QUERY_VAR ] ); // WPCS: CSRF ok.
|
1106 |
+
}
|
1107 |
+
|
1108 |
+
/**
|
1109 |
+
* Finalize validation.
|
1110 |
+
*
|
1111 |
+
* @param DOMDocument $dom Document.
|
1112 |
+
* @param array $args {
|
1113 |
+
* Args.
|
1114 |
+
*
|
1115 |
+
* @type bool $remove_source_comments Whether source comments should be removed. Defaults to true.
|
1116 |
+
* @type bool $append_validation_status_comment Whether the validation errors should be appended as an HTML comment. Defaults to true.
|
1117 |
+
* }
|
1118 |
+
*/
|
1119 |
+
public static function finalize_validation( DOMDocument $dom, $args = array() ) {
|
1120 |
+
$args = array_merge(
|
1121 |
+
array(
|
1122 |
+
'remove_source_comments' => true,
|
1123 |
+
'append_validation_status_comment' => true,
|
1124 |
+
),
|
1125 |
+
$args
|
1126 |
+
);
|
1127 |
+
|
1128 |
+
if ( $args['remove_source_comments'] ) {
|
1129 |
+
self::remove_source_comments( $dom );
|
1130 |
+
}
|
1131 |
+
|
1132 |
+
if ( $args['append_validation_status_comment'] ) {
|
1133 |
+
$encoded = wp_json_encode( self::$validation_errors, 128 /* JSON_PRETTY_PRINT */ );
|
1134 |
+
$encoded = str_replace( '--', '\u002d\u002d', $encoded ); // Prevent "--" in strings from breaking out of HTML comments.
|
1135 |
+
$comment = $dom->createComment( 'AMP_VALIDATION_ERRORS:' . $encoded . "\n" );
|
1136 |
+
$dom->documentElement->appendChild( $comment );
|
1137 |
+
}
|
1138 |
+
}
|
1139 |
+
|
1140 |
+
/**
|
1141 |
+
* Adds the validation callback if front-end validation is needed.
|
1142 |
+
*
|
1143 |
+
* @param array $sanitizers The AMP sanitizers.
|
1144 |
+
* @return array $sanitizers The filtered AMP sanitizers.
|
1145 |
+
*/
|
1146 |
+
public static function add_validation_callback( $sanitizers ) {
|
1147 |
+
foreach ( $sanitizers as $sanitizer => $args ) {
|
1148 |
+
$sanitizers[ $sanitizer ] = array_merge(
|
1149 |
+
$args,
|
1150 |
+
array(
|
1151 |
+
'validation_error_callback' => __CLASS__ . '::add_validation_error',
|
1152 |
+
)
|
1153 |
+
);
|
1154 |
+
}
|
1155 |
+
return $sanitizers;
|
1156 |
+
}
|
1157 |
+
|
1158 |
+
/**
|
1159 |
+
* Registers the post type to store the validation errors.
|
1160 |
+
*
|
1161 |
+
* @return void.
|
1162 |
+
*/
|
1163 |
+
public static function register_post_type() {
|
1164 |
+
$post_type = register_post_type(
|
1165 |
+
self::POST_TYPE_SLUG,
|
1166 |
+
array(
|
1167 |
+
'labels' => array(
|
1168 |
+
'name' => _x( 'Validation Status', 'post type general name', 'amp' ),
|
1169 |
+
'singular_name' => __( 'validation error', 'amp' ),
|
1170 |
+
'not_found' => __( 'No validation errors found', 'amp' ),
|
1171 |
+
'not_found_in_trash' => __( 'No validation errors found in trash', 'amp' ),
|
1172 |
+
'search_items' => __( 'Search statuses', 'amp' ),
|
1173 |
+
'edit_item' => __( 'Validation Status', 'amp' ),
|
1174 |
+
),
|
1175 |
+
'supports' => false,
|
1176 |
+
'public' => false,
|
1177 |
+
'show_ui' => true,
|
1178 |
+
'show_in_menu' => AMP_Options_Manager::OPTION_NAME,
|
1179 |
+
)
|
1180 |
+
);
|
1181 |
+
|
1182 |
+
// Hide the add new post link.
|
1183 |
+
$post_type->cap->create_posts = 'do_not_allow';
|
1184 |
+
}
|
1185 |
+
|
1186 |
+
/**
|
1187 |
+
* Stores the validation errors.
|
1188 |
+
*
|
1189 |
+
* After the preprocessors run, this gets the validation response if the query var is present.
|
1190 |
+
* It then stores the response in a custom post type.
|
1191 |
+
* If there's already an error post for the URL, but there's no error anymore, it deletes it.
|
1192 |
+
*
|
1193 |
+
* @param array $validation_errors Validation errors.
|
1194 |
+
* @param string $url URL on which the validation errors occurred.
|
1195 |
+
* @return int|null $post_id The post ID of the custom post type used, or null.
|
1196 |
+
* @global WP $wp
|
1197 |
+
*/
|
1198 |
+
public static function store_validation_errors( $validation_errors, $url ) {
|
1199 |
+
$post_for_this_url = self::get_validation_status_post( $url );
|
1200 |
+
|
1201 |
+
// Since there are no validation errors and there is an existing $existing_post_id, just delete the post.
|
1202 |
+
if ( empty( $validation_errors ) ) {
|
1203 |
+
if ( $post_for_this_url ) {
|
1204 |
+
wp_delete_post( $post_for_this_url->ID, true );
|
1205 |
+
}
|
1206 |
+
return null;
|
1207 |
+
}
|
1208 |
+
|
1209 |
+
$encoded_errors = wp_json_encode( $validation_errors );
|
1210 |
+
$post_name = md5( $encoded_errors );
|
1211 |
+
|
1212 |
+
// If the post name is unchanged then the errors are the same and there is nothing to do.
|
1213 |
+
if ( $post_for_this_url && $post_for_this_url->post_name === $post_name ) {
|
1214 |
+
return $post_for_this_url->ID;
|
1215 |
+
}
|
1216 |
+
|
1217 |
+
// If there already exists a post for the given validation errors, just amend the $url to the existing post.
|
1218 |
+
$post_for_other_url = get_page_by_path( $post_name, OBJECT, self::POST_TYPE_SLUG );
|
1219 |
+
if ( ! $post_for_other_url ) {
|
1220 |
+
$post_for_other_url = get_page_by_path( $post_name . '__trashed', OBJECT, self::POST_TYPE_SLUG );
|
1221 |
+
}
|
1222 |
+
if ( $post_for_other_url ) {
|
1223 |
+
if ( 'trash' === $post_for_other_url->post_status ) {
|
1224 |
+
wp_untrash_post( $post_for_other_url->ID );
|
1225 |
+
}
|
1226 |
+
if ( ! in_array( $url, get_post_meta( $post_for_other_url->ID, self::AMP_URL_META, false ), true ) ) {
|
1227 |
+
add_post_meta( $post_for_other_url->ID, self::AMP_URL_META, wp_slash( $url ), false );
|
1228 |
+
}
|
1229 |
+
return $post_for_other_url->ID;
|
1230 |
+
}
|
1231 |
+
|
1232 |
+
// Otherwise, create a new validation status post, or update the existing one.
|
1233 |
+
$post_id = wp_insert_post( wp_slash( array(
|
1234 |
+
'ID' => $post_for_this_url ? $post_for_this_url->ID : null,
|
1235 |
+
'post_type' => self::POST_TYPE_SLUG,
|
1236 |
+
'post_title' => $url,
|
1237 |
+
'post_name' => $post_name,
|
1238 |
+
'post_content' => $encoded_errors,
|
1239 |
+
'post_status' => 'publish',
|
1240 |
+
) ) );
|
1241 |
+
if ( ! $post_id ) {
|
1242 |
+
return null;
|
1243 |
+
}
|
1244 |
+
if ( ! in_array( $url, get_post_meta( $post_id, self::AMP_URL_META, false ), true ) ) {
|
1245 |
+
add_post_meta( $post_id, self::AMP_URL_META, wp_slash( $url ), false );
|
1246 |
+
}
|
1247 |
+
return $post_id;
|
1248 |
+
}
|
1249 |
+
|
1250 |
+
/**
|
1251 |
+
* Gets the existing custom post that stores errors for the $url, if it exists.
|
1252 |
+
*
|
1253 |
+
* @param string $url The URL of the post.
|
1254 |
+
* @return WP_Post|null The post of the existing custom post, or null.
|
1255 |
+
*/
|
1256 |
+
public static function get_validation_status_post( $url ) {
|
1257 |
+
if ( ! post_type_exists( self::POST_TYPE_SLUG ) ) {
|
1258 |
+
return null;
|
1259 |
+
}
|
1260 |
+
$query = new WP_Query( array(
|
1261 |
+
'post_type' => self::POST_TYPE_SLUG,
|
1262 |
+
'post_status' => 'publish',
|
1263 |
+
'posts_per_page' => 1,
|
1264 |
+
'meta_query' => array(
|
1265 |
+
array(
|
1266 |
+
'key' => self::AMP_URL_META,
|
1267 |
+
'value' => $url,
|
1268 |
+
),
|
1269 |
+
),
|
1270 |
+
) );
|
1271 |
+
return array_shift( $query->posts );
|
1272 |
+
}
|
1273 |
+
|
1274 |
+
/**
|
1275 |
+
* Validates the latest published post.
|
1276 |
+
*
|
1277 |
+
* @return array|WP_Error The validation errors, or WP_Error.
|
1278 |
+
*/
|
1279 |
+
public static function validate_after_plugin_activation() {
|
1280 |
+
$url = amp_admin_get_preview_permalink();
|
1281 |
+
if ( ! $url ) {
|
1282 |
+
return new WP_Error( 'no_published_post_url_available' );
|
1283 |
+
}
|
1284 |
+
$validation_errors = self::validate_url( $url );
|
1285 |
+
if ( is_array( $validation_errors ) && count( $validation_errors ) > 0 ) {
|
1286 |
+
self::store_validation_errors( $validation_errors, $url );
|
1287 |
+
set_transient( self::PLUGIN_ACTIVATION_VALIDATION_ERRORS_TRANSIENT_KEY, $validation_errors, 60 );
|
1288 |
+
} else {
|
1289 |
+
delete_transient( self::PLUGIN_ACTIVATION_VALIDATION_ERRORS_TRANSIENT_KEY );
|
1290 |
+
}
|
1291 |
+
return $validation_errors;
|
1292 |
+
}
|
1293 |
+
|
1294 |
+
/**
|
1295 |
+
* Validates a given URL.
|
1296 |
+
*
|
1297 |
+
* The validation errors will be stored in the validation status custom post type,
|
1298 |
+
* as well as in a transient.
|
1299 |
+
*
|
1300 |
+
* @param string $url The URL to validate.
|
1301 |
+
* @return array|WP_Error The validation errors, or WP_Error on error.
|
1302 |
+
*/
|
1303 |
+
public static function validate_url( $url ) {
|
1304 |
+
$validation_url = add_query_arg(
|
1305 |
+
array(
|
1306 |
+
self::VALIDATE_QUERY_VAR => 1,
|
1307 |
+
self::CACHE_BUST_QUERY_VAR => wp_rand(),
|
1308 |
+
),
|
1309 |
+
$url
|
1310 |
+
);
|
1311 |
+
|
1312 |
+
$r = wp_remote_get( $validation_url, array(
|
1313 |
+
'cookies' => wp_unslash( $_COOKIE ),
|
1314 |
+
'sslverify' => false,
|
1315 |
+
'headers' => array(
|
1316 |
+
'Cache-Control' => 'no-cache',
|
1317 |
+
),
|
1318 |
+
) );
|
1319 |
+
if ( is_wp_error( $r ) ) {
|
1320 |
+
return $r;
|
1321 |
+
}
|
1322 |
+
if ( wp_remote_retrieve_response_code( $r ) >= 400 ) {
|
1323 |
+
return new WP_Error(
|
1324 |
+
wp_remote_retrieve_response_code( $r ),
|
1325 |
+
wp_remote_retrieve_response_message( $r )
|
1326 |
+
);
|
1327 |
+
}
|
1328 |
+
$response = wp_remote_retrieve_body( $r );
|
1329 |
+
if ( ! preg_match( '#</body>.*?<!--\s*AMP_VALIDATION_ERRORS\s*:\s*(\[.*?\])\s*-->#s', $response, $matches ) ) {
|
1330 |
+
return new WP_Error( 'response_comment_absent' );
|
1331 |
+
}
|
1332 |
+
$validation_errors = json_decode( $matches[1], true );
|
1333 |
+
if ( ! is_array( $validation_errors ) ) {
|
1334 |
+
return new WP_Error( 'malformed_json_validation_errors' );
|
1335 |
+
}
|
1336 |
+
|
1337 |
+
return $validation_errors;
|
1338 |
+
}
|
1339 |
+
|
1340 |
+
/**
|
1341 |
+
* On activating a plugin, display a notice if a plugin causes an AMP validation error.
|
1342 |
+
*
|
1343 |
+
* @return void
|
1344 |
+
*/
|
1345 |
+
public static function plugin_notice() {
|
1346 |
+
global $pagenow;
|
1347 |
+
if ( ( 'plugins.php' === $pagenow ) && ( ! empty( $_GET['activate'] ) || ! empty( $_GET['activate-multi'] ) ) ) { // WPCS: CSRF ok.
|
1348 |
+
$validation_errors = get_transient( self::PLUGIN_ACTIVATION_VALIDATION_ERRORS_TRANSIENT_KEY );
|
1349 |
+
if ( empty( $validation_errors ) || ! is_array( $validation_errors ) ) {
|
1350 |
+
return;
|
1351 |
+
}
|
1352 |
+
delete_transient( self::PLUGIN_ACTIVATION_VALIDATION_ERRORS_TRANSIENT_KEY );
|
1353 |
+
$errors = self::summarize_validation_errors( $validation_errors );
|
1354 |
+
$invalid_plugins = isset( $errors[ self::SOURCES_INVALID_OUTPUT ]['plugin'] ) ? array_unique( $errors[ self::SOURCES_INVALID_OUTPUT ]['plugin'] ) : null;
|
1355 |
+
if ( isset( $invalid_plugins ) ) {
|
1356 |
+
$reported_plugins = array();
|
1357 |
+
foreach ( $invalid_plugins as $plugin ) {
|
1358 |
+
$reported_plugins[] = sprintf( '<code>%s</code>', esc_html( $plugin ) );
|
1359 |
+
}
|
1360 |
+
|
1361 |
+
$more_details_link = sprintf(
|
1362 |
+
'<a href="%s">%s</a>',
|
1363 |
+
esc_url( add_query_arg(
|
1364 |
+
'post_type',
|
1365 |
+
self::POST_TYPE_SLUG,
|
1366 |
+
admin_url( 'edit.php' )
|
1367 |
+
) ),
|
1368 |
+
__( 'More details', 'amp' )
|
1369 |
+
);
|
1370 |
+
printf(
|
1371 |
+
'<div class="notice notice-warning is-dismissible"><p>%s %s %s</p><button type="button" class="notice-dismiss"><span class="screen-reader-text">%s</span></button></div>',
|
1372 |
+
esc_html( _n( 'Warning: The following plugin may be incompatible with AMP:', 'Warning: The following plugins may be incompatible with AMP: ', count( $invalid_plugins ), 'amp' ) ),
|
1373 |
+
implode( ', ', $reported_plugins ),
|
1374 |
+
$more_details_link,
|
1375 |
+
esc_html__( 'Dismiss this notice.', 'amp' )
|
1376 |
+
); // WPCS: XSS ok.
|
1377 |
+
}
|
1378 |
+
}
|
1379 |
+
}
|
1380 |
+
|
1381 |
+
/**
|
1382 |
+
* Adds post columns to the UI for the validation errors.
|
1383 |
+
*
|
1384 |
+
* @param array $columns The post columns.
|
1385 |
+
* @return array $columns The new post columns.
|
1386 |
+
*/
|
1387 |
+
public static function add_post_columns( $columns ) {
|
1388 |
+
$columns = array_merge(
|
1389 |
+
$columns,
|
1390 |
+
array(
|
1391 |
+
'url_count' => esc_html__( 'Count', 'amp' ),
|
1392 |
+
self::REMOVED_ELEMENTS => esc_html__( 'Removed Elements', 'amp' ),
|
1393 |
+
self::REMOVED_ATTRIBUTES => esc_html__( 'Removed Attributes', 'amp' ),
|
1394 |
+
self::SOURCES_INVALID_OUTPUT => esc_html__( 'Incompatible Sources', 'amp' ),
|
1395 |
+
)
|
1396 |
+
);
|
1397 |
+
|
1398 |
+
// Move date to end.
|
1399 |
+
if ( isset( $columns['date'] ) ) {
|
1400 |
+
$date = $columns['date'];
|
1401 |
+
unset( $columns['date'] );
|
1402 |
+
$columns['date'] = $date;
|
1403 |
+
}
|
1404 |
+
|
1405 |
+
return $columns;
|
1406 |
+
}
|
1407 |
+
|
1408 |
+
/**
|
1409 |
+
* Outputs custom columns in the /wp-admin UI for the AMP validation errors.
|
1410 |
+
*
|
1411 |
+
* @param string $column_name The name of the column.
|
1412 |
+
* @param int $post_id The ID of the post for the column.
|
1413 |
+
* @return void
|
1414 |
+
*/
|
1415 |
+
public static function output_custom_column( $column_name, $post_id ) {
|
1416 |
+
$post = get_post( $post_id );
|
1417 |
+
if ( self::POST_TYPE_SLUG !== $post->post_type ) {
|
1418 |
+
return;
|
1419 |
+
}
|
1420 |
+
$validation_errors = json_decode( $post->post_content, true );
|
1421 |
+
if ( ! is_array( $validation_errors ) ) {
|
1422 |
+
return;
|
1423 |
+
}
|
1424 |
+
$errors = self::summarize_validation_errors( $validation_errors );
|
1425 |
+
$urls = get_post_meta( $post_id, self::AMP_URL_META, false );
|
1426 |
+
|
1427 |
+
switch ( $column_name ) {
|
1428 |
+
case 'url_count':
|
1429 |
+
echo count( $urls );
|
1430 |
+
break;
|
1431 |
+
case self::REMOVED_ELEMENTS:
|
1432 |
+
if ( ! empty( $errors[ self::REMOVED_ELEMENTS ] ) ) {
|
1433 |
+
self::output_removed_set( $errors[ self::REMOVED_ELEMENTS ] );
|
1434 |
+
} else {
|
1435 |
+
esc_html_e( '--', 'amp' );
|
1436 |
+
}
|
1437 |
+
break;
|
1438 |
+
case self::REMOVED_ATTRIBUTES:
|
1439 |
+
if ( ! empty( $errors[ self::REMOVED_ATTRIBUTES ] ) ) {
|
1440 |
+
self::output_removed_set( $errors[ self::REMOVED_ATTRIBUTES ] );
|
1441 |
+
} else {
|
1442 |
+
esc_html_e( '--', 'amp' );
|
1443 |
+
}
|
1444 |
+
break;
|
1445 |
+
case self::SOURCES_INVALID_OUTPUT:
|
1446 |
+
if ( isset( $errors[ self::SOURCES_INVALID_OUTPUT ] ) ) {
|
1447 |
+
$sources = array();
|
1448 |
+
foreach ( $errors[ self::SOURCES_INVALID_OUTPUT ] as $type => $names ) {
|
1449 |
+
foreach ( array_unique( $names ) as $name ) {
|
1450 |
+
$sources[] = sprintf( '%s: <code>%s</code>', esc_html( $type ), esc_html( $name ) );
|
1451 |
+
}
|
1452 |
+
}
|
1453 |
+
echo implode( ', ', $sources ); // WPCS: XSS ok.
|
1454 |
+
}
|
1455 |
+
break;
|
1456 |
+
}
|
1457 |
+
}
|
1458 |
+
|
1459 |
+
/**
|
1460 |
+
* Adds a 'Recheck' link to the edit.php row actions.
|
1461 |
+
*
|
1462 |
+
* The logic to add the new action is mainly copied from WP_Posts_List_Table::handle_row_actions().
|
1463 |
+
*
|
1464 |
+
* @param array $actions The actions in the edit.php page.
|
1465 |
+
* @param WP_Post $post The post for the actions.
|
1466 |
+
* @return array $actions The filtered actions.
|
1467 |
+
*/
|
1468 |
+
public static function filter_row_actions( $actions, $post ) {
|
1469 |
+
if ( self::POST_TYPE_SLUG !== $post->post_type ) {
|
1470 |
+
return $actions;
|
1471 |
+
}
|
1472 |
+
|
1473 |
+
$actions['edit'] = sprintf(
|
1474 |
+
'<a href="%s">%s</a>',
|
1475 |
+
esc_url( get_edit_post_link( $post ) ),
|
1476 |
+
esc_html__( 'Details', 'amp' )
|
1477 |
+
);
|
1478 |
+
unset( $actions['inline hide-if-no-js'] );
|
1479 |
+
$url = get_post_meta( $post->ID, self::AMP_URL_META, true );
|
1480 |
+
|
1481 |
+
if ( ! empty( $url ) ) {
|
1482 |
+
$actions[ self::RECHECK_ACTION ] = self::get_recheck_link( $post, get_edit_post_link( $post->ID, 'raw' ), $url );
|
1483 |
+
$actions[ self::DEBUG_QUERY_VAR ] = sprintf(
|
1484 |
+
'<a href="%s" aria-label="%s">%s</a>',
|
1485 |
+
esc_url( self::get_debug_url( $url ) ),
|
1486 |
+
esc_attr__( 'Validate URL on frontend but without invalid elements/attributes removed', 'amp' ),
|
1487 |
+
esc_html__( 'Debug', 'amp' )
|
1488 |
+
);
|
1489 |
+
}
|
1490 |
+
|
1491 |
+
return $actions;
|
1492 |
+
}
|
1493 |
+
|
1494 |
+
/**
|
1495 |
+
* Adds a 'Recheck' bulk action to the edit.php page.
|
1496 |
+
*
|
1497 |
+
* @param array $actions The bulk actions in the edit.php page.
|
1498 |
+
* @return array $actions The filtered bulk actions.
|
1499 |
+
*/
|
1500 |
+
public static function add_bulk_action( $actions ) {
|
1501 |
+
unset( $actions['edit'] );
|
1502 |
+
$actions[ self::RECHECK_ACTION ] = esc_html__( 'Recheck', 'amp' );
|
1503 |
+
return $actions;
|
1504 |
+
}
|
1505 |
+
|
1506 |
+
/**
|
1507 |
+
* Handles the 'Recheck' bulk action on the edit.php page.
|
1508 |
+
*
|
1509 |
+
* @param string $redirect The URL of the redirect.
|
1510 |
+
* @param string $action The action.
|
1511 |
+
* @param array $items The items on which to take the action.
|
1512 |
+
* @return string $redirect The filtered URL of the redirect.
|
1513 |
+
*/
|
1514 |
+
public static function handle_bulk_action( $redirect, $action, $items ) {
|
1515 |
+
if ( self::RECHECK_ACTION !== $action ) {
|
1516 |
+
return $redirect;
|
1517 |
+
}
|
1518 |
+
$remaining_invalid_urls = array();
|
1519 |
+
foreach ( $items as $item ) {
|
1520 |
+
$url = get_post_meta( $item, self::AMP_URL_META, true );
|
1521 |
+
if ( empty( $url ) ) {
|
1522 |
+
continue;
|
1523 |
+
}
|
1524 |
+
|
1525 |
+
$validation_errors = self::validate_url( $url );
|
1526 |
+
if ( ! is_array( $validation_errors ) ) {
|
1527 |
+
continue;
|
1528 |
+
}
|
1529 |
+
|
1530 |
+
self::store_validation_errors( $validation_errors, $url );
|
1531 |
+
if ( ! empty( $validation_errors ) ) {
|
1532 |
+
$remaining_invalid_urls[] = $url;
|
1533 |
+
}
|
1534 |
+
}
|
1535 |
+
|
1536 |
+
// Get the URLs that still have errors after rechecking.
|
1537 |
+
$args = array(
|
1538 |
+
self::URLS_TESTED => count( $items ),
|
1539 |
+
self::REMAINING_ERRORS => empty( $remaining_invalid_urls ) ? '0' : '1',
|
1540 |
+
);
|
1541 |
+
|
1542 |
+
return add_query_arg( $args, $redirect );
|
1543 |
+
}
|
1544 |
+
|
1545 |
+
/**
|
1546 |
+
* Outputs an admin notice after rechecking URL(s) on the custom post page.
|
1547 |
+
*
|
1548 |
+
* @return void
|
1549 |
+
*/
|
1550 |
+
public static function remaining_error_notice() {
|
1551 |
+
if ( ! isset( $_GET[ self::REMAINING_ERRORS ] ) || self::POST_TYPE_SLUG !== get_current_screen()->post_type ) { // WPCS: CSRF ok.
|
1552 |
+
return;
|
1553 |
+
}
|
1554 |
+
|
1555 |
+
$count_urls_tested = isset( $_GET[ self::URLS_TESTED ] ) ? intval( $_GET[ self::URLS_TESTED ] ) : 1; // WPCS: CSRF ok.
|
1556 |
+
$errors_remain = ! empty( $_GET[ self::REMAINING_ERRORS ] ); // WPCS: CSRF ok.
|
1557 |
+
if ( $errors_remain ) {
|
1558 |
+
$class = 'notice-warning';
|
1559 |
+
$message = _n( 'The rechecked URL still has validation errors.', 'The rechecked URLs still have validation errors.', $count_urls_tested, 'amp' );
|
1560 |
+
} else {
|
1561 |
+
$message = _n( 'The rechecked URL has no validation errors.', 'The rechecked URLs have no validation errors.', $count_urls_tested, 'amp' );
|
1562 |
+
$class = 'updated';
|
1563 |
+
}
|
1564 |
+
|
1565 |
+
printf(
|
1566 |
+
'<div class="notice is-dismissible %s"><p>%s</p><button type="button" class="notice-dismiss"><span class="screen-reader-text">%s</span></button></div>',
|
1567 |
+
esc_attr( $class ),
|
1568 |
+
esc_html( $message ),
|
1569 |
+
esc_html__( 'Dismiss this notice.', 'amp' )
|
1570 |
+
);
|
1571 |
+
}
|
1572 |
+
|
1573 |
+
/**
|
1574 |
+
* Handles clicking 'recheck' on the inline post actions.
|
1575 |
+
*
|
1576 |
+
* @param int $post_id The post ID of the recheck.
|
1577 |
+
* @return void
|
1578 |
+
*/
|
1579 |
+
public static function handle_inline_recheck( $post_id ) {
|
1580 |
+
check_admin_referer( self::NONCE_ACTION . $post_id );
|
1581 |
+
$url = get_post_meta( $post_id, self::AMP_URL_META, true );
|
1582 |
+
if ( isset( $_GET['recheck_url'] ) ) {
|
1583 |
+
$url = wp_validate_redirect( wp_unslash( $_GET['recheck_url'] ) );
|
1584 |
+
}
|
1585 |
+
$validation_errors = self::validate_url( $url );
|
1586 |
+
$remaining_errors = true;
|
1587 |
+
if ( is_array( $validation_errors ) ) {
|
1588 |
+
self::store_validation_errors( $validation_errors, $url );
|
1589 |
+
$remaining_errors = ! empty( $validation_errors );
|
1590 |
+
}
|
1591 |
+
|
1592 |
+
$redirect = wp_get_referer();
|
1593 |
+
if ( ! $redirect || empty( $validation_errors ) ) {
|
1594 |
+
// If there are no remaining errors and the post was deleted, redirect to edit.php instead of post.php.
|
1595 |
+
$redirect = add_query_arg(
|
1596 |
+
'post_type',
|
1597 |
+
self::POST_TYPE_SLUG,
|
1598 |
+
admin_url( 'edit.php' )
|
1599 |
+
);
|
1600 |
+
}
|
1601 |
+
$args = array(
|
1602 |
+
self::URLS_TESTED => '1',
|
1603 |
+
self::REMAINING_ERRORS => $remaining_errors ? '1' : '0',
|
1604 |
+
);
|
1605 |
+
wp_safe_redirect( add_query_arg( $args, $redirect ) );
|
1606 |
+
exit();
|
1607 |
+
}
|
1608 |
+
|
1609 |
+
/**
|
1610 |
+
* Removes the 'Publish' meta box from the CPT post.php page.
|
1611 |
+
*
|
1612 |
+
* @return void
|
1613 |
+
*/
|
1614 |
+
public static function remove_publish_meta_box() {
|
1615 |
+
remove_meta_box( 'submitdiv', self::POST_TYPE_SLUG, 'side' );
|
1616 |
+
}
|
1617 |
+
|
1618 |
+
/**
|
1619 |
+
* Adds the meta boxes to the CPT post.php page.
|
1620 |
+
*
|
1621 |
+
* @return void
|
1622 |
+
*/
|
1623 |
+
public static function add_meta_boxes() {
|
1624 |
+
add_meta_box( self::VALIDATION_ERRORS_META_BOX, __( 'Validation Errors', 'amp' ), array( __CLASS__, 'print_validation_errors_meta_box' ), self::POST_TYPE_SLUG, 'normal' );
|
1625 |
+
add_meta_box( self::STATUS_META_BOX, __( 'Status', 'amp' ), array( __CLASS__, 'print_status_meta_box' ), self::POST_TYPE_SLUG, 'side' );
|
1626 |
+
}
|
1627 |
+
|
1628 |
+
/**
|
1629 |
+
* Outputs the markup of the side meta box in the CPT post.php page.
|
1630 |
+
*
|
1631 |
+
* This is partially copied from meta-boxes.php.
|
1632 |
+
* Adds 'Published on,' and links to move to trash and recheck.
|
1633 |
+
*
|
1634 |
+
* @param WP_Post $post The post for which to output the box.
|
1635 |
+
* @return void
|
1636 |
+
*/
|
1637 |
+
public static function print_status_meta_box( $post ) {
|
1638 |
+
$redirect_url = add_query_arg(
|
1639 |
+
'post',
|
1640 |
+
$post->ID,
|
1641 |
+
admin_url( 'post.php' )
|
1642 |
+
);
|
1643 |
+
|
1644 |
+
echo '<div id="submitpost" class="submitbox">';
|
1645 |
+
/* translators: Meta box date format */
|
1646 |
+
$date_format = __( 'M j, Y @ H:i', 'default' );
|
1647 |
+
echo '<div class="curtime misc-pub-section"><span id="timestamp">';
|
1648 |
+
/* translators: %s: The date this was published */
|
1649 |
+
printf( __( 'Published on: <b>%s</b>', 'amp' ), esc_html( date_i18n( $date_format, strtotime( $post->post_date ) ) ) ); // WPCS: XSS ok.
|
1650 |
+
echo '</span></div>';
|
1651 |
+
printf( '<div class="misc-pub-section"><a class="submitdelete deletion" href="%s">%s</a></div>', esc_url( get_delete_post_link( $post->ID ) ), esc_html__( 'Move to Trash', 'default' ) );
|
1652 |
+
|
1653 |
+
echo '<div class="misc-pub-section">';
|
1654 |
+
echo self::get_recheck_link( $post, $redirect_url ); // WPCS: XSS ok.
|
1655 |
+
$url = get_post_meta( $post->ID, self::AMP_URL_META, true );
|
1656 |
+
if ( $url ) {
|
1657 |
+
printf(
|
1658 |
+
' | <a href="%s" aria-label="%s">%s</a>',
|
1659 |
+
esc_url( self::get_debug_url( $url ) ),
|
1660 |
+
esc_attr__( 'Validate URL on frontend but without invalid elements/attributes removed', 'amp' ),
|
1661 |
+
esc_html__( 'Debug', 'amp' )
|
1662 |
+
); // WPCS: XSS ok.
|
1663 |
+
}
|
1664 |
+
echo '</div>';
|
1665 |
+
|
1666 |
+
echo '</div><!-- /submitpost -->';
|
1667 |
+
}
|
1668 |
+
|
1669 |
+
/**
|
1670 |
+
* Outputs the full meta box on the CPT post.php page.
|
1671 |
+
*
|
1672 |
+
* This displays the errors stored in the post content.
|
1673 |
+
* These are output as stored, but using <details> elements.
|
1674 |
+
*
|
1675 |
+
* @param WP_Post $post The post for which to output the box.
|
1676 |
+
* @return void
|
1677 |
+
*/
|
1678 |
+
public static function print_validation_errors_meta_box( $post ) {
|
1679 |
+
$errors = json_decode( $post->post_content, true );
|
1680 |
+
$urls = get_post_meta( $post->ID, self::AMP_URL_META, false );
|
1681 |
+
?>
|
1682 |
+
<style>
|
1683 |
+
.amp-validation-errors .detailed {
|
1684 |
+
margin-left: 30px;
|
1685 |
+
}
|
1686 |
+
.amp-validation-errors .amp-recheck {
|
1687 |
+
float: right;
|
1688 |
+
}
|
1689 |
+
</style>
|
1690 |
+
<div class="amp-validation-errors">
|
1691 |
+
<ul>
|
1692 |
+
<?php foreach ( $errors as $error ) : ?>
|
1693 |
+
<?php
|
1694 |
+
$collasped_details = array();
|
1695 |
+
?>
|
1696 |
+
<li>
|
1697 |
+
<details open>
|
1698 |
+
<summary><code><?php echo esc_html( $error['code'] ); ?></code></summary>
|
1699 |
+
<ul class="detailed">
|
1700 |
+
<?php if ( self::INVALID_ELEMENT_CODE === $error['code'] ) : ?>
|
1701 |
+
<li>
|
1702 |
+
<details open>
|
1703 |
+
<summary><?php esc_html_e( 'Removed:', 'amp' ); ?></summary>
|
1704 |
+
<code class="detailed">
|
1705 |
+
<?php
|
1706 |
+
if ( isset( $error['parent_name'] ) ) {
|
1707 |
+
echo esc_html( sprintf( '<%s …>', $error['parent_name'] ) );
|
1708 |
+
}
|
1709 |
+
?>
|
1710 |
+
<mark>
|
1711 |
+
<?php
|
1712 |
+
echo esc_html( sprintf( '<%s', $error['node_name'] ) );
|
1713 |
+
if ( isset( $error['node_attributes'] ) ) {
|
1714 |
+
foreach ( $error['node_attributes'] as $key => $value ) {
|
1715 |
+
printf( ' %s="%s"', esc_html( $key ), esc_html( $value ) );
|
1716 |
+
}
|
1717 |
+
}
|
1718 |
+
echo esc_html( '>…' );
|
1719 |
+
?>
|
1720 |
+
</mark>
|
1721 |
+
</code>
|
1722 |
+
</details>
|
1723 |
+
<?php
|
1724 |
+
$collasped_details[] = 'node_attributes';
|
1725 |
+
$collasped_details[] = 'node_name';
|
1726 |
+
$collasped_details[] = 'parent_name';
|
1727 |
+
?>
|
1728 |
+
</li>
|
1729 |
+
<?php elseif ( self::INVALID_ATTRIBUTE_CODE === $error['code'] ) : ?>
|
1730 |
+
<li>
|
1731 |
+
<details open>
|
1732 |
+
<summary><?php esc_html_e( 'Removed:', 'amp' ); ?></summary>
|
1733 |
+
<code class="detailed">
|
1734 |
+
<?php
|
1735 |
+
if ( isset( $error['parent_name'] ) ) {
|
1736 |
+
echo esc_html( sprintf( '<%s', $error['parent_name'] ) );
|
1737 |
+
}
|
1738 |
+
foreach ( $error['element_attributes'] as $key => $value ) {
|
1739 |
+
if ( $key === $error['node_name'] ) {
|
1740 |
+
echo '<mark>';
|
1741 |
+
}
|
1742 |
+
printf( ' %s="%s"', esc_html( $key ), esc_html( $value ) );
|
1743 |
+
if ( $key === $error['node_name'] ) {
|
1744 |
+
echo '</mark>';
|
1745 |
+
}
|
1746 |
+
}
|
1747 |
+
echo esc_html( '>' );
|
1748 |
+
?>
|
1749 |
+
</code>
|
1750 |
+
</details>
|
1751 |
+
<?php
|
1752 |
+
$collasped_details[] = 'parent_name';
|
1753 |
+
$collasped_details[] = 'element_attributes';
|
1754 |
+
$collasped_details[] = 'node_name';
|
1755 |
+
?>
|
1756 |
+
</li>
|
1757 |
+
<?php endif; ?>
|
1758 |
+
<?php unset( $error['code'] ); ?>
|
1759 |
+
<?php foreach ( $error as $key => $value ) : ?>
|
1760 |
+
<li>
|
1761 |
+
<details <?php echo ! in_array( $key, $collasped_details, true ) ? 'open' : ''; ?>>
|
1762 |
+
<summary><code><?php echo esc_html( $key ); ?></code></summary>
|
1763 |
+
<div class="detailed">
|
1764 |
+
<?php if ( is_string( $value ) ) : ?>
|
1765 |
+
<?php echo esc_html( $value ); ?>
|
1766 |
+
<?php else : ?>
|
1767 |
+
<pre><?php echo esc_html( wp_json_encode( $value, 128 /* JSON_PRETTY_PRINT */ | 64 /* JSON_UNESCAPED_SLASHES */ ) ); ?></pre>
|
1768 |
+
<?php endif; ?>
|
1769 |
+
</div>
|
1770 |
+
</details>
|
1771 |
+
</li>
|
1772 |
+
<?php endforeach; ?>
|
1773 |
+
</ul>
|
1774 |
+
</details>
|
1775 |
+
</li>
|
1776 |
+
<?php endforeach; ?>
|
1777 |
+
</ul>
|
1778 |
+
<hr>
|
1779 |
+
<h3><?php esc_html_e( 'URLs', 'amp' ); ?></h3>
|
1780 |
+
<ul>
|
1781 |
+
<?php foreach ( $urls as $url ) : ?>
|
1782 |
+
<li>
|
1783 |
+
<a href="<?php echo esc_url( $url ); ?>"><?php echo esc_url( $url ); ?></a>
|
1784 |
+
<span class="amp-recheck">
|
1785 |
+
<?php echo self::get_recheck_link( $post, get_edit_post_link( $post->ID, 'raw' ), $url ); // WPCS: XSS ok. ?>
|
1786 |
+
|
|
1787 |
+
<?php
|
1788 |
+
printf(
|
1789 |
+
'<a href="%s" aria-label="%s">%s</a>',
|
1790 |
+
esc_url( self::get_debug_url( $url ) ),
|
1791 |
+
esc_attr__( 'Validate URL on frontend but without invalid elements/attributes removed', 'amp' ),
|
1792 |
+
esc_html__( 'Debug', 'amp' )
|
1793 |
+
)
|
1794 |
+
?>
|
1795 |
+
</span>
|
1796 |
+
</li>
|
1797 |
+
<?php endforeach; ?>
|
1798 |
+
</ul>
|
1799 |
+
</div>
|
1800 |
+
<?php
|
1801 |
+
}
|
1802 |
+
|
1803 |
+
/**
|
1804 |
+
* Get validation debug UR:.
|
1805 |
+
*
|
1806 |
+
* @param string $url URL to to validate and debug.
|
1807 |
+
* @return string Debug URL.
|
1808 |
+
*/
|
1809 |
+
public static function get_debug_url( $url ) {
|
1810 |
+
return add_query_arg(
|
1811 |
+
array(
|
1812 |
+
self::VALIDATE_QUERY_VAR => 1,
|
1813 |
+
self::DEBUG_QUERY_VAR => 1,
|
1814 |
+
),
|
1815 |
+
$url
|
1816 |
+
) . '#development=1';
|
1817 |
+
}
|
1818 |
+
|
1819 |
+
/**
|
1820 |
+
* Gets the link to recheck the post for AMP validity.
|
1821 |
+
*
|
1822 |
+
* Appends a query var to $redirect_url.
|
1823 |
+
* On clicking the link, it checks if errors still exist for $post.
|
1824 |
+
*
|
1825 |
+
* @param WP_Post $post The post storing the validation error.
|
1826 |
+
* @param string $redirect_url The URL of the redirect.
|
1827 |
+
* @param string $recheck_url The URL to check. Optional.
|
1828 |
+
* @return string $link The link to recheck the post.
|
1829 |
+
*/
|
1830 |
+
public static function get_recheck_link( $post, $redirect_url, $recheck_url = null ) {
|
1831 |
+
return sprintf(
|
1832 |
+
'<a href="%s" aria-label="%s">%s</a>',
|
1833 |
+
wp_nonce_url(
|
1834 |
+
add_query_arg(
|
1835 |
+
array(
|
1836 |
+
'action' => self::RECHECK_ACTION,
|
1837 |
+
'recheck_url' => $recheck_url,
|
1838 |
+
),
|
1839 |
+
$redirect_url
|
1840 |
+
),
|
1841 |
+
self::NONCE_ACTION . $post->ID
|
1842 |
+
),
|
1843 |
+
esc_html__( 'Recheck the URL for AMP validity', 'amp' ),
|
1844 |
+
esc_html__( 'Recheck', 'amp' )
|
1845 |
+
);
|
1846 |
+
}
|
1847 |
+
|
1848 |
+
}
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Widget_Archives
|
4 |
+
*
|
5 |
+
* @since 0.7.0
|
6 |
+
* @package AMP
|
7 |
+
*/
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class AMP_Widget_Archives
|
11 |
+
*
|
12 |
+
* @since 0.7.0
|
13 |
+
* @package AMP
|
14 |
+
*/
|
15 |
+
class AMP_Widget_Archives extends WP_Widget_Archives {
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Echoes the markup of the widget.
|
19 |
+
*
|
20 |
+
* Mainly copied from WP_Widget_Archives::widget()
|
21 |
+
* Changes include:
|
22 |
+
* An id for the <form>.
|
23 |
+
* More escaping.
|
24 |
+
* The dropdown is now filtered with 'wp_dropdown_cats.'
|
25 |
+
* This enables adding an 'on' attribute, with the id of the form.
|
26 |
+
* So changing the dropdown value will redirect to the category page, with valid AMP.
|
27 |
+
*
|
28 |
+
* @since 0.7.0
|
29 |
+
*
|
30 |
+
* @param array $args Widget display data.
|
31 |
+
* @param array $instance Data for widget.
|
32 |
+
* @return void.
|
33 |
+
*/
|
34 |
+
public function widget( $args, $instance ) {
|
35 |
+
if ( ! is_amp_endpoint() ) {
|
36 |
+
parent::widget( $args, $instance );
|
37 |
+
return;
|
38 |
+
}
|
39 |
+
|
40 |
+
$c = ! empty( $instance['count'] ) ? '1' : '0';
|
41 |
+
$d = ! empty( $instance['dropdown'] ) ? '1' : '0';
|
42 |
+
|
43 |
+
/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
|
44 |
+
$title = apply_filters( 'widget_title', empty( $instance['title'] ) ? __( 'Archives', 'default' ) : $instance['title'], $instance, $this->id_base );
|
45 |
+
echo wp_kses_post( $args['before_widget'] );
|
46 |
+
if ( $title ) :
|
47 |
+
echo wp_kses_post( $args['before_title'] . $title . $args['after_title'] );
|
48 |
+
endif;
|
49 |
+
|
50 |
+
if ( $d ) :
|
51 |
+
$dropdown_id = "{$this->id_base}-dropdown-{$this->number}";
|
52 |
+
?>
|
53 |
+
<form action="<?php echo esc_url( home_url() ); ?>" method="get" target="_top">
|
54 |
+
<label class="screen-reader-text" for="<?php echo esc_attr( $dropdown_id ); ?>"><?php echo esc_html( $title ); ?></label>
|
55 |
+
<select id="<?php echo esc_attr( $dropdown_id ); ?>" name="archive-dropdown" on="change:AMP.navigateTo(url=event.value)">
|
56 |
+
<?php
|
57 |
+
|
58 |
+
/** This filter is documented in wp-includes/widgets/class-wp-widget-archives.php */
|
59 |
+
$dropdown_args = apply_filters( 'widget_archives_dropdown_args', array(
|
60 |
+
'type' => 'monthly',
|
61 |
+
'format' => 'option',
|
62 |
+
'show_post_count' => $c,
|
63 |
+
) );
|
64 |
+
|
65 |
+
switch ( $dropdown_args['type'] ) {
|
66 |
+
case 'yearly':
|
67 |
+
$label = __( 'Select Year', 'default' );
|
68 |
+
break;
|
69 |
+
case 'monthly':
|
70 |
+
$label = __( 'Select Month', 'default' );
|
71 |
+
break;
|
72 |
+
case 'daily':
|
73 |
+
$label = __( 'Select Day', 'default' );
|
74 |
+
break;
|
75 |
+
case 'weekly':
|
76 |
+
$label = __( 'Select Week', 'default' );
|
77 |
+
break;
|
78 |
+
default:
|
79 |
+
$label = __( 'Select Post', 'default' );
|
80 |
+
break;
|
81 |
+
}
|
82 |
+
?>
|
83 |
+
<option value=""><?php echo esc_attr( $label ); ?></option>
|
84 |
+
<?php wp_get_archives( $dropdown_args ); ?>
|
85 |
+
</select>
|
86 |
+
</form>
|
87 |
+
<?php else : ?>
|
88 |
+
<ul>
|
89 |
+
<?php
|
90 |
+
|
91 |
+
/** This filter is documented in wp-includes/widgets/class-wp-widget-archives.php */
|
92 |
+
wp_get_archives( apply_filters( 'widget_archives_args', array(
|
93 |
+
'type' => 'monthly',
|
94 |
+
'show_post_count' => $c,
|
95 |
+
) ) );
|
96 |
+
?>
|
97 |
+
</ul>
|
98 |
+
<?php
|
99 |
+
endif;
|
100 |
+
echo wp_kses_post( $args['after_widget'] );
|
101 |
+
}
|
102 |
+
|
103 |
+
}
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Widget_Categories
|
4 |
+
*
|
5 |
+
* @since 0.7.0
|
6 |
+
* @package AMP
|
7 |
+
*/
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class AMP_Widget_Categories
|
11 |
+
*
|
12 |
+
* @since 0.7.0
|
13 |
+
* @package AMP
|
14 |
+
*/
|
15 |
+
class AMP_Widget_Categories extends WP_Widget_Categories {
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Echoes the markup of the widget.
|
19 |
+
*
|
20 |
+
* Mainly copied from WP_Widget_Categories::widget()
|
21 |
+
* There's now an id for the <form>.
|
22 |
+
* And the dropdown is now filtered with 'wp_dropdown_cats.'
|
23 |
+
* This enables adding an 'on' attribute, with the id of the form.
|
24 |
+
* So changing the dropdown value will redirect to the category page, with valid AMP.
|
25 |
+
*
|
26 |
+
* @since 0.7.0
|
27 |
+
*
|
28 |
+
* @param array $args Widget display data.
|
29 |
+
* @param array $instance Data for widget.
|
30 |
+
* @return void
|
31 |
+
*/
|
32 |
+
public function widget( $args, $instance ) {
|
33 |
+
if ( ! is_amp_endpoint() ) {
|
34 |
+
parent::widget( $args, $instance );
|
35 |
+
return;
|
36 |
+
}
|
37 |
+
|
38 |
+
static $first_dropdown = true;
|
39 |
+
$title = ! empty( $instance['title'] ) ? $instance['title'] : __( 'Categories', 'default' );
|
40 |
+
/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
|
41 |
+
$title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
|
42 |
+
$c = ! empty( $instance['count'] ) ? '1' : '0';
|
43 |
+
$h = ! empty( $instance['hierarchical'] ) ? '1' : '0';
|
44 |
+
$d = ! empty( $instance['dropdown'] ) ? '1' : '0';
|
45 |
+
echo wp_kses_post( $args['before_widget'] );
|
46 |
+
if ( $title ) {
|
47 |
+
echo wp_kses_post( $args['before_title'] . $title . $args['after_title'] );
|
48 |
+
}
|
49 |
+
$cat_args = array(
|
50 |
+
'orderby' => 'name',
|
51 |
+
'show_count' => $c,
|
52 |
+
'hierarchical' => $h,
|
53 |
+
);
|
54 |
+
if ( $d ) :
|
55 |
+
$form_id = sprintf( 'widget-categories-dropdown-%d', $this->number );
|
56 |
+
printf( '<form action="%s" method="get" target="_top" id="%s">', esc_url( home_url() ), esc_attr( $form_id ) );
|
57 |
+
$dropdown_id = ( $first_dropdown ) ? 'cat' : "{$this->id_base}-dropdown-{$this->number}";
|
58 |
+
$first_dropdown = false;
|
59 |
+
echo '<label class="screen-reader-text" for="' . esc_attr( $dropdown_id ) . '">' . esc_html( $title ) . '</label>';
|
60 |
+
$cat_args['show_option_none'] = __( 'Select Category', 'default' );
|
61 |
+
$cat_args['id'] = $dropdown_id;
|
62 |
+
|
63 |
+
$dropdown = wp_dropdown_categories( array_merge(
|
64 |
+
/** This filter is documented in wp-includes/widgets/class-wp-widget-categories.php */
|
65 |
+
apply_filters( 'widget_categories_dropdown_args', $cat_args, $instance ),
|
66 |
+
array( 'echo' => false )
|
67 |
+
) );
|
68 |
+
$dropdown = preg_replace(
|
69 |
+
'/(?<=<select\b)/',
|
70 |
+
sprintf( '<select on="change:%s.submit"', esc_attr( $form_id ) ),
|
71 |
+
$dropdown,
|
72 |
+
1
|
73 |
+
);
|
74 |
+
echo $dropdown; // WPCS: XSS OK.
|
75 |
+
echo '</form>';
|
76 |
+
else :
|
77 |
+
?>
|
78 |
+
<ul>
|
79 |
+
<?php
|
80 |
+
$cat_args['title_li'] = '';
|
81 |
+
|
82 |
+
/** This filter is documented in wp-includes/widgets/class-wp-widget-categories.php */
|
83 |
+
wp_list_categories( apply_filters( 'widget_categories_args', $cat_args, $instance ) );
|
84 |
+
?>
|
85 |
+
</ul>
|
86 |
+
<?php
|
87 |
+
endif;
|
88 |
+
echo wp_kses_post( $args['after_widget'] );
|
89 |
+
}
|
90 |
+
|
91 |
+
}
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Widget_Media_Video
|
4 |
+
*
|
5 |
+
* @since 0.7.0
|
6 |
+
* @package AMP
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( class_exists( 'WP_Widget_Media_Video' ) ) {
|
10 |
+
/**
|
11 |
+
* Class AMP_Widget_Media_Video
|
12 |
+
*
|
13 |
+
* @since 0.7.0
|
14 |
+
* @package AMP
|
15 |
+
*/
|
16 |
+
class AMP_Widget_Media_Video extends WP_Widget_Media_Video {
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Overrides the parent callback that strips width and height values.
|
20 |
+
*
|
21 |
+
* @param string $html Video shortcode HTML output.
|
22 |
+
* @return string HTML Output.
|
23 |
+
*/
|
24 |
+
public function inject_video_max_width_style( $html ) {
|
25 |
+
if ( is_amp_endpoint() ) {
|
26 |
+
return $html;
|
27 |
+
}
|
28 |
+
return parent::inject_video_max_width_style( $html );
|
29 |
+
}
|
30 |
+
|
31 |
+
}
|
32 |
+
|
33 |
+
}
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Widget_Recent_Comments
|
4 |
+
*
|
5 |
+
* @since 0.7.0
|
6 |
+
* @package AMP
|
7 |
+
*/
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class AMP_Widget_Recent_Comments
|
11 |
+
*
|
12 |
+
* @since 0.7.0
|
13 |
+
* @package AMP
|
14 |
+
*/
|
15 |
+
class AMP_Widget_Recent_Comments extends WP_Widget_Recent_Comments {
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Instantiates the widget, and prevents inline styling.
|
19 |
+
*
|
20 |
+
* @since 0.7.0
|
21 |
+
*/
|
22 |
+
public function __construct() {
|
23 |
+
parent::__construct();
|
24 |
+
add_filter( 'wp_head', array( $this, 'remove_head_style_in_amp' ), 0 );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Prevent recent comments widget style from printing in AMP,
|
29 |
+
*
|
30 |
+
* @since 0.7.0
|
31 |
+
*/
|
32 |
+
public function remove_head_style_in_amp() {
|
33 |
+
if ( is_amp_endpoint() ) {
|
34 |
+
add_filter( 'show_recent_comments_widget_style', '__return_false' );
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
}
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class AMP_Widget_Text
|
4 |
+
*
|
5 |
+
* @since 0.7.0
|
6 |
+
* @package AMP
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( class_exists( 'WP_Widget_Text' ) ) {
|
10 |
+
/**
|
11 |
+
* Class AMP_Widget_Text
|
12 |
+
*
|
13 |
+
* @since 0.7.0
|
14 |
+
* @package AMP
|
15 |
+
*/
|
16 |
+
class AMP_Widget_Text extends WP_Widget_Text {
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Overrides the parent callback that strips width and height attributes.
|
20 |
+
*
|
21 |
+
* @param array $matches The matches returned from preg_replace_callback().
|
22 |
+
* @return string $html The markup, unaltered.
|
23 |
+
*/
|
24 |
+
public function inject_video_max_width_style( $matches ) {
|
25 |
+
if ( is_amp_endpoint() ) {
|
26 |
+
return $matches[0];
|
27 |
+
}
|
28 |
+
return parent::inject_video_max_width_style( $matches );
|
29 |
+
}
|
30 |
+
|
31 |
+
}
|
32 |
+
|
33 |
+
}
|
@@ -3,22 +3,28 @@ Contributors: batmoo, joen, automattic, potatomaster, albertomedina, google, xwp
|
|
3 |
Tags: amp, mobile
|
4 |
Requires at least: 4.7
|
5 |
Tested up to: 4.9
|
6 |
-
Stable tag: 0.
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
-
Requires PHP: 5.
|
10 |
|
11 |
Enable Accelerated Mobile Pages (AMP) on your WordPress site.
|
12 |
|
13 |
== Description ==
|
14 |
|
15 |
-
|
16 |
|
17 |
-
With the plugin active, all posts on your site will have
|
18 |
|
19 |
-
|
20 |
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
Follow along with or [contribute](https://github.com/Automattic/amp-wp/blob/develop/contributing.md) to the development of this plugin [on GitHub](https://github.com/Automattic/amp-wp). For more information on the plugin, how the plugin works and how to configure and extend it, please see the [project wiki](https://github.com/Automattic/amp-wp/wiki).
|
24 |
|
@@ -38,14 +44,51 @@ Follow along with or [contribute](https://github.com/Automattic/amp-wp/blob/deve
|
|
38 |
|
39 |
== Changelog ==
|
40 |
|
41 |
-
= 0.
|
42 |
-
|
43 |
-
-
|
44 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
= 0.6.1 (2018-02-09) =
|
47 |
|
48 |
-
|
49 |
|
50 |
= 0.6.0 (2018-01-23) =
|
51 |
|
@@ -56,7 +99,7 @@ Bump version to re-release to ensure temporarily-broken 0.6.0 ZIP build is perma
|
|
56 |
- Add generator meta tag for AMP. See [#810](https://github.com/Automattic/amp-wp/pull/810). Props vaporwavre.
|
57 |
- Add code quality checking via phpcs, eslint, jscs, and jshint. See [#795](https://github.com/Automattic/amp-wp/pull/795). Props westonruter.
|
58 |
- Add autoloader to reduce complexity. See [#828](https://github.com/Automattic/amp-wp/pull/828). Props mikeschinkel, westonruter, ThierryA.
|
59 |
-
- Fix Polldaddy amd SoundCloud embeds. Add vanilla WordPress "embed" test page. A new `bin/create-embed-test-post.php`
|
60 |
- Merge AMP Customizer into main Customizer. See [#819](https://github.com/Automattic/amp-wp/pull/819). Props kaitnyl, westonruter.
|
61 |
- Update AMP HTML tags and attributes. A new `bin/amphtml-update.sh` bash script is introduced. Fixes Playbuzz. See [#823](https://github.com/Automattic/amp-wp/pull/823). Props kienstra, ThierryA, westonruter.
|
62 |
- Remove erroneous hash from id on amp-wp-header. See [#853](https://github.com/Automattic/amp-wp/pull/853). Props eshannon3.
|
3 |
Tags: amp, mobile
|
4 |
Requires at least: 4.7
|
5 |
Tested up to: 4.9
|
6 |
+
Stable tag: 0.7.0
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
+
Requires PHP: 5.3
|
10 |
|
11 |
Enable Accelerated Mobile Pages (AMP) on your WordPress site.
|
12 |
|
13 |
== Description ==
|
14 |
|
15 |
+
Bring the speed and features of the open source [AMP project](https://www.ampproject.org/) to your site, the WordPress way.
|
16 |
|
17 |
+
With the plugin active, all posts on your site will have AMP-compatible versions, accessible by appending `/amp/` to the end your post URLs. For example, if your post URL is `http://example.com/2016/01/01/amp-on/`, you can access the AMP version at `http://example.com/2016/01/01/amp-on/amp/`. If you do not have [pretty permalinks](https://codex.wordpress.org/Using_Permalinks#mod_rewrite:_.22Pretty_Permalinks.22) enabled, you can do the same thing by appending `?amp=1`, i.e. `http://example.com/?p=123&=1`
|
18 |
|
19 |
+
Also, your pages and custom post types can have AMP versions. Simply check their boxes on the 'AMP Settings' page in `/wp-admin`.
|
20 |
|
21 |
+
Your entire site can render as "Native AMP" if your theme calls `add_theme_support( 'amp' )`. There will only be one version of each URL: the AMP version. There won't be separate URLs with `/amp` or `?amp` appended. See this [wiki page](https://github.com/Automattic/amp-wp/wiki/Adding-Theme-Support#native-amp) for details and restrictions.
|
22 |
+
|
23 |
+
Your theme can also use [Paired Mode](https://github.com/Automattic/amp-wp/wiki/Adding-Theme-Support#paired-mode), with your own custom templates for the AMP URLs.
|
24 |
+
|
25 |
+
"Native AMP" and "Paired Mode" add full support for commenting and widgets.
|
26 |
+
|
27 |
+
If your theme doesn't support `'amp'`, this will use basic legacy post templates for AMP consumers like Google Search and Twitter. And when visiting the site, the AMP content won't normally appear without appending strings to the URL like `/amp` or `?amp`.
|
28 |
|
29 |
Follow along with or [contribute](https://github.com/Automattic/amp-wp/blob/develop/contributing.md) to the development of this plugin [on GitHub](https://github.com/Automattic/amp-wp). For more information on the plugin, how the plugin works and how to configure and extend it, please see the [project wiki](https://github.com/Automattic/amp-wp/wiki).
|
30 |
|
44 |
|
45 |
== Changelog ==
|
46 |
|
47 |
+
= 0.7.0 (2018-05-03) =
|
48 |
+
|
49 |
+
- Render an entire site as "Native AMP" if the theme calls `add_theme_support( 'amp' )`. See [#857](https://github.com/Automattic/amp-wp/pull/857), [#852](https://github.com/Automattic/amp-wp/pull/852), [#865](https://github.com/Automattic/amp-wp/pull/865), [#888](https://github.com/Automattic/amp-wp/pull/888). Props westonruter, kaitnyl, ThierryA.
|
50 |
+
- Use the AMP spec to automatically discover the required AMP component scripts to include on the page while post-processing. See [#882](https://github.com/Automattic/amp-wp/pull/882), [#885](https://github.com/Automattic/amp-wp/pull/885). Props westonruter.
|
51 |
+
- Automatically concatenate stylesheets from `style` tags with loaded stylesheets from `link` tags combined in one `style[amp-custom]`. See [#887](https://github.com/Automattic/amp-wp/pull/887), [#890](https://github.com/Automattic/amp-wp/pull/890), [#935](https://github.com/Automattic/amp-wp/pull/935). Props westonruter.
|
52 |
+
- Update serialization to use HTML instead of XML; update minimum version of PHP fro, 5.2 to 5.3. See [#891](https://github.com/Automattic/amp-wp/pull/891).
|
53 |
+
- Add support for widgets. See [#870](https://github.com/Automattic/amp-wp/pull/870). Props kienstra.
|
54 |
+
- Add support for forms. See [#907](https://github.com/Automattic/amp-wp/pull/907), [#923](https://github.com/Automattic/amp-wp/pull/923). Props DavidCramer.
|
55 |
+
- Use "Paired Mode" if the theme calls `add_theme_support( 'amp' )` and passes a `'template_dir'` value for the AMP templates. See [#856](https://github.com/Automattic/amp-wp/pull/856), [#877](https://github.com/Automattic/amp-wp/pull/877). Props westonruter, kaitnyl.
|
56 |
+
- Add AMP implementations of audio/video playlists. See [#954](https://github.com/Automattic/amp-wp/pull/954). Props kienstra.
|
57 |
+
- Allow full Customization when the theme supports `'amp'`. See [#952](https://github.com/Automattic/amp-wp/pull/952). Props westonruter.
|
58 |
+
- Add support for all default WordPress widgets. See [#921](https://github.com/Automattic/amp-wp/pull/921), [#917](https://github.com/Automattic/amp-wp/pull/917). Props kienstra, westonruter.
|
59 |
+
- Add support for more default embeds: Issuu, Post, Meetup, Reddit, Screencast, Tumblr, and WordPress Plugin Directory. See [#889](https://github.com/Automattic/amp-wp/pull/889). Props kaitnyl.
|
60 |
+
- Allow native WordPress commenting, in fully valid AMP. See [#1024](https://github.com/Automattic/amp-wp/pull/1024), [#1029](https://github.com/Automattic/amp-wp/pull/1029), [#871](https://github.com/Automattic/amp-wp/pull/871), [#909](https://github.com/Automattic/amp-wp/pull/909). Props DavidCramer, westonruter.
|
61 |
+
- Add a UI for displaying validation errors, including invalid tags and attributes, with tracing for the source for each error according to which theme/plugin's shortcode, widget, or other hook is responsible. Includes debug mode to suspend sanitizer. See [#971](https://github.com/Automattic/amp-wp/pull/971), [#1012](https://github.com/Automattic/amp-wp/pull/1012), [#1016](https://github.com/Automattic/amp-wp/pull/1016). Props westonruter, kienstra.
|
62 |
+
- On activating a plugin, validate a front-end page and display a notice if there were errors. See [#971](https://github.com/Automattic/amp-wp/pull/971). Props westonruter, kienstra.
|
63 |
+
- Creation of AMP-related notifications, on entering invalid content in the 'classic' editor. See [#912](https://github.com/Automattic/amp-wp/pull/912/). Props kienstra, westonruter, ThierryA.
|
64 |
+
- Optionally use `<amp-live-list>` to display comments, avoiding full-page refreshes on adding comments. And enable making requests for an `<amp-live-list>`, like for displaying posts. See [#1029](https://github.com/Automattic/amp-wp/pull/1029), [#915](https://github.com/Automattic/amp-wp/pull/915). Props DavidCramer, westonruter.
|
65 |
+
- Support `<amp-bind>`, enabling more dynamic elements. See [#895](https://github.com/Automattic/amp-wp/pull/895). Props westonruter.
|
66 |
+
- Add output buffering, ensuring the entire page is valid AMP. See [#929](https://github.com/Automattic/amp-wp/pull/929), [#857](https://github.com/Automattic/amp-wp/pull/857), [#931](https://github.com/Automattic/amp-wp/pull/931). Props westonruter, ThierryA.
|
67 |
+
- Add validation of host names in URLs. See [#983](https://github.com/Automattic/amp-wp/pull/983). Props rubengonzalezmrf.
|
68 |
+
- Add WP-CLI scripts to test AMP support of comments and widgets. See [#924](https://github.com/Automattic/amp-wp/pull/924), [#859](https://github.com/Automattic/amp-wp/pull/859). Props DavidCramer, kienstra.
|
69 |
+
- Improve test coverage, including for `AMP_Theme_Support`. See [#1034](https://github.com/Automattic/amp-wp/pull/1034). Props DavidCramer, kienstra.
|
70 |
+
- Update the generated sanitizer file to the AMP spec, and simplify the file that generates it. See [#929](https://github.com/Automattic/amp-wp/pull/929), [#926](https://github.com/Automattic/amp-wp/pull/926). Props westonruter.
|
71 |
+
- Several sanitizer updates, including for styles, and preventing valid tags from being removed. See [#935](https://github.com/Automattic/amp-wp/pull/935), [#944](https://github.com/Automattic/amp-wp/pull/944), [#952](https://github.com/Automattic/amp-wp/pull/952). Props westonruter, davisshaver.
|
72 |
+
- Improve sanitization of `<amp-img>`, `<amp-video>`, and `<amp-iframe>`. See [#937](https://github.com/Automattic/amp-wp/pull/937), [#1054](https://github.com/Automattic/amp-wp/pull/1054). Props kienstra, amedina.
|
73 |
+
- Fix an issue where the JSON inside `<script type="application/json">` was wrapped with CDATA. See [#891](https://github.com/Automattic/amp-wp/pull/891). Props westonruter.
|
74 |
+
- Allow use of AMP components outside of AMP documents, including in [PWA](https://developers.google.com/web/progressive-web-apps/). See [#1013](https://github.com/Automattic/amp-wp/pull/1013). Props westonruter.
|
75 |
+
- Access the AMP query var with `amp_get_slug()`, instead of `AMP_QUERY_VAR`. See [#986](https://github.com/Automattic/amp-wp/pull/986). Props westonruter, mjangda.
|
76 |
+
- Update build scripts, including PHP versions in `.travis.yml`. See [#1058](https://github.com/Automattic/amp-wp/pull/1058/), [#949](https://github.com/Automattic/amp-wp/pull/949). Props westonruter.
|
77 |
+
- Prevent New Relic script from being injected in AMP responses. See [#932](https://github.com/Automattic/amp-wp/pull/932). Props westonruter.
|
78 |
+
- Fix handling of 0 and empty height/width attributes. See [#979](https://github.com/Automattic/amp-wp/pull/979). Props davisshaver.
|
79 |
+
|
80 |
+
For a full list of the closed issues and merged pull requests in this release, see the [0.7 milestone](https://github.com/Automattic/amp-wp/milestone/6?closed=1).
|
81 |
+
|
82 |
+
Contributors in this release, including design, development, testing, and project management: Adam Silverstein (adamsilverstein), Alberto Medina (amedina), Christian Chung (christianc1), Claudio Sossi, David Cramer (DavidCramer), Davis Shaver (davisshaver), Douglas Paul (douglyuckling), Jason Johnston (jhnstn), Joshua Wold (jwold), Kaitlyn (kaitnyl), Leo Postovoit (postphotos), Mackenzie Hartung (MackenzieHartung), Maxim Siebert (MaximSiebert), Mike Crantea (mehigh), Mohammad Jangda (mjangda), Oscar Sanchez (oscarssanchez), Philip John (philipjohn), Piotr Delawski (delawski), Renato Alves (renatonascalves), Rubén (rubengonzalezmrf), Ryan Kienstra (kienstra), Thierry Muller (ThierryA), vortfu, Weston Ruter (westonruter), Ziga Sancin (zigasancin).
|
83 |
+
|
84 |
+
= 0.6.2 (2018-02-28) =
|
85 |
+
|
86 |
+
* Improve logic and use of escaping; limit flushing rewrite rules to only when supported_post_types change. See [#953](https://github.com/Automattic/amp-wp/pull/953). Props philipjohn, westonruter.
|
87 |
+
* Fix AMP preview icon in Firefox. See [#920](https://github.com/Automattic/amp-wp/pull/920). Props zigasancin.
|
88 |
|
89 |
= 0.6.1 (2018-02-09) =
|
90 |
|
91 |
+
Version bump to re-release plugin in order to deal with missing file in 0.6.0 release package that caused fatal error.
|
92 |
|
93 |
= 0.6.0 (2018-01-23) =
|
94 |
|
99 |
- Add generator meta tag for AMP. See [#810](https://github.com/Automattic/amp-wp/pull/810). Props vaporwavre.
|
100 |
- Add code quality checking via phpcs, eslint, jscs, and jshint. See [#795](https://github.com/Automattic/amp-wp/pull/795). Props westonruter.
|
101 |
- Add autoloader to reduce complexity. See [#828](https://github.com/Automattic/amp-wp/pull/828). Props mikeschinkel, westonruter, ThierryA.
|
102 |
+
- Fix Polldaddy amd SoundCloud embeds. Add vanilla WordPress "embed" test page. A new `bin/create-embed-test-post.php` WP-CLI script is introduced. See [#829](https://github.com/Automattic/amp-wp/pull/829). Props kienstra, westonruter, ThierryA.
|
103 |
- Merge AMP Customizer into main Customizer. See [#819](https://github.com/Automattic/amp-wp/pull/819). Props kaitnyl, westonruter.
|
104 |
- Update AMP HTML tags and attributes. A new `bin/amphtml-update.sh` bash script is introduced. Fixes Playbuzz. See [#823](https://github.com/Automattic/amp-wp/pull/823). Props kienstra, ThierryA, westonruter.
|
105 |
- Remove erroneous hash from id on amp-wp-header. See [#853](https://github.com/Automattic/amp-wp/pull/853). Props eshannon3.
|
@@ -1,17 +1,22 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
-
// WPCOM-specific things
|
4 |
add_action( 'pre_amp_render_post', 'jetpack_amp_disable_the_content_filters' );
|
5 |
|
6 |
-
// Disable admin menu
|
7 |
add_filter( 'amp_options_menu_is_enabled', '__return_false', 9999 );
|
8 |
|
9 |
/**
|
10 |
* Disable the_content filters for Jetpack.
|
11 |
*
|
12 |
-
* @
|
13 |
*/
|
14 |
-
function jetpack_amp_disable_the_content_filters(
|
15 |
add_filter( 'post_flair_disable', '__return_true', 99 );
|
16 |
add_filter( 'videopress_show_2015_player', '__return_true' );
|
17 |
add_filter( 'protected_embeds_use_form_post', '__return_false' );
|
@@ -24,7 +29,12 @@ function jetpack_amp_disable_the_content_filters( $post_id ) {
|
|
24 |
|
25 |
add_action( 'amp_post_template_head', 'jetpack_amp_add_og_tags' );
|
26 |
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
28 |
if ( function_exists( 'jetpack_og_tags' ) ) {
|
29 |
jetpack_og_tags();
|
30 |
}
|
@@ -32,9 +42,18 @@ function jetpack_amp_add_og_tags( $amp_template ) {
|
|
32 |
|
33 |
add_filter( 'amp_post_template_metadata', 'jetpack_amp_post_template_metadata', 10, 2 );
|
34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
function jetpack_amp_post_template_metadata( $metadata, $post ) {
|
36 |
if ( isset( $metadata['publisher'] ) && ! isset( $metadata['publisher']['logo'] ) ) {
|
37 |
-
$metadata = wpcom_amp_add_blavatar_to_metadata( $metadata
|
38 |
}
|
39 |
|
40 |
if ( ! isset( $metadata['image'] ) ) {
|
@@ -44,22 +63,40 @@ function jetpack_amp_post_template_metadata( $metadata, $post ) {
|
|
44 |
return $metadata;
|
45 |
}
|
46 |
|
47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
if ( ! function_exists( 'blavatar_domain' ) ) {
|
49 |
return $metadata;
|
50 |
}
|
51 |
|
52 |
$size = 60;
|
|
|
53 |
$metadata['publisher']['logo'] = array(
|
54 |
-
'@type'
|
55 |
-
'url'
|
56 |
-
'width'
|
57 |
'height' => $size,
|
58 |
);
|
59 |
|
60 |
return $metadata;
|
61 |
}
|
62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
function wpcom_amp_add_image_to_metadata( $metadata, $post ) {
|
64 |
if ( ! class_exists( 'Jetpack_PostImages' ) ) {
|
65 |
return wpcom_amp_add_fallback_image_to_metadata( $metadata );
|
@@ -67,10 +104,10 @@ function wpcom_amp_add_image_to_metadata( $metadata, $post ) {
|
|
67 |
|
68 |
$image = Jetpack_PostImages::get_image( $post->ID, array(
|
69 |
'fallback_to_avatars' => true,
|
70 |
-
'avatar_size'
|
71 |
-
// AMP already attempts these
|
72 |
-
'from_thumbnail'
|
73 |
-
'from_attachment'
|
74 |
) );
|
75 |
|
76 |
if ( empty( $image ) ) {
|
@@ -80,30 +117,37 @@ function wpcom_amp_add_image_to_metadata( $metadata, $post ) {
|
|
80 |
if ( ! isset( $image['src_width'] ) ) {
|
81 |
$dimensions = wpcom_amp_extract_image_dimensions_from_getimagesize( array(
|
82 |
$image['src'] => false,
|
83 |
-
|
84 |
-
);
|
85 |
|
86 |
if ( false !== $dimensions[ $image['src'] ] ) {
|
87 |
-
$image['src_width']
|
88 |
$image['src_height'] = $dimensions['height'];
|
89 |
}
|
90 |
}
|
91 |
|
92 |
$metadata['image'] = array(
|
93 |
-
'@type'
|
94 |
-
'url'
|
95 |
-
'width'
|
96 |
'height' => $image['src_height'],
|
97 |
);
|
98 |
|
99 |
return $metadata;
|
100 |
}
|
101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
function wpcom_amp_add_fallback_image_to_metadata( $metadata ) {
|
103 |
$metadata['image'] = array(
|
104 |
-
'@type'
|
105 |
-
'url'
|
106 |
-
'width'
|
107 |
'height' => 200,
|
108 |
);
|
109 |
|
@@ -111,36 +155,50 @@ function wpcom_amp_add_fallback_image_to_metadata( $metadata ) {
|
|
111 |
}
|
112 |
|
113 |
add_action( 'amp_extract_image_dimensions_batch_callbacks_registered', 'wpcom_amp_extract_image_dimensions_batch_add_custom_callbacks' );
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
function wpcom_amp_extract_image_dimensions_batch_add_custom_callbacks() {
|
115 |
// If images are being served from Photon or WP.com files, try extracting the size using querystring.
|
116 |
add_action( 'amp_extract_image_dimensions_batch', 'wpcom_amp_extract_image_dimensions_from_querystring', 9, 1 ); // Hook in before the default extractors.
|
117 |
|
118 |
// Uses a special wpcom lib (wpcom_getimagesize) to extract dimensions as a last resort if we weren't able to figure them out.
|
119 |
-
add_action( 'amp_extract_image_dimensions_batch', 'wpcom_amp_extract_image_dimensions_from_getimagesize', 99, 1 ); // Our last resort, so run late
|
120 |
|
121 |
// The wpcom override obviates this one, so take it out.
|
122 |
-
remove_filter( 'amp_extract_image_dimensions_batch', array( 'AMP_Image_Dimension_Extractor', 'extract_by_downloading_images' ), 999
|
123 |
}
|
124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
function wpcom_amp_extract_image_dimensions_from_querystring( $dimensions ) {
|
126 |
-
foreach ( $dimensions as $url => $value
|
127 |
|
128 |
if ( is_array( $value ) ) {
|
129 |
continue;
|
130 |
}
|
131 |
|
132 |
-
$host =
|
133 |
-
if ( ! wp_endswith( $host, '.wp.com' )
|
134 |
continue;
|
135 |
}
|
136 |
|
137 |
-
parse_str(
|
138 |
$w = isset( $query['w'] ) ? absint( $query['w'] ) : false;
|
139 |
$h = isset( $query['h'] ) ? absint( $query['h'] ) : false;
|
140 |
|
141 |
if ( false !== $w && false !== $h ) {
|
142 |
$dimensions[ $url ] = array(
|
143 |
-
'width'
|
144 |
'height' => $h,
|
145 |
);
|
146 |
}
|
@@ -148,6 +206,14 @@ function wpcom_amp_extract_image_dimensions_from_querystring( $dimensions ) {
|
|
148 |
return $dimensions;
|
149 |
}
|
150 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
function wpcom_amp_extract_image_dimensions_from_getimagesize( $dimensions ) {
|
152 |
if ( ! function_exists( 'require_lib' ) ) {
|
153 |
return $dimensions;
|
@@ -161,7 +227,7 @@ function wpcom_amp_extract_image_dimensions_from_getimagesize( $dimensions ) {
|
|
161 |
$result = wpcom_getimagesize( $url );
|
162 |
if ( is_array( $result ) ) {
|
163 |
$dimensions[ $url ] = array(
|
164 |
-
'width'
|
165 |
'height' => $result[1],
|
166 |
);
|
167 |
}
|
1 |
<?php
|
2 |
+
/**
|
3 |
+
* WPCOM-specific things.
|
4 |
+
*
|
5 |
+
* @todo Move this into Jetpack. See https://github.com/Automattic/amp-wp/issues/1021
|
6 |
+
* @package AMP
|
7 |
+
*/
|
8 |
|
|
|
9 |
add_action( 'pre_amp_render_post', 'jetpack_amp_disable_the_content_filters' );
|
10 |
|
11 |
+
// Disable admin menu.
|
12 |
add_filter( 'amp_options_menu_is_enabled', '__return_false', 9999 );
|
13 |
|
14 |
/**
|
15 |
* Disable the_content filters for Jetpack.
|
16 |
*
|
17 |
+
* @since 0.3
|
18 |
*/
|
19 |
+
function jetpack_amp_disable_the_content_filters() {
|
20 |
add_filter( 'post_flair_disable', '__return_true', 99 );
|
21 |
add_filter( 'videopress_show_2015_player', '__return_true' );
|
22 |
add_filter( 'protected_embeds_use_form_post', '__return_false' );
|
29 |
|
30 |
add_action( 'amp_post_template_head', 'jetpack_amp_add_og_tags' );
|
31 |
|
32 |
+
/**
|
33 |
+
* Add Open Graph tags.
|
34 |
+
*
|
35 |
+
* @since 0.3
|
36 |
+
*/
|
37 |
+
function jetpack_amp_add_og_tags() {
|
38 |
if ( function_exists( 'jetpack_og_tags' ) ) {
|
39 |
jetpack_og_tags();
|
40 |
}
|
42 |
|
43 |
add_filter( 'amp_post_template_metadata', 'jetpack_amp_post_template_metadata', 10, 2 );
|
44 |
|
45 |
+
/**
|
46 |
+
* Add publisher and image metadata.
|
47 |
+
*
|
48 |
+
* @since 0.3
|
49 |
+
*
|
50 |
+
* @param array $metadata Metadata array.
|
51 |
+
* @param WP_Post $post Post.
|
52 |
+
* @return array Modified metadata array.
|
53 |
+
*/
|
54 |
function jetpack_amp_post_template_metadata( $metadata, $post ) {
|
55 |
if ( isset( $metadata['publisher'] ) && ! isset( $metadata['publisher']['logo'] ) ) {
|
56 |
+
$metadata = wpcom_amp_add_blavatar_to_metadata( $metadata );
|
57 |
}
|
58 |
|
59 |
if ( ! isset( $metadata['image'] ) ) {
|
63 |
return $metadata;
|
64 |
}
|
65 |
|
66 |
+
/**
|
67 |
+
* Add blavatar to metadata.
|
68 |
+
*
|
69 |
+
* @since 0.3
|
70 |
+
*
|
71 |
+
* @param array $metadata Metadata.
|
72 |
+
* @return array Metadata.
|
73 |
+
*/
|
74 |
+
function wpcom_amp_add_blavatar_to_metadata( $metadata ) {
|
75 |
if ( ! function_exists( 'blavatar_domain' ) ) {
|
76 |
return $metadata;
|
77 |
}
|
78 |
|
79 |
$size = 60;
|
80 |
+
|
81 |
$metadata['publisher']['logo'] = array(
|
82 |
+
'@type' => 'ImageObject',
|
83 |
+
'url' => blavatar_url( blavatar_domain( site_url() ), 'img', $size, staticize_subdomain( 'https://wordpress.com/i/favicons/apple-touch-icon-60x60.png' ) ),
|
84 |
+
'width' => $size,
|
85 |
'height' => $size,
|
86 |
);
|
87 |
|
88 |
return $metadata;
|
89 |
}
|
90 |
|
91 |
+
/**
|
92 |
+
* Add image to metadata.
|
93 |
+
*
|
94 |
+
* @since 0.3.2
|
95 |
+
*
|
96 |
+
* @param array $metadata Metadata.
|
97 |
+
* @param WP_Post $post Post.
|
98 |
+
* @return array Metadata.
|
99 |
+
*/
|
100 |
function wpcom_amp_add_image_to_metadata( $metadata, $post ) {
|
101 |
if ( ! class_exists( 'Jetpack_PostImages' ) ) {
|
102 |
return wpcom_amp_add_fallback_image_to_metadata( $metadata );
|
104 |
|
105 |
$image = Jetpack_PostImages::get_image( $post->ID, array(
|
106 |
'fallback_to_avatars' => true,
|
107 |
+
'avatar_size' => 200,
|
108 |
+
// AMP already attempts these.
|
109 |
+
'from_thumbnail' => false,
|
110 |
+
'from_attachment' => false,
|
111 |
) );
|
112 |
|
113 |
if ( empty( $image ) ) {
|
117 |
if ( ! isset( $image['src_width'] ) ) {
|
118 |
$dimensions = wpcom_amp_extract_image_dimensions_from_getimagesize( array(
|
119 |
$image['src'] => false,
|
120 |
+
) );
|
|
|
121 |
|
122 |
if ( false !== $dimensions[ $image['src'] ] ) {
|
123 |
+
$image['src_width'] = $dimensions['width'];
|
124 |
$image['src_height'] = $dimensions['height'];
|
125 |
}
|
126 |
}
|
127 |
|
128 |
$metadata['image'] = array(
|
129 |
+
'@type' => 'ImageObject',
|
130 |
+
'url' => $image['src'],
|
131 |
+
'width' => $image['src_width'],
|
132 |
'height' => $image['src_height'],
|
133 |
);
|
134 |
|
135 |
return $metadata;
|
136 |
}
|
137 |
|
138 |
+
/**
|
139 |
+
* Add fallback image to metadata.
|
140 |
+
*
|
141 |
+
* @since 0.3.2
|
142 |
+
*
|
143 |
+
* @param array $metadata Metadata.
|
144 |
+
* @return array Metadata.
|
145 |
+
*/
|
146 |
function wpcom_amp_add_fallback_image_to_metadata( $metadata ) {
|
147 |
$metadata['image'] = array(
|
148 |
+
'@type' => 'ImageObject',
|
149 |
+
'url' => staticize_subdomain( 'https://wordpress.com/i/blank.jpg' ),
|
150 |
+
'width' => 200,
|
151 |
'height' => 200,
|
152 |
);
|
153 |
|
155 |
}
|
156 |
|
157 |
add_action( 'amp_extract_image_dimensions_batch_callbacks_registered', 'wpcom_amp_extract_image_dimensions_batch_add_custom_callbacks' );
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Add hooks to extract image dimensions.
|
161 |
+
*
|
162 |
+
* @since 0.5
|
163 |
+
*/
|
164 |
function wpcom_amp_extract_image_dimensions_batch_add_custom_callbacks() {
|
165 |
// If images are being served from Photon or WP.com files, try extracting the size using querystring.
|
166 |
add_action( 'amp_extract_image_dimensions_batch', 'wpcom_amp_extract_image_dimensions_from_querystring', 9, 1 ); // Hook in before the default extractors.
|
167 |
|
168 |
// Uses a special wpcom lib (wpcom_getimagesize) to extract dimensions as a last resort if we weren't able to figure them out.
|
169 |
+
add_action( 'amp_extract_image_dimensions_batch', 'wpcom_amp_extract_image_dimensions_from_getimagesize', 99, 1 ); // Our last resort, so run late.
|
170 |
|
171 |
// The wpcom override obviates this one, so take it out.
|
172 |
+
remove_filter( 'amp_extract_image_dimensions_batch', array( 'AMP_Image_Dimension_Extractor', 'extract_by_downloading_images' ), 999 );
|
173 |
}
|
174 |
|
175 |
+
/**
|
176 |
+
* Extract image dimensions from query string.
|
177 |
+
*
|
178 |
+
* @since 0.5
|
179 |
+
*
|
180 |
+
* @param array $dimensions Dimensions.
|
181 |
+
* @return array Dimensions.
|
182 |
+
*/
|
183 |
function wpcom_amp_extract_image_dimensions_from_querystring( $dimensions ) {
|
184 |
+
foreach ( $dimensions as $url => $value ) {
|
185 |
|
186 |
if ( is_array( $value ) ) {
|
187 |
continue;
|
188 |
}
|
189 |
|
190 |
+
$host = wp_parse_url( $url, PHP_URL_HOST );
|
191 |
+
if ( ! wp_endswith( $host, '.wp.com' ) && ! wp_endswith( $host, '.files.wordpress.com' ) ) {
|
192 |
continue;
|
193 |
}
|
194 |
|
195 |
+
parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query );
|
196 |
$w = isset( $query['w'] ) ? absint( $query['w'] ) : false;
|
197 |
$h = isset( $query['h'] ) ? absint( $query['h'] ) : false;
|
198 |
|
199 |
if ( false !== $w && false !== $h ) {
|
200 |
$dimensions[ $url ] = array(
|
201 |
+
'width' => $w,
|
202 |
'height' => $h,
|
203 |
);
|
204 |
}
|
206 |
return $dimensions;
|
207 |
}
|
208 |
|
209 |
+
/**
|
210 |
+
* Extract image dimensions via wpcom/imagesize.
|
211 |
+
*
|
212 |
+
* @since 0.5
|
213 |
+
*
|
214 |
+
* @param array $dimensions Dimensions.
|
215 |
+
* @return array Dimensions.
|
216 |
+
*/
|
217 |
function wpcom_amp_extract_image_dimensions_from_getimagesize( $dimensions ) {
|
218 |
if ( ! function_exists( 'require_lib' ) ) {
|
219 |
return $dimensions;
|
227 |
$result = wpcom_getimagesize( $url );
|
228 |
if ( is_array( $result ) ) {
|
229 |
$dimensions[ $url ] = array(
|
230 |
+
'width' => $result[0],
|
231 |
'height' => $result[1],
|
232 |
);
|
233 |
}
|