AMP for WP – Accelerated Mobile Pages - Version 0.9.90

Version Description

Download this release

Release Info

Developer mohammed_kaludi
Plugin Icon 128x128 AMP for WP – Accelerated Mobile Pages
Version 0.9.90
Comparing to
See all releases

Code changes from version 0.9.86.2 to 0.9.90

Files changed (74) hide show
  1. README.md +1 -1
  2. accelerated-moblie-pages.php +1 -1
  3. classes/class-ampforwp-gallery-embed.php +139 -0
  4. classes/class-ampforwp-iframe-sanitizer.php +198 -0
  5. classes/class-ampforwp-img-sanitizer.php +312 -0
  6. classes/class-ampforwp-vimeo-embed.php +135 -0
  7. images/Bitmap.png +0 -0
  8. images/amplogo.png +0 -0
  9. images/converttoamp.png +0 -0
  10. images/ex-1.png +0 -0
  11. images/ex-2.png +0 -0
  12. images/ex-3.png +0 -0
  13. images/layouts-3.png +0 -0
  14. images/layouts-4.png +0 -0
  15. images/layouts-5.png +0 -0
  16. images/layouts-6.png +0 -0
  17. images/nf.png +0 -0
  18. images/rating.png +0 -0
  19. images/tick.png +0 -0
  20. includes/options/extensions/theme_design_selector/extension_theme_design_selector.php +110 -0
  21. includes/options/extensions/theme_design_selector/theme_design_selector/field_theme_design_selector.css +78 -0
  22. includes/options/extensions/theme_design_selector/theme_design_selector/field_theme_design_selector.js +7 -0
  23. includes/options/extensions/theme_design_selector/theme_design_selector/field_theme_design_selector.php +102 -0
  24. includes/vendor/amp/assets/css/amp-customizer.css +123 -0
  25. includes/vendor/amp/assets/css/amp-default.css +11 -0
  26. includes/vendor/amp/assets/css/amp-playlist-shortcode.css +19 -0
  27. includes/vendor/amp/assets/css/amp-post-meta-box.css +66 -0
  28. includes/vendor/amp/assets/images/amp-icon.svg +7 -0
  29. includes/vendor/amp/assets/images/amp-white-icon.svg +9 -0
  30. includes/vendor/amp/assets/js/amp-customize-controls.js +351 -0
  31. includes/vendor/amp/assets/js/amp-customize-preview.js +26 -0
  32. includes/vendor/amp/assets/js/amp-post-meta-box.js +179 -0
  33. includes/vendor/amp/includes/admin/class-amp-post-meta-box.php +246 -0
  34. includes/vendor/amp/includes/class-amp-autoloader.php +152 -0
  35. includes/vendor/amp/includes/class-amp-comment-walker.php +121 -0
  36. includes/vendor/amp/includes/class-amp-post-type-support.php +99 -0
  37. includes/vendor/amp/includes/class-amp-theme-support.php +1201 -0
  38. includes/vendor/amp/includes/embeds/class-amp-issuu-embed-handler.php +65 -0
  39. includes/vendor/amp/includes/embeds/class-amp-meetup-embed-handler.php +45 -0
  40. includes/vendor/amp/includes/embeds/class-amp-playlist-embed-handler.php +324 -0
  41. includes/vendor/amp/includes/embeds/class-amp-reddit-embed-handler.php +74 -0
  42. includes/vendor/amp/includes/embeds/class-amp-tumblr-embed-handler.php +60 -0
  43. includes/vendor/amp/includes/options/class-amp-analytics-options-submenu.php +61 -0
  44. includes/vendor/amp/includes/options/class-amp-options-manager.php +262 -0
  45. includes/vendor/amp/includes/options/class-amp-options-menu.php +143 -0
  46. includes/vendor/amp/includes/options/views/class-amp-analytics-options-submenu-page.php +102 -0
  47. includes/vendor/amp/includes/sanitizers/class-amp-allowed-tags-generated.php +10776 -0
  48. includes/vendor/amp/includes/sanitizers/class-amp-comments-sanitizer.php +156 -0
  49. includes/vendor/amp/includes/sanitizers/class-amp-form-sanitizer.php +141 -0
  50. includes/vendor/amp/includes/sanitizers/class-amp-rule-spec.php +154 -0
  51. includes/vendor/amp/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php +1630 -0
  52. includes/vendor/amp/includes/templates/class-amp-content-sanitizer.php +98 -0
  53. includes/vendor/amp/includes/templates/class-amp-content.php +192 -0
  54. includes/vendor/amp/includes/templates/class-amp-post-template.php +481 -0
  55. includes/vendor/amp/includes/utils/class-amp-validation-utils.php +1848 -0
  56. includes/vendor/amp/includes/widgets/class-amp-widget-archives.php +103 -0
  57. includes/vendor/amp/includes/widgets/class-amp-widget-categories.php +91 -0
  58. includes/vendor/amp/includes/widgets/class-amp-widget-media-video.php +33 -0
  59. includes/vendor/amp/includes/widgets/class-amp-widget-recent-comments.php +38 -0
  60. includes/vendor/amp/includes/widgets/class-amp-widget-text.php +33 -0
  61. includes/vendor/amp/templates/admin/amp-status.php +71 -0
  62. includes/vendor/amp/templates/header.php +14 -0
  63. includes/vendor/amp/templates/html-end.php +18 -0
  64. includes/vendor/amp/templates/html-start.php +26 -0
  65. includes/vendor/amp/templates/page.php +34 -0
  66. includes/vendor/amp/wpcom-helper.php +237 -0
  67. includes/vendor/amp/wpcom/class-amp-polldaddy-embed.php +101 -0
  68. includes/vendor/amp/wpcom/shortcodes.php +20 -0
  69. includes/vendor/vendor-compatibility.php +342 -0
  70. includes/vendor/vendor-files/class-amp-allowed-tags-generated.php +10822 -0
  71. includes/vendor/vendor-files/class-amp-response-headers.php +88 -0
  72. includes/vendor/vendor-files/class-amp-theme-support.php +1148 -0
  73. includes/vendor/vendor-files/sanitizer/class-amp-core-theme-sanitizer.php +91 -0
  74. includes/vendor/vendor-files/sanitizer/class-amp-style-sanitizer.php +854 -0
README.md CHANGED
@@ -4,7 +4,7 @@ Tags: AMP, accelerated mobile pages, mobile, amp project, google amp, amp wp, go
4
  Donate link: https://www.paypal.me/Kaludi/25
5
  Requires at least: 3.0
6
  Tested up to: 4.9.5
7
- Stable tag: 0.9.86.2
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
4
  Donate link: https://www.paypal.me/Kaludi/25
5
  Requires at least: 3.0
6
  Tested up to: 4.9.5
7
+ Stable tag: 0.9.90
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
accelerated-moblie-pages.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Accelerated Mobile Pages
4
  Plugin URI: https://wordpress.org/plugins/accelerated-mobile-pages/
5
  Description: AMP for WP - Accelerated Mobile Pages for WordPress
6
- Version: 0.9.86.2
7
  Author: Ahmed Kaludi, Mohammed Kaludi
8
  Author URI: https://ampforwp.com/
9
  Donate link: https://www.paypal.me/Kaludi/25
3
  Plugin Name: Accelerated Mobile Pages
4
  Plugin URI: https://wordpress.org/plugins/accelerated-mobile-pages/
5
  Description: AMP for WP - Accelerated Mobile Pages for WordPress
6
+ Version: 0.9.90
7
  Author: Ahmed Kaludi, Mohammed Kaludi
8
  Author URI: https://ampforwp.com/
9
  Donate link: https://www.paypal.me/Kaludi/25
classes/class-ampforwp-gallery-embed.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once( AMP__DIR__ . '/includes/embeds/class-amp-base-embed-handler.php' );
4
+
5
+ class AMPforWP_Gallery_Embed_Handler extends AMP_Base_Embed_Handler {
6
+ private static $script_slug = 'amp-carousel';
7
+ private static $script_src = 'https://cdn.ampproject.org/v0/amp-carousel-0.1.js';
8
+
9
+ public function register_embed() {
10
+ add_shortcode( 'gallery', array( $this, 'shortcode' ) );
11
+ }
12
+
13
+ public function unregister_embed() {
14
+ remove_shortcode( 'gallery' );
15
+ }
16
+
17
+ public function get_scripts() {
18
+ if ( ! $this->did_convert_elements ) {
19
+ return array();
20
+ }
21
+
22
+ return array( self::$script_slug => self::$script_src );
23
+ }
24
+
25
+ public function shortcode( $attr ) {
26
+ $post = get_post();
27
+
28
+ if ( ! empty( $attr['ids'] ) ) {
29
+ // 'ids' is explicitly ordered, unless you specify otherwise.
30
+ if ( empty( $attr['orderby'] ) ) {
31
+ $attr['orderby'] = 'post__in';
32
+ }
33
+ $attr['include'] = $attr['ids'];
34
+ }
35
+
36
+ $atts = shortcode_atts( array(
37
+ 'order' => 'ASC',
38
+ 'orderby' => 'menu_order ID',
39
+ 'id' => $post ? $post->ID : 0,
40
+ 'include' => '',
41
+ 'exclude' => '',
42
+ 'size' => array( $this->args['width'], $this->args['height'] ),
43
+ ), $attr, 'gallery' );
44
+
45
+ $id = intval( $atts['id'] );
46
+
47
+ if ( ! empty( $atts['include'] ) ) {
48
+ $attachments = get_posts( array(
49
+ 'include' => $atts['include'],
50
+ 'post_status' => 'inherit',
51
+ 'post_type' => 'attachment',
52
+ 'post_mime_type' => 'image',
53
+ 'order' => $atts['order'],
54
+ 'orderby' => $atts['orderby'],
55
+ 'fields' => 'ids',
56
+ ) );
57
+ } elseif ( ! empty( $atts['exclude'] ) ) {
58
+ $attachments = get_children( array(
59
+ 'post_parent' => $id,
60
+ 'exclude' => $atts['exclude'],
61
+ 'post_status' => 'inherit',
62
+ 'post_type' => 'attachment',
63
+ 'post_mime_type' => 'image',
64
+ 'order' => $atts['order'],
65
+ 'orderby' => $atts['orderby'],
66
+ 'fields' => 'ids',
67
+ ) );
68
+ } else {
69
+ $attachments = get_children( array(
70
+ 'post_parent' => $id,
71
+ 'post_status' => 'inherit',
72
+ 'post_type' => 'attachment',
73
+ 'post_mime_type' => 'image',
74
+ 'order' => $atts['order'],
75
+ 'orderby' => $atts['orderby'],
76
+ 'fields' => 'ids',
77
+ ) );
78
+ }
79
+
80
+ if ( empty( $attachments ) ) {
81
+ return '';
82
+ }
83
+
84
+ $urls = array();
85
+ foreach ( $attachments as $attachment_id ) {
86
+ list( $url, $width, $height ) = wp_get_attachment_image_src( $attachment_id, $atts['size'], true );
87
+
88
+ if ( ! $url ) {
89
+ continue;
90
+ }
91
+
92
+ $urls[] = apply_filters('amp_gallery_image_params', array(
93
+ 'url' => $url,
94
+ 'width' => $width,
95
+ 'height' => $height,
96
+ ),$attachment_id);
97
+ }
98
+ return $this->render( array(
99
+ 'images' => $urls,
100
+ ) );
101
+ }
102
+
103
+ public function render( $args ) {
104
+ $this->did_convert_elements = true;
105
+
106
+ $args = wp_parse_args( $args, array(
107
+ 'images' => false,
108
+ ) );
109
+
110
+ if ( empty( $args['images'] ) ) {
111
+ return '';
112
+ }
113
+ $images = array();
114
+ foreach ( $args['images'] as $key => $image ) {
115
+ $images[$key] = AMP_HTML_Utils::build_tag(
116
+ 'amp-img',
117
+ array(
118
+ 'src' => $image['url'],
119
+ 'width' => $image['width'],
120
+ 'height' => $image['height'],
121
+ 'layout' => 'fill',
122
+ 'class' => 'amp-carousel-img',
123
+ )
124
+ );
125
+ $images[$key] = apply_filters('amp_gallery_images', $images[$key], $image);
126
+ }
127
+ return AMP_HTML_Utils::build_tag(
128
+ 'amp-carousel',
129
+ array(
130
+ 'width' => $this->args['width'],
131
+ 'height' => $this->args['height'],
132
+ 'type' => 'slides',
133
+ 'layout' => 'responsive',
134
+ 'class' => 'collapsible-captions',
135
+ ),
136
+ implode( PHP_EOL, $images )
137
+ );
138
+ }
139
+ }
classes/class-ampforwp-iframe-sanitizer.php ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMPforWP_Iframe_Sanitizer
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Class AMPforWP_Iframe_Sanitizer
10
+ *
11
+ * Converts <iframe> tags to <amp-iframe>
12
+ */
13
+ class AMPforWP_Iframe_Sanitizer extends AMP_Base_Sanitizer {
14
+
15
+ /**
16
+ * Value used for height attribute when $attributes['height'] is empty.
17
+ *
18
+ * @since 0.2
19
+ *
20
+ * @const int
21
+ */
22
+ const FALLBACK_HEIGHT = 400;
23
+
24
+ /**
25
+ * Default values for sandboxing IFrame.
26
+ *
27
+ * @since 0.2
28
+ *
29
+ * @const int
30
+ */
31
+ const SANDBOX_DEFAULTS = 'allow-scripts allow-same-origin allow-popups';
32
+
33
+ /**
34
+ * Tag.
35
+ *
36
+ * @var string HTML <iframe> tag to identify and replace with AMP version.
37
+ *
38
+ * @since 0.2
39
+ */
40
+ public static $tag = 'iframe';
41
+
42
+ /**
43
+ * Default args.
44
+ *
45
+ * @var array
46
+ */
47
+ protected $DEFAULT_ARGS = array(
48
+ 'add_placeholder' => false,
49
+ );
50
+
51
+ /**
52
+ * Sanitize the <iframe> elements from the HTML contained in this instance's DOMDocument.
53
+ *
54
+ * @since 0.2
55
+ */
56
+ public function sanitize() {
57
+ $nodes = $this->dom->getElementsByTagName( self::$tag );
58
+ $num_nodes = $nodes->length;
59
+ if ( 0 === $num_nodes ) {
60
+ return;
61
+ }
62
+
63
+ for ( $i = $num_nodes - 1; $i >= 0; $i-- ) {
64
+ $node = $nodes->item( $i );
65
+ $old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
66
+
67
+ $new_attributes = $this->filter_attributes( $old_attributes );
68
+
69
+ /**
70
+ * If the src doesn't exist, remove the node. Either it never
71
+ * existed or was invalidated while filtering attributes above.
72
+ *
73
+ * @todo: add a filter to allow for a fallback element in this instance.
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
+
90
+ if ( true === $this->args['add_placeholder'] ) {
91
+ $placeholder_node = $this->build_placeholder( $new_attributes );
92
+ $new_node->appendChild( $placeholder_node );
93
+ }
94
+
95
+ $parent_node = $node->parentNode;
96
+ if ( 'p' !== strtolower( $parent_node->tagName ) ) {
97
+ $parent_node->replaceChild( $new_node, $node );
98
+ } else {
99
+ // AMP does not like iframes in <p> tags.
100
+ $parent_node->removeChild( $node );
101
+ $parent_node->parentNode->insertBefore( $new_node, $parent_node->nextSibling );
102
+
103
+ if ( AMP_DOM_Utils::is_node_empty( $parent_node ) ) {
104
+ $parent_node->parentNode->removeChild( $parent_node );
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * "Filter" HTML attributes for <amp-iframe> elements.
112
+ *
113
+ * @since 0.2
114
+ *
115
+ * @param string[] $attributes {
116
+ * Attributes.
117
+ *
118
+ * @type string $src IFrame URL - Empty if HTTPS required per $this->args['require_https_src']
119
+ * @type int $width <iframe> width attribute - Set to numeric value if px or %
120
+ * @type int $height <iframe> width attribute - Set to numeric value if px or %
121
+ * @type string $sandbox <iframe> `sandbox` attribute - Pass along if found; default to value of self::SANDBOX_DEFAULTS
122
+ * @type string $class <iframe> `class` attribute - Pass along if found
123
+ * @type string $sizes <iframe> `sizes` attribute - Pass along if found
124
+ * @type int $frameborder <iframe> `frameborder` attribute - Filter to '0' or '1'; default to '0'
125
+ * @type bool $allowfullscreen <iframe> `allowfullscreen` attribute - Convert 'false' to empty string ''
126
+ * @type bool $allowtransparency <iframe> `allowtransparency` attribute - Convert 'false' to empty string ''
127
+ * }
128
+ * @return array Returns HTML attributes; removes any not specifically declared above from input.
129
+ */
130
+ private function filter_attributes( $attributes ) {
131
+ $out = array();
132
+
133
+ foreach ( $attributes as $name => $value ) {
134
+ switch ( $name ) {
135
+ case 'sandbox':
136
+ case 'class':
137
+ case 'sizes':
138
+ $out[ $name ] = $value;
139
+ break;
140
+
141
+ case 'src':
142
+ $out[ $name ] = $this->maybe_enforce_https_src( $value, true );
143
+ break;
144
+
145
+ case 'width':
146
+ case 'height':
147
+ $out[ $name ] = $this->sanitize_dimension( $value, $name );
148
+ break;
149
+
150
+ case 'frameborder':
151
+ if ( '0' !== $value && '1' !== $value ) {
152
+ $value = '0';
153
+ }
154
+ $out[ $name ] = $value;
155
+ break;
156
+
157
+ case 'allowfullscreen':
158
+ case 'allowtransparency':
159
+ if ( 'false' !== $value ) {
160
+ $out[ $name ] = '';
161
+ }
162
+ break;
163
+
164
+ default:
165
+ break;
166
+ }
167
+ }
168
+
169
+ if ( ! isset( $out['sandbox'] ) ) {
170
+ $out['sandbox'] = self::SANDBOX_DEFAULTS;
171
+ }
172
+
173
+ return $out;
174
+ }
175
+
176
+ /**
177
+ * Builds a DOMElement to use as a placeholder for an <iframe>.
178
+ *
179
+ * @since 0.2
180
+ *
181
+ * @param string[] $parent_attributes {
182
+ * Attributes.
183
+ *
184
+ * @type string $placeholder AMP HTML <amp-iframe> `placeholder` attribute; default to 'amp-wp-iframe-placeholder'
185
+ * @type string $class AMP HTML <amp-iframe> `class` attribute; default to 'amp-wp-iframe-placeholder'
186
+ * }
187
+ * @return DOMElement|false
188
+ */
189
+ private function build_placeholder( $parent_attributes ) {
190
+ $placeholder_node = AMP_DOM_Utils::create_node( $this->dom, 'div', array(
191
+ 'placeholder' => '',
192
+ 'class' => 'amp-wp-iframe-placeholder',
193
+ ) );
194
+
195
+ return $placeholder_node;
196
+ }
197
+
198
+ }
classes/class-ampforwp-img-sanitizer.php ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMPforWP_Img_Sanitizer.
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Class AMPforWP_Img_Sanitizer
10
+ *
11
+ * Converts <img> tags to <amp-img> or <amp-anim>
12
+ */
13
+ class AMPforWP_Img_Sanitizer extends AMP_Base_Sanitizer {
14
+
15
+ /**
16
+ * Value used for width attribute when $attributes['width'] is empty.
17
+ *
18
+ * @since 0.2
19
+ *
20
+ * @const int
21
+ */
22
+ const FALLBACK_WIDTH = 600;
23
+
24
+ /**
25
+ * Value used for height attribute when $attributes['height'] is empty.
26
+ *
27
+ * @since 0.2
28
+ *
29
+ * @const int
30
+ */
31
+ const FALLBACK_HEIGHT = 400;
32
+ protected $is_lightbox = false;
33
+ protected $scripts = array();
34
+ private static $script_slug = 'amp-anim';
35
+ private static $script_src = 'https://cdn.ampproject.org/v0/amp-anim-0.1.js';
36
+ private static $script_slug_lightbox = 'amp-image-lightbox';
37
+ private static $script_src_lightbox = 'https://cdn.ampproject.org/v0/amp-image-lightbox-0.1.js';
38
+
39
+ /**
40
+ * Tag.
41
+ *
42
+ * @var string HTML <img> tag to identify and replace with AMP version.
43
+ *
44
+ * @since 0.2
45
+ */
46
+ public static $tag = 'img';
47
+
48
+ /**
49
+ * Animation extension.
50
+ *
51
+ * @var string
52
+ */
53
+ private static $anim_extension = '.gif';
54
+
55
+ /**
56
+ * Sanitize the <img> elements from the HTML contained in this instance's DOMDocument.
57
+ *
58
+ * @since 0.2
59
+ */
60
+ public function sanitize() {
61
+
62
+ /**
63
+ * Node list.
64
+ *
65
+ * @var DOMNodeList $node
66
+ */
67
+ $nodes = $this->dom->getElementsByTagName( self::$tag );
68
+ $need_dimensions = array();
69
+
70
+ $num_nodes = $nodes->length;
71
+
72
+ if ( 0 === $num_nodes ) {
73
+ return;
74
+ }
75
+
76
+ for ( $i = $num_nodes - 1; $i >= 0; $i-- ) {
77
+ $node = $nodes->item( $i );
78
+ if ( ! $node instanceof DOMElement ) {
79
+ continue;
80
+ }
81
+
82
+ // Add Foo Gallery Support
83
+ if ( $node->hasAttribute( 'data-src-fg' ) ) {
84
+ $image_scr_from_data_src = $node->getAttribute( 'data-src-fg' ) ;
85
+ if ( ! $node->hasAttribute( 'src' ) || '' === $node->getAttribute( 'src' ) ) {
86
+ $node->setAttribute( 'src', $image_scr_from_data_src );
87
+ }
88
+ }
89
+
90
+ if ( ! $node->hasAttribute( 'src' ) || '' === trim( $node->getAttribute( 'src' ) ) ) {
91
+ $this->remove_invalid_child( $node );
92
+ continue;
93
+ }
94
+
95
+ // Determine which images need their dimensions determined/extracted.
96
+ if ( ! is_numeric( $node->getAttribute( 'width' ) ) || ! is_numeric( $node->getAttribute( 'height' ) ) ) {
97
+ $need_dimensions[ $node->getAttribute( 'src' ) ][] = $node;
98
+ } else {
99
+ $this->adjust_and_replace_node( $node );
100
+ }
101
+ }
102
+
103
+ $this->determine_dimensions( $need_dimensions );
104
+ $this->adjust_and_replace_nodes_in_array_map( $need_dimensions );
105
+ }
106
+
107
+ /**
108
+ * "Filter" HTML attributes for <amp-anim> elements.
109
+ *
110
+ * @since 0.2
111
+ *
112
+ * @param string[] $attributes {
113
+ * Attributes.
114
+ *
115
+ * @type string $src Image URL - Pass along if found
116
+ * @type string $alt <img> `alt` attribute - Pass along if found
117
+ * @type string $class <img> `class` attribute - Pass along if found
118
+ * @type string $srcset <img> `srcset` attribute - Pass along if found
119
+ * @type string $sizes <img> `sizes` attribute - Pass along if found
120
+ * @type string $on <img> `on` attribute - Pass along if found
121
+ * @type string $attribution <img> `attribution` attribute - Pass along if found
122
+ * @type int $width <img> width attribute - Set to numeric value if px or %
123
+ * @type int $height <img> width attribute - Set to numeric value if px or %
124
+ * }
125
+ * @return array Returns HTML attributes; removes any not specifically declared above from input.
126
+ */
127
+ private function filter_attributes( $attributes ) {
128
+ $out = array();
129
+
130
+ foreach ( $attributes as $name => $value ) {
131
+ switch ( $name ) {
132
+ case 'src':
133
+ case 'alt':
134
+ case 'class':
135
+ case 'srcset':
136
+ case 'on':
137
+ case 'attribution':
138
+ case 'role':
139
+ case 'tabindex':
140
+ $out[ $name ] = $value;
141
+ break;
142
+
143
+ case 'width':
144
+ case 'height':
145
+ $out[ $name ] = $this->sanitize_dimension( $value, $name );
146
+ break;
147
+
148
+ case 'data-amp-layout':
149
+ $out['layout'] = $value;
150
+ break;
151
+
152
+ default:
153
+ break;
154
+ }
155
+ }
156
+
157
+ return $out;
158
+ }
159
+
160
+ /**
161
+ * Determine width and height attribute values for images without them.
162
+ *
163
+ * Attempt to determine actual dimensions, otherwise set reasonable defaults.
164
+ *
165
+ * @param DOMElement[][] $need_dimensions Map <img> @src URLs to node for images with missing dimensions.
166
+ */
167
+ private function determine_dimensions( $need_dimensions ) {
168
+
169
+ $dimensions_by_url = AMP_Image_Dimension_Extractor::extract( array_keys( $need_dimensions ) );
170
+
171
+ foreach ( $dimensions_by_url as $url => $dimensions ) {
172
+ foreach ( $need_dimensions[ $url ] as $node ) {
173
+ if ( ! $node instanceof DOMElement ) {
174
+ continue;
175
+ }
176
+ if (
177
+ ! is_numeric( $node->getAttribute( 'width' ) ) &&
178
+ ! is_numeric( $node->getAttribute( 'height' ) )
179
+ ) {
180
+ $height = self::FALLBACK_HEIGHT;
181
+ $width = self::FALLBACK_WIDTH;
182
+ $node->setAttribute( 'width', $width );
183
+ $node->setAttribute( 'height', $height );
184
+ $class = $node->hasAttribute( 'class' ) ? $node->getAttribute( 'class' ) . ' amp-wp-unknown-size' : 'amp-wp-unknown-size';
185
+ $node->setAttribute( 'class', $class );
186
+ } elseif (
187
+ ! is_numeric( $node->getAttribute( 'height' ) )
188
+ ) {
189
+ $height = self::FALLBACK_HEIGHT;
190
+ $node->setAttribute( 'height', $height );
191
+ $class = $node->hasAttribute( 'class' ) ? $node->getAttribute( 'class' ) . ' amp-wp-unknown-size amp-wp-unknown-height' : 'amp-wp-unknown-size amp-wp-unknown-height';
192
+ $node->setAttribute( 'class', $class );
193
+ } elseif (
194
+ ! is_numeric( $node->getAttribute( 'width' ) )
195
+ ) {
196
+ $width = self::FALLBACK_WIDTH;
197
+ $node->setAttribute( 'width', $width );
198
+ $class = $node->hasAttribute( 'class' ) ? $node->getAttribute( 'class' ) . ' amp-wp-unknown-size amp-wp-unknown-width' : 'amp-wp-unknown-size amp-wp-unknown-width';
199
+ $node->setAttribute( 'class', $class );
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Now that all images have width and height attributes, make final tweaks and replace original image nodes
207
+ *
208
+ * @param DOMNodeList[] $node_lists Img DOM nodes (now with width and height attributes).
209
+ */
210
+ private function adjust_and_replace_nodes_in_array_map( $node_lists ) {
211
+ foreach ( $node_lists as $node_list ) {
212
+ foreach ( $node_list as $node ) {
213
+ $this->adjust_and_replace_node( $node );
214
+ }
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Make final modifications to DOMNode
220
+ *
221
+ * @param DOMNode $node The DOMNode to adjust and replace.
222
+ */
223
+ private function adjust_and_replace_node( $node ) {
224
+ $old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
225
+ $old_attributes = apply_filters('amp_img_attributes', $old_attributes);
226
+ $new_attributes = $this->filter_attributes( $old_attributes );
227
+ $this->add_or_append_attribute( $new_attributes, 'class', 'amp-wp-enforced-sizes' );
228
+ if ( empty( $new_attributes['layout'] ) && ! empty( $new_attributes['height'] ) && ! empty( $new_attributes['width'] ) ) {
229
+ $new_attributes['layout'] = 'intrinsic';
230
+ }
231
+ if ( $this->is_gif_url( $new_attributes['src'] ) ) {
232
+ $this->did_convert_elements = true;
233
+
234
+ $new_tag = 'amp-anim';
235
+ } else {
236
+ $new_tag = 'amp-img';
237
+ }
238
+ $new_node = AMP_DOM_Utils::create_node( $this->dom, $new_tag, $new_attributes );
239
+ $new_node = $this->handle_centering( $new_node );
240
+ $node->parentNode->replaceChild( $new_node, $node );
241
+ if ( isset($new_attributes['on']) && '' != $new_attributes['on'] ) {
242
+ add_action('amp_post_template_footer', 'ampforwp_amp_img_lightbox');
243
+ $this->is_lightbox = true;
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Determines is a URL is considered a GIF URL
249
+ *
250
+ * @since 0.2
251
+ *
252
+ * @param string $url URL to inspect for GIF vs. JPEG or PNG.
253
+ *
254
+ * @return bool Returns true if $url ends in `.gif`
255
+ */
256
+ private function is_gif_url( $url ) {
257
+ $ext = self::$anim_extension;
258
+ $path = AMP_WP_Utils::parse_url( $url, PHP_URL_PATH );
259
+ return substr( $path, -strlen( $ext ) ) === $ext;
260
+ }
261
+
262
+ /**
263
+ * Handles an issue with the aligncenter class.
264
+ *
265
+ * If the <amp-img> has the class aligncenter, this strips the class and wraps it in a <figure> to center the image.
266
+ * There was an issue where the aligncenter class overrode the "display: inline-block" rule of AMP's layout="intrinsic" attribute.
267
+ * So this strips that class, and instead wraps the image in a <figure> to center it.
268
+ *
269
+ * @since 0.7
270
+ * @see https://github.com/Automattic/amp-wp/issues/1104
271
+ *
272
+ * @param DOMElement $node The <amp-img> node.
273
+ * @return DOMElement $node The <amp-img> node, possibly wrapped in a <figure>.
274
+ */
275
+ public function handle_centering( $node ) {
276
+ $align_class = 'aligncenter';
277
+ $classes = $node->getAttribute( 'class' );
278
+ $width = $node->getAttribute( 'width' );
279
+
280
+ // If this doesn't have a width attribute, centering it in the <figure> wrapper won't work.
281
+ if ( empty( $width ) || ! in_array( $align_class, preg_split( '/\s+/', trim( $classes ) ), true ) ) {
282
+ return $node;
283
+ }
284
+
285
+ // Strip the class, and wrap the <amp-img> in a <figure>.
286
+ $classes = trim( str_replace( $align_class, '', $classes ) );
287
+ $node->setAttribute( 'class', $classes );
288
+ $figure = AMP_DOM_Utils::create_node(
289
+ $this->dom,
290
+ 'figure',
291
+ array(
292
+ 'class' => $align_class,
293
+ //'style' => "max-width: {$width}px;",
294
+ )
295
+ );
296
+ $figure->appendChild( $node );
297
+
298
+ return $figure;
299
+ }
300
+
301
+ public function get_scripts() {
302
+ if ( $this->is_lightbox ) {
303
+ $this->scripts[self::$script_slug_lightbox] = self::$script_src_lightbox;
304
+ }
305
+
306
+ if ( $this->did_convert_elements ) {
307
+ $this->scripts[self::$script_slug] = self::$script_src;
308
+ }
309
+
310
+ return $this->scripts;
311
+ }
312
+ }
classes/class-ampforwp-vimeo-embed.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMPforWP_Vimeo_Embed_Handler
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Class AMPforWP_Vimeo_Embed_Handler
10
+ *
11
+ * Much of this class is borrowed from Jetpack embeds
12
+ */
13
+ class AMPforWP_Vimeo_Embed_Handler extends AMP_Base_Embed_Handler {
14
+
15
+ const URL_PATTERN = '#https?:\/\/(www\.)?vimeo\.com\/.*#i';
16
+
17
+ const RATIO = 0.5625;
18
+
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
+ }
31
+
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() {
39
+ wp_embed_unregister_handler( 'amp-vimeo', -1 );
40
+ remove_shortcode( 'vimeo' );
41
+ }
42
+
43
+ public function shortcode( $attr ) {
44
+ $video_id = false;
45
+
46
+ if ( isset( $attr['id'] ) ) {
47
+ $video_id = $attr['id'];
48
+ } elseif ( isset( $attr['url'] ) ) {
49
+ $video_id = $this->get_video_id_from_url($attr['url']);
50
+ }elseif ( isset( $attr[0] ) ) {
51
+ $video_id = $this->get_video_id_from_url($attr[0]);
52
+ } elseif ( function_exists( 'shortcode_new_to_old_params' ) ) {
53
+ $video_id = shortcode_new_to_old_params( $attr );
54
+ }
55
+
56
+ if ( empty( $video_id ) ) {
57
+ return '';
58
+ }
59
+
60
+ return $this->render( array(
61
+ 'video_id' => $video_id,
62
+ ) );
63
+ }
64
+
65
+ public function oembed( $matches, $attr, $url, $rawattr ) {
66
+ $video_id = $this->get_video_id_from_url( $url );
67
+
68
+ return $this->render( array(
69
+ 'url' => $url,
70
+ 'video_id' => $video_id,
71
+ ) );
72
+ }
73
+
74
+ public function render( $args ) {
75
+ $args = wp_parse_args( $args, array(
76
+ 'video_id' => false,
77
+ ) );
78
+
79
+ if ( empty( $args['video_id'] ) ) {
80
+ return AMP_HTML_Utils::build_tag( 'a', array( 'href' => esc_url( $args['url'] ), 'class' => 'amp-wp-embed-fallback' ), esc_html( $args['url'] ) );
81
+ }
82
+
83
+ $this->did_convert_elements = true;
84
+
85
+ return AMP_HTML_Utils::build_tag(
86
+ 'amp-vimeo',
87
+ array(
88
+ 'data-videoid' => $args['video_id'],
89
+ 'layout' => 'responsive',
90
+ 'width' => $this->args['width'],
91
+ 'height' => $this->args['height'],
92
+ )
93
+ );
94
+ }
95
+
96
+ // get_video_id_from_url()
97
+ // Takes the last component of a Vimeo URL
98
+ // and returns it as the associated video id
99
+ private function get_video_id_from_url( $url ) {
100
+ $parsed_url = parse_url( $url );
101
+ parse_str( $parsed_url['path'], $path );
102
+
103
+ $video_id = "";
104
+ if ( $path ) {
105
+ $tok = explode( '/', $parsed_url['path'] );
106
+ $tok = apply_filters('amp_vimeo_parse_url',$tok);
107
+ $video_id = $tok;
108
+ }
109
+
110
+ return $video_id;
111
+ }
112
+
113
+ /**
114
+ * Override the output of Vimeo videos.
115
+ *
116
+ * This overrides the value in wp_video_shortcode().
117
+ * The pattern matching is copied from WP_Widget_Media_Video::render().
118
+ *
119
+ * @param string $html Empty variable to be replaced with shortcode markup.
120
+ * @param array $attr The shortcode attributes.
121
+ * @return string|null $markup The markup to output.
122
+ */
123
+ public function video_override( $html, $attr ) {
124
+ if ( ! isset( $attr['src'] ) ) {
125
+ return $html;
126
+ }
127
+ $src = $attr['src'];
128
+ $vimeo_pattern = '#^https?://(.+\.)?vimeo\.com/.*#';
129
+ if ( 1 === preg_match( $vimeo_pattern, $src ) ) {
130
+ return $this->shortcode( array( $src ) );
131
+ }
132
+ return $html;
133
+ }
134
+
135
+ }
images/Bitmap.png ADDED
Binary file
images/amplogo.png ADDED
Binary file
images/converttoamp.png ADDED
Binary file
images/ex-1.png ADDED
Binary file
images/ex-2.png ADDED
Binary file
images/ex-3.png ADDED
Binary file
images/layouts-3.png ADDED
Binary file
images/layouts-4.png ADDED
Binary file
images/layouts-5.png ADDED
Binary file
images/layouts-6.png ADDED
Binary file
images/nf.png ADDED
Binary file
images/rating.png ADDED
Binary file
images/tick.png ADDED
Binary file
includes/options/extensions/theme_design_selector/extension_theme_design_selector.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Redux Framework is free software: you can redistribute it and/or modify
4
+ * it under the terms of the GNU General Public License as published by
5
+ * the Free Software Foundation, either version 2 of the License, or
6
+ * any later version.
7
+ *
8
+ * Redux Framework is distributed in the hope that it will be useful,
9
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ * GNU General Public License for more details.
12
+ *
13
+ * You should have received a copy of the GNU General Public License
14
+ * along with Redux Framework. If not, see <http://www.gnu.org/licenses/>.
15
+ *
16
+ * @package ReduxFramework
17
+ * @author Dovy Paukstys (dovy)
18
+ * @version 3.0.0
19
+ */
20
+ // Exit if accessed directly
21
+ if( !defined( 'ABSPATH' ) ) exit;
22
+ // Don't duplicate me!
23
+ if( !class_exists( 'ReduxFramework_extension_theme_design_selector' ) ) {
24
+ /**
25
+ * Main ReduxFramework demolink_image_select extension class
26
+ *
27
+ * @since 3.1.6
28
+ */
29
+ class ReduxFramework_extension_theme_design_selector {
30
+
31
+ // Set the version number of your extension here
32
+ public static $version = '1.0.0';
33
+ // Set the name of your extension here
34
+ public $ext_name = 'Theme Design Selector';
35
+
36
+ // Set the minumum required version of Redux here (optional).
37
+ // Leave blank to require no minimum version.
38
+ // This allows you to specify a minimum required version of Redux in the event
39
+ // you do not want to support older versions.
40
+ public $min_redux_version = '3.0.0';
41
+ // Protected vars
42
+ protected $parent;
43
+ public $extension_url;
44
+ public $extension_dir;
45
+ public static $theInstance;
46
+ /**
47
+ * Class Constructor. Defines the args for the extions class
48
+ *
49
+ * @since 1.0.0
50
+ * @access public
51
+ * @param array $sections Panel sections.
52
+ * @param array $args Class constructor arguments.
53
+ * @param array $extra_tabs Extra panel tabs.
54
+ * @return void
55
+ */
56
+ public function __construct( $parent ) {
57
+
58
+ $this->parent = $parent;
59
+
60
+ if (is_admin() && !$this->is_minimum_version()) {
61
+ return;
62
+ }
63
+
64
+ if ( empty( $this->extension_dir ) ) {
65
+ $this->extension_dir = trailingslashit( str_replace( '\\', '/', dirname( __FILE__ ) ) );
66
+ $this->extension_url = site_url( str_replace( trailingslashit( str_replace( '\\', '/', ABSPATH ) ), '', $this->extension_dir ) );
67
+ }
68
+
69
+ $this->field_name = 'theme_design_selector';
70
+ self::$theInstance = $this;
71
+ add_filter( 'redux/'.$this->parent->args['opt_name'].'/field/class/'.$this->field_name, array( &$this, 'overload_field_path' ) ); // Adds the local field
72
+ }
73
+ public function getInstance() {
74
+ return self::$theInstance;
75
+ }
76
+ // Forces the use of the embeded field path vs what the core typically would use
77
+ public function overload_field_path($field) {
78
+ return dirname(__FILE__).'/'.$this->field_name.'/field_'.$this->field_name.'.php';
79
+ }
80
+ private function is_minimum_version () {
81
+ $redux_ver = ReduxFramework::$_version;
82
+ if ($this->min_redux_version != '') {
83
+ if (version_compare($redux_ver, $this->min_redux_version) < 0) {
84
+ $msg = '<strong>' . esc_html__( 'The', 'redux-framework') . ' ' . $this->ext_name . ' ' . esc_html__('extension requires', 'redux-framework') . ' Redux Framework ' . esc_html__('version', 'redux-framework') . ' ' . $this->min_redux_version . ' ' . esc_html__('or higher.','redux-framework' ) . '</strong>&nbsp;&nbsp;' . esc_html__( 'You are currently running', 'redux-framework') . ' Redux Framework ' . esc_html__('version','redux-framework' ) . ' ' . $redux_ver . '.<br/><br/>' . esc_html__('This field will not render in your option panel, and featuress of this extension will not be available until the latest version of','redux-framework' ) . ' Redux Framework ' . esc_html__('has been installed.','redux-framework' );
85
+
86
+ $data = array(
87
+ 'parent' => $this->parent,
88
+ 'type' => 'error',
89
+ 'msg' => $msg,
90
+ 'id' => $this->ext_name . '_notice_' . self::$version,
91
+ 'dismiss' => false
92
+ );
93
+
94
+ if (method_exists('Redux_Admin_Notices', 'set_notice')) {
95
+ Redux_Admin_Notices::set_notice($data);
96
+ } else {
97
+ echo '<div class="error">';
98
+ echo '<p>';
99
+ echo $msg;
100
+ echo '</p>';
101
+ echo '</div>';
102
+ }
103
+ return false;
104
+ }
105
+ }
106
+
107
+ return true;
108
+ }
109
+ } // class
110
+ } // if
includes/options/extensions/theme_design_selector/theme_design_selector/field_theme_design_selector.css ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ul.theme_design_selector input[type=radio]{
2
+ display:none;
3
+ }
4
+ .theme_design_selector li {
5
+ width: 180px !important;
6
+ float: left;
7
+ text-align: center;
8
+ border-radius: 3px;
9
+ z-index: 100;
10
+ padding: 7px 4px 4px 4px;
11
+ font-size: 13px;
12
+ margin-right: 12px;
13
+ position: relative;
14
+ border: 1px solid #e8e7e7;
15
+ }
16
+ .amp-cnvrtr-wrap{
17
+ position: absolute;
18
+ left: 40px;
19
+ top: 92px;
20
+ font-size: 11px;
21
+ text-transform: uppercase;
22
+ color: #666;
23
+ background: #f1f1f1;
24
+ border-radius: 20px;
25
+ padding: 0px 6px;
26
+ }
27
+ .redux-main .data-full :nth-child(2) span.amp-cnvrtr-wrap {
28
+ left: 80px;
29
+ }
30
+ .theme_design_selector li :first{
31
+ margin-left:0px;
32
+ }
33
+ .theme_design_selector li img{
34
+ width: auto !important;
35
+ display: block;
36
+ margin: 0px auto;
37
+ min-height: 30px !important;
38
+ margin-bottom: 5px;
39
+ height: 45px}
40
+ .redux-main .data-full :nth-child(2) img{
41
+ height: 37px;
42
+ opacity: 0.9;
43
+ margin: 6px auto 7px auto;
44
+ }
45
+ .redux-main .data-full :nth-child(2) .amp-cnvrtr-wrap{
46
+ left: 70px;
47
+ }
48
+ .theme_design_selector li:hover .amp-cnvrtr-wrap {
49
+ background: #5ec600;font-weight: bold;
50
+ color: #fff;
51
+ }
52
+ .theme_design_selector li:nth-child(2):hover .amp-cnvrtr-wrap {
53
+ font-weight: bold;background: #D32F2F; color: #fff;
54
+ }
55
+ .theme_design_selector li:hover{
56
+ background: #f1f1f1
57
+ }
58
+ .theme_design_selector li.active{
59
+ background: #f9f9f9;
60
+ border-color: #ccc
61
+ }
62
+ .theme_design_selector li.active:before {
63
+ content: "\f147";
64
+ position: absolute;
65
+ font-family: dashicons;
66
+ color: #fff;
67
+ background: #5EC600;
68
+ font-size: 22px;
69
+ border-radius: 40px;
70
+ margin: -2px 0 0 -9px;
71
+ padding: 3px 3px 3px 0px;
72
+ }
73
+ .form-table .theme_design_selector label{
74
+ margin-bottom: 5px !important
75
+ }
76
+ .amp-design-type-selection_1 img{
77
+ height: 37px !important;
78
+ }
includes/options/extensions/theme_design_selector/theme_design_selector/field_theme_design_selector.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($){
2
+ $('.theme_design_selector li label').click(function(){
3
+ var current = $(this).parent();
4
+ current.siblings().removeClass('active');
5
+ current.addClass('active');
6
+ });
7
+ });
includes/options/extensions/theme_design_selector/theme_design_selector/field_theme_design_selector.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ if ( ! class_exists( 'ReduxFramework_theme_design_selector' ) ) {
9
+ class ReduxFramework_theme_design_selector {
10
+
11
+ /**
12
+ * Field Constructor.
13
+ * Required - must call the parent constructor, then assign field and value to vars, and obviously call the render field function
14
+ *
15
+ * @since ReduxFramework 1.0.0
16
+ */
17
+ function __construct( $field = array(), $value = '', $parent ) {
18
+ $this->parent = $parent;
19
+ $this->field = $field;
20
+ $this->value = $value;
21
+ $this->time = time();
22
+ if ( defined('AMPFORWP_VERSION') ) {
23
+ $this->time = AMPFORWP_VERSION;
24
+ }
25
+ if ( empty( $this->extension_dir ) ) {
26
+ $this->extension_dir = trailingslashit( str_replace( '\\', '/', dirname( __FILE__ ) ) );
27
+ $this->extension_url = site_url( str_replace( trailingslashit( str_replace( '\\', '/', ABSPATH ) ), '', $this->extension_dir ) );
28
+ }
29
+
30
+ // Set default args for this field to avoid bad indexes. Change this to anything you use.
31
+ $defaults = array(
32
+ 'options' => array(),
33
+ //'stylesheet' => '',
34
+ //'output' => true,
35
+ 'enqueue' => true,
36
+ 'enqueue_frontend' => true
37
+ );
38
+ $this->field = wp_parse_args( $this->field, $defaults );
39
+ }
40
+
41
+ /**
42
+ * Field Render Function.
43
+ * Takes the vars and outputs the HTML for the field in the settings
44
+ *
45
+ * @since ReduxFramework 1.0.0
46
+ */
47
+ function render() {
48
+
49
+ if ( ! empty( $this->field['data'] ) && empty( $this->field['options'] ) ) {
50
+ if ( empty( $this->field['args'] ) ) {
51
+ $this->field['args'] = array();
52
+ }
53
+ $this->field['options'] = $this->parent->get_wordpress_data( $this->field['data'], $this->field['args'] );
54
+ }
55
+
56
+ $this->field['data_class'] = ( isset( $this->field['multi_layout'] ) ) ? 'data-' . $this->field['multi_layout'] : 'data-full';
57
+
58
+ if ( ! empty( $this->field['options'] ) ) {
59
+ echo '<ul class="theme_design_selector ' . $this->field['data_class'] . '">';
60
+
61
+ foreach ( $this->field['options'] as $k => $v ) {
62
+ $liClass = '';
63
+ if($this->value==$k){
64
+ $liClass = 'active';
65
+ }
66
+ echo '<li class="'.$liClass.'">';
67
+ echo '<label for="' . $this->field['id'] . '_' . array_search( $k, array_keys( $this->field['options'] ) ) . '">';
68
+ echo '<span class="image-wrap"><img src="'.$this->field['options_image'][$k].'"></span>';
69
+ echo '<input type="radio" class="radio ' . $this->field['class'] . '" id="' . $this->field['id'] . '_' . array_search( $k, array_keys( $this->field['options'] ) ) . '" name="' . $this->field['name'] . $this->field['name_suffix'] . '" value="' . $k . '" ' . checked( $this->value, $k, false ) . '/>';
70
+ echo ' <span>' . $v . '</span>';
71
+ echo '<span class="amp-cnvrtr-wrap">'.$this->field['options_message'][$k].'</span>';
72
+ echo '</label>';
73
+ echo '</li>';
74
+ }
75
+ //foreach
76
+
77
+ echo '</ul>';
78
+ }
79
+ } //function
80
+ /**
81
+ * Enqueue Function.
82
+ * If this field requires any scripts, or css define this function and register/enqueue the scripts/css
83
+ *
84
+ * @since ReduxFramework 1.0.0
85
+ */
86
+ function enqueue() {
87
+ wp_enqueue_style(
88
+ 'redux-field-theme-design-selector',
89
+ $this->extension_url . 'field_theme_design_selector.css',
90
+ $this->time,
91
+ true
92
+ );
93
+ wp_enqueue_script(
94
+ 'field-theme-design-selector-js',
95
+ $this->extension_url .'field_theme_design_selector.js',
96
+ array('jquery', 'redux-js'),
97
+ $this->time,
98
+ true
99
+ );
100
+ } //function
101
+ } //class
102
+ }
includes/vendor/amp/assets/css/amp-customizer.css ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .amp-toggle {
2
+ position: relative;
3
+ display: inline-block;
4
+ width: 30px;
5
+ height: 15px;
6
+ top: 15px;
7
+ left: 130px;
8
+ }
9
+
10
+ .amp-toggle input,
11
+ .amp-toggle input.disabled {
12
+ position: absolute;
13
+ top: 0;
14
+ left: 0;
15
+ width: 100%;
16
+ height: 100%;
17
+ opacity: 0;
18
+ margin: 0;
19
+ padding: 0;
20
+ z-index: 1;
21
+ }
22
+
23
+ .amp-toggle .slider {
24
+ position: absolute;
25
+ cursor: pointer;
26
+ top: 0;
27
+ left: 0;
28
+ right: 0;
29
+ bottom: 0;
30
+ border-radius: 34px;
31
+ background-color: #555D66;
32
+ -webkit-transition: .3s;
33
+ transition: .3s;
34
+ transition-property: background-color, transform, -webkit-transform, -ms-transform, opacity;
35
+ }
36
+ .amp-toggle input:focus,
37
+ .amp-toggle input:active {
38
+ outline: none;
39
+ }
40
+ .amp-toggle input:hover + .slider,
41
+ .amp-toggle input:focus + .slider,
42
+ .amp-toggle input:active + .slider {
43
+ box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8);
44
+ }
45
+
46
+ .amp-toggle .slider:before {
47
+ position: absolute;
48
+ content: "";
49
+ height: 13px;
50
+ width: 13px;
51
+ top: 1px;
52
+ left: 1px;
53
+ border-radius: 50%;
54
+ background-color: transparent;
55
+ background-image: url( '../images/amp-white-icon.svg' );
56
+ background-size: 13px 13px;
57
+ -webkit-transition: .3s;
58
+ transition: .3s;
59
+ }
60
+
61
+ .amp-toggle input:checked + .slider {
62
+ background-color: #0379C4;
63
+ }
64
+
65
+ .amp-toggle input:checked + .slider:before {
66
+ -webkit-transform: translateX( 15px );
67
+ -ms-transform: translateX( 15px );
68
+ transform: translateX( 15px );
69
+ }
70
+
71
+ .amp-toggle input.disabled + .slider {
72
+ opacity: 0.7;
73
+ }
74
+
75
+ .amp-toggle .tooltip {
76
+ position: absolute;
77
+ bottom: 25px;
78
+ left: -115px;
79
+ width: 230px;
80
+ font-size: 13px;
81
+ text-align: center;
82
+ color: #FFFFFF;
83
+ background: #191E23;
84
+ padding: 15px;
85
+ z-index: 1;
86
+ cursor: default;
87
+ display: none;
88
+ }
89
+ .amp-toggle .tooltip a {
90
+ color: #00a0d2;
91
+ }
92
+ .amp-toggle .tooltip a:hover,
93
+ .amp-toggle .tooltip a:focus,
94
+ .amp-toggle .tooltip a:active {
95
+ color: #54cbf1;
96
+ }
97
+
98
+ .amp-toggle .tooltip:before {
99
+ position: absolute;
100
+ bottom: -8px;
101
+ left: 120px;
102
+ content: "";
103
+ border: solid;
104
+ border-color: #191E23 transparent;
105
+ border-width: 8px 8px 0 8px;
106
+ }
107
+
108
+ .js .accordion-section-title:after {
109
+ z-index: 0;
110
+ }
111
+
112
+ #customize-footer-actions .collapse-sidebar-label {
113
+ font-size: 11px;
114
+ margin-left: -3px;
115
+ }
116
+
117
+ .devices-wrapper .preview-desktop {
118
+ border-left: 1px solid #DDDDDD !important;
119
+ }
120
+
121
+ .wp-full-overlay-footer .devices button:before {
122
+ vertical-align: initial;
123
+ }
includes/vendor/amp/assets/css/amp-default.css ADDED
@@ -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
+ }
includes/vendor/amp/assets/css/amp-playlist-shortcode.css ADDED
@@ -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
+ }
includes/vendor/amp/assets/css/amp-post-meta-box.css ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * 1.0 AMP preview.
3
+ *
4
+ * Submit box preview buttons.
5
+ */
6
+
7
+ /* Core preview button */
8
+ .wp-core-ui #preview-action.has-amp-preview #post-preview {
9
+ border-top-right-radius: 0;
10
+ border-bottom-right-radius: 0;
11
+ float: none;
12
+ }
13
+
14
+ /* AMP preview button */
15
+ .wp-core-ui #amp-post-preview.preview {
16
+ border-top-left-radius: 0;
17
+ border-bottom-left-radius: 0;
18
+ text-indent: -9999px;
19
+ padding-right: 14px;
20
+ padding-left: 14px;
21
+ position: relative;
22
+ }
23
+
24
+ .wp-core-ui #amp-post-preview.preview::after {
25
+ content: "icon";
26
+ background: no-repeat center url( '../images/amp-icon.svg' );
27
+ background-size: 14px !important;
28
+ top: 0;
29
+ right: 0;
30
+ bottom: 0;
31
+ left: 0;
32
+ display: block;
33
+ position: absolute;
34
+ }
35
+
36
+ .wp-core-ui #amp-post-preview.preview.disabled::after {
37
+ opacity: 0.6;
38
+ }
39
+
40
+ /* AMP status */
41
+ .misc-amp-status .amp-icon {
42
+ float: left;
43
+ background: transparent url( '../images/amp-icon.svg' ) no-repeat left;
44
+ background-size: 17px;
45
+ width: 17px;
46
+ height: 17px;
47
+ margin: 0 8px 0 1px;
48
+ }
49
+
50
+ #amp-status-select fieldset {
51
+ margin: 7px 0 0 1px;
52
+ }
53
+
54
+ #amp-status-select .notice {
55
+ margin: 10px 0 -5px 3px;
56
+ }
57
+
58
+ .amp-status-actions {
59
+ margin-top: 10px;
60
+ }
61
+
62
+ @media screen and ( max-width: 782px ) {
63
+ #amp-status-select {
64
+ line-height: 280%;
65
+ }
66
+ }
includes/vendor/amp/assets/images/amp-icon.svg ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg width="62px" height="62px" viewBox="0 0 62 62" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3
+ <title>AMP-Icon</title>
4
+ <g id="AMP-Icon" fill="#82878c">
5
+ <path d="M41.6288667,28.1614333 L28.6243667,49.8035667 L26.2683667,49.8035667 L28.5975,35.7016667 L21.3838,35.7109667 C21.3838,35.7109667 21.3156,35.7130333 21.2835667,35.7130333 C20.6336,35.7130333 20.1076333,35.1870667 20.1076333,34.5371 C20.1076333,34.2581 20.367,33.7858667 20.367,33.7858667 L33.3291333,12.1695667 L35.7244,12.1799 L33.3363667,26.3035 L40.5872667,26.2942 C40.5872667,26.2942 40.6647667,26.2931667 40.7019667,26.2931667 C41.3519333,26.2931667 41.8779,26.8191333 41.8779,27.4691 C41.8779,27.7326 41.7745667,27.9640667 41.6278333,28.1604 L41.6288667,28.1614333 Z M31,0 C13.8787,0 0,13.8797333 0,31 C0,48.1213 13.8787,62 31,62 C48.1202667,62 62,48.1213 62,31 C62,13.8797333 48.1202667,0 31,0 L31,0 Z" id="Fill-1"></path>
6
+ </g>
7
+ </svg>
includes/vendor/amp/assets/images/amp-white-icon.svg ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg width="62px" height="62px" viewBox="0 0 62 62" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3
+ <title>AMP-White-Icon</title>
4
+ <g id="amp-logo-internal-site" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
5
+ <g id="AMP-Brand-White-Icon" fill="#FFFFFF">
6
+ <path d="M41.6288667,28.1614333 L28.6243667,49.8035667 L26.2683667,49.8035667 L28.5975,35.7016667 L21.3838,35.7109667 C21.3838,35.7109667 21.3156,35.7130333 21.2835667,35.7130333 C20.6336,35.7130333 20.1076333,35.1870667 20.1076333,34.5371 C20.1076333,34.2581 20.367,33.7858667 20.367,33.7858667 L33.3291333,12.1695667 L35.7244,12.1799 L33.3363667,26.3035 L40.5872667,26.2942 C40.5872667,26.2942 40.6647667,26.2931667 40.7019667,26.2931667 C41.3519333,26.2931667 41.8779,26.8191333 41.8779,27.4691 C41.8779,27.7326 41.7745667,27.9640667 41.6278333,28.1604 L41.6288667,28.1614333 Z M31,0 C13.8787,0 0,13.8797333 0,31 C0,48.1213 13.8787,62 31,62 C48.1202667,62 62,48.1213 62,31 C62,13.8797333 48.1202667,0 31,0 L31,0 Z" id="Fill-1"></path>
7
+ </g>
8
+ </g>
9
+ </svg>
includes/vendor/amp/assets/js/amp-customize-controls.js ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* exported ampCustomizeControls */
2
+ /* eslint no-magic-numbers: [ "error", { "ignore": [ 0, 1, 250] } ] */
3
+
4
+ var ampCustomizeControls = ( function( api, $ ) {
5
+ 'use strict';
6
+
7
+ var component = {
8
+ data: {
9
+ queryVar: 'amp',
10
+ panelId: '',
11
+ ampUrl: '',
12
+ l10n: {
13
+ unavailableMessage: '',
14
+ unavailableLinkText: ''
15
+ }
16
+ },
17
+ tooltipTimeout: 5000,
18
+ tooltipVisible: new api.Value( false ),
19
+ tooltipFocused: new api.Value( 0 )
20
+ };
21
+
22
+ /**
23
+ * Boot using data sent inline.
24
+ *
25
+ * @param {Object} data Object data.
26
+ * @return {void}
27
+ */
28
+ component.boot = function boot( data ) {
29
+ component.data = data;
30
+
31
+ function initPanel() {
32
+ api.panel( component.data.panelId, component.panelReady );
33
+ }
34
+
35
+ if ( api.state ) {
36
+ component.addState();
37
+ api.bind( 'ready', initPanel );
38
+ } else { // WP<4.9.
39
+ api.bind( 'ready', function() {
40
+ component.addState(); // Needed for WP<4.9.
41
+ initPanel();
42
+ } );
43
+ }
44
+ };
45
+
46
+ /**
47
+ * Add state for AMP.
48
+ *
49
+ * @returns {void}
50
+ */
51
+ component.addState = function addState() {
52
+ api.state.add( 'ampEnabled', new api.Value( false ) );
53
+ api.state.add( 'ampAvailable', new api.Value( false ) );
54
+ };
55
+
56
+ /**
57
+ * Check if the URL is AMPified.
58
+ *
59
+ * @param {string} url URL.
60
+ * @return {boolean} whether it is an AMP URL.
61
+ */
62
+ component.isAmpUrl = function isAmpUrl( url ) {
63
+ var urlParser = document.createElement( 'a' ),
64
+ regexEndpoint = new RegExp( '\\/' + component.data.queryVar + '\\/?$' );
65
+
66
+ urlParser.href = url;
67
+ if ( ! _.isUndefined( wp.customize.utils.parseQueryString( urlParser.search.substr( 1 ) )[ component.data.queryVar ] ) ) {
68
+ return true;
69
+ }
70
+ return regexEndpoint.test( urlParser.pathname );
71
+ };
72
+
73
+ /**
74
+ * Create an non-AMP version of a URL.
75
+ *
76
+ * @param {string} url URL.
77
+ * @return {string} non-AMPified URL.
78
+ */
79
+ component.unampifyUrl = function unampifyUrl( url ) {
80
+ var urlParser = document.createElement( 'a' ),
81
+ regexEndpoint = new RegExp( '\\/' + component.data.queryVar + '\\/?$' ),
82
+ params;
83
+
84
+ urlParser.href = url;
85
+ urlParser.pathname = urlParser.pathname.replace( regexEndpoint, '' );
86
+
87
+ if ( urlParser.search.length > 1 ) {
88
+ params = wp.customize.utils.parseQueryString( urlParser.search.substr( 1 ) );
89
+ delete params[ component.data.queryVar ];
90
+ urlParser.search = $.param( params );
91
+ }
92
+
93
+ return urlParser.href;
94
+ };
95
+
96
+ /**
97
+ * Create an AMP version of a URL.
98
+ *
99
+ * @param {string} url URL.
100
+ * @return {string} AMPified URL.
101
+ */
102
+ component.ampifyUrl = function ampifyUrl( url ) {
103
+ var urlParser = document.createElement( 'a' );
104
+ urlParser.href = component.unampifyUrl( url );
105
+ if ( urlParser.search.length ) {
106
+ urlParser.search += '&';
107
+ }
108
+ urlParser.search += component.data.queryVar + '=1';
109
+ return urlParser.href;
110
+ };
111
+
112
+ /**
113
+ * Try to close the tooltip after a given timeout.
114
+ *
115
+ * @returns {void}
116
+ */
117
+ component.tryToCloseTooltip = function tryToCloseTooltip() {
118
+ clearTimeout( component.tooltipTimeoutId );
119
+ component.tooltipTimeoutId = setTimeout( function() {
120
+ if ( ! component.tooltipVisible.get() ) {
121
+ return;
122
+ }
123
+ if ( component.tooltipFocused.get() > 0 ) {
124
+ component.tryToCloseTooltip();
125
+ } else {
126
+ component.tooltipVisible.set( false );
127
+ }
128
+ }, component.tooltipTimeout );
129
+ };
130
+
131
+ /**
132
+ * Make current URL AMPified if toggle is on.
133
+ *
134
+ * @param {string} url URL.
135
+ * @return {string} AMPified URL.
136
+ */
137
+ component.setCurrentAmpUrl = function setCurrentAmpUrl( url ) {
138
+ var enabled = api.state( 'ampEnabled' ).get();
139
+ if ( ! enabled && component.isAmpUrl( url ) ) {
140
+ return component.unampifyUrl( url );
141
+ } else if ( enabled && ! component.isAmpUrl( url ) ) {
142
+ return component.ampifyUrl( url );
143
+ }
144
+ return url;
145
+ };
146
+
147
+ /**
148
+ * Swap to AMP version of URL in preview.
149
+ *
150
+ * @return {void}
151
+ */
152
+ component.updatePreviewUrl = function updatePreviewUrl() {
153
+ api.previewer.previewUrl.set( component.setCurrentAmpUrl( api.previewer.previewUrl.get() ) );
154
+ };
155
+
156
+ /**
157
+ * Enable AMP and navigate to the given URL.
158
+ *
159
+ * @param {string} url - URL.
160
+ * @returns {void}
161
+ */
162
+ component.enableAndNavigateToUrl = function enableAndNavigateToUrl( url ) {
163
+ api.state( 'ampEnabled' ).set( true );
164
+ api.previewer.previewUrl.set( url );
165
+ };
166
+
167
+ /**
168
+ * Update panel notifications.
169
+ *
170
+ * @returns {void}
171
+ */
172
+ component.updatePanelNotifications = function updatePanelNotifications() {
173
+ var panel = api.panel( component.data.panelId ), containers;
174
+ containers = panel.sections().concat( [ panel ] );
175
+ if ( api.state( 'ampAvailable' ).get() ) {
176
+ _.each( containers, function( container ) {
177
+ container.notifications.remove( 'amp_unavailable' );
178
+ } );
179
+ } else {
180
+ _.each( containers, function( container ) {
181
+ container.notifications.add( new api.Notification( 'amp_unavailable', {
182
+ message: component.data.l10n.unavailableMessage,
183
+ type: 'info',
184
+ linkText: component.data.l10n.unavailableLinkText,
185
+ url: component.data.ampUrl,
186
+ templateId: 'customize-amp-unavailable-notification',
187
+ render: function() {
188
+ var li = api.Notification.prototype.render.call( this );
189
+ li.find( 'a' ).on( 'click', function( event ) {
190
+ event.preventDefault();
191
+ component.enableAndNavigateToUrl( this.href );
192
+ } );
193
+ return li;
194
+ }
195
+ } ) );
196
+ } );
197
+ }
198
+ };
199
+
200
+ /**
201
+ * Hook up all AMP preview interactions once panel is ready.
202
+ *
203
+ * @param {wp.customize.Panel} panel The AMP panel.
204
+ * @return {void}
205
+ */
206
+ component.panelReady = function panelReady( panel ) {
207
+ var ampToggleContainer, checkbox, tooltip, tooltipLink;
208
+
209
+ ampToggleContainer = $( wp.template( 'customize-amp-enabled-toggle' )( {
210
+ message: component.data.l10n.unavailableMessage,
211
+ linkText: component.data.l10n.unavailableLinkText,
212
+ url: component.data.ampUrl
213
+ } ) );
214
+ checkbox = ampToggleContainer.find( 'input[type=checkbox]' );
215
+ tooltip = ampToggleContainer.find( '.tooltip' );
216
+ tooltipLink = tooltip.find( 'a' );
217
+
218
+ // AMP panel triggers the input toggle for AMP preview.
219
+ panel.expanded.bind( function( expanded ) {
220
+ if ( ! expanded ) {
221
+ return;
222
+ }
223
+ if ( api.state( 'ampAvailable' ).get() ) {
224
+ api.state( 'ampEnabled' ).set( panel.expanded.get() );
225
+ } else if ( ! panel.notifications ) {
226
+
227
+ /*
228
+ * This is only done if panel notifications aren't supported.
229
+ * If they are (as of 4.9) then a notification will be shown
230
+ * in the panel and its sections when AMP is not available.
231
+ */
232
+ setTimeout( function() {
233
+ component.tooltipVisible.set( true );
234
+ }, 250 );
235
+ }
236
+ } );
237
+
238
+ if ( panel.notifications ) {
239
+ api.state( 'ampAvailable' ).bind( component.updatePanelNotifications );
240
+ component.updatePanelNotifications();
241
+ api.section.bind( 'add', component.updatePanelNotifications );
242
+ }
243
+
244
+ // Enable AMP toggle if available and mobile device selected.
245
+ api.previewedDevice.bind( function( device ) {
246
+ if ( api.state( 'ampAvailable' ).get() ) {
247
+ api.state( 'ampEnabled' ).set( 'mobile' === device );
248
+ }
249
+ } );
250
+
251
+ // Message coming from previewer.
252
+ api.previewer.bind( 'amp-status', function( data ) {
253
+ api.state( 'ampAvailable' ).set( data.available );
254
+ } );
255
+ function setInitialAmpEnabledState( data ) {
256
+ api.state( 'ampEnabled' ).set( data.enabled );
257
+ api.previewer.unbind( 'amp-status', setInitialAmpEnabledState );
258
+ }
259
+ api.previewer.bind( 'amp-status', setInitialAmpEnabledState );
260
+
261
+ /*
262
+ * Persist the presence or lack of the amp=1 param when navigating in the preview,
263
+ * even if current page is not yet supported.
264
+ */
265
+ api.previewer.previewUrl.validate = ( function( prevValidate ) {
266
+ return function( value ) {
267
+ var val = prevValidate.call( this, value );
268
+ if ( val ) {
269
+ val = component.setCurrentAmpUrl( val );
270
+ }
271
+ return val;
272
+ };
273
+ } )( api.previewer.previewUrl.validate );
274
+
275
+ // Listen for ampEnabled state changes.
276
+ api.state( 'ampEnabled' ).bind( function( enabled ) {
277
+ checkbox.prop( 'checked', enabled );
278
+ component.updatePreviewUrl();
279
+ } );
280
+
281
+ // Listen for ampAvailable state changes.
282
+ api.state( 'ampAvailable' ).bind( function( available ) {
283
+ checkbox.toggleClass( 'disabled', ! available );
284
+
285
+ // Show the unavailable tooltip if AMP is enabled.
286
+ if ( api.state( 'ampEnabled' ).get() ) {
287
+ component.tooltipVisible.set( ! available );
288
+ }
289
+ } );
290
+
291
+ // Adding checkbox toggle before device selection.
292
+ $( '.devices-wrapper' ).before( ampToggleContainer );
293
+
294
+ // User clicked link within tooltip, go to linked post in preview.
295
+ tooltipLink.on( 'click', function( event ) {
296
+ event.preventDefault();
297
+ component.enableAndNavigateToUrl( this.href );
298
+ } );
299
+
300
+ // Toggle visibility of tooltip based on tooltipVisible state.
301
+ component.tooltipVisible.bind( function( visible ) {
302
+ tooltip.attr( 'aria-hidden', visible ? 'false' : 'true' );
303
+ if ( visible ) {
304
+ $( document ).on( 'click.amp-toggle-outside', function( event ) {
305
+ if ( ! $.contains( ampToggleContainer[0], event.target ) ) {
306
+ component.tooltipVisible.set( false );
307
+ }
308
+ } );
309
+ tooltip.fadeIn();
310
+ component.tryToCloseTooltip();
311
+ } else {
312
+ tooltip.fadeOut();
313
+ component.tooltipFocused.set( 0 );
314
+ $( document ).off( 'click.amp-toggle-outside' );
315
+ }
316
+ } );
317
+
318
+ // Handle click on checkbox to either enable the AMP preview or show the tooltip.
319
+ checkbox.on( 'click', function() {
320
+ this.checked = ! this.checked; // Undo what we just did, since state is managed in ampAvailable change handler.
321
+ if ( api.state( 'ampAvailable' ).get() ) {
322
+ api.state( 'ampEnabled' ).set( ! api.state( 'ampEnabled' ).get() );
323
+ } else {
324
+ component.tooltipVisible.set( true );
325
+ }
326
+ } );
327
+
328
+ // Keep track of the user's state interacting with the tooltip.
329
+ tooltip.on( 'mouseenter', function() {
330
+ if ( ! api.state( 'ampAvailable' ).get() ) {
331
+ component.tooltipVisible.set( true );
332
+ }
333
+ component.tooltipFocused.set( component.tooltipFocused.get() + 1 );
334
+ } );
335
+ tooltip.on( 'mouseleave', function() {
336
+ component.tooltipFocused.set( component.tooltipFocused.get() - 1 );
337
+ } );
338
+ tooltipLink.on( 'focus', function() {
339
+ if ( ! api.state( 'ampAvailable' ).get() ) {
340
+ component.tooltipVisible.set( true );
341
+ }
342
+ component.tooltipFocused.set( component.tooltipFocused.get() + 1 );
343
+ } );
344
+ tooltipLink.on( 'blur', function() {
345
+ component.tooltipFocused.set( component.tooltipFocused.get() - 1 );
346
+ } );
347
+ };
348
+
349
+ return component;
350
+
351
+ } )( wp.customize, jQuery );
includes/vendor/amp/assets/js/amp-customize-preview.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* exported ampCustomizePreview */
2
+
3
+ var ampCustomizePreview = ( function( api ) {
4
+ 'use strict';
5
+
6
+ var component = {};
7
+
8
+ /**
9
+ * Boot using data sent inline.
10
+ *
11
+ * @param {Object} data - PHP exports.
12
+ * @param {boolean} data.available - Whether AMP is available.
13
+ * @param {boolean} data.enabled - Whether AMP is enabled.
14
+ * @return {void}
15
+ */
16
+ component.boot = function boot( data ) {
17
+ api.bind( 'preview-ready', function() {
18
+ api.preview.bind( 'active', function() {
19
+ api.preview.send( 'amp-status', data );
20
+ } );
21
+ } );
22
+ };
23
+
24
+ return component;
25
+
26
+ } )( wp.customize );
includes/vendor/amp/assets/js/amp-post-meta-box.js ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* exported ampPostMetaBox */
2
+
3
+ /**
4
+ * AMP Post Meta Box.
5
+ *
6
+ * @todo Rename this to be just the ampEditPostScreen?
7
+ *
8
+ * @since 0.6
9
+ */
10
+ var ampPostMetaBox = ( function( $ ) {
11
+ 'use strict';
12
+
13
+ var component = {
14
+
15
+ /**
16
+ * Holds data.
17
+ *
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.
25
+ statusInputName: '',
26
+ l10n: {
27
+ ampPreviewBtnLabel: ''
28
+ }
29
+ },
30
+
31
+ /**
32
+ * Toggle animation speed.
33
+ *
34
+ * @since 0.6
35
+ */
36
+ toggleSpeed: 200,
37
+
38
+ /**
39
+ * Core preview button selector.
40
+ *
41
+ * @since 0.6
42
+ */
43
+ previewBtnSelector: '#post-preview',
44
+
45
+ /**
46
+ * AMP preview button selector.
47
+ *
48
+ * @since 0.6
49
+ */
50
+ ampPreviewBtnSelector: '#amp-post-preview'
51
+ };
52
+
53
+ /**
54
+ * Boot plugin.
55
+ *
56
+ * @since 0.6
57
+ * @param {Object} data Object data.
58
+ * @return {void}
59
+ */
60
+ component.boot = function boot( data ) {
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();
68
+ } );
69
+ };
70
+
71
+ /**
72
+ * Events listener.
73
+ *
74
+ * @since 0.6
75
+ * @return {void}
76
+ */
77
+ component.listen = function listen() {
78
+ $( component.ampPreviewBtnSelector ).on( 'click.amp-post-preview', function( e ) {
79
+ e.preventDefault();
80
+ component.onAmpPreviewButtonClick();
81
+ } );
82
+
83
+ component.statusRadioInputs.prop( 'disabled', true ); // Prevent cementing setting default status as overridden status.
84
+ $( '.edit-amp-status, [href="#amp_status"]' ).click( function( e ) {
85
+ e.preventDefault();
86
+ component.statusRadioInputs.prop( 'disabled', false );
87
+ component.toggleAmpStatus( $( e.target ) );
88
+ } );
89
+
90
+ $( '#submitpost input[type="submit"]' ).on( 'click', function() {
91
+ $( component.ampPreviewBtnSelector ).addClass( 'disabled' );
92
+ } );
93
+ };
94
+
95
+ /**
96
+ * Add AMP Preview button.
97
+ *
98
+ * @since 0.6
99
+ * @return {void}
100
+ */
101
+ component.addPreviewButton = function addPreviewButton() {
102
+ var previewBtn = $( component.previewBtnSelector );
103
+ previewBtn
104
+ .clone()
105
+ .insertAfter( previewBtn )
106
+ .prop( {
107
+ 'href': component.data.previewLink,
108
+ 'id': component.ampPreviewBtnSelector.replace( '#', '' )
109
+ } )
110
+ .text( component.data.l10n.ampPreviewBtnLabel )
111
+ .parent()
112
+ .addClass( 'has-amp-preview' );
113
+ };
114
+
115
+ /**
116
+ * AMP Preview button click handler.
117
+ *
118
+ * We trigger the Core preview link for events propagation purposes.
119
+ *
120
+ * @since 0.6
121
+ * @return {void}
122
+ */
123
+ component.onAmpPreviewButtonClick = function onAmpPreviewButtonClick() {
124
+ var $input;
125
+
126
+ // Flag the AMP preview referer.
127
+ $input = $( '<input>' )
128
+ .prop( {
129
+ 'type': 'hidden',
130
+ 'name': 'amp-preview',
131
+ 'value': 'do-preview'
132
+ } )
133
+ .insertAfter( component.ampPreviewBtnSelector );
134
+
135
+ // Trigger Core preview button and remove AMP flag.
136
+ $( component.previewBtnSelector ).click();
137
+ $input.remove();
138
+ };
139
+
140
+ /**
141
+ * Add AMP status toggle.
142
+ *
143
+ * @since 0.6
144
+ * @param {Object} $target Event target.
145
+ * @return {void}
146
+ */
147
+ component.toggleAmpStatus = function toggleAmpStatus( $target ) {
148
+ var $container = $( '#amp-status-select' ),
149
+ status = $container.data( 'amp-status' ),
150
+ $checked,
151
+ editAmpStatus = $( '.edit-amp-status' );
152
+
153
+ // Don't modify status on cancel button click.
154
+ if ( ! $target.hasClass( 'button-cancel' ) ) {
155
+ status = component.statusRadioInputs.filter( ':checked' ).val();
156
+ }
157
+
158
+ $checked = $( '#amp-status-' + status );
159
+
160
+ // Toggle elements.
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 );
169
+
170
+ // Update status.
171
+ if ( component.data.canSupport ) {
172
+ $container.data( 'amp-status', status );
173
+ $checked.prop( 'checked', true );
174
+ $( '.amp-status-text' ).text( $checked.next().text() );
175
+ }
176
+ };
177
+
178
+ return component;
179
+ })( window.jQuery );
includes/vendor/amp/includes/admin/class-amp-post-meta-box.php ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AMP meta box settings.
4
+ *
5
+ * @package AMP
6
+ * @since 0.6
7
+ */
8
+
9
+ /**
10
+ * Post meta box class.
11
+ *
12
+ * @since 0.6
13
+ */
14
+ class AMP_Post_Meta_Box {
15
+
16
+ /**
17
+ * Assets handle.
18
+ *
19
+ * @since 0.6
20
+ * @var string
21
+ */
22
+ const ASSETS_HANDLE = 'amp-post-meta-box';
23
+
24
+ /**
25
+ * The enabled status post meta value.
26
+ *
27
+ * @since 0.6
28
+ * @var string
29
+ */
30
+ const ENABLED_STATUS = 'enabled';
31
+
32
+ /**
33
+ * The disabled status post meta value.
34
+ *
35
+ * @since 0.6
36
+ * @var string
37
+ */
38
+ const DISABLED_STATUS = 'disabled';
39
+
40
+ /**
41
+ * The status post meta key.
42
+ *
43
+ * @since 0.6
44
+ * @var string
45
+ */
46
+ const STATUS_POST_META_KEY = 'amp_status';
47
+
48
+ /**
49
+ * The field name for the enabled/disabled radio buttons.
50
+ *
51
+ * @since 0.6
52
+ * @var string
53
+ */
54
+ const STATUS_INPUT_NAME = 'amp_status';
55
+
56
+ /**
57
+ * The nonce name.
58
+ *
59
+ * @since 0.6
60
+ * @var string
61
+ */
62
+ const NONCE_NAME = 'amp-status-nonce';
63
+
64
+ /**
65
+ * The nonce action.
66
+ *
67
+ * @since 0.6
68
+ * @var string
69
+ */
70
+ const NONCE_ACTION = 'amp-update-status';
71
+
72
+ /**
73
+ * Initialize.
74
+ *
75
+ * @since 0.6
76
+ */
77
+ public function init() {
78
+ register_meta( 'post', self::STATUS_POST_META_KEY, array(
79
+ 'sanitize_callback' => array( $this, 'sanitize_status' ),
80
+ 'type' => 'string',
81
+ 'description' => __( 'AMP status.', 'amp' ),
82
+ 'show_in_rest' => true,
83
+ 'single' => true,
84
+ ) );
85
+
86
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
87
+ add_action( 'post_submitbox_misc_actions', array( $this, 'render_status' ) );
88
+ add_action( 'save_post', array( $this, 'save_amp_status' ) );
89
+ add_filter( 'preview_post_link', array( $this, 'preview_post_link' ) );
90
+ }
91
+
92
+ /**
93
+ * Sanitize status.
94
+ *
95
+ * @param string $status Status.
96
+ * @return string Sanitized status. Empty string when invalid.
97
+ */
98
+ public function sanitize_status( $status ) {
99
+ $status = strtolower( trim( $status ) );
100
+ if ( ! in_array( $status, array( 'enabled', 'disabled' ), true ) ) {
101
+ /*
102
+ * In lieu of actual validation being available, clear the status entirely
103
+ * so that the underlying default status will be used instead.
104
+ * In the future it would be ideal if register_meta() accepted a
105
+ * validate_callback as well which the REST API could leverage.
106
+ */
107
+ $status = '';
108
+ }
109
+ return $status;
110
+ }
111
+
112
+ /**
113
+ * Enqueue admin assets.
114
+ *
115
+ * @since 0.6
116
+ */
117
+ public function enqueue_admin_assets() {
118
+ $post = get_post();
119
+ $screen = get_current_screen();
120
+ $validate = (
121
+ isset( $screen->base )
122
+ &&
123
+ 'post' === $screen->base
124
+ &&
125
+ is_post_type_viewable( $post->post_type )
126
+ );
127
+ if ( ! $validate ) {
128
+ return;
129
+ }
130
+
131
+ // Styles.
132
+ wp_enqueue_style(
133
+ self::ASSETS_HANDLE,
134
+ amp_get_asset_url( 'css/amp-post-meta-box.css' ),
135
+ false,
136
+ AMP__VERSION
137
+ );
138
+
139
+ // Scripts.
140
+ wp_enqueue_script(
141
+ self::ASSETS_HANDLE,
142
+ amp_get_asset_url( 'js/amp-post-meta-box.js' ),
143
+ array( 'jquery' ),
144
+ AMP__VERSION
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,
153
+ 'l10n' => array(
154
+ 'ampPreviewBtnLabel' => __( 'Preview changes in AMP (opens in new window)', 'amp' ),
155
+ ),
156
+ ) )
157
+ ) );
158
+ }
159
+
160
+ /**
161
+ * Render AMP status.
162
+ *
163
+ * @since 0.6
164
+ * @param WP_Post $post Post.
165
+ */
166
+ public function render_status( $post ) {
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 ) {
178
+ return;
179
+ }
180
+
181
+ $errors = AMP_Post_Type_Support::get_support_errors( $post );
182
+ $status = post_supports_amp( $post ) ? self::ENABLED_STATUS : self::DISABLED_STATUS;
183
+ $labels = array(
184
+ 'enabled' => __( 'Enabled', 'amp' ),
185
+ 'disabled' => __( 'Disabled', 'amp' ),
186
+ );
187
+
188
+ // The preceding variables are used inside the following amp-status.php template.
189
+ include_once AMP__DIR__ . '/templates/admin/amp-status.php';
190
+ }
191
+
192
+ /**
193
+ * Save AMP Status.
194
+ *
195
+ * @since 0.6
196
+ * @param int $post_id The Post ID.
197
+ */
198
+ public function save_amp_status( $post_id ) {
199
+ $verify = (
200
+ isset( $_POST[ self::NONCE_NAME ] )
201
+ &&
202
+ isset( $_POST[ self::STATUS_INPUT_NAME ] )
203
+ &&
204
+ wp_verify_nonce( sanitize_key( wp_unslash( $_POST[ self::NONCE_NAME ] ) ), self::NONCE_ACTION )
205
+ &&
206
+ current_user_can( 'edit_post', $post_id )
207
+ &&
208
+ ! wp_is_post_revision( $post_id )
209
+ &&
210
+ ! wp_is_post_autosave( $post_id )
211
+ );
212
+
213
+ if ( true === $verify ) {
214
+ update_post_meta(
215
+ $post_id,
216
+ self::STATUS_POST_META_KEY,
217
+ $_POST[ self::STATUS_INPUT_NAME ] // Note: The sanitize_callback has been supplied in the register_meta() call above.
218
+ );
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Modify post preview link.
224
+ *
225
+ * Add the AMP query var is the amp-preview flag is set.
226
+ *
227
+ * @since 0.6
228
+ *
229
+ * @param string $link The post preview link.
230
+ * @return string Preview URL.
231
+ */
232
+ public function preview_post_link( $link ) {
233
+ $is_amp = (
234
+ isset( $_POST['amp-preview'] ) // WPCS: CSRF ok.
235
+ &&
236
+ 'do-preview' === sanitize_key( wp_unslash( $_POST['amp-preview'] ) ) // WPCS: CSRF ok.
237
+ );
238
+
239
+ if ( $is_amp ) {
240
+ $link = add_query_arg( amp_get_slug(), true, $link );
241
+ }
242
+
243
+ return $link;
244
+ }
245
+
246
+ }
includes/vendor/amp/includes/class-amp-autoloader.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Autoloader
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Autoload the classes used by the AMP plugin.
10
+ *
11
+ * Class AMP_Autoloader
12
+ */
13
+ class AMP_Autoloader {
14
+
15
+ /**
16
+ * Map of Classname to relative filepath sans extension.
17
+ *
18
+ * @note We omitted the leading slash and the .php extension from each
19
+ * relative filepath because they are redundant and to include
20
+ * them would take up unnecessary bytes of memory at runtime.
21
+ *
22
+ * @example Format (note no leading / and no .php extension):
23
+ *
24
+ * array(
25
+ * 'Class_Name1' => 'subdir-of-includes/filename1',
26
+ * 'Class_Name2' => '2nd-subdir-of-includes/filename2',
27
+ * );
28
+ *
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',
37
+ 'AMP_Base_Embed_Handler' => 'includes/embeds/class-amp-base-embed-handler',
38
+ 'AMP_DailyMotion_Embed_Handler' => 'includes/embeds/class-amp-dailymotion-embed',
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',
52
+ 'AMP_YouTube_Embed_Handler' => 'includes/embeds/class-amp-youtube-embed',
53
+ 'FastImage' => 'includes/lib/fastimage/class-fastimage',
54
+ 'WillWashburn\Stream\Exception\StreamBufferTooSmallException' => 'includes/lib/fasterimage/Stream/Exception/StreamBufferTooSmallException',
55
+ 'WillWashburn\Stream\StreamableInterface' => 'includes/lib/fasterimage/Stream/StreamableInterface',
56
+ 'WillWashburn\Stream\Stream' => 'includes/lib/fasterimage/Stream/Stream',
57
+ 'FasterImage\Exception\InvalidImageException' => 'includes/lib/fasterimage/Exception/InvalidImageException',
58
+ 'FasterImage\ExifParser' => 'includes/lib/fasterimage/ExifParser',
59
+ 'FasterImage\ImageParser' => 'includes/lib/fasterimage/ImageParser',
60
+ 'FasterImage\FasterImage' => 'includes/lib/fasterimage/FasterImage',
61
+ 'AMP_Analytics_Options_Submenu' => 'includes/options/class-amp-analytics-options-submenu',
62
+ 'AMP_Options_Menu' => 'includes/options/class-amp-options-menu',
63
+ 'AMP_Options_Manager' => 'includes/options/class-amp-options-manager',
64
+ 'AMP_Analytics_Options_Submenu_Page' => 'includes/options/views/class-amp-analytics-options-submenu-page',
65
+ 'AMP_Options_Menu_Page' => 'includes/options/views/class-amp-options-menu-page',
66
+ 'AMP_Rule_Spec' => 'includes/sanitizers/class-amp-rule-spec',
67
+ 'AMP_Allowed_Tags_Generated' => 'includes/sanitizers/class-amp-allowed-tags-generated',
68
+ 'AMP_Audio_Sanitizer' => 'includes/sanitizers/class-amp-audio-sanitizer',
69
+ 'AMP_Base_Sanitizer' => 'includes/sanitizers/class-amp-base-sanitizer',
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',
78
+ 'AMP_Video_Sanitizer' => 'includes/sanitizers/class-amp-video-sanitizer',
79
+ 'AMP_Customizer_Design_Settings' => 'includes/settings/class-amp-customizer-design-settings',
80
+ 'AMP_Customizer_Settings' => 'includes/settings/class-amp-customizer-settings',
81
+ 'AMP_Content' => 'includes/templates/class-amp-content',
82
+ 'AMP_Content_Sanitizer' => 'includes/templates/class-amp-content-sanitizer',
83
+ 'AMP_Post_Template' => 'includes/templates/class-amp-post-template',
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',
98
+ );
99
+
100
+ /**
101
+ * Is registered.
102
+ *
103
+ * @var bool
104
+ */
105
+ public static $is_registered = false;
106
+
107
+ /**
108
+ * Perform the autoload on demand when requested by PHP runtime.
109
+ *
110
+ * Design Goal: Execute as few lines of code as possible each call.
111
+ *
112
+ * @since 0.6
113
+ *
114
+ * @param string $class_name Class name.
115
+ */
116
+ protected static function autoload( $class_name ) {
117
+ if ( ! isset( self::$_classmap[ $class_name ] ) ) {
118
+ return;
119
+ }
120
+ $is_load = apply_filters('ampforwp_update_autoload_class', self::$_classmap, $class_name);
121
+ if($is_load){
122
+ $filepath = self::$_classmap[ $class_name ];
123
+ require AMP__DIR__ . "/{$filepath}.php";
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Registers this autoloader to PHP.
129
+ *
130
+ * @since 0.6
131
+ *
132
+ * Called at the end of this file; calling a second time has no effect.
133
+ */
134
+ public static function register() {
135
+ if ( ! self::$is_registered ) {
136
+ spl_autoload_register( array( __CLASS__, 'autoload' ) );
137
+ self::$is_registered = true;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Allows an extensions plugin to register a class and its file for autoloading
143
+ *
144
+ * @since 0.6
145
+ *
146
+ * @param string $class_name Full classname (include namespace if applicable).
147
+ * @param string $filepath Absolute filepath to class file, including .php extension.
148
+ */
149
+ public static function register_autoload_class( $class_name, $filepath ) {
150
+ self::$_classmap[ $class_name ] = '!' . $filepath;
151
+ }
152
+ }
includes/vendor/amp/includes/class-amp-comment-walker.php ADDED
@@ -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
+ }
includes/vendor/amp/includes/class-amp-post-type-support.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AMP Post type support.
4
+ *
5
+ * @package AMP
6
+ * @since 0.6
7
+ */
8
+
9
+ /**
10
+ * Class AMP_Post_Type_Support.
11
+ */
12
+ class AMP_Post_Type_Support {
13
+
14
+ /**
15
+ * Get post types that plugin supports out of the box (which cannot be disabled).
16
+ *
17
+ * @return string[] Post types.
18
+ */
19
+ public static function get_builtin_supported_post_types() {
20
+ return array_filter( array( 'post' ), 'post_type_exists' );
21
+ }
22
+
23
+ /**
24
+ * Get post types that are eligible for AMP support.
25
+ *
26
+ * @since 0.6
27
+ * @return string[] Post types eligible for AMP.
28
+ */
29
+ public static function get_eligible_post_types() {
30
+ return array_merge(
31
+ self::get_builtin_supported_post_types(),
32
+ array( 'page' ),
33
+ array_values( get_post_types(
34
+ array(
35
+ 'public' => true,
36
+ '_builtin' => false,
37
+ ),
38
+ 'names'
39
+ ) )
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Declare support for post types.
45
+ *
46
+ * This function should only be invoked through the 'after_setup_theme' action to
47
+ * allow plugins/theme to overwrite the post types support.
48
+ *
49
+ * @since 0.6
50
+ */
51
+ public static function add_post_type_support() {
52
+ $post_types = array_merge(
53
+ self::get_builtin_supported_post_types(),
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
+
61
+ /**
62
+ * Return error codes for why a given post does not have AMP support.
63
+ *
64
+ * @since 0.6
65
+ *
66
+ * @param WP_Post|int $post Post.
67
+ * @return array Error codes for why a given post does not have AMP support.
68
+ */
69
+ public static function get_support_errors( $post ) {
70
+ if ( ! ( $post instanceof WP_Post ) ) {
71
+ $post = get_post( $post );
72
+ }
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
+
80
+ if ( post_password_required( $post ) ) {
81
+ $errors[] = 'password-protected';
82
+ }
83
+
84
+ /**
85
+ * Filters whether to skip the post from AMP.
86
+ *
87
+ * @since 0.3
88
+ *
89
+ * @param bool $skipped Skipped.
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
+
97
+ return $errors;
98
+ }
99
+ }
includes/vendor/amp/includes/class-amp-theme-support.php ADDED
@@ -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
+ }
includes/vendor/amp/includes/embeds/class-amp-issuu-embed-handler.php ADDED
@@ -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
+
includes/vendor/amp/includes/embeds/class-amp-meetup-embed-handler.php ADDED
@@ -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
+
includes/vendor/amp/includes/embeds/class-amp-playlist-embed-handler.php ADDED
@@ -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
+ }
includes/vendor/amp/includes/embeds/class-amp-reddit-embed-handler.php ADDED
@@ -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
+
includes/vendor/amp/includes/embeds/class-amp-tumblr-embed-handler.php ADDED
@@ -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
+
includes/vendor/amp/includes/options/class-amp-analytics-options-submenu.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Analytics_Options_Submenu
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Class AMP_Analytics_Options_Submenu
10
+ */
11
+ class AMP_Analytics_Options_Submenu {
12
+
13
+ private $parent_menu_slug;
14
+ private $menu_slug;
15
+ private $menu_page;
16
+
17
+ public function __construct( $parent_menu_slug ) {
18
+ $this->parent_menu_slug = $parent_menu_slug;
19
+ $this->menu_slug = 'amp-analytics-options';
20
+ $this->menu_page = new AMP_Analytics_Options_Submenu_Page();
21
+ }
22
+
23
+ public function init() {
24
+ $this->add_submenu();
25
+ add_action(
26
+ 'admin_print_styles-amp_page_' . $this->menu_slug,
27
+ array( $this, 'amp_options_styles' )
28
+ );
29
+ }
30
+
31
+ private function add_submenu() {
32
+ add_submenu_page(
33
+ $this->parent_menu_slug,
34
+ __( 'AMP Analytics Options', 'amp' ),
35
+ __( 'Analytics', 'amp' ),
36
+ 'manage_options',
37
+ $this->menu_slug,
38
+ array( $this->menu_page, 'render' )
39
+ );
40
+ }
41
+
42
+ public function amp_options_styles() {
43
+ ?>
44
+ <style>
45
+ .analytics-data-container .button.delete,
46
+ .analytics-data-container .button.delete:hover,
47
+ .analytics-data-container .button.delete:active,
48
+ .analytics-data-container .button.delete:focus {
49
+ background: red;
50
+ border-color: red;
51
+ text-shadow: 0 0 0;
52
+ margin: 0 5px;
53
+ }
54
+ .amp-analytics-options.notice {
55
+ width: 300px;
56
+ }
57
+ </style>;
58
+
59
+ <?php
60
+ }
61
+ }
includes/vendor/amp/includes/options/class-amp-options-manager.php ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Options_Manager.
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Class AMP_Options_Manager
10
+ */
11
+ class AMP_Options_Manager {
12
+
13
+ /**
14
+ * Option name.
15
+ *
16
+ * @var string
17
+ */
18
+ const OPTION_NAME = 'amp-options';
19
+
20
+ /**
21
+ * Register settings.
22
+ */
23
+ public static function register_settings() {
24
+ register_setting(
25
+ self::OPTION_NAME,
26
+ self::OPTION_NAME,
27
+ array(
28
+ 'type' => 'array',
29
+ 'sanitize_callback' => array( __CLASS__, 'validate_options' ),
30
+ )
31
+ );
32
+
33
+ add_action( 'update_option_' . self::OPTION_NAME, array( __CLASS__, 'maybe_flush_rewrite_rules' ), 10, 2 );
34
+ }
35
+
36
+ /**
37
+ * Flush rewrite rules if the supported_post_types have changed.
38
+ *
39
+ * @since 0.6.2
40
+ *
41
+ * @param array $old_options Old options.
42
+ * @param array $new_options New options.
43
+ */
44
+ public static function maybe_flush_rewrite_rules( $old_options, $new_options ) {
45
+ $old_post_types = isset( $old_options['supported_post_types'] ) ? $old_options['supported_post_types'] : array();
46
+ $new_post_types = isset( $new_options['supported_post_types'] ) ? $new_options['supported_post_types'] : array();
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
+
54
+ /**
55
+ * Get plugin options.
56
+ *
57
+ * @return array Options.
58
+ */
59
+ public static function get_options() {
60
+ return get_option( self::OPTION_NAME, array() );
61
+ }
62
+
63
+ /**
64
+ * Get plugin option.
65
+ *
66
+ * @param string $option Plugin option name.
67
+ * @param bool $default Default value.
68
+ *
69
+ * @return mixed Option value.
70
+ */
71
+ public static function get_option( $option, $default = false ) {
72
+ $amp_options = self::get_options();
73
+
74
+ if ( ! isset( $amp_options[ $option ] ) ) {
75
+ return $default;
76
+ }
77
+
78
+ return $amp_options[ $option ];
79
+ }
80
+
81
+ /**
82
+ * Validate options.
83
+ *
84
+ * @param array $new_options Plugin options.
85
+ * @return array Options.
86
+ */
87
+ public static function validate_options( $new_options ) {
88
+ $defaults = array(
89
+ 'supported_post_types' => array(),
90
+ 'analytics' => array(),
91
+ );
92
+
93
+ $options = array_merge(
94
+ $defaults,
95
+ self::get_options()
96
+ );
97
+
98
+ // Validate post type support.
99
+ if ( isset( $new_options['supported_post_types'] ) ) {
100
+ $options['supported_post_types'] = array();
101
+ foreach ( $new_options['supported_post_types'] as $post_type ) {
102
+ if ( ! post_type_exists( $post_type ) ) {
103
+ add_settings_error( self::OPTION_NAME, 'unknown_post_type', __( 'Unrecognized post type.', 'amp' ) );
104
+ } else {
105
+ $options['supported_post_types'][] = $post_type;
106
+ }
107
+ }
108
+ }
109
+
110
+ // Validate analytics.
111
+ if ( isset( $new_options['analytics'] ) ) {
112
+ foreach ( $new_options['analytics'] as $id => $data ) {
113
+
114
+ // Check save/delete pre-conditions and proceed if correct.
115
+ if ( empty( $data['type'] ) || empty( $data['config'] ) ) {
116
+ add_settings_error( self::OPTION_NAME, 'missing_analytics_vendor_or_config', __( 'Missing vendor type or config.', 'amp' ) );
117
+ continue;
118
+ }
119
+
120
+ // Validate JSON configuration.
121
+ $is_valid_json = AMP_HTML_Utils::is_valid_json( $data['config'] );
122
+ if ( ! $is_valid_json ) {
123
+ add_settings_error( self::OPTION_NAME, 'invalid_analytics_config_json', __( 'Invalid analytics config JSON.', 'amp' ) );
124
+ continue;
125
+ }
126
+
127
+ $entry_vendor_type = sanitize_key( $data['type'] );
128
+ $entry_config = trim( $data['config'] );
129
+
130
+ if ( ! empty( $data['id'] ) && '__new__' !== $data['id'] ) {
131
+ $entry_id = sanitize_key( $data['id'] );
132
+ } else {
133
+
134
+ // Generate a hash string to uniquely identify this entry.
135
+ $entry_id = substr( md5( $entry_vendor_type . $entry_config ), 0, 12 );
136
+
137
+ // Avoid duplicates.
138
+ if ( isset( $options['analytics'][ $entry_id ] ) ) {
139
+ add_settings_error( self::OPTION_NAME, 'duplicate_analytics_entry', __( 'Duplicate analytics entry found.', 'amp' ) );
140
+ continue;
141
+ }
142
+ }
143
+
144
+ if ( isset( $data['delete'] ) ) {
145
+ unset( $options['analytics'][ $entry_id ] );
146
+ } else {
147
+ $options['analytics'][ $entry_id ] = array(
148
+ 'type' => $entry_vendor_type,
149
+ 'config' => $entry_config,
150
+ );
151
+ }
152
+ }
153
+ }
154
+
155
+ return $options;
156
+ }
157
+
158
+
159
+ /**
160
+ * Check for errors with updating the supported post types.
161
+ *
162
+ * @since 0.6
163
+ * @see add_settings_error()
164
+ */
165
+ public static function check_supported_post_type_update_errors() {
166
+ $builtin_support = AMP_Post_Type_Support::get_builtin_supported_post_types();
167
+ $supported_types = self::get_option( 'supported_post_types', array() );
168
+ foreach ( AMP_Post_Type_Support::get_eligible_post_types() as $name ) {
169
+ $post_type = get_post_type_object( $name );
170
+ if ( empty( $post_type ) || in_array( $post_type->name, $builtin_support, true ) ) {
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;
178
+ $code = null;
179
+ if ( $is_support_elected && ! $post_type_supported ) {
180
+ /* translators: %s: Post type name. */
181
+ $error = __( '"%s" could not be activated because support is removed by a plugin or theme', 'amp' );
182
+ $code = sprintf( '%s_activation_error', $post_type->name );
183
+ } elseif ( ! $is_support_elected && $post_type_supported ) {
184
+ /* translators: %s: Post type name. */
185
+ $error = __( '"%s" could not be deactivated because support is added by a plugin or theme', 'amp' );
186
+ $code = sprintf( '%s_deactivation_error', $post_type->name );
187
+ }
188
+
189
+ if ( isset( $error, $code ) ) {
190
+ add_settings_error(
191
+ self::OPTION_NAME,
192
+ $code,
193
+ sprintf(
194
+ $error,
195
+ isset( $post_type->label ) ? $post_type->label : $post_type->name
196
+ )
197
+ );
198
+ }
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Update plugin option.
204
+ *
205
+ * @param string $option Plugin option name.
206
+ * @param mixed $value Plugin option value.
207
+ *
208
+ * @return bool Whether update succeeded.
209
+ */
210
+ public static function update_option( $option, $value ) {
211
+ $amp_options = self::get_options();
212
+
213
+ $amp_options[ $option ] = $value;
214
+
215
+ return update_option( self::OPTION_NAME, $amp_options, false );
216
+ }
217
+
218
+ /**
219
+ * Handle analytics submission.
220
+ */
221
+ public static function handle_analytics_submit() {
222
+
223
+ // Request must come from user with right capabilities.
224
+ if ( ! current_user_can( 'manage_options' ) ) {
225
+ wp_die( esc_html__( 'Sorry, you do not have the necessary permissions to perform this action', 'amp' ) );
226
+ }
227
+
228
+ // Ensure request is coming from analytics option form.
229
+ check_admin_referer( 'analytics-options', 'analytics-options' );
230
+
231
+ if ( isset( $_POST['amp-options']['analytics'] ) ) {
232
+ self::update_option( 'analytics', wp_unslash( $_POST['amp-options']['analytics'] ) );
233
+
234
+ $errors = get_settings_errors( self::OPTION_NAME );
235
+ if ( empty( $errors ) ) {
236
+ add_settings_error( self::OPTION_NAME, 'settings_updated', __( 'The analytics entry was successfully saved!', 'amp' ), 'updated' );
237
+ $errors = get_settings_errors( self::OPTION_NAME );
238
+ }
239
+ set_transient( 'settings_errors', $errors );
240
+ }
241
+
242
+ /*
243
+ * Redirect to keep the user in the analytics options page.
244
+ * Wrap in is_admin() to enable phpunit tests to exercise this code.
245
+ */
246
+ wp_safe_redirect( admin_url( 'admin.php?page=amp-analytics-options&settings-updated=1' ) );
247
+ exit;
248
+ }
249
+
250
+ /**
251
+ * Update analytics options.
252
+ *
253
+ * @codeCoverageIgnore
254
+ * @deprecated
255
+ * @param array $data Unsanitized unslashed data.
256
+ * @return bool Whether options were updated.
257
+ */
258
+ public static function update_analytics_options( $data ) {
259
+ _deprecated_function( __METHOD__, '0.6', __CLASS__ . '::update_option' );
260
+ return self::update_option( 'analytics', wp_unslash( $data ) );
261
+ }
262
+ }
includes/vendor/amp/includes/options/class-amp-options-menu.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AMP Options.
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * AMP_Options_Menu class.
10
+ */
11
+ class AMP_Options_Menu {
12
+
13
+ /**
14
+ * The AMP svg menu icon.
15
+ *
16
+ * @var string
17
+ */
18
+ const ICON_BASE64_SVG = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iNjJweCIgaGVpZ2h0PSI2MnB4IiB2aWV3Qm94PSIwIDAgNjIgNjIiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+ICAgICAgICA8dGl0bGU+QU1QLUJyYW5kLUJsYWNrLUljb248L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGRlZnM+PC9kZWZzPiAgICA8ZyBpZD0iYW1wLWxvZ28taW50ZXJuYWwtc2l0ZSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+ICAgICAgICA8ZyBpZD0iQU1QLUJyYW5kLUJsYWNrLUljb24iIGZpbGw9IiMwMDAwMDAiPiAgICAgICAgICAgIDxwYXRoIGQ9Ik00MS42Mjg4NjY3LDI4LjE2MTQzMzMgTDI4LjYyNDM2NjcsNDkuODAzNTY2NyBMMjYuMjY4MzY2Nyw0OS44MDM1NjY3IEwyOC41OTc1LDM1LjcwMTY2NjcgTDIxLjM4MzgsMzUuNzEwOTY2NyBDMjEuMzgzOCwzNS43MTA5NjY3IDIxLjMxNTYsMzUuNzEzMDMzMyAyMS4yODM1NjY3LDM1LjcxMzAzMzMgQzIwLjYzMzYsMzUuNzEzMDMzMyAyMC4xMDc2MzMzLDM1LjE4NzA2NjcgMjAuMTA3NjMzMywzNC41MzcxIEMyMC4xMDc2MzMzLDM0LjI1ODEgMjAuMzY3LDMzLjc4NTg2NjcgMjAuMzY3LDMzLjc4NTg2NjcgTDMzLjMyOTEzMzMsMTIuMTY5NTY2NyBMMzUuNzI0NCwxMi4xNzk5IEwzMy4zMzYzNjY3LDI2LjMwMzUgTDQwLjU4NzI2NjcsMjYuMjk0MiBDNDAuNTg3MjY2NywyNi4yOTQyIDQwLjY2NDc2NjcsMjYuMjkzMTY2NyA0MC43MDE5NjY3LDI2LjI5MzE2NjcgQzQxLjM1MTkzMzMsMjYuMjkzMTY2NyA0MS44Nzc5LDI2LjgxOTEzMzMgNDEuODc3OSwyNy40NjkxIEM0MS44Nzc5LDI3LjczMjYgNDEuNzc0NTY2NywyNy45NjQwNjY3IDQxLjYyNzgzMzMsMjguMTYwNCBMNDEuNjI4ODY2NywyOC4xNjE0MzMzIFogTTMxLDAgQzEzLjg3ODcsMCAwLDEzLjg3OTczMzMgMCwzMSBDMCw0OC4xMjEzIDEzLjg3ODcsNjIgMzEsNjIgQzQ4LjEyMDI2NjcsNjIgNjIsNDguMTIxMyA2MiwzMSBDNjIsMTMuODc5NzMzMyA0OC4xMjAyNjY3LDAgMzEsMCBMMzEsMCBaIiBpZD0iRmlsbC0xIj48L3BhdGg+ICAgICAgICA8L2c+ICAgIDwvZz48L3N2Zz4=';
19
+
20
+ /**
21
+ * Initialize.
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
+ /**
29
+ * Add menu.
30
+ */
31
+ public function add_menu_items() {
32
+
33
+ add_menu_page(
34
+ __( 'AMP Options', 'amp' ),
35
+ __( 'AMP', 'amp' ),
36
+ 'manage_options',
37
+ AMP_Options_Manager::OPTION_NAME,
38
+ array( $this, 'render_screen' ),
39
+ self::ICON_BASE64_SVG
40
+ );
41
+
42
+ add_submenu_page(
43
+ AMP_Options_Manager::OPTION_NAME,
44
+ __( 'AMP Settings', 'amp' ),
45
+ __( 'General', 'amp' ),
46
+ 'manage_options',
47
+ AMP_Options_Manager::OPTION_NAME
48
+ );
49
+
50
+ add_settings_section(
51
+ 'post_types',
52
+ false,
53
+ '__return_false',
54
+ AMP_Options_Manager::OPTION_NAME
55
+ );
56
+ add_settings_field(
57
+ 'supported_post_types',
58
+ __( 'Post Type Support', 'amp' ),
59
+ array( $this, 'render_post_types_support' ),
60
+ AMP_Options_Manager::OPTION_NAME,
61
+ 'post_types'
62
+ );
63
+
64
+ $submenus = array(
65
+ new AMP_Analytics_Options_Submenu( AMP_Options_Manager::OPTION_NAME ),
66
+ );
67
+
68
+ // Create submenu items and calls on the Submenu Page object to render the actual contents of the page.
69
+ foreach ( $submenus as $submenu ) {
70
+ $submenu->init();
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Post types support section renderer.
76
+ *
77
+ * @since 0.6
78
+ */
79
+ public function render_post_types_support() {
80
+ $builtin_support = AMP_Post_Type_Support::get_builtin_supported_post_types();
81
+ $element_name = AMP_Options_Manager::OPTION_NAME . '[supported_post_types][]';
82
+ ?>
83
+ <fieldset>
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 ); ?>">
91
+ <?php endif; ?>
92
+ <input
93
+ type="checkbox"
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 ); ?>">
101
+ <?php echo esc_html( $post_type->label ); ?>
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
+ }
117
+
118
+ /**
119
+ * Display Settings.
120
+ *
121
+ * @since 0.6
122
+ */
123
+ public function render_screen() {
124
+ if ( ! empty( $_GET['settings-updated'] ) ) { // WPCS: CSRF ok.
125
+ AMP_Options_Manager::check_supported_post_type_update_errors();
126
+ }
127
+ ?>
128
+ <div class="wrap">
129
+ <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
130
+ <?php settings_errors(); ?>
131
+ <form action="options.php" method="post">
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>
141
+ <?php
142
+ }
143
+ }
includes/vendor/amp/includes/options/views/class-amp-analytics-options-submenu-page.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Analytics_Options_Submenu_Page
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Class AMP_Analytics_Options_Submenu_Page
10
+ */
11
+ class AMP_Analytics_Options_Submenu_Page {
12
+
13
+ /**
14
+ * Render entry.
15
+ *
16
+ * @param string $id Entry ID.
17
+ * @param string $type Entry type.
18
+ * @param string $config Entry config as serialized JSON.
19
+ */
20
+ private function render_entry( $id = '', $type = '', $config = '' ) {
21
+ $is_existing_entry = ! empty( $id );
22
+
23
+ if ( $is_existing_entry ) {
24
+ $entry_slug = sprintf( '%s%s', ( $type ? $type . '-' : '' ), substr( $id, - 6 ) );
25
+ /* translators: %s is the entry slug */
26
+ $analytics_title = sprintf( __( 'Analytics: %s', 'amp' ), $entry_slug );
27
+ } else {
28
+ $analytics_title = __( 'Add new entry:', 'amp' );
29
+ $id = '__new__';
30
+ }
31
+
32
+ $id_base = sprintf( '%s[analytics][%s]', AMP_Options_Manager::OPTION_NAME, $id );
33
+ ?>
34
+ <div class="analytics-data-container">
35
+ <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
36
+ <h2>
37
+ <?php echo esc_html( $analytics_title ); ?>
38
+ </h2>
39
+ <div class="options">
40
+ <p>
41
+ <label>
42
+ <?php esc_html_e( 'Type:', 'amp' ); ?>
43
+ <input class="option-input" type="text" name="<?php echo esc_attr( $id_base . '[type]' ); ?>" value="<?php echo esc_attr( $type ); ?>" />
44
+ </label>
45
+ <label>
46
+ <?php esc_html_e( 'ID:', 'amp' ); ?>
47
+ <input type="text" value="<?php echo esc_attr( $is_existing_entry ? $id : '' ); ?>" readonly />
48
+ </label>
49
+ <input type="hidden" name="<?php echo esc_attr( $id_base . '[id]' ); ?>" value="<?php echo esc_attr( $id ); ?>" />
50
+ </p>
51
+ <p>
52
+ <label>
53
+ <?php esc_html_e( 'JSON Configuration:', 'amp' ); ?>
54
+ <br />
55
+ <textarea rows="10" cols="100" name="<?php echo esc_attr( $id_base . '[config]' ); ?>"><?php echo esc_textarea( $config ); ?></textarea>
56
+ </label>
57
+ </p>
58
+ <input type="hidden" name="action" value="amp_analytics_options">
59
+ </div>
60
+ <p>
61
+ <?php
62
+ wp_nonce_field( 'analytics-options', 'analytics-options' );
63
+ submit_button( esc_html__( 'Save', 'amp' ), 'primary', 'save', false );
64
+ if ( $is_existing_entry ) {
65
+ submit_button( esc_html__( 'Delete', 'amp' ), 'delete button-primary', esc_attr( $id_base . '[delete]' ), false );
66
+ }
67
+ ?>
68
+ </p>
69
+ </form>
70
+ </div><!-- #analytics-data-container -->
71
+ <?php
72
+ }
73
+
74
+ /**
75
+ * Render title.
76
+ */
77
+ public function render_title() {
78
+ ?>
79
+ <div class="wrap">
80
+ <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
81
+ <?php settings_errors(); ?>
82
+ </div><!-- .wrap -->
83
+ <?php
84
+ }
85
+
86
+ /**
87
+ * Render.
88
+ */
89
+ public function render() {
90
+ $analytics_entries = AMP_Options_Manager::get_option( 'analytics', array() );
91
+
92
+ $this->render_title();
93
+
94
+ // Render entries stored in the DB.
95
+ foreach ( $analytics_entries as $entry_id => $entry ) {
96
+ $this->render_entry( $entry_id, $entry['type'], $entry['config'] );
97
+ }
98
+
99
+ // Empty form for adding more entries.
100
+ $this->render_entry();
101
+ }
102
+ }
includes/vendor/amp/includes/sanitizers/class-amp-allowed-tags-generated.php ADDED
@@ -0,0 +1,10776 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Generated by amphtml-update.py - do not edit.
4
+ *
5
+ * This is a list of HTML tags and attributes that are allowed by the
6
+ * AMP specification. Note that tag names have been converted to lowercase.
7
+ *
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(
21
+ array(
22
+ 'attr_spec_list' => array(
23
+ '[href]' => array(),
24
+ 'border' => 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
+ ),
61
+ 'role' => array(),
62
+ 'tabindex' => array(),
63
+ 'target' => array(
64
+ 'value_regex' => '(_blank|_self|_top)',
65
+ ),
66
+ 'type' => array(
67
+ 'value_casei' => 'text/html',
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(
94
+ array(
95
+ 'attr_spec_list' => array(
96
+ 'autoplay' => array(
97
+ 'value' => '',
98
+ ),
99
+ 'data-id' => array(
100
+ 'mandatory' => true,
101
+ ),
102
+ 'media' => array(),
103
+ 'noloading' => array(
104
+ 'value' => '',
105
+ ),
106
+ ),
107
+ 'tag_spec' => array(
108
+ 'requires_extension' => array(
109
+ 'amp-3q-player',
110
+ ),
111
+ ),
112
+ ),
113
+ ),
114
+ 'amp-accordion' => array(
115
+ array(
116
+ 'attr_spec_list' => 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(
133
+ array(
134
+ 'attr_spec_list' => array(
135
+ 'alt' => array(),
136
+ 'json' => array(),
137
+ 'media' => 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(
193
+ 'mandatory' => true,
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(
258
+ 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(
281
+ array(
282
+ 'attr_spec_list' => array(
283
+ 'alt' => array(),
284
+ 'attribution' => array(),
285
+ 'controls' => array(),
286
+ 'media' => array(),
287
+ 'noloading' => array(
288
+ 'value' => '',
289
+ ),
290
+ 'src' => array(
291
+ 'alternative_names' => array(
292
+ 'srcset',
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(
315
+ array(
316
+ 'attr_spec_list' => array(
317
+ 'media' => array(),
318
+ 'noloading' => array(
319
+ 'value' => '',
320
+ ),
321
+ 'trigger' => array(
322
+ 'value' => 'visibility',
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(
343
+ 'value' => '',
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(
355
+ array(
356
+ 'attr_spec_list' => array(
357
+ 'id' => array(
358
+ 'mandatory' => true,
359
+ ),
360
+ 'media' => array(),
361
+ 'noloading' => array(
362
+ 'value' => '',
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(
376
+ array(
377
+ 'attr_spec_list' => array(
378
+ 'album' => array(),
379
+ 'artist' => array(),
380
+ 'artwork' => array(),
381
+ 'autoplay' => array(
382
+ 'value' => '',
383
+ ),
384
+ 'controls' => array(),
385
+ 'controlslist' => array(),
386
+ 'loop' => array(
387
+ 'value' => '',
388
+ ),
389
+ 'media' => array(),
390
+ 'muted' => array(
391
+ 'value' => '',
392
+ ),
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(
421
+ 'album' => array(),
422
+ 'artist' => array(),
423
+ 'artwork' => array(),
424
+ 'autoplay' => array(
425
+ 'mandatory' => true,
426
+ 'value' => '',
427
+ ),
428
+ 'controls' => array(),
429
+ 'controlslist' => array(),
430
+ 'loop' => array(
431
+ 'value' => '',
432
+ ),
433
+ 'media' => array(),
434
+ 'muted' => array(
435
+ 'value' => '',
436
+ ),
437
+ 'noloading' => array(
438
+ 'value' => '',
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(
553
+ array(
554
+ 'attr_spec_list' => array(
555
+ 'autoplay' => array(),
556
+ 'data-outstream' => array(
557
+ 'value_regex' => '[0-9]+',
558
+ ),
559
+ 'data-partner' => array(
560
+ 'mandatory' => true,
561
+ 'value_regex' => '[0-9]+',
562
+ ),
563
+ 'data-player' => array(
564
+ 'mandatory' => true,
565
+ 'value_regex' => '[0-9]+',
566
+ ),
567
+ 'data-playlist' => array(
568
+ 'value_regex' => '[0-9]+',
569
+ ),
570
+ 'data-video' => array(
571
+ 'value_regex' => '[0-9]+',
572
+ ),
573
+ 'media' => array(),
574
+ 'noloading' => array(
575
+ 'value' => '',
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(
587
+ array(
588
+ 'attr_spec_list' => array(
589
+ '[data-account]' => array(),
590
+ '[data-embed]' => array(),
591
+ '[data-player-id]' => array(),
592
+ '[data-player]' => array(),
593
+ '[data-playlist-id]' => array(),
594
+ '[data-video-id]' => array(),
595
+ 'data-account' => array(
596
+ 'mandatory' => true,
597
+ ),
598
+ 'media' => array(),
599
+ 'noloading' => array(
600
+ 'value' => '',
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(
633
+ array(
634
+ 'attr_spec_list' => 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(),
646
+ 'noloading' => array(
647
+ 'value' => '',
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(
659
+ array(
660
+ 'attr_spec_list' => array(
661
+ '[slide]' => array(),
662
+ 'arrows' => array(
663
+ 'value' => '',
664
+ ),
665
+ 'autoplay' => array(
666
+ 'value' => '',
667
+ ),
668
+ 'controls' => array(),
669
+ 'delay' => array(
670
+ 'value_regex' => '[0-9]+',
671
+ ),
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
+ ),
685
+ 'media' => array(),
686
+ 'noloading' => array(
687
+ 'value' => '',
688
+ ),
689
+ 'type' => array(
690
+ 'value_regex' => 'slides|carousel',
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(
718
+ array(
719
+ 'attr_spec_list' => array(
720
+ 'autoplay' => array(),
721
+ 'data-endscreen-enable' => array(
722
+ 'value_regex' => 'true|false',
723
+ ),
724
+ 'data-info' => array(
725
+ 'value_regex' => 'true|false',
726
+ ),
727
+ 'data-mute' => array(
728
+ 'value_regex' => 'true|false',
729
+ ),
730
+ 'data-sharing-enable' => array(
731
+ 'value_regex' => 'true|false',
732
+ ),
733
+ 'data-start' => array(
734
+ 'value_regex' => '[0-9]+',
735
+ ),
736
+ 'data-ui-highlight' => array(
737
+ 'value_regex_casei' => '([0-9a-f]{3}){1,2}',
738
+ ),
739
+ 'data-ui-logo' => array(
740
+ 'value_regex' => 'true|false',
741
+ ),
742
+ 'data-videoid' => array(
743
+ 'mandatory' => true,
744
+ 'value_regex_casei' => '[a-z0-9]+',
745
+ ),
746
+ 'media' => array(),
747
+ 'noloading' => array(
748
+ 'value' => '',
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(
1124
+ 'attr_spec_list' => array(
1125
+ 'data-href' => array(
1126
+ 'mandatory' => true,
1127
+ ),
1128
+ 'media' => array(),
1129
+ 'noloading' => array(
1130
+ 'value' => '',
1131
+ ),
1132
+ ),
1133
+ 'tag_spec' => array(
1134
+ 'requires_extension' => array(
1135
+ 'amp-facebook-comments',
1136
+ ),
1137
+ ),
1138
+ ),
1139
+ ),
1140
+ 'amp-facebook-like' => array(
1141
+ 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(),
1154
+ 'noloading' => array(
1155
+ 'value' => '',
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(
1191
+ array(
1192
+ 'attr_spec_list' => array(
1193
+ 'max-font-size' => array(),
1194
+ 'media' => array(),
1195
+ 'min-font-size' => array(),
1196
+ 'noloading' => 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(
1208
+ array(
1209
+ 'attr_spec_list' => array(
1210
+ 'font-family' => array(
1211
+ 'mandatory' => true,
1212
+ ),
1213
+ 'font-style' => array(),
1214
+ 'font-variant' => array(),
1215
+ 'font-weight' => array(),
1216
+ 'media' => array(),
1217
+ 'noloading' => array(
1218
+ 'value' => '',
1219
+ ),
1220
+ 'on-error-add-class' => array(),
1221
+ 'on-error-remove-class' => array(),
1222
+ 'on-load-add-class' => array(),
1223
+ 'on-load-remove-class' => array(),
1224
+ 'timeout' => array(
1225
+ 'value_regex' => '[0-9]+',
1226
+ ),
1227
+ ),
1228
+ 'tag_spec' => array(
1229
+ 'requires_extension' => array(
1230
+ 'amp-font',
1231
+ ),
1232
+ ),
1233
+ ),
1234
+ ),
1235
+ 'amp-fx-flying-carpet' => array(
1236
+ array(
1237
+ 'attr_spec_list' => array(
1238
+ 'height' => array(
1239
+ 'mandatory' => true,
1240
+ ),
1241
+ 'media' => array(),
1242
+ 'noloading' => array(
1243
+ 'value' => '',
1244
+ ),
1245
+ ),
1246
+ 'tag_spec' => array(
1247
+ 'requires_extension' => array(
1248
+ 'amp-fx-flying-carpet',
1249
+ ),
1250
+ ),
1251
+ ),
1252
+ ),
1253
+ 'amp-gfycat' => array(
1254
+ array(
1255
+ 'attr_spec_list' => array(
1256
+ 'data-gfyid' => array(
1257
+ 'mandatory' => true,
1258
+ ),
1259
+ 'media' => array(),
1260
+ 'noautoplay' => array(
1261
+ 'value' => '',
1262
+ ),
1263
+ 'noloading' => array(
1264
+ 'value' => '',
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(
1276
+ array(
1277
+ 'attr_spec_list' => array(
1278
+ 'data-gistid' => array(
1279
+ 'mandatory' => true,
1280
+ ),
1281
+ 'media' => array(),
1282
+ 'noloading' => array(
1283
+ 'value' => '',
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(
1295
+ array(
1296
+ 'attr_spec_list' => array(
1297
+ 'data-eid' => array(
1298
+ 'mandatory' => true,
1299
+ ),
1300
+ 'media' => array(),
1301
+ 'noloading' => array(
1302
+ 'value' => '',
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
+ ),
1321
+ 'allowpaymentrequest' => array(
1322
+ 'value' => '',
1323
+ ),
1324
+ 'allowtransparency' => array(
1325
+ 'value' => '',
1326
+ ),
1327
+ 'frameborder' => array(
1328
+ 'value_regex' => '0|1',
1329
+ ),
1330
+ 'media' => array(),
1331
+ 'noloading' => array(
1332
+ 'value' => '',
1333
+ ),
1334
+ 'referrerpolicy' => array(),
1335
+ 'resizable' => array(
1336
+ 'value' => '',
1337
+ ),
1338
+ 'sandbox' => array(),
1339
+ 'scrolling' => array(
1340
+ 'value_regex' => 'auto|yes|no',
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(),
1386
+ 'noloading' => array(
1387
+ 'value' => '',
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(
1399
+ array(
1400
+ 'attr_spec_list' => array(
1401
+ 'controls' => array(),
1402
+ 'media' => array(),
1403
+ 'noloading' => array(
1404
+ 'value' => '',
1405
+ ),
1406
+ ),
1407
+ 'tag_spec' => array(
1408
+ 'requires_extension' => array(
1409
+ 'amp-image-lightbox',
1410
+ ),
1411
+ ),
1412
+ ),
1413
+ ),
1414
+ 'amp-img' => array(
1415
+ array(
1416
+ 'attr_spec_list' => array(
1417
+ '[alt]' => array(),
1418
+ '[attribution]' => array(),
1419
+ '[src]' => 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' => '',
1433
+ ),
1434
+ 'placeholder' => array(),
1435
+ 'src' => array(
1436
+ 'alternative_names' => array(
1437
+ 'srcset',
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(
1457
+ array(
1458
+ 'attr_spec_list' => array(
1459
+ 'data-imgur-id' => array(
1460
+ 'mandatory' => true,
1461
+ ),
1462
+ 'media' => array(),
1463
+ 'noloading' => array(
1464
+ 'value' => '',
1465
+ ),
1466
+ ),
1467
+ 'tag_spec' => array(
1468
+ 'requires_extension' => array(
1469
+ 'amp-imgur',
1470
+ ),
1471
+ ),
1472
+ ),
1473
+ ),
1474
+ 'amp-instagram' => array(
1475
+ array(
1476
+ 'attr_spec_list' => array(
1477
+ 'alt' => array(),
1478
+ 'data-shortcode' => array(
1479
+ 'mandatory' => true,
1480
+ ),
1481
+ 'media' => array(),
1482
+ 'noloading' => array(
1483
+ 'value' => '',
1484
+ ),
1485
+ ),
1486
+ 'tag_spec' => array(
1487
+ 'requires_extension' => array(
1488
+ 'amp-instagram',
1489
+ ),
1490
+ ),
1491
+ ),
1492
+ ),
1493
+ 'amp-install-serviceworker' => array(
1494
+ 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(
1524
+ array(
1525
+ 'attr_spec_list' => array(
1526
+ 'data-videoid' => array(
1527
+ 'mandatory' => true,
1528
+ 'value_regex' => '[0-9]+',
1529
+ ),
1530
+ 'media' => array(),
1531
+ 'noloading' => array(
1532
+ 'value' => '',
1533
+ ),
1534
+ ),
1535
+ 'tag_spec' => array(
1536
+ 'requires_extension' => array(
1537
+ 'amp-izlesene',
1538
+ ),
1539
+ ),
1540
+ ),
1541
+ ),
1542
+ 'amp-jwplayer' => array(
1543
+ array(
1544
+ 'attr_spec_list' => array(
1545
+ 'data-media-id' => array(
1546
+ 'value_regex_casei' => '[0-9a-z]{8}',
1547
+ ),
1548
+ 'data-player-id' => array(
1549
+ 'mandatory' => true,
1550
+ 'value_regex_casei' => '[0-9a-z]{8}',
1551
+ ),
1552
+ 'data-playlist-id' => array(
1553
+ 'value_regex_casei' => '[0-9a-z]{8}',
1554
+ ),
1555
+ ),
1556
+ 'tag_spec' => array(
1557
+ 'requires_extension' => array(
1558
+ 'amp-jwplayer',
1559
+ ),
1560
+ ),
1561
+ ),
1562
+ ),
1563
+ 'amp-kaltura-player' => array(
1564
+ array(
1565
+ 'attr_spec_list' => array(
1566
+ 'data-partner' => array(
1567
+ 'mandatory' => true,
1568
+ ),
1569
+ 'media' => array(),
1570
+ 'noloading' => array(
1571
+ 'value' => '',
1572
+ ),
1573
+ ),
1574
+ 'tag_spec' => array(
1575
+ 'requires_extension' => array(
1576
+ 'amp-kaltura-player',
1577
+ ),
1578
+ ),
1579
+ ),
1580
+ ),
1581
+ 'amp-layout' => array(
1582
+ array(
1583
+ 'attr_spec_list' => array(
1584
+ 'media' => array(),
1585
+ 'noloading' => array(
1586
+ 'value' => '',
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(
1595
+ array(
1596
+ 'attr_spec_list' => array(
1597
+ 'controls' => array(),
1598
+ 'from' => array(),
1599
+ 'media' => array(),
1600
+ 'noloading' => array(
1601
+ 'value' => '',
1602
+ ),
1603
+ 'scrollable' => array(),
1604
+ ),
1605
+ 'tag_spec' => array(
1606
+ 'requires_extension' => array(
1607
+ 'amp-lightbox',
1608
+ ),
1609
+ ),
1610
+ ),
1611
+ ),
1612
+ 'amp-list' => array(
1613
+ array(
1614
+ 'attr_spec_list' => array(
1615
+ '[src]' => array(),
1616
+ '[state]' => array(),
1617
+ 'credentials' => array(),
1618
+ 'items' => array(),
1619
+ 'max-items' => array(),
1620
+ 'media' => 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(
1679
+ array(
1680
+ 'attr_spec_list' => array(
1681
+ 'data-max-items-per-page' => array(
1682
+ 'mandatory' => true,
1683
+ 'value_regex' => '\\d+',
1684
+ ),
1685
+ 'data-poll-interval' => array(
1686
+ 'value_regex' => '\\d{5,}',
1687
+ ),
1688
+ 'disabled' => array(
1689
+ 'value' => '',
1690
+ ),
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
+ ),
1734
+ 'data-mode' => array(
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
+ ),
1749
+ 'media' => array(),
1750
+ 'noloading' => array(
1751
+ 'value' => '',
1752
+ ),
1753
+ ),
1754
+ 'tag_spec' => array(
1755
+ 'requires_extension' => array(
1756
+ 'amp-nexxtv-player',
1757
+ ),
1758
+ ),
1759
+ ),
1760
+ ),
1761
+ 'amp-o2-player' => array(
1762
+ array(
1763
+ 'attr_spec_list' => array(
1764
+ 'data-bcid' => array(
1765
+ 'mandatory' => true,
1766
+ ),
1767
+ 'data-pid' => array(
1768
+ 'mandatory' => true,
1769
+ ),
1770
+ 'media' => array(),
1771
+ 'noloading' => array(
1772
+ 'value' => '',
1773
+ ),
1774
+ ),
1775
+ 'tag_spec' => array(
1776
+ 'requires_extension' => array(
1777
+ 'amp-o2-player',
1778
+ ),
1779
+ ),
1780
+ ),
1781
+ ),
1782
+ 'amp-ooyala-player' => array(
1783
+ array(
1784
+ 'attr_spec_list' => array(
1785
+ 'data-embedcode' => array(
1786
+ 'mandatory' => true,
1787
+ ),
1788
+ 'data-pcode' => array(
1789
+ 'mandatory' => true,
1790
+ ),
1791
+ 'data-playerid' => array(
1792
+ 'mandatory' => true,
1793
+ ),
1794
+ 'media' => array(),
1795
+ 'noloading' => array(
1796
+ 'value' => '',
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
+ ),
1813
+ 'media' => array(),
1814
+ 'noloading' => array(
1815
+ 'value' => '',
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' => '',
1833
+ ),
1834
+ 'referrerpolicy' => array(
1835
+ 'value' => 'no-referrer',
1836
+ ),
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(
1855
+ array(
1856
+ 'attr_spec_list' => array(
1857
+ 'data-comments' => array(
1858
+ 'value_regex_casei' => '(false|true)',
1859
+ ),
1860
+ 'data-item' => array(),
1861
+ 'data-item-info' => array(
1862
+ 'value_regex_casei' => '(false|true)',
1863
+ ),
1864
+ 'data-share-buttons' => array(
1865
+ 'value_regex_casei' => '(false|true)',
1866
+ ),
1867
+ 'media' => array(),
1868
+ 'noloading' => array(
1869
+ 'value' => '',
1870
+ ),
1871
+ 'src' => array(),
1872
+ ),
1873
+ 'tag_spec' => array(
1874
+ 'requires_extension' => array(
1875
+ 'amp-playbuzz',
1876
+ ),
1877
+ ),
1878
+ ),
1879
+ ),
1880
+ 'amp-position-observer' => array(
1881
+ array(
1882
+ 'attr_spec_list' => array(
1883
+ 'intersection-ratios' => array(
1884
+ 'value_regex' => '^([0]*?\\.\\d*$|1$|0$)|([0]*?\\.\\d*|1|0)\\s{1}([0]*?\\.\\d*$|1$|0$)',
1885
+ ),
1886
+ 'media' => array(),
1887
+ 'noloading' => array(
1888
+ 'value' => '',
1889
+ ),
1890
+ 'target' => array(),
1891
+ 'viewport-margins' => array(
1892
+ 'value_regex' => '^(\\d+$|\\d+px$|\\d+vh$)|((\\d+|\\d+px|\\d+vh)\\s{1}(\\d+$|\\d+px$|\\d+vh$))',
1893
+ ),
1894
+ ),
1895
+ 'tag_spec' => array(
1896
+ 'requires_extension' => array(
1897
+ 'amp-position-observer',
1898
+ ),
1899
+ ),
1900
+ ),
1901
+ ),
1902
+ 'amp-reach-player' => array(
1903
+ array(
1904
+ 'attr_spec_list' => array(
1905
+ 'data-embed-id' => array(
1906
+ 'mandatory' => true,
1907
+ 'value_regex' => '[0-9a-z-]+',
1908
+ ),
1909
+ 'media' => array(),
1910
+ 'noloading' => array(
1911
+ 'value' => '',
1912
+ ),
1913
+ ),
1914
+ 'tag_spec' => array(
1915
+ 'requires_extension' => array(
1916
+ 'amp-reach-player',
1917
+ ),
1918
+ ),
1919
+ ),
1920
+ ),
1921
+ 'amp-reddit' => array(
1922
+ array(
1923
+ 'attr_spec_list' => array(
1924
+ 'data-embedlive' => array(
1925
+ 'value_regex_casei' => '(false|true)',
1926
+ ),
1927
+ 'data-embedparent' => array(
1928
+ 'value_regex_casei' => '(false|true)',
1929
+ ),
1930
+ 'data-embedtype' => array(
1931
+ 'mandatory' => true,
1932
+ 'value_regex_casei' => '(comment|post)',
1933
+ ),
1934
+ 'data-src' => array(
1935
+ 'mandatory' => true,
1936
+ ),
1937
+ 'media' => array(),
1938
+ 'noloading' => array(
1939
+ 'value' => '',
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' => '',
1976
+ ),
1977
+ 'form' => array(),
1978
+ 'keyboard-select-mode' => array(
1979
+ 'value_regex_casei' => 'focus|none|select',
1980
+ ),
1981
+ 'media' => array(),
1982
+ 'multiple' => array(
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' => '',
1990
+ ),
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(
2003
+ array(
2004
+ 'attr_spec_list' => array(
2005
+ 'media' => array(),
2006
+ 'noloading' => array(
2007
+ 'value' => '',
2008
+ ),
2009
+ 'side' => array(
2010
+ 'value_regex' => '(left|right)',
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(
2022
+ 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(),
2049
+ 'noloading' => array(
2050
+ 'value' => '',
2051
+ ),
2052
+ 'type' => array(
2053
+ 'mandatory' => true,
2054
+ ),
2055
+ ),
2056
+ 'tag_spec' => array(
2057
+ 'requires_extension' => array(
2058
+ 'amp-social-share',
2059
+ ),
2060
+ ),
2061
+ ),
2062
+ ),
2063
+ 'amp-soundcloud' => array(
2064
+ array(
2065
+ 'attr_spec_list' => array(
2066
+ 'data-color' => array(
2067
+ 'value_regex_casei' => '([0-9a-f]{3}){1,2}',
2068
+ ),
2069
+ 'data-playlistid' => 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]+',
2077
+ ),
2078
+ 'data-visual' => array(
2079
+ 'value_regex' => 'true|false',
2080
+ ),
2081
+ 'media' => array(),
2082
+ 'noloading' => array(
2083
+ 'value' => '',
2084
+ ),
2085
+ ),
2086
+ 'tag_spec' => array(
2087
+ 'requires_extension' => array(
2088
+ 'amp-soundcloud',
2089
+ ),
2090
+ ),
2091
+ ),
2092
+ ),
2093
+ 'amp-springboard-player' => array(
2094
+ array(
2095
+ 'attr_spec_list' => array(
2096
+ 'data-content-id' => array(
2097
+ 'mandatory' => true,
2098
+ ),
2099
+ 'data-domain' => array(
2100
+ 'mandatory' => true,
2101
+ ),
2102
+ 'data-items' => array(
2103
+ 'mandatory' => true,
2104
+ ),
2105
+ 'data-mode' => array(
2106
+ 'mandatory' => true,
2107
+ 'value_regex_casei' => 'playlist|video',
2108
+ ),
2109
+ 'data-player-id' => array(
2110
+ 'mandatory' => true,
2111
+ 'value_regex_casei' => '[a-z0-9]+',
2112
+ ),
2113
+ 'data-site-id' => array(
2114
+ 'mandatory' => true,
2115
+ 'value_regex' => '[0-9]+',
2116
+ ),
2117
+ 'media' => array(),
2118
+ 'noloading' => array(
2119
+ 'value' => '',
2120
+ ),
2121
+ ),
2122
+ 'tag_spec' => array(
2123
+ 'requires_extension' => array(
2124
+ 'amp-springboard-player',
2125
+ ),
2126
+ ),
2127
+ ),
2128
+ ),
2129
+ 'amp-state' => array(
2130
+ array(
2131
+ 'attr_spec_list' => array(
2132
+ '[src]' => array(),
2133
+ 'credentials' => 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(
2158
+ array(
2159
+ 'attr_spec_list' => array(
2160
+ 'media' => array(),
2161
+ 'noloading' => array(
2162
+ 'value' => '',
2163
+ ),
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(
2196
+ 'mandatory' => true,
2197
+ 'value' => '',
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(
2230
+ array(
2231
+ 'attr_spec_list' => array(
2232
+ 'template' => array(
2233
+ 'mandatory' => true,
2234
+ 'value_regex' => '(fill|horizontal|vertical|thirds)',
2235
+ ),
2236
+ ),
2237
+ 'tag_spec' => array(
2238
+ 'mandatory_ancestor' => 'amp-story-page',
2239
+ ),
2240
+ ),
2241
+ ),
2242
+ 'amp-story-page' => array(
2243
+ 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(
2255
+ 'mandatory' => true,
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(
2267
+ array(
2268
+ 'attr_spec_list' => array(
2269
+ 'cutoff' => array(
2270
+ 'value_regex' => '\\d+',
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(),
2278
+ 'noloading' => array(
2279
+ 'value' => '',
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(
2291
+ array(
2292
+ 'attr_spec_list' => array(
2293
+ 'data-tweetid' => array(
2294
+ 'mandatory' => true,
2295
+ ),
2296
+ 'media' => array(),
2297
+ 'noloading' => array(
2298
+ 'value' => '',
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(
2330
+ 'value' => 'application/x-www-form-urlencoded',
2331
+ ),
2332
+ 'media' => array(),
2333
+ 'noloading' => array(
2334
+ 'value' => '',
2335
+ ),
2336
+ ),
2337
+ 'tag_spec' => array(
2338
+ 'requires_extension' => array(
2339
+ 'amp-user-notification',
2340
+ ),
2341
+ ),
2342
+ ),
2343
+ ),
2344
+ 'amp-video' => array(
2345
+ array(
2346
+ 'attr_spec_list' => array(
2347
+ '[album]' => array(),
2348
+ '[alt]' => array(),
2349
+ '[artist]' => array(),
2350
+ '[artwork]' => array(),
2351
+ '[attribution]' => array(),
2352
+ '[controls]' => array(),
2353
+ '[controlslist]' => array(),
2354
+ '[loop]' => array(),
2355
+ '[poster]' => array(),
2356
+ '[preload]' => array(),
2357
+ '[src]' => array(),
2358
+ '[title]' => array(),
2359
+ 'album' => array(),
2360
+ 'alt' => array(),
2361
+ 'artist' => array(),
2362
+ 'artwork' => array(),
2363
+ 'attribution' => array(),
2364
+ 'autoplay' => array(
2365
+ 'value' => '',
2366
+ ),
2367
+ 'controls' => array(
2368
+ 'value' => '',
2369
+ ),
2370
+ 'controlslist' => array(),
2371
+ 'crossorigin' => 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
+ ),
2385
+ 'media' => array(),
2386
+ 'muted' => array(
2387
+ 'value' => '',
2388
+ ),
2389
+ 'noloading' => array(
2390
+ 'value' => '',
2391
+ ),
2392
+ 'placeholder' => array(),
2393
+ 'poster' => array(),
2394
+ 'preload' => array(
2395
+ 'value_regex' => '(none|metadata|auto|)',
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(
2419
+ '[album]' => array(),
2420
+ '[alt]' => array(),
2421
+ '[artist]' => array(),
2422
+ '[artwork]' => array(),
2423
+ '[attribution]' => array(),
2424
+ '[controls]' => array(),
2425
+ '[controlslist]' => array(),
2426
+ '[loop]' => array(),
2427
+ '[poster]' => array(),
2428
+ '[preload]' => array(),
2429
+ '[src]' => array(),
2430
+ '[title]' => array(),
2431
+ 'album' => array(),
2432
+ 'alt' => array(),
2433
+ 'artist' => array(),
2434
+ 'artwork' => array(),
2435
+ 'attribution' => array(),
2436
+ 'autoplay' => array(
2437
+ 'value' => '',
2438
+ ),
2439
+ 'controls' => array(
2440
+ 'value' => '',
2441
+ ),
2442
+ 'controlslist' => array(),
2443
+ 'crossorigin' => array(),
2444
+ 'disableremoteplayback' => array(
2445
+ 'value' => '',
2446
+ ),
2447
+ 'loop' => array(
2448
+ 'value' => '',
2449
+ ),
2450
+ 'media' => array(),
2451
+ 'muted' => array(
2452
+ 'value' => '',
2453
+ ),
2454
+ 'noloading' => array(
2455
+ 'value' => '',
2456
+ ),
2457
+ 'placeholder' => array(),
2458
+ 'poster' => array(
2459
+ 'mandatory' => true,
2460
+ ),
2461
+ 'preload' => array(
2462
+ 'value_regex' => '(none|metadata|auto|)',
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(
2485
+ array(
2486
+ 'attr_spec_list' => array(
2487
+ 'data-videoid' => array(
2488
+ 'mandatory' => true,
2489
+ 'value_regex' => '[0-9]+',
2490
+ ),
2491
+ 'media' => array(),
2492
+ 'noloading' => array(
2493
+ 'value' => '',
2494
+ ),
2495
+ ),
2496
+ 'tag_spec' => array(
2497
+ 'requires_extension' => array(
2498
+ 'amp-vimeo',
2499
+ ),
2500
+ ),
2501
+ ),
2502
+ ),
2503
+ 'amp-vine' => array(
2504
+ array(
2505
+ 'attr_spec_list' => array(
2506
+ 'data-vineid' => array(
2507
+ 'mandatory' => true,
2508
+ ),
2509
+ 'media' => array(),
2510
+ 'noloading' => array(
2511
+ 'value' => '',
2512
+ ),
2513
+ ),
2514
+ 'tag_spec' => array(
2515
+ 'requires_extension' => array(
2516
+ 'amp-vine',
2517
+ ),
2518
+ ),
2519
+ ),
2520
+ ),
2521
+ 'amp-vk' => array(
2522
+ array(
2523
+ 'attr_spec_list' => array(
2524
+ 'data-embedtype' => array(
2525
+ 'mandatory' => true,
2526
+ ),
2527
+ 'media' => array(),
2528
+ 'noloading' => array(
2529
+ 'value' => '',
2530
+ ),
2531
+ ),
2532
+ 'tag_spec' => array(
2533
+ 'requires_extension' => array(
2534
+ 'amp-vk',
2535
+ ),
2536
+ ),
2537
+ ),
2538
+ ),
2539
+ 'amp-web-push' => array(
2540
+ 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(
2552
+ 'mandatory' => true,
2553
+ 'value_regex' => 'amp-web-push',
2554
+ ),
2555
+ 'media' => array(),
2556
+ 'noloading' => array(
2557
+ 'value' => '',
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(
2587
+ array(
2588
+ 'attr_spec_list' => array(
2589
+ 'media' => array(),
2590
+ 'noloading' => array(
2591
+ 'value' => '',
2592
+ ),
2593
+ 'visibility' => array(
2594
+ 'mandatory' => true,
2595
+ 'value_regex' => '(blocked|subscribed|unsubscribed)',
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(
2626
+ array(
2627
+ 'attr_spec_list' => array(
2628
+ '[data-videoid]' => array(),
2629
+ 'autoplay' => 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(
2671
+ array(
2672
+ 'attr_spec_list' => array(
2673
+ 'autoplay' => array(),
2674
+ 'controls' => array(),
2675
+ 'loop' => array(),
2676
+ 'muted' => 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(
2725
+ array(
2726
+ 'attr_spec_list' => 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(
2739
+ array(
2740
+ 'attr_spec_list' => 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(
2758
+ array(
2759
+ 'attr_spec_list' => array(),
2760
+ 'tag_spec' => array(
2761
+ 'mandatory' => true,
2762
+ 'mandatory_parent' => 'html',
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(
2775
+ array(
2776
+ 'attr_spec_list' => array(
2777
+ '[disabled]' => array(),
2778
+ '[type]' => array(),
2779
+ '[value]' => array(),
2780
+ 'disabled' => 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(),
2788
+ 'type' => 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' => '',
2800
+ ),
2801
+ 'role' => array(),
2802
+ 'tabindex' => array(),
2803
+ 'type' => array(),
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(
2825
+ array(
2826
+ 'attr_spec_list' => array(
2827
+ 'alignment-baseline' => array(),
2828
+ 'baseline-shift' => array(),
2829
+ 'clip' => array(),
2830
+ 'clip-path' => array(),
2831
+ 'clip-rule' => array(),
2832
+ 'color' => array(),
2833
+ 'color-interpolation' => array(),
2834
+ 'color-interpolation-filters' => array(),
2835
+ 'color-profile' => array(),
2836
+ 'color-rendering' => array(),
2837
+ 'cursor' => array(),
2838
+ 'cx' => array(),
2839
+ 'cy' => array(),
2840
+ 'direction' => array(),
2841
+ 'display' => array(),
2842
+ 'dominant-baseline' => array(),
2843
+ 'enable-background' => array(),
2844
+ 'externalresourcesrequired' => array(),
2845
+ 'fill' => array(),
2846
+ 'fill-opacity' => array(),
2847
+ 'fill-rule' => array(),
2848
+ 'filter' => array(),
2849
+ 'flood-color' => array(),
2850
+ 'flood-opacity' => array(),
2851
+ 'font-family' => array(),
2852
+ 'font-size' => array(),
2853
+ 'font-size-adjust' => array(),
2854
+ 'font-stretch' => array(),
2855
+ 'font-style' => array(),
2856
+ 'font-variant' => array(),
2857
+ 'font-weight' => array(),
2858
+ 'glyph-orientation-horizontal' => array(),
2859
+ 'glyph-orientation-vertical' => array(),
2860
+ 'image-rendering' => array(),
2861
+ 'kerning' => array(),
2862
+ 'letter-spacing' => array(),
2863
+ 'lighting-color' => array(),
2864
+ 'marker-end' => array(),
2865
+ 'marker-mid' => array(),
2866
+ 'marker-start' => array(),
2867
+ 'mask' => array(),
2868
+ 'opacity' => array(),
2869
+ 'overflow' => array(),
2870
+ 'pointer-events' => array(),
2871
+ 'r' => array(),
2872
+ 'requiredextensions' => array(),
2873
+ 'requiredfeatures' => array(),
2874
+ 'shape-rendering' => array(),
2875
+ 'sketch:type' => array(),
2876
+ 'stop-color' => array(),
2877
+ 'stop-opacity' => array(),
2878
+ 'stroke' => array(),
2879
+ 'stroke-dasharray' => array(),
2880
+ 'stroke-dashoffset' => array(),
2881
+ 'stroke-linecap' => array(),
2882
+ 'stroke-linejoin' => array(),
2883
+ 'stroke-miterlimit' => array(),
2884
+ 'stroke-opacity' => array(),
2885
+ 'stroke-width' => array(),
2886
+ 'style' => array(
2887
+ 'blacklisted_value_regex' => '!important',
2888
+ ),
2889
+ 'systemlanguage' => array(),
2890
+ 'text-anchor' => array(),
2891
+ 'text-decoration' => array(),
2892
+ 'text-rendering' => array(),
2893
+ 'transform' => array(),
2894
+ 'unicode-bidi' => array(),
2895
+ 'vector-effect' => array(),
2896
+ 'visibility' => array(),
2897
+ 'word-spacing' => array(),
2898
+ 'writing-mode' => array(),
2899
+ 'xml:lang' => array(),
2900
+ 'xml:space' => array(),
2901
+ 'xmlns' => 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(
2917
+ array(
2918
+ 'attr_spec_list' => array(
2919
+ 'alignment-baseline' => array(),
2920
+ 'baseline-shift' => array(),
2921
+ 'clip' => array(),
2922
+ 'clip-path' => array(),
2923
+ 'clip-rule' => array(),
2924
+ 'clippathunits' => array(),
2925
+ 'color' => array(),
2926
+ 'color-interpolation' => array(),
2927
+ 'color-interpolation-filters' => array(),
2928
+ 'color-profile' => array(),
2929
+ 'color-rendering' => array(),
2930
+ 'cursor' => array(),
2931
+ 'direction' => array(),
2932
+ 'display' => array(),
2933
+ 'dominant-baseline' => array(),
2934
+ 'enable-background' => array(),
2935
+ 'externalresourcesrequired' => array(),
2936
+ 'fill' => array(),
2937
+ 'fill-opacity' => array(),
2938
+ 'fill-rule' => array(),
2939
+ 'filter' => array(),
2940
+ 'flood-color' => array(),
2941
+ 'flood-opacity' => array(),
2942
+ 'font-family' => array(),
2943
+ 'font-size' => array(),
2944
+ 'font-size-adjust' => array(),
2945
+ 'font-stretch' => array(),
2946
+ 'font-style' => array(),
2947
+ 'font-variant' => array(),
2948
+ 'font-weight' => array(),
2949
+ 'glyph-orientation-horizontal' => array(),
2950
+ 'glyph-orientation-vertical' => array(),
2951
+ 'image-rendering' => array(),
2952
+ 'kerning' => array(),
2953
+ 'letter-spacing' => array(),
2954
+ 'lighting-color' => array(),
2955
+ 'marker-end' => array(),
2956
+ 'marker-mid' => array(),
2957
+ 'marker-start' => array(),
2958
+ 'mask' => array(),
2959
+ 'opacity' => array(),
2960
+ 'overflow' => array(),
2961
+ 'pointer-events' => array(),
2962
+ 'requiredextensions' => array(),
2963
+ 'requiredfeatures' => array(),
2964
+ 'shape-rendering' => array(),
2965
+ 'stop-color' => array(),
2966
+ 'stop-opacity' => array(),
2967
+ 'stroke' => array(),
2968
+ 'stroke-dasharray' => array(),
2969
+ 'stroke-dashoffset' => array(),
2970
+ 'stroke-linecap' => array(),
2971
+ 'stroke-linejoin' => array(),
2972
+ 'stroke-miterlimit' => array(),
2973
+ 'stroke-opacity' => array(),
2974
+ 'stroke-width' => array(),
2975
+ 'style' => array(
2976
+ 'blacklisted_value_regex' => '!important',
2977
+ ),
2978
+ 'systemlanguage' => array(),
2979
+ 'text-anchor' => array(),
2980
+ 'text-decoration' => array(),
2981
+ 'text-rendering' => array(),
2982
+ 'transform' => array(),
2983
+ 'unicode-bidi' => array(),
2984
+ 'vector-effect' => array(),
2985
+ 'visibility' => array(),
2986
+ 'word-spacing' => array(),
2987
+ 'writing-mode' => array(),
2988
+ 'xml:lang' => array(),
2989
+ 'xml:space' => array(),
2990
+ 'xmlns' => 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(
3006
+ array(
3007
+ 'attr_spec_list' => array(
3008
+ 'span' => array(),
3009
+ ),
3010
+ 'tag_spec' => array(),
3011
+ ),
3012
+ ),
3013
+ 'colgroup' => array(
3014
+ array(
3015
+ 'attr_spec_list' => 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(
3028
+ array(
3029
+ 'attr_spec_list' => 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(
3042
+ array(
3043
+ 'attr_spec_list' => array(
3044
+ 'alignment-baseline' => array(),
3045
+ 'baseline-shift' => array(),
3046
+ 'clip' => array(),
3047
+ 'clip-path' => array(),
3048
+ 'clip-rule' => array(),
3049
+ 'color' => array(),
3050
+ 'color-interpolation' => array(),
3051
+ 'color-interpolation-filters' => array(),
3052
+ 'color-profile' => array(),
3053
+ 'color-rendering' => array(),
3054
+ 'cursor' => array(),
3055
+ 'direction' => array(),
3056
+ 'display' => array(),
3057
+ 'dominant-baseline' => array(),
3058
+ 'enable-background' => array(),
3059
+ 'externalresourcesrequired' => array(),
3060
+ 'fill' => array(),
3061
+ 'fill-opacity' => array(),
3062
+ 'fill-rule' => array(),
3063
+ 'filter' => array(),
3064
+ 'flood-color' => array(),
3065
+ 'flood-opacity' => array(),
3066
+ 'font-family' => array(),
3067
+ 'font-size' => array(),
3068
+ 'font-size-adjust' => array(),
3069
+ 'font-stretch' => array(),
3070
+ 'font-style' => array(),
3071
+ 'font-variant' => array(),
3072
+ 'font-weight' => array(),
3073
+ 'glyph-orientation-horizontal' => array(),
3074
+ 'glyph-orientation-vertical' => array(),
3075
+ 'image-rendering' => array(),
3076
+ 'kerning' => array(),
3077
+ 'letter-spacing' => array(),
3078
+ 'lighting-color' => array(),
3079
+ 'marker-end' => array(),
3080
+ 'marker-mid' => array(),
3081
+ 'marker-start' => array(),
3082
+ 'mask' => array(),
3083
+ 'opacity' => array(),
3084
+ 'overflow' => array(),
3085
+ 'pointer-events' => array(),
3086
+ 'requiredextensions' => array(),
3087
+ 'requiredfeatures' => array(),
3088
+ 'shape-rendering' => array(),
3089
+ 'stop-color' => array(),
3090
+ 'stop-opacity' => array(),
3091
+ 'stroke' => array(),
3092
+ 'stroke-dasharray' => array(),
3093
+ 'stroke-dashoffset' => array(),
3094
+ 'stroke-linecap' => array(),
3095
+ 'stroke-linejoin' => array(),
3096
+ 'stroke-miterlimit' => array(),
3097
+ 'stroke-opacity' => array(),
3098
+ 'stroke-width' => array(),
3099
+ 'style' => array(
3100
+ 'blacklisted_value_regex' => '!important',
3101
+ ),
3102
+ 'systemlanguage' => array(),
3103
+ 'text-anchor' => array(),
3104
+ 'text-decoration' => array(),
3105
+ 'text-rendering' => array(),
3106
+ 'transform' => array(),
3107
+ 'unicode-bidi' => array(),
3108
+ 'vector-effect' => array(),
3109
+ 'visibility' => array(),
3110
+ 'word-spacing' => array(),
3111
+ 'writing-mode' => array(),
3112
+ 'xml:lang' => array(),
3113
+ 'xml:space' => array(),
3114
+ 'xmlns' => 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(
3124
+ 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(
3143
+ array(
3144
+ 'attr_spec_list' => array(
3145
+ 'style' => array(
3146
+ 'blacklisted_value_regex' => '!important',
3147
+ ),
3148
+ 'xml:lang' => array(),
3149
+ 'xml:space' => array(),
3150
+ 'xmlns' => 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(
3172
+ array(
3173
+ 'attr_spec_list' => 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(
3193
+ 'align' => array(),
3194
+ 'submit-success' => array(
3195
+ 'mandatory' => true,
3196
+ ),
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(
3220
+ 'align' => array(),
3221
+ 'submit-error' => array(
3222
+ 'mandatory' => true,
3223
+ ),
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(
3259
+ array(
3260
+ 'attr_spec_list' => array(
3261
+ 'alignment-baseline' => array(),
3262
+ 'baseline-shift' => array(),
3263
+ 'clip' => array(),
3264
+ 'clip-path' => array(),
3265
+ 'clip-rule' => array(),
3266
+ 'color' => array(),
3267
+ 'color-interpolation' => array(),
3268
+ 'color-interpolation-filters' => array(),
3269
+ 'color-profile' => array(),
3270
+ 'color-rendering' => array(),
3271
+ 'cursor' => array(),
3272
+ 'cx' => array(),
3273
+ 'cy' => array(),
3274
+ 'direction' => array(),
3275
+ 'display' => array(),
3276
+ 'dominant-baseline' => array(),
3277
+ 'enable-background' => array(),
3278
+ 'externalresourcesrequired' => array(),
3279
+ 'fill' => array(),
3280
+ 'fill-opacity' => array(),
3281
+ 'fill-rule' => array(),
3282
+ 'filter' => array(),
3283
+ 'flood-color' => array(),
3284
+ 'flood-opacity' => array(),
3285
+ 'font-family' => array(),
3286
+ 'font-size' => array(),
3287
+ 'font-size-adjust' => array(),
3288
+ 'font-stretch' => array(),
3289
+ 'font-style' => array(),
3290
+ 'font-variant' => array(),
3291
+ 'font-weight' => array(),
3292
+ 'glyph-orientation-horizontal' => array(),
3293
+ 'glyph-orientation-vertical' => array(),
3294
+ 'image-rendering' => array(),
3295
+ 'kerning' => array(),
3296
+ 'letter-spacing' => array(),
3297
+ 'lighting-color' => array(),
3298
+ 'marker-end' => array(),
3299
+ 'marker-mid' => array(),
3300
+ 'marker-start' => array(),
3301
+ 'mask' => array(),
3302
+ 'opacity' => array(),
3303
+ 'overflow' => array(),
3304
+ 'pointer-events' => array(),
3305
+ 'requiredextensions' => array(),
3306
+ 'requiredfeatures' => array(),
3307
+ 'rx' => array(),
3308
+ 'ry' => array(),
3309
+ 'shape-rendering' => array(),
3310
+ 'sketch:type' => array(),
3311
+ 'stop-color' => array(),
3312
+ 'stop-opacity' => array(),
3313
+ 'stroke' => array(),
3314
+ 'stroke-dasharray' => array(),
3315
+ 'stroke-dashoffset' => array(),
3316
+ 'stroke-linecap' => array(),
3317
+ 'stroke-linejoin' => array(),
3318
+ 'stroke-miterlimit' => array(),
3319
+ 'stroke-opacity' => array(),
3320
+ 'stroke-width' => array(),
3321
+ 'style' => array(
3322
+ 'blacklisted_value_regex' => '!important',
3323
+ ),
3324
+ 'systemlanguage' => array(),
3325
+ 'text-anchor' => array(),
3326
+ 'text-decoration' => array(),
3327
+ 'text-rendering' => array(),
3328
+ 'transform' => array(),
3329
+ 'unicode-bidi' => array(),
3330
+ 'vector-effect' => array(),
3331
+ 'visibility' => array(),
3332
+ 'word-spacing' => array(),
3333
+ 'writing-mode' => array(),
3334
+ 'xml:lang' => array(),
3335
+ 'xml:space' => array(),
3336
+ 'xmlns' => 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(
3352
+ array(
3353
+ 'attr_spec_list' => array(
3354
+ 'alignment-baseline' => array(),
3355
+ 'baseline-shift' => array(),
3356
+ 'clip' => array(),
3357
+ 'clip-path' => array(),
3358
+ 'clip-rule' => array(),
3359
+ 'color' => array(),
3360
+ 'color-interpolation' => array(),
3361
+ 'color-interpolation-filters' => array(),
3362
+ 'color-profile' => array(),
3363
+ 'color-rendering' => array(),
3364
+ 'cursor' => array(),
3365
+ 'direction' => array(),
3366
+ 'display' => array(),
3367
+ 'dominant-baseline' => array(),
3368
+ 'enable-background' => array(),
3369
+ 'fill' => array(),
3370
+ 'fill-opacity' => array(),
3371
+ 'fill-rule' => array(),
3372
+ 'filter' => array(),
3373
+ 'flood-color' => array(),
3374
+ 'flood-opacity' => array(),
3375
+ 'font-family' => array(),
3376
+ 'font-size' => array(),
3377
+ 'font-size-adjust' => array(),
3378
+ 'font-stretch' => array(),
3379
+ 'font-style' => array(),
3380
+ 'font-variant' => array(),
3381
+ 'font-weight' => array(),
3382
+ 'glyph-orientation-horizontal' => array(),
3383
+ 'glyph-orientation-vertical' => array(),
3384
+ 'height' => array(),
3385
+ 'image-rendering' => array(),
3386
+ 'in' => array(),
3387
+ 'kerning' => array(),
3388
+ 'letter-spacing' => array(),
3389
+ 'lighting-color' => array(),
3390
+ 'marker-end' => array(),
3391
+ 'marker-mid' => array(),
3392
+ 'marker-start' => array(),
3393
+ 'mask' => array(),
3394
+ 'opacity' => array(),
3395
+ 'overflow' => array(),
3396
+ 'pointer-events' => array(),
3397
+ 'result' => array(),
3398
+ 'shape-rendering' => array(),
3399
+ 'stop-color' => array(),
3400
+ 'stop-opacity' => array(),
3401
+ 'stroke' => array(),
3402
+ 'stroke-dasharray' => array(),
3403
+ 'stroke-dashoffset' => array(),
3404
+ 'stroke-linecap' => array(),
3405
+ 'stroke-linejoin' => array(),
3406
+ 'stroke-miterlimit' => array(),
3407
+ 'stroke-opacity' => array(),
3408
+ 'stroke-width' => array(),
3409
+ 'style' => array(
3410
+ 'blacklisted_value_regex' => '!important',
3411
+ ),
3412
+ 'text-anchor' => array(),
3413
+ 'text-decoration' => array(),
3414
+ 'text-rendering' => array(),
3415
+ 'type' => array(),
3416
+ 'unicode-bidi' => array(),
3417
+ 'values' => array(),
3418
+ 'vector-effect' => array(),
3419
+ 'visibility' => array(),
3420
+ 'width' => array(),
3421
+ 'word-spacing' => array(),
3422
+ 'writing-mode' => array(),
3423
+ 'x' => array(),
3424
+ 'xml:lang' => array(),
3425
+ 'xml:space' => array(),
3426
+ 'xmlns' => array(),
3427
+ 'xmlns:xlink' => 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(
3437
+ array(
3438
+ 'attr_spec_list' => array(
3439
+ 'alignment-baseline' => array(),
3440
+ 'baseline-shift' => array(),
3441
+ 'clip' => array(),
3442
+ 'clip-path' => array(),
3443
+ 'clip-rule' => array(),
3444
+ 'color' => array(),
3445
+ 'color-interpolation' => array(),
3446
+ 'color-interpolation-filters' => array(),
3447
+ 'color-profile' => array(),
3448
+ 'color-rendering' => array(),
3449
+ 'cursor' => array(),
3450
+ 'direction' => array(),
3451
+ 'display' => array(),
3452
+ 'dominant-baseline' => array(),
3453
+ 'enable-background' => array(),
3454
+ 'fill' => array(),
3455
+ 'fill-opacity' => array(),
3456
+ 'fill-rule' => array(),
3457
+ 'filter' => array(),
3458
+ 'flood-color' => array(),
3459
+ 'flood-opacity' => array(),
3460
+ 'font-family' => array(),
3461
+ 'font-size' => array(),
3462
+ 'font-size-adjust' => array(),
3463
+ 'font-stretch' => array(),
3464
+ 'font-style' => array(),
3465
+ 'font-variant' => array(),
3466
+ 'font-weight' => array(),
3467
+ 'glyph-orientation-horizontal' => array(),
3468
+ 'glyph-orientation-vertical' => array(),
3469
+ 'height' => array(),
3470
+ 'image-rendering' => array(),
3471
+ 'in' => array(),
3472
+ 'in2' => array(),
3473
+ 'k1' => array(),
3474
+ 'k2' => array(),
3475
+ 'k3' => array(),
3476
+ 'k4' => array(),
3477
+ 'kerning' => array(),
3478
+ 'letter-spacing' => array(),
3479
+ 'lighting-color' => array(),
3480
+ 'marker-end' => array(),
3481
+ 'marker-mid' => array(),
3482
+ 'marker-start' => array(),
3483
+ 'mask' => array(),
3484
+ 'opacity' => array(),
3485
+ 'operator' => array(),
3486
+ 'overflow' => array(),
3487
+ 'pointer-events' => array(),
3488
+ 'result' => array(),
3489
+ 'shape-rendering' => array(),
3490
+ 'stop-color' => array(),
3491
+ 'stop-opacity' => array(),
3492
+ 'stroke' => array(),
3493
+ 'stroke-dasharray' => array(),
3494
+ 'stroke-dashoffset' => array(),
3495
+ 'stroke-linecap' => array(),
3496
+ 'stroke-linejoin' => array(),
3497
+ 'stroke-miterlimit' => array(),
3498
+ 'stroke-opacity' => array(),
3499
+ 'stroke-width' => array(),
3500
+ 'style' => array(
3501
+ 'blacklisted_value_regex' => '!important',
3502
+ ),
3503
+ 'text-anchor' => array(),
3504
+ 'text-decoration' => array(),
3505
+ 'text-rendering' => array(),
3506
+ 'unicode-bidi' => array(),
3507
+ 'vector-effect' => array(),
3508
+ 'visibility' => array(),
3509
+ 'width' => array(),
3510
+ 'word-spacing' => array(),
3511
+ 'writing-mode' => array(),
3512
+ 'x' => array(),
3513
+ 'xml:lang' => array(),
3514
+ 'xml:space' => array(),
3515
+ 'xmlns' => array(),
3516
+ 'xmlns:xlink' => 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(
3526
+ array(
3527
+ 'attr_spec_list' => array(
3528
+ 'alignment-baseline' => array(),
3529
+ 'baseline-shift' => array(),
3530
+ 'clip' => array(),
3531
+ 'clip-path' => array(),
3532
+ 'clip-rule' => array(),
3533
+ 'color' => array(),
3534
+ 'color-interpolation' => array(),
3535
+ 'color-interpolation-filters' => array(),
3536
+ 'color-profile' => array(),
3537
+ 'color-rendering' => array(),
3538
+ 'cursor' => array(),
3539
+ 'direction' => array(),
3540
+ 'display' => array(),
3541
+ 'dominant-baseline' => array(),
3542
+ 'enable-background' => array(),
3543
+ 'fill' => array(),
3544
+ 'fill-opacity' => array(),
3545
+ 'fill-rule' => array(),
3546
+ 'filter' => array(),
3547
+ 'flood-color' => array(),
3548
+ 'flood-opacity' => array(),
3549
+ 'font-family' => array(),
3550
+ 'font-size' => array(),
3551
+ 'font-size-adjust' => array(),
3552
+ 'font-stretch' => array(),
3553
+ 'font-style' => array(),
3554
+ 'font-variant' => array(),
3555
+ 'font-weight' => array(),
3556
+ 'glyph-orientation-horizontal' => array(),
3557
+ 'glyph-orientation-vertical' => array(),
3558
+ 'height' => array(),
3559
+ 'image-rendering' => array(),
3560
+ 'kerning' => array(),
3561
+ 'letter-spacing' => array(),
3562
+ 'lighting-color' => array(),
3563
+ 'marker-end' => array(),
3564
+ 'marker-mid' => array(),
3565
+ 'marker-start' => array(),
3566
+ 'mask' => array(),
3567
+ 'opacity' => array(),
3568
+ 'overflow' => array(),
3569
+ 'pointer-events' => array(),
3570
+ 'result' => array(),
3571
+ 'shape-rendering' => array(),
3572
+ 'stop-color' => array(),
3573
+ 'stop-opacity' => array(),
3574
+ 'stroke' => array(),
3575
+ 'stroke-dasharray' => array(),
3576
+ 'stroke-dashoffset' => array(),
3577
+ 'stroke-linecap' => array(),
3578
+ 'stroke-linejoin' => array(),
3579
+ 'stroke-miterlimit' => array(),
3580
+ 'stroke-opacity' => array(),
3581
+ 'stroke-width' => array(),
3582
+ 'style' => array(
3583
+ 'blacklisted_value_regex' => '!important',
3584
+ ),
3585
+ 'text-anchor' => array(),
3586
+ 'text-decoration' => array(),
3587
+ 'text-rendering' => array(),
3588
+ 'unicode-bidi' => array(),
3589
+ 'vector-effect' => array(),
3590
+ 'visibility' => array(),
3591
+ 'width' => array(),
3592
+ 'word-spacing' => array(),
3593
+ 'writing-mode' => array(),
3594
+ 'x' => array(),
3595
+ 'xml:lang' => array(),
3596
+ 'xml:space' => array(),
3597
+ 'xmlns' => array(),
3598
+ 'xmlns:xlink' => 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(
3608
+ array(
3609
+ 'attr_spec_list' => array(
3610
+ 'alignment-baseline' => array(),
3611
+ 'baseline-shift' => array(),
3612
+ 'clip' => array(),
3613
+ 'clip-path' => array(),
3614
+ 'clip-rule' => array(),
3615
+ 'color' => array(),
3616
+ 'color-interpolation' => array(),
3617
+ 'color-interpolation-filters' => array(),
3618
+ 'color-profile' => array(),
3619
+ 'color-rendering' => array(),
3620
+ 'cursor' => array(),
3621
+ 'direction' => array(),
3622
+ 'display' => array(),
3623
+ 'dominant-baseline' => array(),
3624
+ 'edgemode' => array(),
3625
+ 'enable-background' => array(),
3626
+ 'fill' => array(),
3627
+ 'fill-opacity' => array(),
3628
+ 'fill-rule' => array(),
3629
+ 'filter' => array(),
3630
+ 'flood-color' => array(),
3631
+ 'flood-opacity' => array(),
3632
+ 'font-family' => array(),
3633
+ 'font-size' => array(),
3634
+ 'font-size-adjust' => array(),
3635
+ 'font-stretch' => array(),
3636
+ 'font-style' => array(),
3637
+ 'font-variant' => array(),
3638
+ 'font-weight' => array(),
3639
+ 'glyph-orientation-horizontal' => array(),
3640
+ 'glyph-orientation-vertical' => array(),
3641
+ 'height' => array(),
3642
+ 'image-rendering' => array(),
3643
+ 'in' => array(),
3644
+ 'kerning' => array(),
3645
+ 'letter-spacing' => array(),
3646
+ 'lighting-color' => array(),
3647
+ 'marker-end' => array(),
3648
+ 'marker-mid' => array(),
3649
+ 'marker-start' => array(),
3650
+ 'mask' => array(),
3651
+ 'opacity' => array(),
3652
+ 'overflow' => array(),
3653
+ 'pointer-events' => array(),
3654
+ 'result' => array(),
3655
+ 'shape-rendering' => array(),
3656
+ 'stddeviation' => array(),
3657
+ 'stop-color' => array(),
3658
+ 'stop-opacity' => array(),
3659
+ 'stroke' => array(),
3660
+ 'stroke-dasharray' => array(),
3661
+ 'stroke-dashoffset' => array(),
3662
+ 'stroke-linecap' => array(),
3663
+ 'stroke-linejoin' => array(),
3664
+ 'stroke-miterlimit' => array(),
3665
+ 'stroke-opacity' => array(),
3666
+ 'stroke-width' => array(),
3667
+ 'style' => array(
3668
+ 'blacklisted_value_regex' => '!important',
3669
+ ),
3670
+ 'text-anchor' => array(),
3671
+ 'text-decoration' => array(),
3672
+ 'text-rendering' => array(),
3673
+ 'unicode-bidi' => array(),
3674
+ 'vector-effect' => array(),
3675
+ 'visibility' => array(),
3676
+ 'width' => array(),
3677
+ 'word-spacing' => array(),
3678
+ 'writing-mode' => array(),
3679
+ 'x' => array(),
3680
+ 'xml:lang' => array(),
3681
+ 'xml:space' => array(),
3682
+ 'xmlns' => array(),
3683
+ 'xmlns:xlink' => 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(
3693
+ array(
3694
+ 'attr_spec_list' => array(
3695
+ 'alignment-baseline' => array(),
3696
+ 'baseline-shift' => array(),
3697
+ 'clip' => array(),
3698
+ 'clip-path' => array(),
3699
+ 'clip-rule' => array(),
3700
+ 'color' => array(),
3701
+ 'color-interpolation' => array(),
3702
+ 'color-interpolation-filters' => array(),
3703
+ 'color-profile' => array(),
3704
+ 'color-rendering' => array(),
3705
+ 'cursor' => array(),
3706
+ 'direction' => array(),
3707
+ 'display' => array(),
3708
+ 'dominant-baseline' => array(),
3709
+ 'enable-background' => array(),
3710
+ 'fill' => array(),
3711
+ 'fill-opacity' => array(),
3712
+ 'fill-rule' => array(),
3713
+ 'filter' => array(),
3714
+ 'flood-color' => array(),
3715
+ 'flood-opacity' => array(),
3716
+ 'font-family' => array(),
3717
+ 'font-size' => array(),
3718
+ 'font-size-adjust' => array(),
3719
+ 'font-stretch' => array(),
3720
+ 'font-style' => array(),
3721
+ 'font-variant' => array(),
3722
+ 'font-weight' => array(),
3723
+ 'glyph-orientation-horizontal' => array(),
3724
+ 'glyph-orientation-vertical' => array(),
3725
+ 'height' => array(),
3726
+ 'image-rendering' => array(),
3727
+ 'kerning' => array(),
3728
+ 'letter-spacing' => array(),
3729
+ 'lighting-color' => array(),
3730
+ 'marker-end' => array(),
3731
+ 'marker-mid' => array(),
3732
+ 'marker-start' => array(),
3733
+ 'mask' => array(),
3734
+ 'opacity' => array(),
3735
+ 'overflow' => array(),
3736
+ 'pointer-events' => array(),
3737
+ 'result' => array(),
3738
+ 'shape-rendering' => array(),
3739
+ 'stop-color' => array(),
3740
+ 'stop-opacity' => array(),
3741
+ 'stroke' => array(),
3742
+ 'stroke-dasharray' => array(),
3743
+ 'stroke-dashoffset' => array(),
3744
+ 'stroke-linecap' => array(),
3745
+ 'stroke-linejoin' => array(),
3746
+ 'stroke-miterlimit' => array(),
3747
+ 'stroke-opacity' => array(),
3748
+ 'stroke-width' => array(),
3749
+ 'style' => array(
3750
+ 'blacklisted_value_regex' => '!important',
3751
+ ),
3752
+ 'text-anchor' => array(),
3753
+ 'text-decoration' => array(),
3754
+ 'text-rendering' => array(),
3755
+ 'unicode-bidi' => array(),
3756
+ 'vector-effect' => array(),
3757
+ 'visibility' => array(),
3758
+ 'width' => array(),
3759
+ 'word-spacing' => array(),
3760
+ 'writing-mode' => array(),
3761
+ 'x' => array(),
3762
+ 'xml:lang' => array(),
3763
+ 'xml:space' => array(),
3764
+ 'xmlns' => array(),
3765
+ 'xmlns:xlink' => 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(
3775
+ array(
3776
+ 'attr_spec_list' => array(
3777
+ 'in' => array(),
3778
+ 'style' => array(
3779
+ 'blacklisted_value_regex' => '!important',
3780
+ ),
3781
+ 'xml:lang' => array(),
3782
+ 'xml:space' => array(),
3783
+ 'xmlns' => 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(
3793
+ array(
3794
+ 'attr_spec_list' => array(
3795
+ 'alignment-baseline' => array(),
3796
+ 'baseline-shift' => array(),
3797
+ 'clip' => array(),
3798
+ 'clip-path' => array(),
3799
+ 'clip-rule' => array(),
3800
+ 'color' => array(),
3801
+ 'color-interpolation' => array(),
3802
+ 'color-interpolation-filters' => array(),
3803
+ 'color-profile' => array(),
3804
+ 'color-rendering' => array(),
3805
+ 'cursor' => array(),
3806
+ 'direction' => array(),
3807
+ 'display' => array(),
3808
+ 'dominant-baseline' => array(),
3809
+ 'dx' => array(),
3810
+ 'dy' => array(),
3811
+ 'enable-background' => array(),
3812
+ 'fill' => array(),
3813
+ 'fill-opacity' => array(),
3814
+ 'fill-rule' => array(),
3815
+ 'filter' => array(),
3816
+ 'flood-color' => array(),
3817
+ 'flood-opacity' => array(),
3818
+ 'font-family' => array(),
3819
+ 'font-size' => array(),
3820
+ 'font-size-adjust' => array(),
3821
+ 'font-stretch' => array(),
3822
+ 'font-style' => array(),
3823
+ 'font-variant' => array(),
3824
+ 'font-weight' => array(),
3825
+ 'glyph-orientation-horizontal' => array(),
3826
+ 'glyph-orientation-vertical' => array(),
3827
+ 'height' => array(),
3828
+ 'image-rendering' => array(),
3829
+ 'in' => array(),
3830
+ 'kerning' => array(),
3831
+ 'letter-spacing' => array(),
3832
+ 'lighting-color' => array(),
3833
+ 'marker-end' => array(),
3834
+ 'marker-mid' => array(),
3835
+ 'marker-start' => array(),
3836
+ 'mask' => array(),
3837
+ 'opacity' => array(),
3838
+ 'overflow' => array(),
3839
+ 'pointer-events' => array(),
3840
+ 'result' => array(),
3841
+ 'shape-rendering' => array(),
3842
+ 'stop-color' => array(),
3843
+ 'stop-opacity' => array(),
3844
+ 'stroke' => array(),
3845
+ 'stroke-dasharray' => array(),
3846
+ 'stroke-dashoffset' => array(),
3847
+ 'stroke-linecap' => array(),
3848
+ 'stroke-linejoin' => array(),
3849
+ 'stroke-miterlimit' => array(),
3850
+ 'stroke-opacity' => array(),
3851
+ 'stroke-width' => array(),
3852
+ 'style' => array(
3853
+ 'blacklisted_value_regex' => '!important',
3854
+ ),
3855
+ 'text-anchor' => array(),
3856
+ 'text-decoration' => array(),
3857
+ 'text-rendering' => array(),
3858
+ 'unicode-bidi' => array(),
3859
+ 'vector-effect' => array(),
3860
+ 'visibility' => array(),
3861
+ 'width' => array(),
3862
+ 'word-spacing' => array(),
3863
+ 'writing-mode' => array(),
3864
+ 'x' => array(),
3865
+ 'xml:lang' => array(),
3866
+ 'xml:space' => array(),
3867
+ 'xmlns' => array(),
3868
+ 'xmlns:xlink' => 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(
3878
+ array(
3879
+ 'attr_spec_list' => 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(
3902
+ array(
3903
+ 'attr_spec_list' => array(
3904
+ 'alignment-baseline' => array(),
3905
+ 'baseline-shift' => array(),
3906
+ 'clip' => array(),
3907
+ 'clip-path' => array(),
3908
+ 'clip-rule' => array(),
3909
+ 'color' => array(),
3910
+ 'color-interpolation' => array(),
3911
+ 'color-interpolation-filters' => array(),
3912
+ 'color-profile' => array(),
3913
+ 'color-rendering' => array(),
3914
+ 'cursor' => array(),
3915
+ 'direction' => array(),
3916
+ 'display' => array(),
3917
+ 'dominant-baseline' => array(),
3918
+ 'enable-background' => array(),
3919
+ 'externalresourcesrequired' => array(),
3920
+ 'fill' => array(),
3921
+ 'fill-opacity' => array(),
3922
+ 'fill-rule' => array(),
3923
+ 'filter' => array(),
3924
+ 'filterres' => array(),
3925
+ 'filterunits' => array(),
3926
+ 'flood-color' => array(),
3927
+ 'flood-opacity' => array(),
3928
+ 'font-family' => array(),
3929
+ 'font-size' => array(),
3930
+ 'font-size-adjust' => array(),
3931
+ 'font-stretch' => array(),
3932
+ 'font-style' => array(),
3933
+ 'font-variant' => array(),
3934
+ 'font-weight' => array(),
3935
+ 'glyph-orientation-horizontal' => array(),
3936
+ 'glyph-orientation-vertical' => array(),
3937
+ 'height' => array(),
3938
+ 'image-rendering' => array(),
3939
+ 'kerning' => array(),
3940
+ 'letter-spacing' => array(),
3941
+ 'lighting-color' => array(),
3942
+ 'marker-end' => array(),
3943
+ 'marker-mid' => array(),
3944
+ 'marker-start' => array(),
3945
+ 'mask' => array(),
3946
+ 'opacity' => array(),
3947
+ 'overflow' => array(),
3948
+ 'pointer-events' => array(),
3949
+ 'primitiveunits' => array(),
3950
+ 'shape-rendering' => array(),
3951
+ 'stop-color' => array(),
3952
+ 'stop-opacity' => array(),
3953
+ 'stroke' => array(),
3954
+ 'stroke-dasharray' => array(),
3955
+ 'stroke-dashoffset' => array(),
3956
+ 'stroke-linecap' => array(),
3957
+ 'stroke-linejoin' => array(),
3958
+ 'stroke-miterlimit' => array(),
3959
+ 'stroke-opacity' => array(),
3960
+ 'stroke-width' => array(),
3961
+ 'style' => array(
3962
+ 'blacklisted_value_regex' => '!important',
3963
+ ),
3964
+ 'text-anchor' => array(),
3965
+ 'text-decoration' => array(),
3966
+ 'text-rendering' => array(),
3967
+ 'unicode-bidi' => array(),
3968
+ 'vector-effect' => array(),
3969
+ 'visibility' => array(),
3970
+ 'width' => array(),
3971
+ 'word-spacing' => array(),
3972
+ 'writing-mode' => array(),
3973
+ 'x' => array(),
3974
+ 'xlink:actuate' => array(),
3975
+ 'xlink:arcrole' => array(),
3976
+ 'xlink:href' => 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(),
3990
+ 'xlink:show' => array(),
3991
+ 'xlink:title' => array(),
3992
+ 'xlink:type' => array(),
3993
+ 'xml:lang' => array(),
3994
+ 'xml:space' => array(),
3995
+ 'xmlns' => array(),
3996
+ 'xmlns:xlink' => 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(
4012
+ array(
4013
+ 'attr_spec_list' => array(
4014
+ 'alignment-baseline' => array(),
4015
+ 'baseline-shift' => array(),
4016
+ 'clip' => array(),
4017
+ 'clip-path' => array(),
4018
+ 'clip-rule' => array(),
4019
+ 'color' => array(),
4020
+ 'color-interpolation' => array(),
4021
+ 'color-interpolation-filters' => array(),
4022
+ 'color-profile' => array(),
4023
+ 'color-rendering' => array(),
4024
+ 'cursor' => array(),
4025
+ 'direction' => array(),
4026
+ 'display' => array(),
4027
+ 'dominant-baseline' => array(),
4028
+ 'enable-background' => array(),
4029
+ 'externalresourcesrequired' => array(),
4030
+ 'fill' => array(),
4031
+ 'fill-opacity' => array(),
4032
+ 'fill-rule' => array(),
4033
+ 'filter' => array(),
4034
+ 'flood-color' => array(),
4035
+ 'flood-opacity' => array(),
4036
+ 'font-family' => array(),
4037
+ 'font-size' => array(),
4038
+ 'font-size-adjust' => array(),
4039
+ 'font-stretch' => array(),
4040
+ 'font-style' => array(),
4041
+ 'font-variant' => array(),
4042
+ 'font-weight' => array(),
4043
+ 'glyph-orientation-horizontal' => array(),
4044
+ 'glyph-orientation-vertical' => array(),
4045
+ 'height' => array(),
4046
+ 'image-rendering' => array(),
4047
+ 'kerning' => array(),
4048
+ 'letter-spacing' => array(),
4049
+ 'lighting-color' => array(),
4050
+ 'marker-end' => array(),
4051
+ 'marker-mid' => array(),
4052
+ 'marker-start' => array(),
4053
+ 'mask' => array(),
4054
+ 'opacity' => array(),
4055
+ 'overflow' => array(),
4056
+ 'pointer-events' => array(),
4057
+ 'requiredextensions' => array(),
4058
+ 'requiredfeatures' => array(),
4059
+ 'shape-rendering' => array(),
4060
+ 'stop-color' => array(),
4061
+ 'stop-opacity' => array(),
4062
+ 'stroke' => array(),
4063
+ 'stroke-dasharray' => array(),
4064
+ 'stroke-dashoffset' => array(),
4065
+ 'stroke-linecap' => array(),
4066
+ 'stroke-linejoin' => array(),
4067
+ 'stroke-miterlimit' => array(),
4068
+ 'stroke-opacity' => array(),
4069
+ 'stroke-width' => array(),
4070
+ 'style' => array(
4071
+ 'blacklisted_value_regex' => '!important',
4072
+ ),
4073
+ 'systemlanguage' => array(),
4074
+ 'text-anchor' => array(),
4075
+ 'text-decoration' => array(),
4076
+ 'text-rendering' => array(),
4077
+ 'transform' => array(),
4078
+ 'unicode-bidi' => array(),
4079
+ 'vector-effect' => array(),
4080
+ 'visibility' => array(),
4081
+ 'width' => array(),
4082
+ 'word-spacing' => array(),
4083
+ 'writing-mode' => array(),
4084
+ 'x' => array(),
4085
+ 'xml:lang' => array(),
4086
+ 'xml:space' => array(),
4087
+ 'xmlns' => array(),
4088
+ 'xmlns:xlink' => 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(
4098
+ array(
4099
+ 'attr_spec_list' => array(
4100
+ 'accept' => array(),
4101
+ 'accept-charset' => 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(),
4122
+ 'custom-validation-reporting' => array(
4123
+ 'value_regex' => '(show-first-on-submit|show-all-on-submit|as-you-go|interact-and-submit)',
4124
+ ),
4125
+ 'enctype' => array(),
4126
+ 'method' => array(
4127
+ 'value_casei' => 'get',
4128
+ ),
4129
+ 'name' => array(),
4130
+ 'novalidate' => array(),
4131
+ 'target' => array(
4132
+ 'mandatory' => true,
4133
+ 'value_regex_casei' => '(_blank|_top)',
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(
4157
+ 'accept' => array(),
4158
+ 'accept-charset' => 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(),
4170
+ 'custom-validation-reporting' => array(
4171
+ 'value_regex' => '(show-first-on-submit|show-all-on-submit|as-you-go)',
4172
+ ),
4173
+ 'enctype' => array(),
4174
+ 'method' => array(
4175
+ 'dispatch_key' => 2,
4176
+ 'mandatory' => true,
4177
+ 'value_casei' => 'post',
4178
+ ),
4179
+ 'name' => array(),
4180
+ 'novalidate' => array(),
4181
+ 'target' => array(
4182
+ 'mandatory' => true,
4183
+ 'value_regex_casei' => '(_blank|_top)',
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(
4207
+ array(
4208
+ 'attr_spec_list' => array(
4209
+ 'alignment-baseline' => array(),
4210
+ 'baseline-shift' => array(),
4211
+ 'clip' => array(),
4212
+ 'clip-path' => array(),
4213
+ 'clip-rule' => array(),
4214
+ 'color' => array(),
4215
+ 'color-interpolation' => array(),
4216
+ 'color-interpolation-filters' => array(),
4217
+ 'color-profile' => array(),
4218
+ 'color-rendering' => array(),
4219
+ 'cursor' => array(),
4220
+ 'direction' => array(),
4221
+ 'display' => array(),
4222
+ 'dominant-baseline' => array(),
4223
+ 'enable-background' => array(),
4224
+ 'externalresourcesrequired' => array(),
4225
+ 'fill' => array(),
4226
+ 'fill-opacity' => array(),
4227
+ 'fill-rule' => array(),
4228
+ 'filter' => array(),
4229
+ 'flood-color' => array(),
4230
+ 'flood-opacity' => array(),
4231
+ 'font-family' => array(),
4232
+ 'font-size' => array(),
4233
+ 'font-size-adjust' => array(),
4234
+ 'font-stretch' => array(),
4235
+ 'font-style' => array(),
4236
+ 'font-variant' => array(),
4237
+ 'font-weight' => array(),
4238
+ 'glyph-orientation-horizontal' => array(),
4239
+ 'glyph-orientation-vertical' => array(),
4240
+ 'image-rendering' => array(),
4241
+ 'kerning' => array(),
4242
+ 'letter-spacing' => array(),
4243
+ 'lighting-color' => array(),
4244
+ 'marker-end' => array(),
4245
+ 'marker-mid' => array(),
4246
+ 'marker-start' => array(),
4247
+ 'mask' => array(),
4248
+ 'opacity' => array(),
4249
+ 'overflow' => array(),
4250
+ 'pointer-events' => array(),
4251
+ 'requiredextensions' => array(),
4252
+ 'requiredfeatures' => array(),
4253
+ 'shape-rendering' => array(),
4254
+ 'stop-color' => array(),
4255
+ 'stop-opacity' => array(),
4256
+ 'stroke' => array(),
4257
+ 'stroke-dasharray' => array(),
4258
+ 'stroke-dashoffset' => array(),
4259
+ 'stroke-linecap' => array(),
4260
+ 'stroke-linejoin' => array(),
4261
+ 'stroke-miterlimit' => array(),
4262
+ 'stroke-opacity' => array(),
4263
+ 'stroke-width' => array(),
4264
+ 'style' => array(
4265
+ 'blacklisted_value_regex' => '!important',
4266
+ ),
4267
+ 'systemlanguage' => array(),
4268
+ 'text-anchor' => array(),
4269
+ 'text-decoration' => array(),
4270
+ 'text-rendering' => array(),
4271
+ 'transform' => array(),
4272
+ 'unicode-bidi' => array(),
4273
+ 'vector-effect' => array(),
4274
+ 'visibility' => array(),
4275
+ 'word-spacing' => array(),
4276
+ 'writing-mode' => array(),
4277
+ 'xml:lang' => array(),
4278
+ 'xml:space' => array(),
4279
+ 'xmlns' => 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(
4289
+ array(
4290
+ 'attr_spec_list' => array(
4291
+ 'alignment-baseline' => array(),
4292
+ 'arabic-form' => array(),
4293
+ 'baseline-shift' => array(),
4294
+ 'clip' => array(),
4295
+ 'clip-path' => array(),
4296
+ 'clip-rule' => array(),
4297
+ 'color' => array(),
4298
+ 'color-interpolation' => array(),
4299
+ 'color-interpolation-filters' => array(),
4300
+ 'color-profile' => array(),
4301
+ 'color-rendering' => array(),
4302
+ 'cursor' => array(),
4303
+ 'd' => array(),
4304
+ 'direction' => array(),
4305
+ 'display' => array(),
4306
+ 'dominant-baseline' => array(),
4307
+ 'enable-background' => array(),
4308
+ 'fill' => array(),
4309
+ 'fill-opacity' => array(),
4310
+ 'fill-rule' => array(),
4311
+ 'filter' => array(),
4312
+ 'flood-color' => array(),
4313
+ 'flood-opacity' => array(),
4314
+ 'font-family' => array(),
4315
+ 'font-size' => array(),
4316
+ 'font-size-adjust' => array(),
4317
+ 'font-stretch' => array(),
4318
+ 'font-style' => array(),
4319
+ 'font-variant' => array(),
4320
+ 'font-weight' => array(),
4321
+ 'glyph-name' => array(),
4322
+ 'glyph-orientation-horizontal' => array(),
4323
+ 'glyph-orientation-vertical' => array(),
4324
+ 'horiz-adv-x' => array(),
4325
+ 'image-rendering' => array(),
4326
+ 'kerning' => array(),
4327
+ 'letter-spacing' => array(),
4328
+ 'lighting-color' => array(),
4329
+ 'marker-end' => array(),
4330
+ 'marker-mid' => array(),
4331
+ 'marker-start' => array(),
4332
+ 'mask' => array(),
4333
+ 'opacity' => array(),
4334
+ 'orientation' => array(),
4335
+ 'overflow' => array(),
4336
+ 'pointer-events' => array(),
4337
+ 'shape-rendering' => array(),
4338
+ 'stop-color' => array(),
4339
+ 'stop-opacity' => array(),
4340
+ 'stroke' => array(),
4341
+ 'stroke-dasharray' => array(),
4342
+ 'stroke-dashoffset' => array(),
4343
+ 'stroke-linecap' => array(),
4344
+ 'stroke-linejoin' => array(),
4345
+ 'stroke-miterlimit' => array(),
4346
+ 'stroke-opacity' => array(),
4347
+ 'stroke-width' => array(),
4348
+ 'style' => array(
4349
+ 'blacklisted_value_regex' => '!important',
4350
+ ),
4351
+ 'text-anchor' => array(),
4352
+ 'text-decoration' => array(),
4353
+ 'text-rendering' => array(),
4354
+ 'unicode' => array(),
4355
+ 'unicode-bidi' => array(),
4356
+ 'vector-effect' => array(),
4357
+ 'vert-adv-y' => array(),
4358
+ 'vert-origin-x' => array(),
4359
+ 'vert-origin-y' => array(),
4360
+ 'visibility' => array(),
4361
+ 'word-spacing' => array(),
4362
+ 'writing-mode' => array(),
4363
+ 'xml:lang' => array(),
4364
+ 'xml:space' => array(),
4365
+ 'xmlns' => 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(
4375
+ array(
4376
+ 'attr_spec_list' => array(
4377
+ 'alignment-baseline' => array(),
4378
+ 'baseline-shift' => array(),
4379
+ 'clip' => array(),
4380
+ 'clip-path' => array(),
4381
+ 'clip-rule' => array(),
4382
+ 'color' => array(),
4383
+ 'color-interpolation' => array(),
4384
+ 'color-interpolation-filters' => array(),
4385
+ 'color-profile' => array(),
4386
+ 'color-rendering' => array(),
4387
+ 'cursor' => array(),
4388
+ 'direction' => array(),
4389
+ 'display' => array(),
4390
+ 'dominant-baseline' => array(),
4391
+ 'dx' => array(),
4392
+ 'dy' => array(),
4393
+ 'enable-background' => array(),
4394
+ 'fill' => array(),
4395
+ 'fill-opacity' => array(),
4396
+ 'fill-rule' => array(),
4397
+ 'filter' => array(),
4398
+ 'flood-color' => array(),
4399
+ 'flood-opacity' => array(),
4400
+ 'font-family' => array(),
4401
+ 'font-size' => array(),
4402
+ 'font-size-adjust' => array(),
4403
+ 'font-stretch' => array(),
4404
+ 'font-style' => array(),
4405
+ 'font-variant' => array(),
4406
+ 'font-weight' => array(),
4407
+ 'format' => array(),
4408
+ 'glyph-orientation-horizontal' => array(),
4409
+ 'glyph-orientation-vertical' => array(),
4410
+ 'glyphref' => array(),
4411
+ 'image-rendering' => array(),
4412
+ 'kerning' => array(),
4413
+ 'letter-spacing' => array(),
4414
+ 'lighting-color' => array(),
4415
+ 'marker-end' => array(),
4416
+ 'marker-mid' => array(),
4417
+ 'marker-start' => array(),
4418
+ 'mask' => array(),
4419
+ 'opacity' => array(),
4420
+ 'overflow' => array(),
4421
+ 'pointer-events' => array(),
4422
+ 'shape-rendering' => array(),
4423
+ 'stop-color' => array(),
4424
+ 'stop-opacity' => array(),
4425
+ 'stroke' => array(),
4426
+ 'stroke-dasharray' => array(),
4427
+ 'stroke-dashoffset' => array(),
4428
+ 'stroke-linecap' => array(),
4429
+ 'stroke-linejoin' => array(),
4430
+ 'stroke-miterlimit' => array(),
4431
+ 'stroke-opacity' => array(),
4432
+ 'stroke-width' => array(),
4433
+ 'style' => array(
4434
+ 'blacklisted_value_regex' => '!important',
4435
+ ),
4436
+ 'text-anchor' => array(),
4437
+ 'text-decoration' => array(),
4438
+ 'text-rendering' => array(),
4439
+ 'unicode-bidi' => array(),
4440
+ 'vector-effect' => array(),
4441
+ 'visibility' => array(),
4442
+ 'word-spacing' => array(),
4443
+ 'writing-mode' => array(),
4444
+ 'x' => array(),
4445
+ 'xlink:actuate' => array(),
4446
+ 'xlink:arcrole' => array(),
4447
+ 'xlink:href' => 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(),
4461
+ 'xlink:show' => array(),
4462
+ 'xlink:title' => array(),
4463
+ 'xlink:type' => array(),
4464
+ 'xml:lang' => array(),
4465
+ 'xml:space' => array(),
4466
+ 'xmlns' => array(),
4467
+ 'xmlns:xlink' => 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(
4477
+ array(
4478
+ 'attr_spec_list' => array(
4479
+ 'align' => array(),
4480
+ ),
4481
+ 'tag_spec' => array(),
4482
+ ),
4483
+ ),
4484
+ 'h2' => array(
4485
+ array(
4486
+ 'attr_spec_list' => array(
4487
+ 'align' => array(),
4488
+ ),
4489
+ 'tag_spec' => array(),
4490
+ ),
4491
+ ),
4492
+ 'h3' => array(
4493
+ array(
4494
+ 'attr_spec_list' => array(
4495
+ 'align' => array(),
4496
+ ),
4497
+ 'tag_spec' => array(),
4498
+ ),
4499
+ ),
4500
+ 'h4' => array(
4501
+ array(
4502
+ 'attr_spec_list' => array(
4503
+ 'align' => array(),
4504
+ ),
4505
+ 'tag_spec' => array(),
4506
+ ),
4507
+ ),
4508
+ 'h5' => array(
4509
+ array(
4510
+ 'attr_spec_list' => array(
4511
+ 'align' => array(),
4512
+ ),
4513
+ 'tag_spec' => array(),
4514
+ ),
4515
+ ),
4516
+ 'h6' => array(
4517
+ array(
4518
+ 'attr_spec_list' => 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(
4548
+ array(
4549
+ 'attr_spec_list' => array(
4550
+ 'g1' => array(),
4551
+ 'g2' => array(),
4552
+ 'k' => array(),
4553
+ 'style' => array(
4554
+ 'blacklisted_value_regex' => '!important',
4555
+ ),
4556
+ 'u1' => array(),
4557
+ 'u2' => array(),
4558
+ 'xml:lang' => array(),
4559
+ 'xml:space' => array(),
4560
+ 'xmlns' => 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(
4602
+ array(
4603
+ 'attr_spec_list' => array(
4604
+ 'frameborder' => array(
4605
+ 'value_regex' => '0|1',
4606
+ ),
4607
+ 'height' => array(),
4608
+ 'name' => array(),
4609
+ 'referrerpolicy' => array(),
4610
+ 'resizable' => array(
4611
+ 'value' => '',
4612
+ ),
4613
+ 'sandbox' => array(),
4614
+ 'scrolling' => array(
4615
+ 'value_regex' => 'auto|yes|no',
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(
4638
+ array(
4639
+ 'attr_spec_list' => array(
4640
+ 'alignment-baseline' => array(),
4641
+ 'baseline-shift' => array(),
4642
+ 'clip' => array(),
4643
+ 'clip-path' => array(),
4644
+ 'clip-rule' => array(),
4645
+ 'color' => array(),
4646
+ 'color-interpolation' => array(),
4647
+ 'color-interpolation-filters' => array(),
4648
+ 'color-profile' => array(),
4649
+ 'color-rendering' => array(),
4650
+ 'cursor' => array(),
4651
+ 'direction' => array(),
4652
+ 'display' => array(),
4653
+ 'dominant-baseline' => array(),
4654
+ 'enable-background' => array(),
4655
+ 'externalresourcesrequired' => array(),
4656
+ 'fill' => array(),
4657
+ 'fill-opacity' => array(),
4658
+ 'fill-rule' => array(),
4659
+ 'filter' => array(),
4660
+ 'flood-color' => array(),
4661
+ 'flood-opacity' => array(),
4662
+ 'font-family' => array(),
4663
+ 'font-size' => array(),
4664
+ 'font-size-adjust' => array(),
4665
+ 'font-stretch' => array(),
4666
+ 'font-style' => array(),
4667
+ 'font-variant' => array(),
4668
+ 'font-weight' => array(),
4669
+ 'glyph-orientation-horizontal' => array(),
4670
+ 'glyph-orientation-vertical' => array(),
4671
+ 'height' => array(),
4672
+ 'image-rendering' => array(),
4673
+ 'kerning' => array(),
4674
+ 'letter-spacing' => array(),
4675
+ 'lighting-color' => array(),
4676
+ 'marker-end' => array(),
4677
+ 'marker-mid' => array(),
4678
+ 'marker-start' => array(),
4679
+ 'mask' => array(),
4680
+ 'opacity' => array(),
4681
+ 'overflow' => array(),
4682
+ 'pointer-events' => array(),
4683
+ 'preserveaspectratio' => array(),
4684
+ 'requiredextensions' => array(),
4685
+ 'requiredfeatures' => array(),
4686
+ 'shape-rendering' => array(),
4687
+ 'stop-color' => array(),
4688
+ 'stop-opacity' => array(),
4689
+ 'stroke' => array(),
4690
+ 'stroke-dasharray' => array(),
4691
+ 'stroke-dashoffset' => array(),
4692
+ 'stroke-linecap' => array(),
4693
+ 'stroke-linejoin' => array(),
4694
+ 'stroke-miterlimit' => array(),
4695
+ 'stroke-opacity' => array(),
4696
+ 'stroke-width' => array(),
4697
+ 'style' => array(
4698
+ 'blacklisted_value_regex' => '!important',
4699
+ ),
4700
+ 'systemlanguage' => array(),
4701
+ 'text-anchor' => array(),
4702
+ 'text-decoration' => array(),
4703
+ 'text-rendering' => array(),
4704
+ 'transform' => array(),
4705
+ 'unicode-bidi' => array(),
4706
+ 'vector-effect' => array(),
4707
+ 'visibility' => array(),
4708
+ 'width' => array(),
4709
+ 'word-spacing' => array(),
4710
+ 'writing-mode' => array(),
4711
+ 'x' => array(),
4712
+ 'xlink:actuate' => array(),
4713
+ 'xlink:arcrole' => array(),
4714
+ 'xlink:href' => array(
4715
+ 'alternative_names' => 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(),
4730
+ 'xlink:show' => array(),
4731
+ 'xlink:title' => array(),
4732
+ 'xlink:type' => array(),
4733
+ 'xml:lang' => array(),
4734
+ 'xml:space' => array(),
4735
+ 'xmlns' => array(),
4736
+ 'xmlns:xlink' => 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(
4746
+ array(
4747
+ 'attr_spec_list' => array(
4748
+ 'alt' => array(),
4749
+ 'border' => array(),
4750
+ 'height' => 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(
4763
+ 'alternative_names' => array(
4764
+ 'srcset',
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(
4786
+ array(
4787
+ 'attr_spec_list' => array(
4788
+ '[accept]' => array(),
4789
+ '[accesskey]' => array(),
4790
+ '[autocomplete]' => array(),
4791
+ '[checked]' => array(),
4792
+ '[disabled]' => array(),
4793
+ '[height]' => array(),
4794
+ '[inputmode]' => array(),
4795
+ '[max]' => array(),
4796
+ '[maxlength]' => array(),
4797
+ '[min]' => array(),
4798
+ '[minlength]' => array(),
4799
+ '[multiple]' => array(),
4800
+ '[pattern]' => array(),
4801
+ '[placeholder]' => array(),
4802
+ '[readonly]' => array(),
4803
+ '[required]' => array(),
4804
+ '[selectiondirection]' => array(),
4805
+ '[size]' => array(),
4806
+ '[spellcheck]' => array(),
4807
+ '[step]' => array(),
4808
+ '[type]' => array(),
4809
+ '[value]' => array(),
4810
+ '[width]' => array(),
4811
+ 'accept' => array(),
4812
+ 'accesskey' => array(),
4813
+ 'autocomplete' => array(),
4814
+ 'autofocus' => array(),
4815
+ 'checked' => array(),
4816
+ 'disabled' => array(),
4817
+ 'height' => array(),
4818
+ 'inputmode' => array(),
4819
+ 'list' => array(),
4820
+ 'max' => array(),
4821
+ 'maxlength' => array(),
4822
+ 'min' => 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(),
4830
+ 'readonly' => array(),
4831
+ 'required' => array(),
4832
+ 'selectiondirection' => array(),
4833
+ 'size' => array(),
4834
+ 'spellcheck' => array(),
4835
+ 'step' => array(),
4836
+ 'tabindex' => array(),
4837
+ 'type' => array(
4838
+ 'blacklisted_value_regex' => '(^|\\s)(button|file|image|password|)(\\s|$)',
4839
+ ),
4840
+ 'value' => array(),
4841
+ 'width' => array(),
4842
+ ),
4843
+ 'tag_spec' => array(
4844
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
4845
+ ),
4846
+ ),
4847
+ ),
4848
+ 'ins' => array(
4849
+ 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(
4874
+ array(
4875
+ 'attr_spec_list' => array(
4876
+ 'for' => array(),
4877
+ ),
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(
4890
+ array(
4891
+ 'attr_spec_list' => array(
4892
+ 'value' => array(
4893
+ 'value_regex' => '[0-9]*',
4894
+ ),
4895
+ ),
4896
+ 'tag_spec' => array(),
4897
+ ),
4898
+ ),
4899
+ 'line' => array(
4900
+ array(
4901
+ 'attr_spec_list' => array(
4902
+ 'alignment-baseline' => array(),
4903
+ 'baseline-shift' => array(),
4904
+ 'clip' => array(),
4905
+ 'clip-path' => array(),
4906
+ 'clip-rule' => array(),
4907
+ 'color' => array(),
4908
+ 'color-interpolation' => array(),
4909
+ 'color-interpolation-filters' => array(),
4910
+ 'color-profile' => array(),
4911
+ 'color-rendering' => array(),
4912
+ 'cursor' => array(),
4913
+ 'direction' => array(),
4914
+ 'display' => array(),
4915
+ 'dominant-baseline' => array(),
4916
+ 'enable-background' => array(),
4917
+ 'externalresourcesrequired' => array(),
4918
+ 'fill' => array(),
4919
+ 'fill-opacity' => array(),
4920
+ 'fill-rule' => array(),
4921
+ 'filter' => array(),
4922
+ 'flood-color' => array(),
4923
+ 'flood-opacity' => array(),
4924
+ 'font-family' => array(),
4925
+ 'font-size' => array(),
4926
+ 'font-size-adjust' => array(),
4927
+ 'font-stretch' => array(),
4928
+ 'font-style' => array(),
4929
+ 'font-variant' => array(),
4930
+ 'font-weight' => array(),
4931
+ 'glyph-orientation-horizontal' => array(),
4932
+ 'glyph-orientation-vertical' => array(),
4933
+ 'image-rendering' => array(),
4934
+ 'kerning' => array(),
4935
+ 'letter-spacing' => array(),
4936
+ 'lighting-color' => array(),
4937
+ 'marker-end' => array(),
4938
+ 'marker-mid' => array(),
4939
+ 'marker-start' => array(),
4940
+ 'mask' => array(),
4941
+ 'opacity' => array(),
4942
+ 'overflow' => array(),
4943
+ 'pointer-events' => array(),
4944
+ 'requiredextensions' => array(),
4945
+ 'requiredfeatures' => array(),
4946
+ 'shape-rendering' => array(),
4947
+ 'sketch:type' => array(),
4948
+ 'stop-color' => array(),
4949
+ 'stop-opacity' => array(),
4950
+ 'stroke' => array(),
4951
+ 'stroke-dasharray' => array(),
4952
+ 'stroke-dashoffset' => array(),
4953
+ 'stroke-linecap' => array(),
4954
+ 'stroke-linejoin' => array(),
4955
+ 'stroke-miterlimit' => array(),
4956
+ 'stroke-opacity' => array(),
4957
+ 'stroke-width' => array(),
4958
+ 'style' => array(
4959
+ 'blacklisted_value_regex' => '!important',
4960
+ ),
4961
+ 'systemlanguage' => array(),
4962
+ 'text-anchor' => array(),
4963
+ 'text-decoration' => array(),
4964
+ 'text-rendering' => array(),
4965
+ 'transform' => array(),
4966
+ 'unicode-bidi' => array(),
4967
+ 'vector-effect' => array(),
4968
+ 'visibility' => array(),
4969
+ 'word-spacing' => array(),
4970
+ 'writing-mode' => array(),
4971
+ 'x1' => array(),
4972
+ 'x2' => array(),
4973
+ 'xml:lang' => array(),
4974
+ 'xml:space' => array(),
4975
+ 'xmlns' => array(),
4976
+ 'xmlns:xlink' => array(),
4977
+ 'y1' => 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(
4987
+ array(
4988
+ 'attr_spec_list' => array(
4989
+ 'alignment-baseline' => array(),
4990
+ 'baseline-shift' => array(),
4991
+ 'clip' => array(),
4992
+ 'clip-path' => array(),
4993
+ 'clip-rule' => array(),
4994
+ 'color' => array(),
4995
+ 'color-interpolation' => array(),
4996
+ 'color-interpolation-filters' => array(),
4997
+ 'color-profile' => array(),
4998
+ 'color-rendering' => array(),
4999
+ 'cursor' => array(),
5000
+ 'direction' => array(),
5001
+ 'display' => array(),
5002
+ 'dominant-baseline' => array(),
5003
+ 'enable-background' => array(),
5004
+ 'externalresourcesrequired' => array(),
5005
+ 'fill' => array(),
5006
+ 'fill-opacity' => array(),
5007
+ 'fill-rule' => array(),
5008
+ 'filter' => array(),
5009
+ 'flood-color' => array(),
5010
+ 'flood-opacity' => array(),
5011
+ 'font-family' => array(),
5012
+ 'font-size' => array(),
5013
+ 'font-size-adjust' => array(),
5014
+ 'font-stretch' => array(),
5015
+ 'font-style' => array(),
5016
+ 'font-variant' => array(),
5017
+ 'font-weight' => array(),
5018
+ 'glyph-orientation-horizontal' => array(),
5019
+ 'glyph-orientation-vertical' => array(),
5020
+ 'gradienttransform' => array(),
5021
+ 'gradientunits' => array(),
5022
+ 'image-rendering' => array(),
5023
+ 'kerning' => array(),
5024
+ 'letter-spacing' => array(),
5025
+ 'lighting-color' => array(),
5026
+ 'marker-end' => array(),
5027
+ 'marker-mid' => array(),
5028
+ 'marker-start' => array(),
5029
+ 'mask' => array(),
5030
+ 'opacity' => array(),
5031
+ 'overflow' => array(),
5032
+ 'pointer-events' => array(),
5033
+ 'shape-rendering' => array(),
5034
+ 'spreadmethod' => array(),
5035
+ 'stop-color' => array(),
5036
+ 'stop-opacity' => array(),
5037
+ 'stroke' => array(),
5038
+ 'stroke-dasharray' => array(),
5039
+ 'stroke-dashoffset' => array(),
5040
+ 'stroke-linecap' => array(),
5041
+ 'stroke-linejoin' => array(),
5042
+ 'stroke-miterlimit' => array(),
5043
+ 'stroke-opacity' => array(),
5044
+ 'stroke-width' => array(),
5045
+ 'style' => array(
5046
+ 'blacklisted_value_regex' => '!important',
5047
+ ),
5048
+ 'text-anchor' => array(),
5049
+ 'text-decoration' => array(),
5050
+ 'text-rendering' => array(),
5051
+ 'unicode-bidi' => array(),
5052
+ 'vector-effect' => array(),
5053
+ 'visibility' => array(),
5054
+ 'word-spacing' => array(),
5055
+ 'writing-mode' => array(),
5056
+ 'x1' => array(),
5057
+ 'x2' => array(),
5058
+ 'xlink:actuate' => array(),
5059
+ 'xlink:arcrole' => array(),
5060
+ 'xlink:href' => 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(),
5074
+ 'xlink:show' => array(),
5075
+ 'xlink:title' => array(),
5076
+ 'xlink:type' => array(),
5077
+ 'xml:lang' => array(),
5078
+ 'xml:space' => array(),
5079
+ 'xmlns' => array(),
5080
+ 'xmlns:xlink' => array(),
5081
+ 'y1' => 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(
5091
+ array(
5092
+ 'attr_spec_list' => array(
5093
+ 'charset' => 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(),
5106
+ 'target' => array(),
5107
+ 'type' => 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(
5119
+ 'charset' => 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(
5243
+ 'charset' => 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(),
5259
+ 'target' => 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(
5293
+ 'charset' => array(
5294
+ 'value_casei' => 'utf-8',
5295
+ ),
5296
+ 'color' => array(),
5297
+ 'crossorigin' => array(),
5298
+ 'href' => array(
5299
+ 'mandatory' => true,
5300
+ ),
5301
+ 'hreflang' => array(),
5302
+ 'media' => array(),
5303
+ 'property' => array(
5304
+ 'mandatory' => true,
5305
+ ),
5306
+ 'sizes' => array(),
5307
+ 'target' => array(),
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(
5335
+ array(
5336
+ 'attr_spec_list' => array(
5337
+ 'alignment-baseline' => array(),
5338
+ 'baseline-shift' => array(),
5339
+ 'clip' => array(),
5340
+ 'clip-path' => array(),
5341
+ 'clip-rule' => array(),
5342
+ 'color' => array(),
5343
+ 'color-interpolation' => array(),
5344
+ 'color-interpolation-filters' => array(),
5345
+ 'color-profile' => array(),
5346
+ 'color-rendering' => array(),
5347
+ 'cursor' => array(),
5348
+ 'direction' => array(),
5349
+ 'display' => array(),
5350
+ 'dominant-baseline' => array(),
5351
+ 'enable-background' => array(),
5352
+ 'externalresourcesrequired' => array(),
5353
+ 'fill' => array(),
5354
+ 'fill-opacity' => array(),
5355
+ 'fill-rule' => array(),
5356
+ 'filter' => array(),
5357
+ 'flood-color' => array(),
5358
+ 'flood-opacity' => array(),
5359
+ 'font-family' => array(),
5360
+ 'font-size' => array(),
5361
+ 'font-size-adjust' => array(),
5362
+ 'font-stretch' => array(),
5363
+ 'font-style' => array(),
5364
+ 'font-variant' => array(),
5365
+ 'font-weight' => array(),
5366
+ 'glyph-orientation-horizontal' => array(),
5367
+ 'glyph-orientation-vertical' => array(),
5368
+ 'image-rendering' => array(),
5369
+ 'kerning' => array(),
5370
+ 'letter-spacing' => array(),
5371
+ 'lighting-color' => array(),
5372
+ 'marker-end' => array(),
5373
+ 'marker-mid' => array(),
5374
+ 'marker-start' => array(),
5375
+ 'markerheight' => array(),
5376
+ 'markerunits' => array(),
5377
+ 'markerwidth' => array(),
5378
+ 'mask' => array(),
5379
+ 'opacity' => array(),
5380
+ 'orient' => array(),
5381
+ 'overflow' => array(),
5382
+ 'pointer-events' => array(),
5383
+ 'preserveaspectratio' => array(),
5384
+ 'refx' => array(),
5385
+ 'refy' => array(),
5386
+ 'shape-rendering' => array(),
5387
+ 'stop-color' => array(),
5388
+ 'stop-opacity' => array(),
5389
+ 'stroke' => array(),
5390
+ 'stroke-dasharray' => array(),
5391
+ 'stroke-dashoffset' => array(),
5392
+ 'stroke-linecap' => array(),
5393
+ 'stroke-linejoin' => array(),
5394
+ 'stroke-miterlimit' => array(),
5395
+ 'stroke-opacity' => array(),
5396
+ 'stroke-width' => array(),
5397
+ 'style' => array(
5398
+ 'blacklisted_value_regex' => '!important',
5399
+ ),
5400
+ 'text-anchor' => array(),
5401
+ 'text-decoration' => array(),
5402
+ 'text-rendering' => array(),
5403
+ 'transform' => array(),
5404
+ 'unicode-bidi' => array(),
5405
+ 'vector-effect' => array(),
5406
+ 'viewbox' => array(),
5407
+ 'visibility' => array(),
5408
+ 'word-spacing' => array(),
5409
+ 'writing-mode' => array(),
5410
+ 'xml:lang' => array(),
5411
+ 'xml:space' => array(),
5412
+ 'xmlns' => 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(
5422
+ array(
5423
+ 'attr_spec_list' => array(
5424
+ 'alignment-baseline' => array(),
5425
+ 'baseline-shift' => array(),
5426
+ 'clip' => array(),
5427
+ 'clip-path' => array(),
5428
+ 'clip-rule' => array(),
5429
+ 'color' => array(),
5430
+ 'color-interpolation' => array(),
5431
+ 'color-interpolation-filters' => array(),
5432
+ 'color-profile' => array(),
5433
+ 'color-rendering' => array(),
5434
+ 'cursor' => array(),
5435
+ 'direction' => array(),
5436
+ 'display' => array(),
5437
+ 'dominant-baseline' => array(),
5438
+ 'enable-background' => array(),
5439
+ 'externalresourcesrequired' => array(),
5440
+ 'fill' => array(),
5441
+ 'fill-opacity' => array(),
5442
+ 'fill-rule' => array(),
5443
+ 'filter' => array(),
5444
+ 'flood-color' => array(),
5445
+ 'flood-opacity' => array(),
5446
+ 'font-family' => array(),
5447
+ 'font-size' => array(),
5448
+ 'font-size-adjust' => array(),
5449
+ 'font-stretch' => array(),
5450
+ 'font-style' => array(),
5451
+ 'font-variant' => array(),
5452
+ 'font-weight' => array(),
5453
+ 'glyph-orientation-horizontal' => array(),
5454
+ 'glyph-orientation-vertical' => array(),
5455
+ 'height' => array(),
5456
+ 'image-rendering' => array(),
5457
+ 'kerning' => array(),
5458
+ 'letter-spacing' => array(),
5459
+ 'lighting-color' => array(),
5460
+ 'marker-end' => array(),
5461
+ 'marker-mid' => array(),
5462
+ 'marker-start' => array(),
5463
+ 'mask' => array(),
5464
+ 'maskcontentunits' => array(),
5465
+ 'maskunits' => array(),
5466
+ 'opacity' => array(),
5467
+ 'overflow' => array(),
5468
+ 'pointer-events' => array(),
5469
+ 'requiredextensions' => array(),
5470
+ 'requiredfeatures' => array(),
5471
+ 'shape-rendering' => array(),
5472
+ 'stop-color' => array(),
5473
+ 'stop-opacity' => array(),
5474
+ 'stroke' => array(),
5475
+ 'stroke-dasharray' => array(),
5476
+ 'stroke-dashoffset' => array(),
5477
+ 'stroke-linecap' => array(),
5478
+ 'stroke-linejoin' => array(),
5479
+ 'stroke-miterlimit' => array(),
5480
+ 'stroke-opacity' => array(),
5481
+ 'stroke-width' => array(),
5482
+ 'style' => array(
5483
+ 'blacklisted_value_regex' => '!important',
5484
+ ),
5485
+ 'systemlanguage' => array(),
5486
+ 'text-anchor' => array(),
5487
+ 'text-decoration' => array(),
5488
+ 'text-rendering' => array(),
5489
+ 'unicode-bidi' => array(),
5490
+ 'vector-effect' => array(),
5491
+ 'visibility' => array(),
5492
+ 'width' => array(),
5493
+ 'word-spacing' => array(),
5494
+ 'writing-mode' => array(),
5495
+ 'x' => array(),
5496
+ 'xml:lang' => array(),
5497
+ 'xml:space' => array(),
5498
+ 'xmlns' => array(),
5499
+ 'xmlns:xlink' => 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(
5718
+ 'content' => array(
5719
+ 'mandatory' => true,
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(
5736
+ 'content' => 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(
5753
+ 'content' => 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(
5770
+ 'content' => 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(
5787
+ 'content' => array(
5788
+ 'mandatory' => true,
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(
5805
+ 'content' => array(
5806
+ 'mandatory' => true,
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(
5823
+ 'content' => 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(
5840
+ 'content' => 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(
5908
+ array(
5909
+ 'attr_spec_list' => array(
5910
+ 'style' => array(
5911
+ 'blacklisted_value_regex' => '!important',
5912
+ ),
5913
+ 'xml:lang' => array(),
5914
+ 'xml:space' => array(),
5915
+ 'xmlns' => 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(
5925
+ array(
5926
+ 'attr_spec_list' => array(
5927
+ 'high' => array(),
5928
+ 'low' => array(),
5929
+ 'max' => array(),
5930
+ 'min' => array(),
5931
+ 'optimum' => 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(
5955
+ 'mandatory' => true,
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(
6004
+ array(
6005
+ 'attr_spec_list' => array(
6006
+ 'reversed' => array(
6007
+ 'value' => '',
6008
+ ),
6009
+ 'start' => 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(
6020
+ array(
6021
+ 'attr_spec_list' => array(
6022
+ '[disabled]' => array(),
6023
+ '[label]' => array(),
6024
+ 'disabled' => array(),
6025
+ 'label' => array(),
6026
+ ),
6027
+ 'tag_spec' => array(
6028
+ 'mandatory_parent' => 'select',
6029
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
6030
+ ),
6031
+ ),
6032
+ ),
6033
+ 'option' => array(
6034
+ array(
6035
+ 'attr_spec_list' => array(
6036
+ '[disabled]' => array(),
6037
+ '[label]' => array(),
6038
+ '[selected]' => array(),
6039
+ '[value]' => array(),
6040
+ 'disabled' => array(),
6041
+ 'label' => array(),
6042
+ 'selected' => array(),
6043
+ 'value' => array(),
6044
+ ),
6045
+ 'tag_spec' => array(
6046
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
6047
+ ),
6048
+ ),
6049
+ ),
6050
+ 'output' => array(
6051
+ array(
6052
+ 'attr_spec_list' => 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(
6063
+ array(
6064
+ 'attr_spec_list' => array(
6065
+ 'align' => array(),
6066
+ ),
6067
+ 'tag_spec' => array(),
6068
+ ),
6069
+ ),
6070
+ 'path' => array(
6071
+ array(
6072
+ 'attr_spec_list' => array(
6073
+ 'alignment-baseline' => array(),
6074
+ 'baseline-shift' => array(),
6075
+ 'clip' => array(),
6076
+ 'clip-path' => array(),
6077
+ 'clip-rule' => array(),
6078
+ 'color' => array(),
6079
+ 'color-interpolation' => array(),
6080
+ 'color-interpolation-filters' => array(),
6081
+ 'color-profile' => array(),
6082
+ 'color-rendering' => array(),
6083
+ 'cursor' => array(),
6084
+ 'd' => array(),
6085
+ 'direction' => array(),
6086
+ 'display' => array(),
6087
+ 'dominant-baseline' => array(),
6088
+ 'enable-background' => array(),
6089
+ 'externalresourcesrequired' => array(),
6090
+ 'fill' => array(),
6091
+ 'fill-opacity' => array(),
6092
+ 'fill-rule' => array(),
6093
+ 'filter' => array(),
6094
+ 'flood-color' => array(),
6095
+ 'flood-opacity' => array(),
6096
+ 'font-family' => array(),
6097
+ 'font-size' => array(),
6098
+ 'font-size-adjust' => array(),
6099
+ 'font-stretch' => array(),
6100
+ 'font-style' => array(),
6101
+ 'font-variant' => array(),
6102
+ 'font-weight' => array(),
6103
+ 'glyph-orientation-horizontal' => array(),
6104
+ 'glyph-orientation-vertical' => array(),
6105
+ 'image-rendering' => array(),
6106
+ 'kerning' => array(),
6107
+ 'letter-spacing' => array(),
6108
+ 'lighting-color' => array(),
6109
+ 'marker-end' => array(),
6110
+ 'marker-mid' => array(),
6111
+ 'marker-start' => array(),
6112
+ 'mask' => array(),
6113
+ 'opacity' => array(),
6114
+ 'overflow' => array(),
6115
+ 'pathlength' => array(),
6116
+ 'pointer-events' => array(),
6117
+ 'requiredextensions' => array(),
6118
+ 'requiredfeatures' => array(),
6119
+ 'shape-rendering' => array(),
6120
+ 'sketch:type' => array(),
6121
+ 'stop-color' => array(),
6122
+ 'stop-opacity' => array(),
6123
+ 'stroke' => array(),
6124
+ 'stroke-dasharray' => array(),
6125
+ 'stroke-dashoffset' => array(),
6126
+ 'stroke-linecap' => array(),
6127
+ 'stroke-linejoin' => array(),
6128
+ 'stroke-miterlimit' => array(),
6129
+ 'stroke-opacity' => array(),
6130
+ 'stroke-width' => array(),
6131
+ 'style' => array(
6132
+ 'blacklisted_value_regex' => '!important',
6133
+ ),
6134
+ 'systemlanguage' => array(),
6135
+ 'text-anchor' => array(),
6136
+ 'text-decoration' => array(),
6137
+ 'text-rendering' => array(),
6138
+ 'transform' => array(),
6139
+ 'unicode-bidi' => array(),
6140
+ 'vector-effect' => array(),
6141
+ 'visibility' => array(),
6142
+ 'word-spacing' => array(),
6143
+ 'writing-mode' => array(),
6144
+ 'xml:lang' => array(),
6145
+ 'xml:space' => array(),
6146
+ 'xmlns' => 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(
6156
+ array(
6157
+ 'attr_spec_list' => array(
6158
+ 'alignment-baseline' => array(),
6159
+ 'baseline-shift' => array(),
6160
+ 'clip' => array(),
6161
+ 'clip-path' => array(),
6162
+ 'clip-rule' => array(),
6163
+ 'color' => array(),
6164
+ 'color-interpolation' => array(),
6165
+ 'color-interpolation-filters' => array(),
6166
+ 'color-profile' => array(),
6167
+ 'color-rendering' => array(),
6168
+ 'cursor' => array(),
6169
+ 'direction' => array(),
6170
+ 'display' => array(),
6171
+ 'dominant-baseline' => array(),
6172
+ 'enable-background' => array(),
6173
+ 'externalresourcesrequired' => array(),
6174
+ 'fill' => array(),
6175
+ 'fill-opacity' => array(),
6176
+ 'fill-rule' => array(),
6177
+ 'filter' => array(),
6178
+ 'flood-color' => array(),
6179
+ 'flood-opacity' => array(),
6180
+ 'font-family' => array(),
6181
+ 'font-size' => array(),
6182
+ 'font-size-adjust' => array(),
6183
+ 'font-stretch' => array(),
6184
+ 'font-style' => array(),
6185
+ 'font-variant' => array(),
6186
+ 'font-weight' => array(),
6187
+ 'glyph-orientation-horizontal' => array(),
6188
+ 'glyph-orientation-vertical' => array(),
6189
+ 'height' => array(),
6190
+ 'image-rendering' => array(),
6191
+ 'kerning' => array(),
6192
+ 'letter-spacing' => array(),
6193
+ 'lighting-color' => array(),
6194
+ 'marker-end' => array(),
6195
+ 'marker-mid' => array(),
6196
+ 'marker-start' => array(),
6197
+ 'mask' => array(),
6198
+ 'opacity' => array(),
6199
+ 'overflow' => array(),
6200
+ 'patterncontentunits' => array(),
6201
+ 'patterntransform' => array(),
6202
+ 'patternunits' => array(),
6203
+ 'pointer-events' => array(),
6204
+ 'preserveaspectratio' => array(),
6205
+ 'requiredextensions' => array(),
6206
+ 'requiredfeatures' => array(),
6207
+ 'shape-rendering' => array(),
6208
+ 'stop-color' => array(),
6209
+ 'stop-opacity' => array(),
6210
+ 'stroke' => array(),
6211
+ 'stroke-dasharray' => array(),
6212
+ 'stroke-dashoffset' => array(),
6213
+ 'stroke-linecap' => array(),
6214
+ 'stroke-linejoin' => array(),
6215
+ 'stroke-miterlimit' => array(),
6216
+ 'stroke-opacity' => array(),
6217
+ 'stroke-width' => array(),
6218
+ 'style' => array(
6219
+ 'blacklisted_value_regex' => '!important',
6220
+ ),
6221
+ 'systemlanguage' => array(),
6222
+ 'text-anchor' => array(),
6223
+ 'text-decoration' => array(),
6224
+ 'text-rendering' => array(),
6225
+ 'unicode-bidi' => array(),
6226
+ 'vector-effect' => array(),
6227
+ 'viewbox' => array(),
6228
+ 'visibility' => array(),
6229
+ 'width' => array(),
6230
+ 'word-spacing' => array(),
6231
+ 'writing-mode' => array(),
6232
+ 'x' => array(),
6233
+ 'xlink:actuate' => array(),
6234
+ 'xlink:arcrole' => array(),
6235
+ 'xlink:href' => 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(),
6249
+ 'xlink:show' => array(),
6250
+ 'xlink:title' => array(),
6251
+ 'xlink:type' => array(),
6252
+ 'xml:lang' => array(),
6253
+ 'xml:space' => array(),
6254
+ 'xmlns' => array(),
6255
+ 'xmlns:xlink' => 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(
6265
+ array(
6266
+ 'attr_spec_list' => array(
6267
+ 'alignment-baseline' => array(),
6268
+ 'baseline-shift' => array(),
6269
+ 'clip' => array(),
6270
+ 'clip-path' => array(),
6271
+ 'clip-rule' => array(),
6272
+ 'color' => array(),
6273
+ 'color-interpolation' => array(),
6274
+ 'color-interpolation-filters' => array(),
6275
+ 'color-profile' => array(),
6276
+ 'color-rendering' => array(),
6277
+ 'cursor' => array(),
6278
+ 'direction' => array(),
6279
+ 'display' => array(),
6280
+ 'dominant-baseline' => array(),
6281
+ 'enable-background' => array(),
6282
+ 'externalresourcesrequired' => array(),
6283
+ 'fill' => array(),
6284
+ 'fill-opacity' => array(),
6285
+ 'fill-rule' => array(),
6286
+ 'filter' => array(),
6287
+ 'flood-color' => array(),
6288
+ 'flood-opacity' => array(),
6289
+ 'font-family' => array(),
6290
+ 'font-size' => array(),
6291
+ 'font-size-adjust' => array(),
6292
+ 'font-stretch' => array(),
6293
+ 'font-style' => array(),
6294
+ 'font-variant' => array(),
6295
+ 'font-weight' => array(),
6296
+ 'glyph-orientation-horizontal' => array(),
6297
+ 'glyph-orientation-vertical' => array(),
6298
+ 'image-rendering' => array(),
6299
+ 'kerning' => array(),
6300
+ 'letter-spacing' => array(),
6301
+ 'lighting-color' => array(),
6302
+ 'marker-end' => array(),
6303
+ 'marker-mid' => array(),
6304
+ 'marker-start' => array(),
6305
+ 'mask' => array(),
6306
+ 'opacity' => array(),
6307
+ 'overflow' => array(),
6308
+ 'pointer-events' => array(),
6309
+ 'points' => array(),
6310
+ 'requiredextensions' => array(),
6311
+ 'requiredfeatures' => array(),
6312
+ 'shape-rendering' => array(),
6313
+ 'sketch:type' => array(),
6314
+ 'stop-color' => array(),
6315
+ 'stop-opacity' => array(),
6316
+ 'stroke' => array(),
6317
+ 'stroke-dasharray' => array(),
6318
+ 'stroke-dashoffset' => array(),
6319
+ 'stroke-linecap' => array(),
6320
+ 'stroke-linejoin' => array(),
6321
+ 'stroke-miterlimit' => array(),
6322
+ 'stroke-opacity' => array(),
6323
+ 'stroke-width' => array(),
6324
+ 'style' => array(
6325
+ 'blacklisted_value_regex' => '!important',
6326
+ ),
6327
+ 'systemlanguage' => array(),
6328
+ 'text-anchor' => array(),
6329
+ 'text-decoration' => array(),
6330
+ 'text-rendering' => array(),
6331
+ 'transform' => array(),
6332
+ 'unicode-bidi' => array(),
6333
+ 'vector-effect' => array(),
6334
+ 'visibility' => array(),
6335
+ 'word-spacing' => array(),
6336
+ 'writing-mode' => array(),
6337
+ 'xml:lang' => array(),
6338
+ 'xml:space' => array(),
6339
+ 'xmlns' => 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(
6349
+ array(
6350
+ 'attr_spec_list' => array(
6351
+ 'alignment-baseline' => array(),
6352
+ 'baseline-shift' => array(),
6353
+ 'clip' => array(),
6354
+ 'clip-path' => array(),
6355
+ 'clip-rule' => array(),
6356
+ 'color' => array(),
6357
+ 'color-interpolation' => array(),
6358
+ 'color-interpolation-filters' => array(),
6359
+ 'color-profile' => array(),
6360
+ 'color-rendering' => array(),
6361
+ 'cursor' => array(),
6362
+ 'direction' => array(),
6363
+ 'display' => array(),
6364
+ 'dominant-baseline' => array(),
6365
+ 'enable-background' => array(),
6366
+ 'externalresourcesrequired' => array(),
6367
+ 'fill' => array(),
6368
+ 'fill-opacity' => array(),
6369
+ 'fill-rule' => array(),
6370
+ 'filter' => array(),
6371
+ 'flood-color' => array(),
6372
+ 'flood-opacity' => array(),
6373
+ 'font-family' => array(),
6374
+ 'font-size' => array(),
6375
+ 'font-size-adjust' => array(),
6376
+ 'font-stretch' => array(),
6377
+ 'font-style' => array(),
6378
+ 'font-variant' => array(),
6379
+ 'font-weight' => array(),
6380
+ 'glyph-orientation-horizontal' => array(),
6381
+ 'glyph-orientation-vertical' => array(),
6382
+ 'image-rendering' => array(),
6383
+ 'kerning' => array(),
6384
+ 'letter-spacing' => array(),
6385
+ 'lighting-color' => array(),
6386
+ 'marker-end' => array(),
6387
+ 'marker-mid' => array(),
6388
+ 'marker-start' => array(),
6389
+ 'mask' => array(),
6390
+ 'opacity' => array(),
6391
+ 'overflow' => array(),
6392
+ 'pointer-events' => array(),
6393
+ 'points' => array(),
6394
+ 'requiredextensions' => array(),
6395
+ 'requiredfeatures' => array(),
6396
+ 'shape-rendering' => array(),
6397
+ 'sketch:type' => array(),
6398
+ 'stop-color' => array(),
6399
+ 'stop-opacity' => array(),
6400
+ 'stroke' => array(),
6401
+ 'stroke-dasharray' => array(),
6402
+ 'stroke-dashoffset' => array(),
6403
+ 'stroke-linecap' => array(),
6404
+ 'stroke-linejoin' => array(),
6405
+ 'stroke-miterlimit' => array(),
6406
+ 'stroke-opacity' => array(),
6407
+ 'stroke-width' => array(),
6408
+ 'style' => array(
6409
+ 'blacklisted_value_regex' => '!important',
6410
+ ),
6411
+ 'systemlanguage' => array(),
6412
+ 'text-anchor' => array(),
6413
+ 'text-decoration' => array(),
6414
+ 'text-rendering' => array(),
6415
+ 'transform' => array(),
6416
+ 'unicode-bidi' => array(),
6417
+ 'vector-effect' => array(),
6418
+ 'visibility' => array(),
6419
+ 'word-spacing' => array(),
6420
+ 'writing-mode' => array(),
6421
+ 'xml:lang' => array(),
6422
+ 'xml:space' => array(),
6423
+ 'xmlns' => 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(
6439
+ array(
6440
+ 'attr_spec_list' => array(
6441
+ 'max' => array(),
6442
+ 'value' => array(),
6443
+ ),
6444
+ 'tag_spec' => array(),
6445
+ ),
6446
+ ),
6447
+ 'q' => array(
6448
+ 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(
6466
+ array(
6467
+ 'attr_spec_list' => array(
6468
+ 'alignment-baseline' => array(),
6469
+ 'baseline-shift' => array(),
6470
+ 'clip' => array(),
6471
+ 'clip-path' => array(),
6472
+ 'clip-rule' => array(),
6473
+ 'color' => array(),
6474
+ 'color-interpolation' => array(),
6475
+ 'color-interpolation-filters' => array(),
6476
+ 'color-profile' => array(),
6477
+ 'color-rendering' => array(),
6478
+ 'cursor' => array(),
6479
+ 'cx' => array(),
6480
+ 'cy' => array(),
6481
+ 'direction' => array(),
6482
+ 'display' => array(),
6483
+ 'dominant-baseline' => array(),
6484
+ 'enable-background' => array(),
6485
+ 'externalresourcesrequired' => array(),
6486
+ 'fill' => array(),
6487
+ 'fill-opacity' => array(),
6488
+ 'fill-rule' => array(),
6489
+ 'filter' => array(),
6490
+ 'flood-color' => array(),
6491
+ 'flood-opacity' => array(),
6492
+ 'font-family' => array(),
6493
+ 'font-size' => array(),
6494
+ 'font-size-adjust' => array(),
6495
+ 'font-stretch' => array(),
6496
+ 'font-style' => array(),
6497
+ 'font-variant' => array(),
6498
+ 'font-weight' => array(),
6499
+ 'fr' => array(),
6500
+ 'fx' => array(),
6501
+ 'fy' => array(),
6502
+ 'glyph-orientation-horizontal' => array(),
6503
+ 'glyph-orientation-vertical' => array(),
6504
+ 'gradienttransform' => array(),
6505
+ 'gradientunits' => array(),
6506
+ 'image-rendering' => array(),
6507
+ 'kerning' => array(),
6508
+ 'letter-spacing' => array(),
6509
+ 'lighting-color' => array(),
6510
+ 'marker-end' => array(),
6511
+ 'marker-mid' => array(),
6512
+ 'marker-start' => array(),
6513
+ 'mask' => array(),
6514
+ 'opacity' => array(),
6515
+ 'overflow' => array(),
6516
+ 'pointer-events' => array(),
6517
+ 'r' => array(),
6518
+ 'shape-rendering' => array(),
6519
+ 'spreadmethod' => array(),
6520
+ 'stop-color' => array(),
6521
+ 'stop-opacity' => array(),
6522
+ 'stroke' => array(),
6523
+ 'stroke-dasharray' => array(),
6524
+ 'stroke-dashoffset' => array(),
6525
+ 'stroke-linecap' => array(),
6526
+ 'stroke-linejoin' => array(),
6527
+ 'stroke-miterlimit' => array(),
6528
+ 'stroke-opacity' => array(),
6529
+ 'stroke-width' => array(),
6530
+ 'style' => array(
6531
+ 'blacklisted_value_regex' => '!important',
6532
+ ),
6533
+ 'text-anchor' => array(),
6534
+ 'text-decoration' => array(),
6535
+ 'text-rendering' => array(),
6536
+ 'unicode-bidi' => array(),
6537
+ 'vector-effect' => array(),
6538
+ 'visibility' => array(),
6539
+ 'word-spacing' => array(),
6540
+ 'writing-mode' => array(),
6541
+ 'xlink:actuate' => array(),
6542
+ 'xlink:arcrole' => array(),
6543
+ 'xlink:href' => 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(),
6557
+ 'xlink:show' => array(),
6558
+ 'xlink:title' => array(),
6559
+ 'xlink:type' => array(),
6560
+ 'xml:lang' => array(),
6561
+ 'xml:space' => array(),
6562
+ 'xmlns' => 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(
6578
+ array(
6579
+ 'attr_spec_list' => array(
6580
+ 'alignment-baseline' => array(),
6581
+ 'baseline-shift' => array(),
6582
+ 'clip' => array(),
6583
+ 'clip-path' => array(),
6584
+ 'clip-rule' => array(),
6585
+ 'color' => array(),
6586
+ 'color-interpolation' => array(),
6587
+ 'color-interpolation-filters' => array(),
6588
+ 'color-profile' => array(),
6589
+ 'color-rendering' => array(),
6590
+ 'cursor' => array(),
6591
+ 'direction' => array(),
6592
+ 'display' => array(),
6593
+ 'dominant-baseline' => array(),
6594
+ 'enable-background' => array(),
6595
+ 'externalresourcesrequired' => array(),
6596
+ 'fill' => array(),
6597
+ 'fill-opacity' => array(),
6598
+ 'fill-rule' => array(),
6599
+ 'filter' => array(),
6600
+ 'flood-color' => array(),
6601
+ 'flood-opacity' => array(),
6602
+ 'font-family' => array(),
6603
+ 'font-size' => array(),
6604
+ 'font-size-adjust' => array(),
6605
+ 'font-stretch' => array(),
6606
+ 'font-style' => array(),
6607
+ 'font-variant' => array(),
6608
+ 'font-weight' => array(),
6609
+ 'glyph-orientation-horizontal' => array(),
6610
+ 'glyph-orientation-vertical' => array(),
6611
+ 'height' => array(),
6612
+ 'image-rendering' => array(),
6613
+ 'kerning' => array(),
6614
+ 'letter-spacing' => array(),
6615
+ 'lighting-color' => array(),
6616
+ 'marker-end' => array(),
6617
+ 'marker-mid' => array(),
6618
+ 'marker-start' => array(),
6619
+ 'mask' => array(),
6620
+ 'opacity' => array(),
6621
+ 'overflow' => array(),
6622
+ 'pointer-events' => array(),
6623
+ 'requiredextensions' => array(),
6624
+ 'requiredfeatures' => array(),
6625
+ 'rx' => array(),
6626
+ 'ry' => array(),
6627
+ 'shape-rendering' => array(),
6628
+ 'sketch:type' => array(),
6629
+ 'stop-color' => array(),
6630
+ 'stop-opacity' => array(),
6631
+ 'stroke' => array(),
6632
+ 'stroke-dasharray' => array(),
6633
+ 'stroke-dashoffset' => array(),
6634
+ 'stroke-linecap' => array(),
6635
+ 'stroke-linejoin' => array(),
6636
+ 'stroke-miterlimit' => array(),
6637
+ 'stroke-opacity' => array(),
6638
+ 'stroke-width' => array(),
6639
+ 'style' => array(
6640
+ 'blacklisted_value_regex' => '!important',
6641
+ ),
6642
+ 'systemlanguage' => array(),
6643
+ 'text-anchor' => array(),
6644
+ 'text-decoration' => array(),
6645
+ 'text-rendering' => array(),
6646
+ 'transform' => array(),
6647
+ 'unicode-bidi' => array(),
6648
+ 'vector-effect' => array(),
6649
+ 'visibility' => array(),
6650
+ 'width' => array(),
6651
+ 'word-spacing' => array(),
6652
+ 'writing-mode' => array(),
6653
+ 'x' => array(),
6654
+ 'xml:lang' => array(),
6655
+ 'xml:space' => array(),
6656
+ 'xmlns' => array(),
6657
+ 'xmlns:xlink' => 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(
8859
+ 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(
8869
+ 'expanded' => array(
8870
+ 'value' => '',
8871
+ ),
8872
+ ),
8873
+ 'tag_spec' => array(
8874
+ 'mandatory_parent' => 'amp-accordion',
8875
+ 'spec_name' => 'amp-accordion > section',
8876
+ ),
8877
+ ),
8878
+ ),
8879
+ 'select' => array(
8880
+ array(
8881
+ 'attr_spec_list' => array(
8882
+ '[autofocus]' => array(),
8883
+ '[disabled]' => array(),
8884
+ '[multiple]' => array(),
8885
+ '[required]' => array(),
8886
+ '[size]' => array(),
8887
+ 'autofocus' => 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(),
8895
+ ),
8896
+ 'tag_spec' => array(
8897
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
8898
+ ),
8899
+ ),
8900
+ ),
8901
+ 'slot' => array(
8902
+ 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(
8916
+ array(
8917
+ 'attr_spec_list' => array(
8918
+ 'alignment-baseline' => array(),
8919
+ 'baseline-shift' => array(),
8920
+ 'clip' => array(),
8921
+ 'clip-path' => array(),
8922
+ 'clip-rule' => array(),
8923
+ 'color' => array(),
8924
+ 'color-interpolation' => array(),
8925
+ 'color-interpolation-filters' => array(),
8926
+ 'color-profile' => array(),
8927
+ 'color-rendering' => array(),
8928
+ 'cursor' => array(),
8929
+ 'direction' => array(),
8930
+ 'display' => array(),
8931
+ 'dominant-baseline' => array(),
8932
+ 'enable-background' => array(),
8933
+ 'fill' => array(),
8934
+ 'fill-opacity' => array(),
8935
+ 'fill-rule' => array(),
8936
+ 'filter' => array(),
8937
+ 'flood-color' => array(),
8938
+ 'flood-opacity' => array(),
8939
+ 'font-family' => array(),
8940
+ 'font-size' => array(),
8941
+ 'font-size-adjust' => array(),
8942
+ 'font-stretch' => array(),
8943
+ 'font-style' => array(),
8944
+ 'font-variant' => array(),
8945
+ 'font-weight' => array(),
8946
+ 'glyph-orientation-horizontal' => array(),
8947
+ 'glyph-orientation-vertical' => array(),
8948
+ 'image-rendering' => array(),
8949
+ 'kerning' => array(),
8950
+ 'letter-spacing' => array(),
8951
+ 'lighting-color' => array(),
8952
+ 'marker-end' => array(),
8953
+ 'marker-mid' => array(),
8954
+ 'marker-start' => array(),
8955
+ 'mask' => array(),
8956
+ 'opacity' => array(),
8957
+ 'overflow' => array(),
8958
+ 'pointer-events' => array(),
8959
+ 'shape-rendering' => array(),
8960
+ 'solid-color' => array(),
8961
+ 'solid-opacity' => array(),
8962
+ 'stop-color' => array(),
8963
+ 'stop-opacity' => array(),
8964
+ 'stroke' => array(),
8965
+ 'stroke-dasharray' => array(),
8966
+ 'stroke-dashoffset' => array(),
8967
+ 'stroke-linecap' => array(),
8968
+ 'stroke-linejoin' => array(),
8969
+ 'stroke-miterlimit' => array(),
8970
+ 'stroke-opacity' => array(),
8971
+ 'stroke-width' => array(),
8972
+ 'style' => array(
8973
+ 'blacklisted_value_regex' => '!important',
8974
+ ),
8975
+ 'text-anchor' => array(),
8976
+ 'text-decoration' => array(),
8977
+ 'text-rendering' => array(),
8978
+ 'unicode-bidi' => array(),
8979
+ 'vector-effect' => array(),
8980
+ 'visibility' => array(),
8981
+ 'word-spacing' => array(),
8982
+ 'writing-mode' => array(),
8983
+ 'xml:lang' => array(),
8984
+ 'xml:space' => array(),
8985
+ 'xmlns' => 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(
8995
+ array(
8996
+ 'attr_spec_list' => array(
8997
+ '[src]' => array(),
8998
+ '[type]' => 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(
9019
+ '[src]' => array(),
9020
+ '[type]' => 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(
9041
+ 'media' => 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(
9053
+ 'mandatory' => true,
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(
9064
+ 'media' => 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(
9076
+ 'mandatory' => true,
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(
9087
+ '[src]' => array(),
9088
+ '[type]' => 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(
9123
+ array(
9124
+ 'attr_spec_list' => array(
9125
+ 'offset' => array(),
9126
+ 'stop-color' => array(),
9127
+ 'stop-opacity' => array(),
9128
+ 'style' => array(
9129
+ 'blacklisted_value_regex' => '!important',
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(
9140
+ 'offset' => array(),
9141
+ 'stop-color' => array(),
9142
+ 'stop-opacity' => array(),
9143
+ 'style' => array(
9144
+ 'blacklisted_value_regex' => '!important',
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',
9229
+ 'spec_name' => 'noscript > style[amp-boilerplate]',
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(
9266
+ array(
9267
+ 'attr_spec_list' => array(
9268
+ 'alignment-baseline' => array(),
9269
+ 'baseline-shift' => array(),
9270
+ 'clip' => array(),
9271
+ 'clip-path' => array(),
9272
+ 'clip-rule' => array(),
9273
+ 'color' => array(),
9274
+ 'color-interpolation' => array(),
9275
+ 'color-interpolation-filters' => array(),
9276
+ 'color-profile' => array(),
9277
+ 'color-rendering' => array(),
9278
+ 'contentscripttype' => array(),
9279
+ 'contentstyletype' => array(),
9280
+ 'cursor' => array(),
9281
+ 'direction' => array(),
9282
+ 'display' => array(),
9283
+ 'dominant-baseline' => array(),
9284
+ 'enable-background' => array(),
9285
+ 'externalresourcesrequired' => array(),
9286
+ 'fill' => array(),
9287
+ 'fill-opacity' => array(),
9288
+ 'fill-rule' => array(),
9289
+ 'filter' => array(),
9290
+ 'flood-color' => array(),
9291
+ 'flood-opacity' => array(),
9292
+ 'font-family' => array(),
9293
+ 'font-size' => array(),
9294
+ 'font-size-adjust' => array(),
9295
+ 'font-stretch' => array(),
9296
+ 'font-style' => array(),
9297
+ 'font-variant' => array(),
9298
+ 'font-weight' => array(),
9299
+ 'glyph-orientation-horizontal' => array(),
9300
+ 'glyph-orientation-vertical' => array(),
9301
+ 'height' => array(),
9302
+ 'image-rendering' => array(),
9303
+ 'kerning' => array(),
9304
+ 'letter-spacing' => array(),
9305
+ 'lighting-color' => array(),
9306
+ 'marker-end' => array(),
9307
+ 'marker-mid' => array(),
9308
+ 'marker-start' => array(),
9309
+ 'mask' => array(),
9310
+ 'opacity' => array(),
9311
+ 'overflow' => array(),
9312
+ 'pointer-events' => array(),
9313
+ 'preserveaspectratio' => array(),
9314
+ 'requiredextensions' => array(),
9315
+ 'requiredfeatures' => array(),
9316
+ 'shape-rendering' => array(),
9317
+ 'stop-color' => array(),
9318
+ 'stop-opacity' => array(),
9319
+ 'stroke' => array(),
9320
+ 'stroke-dasharray' => array(),
9321
+ 'stroke-dashoffset' => array(),
9322
+ 'stroke-linecap' => array(),
9323
+ 'stroke-linejoin' => array(),
9324
+ 'stroke-miterlimit' => array(),
9325
+ 'stroke-opacity' => array(),
9326
+ 'stroke-width' => array(),
9327
+ 'systemlanguage' => array(),
9328
+ 'text-anchor' => array(),
9329
+ 'text-decoration' => array(),
9330
+ 'text-rendering' => array(),
9331
+ 'unicode-bidi' => array(),
9332
+ 'vector-effect' => array(),
9333
+ 'version' => array(
9334
+ 'value_regex' => '(1.0|1.1)',
9335
+ ),
9336
+ 'viewbox' => array(),
9337
+ 'visibility' => array(),
9338
+ 'width' => array(),
9339
+ 'word-spacing' => array(),
9340
+ 'writing-mode' => array(),
9341
+ 'x' => array(),
9342
+ 'xml:lang' => array(),
9343
+ 'xml:space' => array(),
9344
+ 'xmlns' => array(),
9345
+ 'xmlns:xlink' => array(),
9346
+ 'y' => 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(
9355
+ array(
9356
+ 'attr_spec_list' => array(
9357
+ 'alignment-baseline' => array(),
9358
+ 'baseline-shift' => array(),
9359
+ 'clip' => array(),
9360
+ 'clip-path' => array(),
9361
+ 'clip-rule' => array(),
9362
+ 'color' => array(),
9363
+ 'color-interpolation' => array(),
9364
+ 'color-interpolation-filters' => array(),
9365
+ 'color-profile' => array(),
9366
+ 'color-rendering' => array(),
9367
+ 'cursor' => array(),
9368
+ 'direction' => array(),
9369
+ 'display' => array(),
9370
+ 'dominant-baseline' => array(),
9371
+ 'enable-background' => array(),
9372
+ 'fill' => array(),
9373
+ 'fill-opacity' => array(),
9374
+ 'fill-rule' => array(),
9375
+ 'filter' => array(),
9376
+ 'flood-color' => array(),
9377
+ 'flood-opacity' => array(),
9378
+ 'font-family' => array(),
9379
+ 'font-size' => array(),
9380
+ 'font-size-adjust' => array(),
9381
+ 'font-stretch' => array(),
9382
+ 'font-style' => array(),
9383
+ 'font-variant' => array(),
9384
+ 'font-weight' => array(),
9385
+ 'glyph-orientation-horizontal' => array(),
9386
+ 'glyph-orientation-vertical' => array(),
9387
+ 'image-rendering' => array(),
9388
+ 'kerning' => array(),
9389
+ 'letter-spacing' => array(),
9390
+ 'lighting-color' => array(),
9391
+ 'marker-end' => array(),
9392
+ 'marker-mid' => array(),
9393
+ 'marker-start' => array(),
9394
+ 'mask' => array(),
9395
+ 'opacity' => array(),
9396
+ 'overflow' => array(),
9397
+ 'pointer-events' => array(),
9398
+ 'requiredextensions' => array(),
9399
+ 'requiredfeatures' => array(),
9400
+ 'shape-rendering' => array(),
9401
+ 'stop-color' => array(),
9402
+ 'stop-opacity' => array(),
9403
+ 'stroke' => array(),
9404
+ 'stroke-dasharray' => array(),
9405
+ 'stroke-dashoffset' => array(),
9406
+ 'stroke-linecap' => array(),
9407
+ 'stroke-linejoin' => array(),
9408
+ 'stroke-miterlimit' => array(),
9409
+ 'stroke-opacity' => array(),
9410
+ 'stroke-width' => array(),
9411
+ 'style' => array(
9412
+ 'blacklisted_value_regex' => '!important',
9413
+ ),
9414
+ 'systemlanguage' => array(),
9415
+ 'text-anchor' => array(),
9416
+ 'text-decoration' => array(),
9417
+ 'text-rendering' => array(),
9418
+ 'unicode-bidi' => array(),
9419
+ 'vector-effect' => array(),
9420
+ 'visibility' => array(),
9421
+ 'word-spacing' => array(),
9422
+ 'writing-mode' => array(),
9423
+ 'xml:lang' => array(),
9424
+ 'xml:space' => array(),
9425
+ 'xmlns' => 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(
9435
+ array(
9436
+ 'attr_spec_list' => array(
9437
+ 'alignment-baseline' => array(),
9438
+ 'baseline-shift' => array(),
9439
+ 'clip' => array(),
9440
+ 'clip-path' => array(),
9441
+ 'clip-rule' => array(),
9442
+ 'color' => array(),
9443
+ 'color-interpolation' => array(),
9444
+ 'color-interpolation-filters' => array(),
9445
+ 'color-profile' => array(),
9446
+ 'color-rendering' => array(),
9447
+ 'cursor' => array(),
9448
+ 'direction' => array(),
9449
+ 'display' => array(),
9450
+ 'dominant-baseline' => array(),
9451
+ 'enable-background' => array(),
9452
+ 'externalresourcesrequired' => array(),
9453
+ 'fill' => array(),
9454
+ 'fill-opacity' => array(),
9455
+ 'fill-rule' => array(),
9456
+ 'filter' => array(),
9457
+ 'flood-color' => array(),
9458
+ 'flood-opacity' => array(),
9459
+ 'font-family' => array(),
9460
+ 'font-size' => array(),
9461
+ 'font-size-adjust' => array(),
9462
+ 'font-stretch' => array(),
9463
+ 'font-style' => array(),
9464
+ 'font-variant' => array(),
9465
+ 'font-weight' => array(),
9466
+ 'glyph-orientation-horizontal' => array(),
9467
+ 'glyph-orientation-vertical' => array(),
9468
+ 'image-rendering' => array(),
9469
+ 'kerning' => array(),
9470
+ 'letter-spacing' => array(),
9471
+ 'lighting-color' => array(),
9472
+ 'marker-end' => array(),
9473
+ 'marker-mid' => array(),
9474
+ 'marker-start' => array(),
9475
+ 'mask' => array(),
9476
+ 'opacity' => array(),
9477
+ 'overflow' => array(),
9478
+ 'pointer-events' => array(),
9479
+ 'preserveaspectratio' => array(),
9480
+ 'shape-rendering' => array(),
9481
+ 'stop-color' => array(),
9482
+ 'stop-opacity' => array(),
9483
+ 'stroke' => array(),
9484
+ 'stroke-dasharray' => array(),
9485
+ 'stroke-dashoffset' => array(),
9486
+ 'stroke-linecap' => array(),
9487
+ 'stroke-linejoin' => array(),
9488
+ 'stroke-miterlimit' => array(),
9489
+ 'stroke-opacity' => array(),
9490
+ 'stroke-width' => array(),
9491
+ 'style' => array(
9492
+ 'blacklisted_value_regex' => '!important',
9493
+ ),
9494
+ 'text-anchor' => array(),
9495
+ 'text-decoration' => array(),
9496
+ 'text-rendering' => array(),
9497
+ 'unicode-bidi' => array(),
9498
+ 'vector-effect' => array(),
9499
+ 'viewbox' => array(),
9500
+ 'visibility' => array(),
9501
+ 'word-spacing' => array(),
9502
+ 'writing-mode' => array(),
9503
+ 'xml:lang' => array(),
9504
+ 'xml:space' => array(),
9505
+ 'xmlns' => 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(
9515
+ array(
9516
+ 'attr_spec_list' => array(
9517
+ 'align' => array(),
9518
+ 'bgcolor' => array(),
9519
+ 'border' => array(
9520
+ 'value_regex' => '0|1',
9521
+ ),
9522
+ 'cellpadding' => array(),
9523
+ 'cellspacing' => array(),
9524
+ 'sortable' => 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(
9537
+ array(
9538
+ 'attr_spec_list' => array(
9539
+ 'align' => array(),
9540
+ 'bgcolor' => array(),
9541
+ 'colspan' => array(),
9542
+ 'headers' => array(),
9543
+ 'height' => array(),
9544
+ 'rowspan' => array(),
9545
+ 'valign' => array(),
9546
+ 'width' => array(),
9547
+ ),
9548
+ 'tag_spec' => array(),
9549
+ ),
9550
+ ),
9551
+ 'template' => array(
9552
+ array(
9553
+ 'attr_spec_list' => array(
9554
+ 'type' => array(
9555
+ 'mandatory' => true,
9556
+ 'value' => 'amp-mustache',
9557
+ ),
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(
9588
+ array(
9589
+ 'attr_spec_list' => array(
9590
+ 'alignment-baseline' => array(),
9591
+ 'baseline-shift' => array(),
9592
+ 'clip' => array(),
9593
+ 'clip-path' => array(),
9594
+ 'clip-rule' => array(),
9595
+ 'color' => array(),
9596
+ 'color-interpolation' => array(),
9597
+ 'color-interpolation-filters' => array(),
9598
+ 'color-profile' => array(),
9599
+ 'color-rendering' => array(),
9600
+ 'cursor' => array(),
9601
+ 'direction' => array(),
9602
+ 'display' => array(),
9603
+ 'dominant-baseline' => array(),
9604
+ 'dx' => array(),
9605
+ 'dy' => array(),
9606
+ 'enable-background' => array(),
9607
+ 'externalresourcesrequired' => array(),
9608
+ 'fill' => array(),
9609
+ 'fill-opacity' => array(),
9610
+ 'fill-rule' => array(),
9611
+ 'filter' => array(),
9612
+ 'flood-color' => array(),
9613
+ 'flood-opacity' => array(),
9614
+ 'font-family' => array(),
9615
+ 'font-size' => array(),
9616
+ 'font-size-adjust' => array(),
9617
+ 'font-stretch' => array(),
9618
+ 'font-style' => array(),
9619
+ 'font-variant' => array(),
9620
+ 'font-weight' => array(),
9621
+ 'glyph-orientation-horizontal' => array(),
9622
+ 'glyph-orientation-vertical' => array(),
9623
+ 'image-rendering' => array(),
9624
+ 'kerning' => array(),
9625
+ 'lengthadjust' => array(),
9626
+ 'letter-spacing' => array(),
9627
+ 'lighting-color' => array(),
9628
+ 'marker-end' => array(),
9629
+ 'marker-mid' => array(),
9630
+ 'marker-start' => array(),
9631
+ 'mask' => array(),
9632
+ 'opacity' => array(),
9633
+ 'overflow' => array(),
9634
+ 'pointer-events' => array(),
9635
+ 'requiredextensions' => array(),
9636
+ 'requiredfeatures' => array(),
9637
+ 'rotate' => array(),
9638
+ 'shape-rendering' => array(),
9639
+ 'stop-color' => array(),
9640
+ 'stop-opacity' => array(),
9641
+ 'stroke' => array(),
9642
+ 'stroke-dasharray' => array(),
9643
+ 'stroke-dashoffset' => array(),
9644
+ 'stroke-linecap' => array(),
9645
+ 'stroke-linejoin' => array(),
9646
+ 'stroke-miterlimit' => array(),
9647
+ 'stroke-opacity' => array(),
9648
+ 'stroke-width' => array(),
9649
+ 'style' => array(
9650
+ 'blacklisted_value_regex' => '!important',
9651
+ ),
9652
+ 'systemlanguage' => array(),
9653
+ 'text-anchor' => array(),
9654
+ 'text-decoration' => array(),
9655
+ 'text-rendering' => array(),
9656
+ 'textlength' => array(),
9657
+ 'transform' => array(),
9658
+ 'unicode-bidi' => array(),
9659
+ 'vector-effect' => array(),
9660
+ 'visibility' => array(),
9661
+ 'word-spacing' => array(),
9662
+ 'writing-mode' => array(),
9663
+ 'x' => array(),
9664
+ 'xml:lang' => array(),
9665
+ 'xml:space' => array(),
9666
+ 'xmlns' => array(),
9667
+ 'xmlns:xlink' => 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(
9677
+ array(
9678
+ 'attr_spec_list' => array(
9679
+ '[autocomplete]' => array(),
9680
+ '[autofocus]' => array(),
9681
+ '[cols]' => array(),
9682
+ '[disabled]' => array(),
9683
+ '[maxlength]' => array(),
9684
+ '[minlength]' => array(),
9685
+ '[placeholder]' => array(),
9686
+ '[readonly]' => array(),
9687
+ '[required]' => array(),
9688
+ '[rows]' => array(),
9689
+ '[selectiondirection]' => array(),
9690
+ '[selectionend]' => array(),
9691
+ '[selectionstart]' => array(),
9692
+ '[spellcheck]' => array(),
9693
+ '[wrap]' => array(),
9694
+ 'autocomplete' => array(),
9695
+ 'autofocus' => array(),
9696
+ 'cols' => array(),
9697
+ 'disabled' => 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(),
9705
+ 'required' => array(),
9706
+ 'rows' => array(),
9707
+ 'selectiondirection' => array(),
9708
+ 'selectionend' => array(),
9709
+ 'selectionstart' => array(),
9710
+ 'spellcheck' => array(),
9711
+ 'wrap' => array(),
9712
+ ),
9713
+ 'tag_spec' => array(
9714
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
9715
+ ),
9716
+ ),
9717
+ ),
9718
+ 'textpath' => array(
9719
+ array(
9720
+ 'attr_spec_list' => array(
9721
+ 'alignment-baseline' => array(),
9722
+ 'baseline-shift' => array(),
9723
+ 'clip' => array(),
9724
+ 'clip-path' => array(),
9725
+ 'clip-rule' => array(),
9726
+ 'color' => array(),
9727
+ 'color-interpolation' => array(),
9728
+ 'color-interpolation-filters' => array(),
9729
+ 'color-profile' => array(),
9730
+ 'color-rendering' => array(),
9731
+ 'cursor' => array(),
9732
+ 'direction' => array(),
9733
+ 'display' => array(),
9734
+ 'dominant-baseline' => array(),
9735
+ 'enable-background' => array(),
9736
+ 'externalresourcesrequired' => array(),
9737
+ 'fill' => array(),
9738
+ 'fill-opacity' => array(),
9739
+ 'fill-rule' => array(),
9740
+ 'filter' => array(),
9741
+ 'flood-color' => array(),
9742
+ 'flood-opacity' => array(),
9743
+ 'font-family' => array(),
9744
+ 'font-size' => array(),
9745
+ 'font-size-adjust' => array(),
9746
+ 'font-stretch' => array(),
9747
+ 'font-style' => array(),
9748
+ 'font-variant' => array(),
9749
+ 'font-weight' => array(),
9750
+ 'glyph-orientation-horizontal' => array(),
9751
+ 'glyph-orientation-vertical' => array(),
9752
+ 'image-rendering' => array(),
9753
+ 'kerning' => array(),
9754
+ 'letter-spacing' => array(),
9755
+ 'lighting-color' => array(),
9756
+ 'marker-end' => array(),
9757
+ 'marker-mid' => array(),
9758
+ 'marker-start' => array(),
9759
+ 'mask' => array(),
9760
+ 'method' => array(),
9761
+ 'opacity' => array(),
9762
+ 'overflow' => array(),
9763
+ 'pointer-events' => array(),
9764
+ 'requiredextensions' => array(),
9765
+ 'requiredfeatures' => array(),
9766
+ 'shape-rendering' => array(),
9767
+ 'spacing' => array(),
9768
+ 'startoffset' => array(),
9769
+ 'stop-color' => array(),
9770
+ 'stop-opacity' => array(),
9771
+ 'stroke' => array(),
9772
+ 'stroke-dasharray' => array(),
9773
+ 'stroke-dashoffset' => array(),
9774
+ 'stroke-linecap' => array(),
9775
+ 'stroke-linejoin' => array(),
9776
+ 'stroke-miterlimit' => array(),
9777
+ 'stroke-opacity' => array(),
9778
+ 'stroke-width' => array(),
9779
+ 'style' => array(
9780
+ 'blacklisted_value_regex' => '!important',
9781
+ ),
9782
+ 'systemlanguage' => array(),
9783
+ 'text-anchor' => array(),
9784
+ 'text-decoration' => array(),
9785
+ 'text-rendering' => array(),
9786
+ 'unicode-bidi' => array(),
9787
+ 'vector-effect' => array(),
9788
+ 'visibility' => array(),
9789
+ 'word-spacing' => array(),
9790
+ 'writing-mode' => array(),
9791
+ 'xlink:actuate' => array(),
9792
+ 'xlink:arcrole' => array(),
9793
+ 'xlink:href' => 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(),
9807
+ 'xlink:show' => array(),
9808
+ 'xlink:title' => array(),
9809
+ 'xlink:type' => array(),
9810
+ 'xml:lang' => array(),
9811
+ 'xml:space' => array(),
9812
+ 'xmlns' => 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(
9828
+ array(
9829
+ 'attr_spec_list' => array(
9830
+ 'abbr' => array(),
9831
+ 'align' => array(),
9832
+ 'bgcolor' => array(),
9833
+ 'colspan' => array(),
9834
+ 'headers' => array(),
9835
+ 'height' => array(),
9836
+ 'rowspan' => array(),
9837
+ 'scope' => array(),
9838
+ 'sorted' => array(),
9839
+ 'valign' => 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(
9852
+ array(
9853
+ 'attr_spec_list' => array(
9854
+ 'datetime' => array(),
9855
+ ),
9856
+ 'tag_spec' => array(),
9857
+ ),
9858
+ ),
9859
+ 'title' => array(
9860
+ array(
9861
+ 'attr_spec_list' => array(),
9862
+ 'tag_spec' => array(
9863
+ 'spec_name' => 'title',
9864
+ ),
9865
+ ),
9866
+ array(
9867
+ 'attr_spec_list' => array(
9868
+ 'style' => array(
9869
+ 'blacklisted_value_regex' => '!important',
9870
+ ),
9871
+ 'xml:lang' => array(),
9872
+ 'xml:space' => array(),
9873
+ 'xmlns' => 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(
9884
+ array(
9885
+ 'attr_spec_list' => array(
9886
+ 'align' => array(),
9887
+ 'bgcolor' => array(),
9888
+ 'height' => array(),
9889
+ 'valign' => array(),
9890
+ ),
9891
+ 'tag_spec' => array(),
9892
+ ),
9893
+ ),
9894
+ 'track' => array(
9895
+ array(
9896
+ 'attr_spec_list' => array(
9897
+ 'default' => array(
9898
+ 'value' => '',
9899
+ ),
9900
+ 'kind' => array(
9901
+ 'value_regex' => '(captions|descriptions|chapters|metadata)',
9902
+ ),
9903
+ 'label' => 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(
9923
+ 'default' => array(
9924
+ 'value' => '',
9925
+ ),
9926
+ 'kind' => array(
9927
+ 'mandatory' => true,
9928
+ 'value_casei' => 'subtitles',
9929
+ ),
9930
+ 'label' => 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(
9942
+ 'mandatory' => true,
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(
9952
+ 'default' => array(
9953
+ 'value' => '',
9954
+ ),
9955
+ 'kind' => array(
9956
+ 'value_regex' => '(captions|descriptions|chapters|metadata)',
9957
+ ),
9958
+ 'label' => 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(
9978
+ 'default' => array(
9979
+ 'value' => '',
9980
+ ),
9981
+ 'kind' => array(
9982
+ 'mandatory' => true,
9983
+ 'value_casei' => 'subtitles',
9984
+ ),
9985
+ 'label' => 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(
9997
+ 'mandatory' => true,
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(
10007
+ '[label]' => array(),
10008
+ '[src]' => array(),
10009
+ '[srclang]' => array(),
10010
+ 'default' => array(
10011
+ 'value' => '',
10012
+ ),
10013
+ 'kind' => array(
10014
+ 'value_regex' => '(captions|descriptions|chapters|metadata)',
10015
+ ),
10016
+ 'label' => 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(
10036
+ '[label]' => array(),
10037
+ '[src]' => array(),
10038
+ '[srclang]' => array(),
10039
+ 'default' => array(
10040
+ 'value' => '',
10041
+ ),
10042
+ 'kind' => array(
10043
+ 'mandatory' => true,
10044
+ 'value_casei' => 'subtitles',
10045
+ ),
10046
+ 'label' => 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(
10058
+ 'mandatory' => true,
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(
10068
+ '[label]' => array(),
10069
+ '[src]' => array(),
10070
+ '[srclang]' => array(),
10071
+ 'default' => array(
10072
+ 'value' => '',
10073
+ ),
10074
+ 'kind' => array(
10075
+ 'value_regex' => '(captions|descriptions|chapters|metadata)',
10076
+ ),
10077
+ 'label' => 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(
10097
+ '[label]' => array(),
10098
+ '[src]' => array(),
10099
+ '[srclang]' => array(),
10100
+ 'default' => array(
10101
+ 'value' => '',
10102
+ ),
10103
+ 'kind' => array(
10104
+ 'mandatory' => true,
10105
+ 'value_casei' => 'subtitles',
10106
+ ),
10107
+ 'label' => 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(
10119
+ 'mandatory' => true,
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(
10129
+ '[label]' => array(),
10130
+ '[src]' => array(),
10131
+ '[srclang]' => array(),
10132
+ 'default' => array(
10133
+ 'value' => '',
10134
+ ),
10135
+ 'kind' => array(
10136
+ 'mandatory' => true,
10137
+ 'value_casei' => 'subtitles',
10138
+ ),
10139
+ 'label' => 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(
10151
+ 'mandatory' => true,
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(
10162
+ array(
10163
+ 'attr_spec_list' => array(
10164
+ 'alignment-baseline' => array(),
10165
+ 'baseline-shift' => array(),
10166
+ 'clip' => array(),
10167
+ 'clip-path' => array(),
10168
+ 'clip-rule' => array(),
10169
+ 'color' => array(),
10170
+ 'color-interpolation' => array(),
10171
+ 'color-interpolation-filters' => array(),
10172
+ 'color-profile' => array(),
10173
+ 'color-rendering' => array(),
10174
+ 'cursor' => array(),
10175
+ 'direction' => array(),
10176
+ 'display' => array(),
10177
+ 'dominant-baseline' => array(),
10178
+ 'enable-background' => array(),
10179
+ 'externalresourcesrequired' => array(),
10180
+ 'fill' => array(),
10181
+ 'fill-opacity' => array(),
10182
+ 'fill-rule' => array(),
10183
+ 'filter' => array(),
10184
+ 'flood-color' => array(),
10185
+ 'flood-opacity' => array(),
10186
+ 'font-family' => array(),
10187
+ 'font-size' => array(),
10188
+ 'font-size-adjust' => array(),
10189
+ 'font-stretch' => array(),
10190
+ 'font-style' => array(),
10191
+ 'font-variant' => array(),
10192
+ 'font-weight' => array(),
10193
+ 'glyph-orientation-horizontal' => array(),
10194
+ 'glyph-orientation-vertical' => array(),
10195
+ 'image-rendering' => array(),
10196
+ 'kerning' => array(),
10197
+ 'letter-spacing' => array(),
10198
+ 'lighting-color' => array(),
10199
+ 'marker-end' => array(),
10200
+ 'marker-mid' => array(),
10201
+ 'marker-start' => array(),
10202
+ 'mask' => array(),
10203
+ 'opacity' => array(),
10204
+ 'overflow' => array(),
10205
+ 'pointer-events' => array(),
10206
+ 'requiredextensions' => array(),
10207
+ 'requiredfeatures' => array(),
10208
+ 'shape-rendering' => array(),
10209
+ 'stop-color' => array(),
10210
+ 'stop-opacity' => array(),
10211
+ 'stroke' => array(),
10212
+ 'stroke-dasharray' => array(),
10213
+ 'stroke-dashoffset' => array(),
10214
+ 'stroke-linecap' => array(),
10215
+ 'stroke-linejoin' => array(),
10216
+ 'stroke-miterlimit' => array(),
10217
+ 'stroke-opacity' => array(),
10218
+ 'stroke-width' => array(),
10219
+ 'style' => array(
10220
+ 'blacklisted_value_regex' => '!important',
10221
+ ),
10222
+ 'systemlanguage' => array(),
10223
+ 'text-anchor' => array(),
10224
+ 'text-decoration' => array(),
10225
+ 'text-rendering' => array(),
10226
+ 'unicode-bidi' => array(),
10227
+ 'vector-effect' => array(),
10228
+ 'visibility' => array(),
10229
+ 'word-spacing' => array(),
10230
+ 'writing-mode' => array(),
10231
+ 'xlink:actuate' => array(),
10232
+ 'xlink:arcrole' => array(),
10233
+ 'xlink:href' => 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(),
10247
+ 'xlink:show' => array(),
10248
+ 'xlink:title' => array(),
10249
+ 'xlink:type' => array(),
10250
+ 'xml:lang' => array(),
10251
+ 'xml:space' => array(),
10252
+ 'xmlns' => 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(
10262
+ array(
10263
+ 'attr_spec_list' => array(
10264
+ 'alignment-baseline' => array(),
10265
+ 'baseline-shift' => array(),
10266
+ 'clip' => array(),
10267
+ 'clip-path' => array(),
10268
+ 'clip-rule' => array(),
10269
+ 'color' => array(),
10270
+ 'color-interpolation' => array(),
10271
+ 'color-interpolation-filters' => array(),
10272
+ 'color-profile' => array(),
10273
+ 'color-rendering' => array(),
10274
+ 'cursor' => array(),
10275
+ 'direction' => array(),
10276
+ 'display' => array(),
10277
+ 'dominant-baseline' => array(),
10278
+ 'dx' => array(),
10279
+ 'dy' => array(),
10280
+ 'enable-background' => array(),
10281
+ 'externalresourcesrequired' => array(),
10282
+ 'fill' => array(),
10283
+ 'fill-opacity' => array(),
10284
+ 'fill-rule' => array(),
10285
+ 'filter' => array(),
10286
+ 'flood-color' => array(),
10287
+ 'flood-opacity' => array(),
10288
+ 'font-family' => array(),
10289
+ 'font-size' => array(),
10290
+ 'font-size-adjust' => array(),
10291
+ 'font-stretch' => array(),
10292
+ 'font-style' => array(),
10293
+ 'font-variant' => array(),
10294
+ 'font-weight' => array(),
10295
+ 'glyph-orientation-horizontal' => array(),
10296
+ 'glyph-orientation-vertical' => array(),
10297
+ 'image-rendering' => array(),
10298
+ 'kerning' => array(),
10299
+ 'lengthadjust' => array(),
10300
+ 'letter-spacing' => array(),
10301
+ 'lighting-color' => array(),
10302
+ 'marker-end' => array(),
10303
+ 'marker-mid' => array(),
10304
+ 'marker-start' => array(),
10305
+ 'mask' => array(),
10306
+ 'opacity' => array(),
10307
+ 'overflow' => array(),
10308
+ 'pointer-events' => array(),
10309
+ 'requiredextensions' => array(),
10310
+ 'requiredfeatures' => array(),
10311
+ 'rotate' => array(),
10312
+ 'shape-rendering' => array(),
10313
+ 'stop-color' => array(),
10314
+ 'stop-opacity' => array(),
10315
+ 'stroke' => array(),
10316
+ 'stroke-dasharray' => array(),
10317
+ 'stroke-dashoffset' => array(),
10318
+ 'stroke-linecap' => array(),
10319
+ 'stroke-linejoin' => array(),
10320
+ 'stroke-miterlimit' => array(),
10321
+ 'stroke-opacity' => array(),
10322
+ 'stroke-width' => array(),
10323
+ 'style' => array(
10324
+ 'blacklisted_value_regex' => '!important',
10325
+ ),
10326
+ 'systemlanguage' => array(),
10327
+ 'text-anchor' => array(),
10328
+ 'text-decoration' => array(),
10329
+ 'text-rendering' => array(),
10330
+ 'textlength' => array(),
10331
+ 'unicode-bidi' => array(),
10332
+ 'vector-effect' => array(),
10333
+ 'visibility' => array(),
10334
+ 'word-spacing' => array(),
10335
+ 'writing-mode' => array(),
10336
+ 'x' => array(),
10337
+ 'xml:lang' => array(),
10338
+ 'xml:space' => array(),
10339
+ 'xmlns' => array(),
10340
+ 'xmlns:xlink' => 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(
10368
+ array(
10369
+ 'attr_spec_list' => array(
10370
+ 'alignment-baseline' => array(),
10371
+ 'baseline-shift' => array(),
10372
+ 'clip' => array(),
10373
+ 'clip-path' => array(),
10374
+ 'clip-rule' => array(),
10375
+ 'color' => array(),
10376
+ 'color-interpolation' => array(),
10377
+ 'color-interpolation-filters' => array(),
10378
+ 'color-profile' => array(),
10379
+ 'color-rendering' => array(),
10380
+ 'cursor' => array(),
10381
+ 'direction' => array(),
10382
+ 'display' => array(),
10383
+ 'dominant-baseline' => array(),
10384
+ 'enable-background' => array(),
10385
+ 'externalresourcesrequired' => array(),
10386
+ 'fill' => array(),
10387
+ 'fill-opacity' => array(),
10388
+ 'fill-rule' => array(),
10389
+ 'filter' => array(),
10390
+ 'flood-color' => array(),
10391
+ 'flood-opacity' => array(),
10392
+ 'font-family' => array(),
10393
+ 'font-size' => array(),
10394
+ 'font-size-adjust' => array(),
10395
+ 'font-stretch' => array(),
10396
+ 'font-style' => array(),
10397
+ 'font-variant' => array(),
10398
+ 'font-weight' => array(),
10399
+ 'glyph-orientation-horizontal' => array(),
10400
+ 'glyph-orientation-vertical' => array(),
10401
+ 'height' => array(),
10402
+ 'image-rendering' => array(),
10403
+ 'kerning' => array(),
10404
+ 'letter-spacing' => array(),
10405
+ 'lighting-color' => array(),
10406
+ 'marker-end' => array(),
10407
+ 'marker-mid' => array(),
10408
+ 'marker-start' => array(),
10409
+ 'mask' => array(),
10410
+ 'opacity' => array(),
10411
+ 'overflow' => array(),
10412
+ 'pointer-events' => array(),
10413
+ 'requiredextensions' => array(),
10414
+ 'requiredfeatures' => array(),
10415
+ 'shape-rendering' => array(),
10416
+ 'stop-color' => array(),
10417
+ 'stop-opacity' => array(),
10418
+ 'stroke' => array(),
10419
+ 'stroke-dasharray' => array(),
10420
+ 'stroke-dashoffset' => array(),
10421
+ 'stroke-linecap' => array(),
10422
+ 'stroke-linejoin' => array(),
10423
+ 'stroke-miterlimit' => array(),
10424
+ 'stroke-opacity' => array(),
10425
+ 'stroke-width' => array(),
10426
+ 'style' => array(
10427
+ 'blacklisted_value_regex' => '!important',
10428
+ ),
10429
+ 'systemlanguage' => array(),
10430
+ 'text-anchor' => array(),
10431
+ 'text-decoration' => array(),
10432
+ 'text-rendering' => array(),
10433
+ 'transform' => array(),
10434
+ 'unicode-bidi' => array(),
10435
+ 'vector-effect' => array(),
10436
+ 'visibility' => array(),
10437
+ 'width' => array(),
10438
+ 'word-spacing' => array(),
10439
+ 'writing-mode' => array(),
10440
+ 'x' => array(),
10441
+ 'xlink:actuate' => array(),
10442
+ 'xlink:arcrole' => array(),
10443
+ 'xlink:href' => 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(),
10457
+ 'xlink:show' => array(),
10458
+ 'xlink:title' => array(),
10459
+ 'xlink:type' => array(),
10460
+ 'xml:lang' => array(),
10461
+ 'xml:space' => array(),
10462
+ 'xmlns' => array(),
10463
+ 'xmlns:xlink' => 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(
10479
+ array(
10480
+ 'attr_spec_list' => array(
10481
+ 'autoplay' => array(),
10482
+ 'controls' => array(),
10483
+ 'height' => array(),
10484
+ 'loop' => array(),
10485
+ 'muted' => array(),
10486
+ 'playsinline' => array(),
10487
+ 'poster' => 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(
10509
+ array(
10510
+ 'attr_spec_list' => array(
10511
+ 'externalresourcesrequired' => array(),
10512
+ 'preserveaspectratio' => array(),
10513
+ 'style' => array(
10514
+ 'blacklisted_value_regex' => '!important',
10515
+ ),
10516
+ 'viewbox' => array(),
10517
+ 'viewtarget' => array(),
10518
+ 'xml:lang' => array(),
10519
+ 'xml:space' => array(),
10520
+ 'xmlns' => array(),
10521
+ 'xmlns:xlink' => 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(
10531
+ array(
10532
+ 'attr_spec_list' => array(
10533
+ 'g1' => array(),
10534
+ 'g2' => array(),
10535
+ 'k' => array(),
10536
+ 'style' => array(
10537
+ 'blacklisted_value_regex' => '!important',
10538
+ ),
10539
+ 'u1' => array(),
10540
+ 'u2' => array(),
10541
+ 'xml:lang' => array(),
10542
+ 'xml:space' => array(),
10543
+ 'xmlns' => 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
+ );
10565
+
10566
+ private static $layout_allowed_attrs = array(
10567
+ '[height]' => array(),
10568
+ '[width]' => array(),
10569
+ 'height' => array(),
10570
+ 'heights' => array(),
10571
+ 'layout' => array(),
10572
+ 'sizes' => array(),
10573
+ 'width' => array(),
10574
+ );
10575
+
10576
+
10577
+ private static $globally_allowed_attrs = array(
10578
+ '[aria-activedescendant]' => array(),
10579
+ '[aria-atomic]' => array(),
10580
+ '[aria-autocomplete]' => array(),
10581
+ '[aria-busy]' => array(),
10582
+ '[aria-checked]' => array(),
10583
+ '[aria-controls]' => array(),
10584
+ '[aria-describedby]' => array(),
10585
+ '[aria-disabled]' => array(),
10586
+ '[aria-dropeffect]' => array(),
10587
+ '[aria-expanded]' => array(),
10588
+ '[aria-flowto]' => array(),
10589
+ '[aria-grabbed]' => array(),
10590
+ '[aria-haspopup]' => array(),
10591
+ '[aria-hidden]' => array(),
10592
+ '[aria-invalid]' => array(),
10593
+ '[aria-label]' => array(),
10594
+ '[aria-labelledby]' => array(),
10595
+ '[aria-level]' => array(),
10596
+ '[aria-live]' => array(),
10597
+ '[aria-multiline]' => array(),
10598
+ '[aria-multiselectable]' => array(),
10599
+ '[aria-orientation]' => array(),
10600
+ '[aria-owns]' => array(),
10601
+ '[aria-posinset]' => array(),
10602
+ '[aria-pressed]' => array(),
10603
+ '[aria-readonly]' => array(),
10604
+ '[aria-relevant]' => array(),
10605
+ '[aria-required]' => array(),
10606
+ '[aria-selected]' => array(),
10607
+ '[aria-setsize]' => array(),
10608
+ '[aria-sort]' => array(),
10609
+ '[aria-valuemax]' => array(),
10610
+ '[aria-valuemin]' => array(),
10611
+ '[aria-valuenow]' => array(),
10612
+ '[aria-valuetext]' => array(),
10613
+ '[class]' => array(),
10614
+ '[hidden]' => array(),
10615
+ '[text]' => array(),
10616
+ 'about' => array(),
10617
+ 'accesskey' => array(),
10618
+ 'amp-access' => array(),
10619
+ 'amp-access-behavior' => array(),
10620
+ 'amp-access-hide' => array(),
10621
+ 'amp-access-id' => array(),
10622
+ 'amp-access-loader' => array(),
10623
+ 'amp-access-loading' => array(),
10624
+ 'amp-access-off' => array(),
10625
+ 'amp-access-on' => array(),
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(),
10635
+ 'aria-busy' => array(),
10636
+ 'aria-checked' => array(),
10637
+ 'aria-controls' => array(),
10638
+ 'aria-current' => array(),
10639
+ 'aria-describedby' => array(),
10640
+ 'aria-disabled' => array(),
10641
+ 'aria-dropeffect' => array(),
10642
+ 'aria-expanded' => array(),
10643
+ 'aria-flowto' => array(),
10644
+ 'aria-grabbed' => array(),
10645
+ 'aria-haspopup' => array(),
10646
+ 'aria-hidden' => array(),
10647
+ 'aria-invalid' => array(),
10648
+ 'aria-label' => array(),
10649
+ 'aria-labelledby' => array(),
10650
+ 'aria-level' => array(),
10651
+ 'aria-live' => array(),
10652
+ 'aria-multiline' => array(),
10653
+ 'aria-multiselectable' => array(),
10654
+ 'aria-orientation' => array(),
10655
+ 'aria-owns' => array(),
10656
+ 'aria-posinset' => array(),
10657
+ 'aria-pressed' => array(),
10658
+ 'aria-readonly' => array(),
10659
+ 'aria-relevant' => array(),
10660
+ 'aria-required' => array(),
10661
+ 'aria-selected' => array(),
10662
+ 'aria-setsize' => array(),
10663
+ 'aria-sort' => array(),
10664
+ 'aria-valuemax' => array(),
10665
+ 'aria-valuemin' => 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(),
10687
+ 'itemprop' => array(),
10688
+ 'itemref' => array(),
10689
+ 'itemscope' => array(),
10690
+ 'itemtype' => array(),
10691
+ 'lang' => array(),
10692
+ 'lightbox' => 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
+ }
includes/vendor/amp/includes/sanitizers/class-amp-comments-sanitizer.php ADDED
@@ -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
+ }
includes/vendor/amp/includes/sanitizers/class-amp-form-sanitizer.php ADDED
@@ -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
+ }
includes/vendor/amp/includes/sanitizers/class-amp-rule-spec.php ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Rule_Spec
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Class AMP_Rule_Spec
10
+ *
11
+ * Set of constants used throughout the sanitizer.
12
+ */
13
+ abstract class AMP_Rule_Spec {
14
+
15
+ /**
16
+ * AMP rule_spec types
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
33
+ */
34
+ const DISALLOWED_ANCESTOR = 'disallowed_ancestor';
35
+ const MANDATORY_ANCESTOR = 'mandatory_ancestor';
36
+ const MANDATORY_PARENT = 'mandatory_parent';
37
+
38
+ /**
39
+ * HTML Element Attribute rule names
40
+ */
41
+ const ALLOW_EMPTY = 'allow_empty';
42
+ const ALLOW_RELATIVE = 'allow_relative';
43
+ const ALLOWED_PROTOCOL = 'allowed_protocol';
44
+ const ALTERNATIVE_NAMES = 'alternative_names';
45
+ const BLACKLISTED_VALUE_REGEX = 'blacklisted_value_regex';
46
+ const DISALLOWED_DOMAIN = 'disallowed_domain';
47
+ const MANDATORY = 'mandatory';
48
+ const VALUE = 'value';
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
57
+ * removed if it is invalid. This is mainly because any children will be
58
+ * non-functional without this parent.
59
+ *
60
+ * If a tag is not listed here, it will be replaced by its children if it
61
+ * is invalid.
62
+ *
63
+ * @todo There are other nodes that should probably be listed here as well.
64
+ *
65
+ * @var array
66
+ */
67
+ public static $node_types_to_remove_if_invalid = array(
68
+ 'form',
69
+ 'input',
70
+ 'link',
71
+ 'meta',
72
+ 'style',
73
+ // Include 'script' here?
74
+ );
75
+
76
+ /**
77
+ * It is mentioned in the documentation in several places that data-*
78
+ * is generally allowed, but there is no specific rule for it in the
79
+ * protoascii file, so we include it here.
80
+ *
81
+ * @var array
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
+ /**
142
+ * Additional allowed tags.
143
+ *
144
+ * @var array
145
+ */
146
+ public static $additional_allowed_tags = array(
147
+
148
+ // An experimental tag with no protoascii.
149
+ 'amp-share-tracking' => array(
150
+ 'attr_spec_list' => array(),
151
+ 'tag_spec' => array(),
152
+ ),
153
+ );
154
+ }
includes/vendor/amp/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php ADDED
@@ -0,0 +1,1630 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Tag_And_Attribute_Sanitizer
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Strips the tags and attributes from the content that are not allowed by the AMP spec.
10
+ *
11
+ * Allowed tags array is generated from this protocol buffer:
12
+ *
13
+ * https://github.com/ampproject/amphtml/blob/master/validator/validator-main.protoascii
14
+ * by the python script in amp-wp/bin/amp_wp_build.py. See the comment at the top
15
+ * of that file for instructions to generate class-amp-allowed-tags-generated.php.
16
+ *
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.
23
+ * - `mandatory_oneof` - Within the context of the tag, exactly one of the attributes
24
+ * must be present.
25
+ */
26
+ class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {
27
+
28
+ /**
29
+ * Allowed tags.
30
+ *
31
+ * @since 0.5
32
+ *
33
+ * @var string[]
34
+ */
35
+ protected $allowed_tags;
36
+
37
+ /**
38
+ * Globally-allowed attributes.
39
+ *
40
+ * @since 0.5
41
+ *
42
+ * @var array[][]
43
+ */
44
+ protected $globally_allowed_attributes;
45
+
46
+ /**
47
+ * Layout-allowed attributes.
48
+ *
49
+ * @since 0.5
50
+ *
51
+ * @var string[]
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
+ *
66
+ * @since 0.5
67
+ *
68
+ * @var DOMElement[]
69
+ */
70
+ private $stack = array();
71
+
72
+ /**
73
+ * Default args.
74
+ *
75
+ * @since 0.5
76
+ *
77
+ * @var array
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
+ *
91
+ * @since 0.5
92
+ *
93
+ * @param DOMDocument $dom DOM.
94
+ * @param array $args Args.
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.
247
+ */
248
+ while ( ! empty( $this->stack ) ) {
249
+
250
+ // Get the next node to process.
251
+ $node = array_pop( $this->stack );
252
+
253
+ /**
254
+ * Process this node.
255
+ */
256
+ $this->process_node( $node );
257
+
258
+ /*
259
+ * Push child nodes onto the stack, if any exist.
260
+ * if node was removed, then it's parentNode value is null.
261
+ */
262
+ if ( $node->parentNode ) {
263
+ $child = $node->firstChild;
264
+ while ( $child ) {
265
+ $this->stack[] = $child;
266
+ $child = $child->nextSibling;
267
+ }
268
+ }
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Process a node by sanitizing and/or validating it per.
274
+ *
275
+ * @param DOMNode $node Node.
276
+ */
277
+ private function process_node( $node ) {
278
+
279
+ // Don't process text or comment nodes.
280
+ if ( XML_TEXT_NODE === $node->nodeType || XML_COMMENT_NODE === $node->nodeType || XML_CDATA_SECTION_NODE === $node->nodeType ) {
281
+ return;
282
+ }
283
+
284
+ // Remove nodes with tags that have not been whitelisted.
285
+ if ( ! $this->is_amp_allowed_tag( $node ) ) {
286
+
287
+ // If it's not an allowed tag, replace the node with it's children.
288
+ $this->replace_node_with_children( $node );
289
+
290
+ // Return early since this node no longer exists.
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.
303
+ */
304
+ $rule_spec_list_to_validate = array();
305
+ $rule_spec_list = array();
306
+ if ( isset( $this->allowed_tags[ $node->nodeName ] ) ) {
307
+ $rule_spec_list = $this->allowed_tags[ $node->nodeName ];
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
+ }
335
+
336
+ // If no valid rule_specs exist, then remove this node and return.
337
+ if ( empty( $rule_spec_list_to_validate ) ) {
338
+ $this->remove_node( $node );
339
+ return;
340
+ }
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
349
+ * to validate the node's attributes.
350
+ */
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,
361
+ * then try to deduce which one is intended by inspecting
362
+ * the node's attributes.
363
+ */
364
+
365
+ /*
366
+ * Get a score from each attr_spec_list by seeing how many
367
+ * attributes and values match the node.
368
+ */
369
+ $attr_spec_scores = array();
370
+ foreach ( $rule_spec_list_to_validate as $spec_id => $rule_spec ) {
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
396
+ // be used. Let's use the top scoring ones.
397
+ foreach ( $spec_ids_sorted as $id ) {
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 );
411
+ if ( empty( $attr_spec_list ) && isset( $first_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ] ) ) {
412
+ $attr_spec_list = $first_spec[ AMP_Rule_Spec::ATTR_SPEC_LIST ];
413
+ }
414
+ }
415
+ } // End if().
416
+
417
+ if ( ! empty( $attr_spec_list ) && $this->is_missing_mandatory_attribute( $attr_spec_list, $node ) ) {
418
+ $this->remove_node( $node );
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 ) {
487
+ if ( ! is_array( $attr_spec ) ) {
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
+ }
510
+ }
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
+ *
535
+ * Checks to see if a node's placement with the DOM is be valid for the
536
+ * given tag_spec. If there are restrictions placed on the type of node
537
+ * that can be an immediate parent or an ancestor of this node, then make
538
+ * sure those restrictions are met.
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 ) ) {
560
+ return false;
561
+ }
562
+ }
563
+ }
564
+
565
+ if ( ! empty( $tag_spec[ AMP_Rule_Spec::MANDATORY_ANCESTOR ] ) && ! $this->has_ancestor( $node, $tag_spec[ AMP_Rule_Spec::MANDATORY_ANCESTOR ] ) ) {
566
+ return false;
567
+ }
568
+
569
+ return true;
570
+ }
571
+
572
+ /**
573
+ * Checks to see if a spec is potentially valid.
574
+ *
575
+ * Checks the given node based on the attributes present in the node.
576
+ *
577
+ * @note This can be a very expensive function. Use it sparingly.
578
+ *
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
+
606
+ foreach ( $node->attributes as $attr_name => $attr_node ) {
607
+ if ( ! isset( $attr_spec_list[ $attr_name ][ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
608
+ continue;
609
+ }
610
+ foreach ( $attr_spec_list[ $attr_name ][ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $attr_alt_name ) {
611
+ $attr_spec_list[ $attr_alt_name ] = $attr_spec_list[ $attr_name ];
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`,
627
+ * and an `$attr_spec_rule`. If the `$attr_spec_rule` seems to be valid
628
+ * for the given node, then the filter should increment the score by one.
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
+
649
+ /*
650
+ * Check 'value' - case sensitive
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
+
662
+ /*
663
+ * Check 'value_regex' - case sensitive regex match
664
+ * Given attribute's value must be a case insensitive match to regex pattern
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
+
676
+ /*
677
+ * Check 'value_casei' - case insensitive
678
+ * Given attribute's value must be a case insensitive match to the value of
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
+
690
+ /*
691
+ * Check 'value_regex_casei' - case insensitive regex match
692
+ * Given attribute's value must be a case insensitive match to the regex
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
+
704
+ /*
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
+
730
+ /*
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
+
743
+ /*
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
+
756
+ /*
757
+ * If the given attribute's value is a URL and does not match any of the list
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
+
769
+ /*
770
+ * If the attribute's value exists and does not match the regex specified
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;
799
+ }
800
+
801
+ /**
802
+ * Remove attributes from $node that are not listed in $allowed_attrs.
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
+ /*
819
+ * We can't remove attributes inside the 'foreach' loop without
820
+ * breaking the iteration. So we keep track of the attributes to
821
+ * remove in the first loop, then remove them in the second loop.
822
+ */
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
+ /**
834
+ * Remove invalid AMP attributes values from $node that have been implicitly disallowed.
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
+ /**
864
+ * Remove attributes values from $node that have been disallowed by AMP.
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 ) {
877
+ if ( isset( $attr_spec_list[ $attr_name ][ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
878
+ foreach ( $attr_spec_list[ $attr_name ][ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $attr_alt_name ) {
879
+ $attr_spec_list[ $attr_alt_name ] = $attr_spec_list[ $attr_name ];
880
+ }
881
+ }
882
+ }
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
+
890
+ $should_remove_node = false;
891
+ $attr_spec_rule = $attr_spec_list[ $attr_name ];
892
+
893
+ if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE ] ) &&
894
+ AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_value( $node, $attr_name, $attr_spec_rule ) ) {
895
+ $should_remove_node = true;
896
+ } elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_CASEI ] ) &&
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 ] ) &&
918
+ AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_disallowed_domain( $node, $attr_name, $attr_spec_rule ) ) {
919
+ $should_remove_node = true;
920
+ } elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::BLACKLISTED_VALUE_REGEX ] ) &&
921
+ AMP_Rule_Spec::FAIL === $this->check_attr_spec_rule_blacklisted_value_regex( $node, $attr_name, $attr_spec_rule ) ) {
922
+ $should_remove_node = true;
923
+ }
924
+
925
+ if ( $should_remove_node ) {
926
+ $is_mandatory =
927
+ isset( $attr_spec_rule[ AMP_Rule_Spec::MANDATORY ] )
928
+ ? (bool) $attr_spec_rule[ AMP_Rule_Spec::MANDATORY ]
929
+ : false;
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
+ /**
954
+ * Check if attribute is mandatory determine whether it exists in $node.
955
+ *
956
+ * When checking for the given attribute it also checks valid alternates.
957
+ *
958
+ * @param DOMElement $node Node.
959
+ * @param string $attr_name Attribute name.
960
+ * @param array[] $attr_spec_rule Attribute spec rule.
961
+ *
962
+ * @return string:
963
+ * - AMP_Rule_Spec::PASS - $attr_name is mandatory and it exists
964
+ * - AMP_Rule_Spec::FAIL - $attr_name is mandatory, but doesn't exist
965
+ * - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name is not mandatory
966
+ */
967
+ private function check_attr_spec_rule_mandatory( $node, $attr_name, $attr_spec_rule ) {
968
+ if ( isset( $attr_spec_rule[ AMP_Rule_Spec::MANDATORY ] ) && ( $attr_spec_rule[ AMP_Rule_Spec::MANDATORY ] ) ) {
969
+ if ( $node->hasAttribute( $attr_name ) ) {
970
+ return AMP_Rule_Spec::PASS;
971
+ } else {
972
+ // Check if an alternative name list is specified.
973
+ if ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
974
+ foreach ( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alt_name ) {
975
+ if ( $node->hasAttribute( $alt_name ) ) {
976
+ return AMP_Rule_Spec::PASS;
977
+ }
978
+ }
979
+ }
980
+ return AMP_Rule_Spec::FAIL;
981
+ }
982
+ }
983
+ return AMP_Rule_Spec::NOT_APPLICABLE;
984
+ }
985
+
986
+ /**
987
+ * Check if attribute has a value rule determine if its value is valid.
988
+ *
989
+ * Checks for value validity by matches against valid values.
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.
997
+ * - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
998
+ * - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
999
+ * is no rule for this attribute.
1000
+ */
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;
1008
+ }
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;
1016
+ }
1017
+ }
1018
+ }
1019
+ }
1020
+ }
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
+ *
1054
+ * @param DOMElement $node Node.
1055
+ * @param string $attr_name Attribute name.
1056
+ * @param array[]|string[] $attr_spec_rule Attribute spec rule.
1057
+ *
1058
+ * @return string:
1059
+ * - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
1060
+ * - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
1061
+ * - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
1062
+ * is no rule for this attribute.
1063
+ */
1064
+ private function check_attr_spec_rule_value_casei( $node, $attr_name, $attr_spec_rule ) {
1065
+ /**
1066
+ * Check 'value_casei' - case insensitive
1067
+ */
1068
+ if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_CASEI ] ) ) {
1069
+ $rule_value = strtolower( $attr_spec_rule[ AMP_Rule_Spec::VALUE_CASEI ] );
1070
+ if ( $node->hasAttribute( $attr_name ) ) {
1071
+ $attr_value = strtolower( $node->getAttribute( $attr_name ) );
1072
+ if ( $attr_value === (string) $rule_value ) {
1073
+ return AMP_Rule_Spec::PASS;
1074
+ } else {
1075
+ return AMP_Rule_Spec::FAIL;
1076
+ }
1077
+ } elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
1078
+ foreach ( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_name ) {
1079
+ if ( $node->hasAttribute( $alternative_name ) ) {
1080
+ $attr_value = strtolower( $node->getAttribute( $alternative_name ) );
1081
+ if ( $attr_value === (string) $rule_value ) {
1082
+ return AMP_Rule_Spec::PASS;
1083
+ } else {
1084
+ return AMP_Rule_Spec::FAIL;
1085
+ }
1086
+ }
1087
+ }
1088
+ }
1089
+ }
1090
+ return AMP_Rule_Spec::NOT_APPLICABLE;
1091
+ }
1092
+
1093
+ /**
1094
+ * Check if attribute has a regex value rule determine if it matches.
1095
+ *
1096
+ * @param DOMElement $node Node.
1097
+ * @param string $attr_name Attribute name.
1098
+ * @param array[]|string[] $attr_spec_rule Attribute spec rule.
1099
+ *
1100
+ * @return string:
1101
+ * - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
1102
+ * - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
1103
+ * - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
1104
+ * is no rule for this attribute.
1105
+ */
1106
+ private function check_attr_spec_rule_value_regex( $node, $attr_name, $attr_spec_rule ) {
1107
+ // Check 'value_regex' - case sensitive regex match.
1108
+ if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX ] ) && $node->hasAttribute( $attr_name ) ) {
1109
+ $rule_value = $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX ];
1110
+
1111
+ /*
1112
+ * The regex pattern has '^' and '$' though they are not in the AMP spec.
1113
+ * Leaving them out would allow both '_blank' and 'yyy_blankzzz' to be
1114
+ * matched by a regex rule of '(_blank|_self|_top)'. As the AMP JS validator
1115
+ * only accepts '_blank' we leave it this way for now.
1116
+ */
1117
+ if ( preg_match( '@^' . $rule_value . '$@u', $node->getAttribute( $attr_name ) ) ) {
1118
+ return AMP_Rule_Spec::PASS;
1119
+ } else {
1120
+ return AMP_Rule_Spec::FAIL;
1121
+ }
1122
+ }
1123
+ return AMP_Rule_Spec::NOT_APPLICABLE;
1124
+ }
1125
+
1126
+ /**
1127
+ * Check if attribute has a case-insensitive regex value rule determine if it matches.
1128
+ *
1129
+ * @param DOMElement $node Node.
1130
+ * @param string $attr_name Attribute name.
1131
+ * @param array[]|string[] $attr_spec_rule Attribute spec rule.
1132
+ *
1133
+ * @return string:
1134
+ * - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
1135
+ * - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
1136
+ * - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
1137
+ * is no rule for this attribute.
1138
+ */
1139
+ private function check_attr_spec_rule_value_regex_casei( $node, $attr_name, $attr_spec_rule ) {
1140
+ /**
1141
+ * Check 'value_regex_casei' - case insensitive regex match
1142
+ */
1143
+ if ( isset( $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX_CASEI ] ) && $node->hasAttribute( $attr_name ) ) {
1144
+ $rule_value = $attr_spec_rule[ AMP_Rule_Spec::VALUE_REGEX_CASEI ];
1145
+
1146
+ // See note above regarding the '^' and '$' that are added here.
1147
+ if ( preg_match( '/^' . $rule_value . '$/ui', $node->getAttribute( $attr_name ) ) ) {
1148
+ return AMP_Rule_Spec::PASS;
1149
+ } else {
1150
+ return AMP_Rule_Spec::FAIL;
1151
+ }
1152
+ }
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
+ *
1200
+ * @param DOMElement $node Node.
1201
+ * @param string $attr_name Attribute name.
1202
+ * @param array[]|string[] $attr_spec_rule Attribute spec rule.
1203
+ *
1204
+ * @return string:
1205
+ * - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
1206
+ * - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
1207
+ * - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
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
1217
+ * will allow a URL with no protocol to pass validation.
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
+ }
1225
+ }
1226
+ return AMP_Rule_Spec::PASS;
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
1234
+ * will allow a URL with no protocol to pass validation.
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
+ }
1242
+ }
1243
+ return AMP_Rule_Spec::PASS;
1244
+ }
1245
+ }
1246
+ }
1247
+ }
1248
+ return AMP_Rule_Spec::NOT_APPLICABLE;
1249
+ }
1250
+
1251
+ /**
1252
+ * Check if attribute has disallowed relative value rule determine if disallowed relative value matches.
1253
+ *
1254
+ * @param DOMElement $node Node.
1255
+ * @param string $attr_name Attribute name.
1256
+ * @param array[]|string[] $attr_spec_rule Attribute spec rule.
1257
+ *
1258
+ * @return string:
1259
+ * - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
1260
+ * - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
1261
+ * - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
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
+
1271
+ /*
1272
+ * The JS AMP validator seems to consider 'relative' to mean
1273
+ * *protocol* relative, not *host* relative for this rule. So,
1274
+ * a url with an empty 'scheme' is considered "relative" by AMP.
1275
+ * ie. '//domain.com/path' and '/path' should both be considered
1276
+ * relative for purposes of AMP validation.
1277
+ */
1278
+ if ( empty( $parsed_url['scheme'] ) ) {
1279
+ return AMP_Rule_Spec::FAIL;
1280
+ }
1281
+ }
1282
+ return AMP_Rule_Spec::PASS;
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'] ) ) {
1290
+ return AMP_Rule_Spec::FAIL;
1291
+ }
1292
+ }
1293
+ }
1294
+ }
1295
+ return AMP_Rule_Spec::PASS;
1296
+ }
1297
+ }
1298
+ return AMP_Rule_Spec::NOT_APPLICABLE;
1299
+ }
1300
+
1301
+ /**
1302
+ * Check if attribute has disallowed empty value rule determine if value is empty.
1303
+ *
1304
+ * @param DOMElement $node Node.
1305
+ * @param string $attr_name Attribute name.
1306
+ * @param array[]|string[] $attr_spec_rule Attribute spec rule.
1307
+ *
1308
+ * @return string:
1309
+ * - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
1310
+ * - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
1311
+ * - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
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;
1319
+ }
1320
+ return AMP_Rule_Spec::PASS;
1321
+ }
1322
+ return AMP_Rule_Spec::NOT_APPLICABLE;
1323
+ }
1324
+
1325
+ /**
1326
+ * Check if attribute has disallowed domain value rule determine if value matches.
1327
+ *
1328
+ * @param DOMElement $node Node.
1329
+ * @param string $attr_name Attribute name.
1330
+ * @param array[]|string[] $attr_spec_rule Attribute spec rule.
1331
+ *
1332
+ * @return string:
1333
+ * - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
1334
+ * - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
1335
+ * - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
1336
+ * is no rule for this attribute.
1337
+ */
1338
+ private function check_attr_spec_rule_disallowed_domain( $node, $attr_name, $attr_spec_rule ) {
1339
+ if ( isset( $attr_spec_rule[ AMP_Rule_Spec::DISALLOWED_DOMAIN ] ) && $node->hasAttribute( $attr_name ) ) {
1340
+ $attr_value = $node->getAttribute( $attr_name );
1341
+ $url_domain = AMP_WP_Utils::parse_url( $attr_value, PHP_URL_HOST );
1342
+ if ( ! empty( $url_domain ) ) {
1343
+ foreach ( $attr_spec_rule[ AMP_Rule_Spec::DISALLOWED_DOMAIN ] as $disallowed_domain ) {
1344
+ if ( strtolower( $url_domain ) === strtolower( $disallowed_domain ) ) {
1345
+
1346
+ // Found a disallowed domain, fail validation.
1347
+ return AMP_Rule_Spec::FAIL;
1348
+ }
1349
+ }
1350
+ return AMP_Rule_Spec::PASS;
1351
+ }
1352
+ }
1353
+ return AMP_Rule_Spec::NOT_APPLICABLE;
1354
+ }
1355
+
1356
+ /**
1357
+ * Check if attribute has blacklisted value via regex match determine if value matches.
1358
+ *
1359
+ * @since 0.5
1360
+ *
1361
+ * @param DOMElement $node Node.
1362
+ * @param string $attr_name Attribute name.
1363
+ * @param array[]|string[] $attr_spec_rule Attribute spec rule.
1364
+ *
1365
+ * @return string:
1366
+ * - AMP_Rule_Spec::PASS - $attr_name has a value that matches the rule.
1367
+ * - AMP_Rule_Spec::FAIL - $attr_name has a value that does *not* match rule.
1368
+ * - AMP_Rule_Spec::NOT_APPLICABLE - $attr_name does not exist or there
1369
+ * is no rule for this attribute.
1370
+ */
1371
+ private function check_attr_spec_rule_blacklisted_value_regex( $node, $attr_name, $attr_spec_rule ) {
1372
+ if ( isset( $attr_spec_rule[ AMP_Rule_Spec::BLACKLISTED_VALUE_REGEX ] ) ) {
1373
+ $pattern = '/' . $attr_spec_rule[ AMP_Rule_Spec::BLACKLISTED_VALUE_REGEX ] . '/u';
1374
+ if ( $node->hasAttribute( $attr_name ) ) {
1375
+ $attr_value = $node->getAttribute( $attr_name );
1376
+ if ( preg_match( $pattern, $attr_value ) ) {
1377
+ return AMP_Rule_Spec::FAIL;
1378
+ } else {
1379
+ return AMP_Rule_Spec::PASS;
1380
+ }
1381
+ } elseif ( isset( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] ) ) {
1382
+ foreach ( $attr_spec_rule[ AMP_Rule_Spec::ALTERNATIVE_NAMES ] as $alternative_name ) {
1383
+ if ( $node->hasAttribute( $alternative_name ) ) {
1384
+ $attr_value = $node->getAttribute( $alternative_name );
1385
+ if ( preg_match( $pattern, $attr_value ) ) {
1386
+ return AMP_Rule_Spec::FAIL;
1387
+ } else {
1388
+ return AMP_Rule_Spec::PASS;
1389
+ }
1390
+ }
1391
+ }
1392
+ }
1393
+ }
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
+ *
1461
+ * @since 0.5
1462
+ *
1463
+ * @param string $attr_name Attribute name.
1464
+ * @param array[]|string[] $attr_spec_list Attribute spec list.
1465
+ * @return bool Return true if attribute name is valid for this attr_spec_list, false otherwise.
1466
+ */
1467
+ private function is_amp_allowed_attribute( $attr_name, $attr_spec_list ) {
1468
+ if ( isset( $this->globally_allowed_attributes[ $attr_name ] ) || isset( $this->layout_allowed_attributes[ $attr_name ] ) || isset( $attr_spec_list[ $attr_name ] ) ) {
1469
+ return true;
1470
+ } else {
1471
+ foreach ( AMP_Rule_Spec::$whitelisted_attr_regex as $whitelisted_attr_regex ) {
1472
+ if ( preg_match( $whitelisted_attr_regex, $attr_name ) ) {
1473
+ return true;
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
+
1490
+ /**
1491
+ * Determine if the supplied $node's HTML tag is allowed for AMP.
1492
+ *
1493
+ * @since 0.5
1494
+ *
1495
+ * @param DOMNode $node Node.
1496
+ * @return bool Return true if the specified node's name is an AMP allowed tag, false otherwise.
1497
+ */
1498
+ private function is_amp_allowed_tag( $node ) {
1499
+ if ( ! $node instanceof DOMElement ) {
1500
+ return false;
1501
+ }
1502
+ /**
1503
+ * Return true if node is an allowed tags or is a text or comment node.
1504
+ */
1505
+ return (
1506
+ ( XML_TEXT_NODE === $node->nodeType ) ||
1507
+ isset( $this->allowed_tags[ $node->nodeName ] ) ||
1508
+ ( XML_COMMENT_NODE === $node->nodeType ) ||
1509
+ ( XML_CDATA_SECTION_NODE === $node->nodeType )
1510
+ );
1511
+ }
1512
+
1513
+ /**
1514
+ * Determine if the supplied $node has a parent with the specified tag name.
1515
+ *
1516
+ * @since 0.5
1517
+ *
1518
+ * @param DOMNode $node Node.
1519
+ * @param string $parent_tag_name Parent tag name.
1520
+ * @return bool Return true if given node has direct parent with the given name, false otherwise.
1521
+ */
1522
+ private function has_parent( $node, $parent_tag_name ) {
1523
+ if ( $node && $node->parentNode && ( $node->parentNode->nodeName === $parent_tag_name ) ) {
1524
+ return true;
1525
+ }
1526
+ return false;
1527
+ }
1528
+
1529
+ /**
1530
+ * Determine if the supplied $node has an ancestor with the specified tag name.
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.
1538
+ */
1539
+ private function has_ancestor( $node, $ancestor_tag_name ) {
1540
+ if ( $this->get_ancestor_with_tag_name( $node, $ancestor_tag_name ) ) {
1541
+ return true;
1542
+ }
1543
+ return false;
1544
+ }
1545
+
1546
+ /**
1547
+ * Get the first ancestor node matching the specified tag name for the supplied $node.
1548
+ *
1549
+ * @since 0.5
1550
+ *
1551
+ * @param DOMNode $node Node.
1552
+ * @param string $ancestor_tag_name Ancestor tag name.
1553
+ * @return DOMNode|null Returns an ancestor node for the name specified, or null if not found.
1554
+ */
1555
+ private function get_ancestor_with_tag_name( $node, $ancestor_tag_name ) {
1556
+ while ( $node && $node->parentNode ) {
1557
+ $node = $node->parentNode;
1558
+ if ( $node->nodeName === $ancestor_tag_name ) {
1559
+ return $node;
1560
+ }
1561
+ }
1562
+ return null;
1563
+ }
1564
+
1565
+ /**
1566
+ * Replaces the given node with it's child nodes, if any
1567
+ *
1568
+ * Also adds them to the stack for processing by the sanitize() function.
1569
+ *
1570
+ * @since 0.3.3
1571
+ *
1572
+ * @param DOMNode $node Node.
1573
+ */
1574
+ private function replace_node_with_children( $node ) {
1575
+
1576
+ if ( ! $node->hasChildNodes() || ! $node->parentNode ) {
1577
+ // If node has no children or no parent, just remove the node.
1578
+ $this->remove_node( $node );
1579
+
1580
+ } else {
1581
+ /*
1582
+ * If node has children, replace it with them and push children onto stack
1583
+ *
1584
+ * Create a DOM fragment to hold the children
1585
+ */
1586
+ $fragment = $this->dom->createDocumentFragment();
1587
+
1588
+ // Add all children to fragment/stack.
1589
+ $child = $node->firstChild;
1590
+ while ( $child ) {
1591
+ $fragment->appendChild( $child );
1592
+ $this->stack[] = $child;
1593
+ $child = $node->firstChild;
1594
+ }
1595
+
1596
+ // Replace node with fragment.
1597
+ $node->parentNode->replaceChild( $fragment, $node );
1598
+
1599
+ }
1600
+ }
1601
+
1602
+ /**
1603
+ * Removes a node from its parent node.
1604
+ *
1605
+ * If removing the node makes the parent node empty, then it will remove the parent
1606
+ * too. It will Continue until a non-empty parent or the 'body' element is reached.
1607
+ *
1608
+ * @since 0.5
1609
+ *
1610
+ * @param DOMNode $node Node.
1611
+ */
1612
+ private function remove_node( $node ) {
1613
+ /**
1614
+ * Parent.
1615
+ *
1616
+ * @var DOMNode $parent
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 ) {
1626
+ $parent->removeChild( $node );
1627
+ }
1628
+ }
1629
+ }
1630
+ }
includes/vendor/amp/includes/templates/class-amp-content-sanitizer.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Content_Sanitizer
4
+ *
5
+ * @package AMP
6
+ */
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 */
81
+ _doing_it_wrong( __METHOD__, sprintf( esc_html__( 'Sanitizer (%s) must extend `AMP_Base_Sanitizer`', 'amp' ), esc_html( $sanitizer_class ) ), '0.1' );
82
+ continue;
83
+ }
84
+
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
+
includes/vendor/amp/includes/templates/class-amp-content.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Content
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Class AMP_Content
10
+ */
11
+ class AMP_Content {
12
+
13
+ /**
14
+ * Content.
15
+ *
16
+ * @var string
17
+ */
18
+ private $content;
19
+
20
+ /**
21
+ * AMP content.
22
+ *
23
+ * @var string
24
+ */
25
+ private $amp_content = '';
26
+
27
+ /**
28
+ * AMP scripts.
29
+ *
30
+ * @var array
31
+ */
32
+ private $amp_scripts = array();
33
+
34
+ /**
35
+ * AMP styles.
36
+ *
37
+ * @var array
38
+ */
39
+ private $amp_styles = array();
40
+
41
+ /**
42
+ * Args.
43
+ *
44
+ * @var array
45
+ */
46
+ private $args = array();
47
+
48
+ /**
49
+ * Embed handler class names.
50
+ *
51
+ * @var string[]
52
+ */
53
+ private $embed_handler_classes = array();
54
+
55
+ /**
56
+ * Sanitizer class names.
57
+ *
58
+ * @var string[]
59
+ */
60
+ private $sanitizer_classes = array();
61
+
62
+ /**
63
+ * AMP_Content constructor.
64
+ *
65
+ * @param string $content Content.
66
+ * @param string[] $embed_handler_classes Embed handler class names.
67
+ * @param string[] $sanitizer_classes Sanitizer class names.
68
+ * @param array $args Args.
69
+ */
70
+ public function __construct( $content, $embed_handler_classes, $sanitizer_classes, $args = array() ) {
71
+ $this->content = $content;
72
+ $this->args = $args;
73
+ $this->embed_handler_classes = $embed_handler_classes;
74
+ $this->sanitizer_classes = $sanitizer_classes;
75
+
76
+ $this->transform();
77
+ }
78
+
79
+ /**
80
+ * Get AMP content.
81
+ *
82
+ * @return string
83
+ */
84
+ public function get_amp_content() {
85
+ return $this->amp_content;
86
+ }
87
+
88
+ /**
89
+ * Get AMP scripts.
90
+ *
91
+ * @return array
92
+ */
93
+ public function get_amp_scripts() {
94
+ return $this->amp_scripts;
95
+ }
96
+
97
+ /**
98
+ * Get AMP styles.
99
+ *
100
+ * @return array
101
+ */
102
+ public function get_amp_styles() {
103
+ return $this->amp_styles;
104
+ }
105
+
106
+ /**
107
+ * Transform.
108
+ */
109
+ private function transform() {
110
+ $content = $this->content;
111
+
112
+ // First, embeds + the_content filter.
113
+ $embed_handlers = $this->register_embed_handlers();
114
+ $content = apply_filters( 'the_content', $content );
115
+ $this->unregister_embed_handlers( $embed_handlers );
116
+
117
+ // Then, sanitize to strip and/or convert non-amp content.
118
+ $content = $this->sanitize( $content );
119
+
120
+ $this->amp_content = $content;
121
+ }
122
+
123
+ /**
124
+ * Add scripts.
125
+ *
126
+ * @param array $scripts Scripts.
127
+ */
128
+ private function add_scripts( $scripts ) {
129
+ $this->amp_scripts = array_merge( $this->amp_scripts, $scripts );
130
+ }
131
+
132
+ /**
133
+ * Add styles.
134
+ *
135
+ * @param array $styles Styles.
136
+ */
137
+ private function add_styles( $styles ) {
138
+ $this->amp_styles = array_merge( $this->amp_styles, $styles );
139
+ }
140
+
141
+ /**
142
+ * Register embed handlers.
143
+ *
144
+ * @return array
145
+ */
146
+ private function register_embed_handlers() {
147
+ $embed_handlers = array();
148
+
149
+ foreach ( $this->embed_handler_classes as $embed_handler_class => $args ) {
150
+ $embed_handler = new $embed_handler_class( array_merge( $this->args, $args ) );
151
+
152
+ if ( ! is_subclass_of( $embed_handler, 'AMP_Base_Embed_Handler' ) ) {
153
+ /* translators: %s is embed handler */
154
+ _doing_it_wrong( __METHOD__, esc_html( sprintf( __( 'Embed Handler (%s) must extend `AMP_Embed_Handler`', 'amp' ), $embed_handler_class ) ), '0.1' );
155
+ continue;
156
+ }
157
+
158
+ $embed_handler->register_embed();
159
+ $embed_handlers[] = $embed_handler;
160
+ }
161
+
162
+ return $embed_handlers;
163
+ }
164
+
165
+ /**
166
+ * Unregister embed handlers.
167
+ *
168
+ * @param array $embed_handlers Embed handlers.
169
+ */
170
+ private function unregister_embed_handlers( $embed_handlers ) {
171
+ foreach ( $embed_handlers as $embed_handler ) {
172
+ $this->add_scripts( $embed_handler->get_scripts() );
173
+ $embed_handler->unregister_embed();
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Sanitize.
179
+ *
180
+ * @see AMP_Content_Sanitizer::sanitize()
181
+ * @param string $content Content.
182
+ * @return array Sanitized content.
183
+ */
184
+ private function sanitize( $content ) {
185
+ list( $sanitized_content, $scripts, $styles ) = AMP_Content_Sanitizer::sanitize( $content, $this->sanitizer_classes, $this->args );
186
+
187
+ $this->add_scripts( $scripts );
188
+ $this->add_styles( $styles );
189
+
190
+ return $sanitized_content;
191
+ }
192
+ }
includes/vendor/amp/includes/templates/class-amp-post-template.php ADDED
@@ -0,0 +1,481 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AMP_Post_Template class.
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Class AMP_Post_Template
10
+ *
11
+ * @since 0.2
12
+ */
13
+ class AMP_Post_Template {
14
+
15
+ /**
16
+ * Site icon size.
17
+ *
18
+ * @since 0.2
19
+ * @var int
20
+ */
21
+ const SITE_ICON_SIZE = 32;
22
+
23
+ /**
24
+ * Content max width.
25
+ *
26
+ * @since 0.4
27
+ * @var int
28
+ */
29
+ const CONTENT_MAX_WIDTH = 600;
30
+
31
+ /**
32
+ * Default navbar background.
33
+ *
34
+ * Needed for 0.3 back-compat
35
+ *
36
+ * @since 0.4
37
+ * @var string
38
+ */
39
+ const DEFAULT_NAVBAR_BACKGROUND = '#0a89c0';
40
+
41
+ /**
42
+ * Default navbar color.
43
+ *
44
+ * Needed for 0.3 back-compat
45
+ *
46
+ * @since 0.4
47
+ * @var string
48
+ */
49
+ const DEFAULT_NAVBAR_COLOR = '#fff';
50
+
51
+ /**
52
+ * Template directory.
53
+ *
54
+ * @since 0.2
55
+ * @var string
56
+ */
57
+ private $template_dir;
58
+
59
+ /**
60
+ * Post template data.
61
+ *
62
+ * @since 0.2
63
+ * @var array
64
+ */
65
+ private $data;
66
+
67
+ /**
68
+ * Post ID.
69
+ *
70
+ * @since 0.2
71
+ * @var int
72
+ */
73
+ public $ID;
74
+
75
+ /**
76
+ * Post.
77
+ *
78
+ * @since 0.2
79
+ * @var WP_Post
80
+ */
81
+ public $post;
82
+
83
+ /**
84
+ * AMP_Post_Template constructor.
85
+ *
86
+ * @param WP_Post|int $post Post.
87
+ */
88
+ public function __construct( $post ) {
89
+
90
+ $this->template_dir = apply_filters( 'amp_post_template_dir', AMP__DIR__ . '/templates' );
91
+
92
+ if ( $post instanceof WP_Post ) {
93
+ $this->post = $post;
94
+ } else {
95
+ $this->post = get_post( $post );
96
+ }
97
+
98
+ // Make sure we have a post, or bail if not.
99
+ if ( is_a( $this->post, 'WP_Post' ) ) {
100
+ $this->ID = $this->post->ID;
101
+ } else {
102
+ return;
103
+ }
104
+ if ( ampforwp_is_front_page() ) {
105
+ $this->ID = ampforwp_get_frontpage_id();
106
+ }
107
+ $content_max_width = self::CONTENT_MAX_WIDTH;
108
+ if ( isset( $GLOBALS['content_width'] ) && $GLOBALS['content_width'] > 0 ) {
109
+ $content_max_width = $GLOBALS['content_width'];
110
+ }
111
+ $content_max_width = apply_filters( 'amp_content_max_width', $content_max_width );
112
+
113
+ $this->data = array(
114
+ 'content_max_width' => $content_max_width,
115
+
116
+ 'document_title' => function_exists( 'wp_get_document_title' ) ? wp_get_document_title() : wp_title( '', false ), // Back-compat with 4.3.
117
+ 'canonical_url' => get_permalink( $this->ID ),
118
+ 'home_url' => home_url(),
119
+ 'blog_name' => get_bloginfo( 'name' ),
120
+
121
+ 'html_tag_attributes' => array(),
122
+ 'body_class' => '',
123
+
124
+ 'site_icon_url' => apply_filters( 'amp_site_icon_url', function_exists( 'get_site_icon_url' ) ? get_site_icon_url( self::SITE_ICON_SIZE ) : '' ),
125
+ 'placeholder_image_url' => amp_get_asset_url( 'images/placeholder-icon.png' ),
126
+
127
+ 'featured_image' => false,
128
+ 'comments_link_url' => false,
129
+ 'comments_link_text' => false,
130
+
131
+ 'amp_runtime_script' => 'https://cdn.ampproject.org/v0.js',
132
+ 'amp_component_scripts' => array(),
133
+
134
+ 'customizer_settings' => array(),
135
+
136
+ 'font_urls' => array(
137
+ 'merriweather' => 'https://fonts.googleapis.com/css?family=Merriweather:400,400italic,700,700italic',
138
+ ),
139
+
140
+ 'post_amp_styles' => array(),
141
+
142
+ 'amp_analytics' => amp_add_custom_analytics(),
143
+ );
144
+
145
+ $this->build_post_content();
146
+ $this->build_post_data();
147
+ $this->build_customizer_settings();
148
+ $this->build_html_tag_attributes();
149
+
150
+ /**
151
+ * Filters AMP template data.
152
+ *
153
+ * @since 0.2
154
+ *
155
+ * @param array $data Template data.
156
+ * @param WP_Post $post Post.
157
+ */
158
+ $this->data = apply_filters( 'amp_post_template_data', $this->data, $this->post );
159
+ }
160
+
161
+ /**
162
+ * Getter.
163
+ *
164
+ * @param string $property Property name.
165
+ * @param mixed $default Default value.
166
+ *
167
+ * @return mixed Value.
168
+ */
169
+ public function get( $property, $default = null ) {
170
+ if ( isset( $this->data[ $property ] ) ) {
171
+ return $this->data[ $property ];
172
+ } else {
173
+ /* translators: %s is key name */
174
+ _doing_it_wrong( __METHOD__, esc_html( sprintf( __( 'Called for non-existent key ("%s").', 'amp' ), $property ) ), '0.1' );
175
+ }
176
+
177
+ return $default;
178
+ }
179
+
180
+ /**
181
+ * Get customizer setting.
182
+ *
183
+ * @param string $name Name.
184
+ * @param mixed $default Default value.
185
+ * @return mixed value.
186
+ */
187
+ public function get_customizer_setting( $name, $default = null ) {
188
+ $settings = $this->get( 'customizer_settings' );
189
+ if ( ! empty( $settings[ $name ] ) ) {
190
+ return $settings[ $name ];
191
+ }
192
+
193
+ return $default;
194
+ }
195
+
196
+ /**
197
+ * Load and print the template parts for the given post.
198
+ */
199
+ public function load() {
200
+ global $wp_query;
201
+ $template = is_page() || $wp_query->is_posts_page ? 'page' : 'single';
202
+ $this->load_parts( array( $template ) );
203
+ }
204
+
205
+ /**
206
+ * Load template parts.
207
+ *
208
+ * @param string[] $templates Templates.
209
+ */
210
+ public function load_parts( $templates ) {
211
+ foreach ( $templates as $template ) {
212
+ $file = $this->get_template_path( $template );
213
+ $this->verify_and_include( $file, $template );
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Get template path.
219
+ *
220
+ * @param string $template Template name.
221
+ * @return string Template path.
222
+ */
223
+ private function get_template_path( $template ) {
224
+ return sprintf( '%s/%s.php', $this->template_dir, $template );
225
+ }
226
+
227
+ /**
228
+ * Add data.
229
+ *
230
+ * @param array $data Data.
231
+ */
232
+ private function add_data( $data ) {
233
+ $this->data = array_merge( $this->data, $data );
234
+ }
235
+
236
+ /**
237
+ * Add data by key.
238
+ *
239
+ * @param string $key Key.
240
+ * @param mixed $value Value.
241
+ */
242
+ private function add_data_by_key( $key, $value ) {
243
+ $this->data[ $key ] = $value;
244
+ }
245
+
246
+ /**
247
+ * Merge data for key.
248
+ *
249
+ * @param string $key Key.
250
+ * @param mixed $value Value.
251
+ */
252
+ private function merge_data_for_key( $key, $value ) {
253
+ if ( is_array( $this->data[ $key ] ) ) {
254
+ $this->data[ $key ] = array_merge( $this->data[ $key ], $value );
255
+ } else {
256
+ $this->add_data_by_key( $key, $value );
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Build post data.
262
+ *
263
+ * @since 0.2
264
+ */
265
+ private function build_post_data() {
266
+ $post_title = get_the_title( $this->ID );
267
+ $post_publish_timestamp = get_the_date( 'U', $this->ID );
268
+ $post_modified_timestamp = get_post_modified_time( 'U', false, $this->post );
269
+ $post_author = get_userdata( $this->post->post_author );
270
+
271
+ $this->add_data(
272
+ array(
273
+ 'post' => $this->post,
274
+ 'post_id' => $this->ID,
275
+ 'post_title' => $post_title,
276
+ 'post_publish_timestamp' => $post_publish_timestamp,
277
+ 'post_modified_timestamp' => $post_modified_timestamp,
278
+ 'post_author' => $post_author,
279
+ )
280
+ );
281
+
282
+ $this->build_post_featured_image();
283
+ $this->build_post_commments_data();
284
+ }
285
+
286
+ /**
287
+ * Buuild post comments data.
288
+ */
289
+ private function build_post_commments_data() {
290
+ if ( ! post_type_supports( $this->post->post_type, 'comments' ) ) {
291
+ return;
292
+ }
293
+
294
+ $comments_open = comments_open( $this->ID );
295
+
296
+ // Don't show link if close and no comments.
297
+ if ( ! $comments_open
298
+ && ! $this->post->comment_count ) {
299
+ return;
300
+ }
301
+
302
+ $comments_link_url = get_comments_link( $this->ID );
303
+ $comments_link_text = $comments_open
304
+ ? __( 'Leave a Comment', 'amp' )
305
+ : __( 'View Comments', 'amp' );
306
+
307
+ $this->add_data(
308
+ array(
309
+ 'comments_link_url' => $comments_link_url,
310
+ 'comments_link_text' => $comments_link_text,
311
+ )
312
+ );
313
+ }
314
+
315
+ /**
316
+ * Build post content.
317
+ */
318
+ private function build_post_content() {
319
+ if( !empty($this->post->post_content) && false === ampforwp_is_home() && false === is_archive() ){
320
+ $post_content = $this->post->post_content;
321
+ $post_content = apply_filters( 'ampforwp_post_content_filter', $post_content );
322
+ $amp_content = new AMP_Content(
323
+ $post_content,
324
+ amp_get_content_embed_handlers( $this->post ),
325
+ amp_get_content_sanitizers( $this->post ),
326
+ array(
327
+ 'content_max_width' => $this->get( 'content_max_width' ),
328
+ )
329
+ );
330
+
331
+ $this->add_data_by_key( 'post_amp_content', $amp_content->get_amp_content() );
332
+ $this->merge_data_for_key( 'amp_component_scripts', $amp_content->get_amp_scripts() );
333
+ $this->merge_data_for_key( 'post_amp_styles', $amp_content->get_amp_styles() );
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Build post featured image.
339
+ */
340
+ private function build_post_featured_image() {
341
+ $post_id = $this->ID;
342
+ $featured_html = get_the_post_thumbnail( $post_id, 'large' );
343
+
344
+ // Skip featured image if no featured image is available.
345
+ if ( ! $featured_html ) {
346
+ return;
347
+ }
348
+
349
+ $featured_id = get_post_thumbnail_id( $post_id );
350
+
351
+ // If an image with the same ID as the featured image exists in the content, skip the featured image markup.
352
+ // Prevents duplicate images, which is especially problematic for photo blogs.
353
+ // A bit crude but it's fast and should cover most cases.
354
+ $post_content = $this->post->post_content;
355
+ if ( true !== apply_filters('ampforwp_allow_featured_image', false) && ( false !== strpos( $post_content, 'wp-image-' . $featured_id ) || false !== strpos( $post_content, 'attachment_' . $featured_id ) ) ) {
356
+ return;
357
+ }
358
+
359
+ $featured_image = get_post( $featured_id );
360
+
361
+ list( $sanitized_html, $featured_scripts, $featured_styles ) = AMP_Content_Sanitizer::sanitize(
362
+ $featured_html,
363
+ amp_get_content_sanitizers( $this->post ),
364
+ array(
365
+ 'content_max_width' => $this->get( 'content_max_width' ),
366
+ )
367
+ );
368
+
369
+ $this->add_data_by_key(
370
+ 'featured_image', array(
371
+ 'amp_html' => $sanitized_html,
372
+ 'caption' => $featured_image->post_excerpt,
373
+ )
374
+ );
375
+
376
+ if ( $featured_scripts ) {
377
+ $this->merge_data_for_key( 'amp_component_scripts', $featured_scripts );
378
+ }
379
+
380
+ if ( $featured_styles ) {
381
+ $this->merge_data_for_key( 'post_amp_styles', $featured_styles );
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Build customizer settings.
387
+ */
388
+ private function build_customizer_settings() {
389
+ $settings = AMP_Customizer_Settings::get_settings();
390
+
391
+ /**
392
+ * Filter AMP Customizer settings.
393
+ *
394
+ * Inject your Customizer settings here to make them accessible via the getter in your custom style.php template.
395
+ *
396
+ * Example:
397
+ *
398
+ * echo esc_html( $this->get_customizer_setting( 'your_setting_key', 'your_default_value' ) );
399
+ *
400
+ * @since 0.4
401
+ *
402
+ * @param array $settings Array of AMP Customizer settings.
403
+ * @param WP_Post $post Current post object.
404
+ */
405
+ $this->add_data_by_key( 'customizer_settings', apply_filters( 'amp_post_template_customizer_settings', $settings, $this->post ) );
406
+ }
407
+
408
+ /**
409
+ * Build HTML tag attributes.
410
+ */
411
+ private function build_html_tag_attributes() {
412
+ $attributes = array();
413
+
414
+ if ( function_exists( 'is_rtl' ) && is_rtl() ) {
415
+ $attributes['dir'] = 'rtl';
416
+ }
417
+
418
+ $lang = get_bloginfo( 'language' );
419
+ if ( $lang ) {
420
+ $attributes['lang'] = $lang;
421
+ }
422
+
423
+ $this->add_data_by_key( 'html_tag_attributes', $attributes );
424
+ }
425
+
426
+ /**
427
+ * Verify and include.
428
+ *
429
+ * @param string $file File.
430
+ * @param string $template_type Template type.
431
+ */
432
+ private function verify_and_include( $file, $template_type ) {
433
+ $located_file = $this->locate_template( $file );
434
+ if ( $located_file ) {
435
+ $file = $located_file;
436
+ }
437
+
438
+ $file = apply_filters( 'amp_post_template_file', $file, $template_type, $this->post );
439
+ if ( ! $this->is_valid_template( $file ) ) {
440
+ /* translators: %1$s is template file, %2$s is 'WP_CONTENT_DIR' string. */
441
+ _doing_it_wrong( __METHOD__, sprintf( esc_html__( 'Path validation for template (%1$s) failed. Path cannot traverse and must be located in `%2$s`.', 'amp' ), esc_html( $file ), 'WP_CONTENT_DIR' ), '0.1' );
442
+ return;
443
+ }
444
+
445
+ do_action( 'amp_post_template_include_' . $template_type, $this );
446
+ include $file;
447
+ }
448
+
449
+ /**
450
+ * Locate template.
451
+ *
452
+ * @param string $file File.
453
+ * @return string The template filename if one is located.
454
+ */
455
+ private function locate_template( $file ) {
456
+ $search_file = sprintf( 'amp/%s', basename( $file ) );
457
+ return locate_template( array( $search_file ), false );
458
+ }
459
+
460
+ /**
461
+ * Is valid template.
462
+ *
463
+ * @param string $template Template name.
464
+ * @return bool Whether valid.
465
+ */
466
+ private function is_valid_template( $template ) {
467
+ if ( false !== strpos( $template, '..' ) ) {
468
+ return false;
469
+ }
470
+
471
+ if ( false !== strpos( $template, './' ) ) {
472
+ return false;
473
+ }
474
+
475
+ if ( ! file_exists( $template ) ) {
476
+ return false;
477
+ }
478
+
479
+ return true;
480
+ }
481
+ }
includes/vendor/amp/includes/utils/class-amp-validation-utils.php ADDED
@@ -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
+ }
includes/vendor/amp/includes/widgets/class-amp-widget-archives.php ADDED
@@ -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
+ }
includes/vendor/amp/includes/widgets/class-amp-widget-categories.php ADDED
@@ -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
+ }
includes/vendor/amp/includes/widgets/class-amp-widget-media-video.php ADDED
@@ -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
+ }
includes/vendor/amp/includes/widgets/class-amp-widget-recent-comments.php ADDED
@@ -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
+ }
includes/vendor/amp/includes/widgets/class-amp-widget-text.php ADDED
@@ -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
+ }
includes/vendor/amp/templates/admin/amp-status.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AMP status option in the submit meta box.
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ // Check referrer.
9
+ if ( ! ( $this instanceof AMP_Post_Meta_Box ) ) {
10
+ return;
11
+ }
12
+
13
+ /**
14
+ * Inherited template vars.
15
+ *
16
+ * @var array $labels Labels for enabled or disabled.
17
+ * @var string $status Enabled or disabled.
18
+ * @var array $errors Support errors.
19
+ */
20
+ ?>
21
+ <div class="misc-pub-section misc-amp-status">
22
+ <span class="amp-icon"></span>
23
+ <?php esc_html_e( 'AMP:', 'amp' ); ?>
24
+ <strong class="amp-status-text"><?php echo esc_html( $labels[ $status ] ); ?></strong>
25
+ <a href="#amp_status" class="edit-amp-status hide-if-no-js" role="button">
26
+ <span aria-hidden="true"><?php esc_html_e( 'Edit', 'amp' ); ?></span>
27
+ <span class="screen-reader-text"><?php esc_html_e( 'Edit Status', 'amp' ); ?></span>
28
+ </a>
29
+ <div id="amp-status-select" class="hide-if-js" data-amp-status="<?php echo esc_attr( $status ); ?>">
30
+ <?php if ( empty( $errors ) ) : ?>
31
+ <fieldset>
32
+ <input id="amp-status-enabled" type="radio" name="<?php echo esc_attr( self::STATUS_INPUT_NAME ); ?>" value="<?php echo esc_attr( self::ENABLED_STATUS ); ?>" <?php checked( self::ENABLED_STATUS, $status ); ?>>
33
+ <label for="amp-status-enabled" class="selectit"><?php echo esc_html( $labels['enabled'] ); ?></label>
34
+ <br />
35
+ <input id="amp-status-disabled" type="radio" name="<?php echo esc_attr( self::STATUS_INPUT_NAME ); ?>" value="<?php echo esc_attr( self::DISABLED_STATUS ); ?>" <?php checked( self::DISABLED_STATUS, $status ); ?>>
36
+ <label for="amp-status-disabled" class="selectit"><?php echo esc_html( $labels['disabled'] ); ?></label>
37
+ <br />
38
+ <?php wp_nonce_field( self::NONCE_ACTION, self::NONCE_NAME ); ?>
39
+ </fieldset>
40
+ <?php else : ?>
41
+ <div class="inline notice notice-warning notice-alt">
42
+ <p>
43
+ <?php
44
+ $support_errors_codes = AMP_Post_Type_Support::get_support_errors( $post );
45
+ $support_errors = array();
46
+ if ( in_array( 'password-protected', $support_errors_codes, true ) ) {
47
+ $support_errors[] = __( 'AMP cannot be enabled on password protected posts.', 'amp' );
48
+ }
49
+ if ( in_array( 'post-type-support', $support_errors_codes, true ) ) {
50
+ /* translators: %s is URL to AMP settings screen */
51
+ $support_errors[] = wp_kses_post( sprintf( __( 'AMP cannot be enabled because this <a href="%s">post type does not support it</a>.', 'amp' ), admin_url( 'admin.php?page=amp-options' ) ) );
52
+ }
53
+ if ( in_array( 'skip-post', $support_errors_codes, true ) ) {
54
+ $support_errors[] = __( 'A plugin or theme has disabled AMP support.', 'amp' );
55
+ }
56
+ if ( count( array_diff( $support_errors_codes, array( 'page-on-front', 'page-for-posts', 'password-protected', 'post-type-support', 'skip-post' ) ) ) > 0 ) {
57
+ $support_errors[] = __( 'Unavailable for an unknown reason.', 'amp' );
58
+ }
59
+ echo implode( ' ', $support_errors ); // WPCS: xss ok.
60
+ ?>
61
+ </p>
62
+ </div>
63
+ <?php endif; ?>
64
+ <div class="amp-status-actions">
65
+ <?php if ( empty( $errors ) ) : ?>
66
+ <a href="#amp_status" class="save-amp-status hide-if-no-js button"><?php esc_html_e( 'OK', 'amp' ); ?></a>
67
+ <?php endif; ?>
68
+ <a href="#amp_status" class="cancel-amp-status hide-if-no-js button-cancel"><?php esc_html_e( 'Cancel', 'amp' ); ?></a>
69
+ </div>
70
+ </div>
71
+ </div>
includes/vendor/amp/templates/header.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Header template part.
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Context.
10
+ *
11
+ * @var AMP_Post_Template $this
12
+ */
13
+
14
+ $this->load_parts( array( 'header-bar' ) );
includes/vendor/amp/templates/html-end.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * HTML end template part.
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Context.
10
+ *
11
+ * @var AMP_Post_Template $this
12
+ */
13
+ ?>
14
+
15
+ <?php do_action( 'amp_post_template_footer', $this ); ?>
16
+
17
+ </body>
18
+ </html>
includes/vendor/amp/templates/html-start.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * HTML start template part.
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Context.
10
+ *
11
+ * @var AMP_Post_Template $this
12
+ */
13
+ ?>
14
+ <!doctype html>
15
+ <html amp <?php echo AMP_HTML_Utils::build_attributes_string( $this->get( 'html_tag_attributes' ) ); // WPCS: XSS ok. ?>>
16
+ <head>
17
+ <meta charset="utf-8">
18
+ <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
19
+ <?php do_action( 'amp_post_template_head', $this ); ?>
20
+ <style amp-custom>
21
+ <?php $this->load_parts( array( 'style' ) ); ?>
22
+ <?php do_action( 'amp_post_template_css', $this ); ?>
23
+ </style>
24
+ </head>
25
+
26
+ <body class="<?php echo esc_attr( $this->get( 'body_class' ) ); ?>">
includes/vendor/amp/templates/page.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Page view template.
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Context.
10
+ *
11
+ * @var AMP_Post_Template $this
12
+ */
13
+
14
+ $this->load_parts( array( 'html-start' ) );
15
+ ?>
16
+
17
+ <?php $this->load_parts( array( 'header' ) ); ?>
18
+
19
+ <article class="amp-wp-article">
20
+ <header class="amp-wp-article-header">
21
+ <h1 class="amp-wp-title"><?php echo esc_html( $this->get( 'post_title' ) ); ?></h1>
22
+ </header>
23
+
24
+ <?php $this->load_parts( array( 'featured-image' ) ); ?>
25
+
26
+ <div class="amp-wp-article-content">
27
+ <?php echo $this->get( 'post_amp_content' ); // WPCS: XSS ok. Handled in AMP_Content::transform(). ?>
28
+ </div>
29
+ </article>
30
+
31
+ <?php $this->load_parts( array( 'footer' ) ); ?>
32
+
33
+ <?php
34
+ $this->load_parts( array( 'html-end' ) );
includes/vendor/amp/wpcom-helper.php ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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' );
23
+
24
+ remove_filter( 'the_title', 'widont' );
25
+
26
+ remove_filter( 'pre_kses', array( 'Filter_Embedded_HTML_Objects', 'filter' ), 11 );
27
+ remove_filter( 'pre_kses', array( 'Filter_Embedded_HTML_Objects', 'maybe_create_links' ), 100 );
28
+ }
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
+ }
41
+ }
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'] ) ) {
60
+ $metadata = wpcom_amp_add_image_to_metadata( $metadata, $post );
61
+ }
62
+
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 );
103
+ }
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 ) ) {
114
+ return wpcom_amp_add_fallback_image_to_metadata( $metadata );
115
+ }
116
+
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
+
154
+ return $metadata;
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
+ }
205
+ }
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;
220
+ }
221
+ require_lib( 'wpcom/imagesize' );
222
+
223
+ foreach ( $dimensions as $url => $value ) {
224
+ if ( is_array( $value ) ) {
225
+ continue;
226
+ }
227
+ $result = wpcom_getimagesize( $url );
228
+ if ( is_array( $result ) ) {
229
+ $dimensions[ $url ] = array(
230
+ 'width' => $result[0],
231
+ 'height' => $result[1],
232
+ );
233
+ }
234
+ }
235
+
236
+ return $dimensions;
237
+ }
includes/vendor/amp/wpcom/class-amp-polldaddy-embed.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPCOM_AMP_Polldaddy_Embed
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ /**
9
+ * Class WPCOM_AMP_Polldaddy_Embed
10
+ */
11
+ class WPCOM_AMP_Polldaddy_Embed extends AMP_Base_Embed_Handler {
12
+
13
+ /**
14
+ * Register embed.
15
+ */
16
+ public function register_embed() {
17
+ add_shortcode( 'polldaddy', array( $this, 'shortcode' ) );
18
+ add_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10, 3 );
19
+ }
20
+
21
+ /**
22
+ * Unregister embed.
23
+ */
24
+ public function unregister_embed() {
25
+ remove_shortcode( 'polldaddy' );
26
+ remove_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10 );
27
+ }
28
+
29
+ /**
30
+ * Shortcode.
31
+ *
32
+ * @param array $attr Shortcode attributes.
33
+ * @return string Shortcode.
34
+ * @global WP_Embed $wp_embed
35
+ */
36
+ public function shortcode( $attr ) {
37
+ global $wp_embed;
38
+
39
+ $output = '';
40
+ $url = 'https://polldaddy.com/';
41
+ if ( ! empty( $attr['poll'] ) ) {
42
+ $url .= 'poll/' . $attr['poll'] . '/';
43
+ } elseif ( ! empty( $attr['survey'] ) ) {
44
+ $url .= 's/' . $attr['survey'] . '/';
45
+ }
46
+
47
+ if ( ! empty( $attr['title'] ) ) {
48
+ $output = $this->render_link( $url, $attr['title'] );
49
+ } elseif ( $url ) {
50
+ $output = $wp_embed->shortcode( $attr, $url );
51
+ }
52
+
53
+ return $output;
54
+ }
55
+
56
+ /**
57
+ * Filter oEmbed HTML for PollDaddy to for AMP output.
58
+ *
59
+ * @param string $cache Cache for oEmbed.
60
+ * @param string $url Embed URL.
61
+ * @param array $attr Shortcode attributes.
62
+ * @return string Embed.
63
+ */
64
+ public function filter_embed_oembed_html( $cache, $url, $attr ) {
65
+ $parsed_url = wp_parse_url( $url );
66
+ if ( false === strpos( $parsed_url['host'], 'polldaddy.com' ) ) {
67
+ return $cache;
68
+ }
69
+
70
+ $output = '';
71
+
72
+ // Poll oEmbed responses include noscript.
73
+ if ( preg_match( '#<noscript>(.+?)</noscript>#', $cache, $matches ) ) {
74
+ $output = $matches[1];
75
+ }
76
+
77
+ if ( empty( $output ) ) {
78
+ if ( ! empty( $attr['title'] ) ) {
79
+ $name = $attr['title'];
80
+ } elseif ( false !== strpos( $url, 'polldaddy.com/s' ) ) {
81
+ $name = __( 'View Survey', 'amp' );
82
+ } else {
83
+ $name = __( 'View Poll', 'amp' );
84
+ }
85
+ $output = $this->render_link( $url, $name );
86
+ }
87
+
88
+ return $output;
89
+ }
90
+
91
+ /**
92
+ * Render poll/survey link.
93
+ *
94
+ * @param string $url Link URL.
95
+ * @param string $title Link Text.
96
+ * @return string Link.
97
+ */
98
+ private function render_link( $url, $title ) {
99
+ return sprintf( '<p><a href="' . esc_url( $url ) . '">' . esc_html( $title ) . '</a></p>' );
100
+ }
101
+ }
includes/vendor/amp/wpcom/shortcodes.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Shortcode functions for WordPress.com.
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ _deprecated_file( __FILE__, '0.6' );
9
+
10
+ /**
11
+ * Add custom embeds for WordPress.com.
12
+ *
13
+ * @deprecated Now PollDaddy is supported in core AMP.
14
+ * @param array $embed_handler_classes Embed handler classes.
15
+ * @return mixed
16
+ */
17
+ function wpcom_amp_add_custom_embeds( $embed_handler_classes ) {
18
+ _deprecated_function( __FUNCTION__, '0.6' );
19
+ return $embed_handler_classes;
20
+ }
includes/vendor/vendor-compatibility.php ADDED
@@ -0,0 +1,342 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ add_action('plugins_loaded', function(){
3
+ if(!is_admin()){
4
+ add_filter("ampforwp_update_autoload_class", 'ampforwp_update_class',10,2);
5
+
6
+ add_action("wp",'ampforwp_enable_support_for_otherpages');
7
+
8
+ add_filter("ampforwp_content_sanitizers", 'content_sanitizers_remove_blacklist', 999);
9
+ add_filter("amp_content_sanitizers", 'content_sanitizers_remove_blacklist', 999);
10
+ add_filter('amp_content_embed_handlers', 'ampforwp_modified_embed_handlers');
11
+ add_filter( 'amp_post_status_default_enabled', 'ampforwp_post_status' );
12
+ add_filter( 'amp_post_template_file', 'ampforwp_custom_template', 10, 3 );
13
+ add_filter( 'amp_get_permalink', 'ampforwp_change_end_point' );
14
+ add_filter('amp_post_template_data', 'ampforwp_post_template_data');
15
+ }
16
+ });
17
+
18
+ add_action('init','ampforwp_modifying_front_page_query');
19
+ function ampforwp_modifying_front_page_query() {
20
+ remove_action( 'parse_query', 'amp_correct_query_when_is_front_page',10 );
21
+ add_action( 'parse_query', 'ampforwp_correct_query_front_page',15 );
22
+ }
23
+
24
+ function ampforwp_correct_query_front_page(WP_Query $query){
25
+
26
+ if ( start_non_amp_to_amp_conversion()) {
27
+ return false;
28
+ }
29
+ if ( false !== $query->get( amp_get_slug(), false ) ) {
30
+ global $redux_builder_amp;
31
+ $amp_frontpage_id = $amp_frontpage = '';
32
+ if ( isset($redux_builder_amp['amp-frontpage-select-option']) && $redux_builder_amp['amp-frontpage-select-option'] ) {
33
+ $amp_frontpage = true;
34
+ }
35
+ $amp_home_id = 0;
36
+ if (ampforwp_is_blog() ) {
37
+ $amp_home_id = ampforwp_get_blog_details('id');
38
+ }
39
+ if ( 'page' === get_option( 'show_on_front' ) ) {
40
+ $amp_frontpage_id = get_option( 'page_on_front' );
41
+ }
42
+ // Frontpage id should be assigned
43
+ if ( ampforwp_is_front_page() ) {
44
+ $amp_frontpage_id = ampforwp_get_frontpage_id();
45
+ }
46
+ $is_front_page_query = (
47
+ $query->is_main_query()
48
+ &&
49
+ $query->is_home()
50
+ &&
51
+ // Is query not yet fixed up to be front page.
52
+ ! $query->is_front_page()
53
+ &&
54
+ // is Homepage support enabeld from options panel
55
+ $amp_frontpage
56
+ &&
57
+ // See line in WP_Query::parse_query() at <https://github.com/WordPress/wordpress-develop/blob/0baa8ae/src/wp-includes/class-wp-query.php#L961>.
58
+ 0 === count( array_diff( array_keys( wp_parse_args( $query->query ) ), array( amp_get_slug(), 'preview', 'page', 'paged', 'cpage' ) ) )
59
+ );
60
+
61
+ if ( $is_front_page_query ) {
62
+ $query->is_home = false;
63
+ $query->is_page = true;
64
+ $query->is_singular = true;
65
+ $query->set( 'page_id', $amp_frontpage_id );
66
+ }
67
+ elseif(ampforwp_is_blog() || ampforwp_is_home() ) {
68
+ $query->is_home = true;
69
+ $query->set( 'page_id', $amp_home_id );
70
+ }
71
+
72
+ }
73
+ else{
74
+ $query->set( 'amp', false );
75
+ }
76
+ }
77
+
78
+ /*
79
+ * is_archive() || is_search() Support added after 0.7 vendor amp
80
+ */
81
+ function ampforwp_enable_support_for_otherpages(){
82
+ if ( start_non_amp_to_amp_conversion()) {
83
+ return ;
84
+ }
85
+ global $redux_builder_amp;
86
+ $is_amp_endpoint_needed = ampforwp_is_amp_endpoint();
87
+ $hide_cats_amp = '';
88
+ $hide_cats_amp = is_category_amp_disabled();
89
+ $support_for_archives = '';
90
+
91
+ if ( isset($redux_builder_amp['ampforwp-amp-convert-to-wp']) && $redux_builder_amp['ampforwp-amp-convert-to-wp'] && ampforwp_is_non_amp() ) {
92
+ add_action( 'wp_head', 'ampforwp_home_archive_rel_canonical', 1 );
93
+ }
94
+ if( (isset($redux_builder_amp['ampforwp-archive-support']) && $redux_builder_amp['ampforwp-archive-support']) && is_archive() ){
95
+ $support_for_archives = true;
96
+ }
97
+ else{
98
+ $support_for_archives = false;
99
+ }
100
+ $amp_frontpage_id = ampforwp_get_frontpage_id();
101
+
102
+ if( ( ($support_for_archives && !$hide_cats_amp) || is_search() || is_front_page() || ampforwp_is_blog() || ampforwp_is_home() || is_404() ) && $is_amp_endpoint_needed ){
103
+ remove_action( 'template_redirect', 'amp_render' );
104
+ if ( is_front_page() && $amp_frontpage_id ) {
105
+ $amp_frontpage_post = get_post($amp_frontpage_id);
106
+ amp_render_post($amp_frontpage_post);
107
+ }
108
+ elseif(is_404()){
109
+ $fourofour = get_post(2);
110
+ amp_render_post($fourofour);
111
+ }
112
+ else
113
+ amp_render_post(0);
114
+ exit;
115
+ }
116
+ }
117
+
118
+ // Sanitizers
119
+ function content_sanitizers_remove_blacklist($sanitizer_classes){
120
+ global $redux_builder_amp;
121
+ // Whitelist sanitizer
122
+ if(isset($sanitizer_classes['AMP_Blacklist_Sanitizer'])) {
123
+ unset($sanitizer_classes['AMP_Blacklist_Sanitizer']);
124
+ $sanitizer_classes['AMP_Tag_And_Attribute_Sanitizer']= array();
125
+ }
126
+ if(isset($sanitizer_classes['AMP_Base_Sanitizer'])) {
127
+ unset($sanitizer_classes['AMP_Base_Sanitizer']);
128
+ }
129
+ // New image sanitizer For Lightbox and FooGallery support
130
+ if( isset( $sanitizer_classes['AMP_Img_Sanitizer']) ) {
131
+ require_once( AMPFORWP_PLUGIN_DIR. 'classes/class-ampforwp-img-sanitizer.php' );
132
+ unset($sanitizer_classes['AMP_Img_Sanitizer']);
133
+ $sanitizer_classes['AMPforWP_Img_Sanitizer']= array();
134
+ }
135
+ // New Iframe sanitizer to allow popups
136
+ if(isset( $sanitizer_classes['AMP_Iframe_Sanitizer'] ) ) {
137
+ require_once( AMPFORWP_PLUGIN_DIR. 'classes/class-ampforwp-iframe-sanitizer.php' );
138
+ unset($sanitizer_classes['AMP_Iframe_Sanitizer']);
139
+ $sanitizer_classes['AMPforWP_Iframe_Sanitizer']= array();
140
+ }
141
+ return $sanitizer_classes;
142
+ }
143
+
144
+ // Embed Handlers
145
+ function ampforwp_modified_embed_handlers($handlers){
146
+ // New Gallery Embed Handler for Gallery with Captions
147
+ if(isset($handlers['AMP_Gallery_Embed_Handler'])) {
148
+ require_once(AMPFORWP_PLUGIN_DIR. 'classes/class-ampforwp-gallery-embed.php');
149
+ unset($handlers['AMP_Gallery_Embed_Handler']);
150
+ $handlers['AMPforWP_Gallery_Embed_Handler'] = array();
151
+ }
152
+ // New Vimeo Embed Handler
153
+ if (isset($handlers['AMP_Vimeo_Embed_Handler'])) {
154
+ require_once(AMPFORWP_PLUGIN_DIR. 'classes/class-ampforwp-vimeo-embed.php');
155
+ unset($handlers['AMP_Vimeo_Embed_Handler']);
156
+ $handlers['AMPforWP_Vimeo_Embed_Handler'] = array();
157
+ }
158
+ return $handlers;
159
+ }
160
+
161
+
162
+
163
+ function ampforwp_post_status($enabled){
164
+ global $redux_builder_amp, $post;
165
+ if ( ( is_single() && 'post' === $post->post_type && ! $redux_builder_amp['amp-on-off-for-all-posts'] ) || ( is_page() && ! $redux_builder_amp['amp-on-off-for-all-pages'] ) ){
166
+ $enabled = false;
167
+ }
168
+ if( ( ampforwp_is_home() || ampforwp_is_front_page() ) && ! $redux_builder_amp['ampforwp-homepage-on-off-support'] ){
169
+ // returning false will redirect the homepage to the last post
170
+ // Redirect the Homepage from here itself
171
+ wp_safe_redirect( get_bloginfo('url'), 301 );
172
+ exit;
173
+ }
174
+ return $enabled;
175
+ }
176
+
177
+ // Template Overriding for Home, Blog, FrontPage , Archives and Search
178
+ function ampforwp_custom_template( $file, $type, $post ) {
179
+ if ( current_theme_supports( 'amp' ) || start_non_amp_to_amp_conversion() ) {
180
+ return $file;
181
+ }
182
+ global $redux_builder_amp, $wp_query;
183
+ $slug = array();
184
+ $current_url_in_pieces = array();
185
+ $ampforwp_custom_post_page = ampforwp_custom_post_page();
186
+ if ( 'single' === $type ) {
187
+ // Homepage
188
+ if ( ampforwp_is_home() ) {
189
+ $file = AMPFORWP_PLUGIN_DIR . '/templates/design-manager/design-'. ampforwp_design_selector() .'/index.php';
190
+ }
191
+ // Archive Pages
192
+ if ( is_archive() && $redux_builder_amp['ampforwp-archive-support'] ) {
193
+
194
+ $file = AMPFORWP_PLUGIN_DIR . '/templates/design-manager/design-'. ampforwp_design_selector() .'/archive.php';
195
+ }
196
+ // Search pages
197
+ if ( is_search() &&
198
+ ( $redux_builder_amp['amp-design-1-search-feature'] ||
199
+ $redux_builder_amp['amp-design-2-search-feature'] ||
200
+ $redux_builder_amp['amp-design-3-search-feature'] )
201
+ ) {
202
+ $file = AMPFORWP_PLUGIN_DIR . '/templates/design-manager/design-'. ampforwp_design_selector() .'/search.php';
203
+ }
204
+ // FrontPage
205
+ if ( ampforwp_is_front_page() ) {
206
+ $file = AMPFORWP_PLUGIN_DIR . '/templates/design-manager/design-'. ampforwp_design_selector() .'/frontpage.php';
207
+ }
208
+ }
209
+
210
+ // Polylang compatibility
211
+ // For Frontpage
212
+ if ( 'single' === $type && ampforwp_polylang_front_page() && true == $redux_builder_amp['amp-frontpage-select-option'] ) {
213
+ $file = AMPFORWP_PLUGIN_DIR . '/templates/design-manager/design-'. ampforwp_design_selector() .'/frontpage.php';
214
+ }
215
+ if( 'page' === $type ) {
216
+ // pages
217
+ if ( is_page() ) {
218
+ $file = AMPFORWP_PLUGIN_DIR . '/templates/design-manager/design-'. ampforwp_design_selector() .'/single.php';
219
+ }
220
+ // Blog
221
+ if ( ampforwp_is_blog() ) {
222
+ $file = AMPFORWP_PLUGIN_DIR . '/templates/design-manager/design-'. ampforwp_design_selector() .'/index.php';
223
+ }
224
+ // FrontPage
225
+ if ( ampforwp_is_front_page() ) {
226
+ $file = AMPFORWP_PLUGIN_DIR . '/templates/design-manager/design-'. ampforwp_design_selector() .'/frontpage.php';
227
+ }
228
+
229
+ }
230
+ return $file;
231
+ }
232
+
233
+
234
+
235
+ // End-point (?amp) and correct amphtml for pages after 0.7
236
+ function ampforwp_change_end_point($url){
237
+ global $redux_builder_amp;
238
+ $post_id = get_the_ID();
239
+ $amp_url = $url;
240
+ if( is_page() && is_post_type_hierarchical( get_post_type( $post_id ) )){
241
+ $amp_url = remove_query_arg( 'amp', $amp_url );
242
+ $amp_url = trailingslashit( $amp_url );
243
+ $amp_url = user_trailingslashit( $amp_url . AMPFORWP_AMP_QUERY_VAR );
244
+ }
245
+ if(isset($redux_builder_amp['amp-core-end-point']) && $redux_builder_amp['amp-core-end-point']){
246
+ $amp_url = get_permalink();
247
+ $amp_url = add_query_arg(AMPFORWP_AMP_QUERY_VAR,'',$amp_url);
248
+ }
249
+ return $amp_url;
250
+ }
251
+
252
+ /*
253
+ * Function Check wp theme will convert as AMP theme or not
254
+ * Its @return true when Convert option Enabled
255
+ */
256
+ function start_non_amp_to_amp_conversion(){
257
+ global $redux_builder_amp;
258
+ if(
259
+ isset( $redux_builder_amp['amp-design-type-selection'] )
260
+ && 'amp-converter' == $redux_builder_amp['amp-design-type-selection']
261
+ ){
262
+ $url_path = trim(parse_url(add_query_arg(array()), PHP_URL_PATH), '/');
263
+ $pos = strpos($url_path, amp_get_slug());
264
+ if(false!== $pos){
265
+ return true;
266
+ }
267
+ return false;
268
+ }
269
+ return false;
270
+ }
271
+
272
+ // Integer value for date, more info: #1241
273
+ function ampforwp_post_template_data( $data ) {
274
+ // post publish timestamp. Integer value for date, more info: #1241
275
+ $data['post_publish_timestamp'] = intval($data['post_publish_timestamp']);
276
+ // Placeholder Image. for more info: #1310
277
+ $data['placeholder_image_url'] = AMPFORWP_IMAGE_DIR. '/placeholder-icon.png';
278
+
279
+ return $data;
280
+ }
281
+
282
+
283
+ function ampforwp_update_class($classList, $currentClass){
284
+ $updateClass = array('AMP_Theme_Support',
285
+ 'AMP_Tag_And_Attribute_Sanitizer',
286
+ 'AMP_Style_Sanitizer',
287
+ 'AMP_Allowed_Tags_Generated',
288
+ );
289
+ if(!in_array($currentClass, $updateClass)){
290
+ return true;
291
+ }
292
+ switch ($currentClass) {
293
+ case 'AMP_Theme_Support':
294
+ if ( file_exists( AMPFORWP_PLUGIN_DIR .'/includes/vendor/vendor-files/vendor/autoload.php' ) ) {
295
+ require_once AMPFORWP_PLUGIN_DIR .'/includes/vendor/vendor-files/vendor/autoload.php';
296
+ }
297
+ require AMPFORWP_PLUGIN_DIR . "/includes/vendor/vendor-files/class-amp-theme-support.php";
298
+ //AMP_Response_Headers
299
+ require AMPFORWP_PLUGIN_DIR . "/includes/vendor/vendor-files/class-amp-response-headers.php";
300
+ //AMP_Core_Theme_Sanitizer
301
+ require AMPFORWP_PLUGIN_DIR . "/includes/vendor/vendor-files/sanitizer/class-amp-core-theme-sanitizer.php";
302
+ break;
303
+ case "AMP_Tag_And_Attribute_Sanitizer":
304
+ require AMPFORWP_PLUGIN_DIR . "/includes/vendor/vendor-files/sanitizer/class-amp-tag-and-attribute-sanitizer.php";
305
+ break;
306
+ case "AMP_Style_Sanitizer":
307
+ require AMPFORWP_PLUGIN_DIR . "/includes/vendor/vendor-files/sanitizer/class-amp-style-sanitizer.php";
308
+ break;
309
+ case "AMP_Allowed_Tags_Generated":
310
+ require AMPFORWP_PLUGIN_DIR . "/includes/vendor/vendor-files/class-amp-allowed-tags-generated.php";
311
+ break;
312
+ default:
313
+ # code...
314
+ break;
315
+ }
316
+ return false;
317
+ }
318
+
319
+
320
+ if(!function_exists('ampforwp_isexternal')){
321
+ function ampforwp_isexternal($url) {
322
+ $components = parse_url($url);
323
+ if ( empty($components['host']) ) return false; // we will treat url like '/relative.php' as relative
324
+ if ( strcasecmp($components['host'], $_SERVER['HTTP_HOST']) === 0 ) return false; // url host looks exactly like the local host
325
+ return strrpos(strtolower($components['host']), $_SERVER['HTTP_HOST']) !== strlen($components['host']) - strlen($_SERVER['HTTP_HOST']); // check if the url host is a subdomain
326
+ }//Function function_exists
327
+ }// ampforwp_isexternal function_exists close
328
+
329
+ if(!function_exists('ampforwp_findInternalUrl')){
330
+ function ampforwp_findInternalUrl($url){
331
+ if(!ampforwp_isexternal($url) && strpos($url, amp_get_slug())=== False){
332
+ if(strpos($url, "#")!==false){
333
+ $url = explode("#",$url);
334
+ $url = trailingslashit($url[0]).user_trailingslashit(amp_get_slug()).'#'.$url[1];
335
+ }else{
336
+ $url = trailingslashit($url).user_trailingslashit(amp_get_slug());
337
+ }
338
+ return $url;
339
+ }
340
+ return $url;
341
+ }// function Close
342
+ }// function_exists ampforwp_findInternalUrl close
includes/vendor/vendor-files/class-amp-allowed-tags-generated.php ADDED
@@ -0,0 +1,10822 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Generated by amphtml-update.py - do not edit.
4
+ *
5
+ * This is a list of HTML tags and attributes that are allowed by the
6
+ * AMP specification. Note that tag names have been converted to lowercase.
7
+ *
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(
21
+ array(
22
+ 'attr_spec_list' => array(
23
+ '[href]' => array(),
24
+ 'border' => 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
+ ),
61
+ 'role' => array(),
62
+ 'tabindex' => array(),
63
+ 'target' => array(
64
+ 'value_regex' => '(_blank|_self|_top)',
65
+ ),
66
+ 'type' => array(
67
+ 'value_casei' => 'text/html',
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(
94
+ array(
95
+ 'attr_spec_list' => array(
96
+ 'autoplay' => array(
97
+ 'value' => '',
98
+ ),
99
+ 'data-id' => array(
100
+ 'mandatory' => true,
101
+ ),
102
+ 'media' => array(),
103
+ 'noloading' => array(
104
+ 'value' => '',
105
+ ),
106
+ ),
107
+ 'tag_spec' => array(
108
+ 'requires_extension' => array(
109
+ 'amp-3q-player',
110
+ ),
111
+ ),
112
+ ),
113
+ ),
114
+ 'amp-accordion' => array(
115
+ array(
116
+ 'attr_spec_list' => 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(
133
+ array(
134
+ 'attr_spec_list' => array(
135
+ 'alt' => array(),
136
+ 'json' => array(),
137
+ 'media' => 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(
193
+ 'mandatory' => true,
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(
258
+ 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(
281
+ array(
282
+ 'attr_spec_list' => array(
283
+ 'alt' => array(),
284
+ 'attribution' => array(),
285
+ 'controls' => array(),
286
+ 'media' => array(),
287
+ 'noloading' => array(
288
+ 'value' => '',
289
+ ),
290
+ 'src' => array(
291
+ 'alternative_names' => array(
292
+ 'srcset',
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(
315
+ array(
316
+ 'attr_spec_list' => array(
317
+ 'media' => array(),
318
+ 'noloading' => array(
319
+ 'value' => '',
320
+ ),
321
+ 'trigger' => array(
322
+ 'value' => 'visibility',
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(
343
+ 'value' => '',
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(
355
+ array(
356
+ 'attr_spec_list' => array(
357
+ 'id' => array(
358
+ 'mandatory' => true,
359
+ ),
360
+ 'media' => array(),
361
+ 'noloading' => array(
362
+ 'value' => '',
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(
376
+ array(
377
+ 'attr_spec_list' => array(
378
+ 'album' => array(),
379
+ 'artist' => array(),
380
+ 'artwork' => array(),
381
+ 'autoplay' => array(
382
+ 'value' => '',
383
+ ),
384
+ 'controls' => array(),
385
+ 'controlslist' => array(),
386
+ 'loop' => array(
387
+ 'value' => '',
388
+ ),
389
+ 'media' => array(),
390
+ 'muted' => array(
391
+ 'value' => '',
392
+ ),
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(
421
+ 'album' => array(),
422
+ 'artist' => array(),
423
+ 'artwork' => array(),
424
+ 'autoplay' => array(
425
+ 'mandatory' => true,
426
+ 'value' => '',
427
+ ),
428
+ 'controls' => array(),
429
+ 'controlslist' => array(),
430
+ 'loop' => array(
431
+ 'value' => '',
432
+ ),
433
+ 'media' => array(),
434
+ 'muted' => array(
435
+ 'value' => '',
436
+ ),
437
+ 'noloading' => array(
438
+ 'value' => '',
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(
553
+ array(
554
+ 'attr_spec_list' => array(
555
+ 'autoplay' => array(),
556
+ 'data-outstream' => array(
557
+ 'value_regex' => '[0-9]+',
558
+ ),
559
+ 'data-partner' => array(
560
+ 'mandatory' => true,
561
+ 'value_regex' => '[0-9]+',
562
+ ),
563
+ 'data-player' => array(
564
+ 'mandatory' => true,
565
+ 'value_regex' => '[0-9]+',
566
+ ),
567
+ 'data-playlist' => array(
568
+ 'value_regex' => '[0-9]+',
569
+ ),
570
+ 'data-video' => array(
571
+ 'value_regex' => '[0-9]+',
572
+ ),
573
+ 'media' => array(),
574
+ 'noloading' => array(
575
+ 'value' => '',
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(
587
+ array(
588
+ 'attr_spec_list' => array(
589
+ '[data-account]' => array(),
590
+ '[data-embed]' => array(),
591
+ '[data-player-id]' => array(),
592
+ '[data-player]' => array(),
593
+ '[data-playlist-id]' => array(),
594
+ '[data-video-id]' => array(),
595
+ 'data-account' => array(
596
+ 'mandatory' => true,
597
+ ),
598
+ 'media' => array(),
599
+ 'noloading' => array(
600
+ 'value' => '',
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(
633
+ array(
634
+ 'attr_spec_list' => 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(),
646
+ 'noloading' => array(
647
+ 'value' => '',
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(
659
+ array(
660
+ 'attr_spec_list' => array(
661
+ '[slide]' => array(),
662
+ 'arrows' => array(
663
+ 'value' => '',
664
+ ),
665
+ 'autoplay' => array(
666
+ 'value' => '',
667
+ ),
668
+ 'controls' => array(),
669
+ 'delay' => array(
670
+ 'value_regex' => '[0-9]+',
671
+ ),
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
+ ),
685
+ 'media' => array(),
686
+ 'noloading' => array(
687
+ 'value' => '',
688
+ ),
689
+ 'type' => array(
690
+ 'value_regex' => 'slides|carousel',
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(
718
+ array(
719
+ 'attr_spec_list' => array(
720
+ 'autoplay' => array(),
721
+ 'data-endscreen-enable' => array(
722
+ 'value_regex' => 'true|false',
723
+ ),
724
+ 'data-info' => array(
725
+ 'value_regex' => 'true|false',
726
+ ),
727
+ 'data-mute' => array(
728
+ 'value_regex' => 'true|false',
729
+ ),
730
+ 'data-sharing-enable' => array(
731
+ 'value_regex' => 'true|false',
732
+ ),
733
+ 'data-start' => array(
734
+ 'value_regex' => '[0-9]+',
735
+ ),
736
+ 'data-ui-highlight' => array(
737
+ 'value_regex_casei' => '([0-9a-f]{3}){1,2}',
738
+ ),
739
+ 'data-ui-logo' => array(
740
+ 'value_regex' => 'true|false',
741
+ ),
742
+ 'data-videoid' => array(
743
+ 'mandatory' => true,
744
+ 'value_regex_casei' => '[a-z0-9]+',
745
+ ),
746
+ 'media' => array(),
747
+ 'noloading' => array(
748
+ 'value' => '',
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(
1124
+ 'attr_spec_list' => array(
1125
+ 'data-href' => array(
1126
+ 'mandatory' => true,
1127
+ ),
1128
+ 'media' => array(),
1129
+ 'noloading' => array(
1130
+ 'value' => '',
1131
+ ),
1132
+ ),
1133
+ 'tag_spec' => array(
1134
+ 'requires_extension' => array(
1135
+ 'amp-facebook-comments',
1136
+ ),
1137
+ ),
1138
+ ),
1139
+ ),
1140
+ 'amp-facebook-like' => array(
1141
+ 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(),
1154
+ 'noloading' => array(
1155
+ 'value' => '',
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(
1191
+ array(
1192
+ 'attr_spec_list' => array(
1193
+ 'max-font-size' => array(),
1194
+ 'media' => array(),
1195
+ 'min-font-size' => array(),
1196
+ 'noloading' => 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(
1208
+ array(
1209
+ 'attr_spec_list' => array(
1210
+ 'font-family' => array(
1211
+ 'mandatory' => true,
1212
+ ),
1213
+ 'font-style' => array(),
1214
+ 'font-variant' => array(),
1215
+ 'font-weight' => array(),
1216
+ 'media' => array(),
1217
+ 'noloading' => array(
1218
+ 'value' => '',
1219
+ ),
1220
+ 'on-error-add-class' => array(),
1221
+ 'on-error-remove-class' => array(),
1222
+ 'on-load-add-class' => array(),
1223
+ 'on-load-remove-class' => array(),
1224
+ 'timeout' => array(
1225
+ 'value_regex' => '[0-9]+',
1226
+ ),
1227
+ ),
1228
+ 'tag_spec' => array(
1229
+ 'requires_extension' => array(
1230
+ 'amp-font',
1231
+ ),
1232
+ ),
1233
+ ),
1234
+ ),
1235
+ 'amp-fx-flying-carpet' => array(
1236
+ array(
1237
+ 'attr_spec_list' => array(
1238
+ 'height' => array(
1239
+ 'mandatory' => true,
1240
+ ),
1241
+ 'media' => array(),
1242
+ 'noloading' => array(
1243
+ 'value' => '',
1244
+ ),
1245
+ ),
1246
+ 'tag_spec' => array(
1247
+ 'requires_extension' => array(
1248
+ 'amp-fx-flying-carpet',
1249
+ ),
1250
+ ),
1251
+ ),
1252
+ ),
1253
+ 'amp-gfycat' => array(
1254
+ array(
1255
+ 'attr_spec_list' => array(
1256
+ 'data-gfyid' => array(
1257
+ 'mandatory' => true,
1258
+ ),
1259
+ 'media' => array(),
1260
+ 'noautoplay' => array(
1261
+ 'value' => '',
1262
+ ),
1263
+ 'noloading' => array(
1264
+ 'value' => '',
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(
1276
+ array(
1277
+ 'attr_spec_list' => array(
1278
+ 'data-gistid' => array(
1279
+ 'mandatory' => true,
1280
+ ),
1281
+ 'media' => array(),
1282
+ 'noloading' => array(
1283
+ 'value' => '',
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(
1295
+ array(
1296
+ 'attr_spec_list' => array(
1297
+ 'data-eid' => array(
1298
+ 'mandatory' => true,
1299
+ ),
1300
+ 'media' => array(),
1301
+ 'noloading' => array(
1302
+ 'value' => '',
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
+ ),
1321
+ 'allowpaymentrequest' => array(
1322
+ 'value' => '',
1323
+ ),
1324
+ 'allowtransparency' => array(
1325
+ 'value' => '',
1326
+ ),
1327
+ 'frameborder' => array(
1328
+ 'value_regex' => '0|1',
1329
+ ),
1330
+ 'media' => array(),
1331
+ 'noloading' => array(
1332
+ 'value' => '',
1333
+ ),
1334
+ 'referrerpolicy' => array(),
1335
+ 'resizable' => array(
1336
+ 'value' => '',
1337
+ ),
1338
+ 'sandbox' => array(),
1339
+ 'scrolling' => array(
1340
+ 'value_regex' => 'auto|yes|no',
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(),
1386
+ 'noloading' => array(
1387
+ 'value' => '',
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(
1399
+ array(
1400
+ 'attr_spec_list' => array(
1401
+ 'controls' => array(),
1402
+ 'media' => array(),
1403
+ 'noloading' => array(
1404
+ 'value' => '',
1405
+ ),
1406
+ ),
1407
+ 'tag_spec' => array(
1408
+ 'requires_extension' => array(
1409
+ 'amp-image-lightbox',
1410
+ ),
1411
+ ),
1412
+ ),
1413
+ ),
1414
+ 'amp-img' => array(
1415
+ array(
1416
+ 'attr_spec_list' => array(
1417
+ '[alt]' => array(),
1418
+ '[attribution]' => array(),
1419
+ '[src]' => 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' => '',
1433
+ ),
1434
+ 'placeholder' => array(),
1435
+ 'src' => array(
1436
+ 'alternative_names' => array(
1437
+ 'srcset',
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(
1457
+ array(
1458
+ 'attr_spec_list' => array(
1459
+ 'data-imgur-id' => array(
1460
+ 'mandatory' => true,
1461
+ ),
1462
+ 'media' => array(),
1463
+ 'noloading' => array(
1464
+ 'value' => '',
1465
+ ),
1466
+ ),
1467
+ 'tag_spec' => array(
1468
+ 'requires_extension' => array(
1469
+ 'amp-imgur',
1470
+ ),
1471
+ ),
1472
+ ),
1473
+ ),
1474
+ 'amp-instagram' => array(
1475
+ array(
1476
+ 'attr_spec_list' => array(
1477
+ 'alt' => array(),
1478
+ 'data-shortcode' => array(
1479
+ 'mandatory' => true,
1480
+ ),
1481
+ 'media' => array(),
1482
+ 'noloading' => array(
1483
+ 'value' => '',
1484
+ ),
1485
+ ),
1486
+ 'tag_spec' => array(
1487
+ 'requires_extension' => array(
1488
+ 'amp-instagram',
1489
+ ),
1490
+ ),
1491
+ ),
1492
+ ),
1493
+ 'amp-install-serviceworker' => array(
1494
+ 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(
1524
+ array(
1525
+ 'attr_spec_list' => array(
1526
+ 'data-videoid' => array(
1527
+ 'mandatory' => true,
1528
+ 'value_regex' => '[0-9]+',
1529
+ ),
1530
+ 'media' => array(),
1531
+ 'noloading' => array(
1532
+ 'value' => '',
1533
+ ),
1534
+ ),
1535
+ 'tag_spec' => array(
1536
+ 'requires_extension' => array(
1537
+ 'amp-izlesene',
1538
+ ),
1539
+ ),
1540
+ ),
1541
+ ),
1542
+ 'amp-jwplayer' => array(
1543
+ array(
1544
+ 'attr_spec_list' => array(
1545
+ 'data-media-id' => array(
1546
+ 'value_regex_casei' => '[0-9a-z]{8}',
1547
+ ),
1548
+ 'data-player-id' => array(
1549
+ 'mandatory' => true,
1550
+ 'value_regex_casei' => '[0-9a-z]{8}',
1551
+ ),
1552
+ 'data-playlist-id' => array(
1553
+ 'value_regex_casei' => '[0-9a-z]{8}',
1554
+ ),
1555
+ ),
1556
+ 'tag_spec' => array(
1557
+ 'requires_extension' => array(
1558
+ 'amp-jwplayer',
1559
+ ),
1560
+ ),
1561
+ ),
1562
+ ),
1563
+ 'amp-kaltura-player' => array(
1564
+ array(
1565
+ 'attr_spec_list' => array(
1566
+ 'data-partner' => array(
1567
+ 'mandatory' => true,
1568
+ ),
1569
+ 'media' => array(),
1570
+ 'noloading' => array(
1571
+ 'value' => '',
1572
+ ),
1573
+ ),
1574
+ 'tag_spec' => array(
1575
+ 'requires_extension' => array(
1576
+ 'amp-kaltura-player',
1577
+ ),
1578
+ ),
1579
+ ),
1580
+ ),
1581
+ 'amp-layout' => array(
1582
+ array(
1583
+ 'attr_spec_list' => array(
1584
+ 'media' => array(),
1585
+ 'noloading' => array(
1586
+ 'value' => '',
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(
1595
+ array(
1596
+ 'attr_spec_list' => array(
1597
+ 'controls' => array(),
1598
+ 'from' => array(),
1599
+ 'media' => array(),
1600
+ 'noloading' => array(
1601
+ 'value' => '',
1602
+ ),
1603
+ 'scrollable' => array(),
1604
+ ),
1605
+ 'tag_spec' => array(
1606
+ 'requires_extension' => array(
1607
+ 'amp-lightbox',
1608
+ ),
1609
+ ),
1610
+ ),
1611
+ ),
1612
+ 'amp-list' => array(
1613
+ array(
1614
+ 'attr_spec_list' => array(
1615
+ '[src]' => array(),
1616
+ '[state]' => array(),
1617
+ 'credentials' => array(),
1618
+ 'items' => array(),
1619
+ 'max-items' => array(),
1620
+ 'media' => 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(
1679
+ array(
1680
+ 'attr_spec_list' => array(
1681
+ 'data-max-items-per-page' => array(
1682
+ 'mandatory' => true,
1683
+ 'value_regex' => '\\d+',
1684
+ ),
1685
+ 'data-poll-interval' => array(
1686
+ 'value_regex' => '\\d{5,}',
1687
+ ),
1688
+ 'disabled' => array(
1689
+ 'value' => '',
1690
+ ),
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
+ ),
1734
+ 'data-mode' => array(
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
+ ),
1749
+ 'media' => array(),
1750
+ 'noloading' => array(
1751
+ 'value' => '',
1752
+ ),
1753
+ ),
1754
+ 'tag_spec' => array(
1755
+ 'requires_extension' => array(
1756
+ 'amp-nexxtv-player',
1757
+ ),
1758
+ ),
1759
+ ),
1760
+ ),
1761
+ 'amp-o2-player' => array(
1762
+ array(
1763
+ 'attr_spec_list' => array(
1764
+ 'data-bcid' => array(
1765
+ 'mandatory' => true,
1766
+ ),
1767
+ 'data-pid' => array(
1768
+ 'mandatory' => true,
1769
+ ),
1770
+ 'media' => array(),
1771
+ 'noloading' => array(
1772
+ 'value' => '',
1773
+ ),
1774
+ ),
1775
+ 'tag_spec' => array(
1776
+ 'requires_extension' => array(
1777
+ 'amp-o2-player',
1778
+ ),
1779
+ ),
1780
+ ),
1781
+ ),
1782
+ 'amp-ooyala-player' => array(
1783
+ array(
1784
+ 'attr_spec_list' => array(
1785
+ 'data-embedcode' => array(
1786
+ 'mandatory' => true,
1787
+ ),
1788
+ 'data-pcode' => array(
1789
+ 'mandatory' => true,
1790
+ ),
1791
+ 'data-playerid' => array(
1792
+ 'mandatory' => true,
1793
+ ),
1794
+ 'media' => array(),
1795
+ 'noloading' => array(
1796
+ 'value' => '',
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
+ ),
1813
+ 'media' => array(),
1814
+ 'noloading' => array(
1815
+ 'value' => '',
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' => '',
1833
+ ),
1834
+ 'referrerpolicy' => array(
1835
+ 'value' => 'no-referrer',
1836
+ ),
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(
1855
+ array(
1856
+ 'attr_spec_list' => array(
1857
+ 'data-comments' => array(
1858
+ 'value_regex_casei' => '(false|true)',
1859
+ ),
1860
+ 'data-item' => array(),
1861
+ 'data-item-info' => array(
1862
+ 'value_regex_casei' => '(false|true)',
1863
+ ),
1864
+ 'data-share-buttons' => array(
1865
+ 'value_regex_casei' => '(false|true)',
1866
+ ),
1867
+ 'media' => array(),
1868
+ 'noloading' => array(
1869
+ 'value' => '',
1870
+ ),
1871
+ 'src' => array(),
1872
+ ),
1873
+ 'tag_spec' => array(
1874
+ 'requires_extension' => array(
1875
+ 'amp-playbuzz',
1876
+ ),
1877
+ ),
1878
+ ),
1879
+ ),
1880
+ 'amp-position-observer' => array(
1881
+ array(
1882
+ 'attr_spec_list' => array(
1883
+ 'intersection-ratios' => array(
1884
+ 'value_regex' => '^([0]*?\\.\\d*$|1$|0$)|([0]*?\\.\\d*|1|0)\\s{1}([0]*?\\.\\d*$|1$|0$)',
1885
+ ),
1886
+ 'media' => array(),
1887
+ 'noloading' => array(
1888
+ 'value' => '',
1889
+ ),
1890
+ 'target' => array(),
1891
+ 'viewport-margins' => array(
1892
+ 'value_regex' => '^(\\d+$|\\d+px$|\\d+vh$)|((\\d+|\\d+px|\\d+vh)\\s{1}(\\d+$|\\d+px$|\\d+vh$))',
1893
+ ),
1894
+ ),
1895
+ 'tag_spec' => array(
1896
+ 'requires_extension' => array(
1897
+ 'amp-position-observer',
1898
+ ),
1899
+ ),
1900
+ ),
1901
+ ),
1902
+ 'amp-reach-player' => array(
1903
+ array(
1904
+ 'attr_spec_list' => array(
1905
+ 'data-embed-id' => array(
1906
+ 'mandatory' => true,
1907
+ 'value_regex' => '[0-9a-z-]+',
1908
+ ),
1909
+ 'media' => array(),
1910
+ 'noloading' => array(
1911
+ 'value' => '',
1912
+ ),
1913
+ ),
1914
+ 'tag_spec' => array(
1915
+ 'requires_extension' => array(
1916
+ 'amp-reach-player',
1917
+ ),
1918
+ ),
1919
+ ),
1920
+ ),
1921
+ 'amp-reddit' => array(
1922
+ array(
1923
+ 'attr_spec_list' => array(
1924
+ 'data-embedlive' => array(
1925
+ 'value_regex_casei' => '(false|true)',
1926
+ ),
1927
+ 'data-embedparent' => array(
1928
+ 'value_regex_casei' => '(false|true)',
1929
+ ),
1930
+ 'data-embedtype' => array(
1931
+ 'mandatory' => true,
1932
+ 'value_regex_casei' => '(comment|post)',
1933
+ ),
1934
+ 'data-src' => array(
1935
+ 'mandatory' => true,
1936
+ ),
1937
+ 'media' => array(),
1938
+ 'noloading' => array(
1939
+ 'value' => '',
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' => '',
1976
+ ),
1977
+ 'form' => array(),
1978
+ 'keyboard-select-mode' => array(
1979
+ 'value_regex_casei' => 'focus|none|select',
1980
+ ),
1981
+ 'media' => array(),
1982
+ 'multiple' => array(
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' => '',
1990
+ ),
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(
2003
+ array(
2004
+ 'attr_spec_list' => array(
2005
+ 'media' => array(),
2006
+ 'noloading' => array(
2007
+ 'value' => '',
2008
+ ),
2009
+ 'side' => array(
2010
+ 'value_regex' => '(left|right)',
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(
2022
+ 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(),
2049
+ 'noloading' => array(
2050
+ 'value' => '',
2051
+ ),
2052
+ 'type' => array(
2053
+ 'mandatory' => true,
2054
+ ),
2055
+ ),
2056
+ 'tag_spec' => array(
2057
+ 'requires_extension' => array(
2058
+ 'amp-social-share',
2059
+ ),
2060
+ ),
2061
+ ),
2062
+ ),
2063
+ 'amp-soundcloud' => array(
2064
+ array(
2065
+ 'attr_spec_list' => array(
2066
+ 'data-color' => array(
2067
+ 'value_regex_casei' => '([0-9a-f]{3}){1,2}',
2068
+ ),
2069
+ 'data-playlistid' => 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]+',
2077
+ ),
2078
+ 'data-visual' => array(
2079
+ 'value_regex' => 'true|false',
2080
+ ),
2081
+ 'media' => array(),
2082
+ 'noloading' => array(
2083
+ 'value' => '',
2084
+ ),
2085
+ ),
2086
+ 'tag_spec' => array(
2087
+ 'requires_extension' => array(
2088
+ 'amp-soundcloud',
2089
+ ),
2090
+ ),
2091
+ ),
2092
+ ),
2093
+ 'amp-springboard-player' => array(
2094
+ array(
2095
+ 'attr_spec_list' => array(
2096
+ 'data-content-id' => array(
2097
+ 'mandatory' => true,
2098
+ ),
2099
+ 'data-domain' => array(
2100
+ 'mandatory' => true,
2101
+ ),
2102
+ 'data-items' => array(
2103
+ 'mandatory' => true,
2104
+ ),
2105
+ 'data-mode' => array(
2106
+ 'mandatory' => true,
2107
+ 'value_regex_casei' => 'playlist|video',
2108
+ ),
2109
+ 'data-player-id' => array(
2110
+ 'mandatory' => true,
2111
+ 'value_regex_casei' => '[a-z0-9]+',
2112
+ ),
2113
+ 'data-site-id' => array(
2114
+ 'mandatory' => true,
2115
+ 'value_regex' => '[0-9]+',
2116
+ ),
2117
+ 'media' => array(),
2118
+ 'noloading' => array(
2119
+ 'value' => '',
2120
+ ),
2121
+ ),
2122
+ 'tag_spec' => array(
2123
+ 'requires_extension' => array(
2124
+ 'amp-springboard-player',
2125
+ ),
2126
+ ),
2127
+ ),
2128
+ ),
2129
+ 'amp-state' => array(
2130
+ array(
2131
+ 'attr_spec_list' => array(
2132
+ '[src]' => array(),
2133
+ 'credentials' => 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(
2158
+ array(
2159
+ 'attr_spec_list' => array(
2160
+ 'media' => array(),
2161
+ 'noloading' => array(
2162
+ 'value' => '',
2163
+ ),
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(
2196
+ 'mandatory' => true,
2197
+ 'value' => '',
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(
2230
+ array(
2231
+ 'attr_spec_list' => array(
2232
+ 'template' => array(
2233
+ 'mandatory' => true,
2234
+ 'value_regex' => '(fill|horizontal|vertical|thirds)',
2235
+ ),
2236
+ ),
2237
+ 'tag_spec' => array(
2238
+ 'mandatory_ancestor' => 'amp-story-page',
2239
+ ),
2240
+ ),
2241
+ ),
2242
+ 'amp-story-page' => array(
2243
+ 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(
2255
+ 'mandatory' => true,
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(
2267
+ array(
2268
+ 'attr_spec_list' => array(
2269
+ 'cutoff' => array(
2270
+ 'value_regex' => '\\d+',
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(),
2278
+ 'noloading' => array(
2279
+ 'value' => '',
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(
2291
+ array(
2292
+ 'attr_spec_list' => array(
2293
+ 'data-tweetid' => array(
2294
+ 'mandatory' => true,
2295
+ ),
2296
+ 'media' => array(),
2297
+ 'noloading' => array(
2298
+ 'value' => '',
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(
2330
+ 'value' => 'application/x-www-form-urlencoded',
2331
+ ),
2332
+ 'media' => array(),
2333
+ 'noloading' => array(
2334
+ 'value' => '',
2335
+ ),
2336
+ ),
2337
+ 'tag_spec' => array(
2338
+ 'requires_extension' => array(
2339
+ 'amp-user-notification',
2340
+ ),
2341
+ ),
2342
+ ),
2343
+ ),
2344
+ 'amp-video' => array(
2345
+ array(
2346
+ 'attr_spec_list' => array(
2347
+ '[album]' => array(),
2348
+ '[alt]' => array(),
2349
+ '[artist]' => array(),
2350
+ '[artwork]' => array(),
2351
+ '[attribution]' => array(),
2352
+ '[controls]' => array(),
2353
+ '[controlslist]' => array(),
2354
+ '[loop]' => array(),
2355
+ '[poster]' => array(),
2356
+ '[preload]' => array(),
2357
+ '[src]' => array(),
2358
+ '[title]' => array(),
2359
+ 'album' => array(),
2360
+ 'alt' => array(),
2361
+ 'artist' => array(),
2362
+ 'artwork' => array(),
2363
+ 'attribution' => array(),
2364
+ 'autoplay' => array(
2365
+ 'value' => '',
2366
+ ),
2367
+ 'controls' => array(
2368
+ 'value' => '',
2369
+ ),
2370
+ 'controlslist' => array(),
2371
+ 'crossorigin' => 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
+ ),
2385
+ 'media' => array(),
2386
+ 'muted' => array(
2387
+ 'value' => '',
2388
+ ),
2389
+ 'noloading' => array(
2390
+ 'value' => '',
2391
+ ),
2392
+ 'placeholder' => array(),
2393
+ 'poster' => array(),
2394
+ 'preload' => array(
2395
+ 'value_regex' => '(none|metadata|auto|)',
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(
2419
+ '[album]' => array(),
2420
+ '[alt]' => array(),
2421
+ '[artist]' => array(),
2422
+ '[artwork]' => array(),
2423
+ '[attribution]' => array(),
2424
+ '[controls]' => array(),
2425
+ '[controlslist]' => array(),
2426
+ '[loop]' => array(),
2427
+ '[poster]' => array(),
2428
+ '[preload]' => array(),
2429
+ '[src]' => array(),
2430
+ '[title]' => array(),
2431
+ 'album' => array(),
2432
+ 'alt' => array(),
2433
+ 'artist' => array(),
2434
+ 'artwork' => array(),
2435
+ 'attribution' => array(),
2436
+ 'autoplay' => array(
2437
+ 'value' => '',
2438
+ ),
2439
+ 'controls' => array(
2440
+ 'value' => '',
2441
+ ),
2442
+ 'controlslist' => array(),
2443
+ 'crossorigin' => array(),
2444
+ 'disableremoteplayback' => array(
2445
+ 'value' => '',
2446
+ ),
2447
+ 'loop' => array(
2448
+ 'value' => '',
2449
+ ),
2450
+ 'media' => array(),
2451
+ 'muted' => array(
2452
+ 'value' => '',
2453
+ ),
2454
+ 'noloading' => array(
2455
+ 'value' => '',
2456
+ ),
2457
+ 'placeholder' => array(),
2458
+ 'poster' => array(
2459
+ 'mandatory' => true,
2460
+ ),
2461
+ 'preload' => array(
2462
+ 'value_regex' => '(none|metadata|auto|)',
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(
2485
+ array(
2486
+ 'attr_spec_list' => array(
2487
+ 'data-videoid' => array(
2488
+ 'mandatory' => true,
2489
+ 'value_regex' => '[0-9]+',
2490
+ ),
2491
+ 'media' => array(),
2492
+ 'noloading' => array(
2493
+ 'value' => '',
2494
+ ),
2495
+ ),
2496
+ 'tag_spec' => array(
2497
+ 'requires_extension' => array(
2498
+ 'amp-vimeo',
2499
+ ),
2500
+ ),
2501
+ ),
2502
+ ),
2503
+ 'amp-vine' => array(
2504
+ array(
2505
+ 'attr_spec_list' => array(
2506
+ 'data-vineid' => array(
2507
+ 'mandatory' => true,
2508
+ ),
2509
+ 'media' => array(),
2510
+ 'noloading' => array(
2511
+ 'value' => '',
2512
+ ),
2513
+ ),
2514
+ 'tag_spec' => array(
2515
+ 'requires_extension' => array(
2516
+ 'amp-vine',
2517
+ ),
2518
+ ),
2519
+ ),
2520
+ ),
2521
+ 'amp-vk' => array(
2522
+ array(
2523
+ 'attr_spec_list' => array(
2524
+ 'data-embedtype' => array(
2525
+ 'mandatory' => true,
2526
+ ),
2527
+ 'media' => array(),
2528
+ 'noloading' => array(
2529
+ 'value' => '',
2530
+ ),
2531
+ ),
2532
+ 'tag_spec' => array(
2533
+ 'requires_extension' => array(
2534
+ 'amp-vk',
2535
+ ),
2536
+ ),
2537
+ ),
2538
+ ),
2539
+ 'amp-web-push' => array(
2540
+ 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(
2552
+ 'mandatory' => true,
2553
+ 'value_regex' => 'amp-web-push',
2554
+ ),
2555
+ 'media' => array(),
2556
+ 'noloading' => array(
2557
+ 'value' => '',
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(
2587
+ array(
2588
+ 'attr_spec_list' => array(
2589
+ 'media' => array(),
2590
+ 'noloading' => array(
2591
+ 'value' => '',
2592
+ ),
2593
+ 'visibility' => array(
2594
+ 'mandatory' => true,
2595
+ 'value_regex' => '(blocked|subscribed|unsubscribed)',
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(
2626
+ array(
2627
+ 'attr_spec_list' => array(
2628
+ '[data-videoid]' => array(),
2629
+ 'autoplay' => 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(
2671
+ array(
2672
+ 'attr_spec_list' => array(
2673
+ 'autoplay' => array(),
2674
+ 'controls' => array(),
2675
+ 'loop' => array(),
2676
+ 'muted' => 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(
2725
+ array(
2726
+ 'attr_spec_list' => 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(
2739
+ array(
2740
+ 'attr_spec_list' => 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(
2758
+ array(
2759
+ 'attr_spec_list' => array(),
2760
+ 'tag_spec' => array(
2761
+ 'mandatory' => true,
2762
+ 'mandatory_parent' => 'html',
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(
2775
+ array(
2776
+ 'attr_spec_list' => array(
2777
+ '[disabled]' => array(),
2778
+ '[type]' => array(),
2779
+ '[value]' => array(),
2780
+ 'disabled' => 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(),
2788
+ 'type' => 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' => '',
2800
+ ),
2801
+ 'role' => array(),
2802
+ 'tabindex' => array(),
2803
+ 'type' => array(),
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(
2825
+ array(
2826
+ 'attr_spec_list' => array(
2827
+ 'alignment-baseline' => array(),
2828
+ 'baseline-shift' => array(),
2829
+ 'clip' => array(),
2830
+ 'clip-path' => array(),
2831
+ 'clip-rule' => array(),
2832
+ 'color' => array(),
2833
+ 'color-interpolation' => array(),
2834
+ 'color-interpolation-filters' => array(),
2835
+ 'color-profile' => array(),
2836
+ 'color-rendering' => array(),
2837
+ 'cursor' => array(),
2838
+ 'cx' => array(),
2839
+ 'cy' => array(),
2840
+ 'direction' => array(),
2841
+ 'display' => array(),
2842
+ 'dominant-baseline' => array(),
2843
+ 'enable-background' => array(),
2844
+ 'externalresourcesrequired' => array(),
2845
+ 'fill' => array(),
2846
+ 'fill-opacity' => array(),
2847
+ 'fill-rule' => array(),
2848
+ 'filter' => array(),
2849
+ 'flood-color' => array(),
2850
+ 'flood-opacity' => array(),
2851
+ 'font-family' => array(),
2852
+ 'font-size' => array(),
2853
+ 'font-size-adjust' => array(),
2854
+ 'font-stretch' => array(),
2855
+ 'font-style' => array(),
2856
+ 'font-variant' => array(),
2857
+ 'font-weight' => array(),
2858
+ 'glyph-orientation-horizontal' => array(),
2859
+ 'glyph-orientation-vertical' => array(),
2860
+ 'image-rendering' => array(),
2861
+ 'kerning' => array(),
2862
+ 'letter-spacing' => array(),
2863
+ 'lighting-color' => array(),
2864
+ 'marker-end' => array(),
2865
+ 'marker-mid' => array(),
2866
+ 'marker-start' => array(),
2867
+ 'mask' => array(),
2868
+ 'opacity' => array(),
2869
+ 'overflow' => array(),
2870
+ 'pointer-events' => array(),
2871
+ 'r' => array(),
2872
+ 'requiredextensions' => array(),
2873
+ 'requiredfeatures' => array(),
2874
+ 'shape-rendering' => array(),
2875
+ 'sketch:type' => array(),
2876
+ 'stop-color' => array(),
2877
+ 'stop-opacity' => array(),
2878
+ 'stroke' => array(),
2879
+ 'stroke-dasharray' => array(),
2880
+ 'stroke-dashoffset' => array(),
2881
+ 'stroke-linecap' => array(),
2882
+ 'stroke-linejoin' => array(),
2883
+ 'stroke-miterlimit' => array(),
2884
+ 'stroke-opacity' => array(),
2885
+ 'stroke-width' => array(),
2886
+ 'style' => array(
2887
+ 'blacklisted_value_regex' => '!important',
2888
+ ),
2889
+ 'systemlanguage' => array(),
2890
+ 'text-anchor' => array(),
2891
+ 'text-decoration' => array(),
2892
+ 'text-rendering' => array(),
2893
+ 'transform' => array(),
2894
+ 'unicode-bidi' => array(),
2895
+ 'vector-effect' => array(),
2896
+ 'visibility' => array(),
2897
+ 'word-spacing' => array(),
2898
+ 'writing-mode' => array(),
2899
+ 'xml:lang' => array(),
2900
+ 'xml:space' => array(),
2901
+ 'xmlns' => 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(
2917
+ array(
2918
+ 'attr_spec_list' => array(
2919
+ 'alignment-baseline' => array(),
2920
+ 'baseline-shift' => array(),
2921
+ 'clip' => array(),
2922
+ 'clip-path' => array(),
2923
+ 'clip-rule' => array(),
2924
+ 'clippathunits' => array(),
2925
+ 'color' => array(),
2926
+ 'color-interpolation' => array(),
2927
+ 'color-interpolation-filters' => array(),
2928
+ 'color-profile' => array(),
2929
+ 'color-rendering' => array(),
2930
+ 'cursor' => array(),
2931
+ 'direction' => array(),
2932
+ 'display' => array(),
2933
+ 'dominant-baseline' => array(),
2934
+ 'enable-background' => array(),
2935
+ 'externalresourcesrequired' => array(),
2936
+ 'fill' => array(),
2937
+ 'fill-opacity' => array(),
2938
+ 'fill-rule' => array(),
2939
+ 'filter' => array(),
2940
+ 'flood-color' => array(),
2941
+ 'flood-opacity' => array(),
2942
+ 'font-family' => array(),
2943
+ 'font-size' => array(),
2944
+ 'font-size-adjust' => array(),
2945
+ 'font-stretch' => array(),
2946
+ 'font-style' => array(),
2947
+ 'font-variant' => array(),
2948
+ 'font-weight' => array(),
2949
+ 'glyph-orientation-horizontal' => array(),
2950
+ 'glyph-orientation-vertical' => array(),
2951
+ 'image-rendering' => array(),
2952
+ 'kerning' => array(),
2953
+ 'letter-spacing' => array(),
2954
+ 'lighting-color' => array(),
2955
+ 'marker-end' => array(),
2956
+ 'marker-mid' => array(),
2957
+ 'marker-start' => array(),
2958
+ 'mask' => array(),
2959
+ 'opacity' => array(),
2960
+ 'overflow' => array(),
2961
+ 'pointer-events' => array(),
2962
+ 'requiredextensions' => array(),
2963
+ 'requiredfeatures' => array(),
2964
+ 'shape-rendering' => array(),
2965
+ 'stop-color' => array(),
2966
+ 'stop-opacity' => array(),
2967
+ 'stroke' => array(),
2968
+ 'stroke-dasharray' => array(),
2969
+ 'stroke-dashoffset' => array(),
2970
+ 'stroke-linecap' => array(),
2971
+ 'stroke-linejoin' => array(),
2972
+ 'stroke-miterlimit' => array(),
2973
+ 'stroke-opacity' => array(),
2974
+ 'stroke-width' => array(),
2975
+ 'style' => array(
2976
+ 'blacklisted_value_regex' => '!important',
2977
+ ),
2978
+ 'systemlanguage' => array(),
2979
+ 'text-anchor' => array(),
2980
+ 'text-decoration' => array(),
2981
+ 'text-rendering' => array(),
2982
+ 'transform' => array(),
2983
+ 'unicode-bidi' => array(),
2984
+ 'vector-effect' => array(),
2985
+ 'visibility' => array(),
2986
+ 'word-spacing' => array(),
2987
+ 'writing-mode' => array(),
2988
+ 'xml:lang' => array(),
2989
+ 'xml:space' => array(),
2990
+ 'xmlns' => 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(
3006
+ array(
3007
+ 'attr_spec_list' => array(
3008
+ 'span' => array(),
3009
+ ),
3010
+ 'tag_spec' => array(),
3011
+ ),
3012
+ ),
3013
+ 'colgroup' => array(
3014
+ array(
3015
+ 'attr_spec_list' => 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(
3028
+ array(
3029
+ 'attr_spec_list' => 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(
3042
+ array(
3043
+ 'attr_spec_list' => array(
3044
+ 'alignment-baseline' => array(),
3045
+ 'baseline-shift' => array(),
3046
+ 'clip' => array(),
3047
+ 'clip-path' => array(),
3048
+ 'clip-rule' => array(),
3049
+ 'color' => array(),
3050
+ 'color-interpolation' => array(),
3051
+ 'color-interpolation-filters' => array(),
3052
+ 'color-profile' => array(),
3053
+ 'color-rendering' => array(),
3054
+ 'cursor' => array(),
3055
+ 'direction' => array(),
3056
+ 'display' => array(),
3057
+ 'dominant-baseline' => array(),
3058
+ 'enable-background' => array(),
3059
+ 'externalresourcesrequired' => array(),
3060
+ 'fill' => array(),
3061
+ 'fill-opacity' => array(),
3062
+ 'fill-rule' => array(),
3063
+ 'filter' => array(),
3064
+ 'flood-color' => array(),
3065
+ 'flood-opacity' => array(),
3066
+ 'font-family' => array(),
3067
+ 'font-size' => array(),
3068
+ 'font-size-adjust' => array(),
3069
+ 'font-stretch' => array(),
3070
+ 'font-style' => array(),
3071
+ 'font-variant' => array(),
3072
+ 'font-weight' => array(),
3073
+ 'glyph-orientation-horizontal' => array(),
3074
+ 'glyph-orientation-vertical' => array(),
3075
+ 'image-rendering' => array(),
3076
+ 'kerning' => array(),
3077
+ 'letter-spacing' => array(),
3078
+ 'lighting-color' => array(),
3079
+ 'marker-end' => array(),
3080
+ 'marker-mid' => array(),
3081
+ 'marker-start' => array(),
3082
+ 'mask' => array(),
3083
+ 'opacity' => array(),
3084
+ 'overflow' => array(),
3085
+ 'pointer-events' => array(),
3086
+ 'requiredextensions' => array(),
3087
+ 'requiredfeatures' => array(),
3088
+ 'shape-rendering' => array(),
3089
+ 'stop-color' => array(),
3090
+ 'stop-opacity' => array(),
3091
+ 'stroke' => array(),
3092
+ 'stroke-dasharray' => array(),
3093
+ 'stroke-dashoffset' => array(),
3094
+ 'stroke-linecap' => array(),
3095
+ 'stroke-linejoin' => array(),
3096
+ 'stroke-miterlimit' => array(),
3097
+ 'stroke-opacity' => array(),
3098
+ 'stroke-width' => array(),
3099
+ 'style' => array(
3100
+ 'blacklisted_value_regex' => '!important',
3101
+ ),
3102
+ 'systemlanguage' => array(),
3103
+ 'text-anchor' => array(),
3104
+ 'text-decoration' => array(),
3105
+ 'text-rendering' => array(),
3106
+ 'transform' => array(),
3107
+ 'unicode-bidi' => array(),
3108
+ 'vector-effect' => array(),
3109
+ 'visibility' => array(),
3110
+ 'word-spacing' => array(),
3111
+ 'writing-mode' => array(),
3112
+ 'xml:lang' => array(),
3113
+ 'xml:space' => array(),
3114
+ 'xmlns' => 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(
3124
+ 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(
3143
+ array(
3144
+ 'attr_spec_list' => array(
3145
+ 'style' => array(
3146
+ 'blacklisted_value_regex' => '!important',
3147
+ ),
3148
+ 'xml:lang' => array(),
3149
+ 'xml:space' => array(),
3150
+ 'xmlns' => 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(
3172
+ array(
3173
+ 'attr_spec_list' => 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(
3193
+ 'align' => array(),
3194
+ 'submit-success' => array(
3195
+ 'mandatory' => true,
3196
+ ),
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(
3220
+ 'align' => array(),
3221
+ 'submit-error' => array(
3222
+ 'mandatory' => true,
3223
+ ),
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(
3259
+ array(
3260
+ 'attr_spec_list' => array(
3261
+ 'alignment-baseline' => array(),
3262
+ 'baseline-shift' => array(),
3263
+ 'clip' => array(),
3264
+ 'clip-path' => array(),
3265
+ 'clip-rule' => array(),
3266
+ 'color' => array(),
3267
+ 'color-interpolation' => array(),
3268
+ 'color-interpolation-filters' => array(),
3269
+ 'color-profile' => array(),
3270
+ 'color-rendering' => array(),
3271
+ 'cursor' => array(),
3272
+ 'cx' => array(),
3273
+ 'cy' => array(),
3274
+ 'direction' => array(),
3275
+ 'display' => array(),
3276
+ 'dominant-baseline' => array(),
3277
+ 'enable-background' => array(),
3278
+ 'externalresourcesrequired' => array(),
3279
+ 'fill' => array(),
3280
+ 'fill-opacity' => array(),
3281
+ 'fill-rule' => array(),
3282
+ 'filter' => array(),
3283
+ 'flood-color' => array(),
3284
+ 'flood-opacity' => array(),
3285
+ 'font-family' => array(),
3286
+ 'font-size' => array(),
3287
+ 'font-size-adjust' => array(),
3288
+ 'font-stretch' => array(),
3289
+ 'font-style' => array(),
3290
+ 'font-variant' => array(),
3291
+ 'font-weight' => array(),
3292
+ 'glyph-orientation-horizontal' => array(),
3293
+ 'glyph-orientation-vertical' => array(),
3294
+ 'image-rendering' => array(),
3295
+ 'kerning' => array(),
3296
+ 'letter-spacing' => array(),
3297
+ 'lighting-color' => array(),
3298
+ 'marker-end' => array(),
3299
+ 'marker-mid' => array(),
3300
+ 'marker-start' => array(),
3301
+ 'mask' => array(),
3302
+ 'opacity' => array(),
3303
+ 'overflow' => array(),
3304
+ 'pointer-events' => array(),
3305
+ 'requiredextensions' => array(),
3306
+ 'requiredfeatures' => array(),
3307
+ 'rx' => array(),
3308
+ 'ry' => array(),
3309
+ 'shape-rendering' => array(),
3310
+ 'sketch:type' => array(),
3311
+ 'stop-color' => array(),
3312
+ 'stop-opacity' => array(),
3313
+ 'stroke' => array(),
3314
+ 'stroke-dasharray' => array(),
3315
+ 'stroke-dashoffset' => array(),
3316
+ 'stroke-linecap' => array(),
3317
+ 'stroke-linejoin' => array(),
3318
+ 'stroke-miterlimit' => array(),
3319
+ 'stroke-opacity' => array(),
3320
+ 'stroke-width' => array(),
3321
+ 'style' => array(
3322
+ 'blacklisted_value_regex' => '!important',
3323
+ ),
3324
+ 'systemlanguage' => array(),
3325
+ 'text-anchor' => array(),
3326
+ 'text-decoration' => array(),
3327
+ 'text-rendering' => array(),
3328
+ 'transform' => array(),
3329
+ 'unicode-bidi' => array(),
3330
+ 'vector-effect' => array(),
3331
+ 'visibility' => array(),
3332
+ 'word-spacing' => array(),
3333
+ 'writing-mode' => array(),
3334
+ 'xml:lang' => array(),
3335
+ 'xml:space' => array(),
3336
+ 'xmlns' => 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(
3352
+ array(
3353
+ 'attr_spec_list' => array(
3354
+ 'alignment-baseline' => array(),
3355
+ 'baseline-shift' => array(),
3356
+ 'clip' => array(),
3357
+ 'clip-path' => array(),
3358
+ 'clip-rule' => array(),
3359
+ 'color' => array(),
3360
+ 'color-interpolation' => array(),
3361
+ 'color-interpolation-filters' => array(),
3362
+ 'color-profile' => array(),
3363
+ 'color-rendering' => array(),
3364
+ 'cursor' => array(),
3365
+ 'direction' => array(),
3366
+ 'display' => array(),
3367
+ 'dominant-baseline' => array(),
3368
+ 'enable-background' => array(),
3369
+ 'fill' => array(),
3370
+ 'fill-opacity' => array(),
3371
+ 'fill-rule' => array(),
3372
+ 'filter' => array(),
3373
+ 'flood-color' => array(),
3374
+ 'flood-opacity' => array(),
3375
+ 'font-family' => array(),
3376
+ 'font-size' => array(),
3377
+ 'font-size-adjust' => array(),
3378
+ 'font-stretch' => array(),
3379
+ 'font-style' => array(),
3380
+ 'font-variant' => array(),
3381
+ 'font-weight' => array(),
3382
+ 'glyph-orientation-horizontal' => array(),
3383
+ 'glyph-orientation-vertical' => array(),
3384
+ 'height' => array(),
3385
+ 'image-rendering' => array(),
3386
+ 'in' => array(),
3387
+ 'kerning' => array(),
3388
+ 'letter-spacing' => array(),
3389
+ 'lighting-color' => array(),
3390
+ 'marker-end' => array(),
3391
+ 'marker-mid' => array(),
3392
+ 'marker-start' => array(),
3393
+ 'mask' => array(),
3394
+ 'opacity' => array(),
3395
+ 'overflow' => array(),
3396
+ 'pointer-events' => array(),
3397
+ 'result' => array(),
3398
+ 'shape-rendering' => array(),
3399
+ 'stop-color' => array(),
3400
+ 'stop-opacity' => array(),
3401
+ 'stroke' => array(),
3402
+ 'stroke-dasharray' => array(),
3403
+ 'stroke-dashoffset' => array(),
3404
+ 'stroke-linecap' => array(),
3405
+ 'stroke-linejoin' => array(),
3406
+ 'stroke-miterlimit' => array(),
3407
+ 'stroke-opacity' => array(),
3408
+ 'stroke-width' => array(),
3409
+ 'style' => array(
3410
+ 'blacklisted_value_regex' => '!important',
3411
+ ),
3412
+ 'text-anchor' => array(),
3413
+ 'text-decoration' => array(),
3414
+ 'text-rendering' => array(),
3415
+ 'type' => array(),
3416
+ 'unicode-bidi' => array(),
3417
+ 'values' => array(),
3418
+ 'vector-effect' => array(),
3419
+ 'visibility' => array(),
3420
+ 'width' => array(),
3421
+ 'word-spacing' => array(),
3422
+ 'writing-mode' => array(),
3423
+ 'x' => array(),
3424
+ 'xml:lang' => array(),
3425
+ 'xml:space' => array(),
3426
+ 'xmlns' => array(),
3427
+ 'xmlns:xlink' => 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(
3437
+ array(
3438
+ 'attr_spec_list' => array(
3439
+ 'alignment-baseline' => array(),
3440
+ 'baseline-shift' => array(),
3441
+ 'clip' => array(),
3442
+ 'clip-path' => array(),
3443
+ 'clip-rule' => array(),
3444
+ 'color' => array(),
3445
+ 'color-interpolation' => array(),
3446
+ 'color-interpolation-filters' => array(),
3447
+ 'color-profile' => array(),
3448
+ 'color-rendering' => array(),
3449
+ 'cursor' => array(),
3450
+ 'direction' => array(),
3451
+ 'display' => array(),
3452
+ 'dominant-baseline' => array(),
3453
+ 'enable-background' => array(),
3454
+ 'fill' => array(),
3455
+ 'fill-opacity' => array(),
3456
+ 'fill-rule' => array(),
3457
+ 'filter' => array(),
3458
+ 'flood-color' => array(),
3459
+ 'flood-opacity' => array(),
3460
+ 'font-family' => array(),
3461
+ 'font-size' => array(),
3462
+ 'font-size-adjust' => array(),
3463
+ 'font-stretch' => array(),
3464
+ 'font-style' => array(),
3465
+ 'font-variant' => array(),
3466
+ 'font-weight' => array(),
3467
+ 'glyph-orientation-horizontal' => array(),
3468
+ 'glyph-orientation-vertical' => array(),
3469
+ 'height' => array(),
3470
+ 'image-rendering' => array(),
3471
+ 'in' => array(),
3472
+ 'in2' => array(),
3473
+ 'k1' => array(),
3474
+ 'k2' => array(),
3475
+ 'k3' => array(),
3476
+ 'k4' => array(),
3477
+ 'kerning' => array(),
3478
+ 'letter-spacing' => array(),
3479
+ 'lighting-color' => array(),
3480
+ 'marker-end' => array(),
3481
+ 'marker-mid' => array(),
3482
+ 'marker-start' => array(),
3483
+ 'mask' => array(),
3484
+ 'opacity' => array(),
3485
+ 'operator' => array(),
3486
+ 'overflow' => array(),
3487
+ 'pointer-events' => array(),
3488
+ 'result' => array(),
3489
+ 'shape-rendering' => array(),
3490
+ 'stop-color' => array(),
3491
+ 'stop-opacity' => array(),
3492
+ 'stroke' => array(),
3493
+ 'stroke-dasharray' => array(),
3494
+ 'stroke-dashoffset' => array(),
3495
+ 'stroke-linecap' => array(),
3496
+ 'stroke-linejoin' => array(),
3497
+ 'stroke-miterlimit' => array(),
3498
+ 'stroke-opacity' => array(),
3499
+ 'stroke-width' => array(),
3500
+ 'style' => array(
3501
+ 'blacklisted_value_regex' => '!important',
3502
+ ),
3503
+ 'text-anchor' => array(),
3504
+ 'text-decoration' => array(),
3505
+ 'text-rendering' => array(),
3506
+ 'unicode-bidi' => array(),
3507
+ 'vector-effect' => array(),
3508
+ 'visibility' => array(),
3509
+ 'width' => array(),
3510
+ 'word-spacing' => array(),
3511
+ 'writing-mode' => array(),
3512
+ 'x' => array(),
3513
+ 'xml:lang' => array(),
3514
+ 'xml:space' => array(),
3515
+ 'xmlns' => array(),
3516
+ 'xmlns:xlink' => 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(
3526
+ array(
3527
+ 'attr_spec_list' => array(
3528
+ 'alignment-baseline' => array(),
3529
+ 'baseline-shift' => array(),
3530
+ 'clip' => array(),
3531
+ 'clip-path' => array(),
3532
+ 'clip-rule' => array(),
3533
+ 'color' => array(),
3534
+ 'color-interpolation' => array(),
3535
+ 'color-interpolation-filters' => array(),
3536
+ 'color-profile' => array(),
3537
+ 'color-rendering' => array(),
3538
+ 'cursor' => array(),
3539
+ 'direction' => array(),
3540
+ 'display' => array(),
3541
+ 'dominant-baseline' => array(),
3542
+ 'enable-background' => array(),
3543
+ 'fill' => array(),
3544
+ 'fill-opacity' => array(),
3545
+ 'fill-rule' => array(),
3546
+ 'filter' => array(),
3547
+ 'flood-color' => array(),
3548
+ 'flood-opacity' => array(),
3549
+ 'font-family' => array(),
3550
+ 'font-size' => array(),
3551
+ 'font-size-adjust' => array(),
3552
+ 'font-stretch' => array(),
3553
+ 'font-style' => array(),
3554
+ 'font-variant' => array(),
3555
+ 'font-weight' => array(),
3556
+ 'glyph-orientation-horizontal' => array(),
3557
+ 'glyph-orientation-vertical' => array(),
3558
+ 'height' => array(),
3559
+ 'image-rendering' => array(),
3560
+ 'kerning' => array(),
3561
+ 'letter-spacing' => array(),
3562
+ 'lighting-color' => array(),
3563
+ 'marker-end' => array(),
3564
+ 'marker-mid' => array(),
3565
+ 'marker-start' => array(),
3566
+ 'mask' => array(),
3567
+ 'opacity' => array(),
3568
+ 'overflow' => array(),
3569
+ 'pointer-events' => array(),
3570
+ 'result' => array(),
3571
+ 'shape-rendering' => array(),
3572
+ 'stop-color' => array(),
3573
+ 'stop-opacity' => array(),
3574
+ 'stroke' => array(),
3575
+ 'stroke-dasharray' => array(),
3576
+ 'stroke-dashoffset' => array(),
3577
+ 'stroke-linecap' => array(),
3578
+ 'stroke-linejoin' => array(),
3579
+ 'stroke-miterlimit' => array(),
3580
+ 'stroke-opacity' => array(),
3581
+ 'stroke-width' => array(),
3582
+ 'style' => array(
3583
+ 'blacklisted_value_regex' => '!important',
3584
+ ),
3585
+ 'text-anchor' => array(),
3586
+ 'text-decoration' => array(),
3587
+ 'text-rendering' => array(),
3588
+ 'unicode-bidi' => array(),
3589
+ 'vector-effect' => array(),
3590
+ 'visibility' => array(),
3591
+ 'width' => array(),
3592
+ 'word-spacing' => array(),
3593
+ 'writing-mode' => array(),
3594
+ 'x' => array(),
3595
+ 'xml:lang' => array(),
3596
+ 'xml:space' => array(),
3597
+ 'xmlns' => array(),
3598
+ 'xmlns:xlink' => 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(
3608
+ array(
3609
+ 'attr_spec_list' => array(
3610
+ 'alignment-baseline' => array(),
3611
+ 'baseline-shift' => array(),
3612
+ 'clip' => array(),
3613
+ 'clip-path' => array(),
3614
+ 'clip-rule' => array(),
3615
+ 'color' => array(),
3616
+ 'color-interpolation' => array(),
3617
+ 'color-interpolation-filters' => array(),
3618
+ 'color-profile' => array(),
3619
+ 'color-rendering' => array(),
3620
+ 'cursor' => array(),
3621
+ 'direction' => array(),
3622
+ 'display' => array(),
3623
+ 'dominant-baseline' => array(),
3624
+ 'edgemode' => array(),
3625
+ 'enable-background' => array(),
3626
+ 'fill' => array(),
3627
+ 'fill-opacity' => array(),
3628
+ 'fill-rule' => array(),
3629
+ 'filter' => array(),
3630
+ 'flood-color' => array(),
3631
+ 'flood-opacity' => array(),
3632
+ 'font-family' => array(),
3633
+ 'font-size' => array(),
3634
+ 'font-size-adjust' => array(),
3635
+ 'font-stretch' => array(),
3636
+ 'font-style' => array(),
3637
+ 'font-variant' => array(),
3638
+ 'font-weight' => array(),
3639
+ 'glyph-orientation-horizontal' => array(),
3640
+ 'glyph-orientation-vertical' => array(),
3641
+ 'height' => array(),
3642
+ 'image-rendering' => array(),
3643
+ 'in' => array(),
3644
+ 'kerning' => array(),
3645
+ 'letter-spacing' => array(),
3646
+ 'lighting-color' => array(),
3647
+ 'marker-end' => array(),
3648
+ 'marker-mid' => array(),
3649
+ 'marker-start' => array(),
3650
+ 'mask' => array(),
3651
+ 'opacity' => array(),
3652
+ 'overflow' => array(),
3653
+ 'pointer-events' => array(),
3654
+ 'result' => array(),
3655
+ 'shape-rendering' => array(),
3656
+ 'stddeviation' => array(),
3657
+ 'stop-color' => array(),
3658
+ 'stop-opacity' => array(),
3659
+ 'stroke' => array(),
3660
+ 'stroke-dasharray' => array(),
3661
+ 'stroke-dashoffset' => array(),
3662
+ 'stroke-linecap' => array(),
3663
+ 'stroke-linejoin' => array(),
3664
+ 'stroke-miterlimit' => array(),
3665
+ 'stroke-opacity' => array(),
3666
+ 'stroke-width' => array(),
3667
+ 'style' => array(
3668
+ 'blacklisted_value_regex' => '!important',
3669
+ ),
3670
+ 'text-anchor' => array(),
3671
+ 'text-decoration' => array(),
3672
+ 'text-rendering' => array(),
3673
+ 'unicode-bidi' => array(),
3674
+ 'vector-effect' => array(),
3675
+ 'visibility' => array(),
3676
+ 'width' => array(),
3677
+ 'word-spacing' => array(),
3678
+ 'writing-mode' => array(),
3679
+ 'x' => array(),
3680
+ 'xml:lang' => array(),
3681
+ 'xml:space' => array(),
3682
+ 'xmlns' => array(),
3683
+ 'xmlns:xlink' => 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(
3693
+ array(
3694
+ 'attr_spec_list' => array(
3695
+ 'alignment-baseline' => array(),
3696
+ 'baseline-shift' => array(),
3697
+ 'clip' => array(),
3698
+ 'clip-path' => array(),
3699
+ 'clip-rule' => array(),
3700
+ 'color' => array(),
3701
+ 'color-interpolation' => array(),
3702
+ 'color-interpolation-filters' => array(),
3703
+ 'color-profile' => array(),
3704
+ 'color-rendering' => array(),
3705
+ 'cursor' => array(),
3706
+ 'direction' => array(),
3707
+ 'display' => array(),
3708
+ 'dominant-baseline' => array(),
3709
+ 'enable-background' => array(),
3710
+ 'fill' => array(),
3711
+ 'fill-opacity' => array(),
3712
+ 'fill-rule' => array(),
3713
+ 'filter' => array(),
3714
+ 'flood-color' => array(),
3715
+ 'flood-opacity' => array(),
3716
+ 'font-family' => array(),
3717
+ 'font-size' => array(),
3718
+ 'font-size-adjust' => array(),
3719
+ 'font-stretch' => array(),
3720
+ 'font-style' => array(),
3721
+ 'font-variant' => array(),
3722
+ 'font-weight' => array(),
3723
+ 'glyph-orientation-horizontal' => array(),
3724
+ 'glyph-orientation-vertical' => array(),
3725
+ 'height' => array(),
3726
+ 'image-rendering' => array(),
3727
+ 'kerning' => array(),
3728
+ 'letter-spacing' => array(),
3729
+ 'lighting-color' => array(),
3730
+ 'marker-end' => array(),
3731
+ 'marker-mid' => array(),
3732
+ 'marker-start' => array(),
3733
+ 'mask' => array(),
3734
+ 'opacity' => array(),
3735
+ 'overflow' => array(),
3736
+ 'pointer-events' => array(),
3737
+ 'result' => array(),
3738
+ 'shape-rendering' => array(),
3739
+ 'stop-color' => array(),
3740
+ 'stop-opacity' => array(),
3741
+ 'stroke' => array(),
3742
+ 'stroke-dasharray' => array(),
3743
+ 'stroke-dashoffset' => array(),
3744
+ 'stroke-linecap' => array(),
3745
+ 'stroke-linejoin' => array(),
3746
+ 'stroke-miterlimit' => array(),
3747
+ 'stroke-opacity' => array(),
3748
+ 'stroke-width' => array(),
3749
+ 'style' => array(
3750
+ 'blacklisted_value_regex' => '!important',
3751
+ ),
3752
+ 'text-anchor' => array(),
3753
+ 'text-decoration' => array(),
3754
+ 'text-rendering' => array(),
3755
+ 'unicode-bidi' => array(),
3756
+ 'vector-effect' => array(),
3757
+ 'visibility' => array(),
3758
+ 'width' => array(),
3759
+ 'word-spacing' => array(),
3760
+ 'writing-mode' => array(),
3761
+ 'x' => array(),
3762
+ 'xml:lang' => array(),
3763
+ 'xml:space' => array(),
3764
+ 'xmlns' => array(),
3765
+ 'xmlns:xlink' => 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(
3775
+ array(
3776
+ 'attr_spec_list' => array(
3777
+ 'in' => array(),
3778
+ 'style' => array(
3779
+ 'blacklisted_value_regex' => '!important',
3780
+ ),
3781
+ 'xml:lang' => array(),
3782
+ 'xml:space' => array(),
3783
+ 'xmlns' => 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(
3793
+ array(
3794
+ 'attr_spec_list' => array(
3795
+ 'alignment-baseline' => array(),
3796
+ 'baseline-shift' => array(),
3797
+ 'clip' => array(),
3798
+ 'clip-path' => array(),
3799
+ 'clip-rule' => array(),
3800
+ 'color' => array(),
3801
+ 'color-interpolation' => array(),
3802
+ 'color-interpolation-filters' => array(),
3803
+ 'color-profile' => array(),
3804
+ 'color-rendering' => array(),
3805
+ 'cursor' => array(),
3806
+ 'direction' => array(),
3807
+ 'display' => array(),
3808
+ 'dominant-baseline' => array(),
3809
+ 'dx' => array(),
3810
+ 'dy' => array(),
3811
+ 'enable-background' => array(),
3812
+ 'fill' => array(),
3813
+ 'fill-opacity' => array(),
3814
+ 'fill-rule' => array(),
3815
+ 'filter' => array(),
3816
+ 'flood-color' => array(),
3817
+ 'flood-opacity' => array(),
3818
+ 'font-family' => array(),
3819
+ 'font-size' => array(),
3820
+ 'font-size-adjust' => array(),
3821
+ 'font-stretch' => array(),
3822
+ 'font-style' => array(),
3823
+ 'font-variant' => array(),
3824
+ 'font-weight' => array(),
3825
+ 'glyph-orientation-horizontal' => array(),
3826
+ 'glyph-orientation-vertical' => array(),
3827
+ 'height' => array(),
3828
+ 'image-rendering' => array(),
3829
+ 'in' => array(),
3830
+ 'kerning' => array(),
3831
+ 'letter-spacing' => array(),
3832
+ 'lighting-color' => array(),
3833
+ 'marker-end' => array(),
3834
+ 'marker-mid' => array(),
3835
+ 'marker-start' => array(),
3836
+ 'mask' => array(),
3837
+ 'opacity' => array(),
3838
+ 'overflow' => array(),
3839
+ 'pointer-events' => array(),
3840
+ 'result' => array(),
3841
+ 'shape-rendering' => array(),
3842
+ 'stop-color' => array(),
3843
+ 'stop-opacity' => array(),
3844
+ 'stroke' => array(),
3845
+ 'stroke-dasharray' => array(),
3846
+ 'stroke-dashoffset' => array(),
3847
+ 'stroke-linecap' => array(),
3848
+ 'stroke-linejoin' => array(),
3849
+ 'stroke-miterlimit' => array(),
3850
+ 'stroke-opacity' => array(),
3851
+ 'stroke-width' => array(),
3852
+ 'style' => array(
3853
+ 'blacklisted_value_regex' => '!important',
3854
+ ),
3855
+ 'text-anchor' => array(),
3856
+ 'text-decoration' => array(),
3857
+ 'text-rendering' => array(),
3858
+ 'unicode-bidi' => array(),
3859
+ 'vector-effect' => array(),
3860
+ 'visibility' => array(),
3861
+ 'width' => array(),
3862
+ 'word-spacing' => array(),
3863
+ 'writing-mode' => array(),
3864
+ 'x' => array(),
3865
+ 'xml:lang' => array(),
3866
+ 'xml:space' => array(),
3867
+ 'xmlns' => array(),
3868
+ 'xmlns:xlink' => 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(
3878
+ array(
3879
+ 'attr_spec_list' => 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(
3902
+ array(
3903
+ 'attr_spec_list' => array(
3904
+ 'alignment-baseline' => array(),
3905
+ 'baseline-shift' => array(),
3906
+ 'clip' => array(),
3907
+ 'clip-path' => array(),
3908
+ 'clip-rule' => array(),
3909
+ 'color' => array(),
3910
+ 'color-interpolation' => array(),
3911
+ 'color-interpolation-filters' => array(),
3912
+ 'color-profile' => array(),
3913
+ 'color-rendering' => array(),
3914
+ 'cursor' => array(),
3915
+ 'direction' => array(),
3916
+ 'display' => array(),
3917
+ 'dominant-baseline' => array(),
3918
+ 'enable-background' => array(),
3919
+ 'externalresourcesrequired' => array(),
3920
+ 'fill' => array(),
3921
+ 'fill-opacity' => array(),
3922
+ 'fill-rule' => array(),
3923
+ 'filter' => array(),
3924
+ 'filterres' => array(),
3925
+ 'filterunits' => array(),
3926
+ 'flood-color' => array(),
3927
+ 'flood-opacity' => array(),
3928
+ 'font-family' => array(),
3929
+ 'font-size' => array(),
3930
+ 'font-size-adjust' => array(),
3931
+ 'font-stretch' => array(),
3932
+ 'font-style' => array(),
3933
+ 'font-variant' => array(),
3934
+ 'font-weight' => array(),
3935
+ 'glyph-orientation-horizontal' => array(),
3936
+ 'glyph-orientation-vertical' => array(),
3937
+ 'height' => array(),
3938
+ 'image-rendering' => array(),
3939
+ 'kerning' => array(),
3940
+ 'letter-spacing' => array(),
3941
+ 'lighting-color' => array(),
3942
+ 'marker-end' => array(),
3943
+ 'marker-mid' => array(),
3944
+ 'marker-start' => array(),
3945
+ 'mask' => array(),
3946
+ 'opacity' => array(),
3947
+ 'overflow' => array(),
3948
+ 'pointer-events' => array(),
3949
+ 'primitiveunits' => array(),
3950
+ 'shape-rendering' => array(),
3951
+ 'stop-color' => array(),
3952
+ 'stop-opacity' => array(),
3953
+ 'stroke' => array(),
3954
+ 'stroke-dasharray' => array(),
3955
+ 'stroke-dashoffset' => array(),
3956
+ 'stroke-linecap' => array(),
3957
+ 'stroke-linejoin' => array(),
3958
+ 'stroke-miterlimit' => array(),
3959
+ 'stroke-opacity' => array(),
3960
+ 'stroke-width' => array(),
3961
+ 'style' => array(
3962
+ 'blacklisted_value_regex' => '!important',
3963
+ ),
3964
+ 'text-anchor' => array(),
3965
+ 'text-decoration' => array(),
3966
+ 'text-rendering' => array(),
3967
+ 'unicode-bidi' => array(),
3968
+ 'vector-effect' => array(),
3969
+ 'visibility' => array(),
3970
+ 'width' => array(),
3971
+ 'word-spacing' => array(),
3972
+ 'writing-mode' => array(),
3973
+ 'x' => array(),
3974
+ 'xlink:actuate' => array(),
3975
+ 'xlink:arcrole' => array(),
3976
+ 'xlink:href' => 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(),
3990
+ 'xlink:show' => array(),
3991
+ 'xlink:title' => array(),
3992
+ 'xlink:type' => array(),
3993
+ 'xml:lang' => array(),
3994
+ 'xml:space' => array(),
3995
+ 'xmlns' => array(),
3996
+ 'xmlns:xlink' => 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(
4012
+ array(
4013
+ 'attr_spec_list' => array(
4014
+ 'alignment-baseline' => array(),
4015
+ 'baseline-shift' => array(),
4016
+ 'clip' => array(),
4017
+ 'clip-path' => array(),
4018
+ 'clip-rule' => array(),
4019
+ 'color' => array(),
4020
+ 'color-interpolation' => array(),
4021
+ 'color-interpolation-filters' => array(),
4022
+ 'color-profile' => array(),
4023
+ 'color-rendering' => array(),
4024
+ 'cursor' => array(),
4025
+ 'direction' => array(),
4026
+ 'display' => array(),
4027
+ 'dominant-baseline' => array(),
4028
+ 'enable-background' => array(),
4029
+ 'externalresourcesrequired' => array(),
4030
+ 'fill' => array(),
4031
+ 'fill-opacity' => array(),
4032
+ 'fill-rule' => array(),
4033
+ 'filter' => array(),
4034
+ 'flood-color' => array(),
4035
+ 'flood-opacity' => array(),
4036
+ 'font-family' => array(),
4037
+ 'font-size' => array(),
4038
+ 'font-size-adjust' => array(),
4039
+ 'font-stretch' => array(),
4040
+ 'font-style' => array(),
4041
+ 'font-variant' => array(),
4042
+ 'font-weight' => array(),
4043
+ 'glyph-orientation-horizontal' => array(),
4044
+ 'glyph-orientation-vertical' => array(),
4045
+ 'height' => array(),
4046
+ 'image-rendering' => array(),
4047
+ 'kerning' => array(),
4048
+ 'letter-spacing' => array(),
4049
+ 'lighting-color' => array(),
4050
+ 'marker-end' => array(),
4051
+ 'marker-mid' => array(),
4052
+ 'marker-start' => array(),
4053
+ 'mask' => array(),
4054
+ 'opacity' => array(),
4055
+ 'overflow' => array(),
4056
+ 'pointer-events' => array(),
4057
+ 'requiredextensions' => array(),
4058
+ 'requiredfeatures' => array(),
4059
+ 'shape-rendering' => array(),
4060
+ 'stop-color' => array(),
4061
+ 'stop-opacity' => array(),
4062
+ 'stroke' => array(),
4063
+ 'stroke-dasharray' => array(),
4064
+ 'stroke-dashoffset' => array(),
4065
+ 'stroke-linecap' => array(),
4066
+ 'stroke-linejoin' => array(),
4067
+ 'stroke-miterlimit' => array(),
4068
+ 'stroke-opacity' => array(),
4069
+ 'stroke-width' => array(),
4070
+ 'style' => array(
4071
+ 'blacklisted_value_regex' => '!important',
4072
+ ),
4073
+ 'systemlanguage' => array(),
4074
+ 'text-anchor' => array(),
4075
+ 'text-decoration' => array(),
4076
+ 'text-rendering' => array(),
4077
+ 'transform' => array(),
4078
+ 'unicode-bidi' => array(),
4079
+ 'vector-effect' => array(),
4080
+ 'visibility' => array(),
4081
+ 'width' => array(),
4082
+ 'word-spacing' => array(),
4083
+ 'writing-mode' => array(),
4084
+ 'x' => array(),
4085
+ 'xml:lang' => array(),
4086
+ 'xml:space' => array(),
4087
+ 'xmlns' => array(),
4088
+ 'xmlns:xlink' => 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(
4098
+ array(
4099
+ 'attr_spec_list' => array(
4100
+ 'accept' => array(),
4101
+ 'accept-charset' => 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(),
4122
+ 'custom-validation-reporting' => array(
4123
+ 'value_regex' => '(show-first-on-submit|show-all-on-submit|as-you-go|interact-and-submit)',
4124
+ ),
4125
+ 'enctype' => array(),
4126
+ 'method' => array(
4127
+ 'value_casei' => 'get',
4128
+ ),
4129
+ 'name' => array(),
4130
+ 'novalidate' => array(),
4131
+ 'target' => array(
4132
+ 'mandatory' => true,
4133
+ 'value_regex_casei' => '(_blank|_top)',
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(
4157
+ 'accept' => array(),
4158
+ 'accept-charset' => 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(),
4170
+ 'custom-validation-reporting' => array(
4171
+ 'value_regex' => '(show-first-on-submit|show-all-on-submit|as-you-go)',
4172
+ ),
4173
+ 'enctype' => array(),
4174
+ 'method' => array(
4175
+ 'dispatch_key' => 2,
4176
+ 'mandatory' => true,
4177
+ 'value_casei' => 'post',
4178
+ ),
4179
+ 'name' => array(),
4180
+ 'novalidate' => array(),
4181
+ 'target' => array(
4182
+ 'mandatory' => true,
4183
+ 'value_regex_casei' => '(_blank|_top)',
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(
4207
+ array(
4208
+ 'attr_spec_list' => array(
4209
+ 'alignment-baseline' => array(),
4210
+ 'baseline-shift' => array(),
4211
+ 'clip' => array(),
4212
+ 'clip-path' => array(),
4213
+ 'clip-rule' => array(),
4214
+ 'color' => array(),
4215
+ 'color-interpolation' => array(),
4216
+ 'color-interpolation-filters' => array(),
4217
+ 'color-profile' => array(),
4218
+ 'color-rendering' => array(),
4219
+ 'cursor' => array(),
4220
+ 'direction' => array(),
4221
+ 'display' => array(),
4222
+ 'dominant-baseline' => array(),
4223
+ 'enable-background' => array(),
4224
+ 'externalresourcesrequired' => array(),
4225
+ 'fill' => array(),
4226
+ 'fill-opacity' => array(),
4227
+ 'fill-rule' => array(),
4228
+ 'filter' => array(),
4229
+ 'flood-color' => array(),
4230
+ 'flood-opacity' => array(),
4231
+ 'font-family' => array(),
4232
+ 'font-size' => array(),
4233
+ 'font-size-adjust' => array(),
4234
+ 'font-stretch' => array(),
4235
+ 'font-style' => array(),
4236
+ 'font-variant' => array(),
4237
+ 'font-weight' => array(),
4238
+ 'glyph-orientation-horizontal' => array(),
4239
+ 'glyph-orientation-vertical' => array(),
4240
+ 'image-rendering' => array(),
4241
+ 'kerning' => array(),
4242
+ 'letter-spacing' => array(),
4243
+ 'lighting-color' => array(),
4244
+ 'marker-end' => array(),
4245
+ 'marker-mid' => array(),
4246
+ 'marker-start' => array(),
4247
+ 'mask' => array(),
4248
+ 'opacity' => array(),
4249
+ 'overflow' => array(),
4250
+ 'pointer-events' => array(),
4251
+ 'requiredextensions' => array(),
4252
+ 'requiredfeatures' => array(),
4253
+ 'shape-rendering' => array(),
4254
+ 'stop-color' => array(),
4255
+ 'stop-opacity' => array(),
4256
+ 'stroke' => array(),
4257
+ 'stroke-dasharray' => array(),
4258
+ 'stroke-dashoffset' => array(),
4259
+ 'stroke-linecap' => array(),
4260
+ 'stroke-linejoin' => array(),
4261
+ 'stroke-miterlimit' => array(),
4262
+ 'stroke-opacity' => array(),
4263
+ 'stroke-width' => array(),
4264
+ 'style' => array(
4265
+ 'blacklisted_value_regex' => '!important',
4266
+ ),
4267
+ 'systemlanguage' => array(),
4268
+ 'text-anchor' => array(),
4269
+ 'text-decoration' => array(),
4270
+ 'text-rendering' => array(),
4271
+ 'transform' => array(),
4272
+ 'unicode-bidi' => array(),
4273
+ 'vector-effect' => array(),
4274
+ 'visibility' => array(),
4275
+ 'word-spacing' => array(),
4276
+ 'writing-mode' => array(),
4277
+ 'xml:lang' => array(),
4278
+ 'xml:space' => array(),
4279
+ 'xmlns' => 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(
4289
+ array(
4290
+ 'attr_spec_list' => array(
4291
+ 'alignment-baseline' => array(),
4292
+ 'arabic-form' => array(),
4293
+ 'baseline-shift' => array(),
4294
+ 'clip' => array(),
4295
+ 'clip-path' => array(),
4296
+ 'clip-rule' => array(),
4297
+ 'color' => array(),
4298
+ 'color-interpolation' => array(),
4299
+ 'color-interpolation-filters' => array(),
4300
+ 'color-profile' => array(),
4301
+ 'color-rendering' => array(),
4302
+ 'cursor' => array(),
4303
+ 'd' => array(),
4304
+ 'direction' => array(),
4305
+ 'display' => array(),
4306
+ 'dominant-baseline' => array(),
4307
+ 'enable-background' => array(),
4308
+ 'fill' => array(),
4309
+ 'fill-opacity' => array(),
4310
+ 'fill-rule' => array(),
4311
+ 'filter' => array(),
4312
+ 'flood-color' => array(),
4313
+ 'flood-opacity' => array(),
4314
+ 'font-family' => array(),
4315
+ 'font-size' => array(),
4316
+ 'font-size-adjust' => array(),
4317
+ 'font-stretch' => array(),
4318
+ 'font-style' => array(),
4319
+ 'font-variant' => array(),
4320
+ 'font-weight' => array(),
4321
+ 'glyph-name' => array(),
4322
+ 'glyph-orientation-horizontal' => array(),
4323
+ 'glyph-orientation-vertical' => array(),
4324
+ 'horiz-adv-x' => array(),
4325
+ 'image-rendering' => array(),
4326
+ 'kerning' => array(),
4327
+ 'letter-spacing' => array(),
4328
+ 'lighting-color' => array(),
4329
+ 'marker-end' => array(),
4330
+ 'marker-mid' => array(),
4331
+ 'marker-start' => array(),
4332
+ 'mask' => array(),
4333
+ 'opacity' => array(),
4334
+ 'orientation' => array(),
4335
+ 'overflow' => array(),
4336
+ 'pointer-events' => array(),
4337
+ 'shape-rendering' => array(),
4338
+ 'stop-color' => array(),
4339
+ 'stop-opacity' => array(),
4340
+ 'stroke' => array(),
4341
+ 'stroke-dasharray' => array(),
4342
+ 'stroke-dashoffset' => array(),
4343
+ 'stroke-linecap' => array(),
4344
+ 'stroke-linejoin' => array(),
4345
+ 'stroke-miterlimit' => array(),
4346
+ 'stroke-opacity' => array(),
4347
+ 'stroke-width' => array(),
4348
+ 'style' => array(
4349
+ 'blacklisted_value_regex' => '!important',
4350
+ ),
4351
+ 'text-anchor' => array(),
4352
+ 'text-decoration' => array(),
4353
+ 'text-rendering' => array(),
4354
+ 'unicode' => array(),
4355
+ 'unicode-bidi' => array(),
4356
+ 'vector-effect' => array(),
4357
+ 'vert-adv-y' => array(),
4358
+ 'vert-origin-x' => array(),
4359
+ 'vert-origin-y' => array(),
4360
+ 'visibility' => array(),
4361
+ 'word-spacing' => array(),
4362
+ 'writing-mode' => array(),
4363
+ 'xml:lang' => array(),
4364
+ 'xml:space' => array(),
4365
+ 'xmlns' => 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(
4375
+ array(
4376
+ 'attr_spec_list' => array(
4377
+ 'alignment-baseline' => array(),
4378
+ 'baseline-shift' => array(),
4379
+ 'clip' => array(),
4380
+ 'clip-path' => array(),
4381
+ 'clip-rule' => array(),
4382
+ 'color' => array(),
4383
+ 'color-interpolation' => array(),
4384
+ 'color-interpolation-filters' => array(),
4385
+ 'color-profile' => array(),
4386
+ 'color-rendering' => array(),
4387
+ 'cursor' => array(),
4388
+ 'direction' => array(),
4389
+ 'display' => array(),
4390
+ 'dominant-baseline' => array(),
4391
+ 'dx' => array(),
4392
+ 'dy' => array(),
4393
+ 'enable-background' => array(),
4394
+ 'fill' => array(),
4395
+ 'fill-opacity' => array(),
4396
+ 'fill-rule' => array(),
4397
+ 'filter' => array(),
4398
+ 'flood-color' => array(),
4399
+ 'flood-opacity' => array(),
4400
+ 'font-family' => array(),
4401
+ 'font-size' => array(),
4402
+ 'font-size-adjust' => array(),
4403
+ 'font-stretch' => array(),
4404
+ 'font-style' => array(),
4405
+ 'font-variant' => array(),
4406
+ 'font-weight' => array(),
4407
+ 'format' => array(),
4408
+ 'glyph-orientation-horizontal' => array(),
4409
+ 'glyph-orientation-vertical' => array(),
4410
+ 'glyphref' => array(),
4411
+ 'image-rendering' => array(),
4412
+ 'kerning' => array(),
4413
+ 'letter-spacing' => array(),
4414
+ 'lighting-color' => array(),
4415
+ 'marker-end' => array(),
4416
+ 'marker-mid' => array(),
4417
+ 'marker-start' => array(),
4418
+ 'mask' => array(),
4419
+ 'opacity' => array(),
4420
+ 'overflow' => array(),
4421
+ 'pointer-events' => array(),
4422
+ 'shape-rendering' => array(),
4423
+ 'stop-color' => array(),
4424
+ 'stop-opacity' => array(),
4425
+ 'stroke' => array(),
4426
+ 'stroke-dasharray' => array(),
4427
+ 'stroke-dashoffset' => array(),
4428
+ 'stroke-linecap' => array(),
4429
+ 'stroke-linejoin' => array(),
4430
+ 'stroke-miterlimit' => array(),
4431
+ 'stroke-opacity' => array(),
4432
+ 'stroke-width' => array(),
4433
+ 'style' => array(
4434
+ 'blacklisted_value_regex' => '!important',
4435
+ ),
4436
+ 'text-anchor' => array(),
4437
+ 'text-decoration' => array(),
4438
+ 'text-rendering' => array(),
4439
+ 'unicode-bidi' => array(),
4440
+ 'vector-effect' => array(),
4441
+ 'visibility' => array(),
4442
+ 'word-spacing' => array(),
4443
+ 'writing-mode' => array(),
4444
+ 'x' => array(),
4445
+ 'xlink:actuate' => array(),
4446
+ 'xlink:arcrole' => array(),
4447
+ 'xlink:href' => 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(),
4461
+ 'xlink:show' => array(),
4462
+ 'xlink:title' => array(),
4463
+ 'xlink:type' => array(),
4464
+ 'xml:lang' => array(),
4465
+ 'xml:space' => array(),
4466
+ 'xmlns' => array(),
4467
+ 'xmlns:xlink' => 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(
4477
+ array(
4478
+ 'attr_spec_list' => array(
4479
+ 'align' => array(),
4480
+ ),
4481
+ 'tag_spec' => array(),
4482
+ ),
4483
+ ),
4484
+ 'h2' => array(
4485
+ array(
4486
+ 'attr_spec_list' => array(
4487
+ 'align' => array(),
4488
+ ),
4489
+ 'tag_spec' => array(),
4490
+ ),
4491
+ ),
4492
+ 'h3' => array(
4493
+ array(
4494
+ 'attr_spec_list' => array(
4495
+ 'align' => array(),
4496
+ ),
4497
+ 'tag_spec' => array(),
4498
+ ),
4499
+ ),
4500
+ 'h4' => array(
4501
+ array(
4502
+ 'attr_spec_list' => array(
4503
+ 'align' => array(),
4504
+ ),
4505
+ 'tag_spec' => array(),
4506
+ ),
4507
+ ),
4508
+ 'h5' => array(
4509
+ array(
4510
+ 'attr_spec_list' => array(
4511
+ 'align' => array(),
4512
+ ),
4513
+ 'tag_spec' => array(),
4514
+ ),
4515
+ ),
4516
+ 'h6' => array(
4517
+ array(
4518
+ 'attr_spec_list' => 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(
4548
+ array(
4549
+ 'attr_spec_list' => array(
4550
+ 'g1' => array(),
4551
+ 'g2' => array(),
4552
+ 'k' => array(),
4553
+ 'style' => array(
4554
+ 'blacklisted_value_regex' => '!important',
4555
+ ),
4556
+ 'u1' => array(),
4557
+ 'u2' => array(),
4558
+ 'xml:lang' => array(),
4559
+ 'xml:space' => array(),
4560
+ 'xmlns' => 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(
4602
+ array(
4603
+ 'attr_spec_list' => array(
4604
+ 'frameborder' => array(
4605
+ 'value_regex' => '0|1',
4606
+ ),
4607
+ 'height' => array(),
4608
+ 'name' => array(),
4609
+ 'referrerpolicy' => array(),
4610
+ 'resizable' => array(
4611
+ 'value' => '',
4612
+ ),
4613
+ 'sandbox' => array(),
4614
+ 'scrolling' => array(
4615
+ 'value_regex' => 'auto|yes|no',
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(
4638
+ array(
4639
+ 'attr_spec_list' => array(
4640
+ 'alignment-baseline' => array(),
4641
+ 'baseline-shift' => array(),
4642
+ 'clip' => array(),
4643
+ 'clip-path' => array(),
4644
+ 'clip-rule' => array(),
4645
+ 'color' => array(),
4646
+ 'color-interpolation' => array(),
4647
+ 'color-interpolation-filters' => array(),
4648
+ 'color-profile' => array(),
4649
+ 'color-rendering' => array(),
4650
+ 'cursor' => array(),
4651
+ 'direction' => array(),
4652
+ 'display' => array(),
4653
+ 'dominant-baseline' => array(),
4654
+ 'enable-background' => array(),
4655
+ 'externalresourcesrequired' => array(),
4656
+ 'fill' => array(),
4657
+ 'fill-opacity' => array(),
4658
+ 'fill-rule' => array(),
4659
+ 'filter' => array(),
4660
+ 'flood-color' => array(),
4661
+ 'flood-opacity' => array(),
4662
+ 'font-family' => array(),
4663
+ 'font-size' => array(),
4664
+ 'font-size-adjust' => array(),
4665
+ 'font-stretch' => array(),
4666
+ 'font-style' => array(),
4667
+ 'font-variant' => array(),
4668
+ 'font-weight' => array(),
4669
+ 'glyph-orientation-horizontal' => array(),
4670
+ 'glyph-orientation-vertical' => array(),
4671
+ 'height' => array(),
4672
+ 'image-rendering' => array(),
4673
+ 'kerning' => array(),
4674
+ 'letter-spacing' => array(),
4675
+ 'lighting-color' => array(),
4676
+ 'marker-end' => array(),
4677
+ 'marker-mid' => array(),
4678
+ 'marker-start' => array(),
4679
+ 'mask' => array(),
4680
+ 'opacity' => array(),
4681
+ 'overflow' => array(),
4682
+ 'pointer-events' => array(),
4683
+ 'preserveaspectratio' => array(),
4684
+ 'requiredextensions' => array(),
4685
+ 'requiredfeatures' => array(),
4686
+ 'shape-rendering' => array(),
4687
+ 'stop-color' => array(),
4688
+ 'stop-opacity' => array(),
4689
+ 'stroke' => array(),
4690
+ 'stroke-dasharray' => array(),
4691
+ 'stroke-dashoffset' => array(),
4692
+ 'stroke-linecap' => array(),
4693
+ 'stroke-linejoin' => array(),
4694
+ 'stroke-miterlimit' => array(),
4695
+ 'stroke-opacity' => array(),
4696
+ 'stroke-width' => array(),
4697
+ 'style' => array(
4698
+ 'blacklisted_value_regex' => '!important',
4699
+ ),
4700
+ 'systemlanguage' => array(),
4701
+ 'text-anchor' => array(),
4702
+ 'text-decoration' => array(),
4703
+ 'text-rendering' => array(),
4704
+ 'transform' => array(),
4705
+ 'unicode-bidi' => array(),
4706
+ 'vector-effect' => array(),
4707
+ 'visibility' => array(),
4708
+ 'width' => array(),
4709
+ 'word-spacing' => array(),
4710
+ 'writing-mode' => array(),
4711
+ 'x' => array(),
4712
+ 'xlink:actuate' => array(),
4713
+ 'xlink:arcrole' => array(),
4714
+ 'xlink:href' => array(
4715
+ 'alternative_names' => 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(),
4730
+ 'xlink:show' => array(),
4731
+ 'xlink:title' => array(),
4732
+ 'xlink:type' => array(),
4733
+ 'xml:lang' => array(),
4734
+ 'xml:space' => array(),
4735
+ 'xmlns' => array(),
4736
+ 'xmlns:xlink' => 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(
4746
+ array(
4747
+ 'attr_spec_list' => array(
4748
+ 'alt' => array(),
4749
+ 'border' => array(),
4750
+ 'height' => 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(
4763
+ 'alternative_names' => array(
4764
+ 'srcset',
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(
4786
+ array(
4787
+ 'attr_spec_list' => array(
4788
+ '[accept]' => array(),
4789
+ '[accesskey]' => array(),
4790
+ '[autocomplete]' => array(),
4791
+ '[checked]' => array(),
4792
+ '[disabled]' => array(),
4793
+ '[height]' => array(),
4794
+ '[inputmode]' => array(),
4795
+ '[max]' => array(),
4796
+ '[maxlength]' => array(),
4797
+ '[min]' => array(),
4798
+ '[minlength]' => array(),
4799
+ '[multiple]' => array(),
4800
+ '[pattern]' => array(),
4801
+ '[placeholder]' => array(),
4802
+ '[readonly]' => array(),
4803
+ '[required]' => array(),
4804
+ '[selectiondirection]' => array(),
4805
+ '[size]' => array(),
4806
+ '[spellcheck]' => array(),
4807
+ '[step]' => array(),
4808
+ '[type]' => array(),
4809
+ '[value]' => array(),
4810
+ '[width]' => array(),
4811
+ 'accept' => array(),
4812
+ 'accesskey' => array(),
4813
+ 'autocomplete' => array(),
4814
+ 'autofocus' => array(),
4815
+ 'checked' => array(),
4816
+ 'disabled' => array(),
4817
+ 'height' => array(),
4818
+ 'inputmode' => array(),
4819
+ 'list' => array(),
4820
+ 'max' => array(),
4821
+ 'maxlength' => array(),
4822
+ 'min' => 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(),
4830
+ 'readonly' => array(),
4831
+ 'required' => array(),
4832
+ 'selectiondirection' => array(),
4833
+ 'size' => array(),
4834
+ 'spellcheck' => array(),
4835
+ 'step' => array(),
4836
+ 'tabindex' => array(),
4837
+ 'type' => array(
4838
+ 'blacklisted_value_regex' => '(^|\\s)(button|file|image|password|)(\\s|$)',
4839
+ ),
4840
+ 'value' => array(),
4841
+ 'width' => array(),
4842
+ ),
4843
+ 'tag_spec' => array(
4844
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
4845
+ ),
4846
+ ),
4847
+ ),
4848
+ 'ins' => array(
4849
+ 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(
4874
+ array(
4875
+ 'attr_spec_list' => array(
4876
+ 'for' => array(),
4877
+ ),
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(
4890
+ array(
4891
+ 'attr_spec_list' => array(
4892
+ 'value' => array(
4893
+ 'value_regex' => '[0-9]*',
4894
+ ),
4895
+ ),
4896
+ 'tag_spec' => array(),
4897
+ ),
4898
+ ),
4899
+ 'line' => array(
4900
+ array(
4901
+ 'attr_spec_list' => array(
4902
+ 'alignment-baseline' => array(),
4903
+ 'baseline-shift' => array(),
4904
+ 'clip' => array(),
4905
+ 'clip-path' => array(),
4906
+ 'clip-rule' => array(),
4907
+ 'color' => array(),
4908
+ 'color-interpolation' => array(),
4909
+ 'color-interpolation-filters' => array(),
4910
+ 'color-profile' => array(),
4911
+ 'color-rendering' => array(),
4912
+ 'cursor' => array(),
4913
+ 'direction' => array(),
4914
+ 'display' => array(),
4915
+ 'dominant-baseline' => array(),
4916
+ 'enable-background' => array(),
4917
+ 'externalresourcesrequired' => array(),
4918
+ 'fill' => array(),
4919
+ 'fill-opacity' => array(),
4920
+ 'fill-rule' => array(),
4921
+ 'filter' => array(),
4922
+ 'flood-color' => array(),
4923
+ 'flood-opacity' => array(),
4924
+ 'font-family' => array(),
4925
+ 'font-size' => array(),
4926
+ 'font-size-adjust' => array(),
4927
+ 'font-stretch' => array(),
4928
+ 'font-style' => array(),
4929
+ 'font-variant' => array(),
4930
+ 'font-weight' => array(),
4931
+ 'glyph-orientation-horizontal' => array(),
4932
+ 'glyph-orientation-vertical' => array(),
4933
+ 'image-rendering' => array(),
4934
+ 'kerning' => array(),
4935
+ 'letter-spacing' => array(),
4936
+ 'lighting-color' => array(),
4937
+ 'marker-end' => array(),
4938
+ 'marker-mid' => array(),
4939
+ 'marker-start' => array(),
4940
+ 'mask' => array(),
4941
+ 'opacity' => array(),
4942
+ 'overflow' => array(),
4943
+ 'pointer-events' => array(),
4944
+ 'requiredextensions' => array(),
4945
+ 'requiredfeatures' => array(),
4946
+ 'shape-rendering' => array(),
4947
+ 'sketch:type' => array(),
4948
+ 'stop-color' => array(),
4949
+ 'stop-opacity' => array(),
4950
+ 'stroke' => array(),
4951
+ 'stroke-dasharray' => array(),
4952
+ 'stroke-dashoffset' => array(),
4953
+ 'stroke-linecap' => array(),
4954
+ 'stroke-linejoin' => array(),
4955
+ 'stroke-miterlimit' => array(),
4956
+ 'stroke-opacity' => array(),
4957
+ 'stroke-width' => array(),
4958
+ 'style' => array(
4959
+ 'blacklisted_value_regex' => '!important',
4960
+ ),
4961
+ 'systemlanguage' => array(),
4962
+ 'text-anchor' => array(),
4963
+ 'text-decoration' => array(),
4964
+ 'text-rendering' => array(),
4965
+ 'transform' => array(),
4966
+ 'unicode-bidi' => array(),
4967
+ 'vector-effect' => array(),
4968
+ 'visibility' => array(),
4969
+ 'word-spacing' => array(),
4970
+ 'writing-mode' => array(),
4971
+ 'x1' => array(),
4972
+ 'x2' => array(),
4973
+ 'xml:lang' => array(),
4974
+ 'xml:space' => array(),
4975
+ 'xmlns' => array(),
4976
+ 'xmlns:xlink' => array(),
4977
+ 'y1' => 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(
4987
+ array(
4988
+ 'attr_spec_list' => array(
4989
+ 'alignment-baseline' => array(),
4990
+ 'baseline-shift' => array(),
4991
+ 'clip' => array(),
4992
+ 'clip-path' => array(),
4993
+ 'clip-rule' => array(),
4994
+ 'color' => array(),
4995
+ 'color-interpolation' => array(),
4996
+ 'color-interpolation-filters' => array(),
4997
+ 'color-profile' => array(),
4998
+ 'color-rendering' => array(),
4999
+ 'cursor' => array(),
5000
+ 'direction' => array(),
5001
+ 'display' => array(),
5002
+ 'dominant-baseline' => array(),
5003
+ 'enable-background' => array(),
5004
+ 'externalresourcesrequired' => array(),
5005
+ 'fill' => array(),
5006
+ 'fill-opacity' => array(),
5007
+ 'fill-rule' => array(),
5008
+ 'filter' => array(),
5009
+ 'flood-color' => array(),
5010
+ 'flood-opacity' => array(),
5011
+ 'font-family' => array(),
5012
+ 'font-size' => array(),
5013
+ 'font-size-adjust' => array(),
5014
+ 'font-stretch' => array(),
5015
+ 'font-style' => array(),
5016
+ 'font-variant' => array(),
5017
+ 'font-weight' => array(),
5018
+ 'glyph-orientation-horizontal' => array(),
5019
+ 'glyph-orientation-vertical' => array(),
5020
+ 'gradienttransform' => array(),
5021
+ 'gradientunits' => array(),
5022
+ 'image-rendering' => array(),
5023
+ 'kerning' => array(),
5024
+ 'letter-spacing' => array(),
5025
+ 'lighting-color' => array(),
5026
+ 'marker-end' => array(),
5027
+ 'marker-mid' => array(),
5028
+ 'marker-start' => array(),
5029
+ 'mask' => array(),
5030
+ 'opacity' => array(),
5031
+ 'overflow' => array(),
5032
+ 'pointer-events' => array(),
5033
+ 'shape-rendering' => array(),
5034
+ 'spreadmethod' => array(),
5035
+ 'stop-color' => array(),
5036
+ 'stop-opacity' => array(),
5037
+ 'stroke' => array(),
5038
+ 'stroke-dasharray' => array(),
5039
+ 'stroke-dashoffset' => array(),
5040
+ 'stroke-linecap' => array(),
5041
+ 'stroke-linejoin' => array(),
5042
+ 'stroke-miterlimit' => array(),
5043
+ 'stroke-opacity' => array(),
5044
+ 'stroke-width' => array(),
5045
+ 'style' => array(
5046
+ 'blacklisted_value_regex' => '!important',
5047
+ ),
5048
+ 'text-anchor' => array(),
5049
+ 'text-decoration' => array(),
5050
+ 'text-rendering' => array(),
5051
+ 'unicode-bidi' => array(),
5052
+ 'vector-effect' => array(),
5053
+ 'visibility' => array(),
5054
+ 'word-spacing' => array(),
5055
+ 'writing-mode' => array(),
5056
+ 'x1' => array(),
5057
+ 'x2' => array(),
5058
+ 'xlink:actuate' => array(),
5059
+ 'xlink:arcrole' => array(),
5060
+ 'xlink:href' => 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(),
5074
+ 'xlink:show' => array(),
5075
+ 'xlink:title' => array(),
5076
+ 'xlink:type' => array(),
5077
+ 'xml:lang' => array(),
5078
+ 'xml:space' => array(),
5079
+ 'xmlns' => array(),
5080
+ 'xmlns:xlink' => array(),
5081
+ 'y1' => 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(
5091
+ array(
5092
+ 'attr_spec_list' => array(
5093
+ 'charset' => 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(),
5106
+ 'target' => array(),
5107
+ 'type' => 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(
5119
+ 'charset' => 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(
5243
+ 'charset' => 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(),
5259
+ 'target' => 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(
5293
+ 'charset' => array(
5294
+ 'value_casei' => 'utf-8',
5295
+ ),
5296
+ 'color' => array(),
5297
+ 'crossorigin' => array(),
5298
+ 'href' => array(
5299
+ 'mandatory' => true,
5300
+ ),
5301
+ 'hreflang' => array(),
5302
+ 'media' => array(),
5303
+ 'property' => array(
5304
+ 'mandatory' => true,
5305
+ ),
5306
+ 'sizes' => array(),
5307
+ 'target' => array(),
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(
5335
+ array(
5336
+ 'attr_spec_list' => array(
5337
+ 'alignment-baseline' => array(),
5338
+ 'baseline-shift' => array(),
5339
+ 'clip' => array(),
5340
+ 'clip-path' => array(),
5341
+ 'clip-rule' => array(),
5342
+ 'color' => array(),
5343
+ 'color-interpolation' => array(),
5344
+ 'color-interpolation-filters' => array(),
5345
+ 'color-profile' => array(),
5346
+ 'color-rendering' => array(),
5347
+ 'cursor' => array(),
5348
+ 'direction' => array(),
5349
+ 'display' => array(),
5350
+ 'dominant-baseline' => array(),
5351
+ 'enable-background' => array(),
5352
+ 'externalresourcesrequired' => array(),
5353
+ 'fill' => array(),
5354
+ 'fill-opacity' => array(),
5355
+ 'fill-rule' => array(),
5356
+ 'filter' => array(),
5357
+ 'flood-color' => array(),
5358
+ 'flood-opacity' => array(),
5359
+ 'font-family' => array(),
5360
+ 'font-size' => array(),
5361
+ 'font-size-adjust' => array(),
5362
+ 'font-stretch' => array(),
5363
+ 'font-style' => array(),
5364
+ 'font-variant' => array(),
5365
+ 'font-weight' => array(),
5366
+ 'glyph-orientation-horizontal' => array(),
5367
+ 'glyph-orientation-vertical' => array(),
5368
+ 'image-rendering' => array(),
5369
+ 'kerning' => array(),
5370
+ 'letter-spacing' => array(),
5371
+ 'lighting-color' => array(),
5372
+ 'marker-end' => array(),
5373
+ 'marker-mid' => array(),
5374
+ 'marker-start' => array(),
5375
+ 'markerheight' => array(),
5376
+ 'markerunits' => array(),
5377
+ 'markerwidth' => array(),
5378
+ 'mask' => array(),
5379
+ 'opacity' => array(),
5380
+ 'orient' => array(),
5381
+ 'overflow' => array(),
5382
+ 'pointer-events' => array(),
5383
+ 'preserveaspectratio' => array(),
5384
+ 'refx' => array(),
5385
+ 'refy' => array(),
5386
+ 'shape-rendering' => array(),
5387
+ 'stop-color' => array(),
5388
+ 'stop-opacity' => array(),
5389
+ 'stroke' => array(),
5390
+ 'stroke-dasharray' => array(),
5391
+ 'stroke-dashoffset' => array(),
5392
+ 'stroke-linecap' => array(),
5393
+ 'stroke-linejoin' => array(),
5394
+ 'stroke-miterlimit' => array(),
5395
+ 'stroke-opacity' => array(),
5396
+ 'stroke-width' => array(),
5397
+ 'style' => array(
5398
+ 'blacklisted_value_regex' => '!important',
5399
+ ),
5400
+ 'text-anchor' => array(),
5401
+ 'text-decoration' => array(),
5402
+ 'text-rendering' => array(),
5403
+ 'transform' => array(),
5404
+ 'unicode-bidi' => array(),
5405
+ 'vector-effect' => array(),
5406
+ 'viewbox' => array(),
5407
+ 'visibility' => array(),
5408
+ 'word-spacing' => array(),
5409
+ 'writing-mode' => array(),
5410
+ 'xml:lang' => array(),
5411
+ 'xml:space' => array(),
5412
+ 'xmlns' => 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(
5422
+ array(
5423
+ 'attr_spec_list' => array(
5424
+ 'alignment-baseline' => array(),
5425
+ 'baseline-shift' => array(),
5426
+ 'clip' => array(),
5427
+ 'clip-path' => array(),
5428
+ 'clip-rule' => array(),
5429
+ 'color' => array(),
5430
+ 'color-interpolation' => array(),
5431
+ 'color-interpolation-filters' => array(),
5432
+ 'color-profile' => array(),
5433
+ 'color-rendering' => array(),
5434
+ 'cursor' => array(),
5435
+ 'direction' => array(),
5436
+ 'display' => array(),
5437
+ 'dominant-baseline' => array(),
5438
+ 'enable-background' => array(),
5439
+ 'externalresourcesrequired' => array(),
5440
+ 'fill' => array(),
5441
+ 'fill-opacity' => array(),
5442
+ 'fill-rule' => array(),
5443
+ 'filter' => array(),
5444
+ 'flood-color' => array(),
5445
+ 'flood-opacity' => array(),
5446
+ 'font-family' => array(),
5447
+ 'font-size' => array(),
5448
+ 'font-size-adjust' => array(),
5449
+ 'font-stretch' => array(),
5450
+ 'font-style' => array(),
5451
+ 'font-variant' => array(),
5452
+ 'font-weight' => array(),
5453
+ 'glyph-orientation-horizontal' => array(),
5454
+ 'glyph-orientation-vertical' => array(),
5455
+ 'height' => array(),
5456
+ 'image-rendering' => array(),
5457
+ 'kerning' => array(),
5458
+ 'letter-spacing' => array(),
5459
+ 'lighting-color' => array(),
5460
+ 'marker-end' => array(),
5461
+ 'marker-mid' => array(),
5462
+ 'marker-start' => array(),
5463
+ 'mask' => array(),
5464
+ 'maskcontentunits' => array(),
5465
+ 'maskunits' => array(),
5466
+ 'opacity' => array(),
5467
+ 'overflow' => array(),
5468
+ 'pointer-events' => array(),
5469
+ 'requiredextensions' => array(),
5470
+ 'requiredfeatures' => array(),
5471
+ 'shape-rendering' => array(),
5472
+ 'stop-color' => array(),
5473
+ 'stop-opacity' => array(),
5474
+ 'stroke' => array(),
5475
+ 'stroke-dasharray' => array(),
5476
+ 'stroke-dashoffset' => array(),
5477
+ 'stroke-linecap' => array(),
5478
+ 'stroke-linejoin' => array(),
5479
+ 'stroke-miterlimit' => array(),
5480
+ 'stroke-opacity' => array(),
5481
+ 'stroke-width' => array(),
5482
+ 'style' => array(
5483
+ 'blacklisted_value_regex' => '!important',
5484
+ ),
5485
+ 'systemlanguage' => array(),
5486
+ 'text-anchor' => array(),
5487
+ 'text-decoration' => array(),
5488
+ 'text-rendering' => array(),
5489
+ 'unicode-bidi' => array(),
5490
+ 'vector-effect' => array(),
5491
+ 'visibility' => array(),
5492
+ 'width' => array(),
5493
+ 'word-spacing' => array(),
5494
+ 'writing-mode' => array(),
5495
+ 'x' => array(),
5496
+ 'xml:lang' => array(),
5497
+ 'xml:space' => array(),
5498
+ 'xmlns' => array(),
5499
+ 'xmlns:xlink' => 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(
5718
+ 'content' => array(
5719
+ 'mandatory' => true,
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(
5736
+ 'content' => 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(
5753
+ 'content' => 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(
5770
+ 'content' => 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(
5787
+ 'content' => array(
5788
+ 'mandatory' => true,
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(
5805
+ 'content' => array(
5806
+ 'mandatory' => true,
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(
5823
+ 'content' => 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(
5840
+ 'content' => 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(
5908
+ array(
5909
+ 'attr_spec_list' => array(
5910
+ 'style' => array(
5911
+ 'blacklisted_value_regex' => '!important',
5912
+ ),
5913
+ 'xml:lang' => array(),
5914
+ 'xml:space' => array(),
5915
+ 'xmlns' => 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(
5925
+ array(
5926
+ 'attr_spec_list' => array(
5927
+ 'high' => array(),
5928
+ 'low' => array(),
5929
+ 'max' => array(),
5930
+ 'min' => array(),
5931
+ 'optimum' => 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(
5955
+ 'mandatory' => true,
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(
6004
+ array(
6005
+ 'attr_spec_list' => array(
6006
+ 'reversed' => array(
6007
+ 'value' => '',
6008
+ ),
6009
+ 'start' => 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(
6020
+ array(
6021
+ 'attr_spec_list' => array(
6022
+ '[disabled]' => array(),
6023
+ '[label]' => array(),
6024
+ 'disabled' => array(),
6025
+ 'label' => array(),
6026
+ ),
6027
+ 'tag_spec' => array(
6028
+ 'mandatory_parent' => 'select',
6029
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
6030
+ ),
6031
+ ),
6032
+ ),
6033
+ 'option' => array(
6034
+ array(
6035
+ 'attr_spec_list' => array(
6036
+ '[disabled]' => array(),
6037
+ '[label]' => array(),
6038
+ '[selected]' => array(),
6039
+ '[value]' => array(),
6040
+ 'disabled' => array(),
6041
+ 'label' => array(),
6042
+ 'selected' => array(),
6043
+ 'value' => array(),
6044
+ ),
6045
+ 'tag_spec' => array(
6046
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
6047
+ ),
6048
+ ),
6049
+ ),
6050
+ 'output' => array(
6051
+ array(
6052
+ 'attr_spec_list' => 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(
6063
+ array(
6064
+ 'attr_spec_list' => array(
6065
+ 'align' => array(),
6066
+ ),
6067
+ 'tag_spec' => array(),
6068
+ ),
6069
+ ),
6070
+ 'path' => array(
6071
+ array(
6072
+ 'attr_spec_list' => array(
6073
+ 'alignment-baseline' => array(),
6074
+ 'baseline-shift' => array(),
6075
+ 'clip' => array(),
6076
+ 'clip-path' => array(),
6077
+ 'clip-rule' => array(),
6078
+ 'color' => array(),
6079
+ 'color-interpolation' => array(),
6080
+ 'color-interpolation-filters' => array(),
6081
+ 'color-profile' => array(),
6082
+ 'color-rendering' => array(),
6083
+ 'cursor' => array(),
6084
+ 'd' => array(),
6085
+ 'direction' => array(),
6086
+ 'display' => array(),
6087
+ 'dominant-baseline' => array(),
6088
+ 'enable-background' => array(),
6089
+ 'externalresourcesrequired' => array(),
6090
+ 'fill' => array(),
6091
+ 'fill-opacity' => array(),
6092
+ 'fill-rule' => array(),
6093
+ 'filter' => array(),
6094
+ 'flood-color' => array(),
6095
+ 'flood-opacity' => array(),
6096
+ 'font-family' => array(),
6097
+ 'font-size' => array(),
6098
+ 'font-size-adjust' => array(),
6099
+ 'font-stretch' => array(),
6100
+ 'font-style' => array(),
6101
+ 'font-variant' => array(),
6102
+ 'font-weight' => array(),
6103
+ 'glyph-orientation-horizontal' => array(),
6104
+ 'glyph-orientation-vertical' => array(),
6105
+ 'image-rendering' => array(),
6106
+ 'kerning' => array(),
6107
+ 'letter-spacing' => array(),
6108
+ 'lighting-color' => array(),
6109
+ 'marker-end' => array(),
6110
+ 'marker-mid' => array(),
6111
+ 'marker-start' => array(),
6112
+ 'mask' => array(),
6113
+ 'opacity' => array(),
6114
+ 'overflow' => array(),
6115
+ 'pathlength' => array(),
6116
+ 'pointer-events' => array(),
6117
+ 'requiredextensions' => array(),
6118
+ 'requiredfeatures' => array(),
6119
+ 'shape-rendering' => array(),
6120
+ 'sketch:type' => array(),
6121
+ 'stop-color' => array(),
6122
+ 'stop-opacity' => array(),
6123
+ 'stroke' => array(),
6124
+ 'stroke-dasharray' => array(),
6125
+ 'stroke-dashoffset' => array(),
6126
+ 'stroke-linecap' => array(),
6127
+ 'stroke-linejoin' => array(),
6128
+ 'stroke-miterlimit' => array(),
6129
+ 'stroke-opacity' => array(),
6130
+ 'stroke-width' => array(),
6131
+ 'style' => array(
6132
+ 'blacklisted_value_regex' => '!important',
6133
+ ),
6134
+ 'systemlanguage' => array(),
6135
+ 'text-anchor' => array(),
6136
+ 'text-decoration' => array(),
6137
+ 'text-rendering' => array(),
6138
+ 'transform' => array(),
6139
+ 'unicode-bidi' => array(),
6140
+ 'vector-effect' => array(),
6141
+ 'visibility' => array(),
6142
+ 'word-spacing' => array(),
6143
+ 'writing-mode' => array(),
6144
+ 'xml:lang' => array(),
6145
+ 'xml:space' => array(),
6146
+ 'xmlns' => 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(
6156
+ array(
6157
+ 'attr_spec_list' => array(
6158
+ 'alignment-baseline' => array(),
6159
+ 'baseline-shift' => array(),
6160
+ 'clip' => array(),
6161
+ 'clip-path' => array(),
6162
+ 'clip-rule' => array(),
6163
+ 'color' => array(),
6164
+ 'color-interpolation' => array(),
6165
+ 'color-interpolation-filters' => array(),
6166
+ 'color-profile' => array(),
6167
+ 'color-rendering' => array(),
6168
+ 'cursor' => array(),
6169
+ 'direction' => array(),
6170
+ 'display' => array(),
6171
+ 'dominant-baseline' => array(),
6172
+ 'enable-background' => array(),
6173
+ 'externalresourcesrequired' => array(),
6174
+ 'fill' => array(),
6175
+ 'fill-opacity' => array(),
6176
+ 'fill-rule' => array(),
6177
+ 'filter' => array(),
6178
+ 'flood-color' => array(),
6179
+ 'flood-opacity' => array(),
6180
+ 'font-family' => array(),
6181
+ 'font-size' => array(),
6182
+ 'font-size-adjust' => array(),
6183
+ 'font-stretch' => array(),
6184
+ 'font-style' => array(),
6185
+ 'font-variant' => array(),
6186
+ 'font-weight' => array(),
6187
+ 'glyph-orientation-horizontal' => array(),
6188
+ 'glyph-orientation-vertical' => array(),
6189
+ 'height' => array(),
6190
+ 'image-rendering' => array(),
6191
+ 'kerning' => array(),
6192
+ 'letter-spacing' => array(),
6193
+ 'lighting-color' => array(),
6194
+ 'marker-end' => array(),
6195
+ 'marker-mid' => array(),
6196
+ 'marker-start' => array(),
6197
+ 'mask' => array(),
6198
+ 'opacity' => array(),
6199
+ 'overflow' => array(),
6200
+ 'patterncontentunits' => array(),
6201
+ 'patterntransform' => array(),
6202
+ 'patternunits' => array(),
6203
+ 'pointer-events' => array(),
6204
+ 'preserveaspectratio' => array(),
6205
+ 'requiredextensions' => array(),
6206
+ 'requiredfeatures' => array(),
6207
+ 'shape-rendering' => array(),
6208
+ 'stop-color' => array(),
6209
+ 'stop-opacity' => array(),
6210
+ 'stroke' => array(),
6211
+ 'stroke-dasharray' => array(),
6212
+ 'stroke-dashoffset' => array(),
6213
+ 'stroke-linecap' => array(),
6214
+ 'stroke-linejoin' => array(),
6215
+ 'stroke-miterlimit' => array(),
6216
+ 'stroke-opacity' => array(),
6217
+ 'stroke-width' => array(),
6218
+ 'style' => array(
6219
+ 'blacklisted_value_regex' => '!important',
6220
+ ),
6221
+ 'systemlanguage' => array(),
6222
+ 'text-anchor' => array(),
6223
+ 'text-decoration' => array(),
6224
+ 'text-rendering' => array(),
6225
+ 'unicode-bidi' => array(),
6226
+ 'vector-effect' => array(),
6227
+ 'viewbox' => array(),
6228
+ 'visibility' => array(),
6229
+ 'width' => array(),
6230
+ 'word-spacing' => array(),
6231
+ 'writing-mode' => array(),
6232
+ 'x' => array(),
6233
+ 'xlink:actuate' => array(),
6234
+ 'xlink:arcrole' => array(),
6235
+ 'xlink:href' => 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(),
6249
+ 'xlink:show' => array(),
6250
+ 'xlink:title' => array(),
6251
+ 'xlink:type' => array(),
6252
+ 'xml:lang' => array(),
6253
+ 'xml:space' => array(),
6254
+ 'xmlns' => array(),
6255
+ 'xmlns:xlink' => 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(
6265
+ array(
6266
+ 'attr_spec_list' => array(
6267
+ 'alignment-baseline' => array(),
6268
+ 'baseline-shift' => array(),
6269
+ 'clip' => array(),
6270
+ 'clip-path' => array(),
6271
+ 'clip-rule' => array(),
6272
+ 'color' => array(),
6273
+ 'color-interpolation' => array(),
6274
+ 'color-interpolation-filters' => array(),
6275
+ 'color-profile' => array(),
6276
+ 'color-rendering' => array(),
6277
+ 'cursor' => array(),
6278
+ 'direction' => array(),
6279
+ 'display' => array(),
6280
+ 'dominant-baseline' => array(),
6281
+ 'enable-background' => array(),
6282
+ 'externalresourcesrequired' => array(),
6283
+ 'fill' => array(),
6284
+ 'fill-opacity' => array(),
6285
+ 'fill-rule' => array(),
6286
+ 'filter' => array(),
6287
+ 'flood-color' => array(),
6288
+ 'flood-opacity' => array(),
6289
+ 'font-family' => array(),
6290
+ 'font-size' => array(),
6291
+ 'font-size-adjust' => array(),
6292
+ 'font-stretch' => array(),
6293
+ 'font-style' => array(),
6294
+ 'font-variant' => array(),
6295
+ 'font-weight' => array(),
6296
+ 'glyph-orientation-horizontal' => array(),
6297
+ 'glyph-orientation-vertical' => array(),
6298
+ 'image-rendering' => array(),
6299
+ 'kerning' => array(),
6300
+ 'letter-spacing' => array(),
6301
+ 'lighting-color' => array(),
6302
+ 'marker-end' => array(),
6303
+ 'marker-mid' => array(),
6304
+ 'marker-start' => array(),
6305
+ 'mask' => array(),
6306
+ 'opacity' => array(),
6307
+ 'overflow' => array(),
6308
+ 'pointer-events' => array(),
6309
+ 'points' => array(),
6310
+ 'requiredextensions' => array(),
6311
+ 'requiredfeatures' => array(),
6312
+ 'shape-rendering' => array(),
6313
+ 'sketch:type' => array(),
6314
+ 'stop-color' => array(),
6315
+ 'stop-opacity' => array(),
6316
+ 'stroke' => array(),
6317
+ 'stroke-dasharray' => array(),
6318
+ 'stroke-dashoffset' => array(),
6319
+ 'stroke-linecap' => array(),
6320
+ 'stroke-linejoin' => array(),
6321
+ 'stroke-miterlimit' => array(),
6322
+ 'stroke-opacity' => array(),
6323
+ 'stroke-width' => array(),
6324
+ 'style' => array(
6325
+ 'blacklisted_value_regex' => '!important',
6326
+ ),
6327
+ 'systemlanguage' => array(),
6328
+ 'text-anchor' => array(),
6329
+ 'text-decoration' => array(),
6330
+ 'text-rendering' => array(),
6331
+ 'transform' => array(),
6332
+ 'unicode-bidi' => array(),
6333
+ 'vector-effect' => array(),
6334
+ 'visibility' => array(),
6335
+ 'word-spacing' => array(),
6336
+ 'writing-mode' => array(),
6337
+ 'xml:lang' => array(),
6338
+ 'xml:space' => array(),
6339
+ 'xmlns' => 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(
6349
+ array(
6350
+ 'attr_spec_list' => array(
6351
+ 'alignment-baseline' => array(),
6352
+ 'baseline-shift' => array(),
6353
+ 'clip' => array(),
6354
+ 'clip-path' => array(),
6355
+ 'clip-rule' => array(),
6356
+ 'color' => array(),
6357
+ 'color-interpolation' => array(),
6358
+ 'color-interpolation-filters' => array(),
6359
+ 'color-profile' => array(),
6360
+ 'color-rendering' => array(),
6361
+ 'cursor' => array(),
6362
+ 'direction' => array(),
6363
+ 'display' => array(),
6364
+ 'dominant-baseline' => array(),
6365
+ 'enable-background' => array(),
6366
+ 'externalresourcesrequired' => array(),
6367
+ 'fill' => array(),
6368
+ 'fill-opacity' => array(),
6369
+ 'fill-rule' => array(),
6370
+ 'filter' => array(),
6371
+ 'flood-color' => array(),
6372
+ 'flood-opacity' => array(),
6373
+ 'font-family' => array(),
6374
+ 'font-size' => array(),
6375
+ 'font-size-adjust' => array(),
6376
+ 'font-stretch' => array(),
6377
+ 'font-style' => array(),
6378
+ 'font-variant' => array(),
6379
+ 'font-weight' => array(),
6380
+ 'glyph-orientation-horizontal' => array(),
6381
+ 'glyph-orientation-vertical' => array(),
6382
+ 'image-rendering' => array(),
6383
+ 'kerning' => array(),
6384
+ 'letter-spacing' => array(),
6385
+ 'lighting-color' => array(),
6386
+ 'marker-end' => array(),
6387
+ 'marker-mid' => array(),
6388
+ 'marker-start' => array(),
6389
+ 'mask' => array(),
6390
+ 'opacity' => array(),
6391
+ 'overflow' => array(),
6392
+ 'pointer-events' => array(),
6393
+ 'points' => array(),
6394
+ 'requiredextensions' => array(),
6395
+ 'requiredfeatures' => array(),
6396
+ 'shape-rendering' => array(),
6397
+ 'sketch:type' => array(),
6398
+ 'stop-color' => array(),
6399
+ 'stop-opacity' => array(),
6400
+ 'stroke' => array(),
6401
+ 'stroke-dasharray' => array(),
6402
+ 'stroke-dashoffset' => array(),
6403
+ 'stroke-linecap' => array(),
6404
+ 'stroke-linejoin' => array(),
6405
+ 'stroke-miterlimit' => array(),
6406
+ 'stroke-opacity' => array(),
6407
+ 'stroke-width' => array(),
6408
+ 'style' => array(
6409
+ 'blacklisted_value_regex' => '!important',
6410
+ ),
6411
+ 'systemlanguage' => array(),
6412
+ 'text-anchor' => array(),
6413
+ 'text-decoration' => array(),
6414
+ 'text-rendering' => array(),
6415
+ 'transform' => array(),
6416
+ 'unicode-bidi' => array(),
6417
+ 'vector-effect' => array(),
6418
+ 'visibility' => array(),
6419
+ 'word-spacing' => array(),
6420
+ 'writing-mode' => array(),
6421
+ 'xml:lang' => array(),
6422
+ 'xml:space' => array(),
6423
+ 'xmlns' => 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(
6439
+ array(
6440
+ 'attr_spec_list' => array(
6441
+ 'max' => array(),
6442
+ 'value' => array(),
6443
+ ),
6444
+ 'tag_spec' => array(),
6445
+ ),
6446
+ ),
6447
+ 'q' => array(
6448
+ 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(
6466
+ array(
6467
+ 'attr_spec_list' => array(
6468
+ 'alignment-baseline' => array(),
6469
+ 'baseline-shift' => array(),
6470
+ 'clip' => array(),
6471
+ 'clip-path' => array(),
6472
+ 'clip-rule' => array(),
6473
+ 'color' => array(),
6474
+ 'color-interpolation' => array(),
6475
+ 'color-interpolation-filters' => array(),
6476
+ 'color-profile' => array(),
6477
+ 'color-rendering' => array(),
6478
+ 'cursor' => array(),
6479
+ 'cx' => array(),
6480
+ 'cy' => array(),
6481
+ 'direction' => array(),
6482
+ 'display' => array(),
6483
+ 'dominant-baseline' => array(),
6484
+ 'enable-background' => array(),
6485
+ 'externalresourcesrequired' => array(),
6486
+ 'fill' => array(),
6487
+ 'fill-opacity' => array(),
6488
+ 'fill-rule' => array(),
6489
+ 'filter' => array(),
6490
+ 'flood-color' => array(),
6491
+ 'flood-opacity' => array(),
6492
+ 'font-family' => array(),
6493
+ 'font-size' => array(),
6494
+ 'font-size-adjust' => array(),
6495
+ 'font-stretch' => array(),
6496
+ 'font-style' => array(),
6497
+ 'font-variant' => array(),
6498
+ 'font-weight' => array(),
6499
+ 'fr' => array(),
6500
+ 'fx' => array(),
6501
+ 'fy' => array(),
6502
+ 'glyph-orientation-horizontal' => array(),
6503
+ 'glyph-orientation-vertical' => array(),
6504
+ 'gradienttransform' => array(),
6505
+ 'gradientunits' => array(),
6506
+ 'image-rendering' => array(),
6507
+ 'kerning' => array(),
6508
+ 'letter-spacing' => array(),
6509
+ 'lighting-color' => array(),
6510
+ 'marker-end' => array(),
6511
+ 'marker-mid' => array(),
6512
+ 'marker-start' => array(),
6513
+ 'mask' => array(),
6514
+ 'opacity' => array(),
6515
+ 'overflow' => array(),
6516
+ 'pointer-events' => array(),
6517
+ 'r' => array(),
6518
+ 'shape-rendering' => array(),
6519
+ 'spreadmethod' => array(),
6520
+ 'stop-color' => array(),
6521
+ 'stop-opacity' => array(),
6522
+ 'stroke' => array(),
6523
+ 'stroke-dasharray' => array(),
6524
+ 'stroke-dashoffset' => array(),
6525
+ 'stroke-linecap' => array(),
6526
+ 'stroke-linejoin' => array(),
6527
+ 'stroke-miterlimit' => array(),
6528
+ 'stroke-opacity' => array(),
6529
+ 'stroke-width' => array(),
6530
+ 'style' => array(
6531
+ 'blacklisted_value_regex' => '!important',
6532
+ ),
6533
+ 'text-anchor' => array(),
6534
+ 'text-decoration' => array(),
6535
+ 'text-rendering' => array(),
6536
+ 'unicode-bidi' => array(),
6537
+ 'vector-effect' => array(),
6538
+ 'visibility' => array(),
6539
+ 'word-spacing' => array(),
6540
+ 'writing-mode' => array(),
6541
+ 'xlink:actuate' => array(),
6542
+ 'xlink:arcrole' => array(),
6543
+ 'xlink:href' => 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(),
6557
+ 'xlink:show' => array(),
6558
+ 'xlink:title' => array(),
6559
+ 'xlink:type' => array(),
6560
+ 'xml:lang' => array(),
6561
+ 'xml:space' => array(),
6562
+ 'xmlns' => 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(
6578
+ array(
6579
+ 'attr_spec_list' => array(
6580
+ 'alignment-baseline' => array(),
6581
+ 'baseline-shift' => array(),
6582
+ 'clip' => array(),
6583
+ 'clip-path' => array(),
6584
+ 'clip-rule' => array(),
6585
+ 'color' => array(),
6586
+ 'color-interpolation' => array(),
6587
+ 'color-interpolation-filters' => array(),
6588
+ 'color-profile' => array(),
6589
+ 'color-rendering' => array(),
6590
+ 'cursor' => array(),
6591
+ 'direction' => array(),
6592
+ 'display' => array(),
6593
+ 'dominant-baseline' => array(),
6594
+ 'enable-background' => array(),
6595
+ 'externalresourcesrequired' => array(),
6596
+ 'fill' => array(),
6597
+ 'fill-opacity' => array(),
6598
+ 'fill-rule' => array(),
6599
+ 'filter' => array(),
6600
+ 'flood-color' => array(),
6601
+ 'flood-opacity' => array(),
6602
+ 'font-family' => array(),
6603
+ 'font-size' => array(),
6604
+ 'font-size-adjust' => array(),
6605
+ 'font-stretch' => array(),
6606
+ 'font-style' => array(),
6607
+ 'font-variant' => array(),
6608
+ 'font-weight' => array(),
6609
+ 'glyph-orientation-horizontal' => array(),
6610
+ 'glyph-orientation-vertical' => array(),
6611
+ 'height' => array(),
6612
+ 'image-rendering' => array(),
6613
+ 'kerning' => array(),
6614
+ 'letter-spacing' => array(),
6615
+ 'lighting-color' => array(),
6616
+ 'marker-end' => array(),
6617
+ 'marker-mid' => array(),
6618
+ 'marker-start' => array(),
6619
+ 'mask' => array(),
6620
+ 'opacity' => array(),
6621
+ 'overflow' => array(),
6622
+ 'pointer-events' => array(),
6623
+ 'requiredextensions' => array(),
6624
+ 'requiredfeatures' => array(),
6625
+ 'rx' => array(),
6626
+ 'ry' => array(),
6627
+ 'shape-rendering' => array(),
6628
+ 'sketch:type' => array(),
6629
+ 'stop-color' => array(),
6630
+ 'stop-opacity' => array(),
6631
+ 'stroke' => array(),
6632
+ 'stroke-dasharray' => array(),
6633
+ 'stroke-dashoffset' => array(),
6634
+ 'stroke-linecap' => array(),
6635
+ 'stroke-linejoin' => array(),
6636
+ 'stroke-miterlimit' => array(),
6637
+ 'stroke-opacity' => array(),
6638
+ 'stroke-width' => array(),
6639
+ 'style' => array(
6640
+ 'blacklisted_value_regex' => '!important',
6641
+ ),
6642
+ 'systemlanguage' => array(),
6643
+ 'text-anchor' => array(),
6644
+ 'text-decoration' => array(),
6645
+ 'text-rendering' => array(),
6646
+ 'transform' => array(),
6647
+ 'unicode-bidi' => array(),
6648
+ 'vector-effect' => array(),
6649
+ 'visibility' => array(),
6650
+ 'width' => array(),
6651
+ 'word-spacing' => array(),
6652
+ 'writing-mode' => array(),
6653
+ 'x' => array(),
6654
+ 'xml:lang' => array(),
6655
+ 'xml:space' => array(),
6656
+ 'xmlns' => array(),
6657
+ 'xmlns:xlink' => 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(
8859
+ 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(
8869
+ 'expanded' => array(
8870
+ 'value' => '',
8871
+ ),
8872
+ ),
8873
+ 'tag_spec' => array(
8874
+ 'mandatory_parent' => 'amp-accordion',
8875
+ 'spec_name' => 'amp-accordion > section',
8876
+ ),
8877
+ ),
8878
+ ),
8879
+ 'select' => array(
8880
+ array(
8881
+ 'attr_spec_list' => array(
8882
+ '[autofocus]' => array(),
8883
+ '[disabled]' => array(),
8884
+ '[multiple]' => array(),
8885
+ '[required]' => array(),
8886
+ '[size]' => array(),
8887
+ 'autofocus' => 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(),
8895
+ ),
8896
+ 'tag_spec' => array(
8897
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
8898
+ ),
8899
+ ),
8900
+ ),
8901
+ 'slot' => array(
8902
+ 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(
8916
+ array(
8917
+ 'attr_spec_list' => array(
8918
+ 'alignment-baseline' => array(),
8919
+ 'baseline-shift' => array(),
8920
+ 'clip' => array(),
8921
+ 'clip-path' => array(),
8922
+ 'clip-rule' => array(),
8923
+ 'color' => array(),
8924
+ 'color-interpolation' => array(),
8925
+ 'color-interpolation-filters' => array(),
8926
+ 'color-profile' => array(),
8927
+ 'color-rendering' => array(),
8928
+ 'cursor' => array(),
8929
+ 'direction' => array(),
8930
+ 'display' => array(),
8931
+ 'dominant-baseline' => array(),
8932
+ 'enable-background' => array(),
8933
+ 'fill' => array(),
8934
+ 'fill-opacity' => array(),
8935
+ 'fill-rule' => array(),
8936
+ 'filter' => array(),
8937
+ 'flood-color' => array(),
8938
+ 'flood-opacity' => array(),
8939
+ 'font-family' => array(),
8940
+ 'font-size' => array(),
8941
+ 'font-size-adjust' => array(),
8942
+ 'font-stretch' => array(),
8943
+ 'font-style' => array(),
8944
+ 'font-variant' => array(),
8945
+ 'font-weight' => array(),
8946
+ 'glyph-orientation-horizontal' => array(),
8947
+ 'glyph-orientation-vertical' => array(),
8948
+ 'image-rendering' => array(),
8949
+ 'kerning' => array(),
8950
+ 'letter-spacing' => array(),
8951
+ 'lighting-color' => array(),
8952
+ 'marker-end' => array(),
8953
+ 'marker-mid' => array(),
8954
+ 'marker-start' => array(),
8955
+ 'mask' => array(),
8956
+ 'opacity' => array(),
8957
+ 'overflow' => array(),
8958
+ 'pointer-events' => array(),
8959
+ 'shape-rendering' => array(),
8960
+ 'solid-color' => array(),
8961
+ 'solid-opacity' => array(),
8962
+ 'stop-color' => array(),
8963
+ 'stop-opacity' => array(),
8964
+ 'stroke' => array(),
8965
+ 'stroke-dasharray' => array(),
8966
+ 'stroke-dashoffset' => array(),
8967
+ 'stroke-linecap' => array(),
8968
+ 'stroke-linejoin' => array(),
8969
+ 'stroke-miterlimit' => array(),
8970
+ 'stroke-opacity' => array(),
8971
+ 'stroke-width' => array(),
8972
+ 'style' => array(
8973
+ 'blacklisted_value_regex' => '!important',
8974
+ ),
8975
+ 'text-anchor' => array(),
8976
+ 'text-decoration' => array(),
8977
+ 'text-rendering' => array(),
8978
+ 'unicode-bidi' => array(),
8979
+ 'vector-effect' => array(),
8980
+ 'visibility' => array(),
8981
+ 'word-spacing' => array(),
8982
+ 'writing-mode' => array(),
8983
+ 'xml:lang' => array(),
8984
+ 'xml:space' => array(),
8985
+ 'xmlns' => 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(
8995
+ array(
8996
+ 'attr_spec_list' => array(
8997
+ '[src]' => array(),
8998
+ '[type]' => 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(
9019
+ '[src]' => array(),
9020
+ '[type]' => 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(
9041
+ 'media' => 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(
9053
+ 'mandatory' => true,
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(
9064
+ 'media' => 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(
9076
+ 'mandatory' => true,
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(
9087
+ '[src]' => array(),
9088
+ '[type]' => 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(
9123
+ array(
9124
+ 'attr_spec_list' => array(
9125
+ 'offset' => array(),
9126
+ 'stop-color' => array(),
9127
+ 'stop-opacity' => array(),
9128
+ 'style' => array(
9129
+ 'blacklisted_value_regex' => '!important',
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(
9140
+ 'offset' => array(),
9141
+ 'stop-color' => array(),
9142
+ 'stop-opacity' => array(),
9143
+ 'style' => array(
9144
+ 'blacklisted_value_regex' => '!important',
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
+ 'css_spec' => array(
9184
+ 'allowed_at_rules' => array(
9185
+ 'font-face',
9186
+ 'keyframes',
9187
+ 'media',
9188
+ 'supports',
9189
+ ),
9190
+ 'allowed_declarations' => array(),
9191
+ 'font_url_spec' => array(
9192
+ 'allow_empty' => true,
9193
+ 'allow_relative' => true,
9194
+ 'allowed_protocol' => array(
9195
+ 'https',
9196
+ 'http',
9197
+ 'data',
9198
+ ),
9199
+ ),
9200
+ 'image_url_spec' => array(
9201
+ 'allow_empty' => true,
9202
+ 'allow_relative' => true,
9203
+ 'allowed_protocol' => array(
9204
+ 'https',
9205
+ 'http',
9206
+ 'data',
9207
+ 'absolute',
9208
+ ),
9209
+ ),
9210
+ 'validate_keyframes' => false,
9211
+ ),
9212
+ 'max_bytes' => 50000,
9213
+ 'max_bytes_spec_url' => 'https://www.ampproject.org/docs/reference/spec#maximum-size',
9214
+ ),
9215
+ 'tag_spec' => array(
9216
+ 'mandatory_parent' => 'head',
9217
+ 'spec_name' => 'style amp-custom',
9218
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#stylesheets',
9219
+ 'unique' => true,
9220
+ ),
9221
+ ),
9222
+ array(
9223
+ 'attr_spec_list' => array(
9224
+ 'amp-boilerplate' => array(
9225
+ 'dispatch_key' => 3,
9226
+ 'mandatory' => true,
9227
+ 'value' => '',
9228
+ ),
9229
+ 'nonce' => array(),
9230
+ ),
9231
+ 'cdata' => array(
9232
+ '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*',
9233
+ ),
9234
+ 'tag_spec' => array(
9235
+ 'mandatory_alternatives' => 'head > style[amp-boilerplate]',
9236
+ 'mandatory_parent' => 'head',
9237
+ 'spec_name' => 'head > style[amp-boilerplate]',
9238
+ 'spec_url' => 'https://github.com/ampproject/amphtml/blob/master/spec/amp-boilerplate.md',
9239
+ 'unique' => true,
9240
+ ),
9241
+ ),
9242
+ array(
9243
+ 'attr_spec_list' => array(
9244
+ 'amp-boilerplate' => array(
9245
+ 'dispatch_key' => 3,
9246
+ 'mandatory' => true,
9247
+ 'value' => '',
9248
+ ),
9249
+ 'nonce' => array(),
9250
+ ),
9251
+ 'cdata' => array(
9252
+ 'cdata_regex' => '\\s*body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}\\s*',
9253
+ ),
9254
+ 'tag_spec' => array(
9255
+ 'mandatory_alternatives' => 'noscript > style[amp-boilerplate]',
9256
+ 'mandatory_ancestor' => 'head',
9257
+ 'mandatory_parent' => 'noscript',
9258
+ 'spec_name' => 'noscript > style[amp-boilerplate]',
9259
+ 'spec_url' => 'https://github.com/ampproject/amphtml/blob/master/spec/amp-boilerplate.md',
9260
+ 'unique' => true,
9261
+ ),
9262
+ ),
9263
+ array(
9264
+ 'attr_spec_list' => array(
9265
+ 'amp-keyframes' => array(
9266
+ 'dispatch_key' => 1,
9267
+ 'mandatory' => true,
9268
+ 'value' => '',
9269
+ ),
9270
+ ),
9271
+ 'cdata' => array(
9272
+ 'css_spec' => array(
9273
+ 'allowed_at_rules' => array(
9274
+ 'keyframes',
9275
+ 'media',
9276
+ 'supports',
9277
+ ),
9278
+ 'allowed_declarations' => array(
9279
+ 'animation-timing-function',
9280
+ 'offset-distance',
9281
+ 'opacity',
9282
+ 'transform',
9283
+ 'visibility',
9284
+ ),
9285
+ 'font_url_spec' => array(),
9286
+ 'image_url_spec' => array(),
9287
+ 'validate_keyframes' => true,
9288
+ ),
9289
+ 'max_bytes' => 500000,
9290
+ 'max_bytes_spec_url' => 'https://www.ampproject.org/docs/reference/spec#keyframes-stylesheet',
9291
+ ),
9292
+ 'tag_spec' => array(
9293
+ 'mandatory_parent' => 'body',
9294
+ 'spec_name' => 'style[amp-keyframes]',
9295
+ 'unique' => true,
9296
+ ),
9297
+ ),
9298
+ ),
9299
+ 'sub' => array(
9300
+ array(
9301
+ 'attr_spec_list' => array(),
9302
+ 'tag_spec' => array(),
9303
+ ),
9304
+ ),
9305
+ 'sup' => array(
9306
+ array(
9307
+ 'attr_spec_list' => array(),
9308
+ 'tag_spec' => array(),
9309
+ ),
9310
+ ),
9311
+ 'svg' => array(
9312
+ array(
9313
+ 'attr_spec_list' => array(
9314
+ 'alignment-baseline' => array(),
9315
+ 'baseline-shift' => array(),
9316
+ 'clip' => array(),
9317
+ 'clip-path' => array(),
9318
+ 'clip-rule' => array(),
9319
+ 'color' => array(),
9320
+ 'color-interpolation' => array(),
9321
+ 'color-interpolation-filters' => array(),
9322
+ 'color-profile' => array(),
9323
+ 'color-rendering' => array(),
9324
+ 'contentscripttype' => array(),
9325
+ 'contentstyletype' => array(),
9326
+ 'cursor' => array(),
9327
+ 'direction' => array(),
9328
+ 'display' => array(),
9329
+ 'dominant-baseline' => array(),
9330
+ 'enable-background' => array(),
9331
+ 'externalresourcesrequired' => array(),
9332
+ 'fill' => array(),
9333
+ 'fill-opacity' => array(),
9334
+ 'fill-rule' => array(),
9335
+ 'filter' => array(),
9336
+ 'flood-color' => array(),
9337
+ 'flood-opacity' => array(),
9338
+ 'font-family' => array(),
9339
+ 'font-size' => array(),
9340
+ 'font-size-adjust' => array(),
9341
+ 'font-stretch' => array(),
9342
+ 'font-style' => array(),
9343
+ 'font-variant' => array(),
9344
+ 'font-weight' => array(),
9345
+ 'glyph-orientation-horizontal' => array(),
9346
+ 'glyph-orientation-vertical' => array(),
9347
+ 'height' => array(),
9348
+ 'image-rendering' => array(),
9349
+ 'kerning' => array(),
9350
+ 'letter-spacing' => array(),
9351
+ 'lighting-color' => array(),
9352
+ 'marker-end' => array(),
9353
+ 'marker-mid' => array(),
9354
+ 'marker-start' => array(),
9355
+ 'mask' => array(),
9356
+ 'opacity' => array(),
9357
+ 'overflow' => array(),
9358
+ 'pointer-events' => array(),
9359
+ 'preserveaspectratio' => array(),
9360
+ 'requiredextensions' => array(),
9361
+ 'requiredfeatures' => array(),
9362
+ 'shape-rendering' => array(),
9363
+ 'stop-color' => array(),
9364
+ 'stop-opacity' => array(),
9365
+ 'stroke' => array(),
9366
+ 'stroke-dasharray' => array(),
9367
+ 'stroke-dashoffset' => array(),
9368
+ 'stroke-linecap' => array(),
9369
+ 'stroke-linejoin' => array(),
9370
+ 'stroke-miterlimit' => array(),
9371
+ 'stroke-opacity' => array(),
9372
+ 'stroke-width' => array(),
9373
+ 'systemlanguage' => array(),
9374
+ 'text-anchor' => array(),
9375
+ 'text-decoration' => array(),
9376
+ 'text-rendering' => array(),
9377
+ 'unicode-bidi' => array(),
9378
+ 'vector-effect' => array(),
9379
+ 'version' => array(
9380
+ 'value_regex' => '(1.0|1.1)',
9381
+ ),
9382
+ 'viewbox' => array(),
9383
+ 'visibility' => array(),
9384
+ 'width' => array(),
9385
+ 'word-spacing' => array(),
9386
+ 'writing-mode' => array(),
9387
+ 'x' => array(),
9388
+ 'xml:lang' => array(),
9389
+ 'xml:space' => array(),
9390
+ 'xmlns' => array(),
9391
+ 'xmlns:xlink' => array(),
9392
+ 'y' => array(),
9393
+ 'zoomandpan' => array(),
9394
+ ),
9395
+ 'tag_spec' => array(
9396
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
9397
+ ),
9398
+ ),
9399
+ ),
9400
+ 'switch' => array(
9401
+ array(
9402
+ 'attr_spec_list' => array(
9403
+ 'alignment-baseline' => array(),
9404
+ 'baseline-shift' => array(),
9405
+ 'clip' => array(),
9406
+ 'clip-path' => array(),
9407
+ 'clip-rule' => array(),
9408
+ 'color' => array(),
9409
+ 'color-interpolation' => array(),
9410
+ 'color-interpolation-filters' => array(),
9411
+ 'color-profile' => array(),
9412
+ 'color-rendering' => array(),
9413
+ 'cursor' => array(),
9414
+ 'direction' => array(),
9415
+ 'display' => array(),
9416
+ 'dominant-baseline' => array(),
9417
+ 'enable-background' => array(),
9418
+ 'fill' => array(),
9419
+ 'fill-opacity' => array(),
9420
+ 'fill-rule' => array(),
9421
+ 'filter' => array(),
9422
+ 'flood-color' => array(),
9423
+ 'flood-opacity' => array(),
9424
+ 'font-family' => array(),
9425
+ 'font-size' => array(),
9426
+ 'font-size-adjust' => array(),
9427
+ 'font-stretch' => array(),
9428
+ 'font-style' => array(),
9429
+ 'font-variant' => array(),
9430
+ 'font-weight' => array(),
9431
+ 'glyph-orientation-horizontal' => array(),
9432
+ 'glyph-orientation-vertical' => array(),
9433
+ 'image-rendering' => array(),
9434
+ 'kerning' => array(),
9435
+ 'letter-spacing' => array(),
9436
+ 'lighting-color' => array(),
9437
+ 'marker-end' => array(),
9438
+ 'marker-mid' => array(),
9439
+ 'marker-start' => array(),
9440
+ 'mask' => array(),
9441
+ 'opacity' => array(),
9442
+ 'overflow' => array(),
9443
+ 'pointer-events' => array(),
9444
+ 'requiredextensions' => array(),
9445
+ 'requiredfeatures' => array(),
9446
+ 'shape-rendering' => array(),
9447
+ 'stop-color' => array(),
9448
+ 'stop-opacity' => array(),
9449
+ 'stroke' => array(),
9450
+ 'stroke-dasharray' => array(),
9451
+ 'stroke-dashoffset' => array(),
9452
+ 'stroke-linecap' => array(),
9453
+ 'stroke-linejoin' => array(),
9454
+ 'stroke-miterlimit' => array(),
9455
+ 'stroke-opacity' => array(),
9456
+ 'stroke-width' => array(),
9457
+ 'style' => array(
9458
+ 'blacklisted_value_regex' => '!important',
9459
+ ),
9460
+ 'systemlanguage' => array(),
9461
+ 'text-anchor' => array(),
9462
+ 'text-decoration' => array(),
9463
+ 'text-rendering' => array(),
9464
+ 'unicode-bidi' => array(),
9465
+ 'vector-effect' => array(),
9466
+ 'visibility' => array(),
9467
+ 'word-spacing' => array(),
9468
+ 'writing-mode' => array(),
9469
+ 'xml:lang' => array(),
9470
+ 'xml:space' => array(),
9471
+ 'xmlns' => array(),
9472
+ 'xmlns:xlink' => array(),
9473
+ ),
9474
+ 'tag_spec' => array(
9475
+ 'mandatory_ancestor' => 'svg',
9476
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
9477
+ ),
9478
+ ),
9479
+ ),
9480
+ 'symbol' => array(
9481
+ array(
9482
+ 'attr_spec_list' => array(
9483
+ 'alignment-baseline' => array(),
9484
+ 'baseline-shift' => array(),
9485
+ 'clip' => array(),
9486
+ 'clip-path' => array(),
9487
+ 'clip-rule' => array(),
9488
+ 'color' => array(),
9489
+ 'color-interpolation' => array(),
9490
+ 'color-interpolation-filters' => array(),
9491
+ 'color-profile' => array(),
9492
+ 'color-rendering' => array(),
9493
+ 'cursor' => array(),
9494
+ 'direction' => array(),
9495
+ 'display' => array(),
9496
+ 'dominant-baseline' => array(),
9497
+ 'enable-background' => array(),
9498
+ 'externalresourcesrequired' => array(),
9499
+ 'fill' => array(),
9500
+ 'fill-opacity' => array(),
9501
+ 'fill-rule' => array(),
9502
+ 'filter' => array(),
9503
+ 'flood-color' => array(),
9504
+ 'flood-opacity' => array(),
9505
+ 'font-family' => array(),
9506
+ 'font-size' => array(),
9507
+ 'font-size-adjust' => array(),
9508
+ 'font-stretch' => array(),
9509
+ 'font-style' => array(),
9510
+ 'font-variant' => array(),
9511
+ 'font-weight' => array(),
9512
+ 'glyph-orientation-horizontal' => array(),
9513
+ 'glyph-orientation-vertical' => array(),
9514
+ 'image-rendering' => array(),
9515
+ 'kerning' => array(),
9516
+ 'letter-spacing' => array(),
9517
+ 'lighting-color' => array(),
9518
+ 'marker-end' => array(),
9519
+ 'marker-mid' => array(),
9520
+ 'marker-start' => array(),
9521
+ 'mask' => array(),
9522
+ 'opacity' => array(),
9523
+ 'overflow' => array(),
9524
+ 'pointer-events' => array(),
9525
+ 'preserveaspectratio' => array(),
9526
+ 'shape-rendering' => array(),
9527
+ 'stop-color' => array(),
9528
+ 'stop-opacity' => array(),
9529
+ 'stroke' => array(),
9530
+ 'stroke-dasharray' => array(),
9531
+ 'stroke-dashoffset' => array(),
9532
+ 'stroke-linecap' => array(),
9533
+ 'stroke-linejoin' => array(),
9534
+ 'stroke-miterlimit' => array(),
9535
+ 'stroke-opacity' => array(),
9536
+ 'stroke-width' => array(),
9537
+ 'style' => array(
9538
+ 'blacklisted_value_regex' => '!important',
9539
+ ),
9540
+ 'text-anchor' => array(),
9541
+ 'text-decoration' => array(),
9542
+ 'text-rendering' => array(),
9543
+ 'unicode-bidi' => array(),
9544
+ 'vector-effect' => array(),
9545
+ 'viewbox' => array(),
9546
+ 'visibility' => array(),
9547
+ 'word-spacing' => array(),
9548
+ 'writing-mode' => array(),
9549
+ 'xml:lang' => array(),
9550
+ 'xml:space' => array(),
9551
+ 'xmlns' => array(),
9552
+ 'xmlns:xlink' => array(),
9553
+ ),
9554
+ 'tag_spec' => array(
9555
+ 'mandatory_ancestor' => 'svg',
9556
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
9557
+ ),
9558
+ ),
9559
+ ),
9560
+ 'table' => array(
9561
+ array(
9562
+ 'attr_spec_list' => array(
9563
+ 'align' => array(),
9564
+ 'bgcolor' => array(),
9565
+ 'border' => array(
9566
+ 'value_regex' => '0|1',
9567
+ ),
9568
+ 'cellpadding' => array(),
9569
+ 'cellspacing' => array(),
9570
+ 'sortable' => array(),
9571
+ 'width' => array(),
9572
+ ),
9573
+ 'tag_spec' => array(),
9574
+ ),
9575
+ ),
9576
+ 'tbody' => array(
9577
+ array(
9578
+ 'attr_spec_list' => array(),
9579
+ 'tag_spec' => array(),
9580
+ ),
9581
+ ),
9582
+ 'td' => array(
9583
+ array(
9584
+ 'attr_spec_list' => array(
9585
+ 'align' => array(),
9586
+ 'bgcolor' => array(),
9587
+ 'colspan' => array(),
9588
+ 'headers' => array(),
9589
+ 'height' => array(),
9590
+ 'rowspan' => array(),
9591
+ 'valign' => array(),
9592
+ 'width' => array(),
9593
+ ),
9594
+ 'tag_spec' => array(),
9595
+ ),
9596
+ ),
9597
+ 'template' => array(
9598
+ array(
9599
+ 'attr_spec_list' => array(
9600
+ 'type' => array(
9601
+ 'mandatory' => true,
9602
+ 'value' => 'amp-mustache',
9603
+ ),
9604
+ ),
9605
+ 'tag_spec' => array(
9606
+ 'disallowed_ancestor' => array(
9607
+ 'template',
9608
+ 'amp-story-auto-ads',
9609
+ 'form > div [submit-success][template]',
9610
+ 'form > div [submit-error][template]',
9611
+ ),
9612
+ 'requires_extension' => array(
9613
+ 'amp-mustache',
9614
+ ),
9615
+ ),
9616
+ ),
9617
+ array(
9618
+ 'attr_spec_list' => array(
9619
+ 'type' => array(
9620
+ 'mandatory' => true,
9621
+ 'value' => 'amp-mustache',
9622
+ ),
9623
+ ),
9624
+ 'tag_spec' => array(
9625
+ 'mandatory_parent' => 'amp-story-auto-ads',
9626
+ 'requires_extension' => array(
9627
+ 'amp-mustache',
9628
+ ),
9629
+ 'spec_name' => 'amp-story-auto-ads > template',
9630
+ ),
9631
+ ),
9632
+ ),
9633
+ 'text' => array(
9634
+ array(
9635
+ 'attr_spec_list' => array(
9636
+ 'alignment-baseline' => array(),
9637
+ 'baseline-shift' => array(),
9638
+ 'clip' => array(),
9639
+ 'clip-path' => array(),
9640
+ 'clip-rule' => array(),
9641
+ 'color' => array(),
9642
+ 'color-interpolation' => array(),
9643
+ 'color-interpolation-filters' => array(),
9644
+ 'color-profile' => array(),
9645
+ 'color-rendering' => array(),
9646
+ 'cursor' => array(),
9647
+ 'direction' => array(),
9648
+ 'display' => array(),
9649
+ 'dominant-baseline' => array(),
9650
+ 'dx' => array(),
9651
+ 'dy' => array(),
9652
+ 'enable-background' => array(),
9653
+ 'externalresourcesrequired' => array(),
9654
+ 'fill' => array(),
9655
+ 'fill-opacity' => array(),
9656
+ 'fill-rule' => array(),
9657
+ 'filter' => array(),
9658
+ 'flood-color' => array(),
9659
+ 'flood-opacity' => array(),
9660
+ 'font-family' => array(),
9661
+ 'font-size' => array(),
9662
+ 'font-size-adjust' => array(),
9663
+ 'font-stretch' => array(),
9664
+ 'font-style' => array(),
9665
+ 'font-variant' => array(),
9666
+ 'font-weight' => array(),
9667
+ 'glyph-orientation-horizontal' => array(),
9668
+ 'glyph-orientation-vertical' => array(),
9669
+ 'image-rendering' => array(),
9670
+ 'kerning' => array(),
9671
+ 'lengthadjust' => array(),
9672
+ 'letter-spacing' => array(),
9673
+ 'lighting-color' => array(),
9674
+ 'marker-end' => array(),
9675
+ 'marker-mid' => array(),
9676
+ 'marker-start' => array(),
9677
+ 'mask' => array(),
9678
+ 'opacity' => array(),
9679
+ 'overflow' => array(),
9680
+ 'pointer-events' => array(),
9681
+ 'requiredextensions' => array(),
9682
+ 'requiredfeatures' => array(),
9683
+ 'rotate' => array(),
9684
+ 'shape-rendering' => array(),
9685
+ 'stop-color' => array(),
9686
+ 'stop-opacity' => array(),
9687
+ 'stroke' => array(),
9688
+ 'stroke-dasharray' => array(),
9689
+ 'stroke-dashoffset' => array(),
9690
+ 'stroke-linecap' => array(),
9691
+ 'stroke-linejoin' => array(),
9692
+ 'stroke-miterlimit' => array(),
9693
+ 'stroke-opacity' => array(),
9694
+ 'stroke-width' => array(),
9695
+ 'style' => array(
9696
+ 'blacklisted_value_regex' => '!important',
9697
+ ),
9698
+ 'systemlanguage' => array(),
9699
+ 'text-anchor' => array(),
9700
+ 'text-decoration' => array(),
9701
+ 'text-rendering' => array(),
9702
+ 'textlength' => array(),
9703
+ 'transform' => array(),
9704
+ 'unicode-bidi' => array(),
9705
+ 'vector-effect' => array(),
9706
+ 'visibility' => array(),
9707
+ 'word-spacing' => array(),
9708
+ 'writing-mode' => array(),
9709
+ 'x' => array(),
9710
+ 'xml:lang' => array(),
9711
+ 'xml:space' => array(),
9712
+ 'xmlns' => array(),
9713
+ 'xmlns:xlink' => array(),
9714
+ 'y' => array(),
9715
+ ),
9716
+ 'tag_spec' => array(
9717
+ 'mandatory_ancestor' => 'svg',
9718
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
9719
+ ),
9720
+ ),
9721
+ ),
9722
+ 'textarea' => array(
9723
+ array(
9724
+ 'attr_spec_list' => array(
9725
+ '[autocomplete]' => array(),
9726
+ '[autofocus]' => array(),
9727
+ '[cols]' => array(),
9728
+ '[disabled]' => array(),
9729
+ '[maxlength]' => array(),
9730
+ '[minlength]' => array(),
9731
+ '[placeholder]' => array(),
9732
+ '[readonly]' => array(),
9733
+ '[required]' => array(),
9734
+ '[rows]' => array(),
9735
+ '[selectiondirection]' => array(),
9736
+ '[selectionend]' => array(),
9737
+ '[selectionstart]' => array(),
9738
+ '[spellcheck]' => array(),
9739
+ '[wrap]' => array(),
9740
+ 'autocomplete' => array(),
9741
+ 'autofocus' => array(),
9742
+ 'cols' => array(),
9743
+ 'disabled' => array(),
9744
+ 'maxlength' => array(),
9745
+ 'minlength' => array(),
9746
+ 'name' => array(
9747
+ '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|$)',
9748
+ ),
9749
+ 'placeholder' => array(),
9750
+ 'readonly' => array(),
9751
+ 'required' => array(),
9752
+ 'rows' => array(),
9753
+ 'selectiondirection' => array(),
9754
+ 'selectionend' => array(),
9755
+ 'selectionstart' => array(),
9756
+ 'spellcheck' => array(),
9757
+ 'wrap' => array(),
9758
+ ),
9759
+ 'tag_spec' => array(
9760
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-form',
9761
+ ),
9762
+ ),
9763
+ ),
9764
+ 'textpath' => array(
9765
+ array(
9766
+ 'attr_spec_list' => array(
9767
+ 'alignment-baseline' => array(),
9768
+ 'baseline-shift' => array(),
9769
+ 'clip' => array(),
9770
+ 'clip-path' => array(),
9771
+ 'clip-rule' => array(),
9772
+ 'color' => array(),
9773
+ 'color-interpolation' => array(),
9774
+ 'color-interpolation-filters' => array(),
9775
+ 'color-profile' => array(),
9776
+ 'color-rendering' => array(),
9777
+ 'cursor' => array(),
9778
+ 'direction' => array(),
9779
+ 'display' => array(),
9780
+ 'dominant-baseline' => array(),
9781
+ 'enable-background' => array(),
9782
+ 'externalresourcesrequired' => array(),
9783
+ 'fill' => array(),
9784
+ 'fill-opacity' => array(),
9785
+ 'fill-rule' => array(),
9786
+ 'filter' => array(),
9787
+ 'flood-color' => array(),
9788
+ 'flood-opacity' => array(),
9789
+ 'font-family' => array(),
9790
+ 'font-size' => array(),
9791
+ 'font-size-adjust' => array(),
9792
+ 'font-stretch' => array(),
9793
+ 'font-style' => array(),
9794
+ 'font-variant' => array(),
9795
+ 'font-weight' => array(),
9796
+ 'glyph-orientation-horizontal' => array(),
9797
+ 'glyph-orientation-vertical' => array(),
9798
+ 'image-rendering' => array(),
9799
+ 'kerning' => array(),
9800
+ 'letter-spacing' => array(),
9801
+ 'lighting-color' => array(),
9802
+ 'marker-end' => array(),
9803
+ 'marker-mid' => array(),
9804
+ 'marker-start' => array(),
9805
+ 'mask' => array(),
9806
+ 'method' => array(),
9807
+ 'opacity' => array(),
9808
+ 'overflow' => array(),
9809
+ 'pointer-events' => array(),
9810
+ 'requiredextensions' => array(),
9811
+ 'requiredfeatures' => array(),
9812
+ 'shape-rendering' => array(),
9813
+ 'spacing' => array(),
9814
+ 'startoffset' => array(),
9815
+ 'stop-color' => array(),
9816
+ 'stop-opacity' => array(),
9817
+ 'stroke' => array(),
9818
+ 'stroke-dasharray' => array(),
9819
+ 'stroke-dashoffset' => array(),
9820
+ 'stroke-linecap' => array(),
9821
+ 'stroke-linejoin' => array(),
9822
+ 'stroke-miterlimit' => array(),
9823
+ 'stroke-opacity' => array(),
9824
+ 'stroke-width' => array(),
9825
+ 'style' => array(
9826
+ 'blacklisted_value_regex' => '!important',
9827
+ ),
9828
+ 'systemlanguage' => array(),
9829
+ 'text-anchor' => array(),
9830
+ 'text-decoration' => array(),
9831
+ 'text-rendering' => array(),
9832
+ 'unicode-bidi' => array(),
9833
+ 'vector-effect' => array(),
9834
+ 'visibility' => array(),
9835
+ 'word-spacing' => array(),
9836
+ 'writing-mode' => array(),
9837
+ 'xlink:actuate' => array(),
9838
+ 'xlink:arcrole' => array(),
9839
+ 'xlink:href' => array(
9840
+ 'alternative_names' => array(
9841
+ 'href',
9842
+ ),
9843
+ 'value_url' => array(
9844
+ 'allow_empty' => false,
9845
+ 'allow_relative' => true,
9846
+ 'allowed_protocol' => array(
9847
+ 'http',
9848
+ 'https',
9849
+ ),
9850
+ ),
9851
+ ),
9852
+ 'xlink:role' => array(),
9853
+ 'xlink:show' => array(),
9854
+ 'xlink:title' => array(),
9855
+ 'xlink:type' => array(),
9856
+ 'xml:lang' => array(),
9857
+ 'xml:space' => array(),
9858
+ 'xmlns' => array(),
9859
+ 'xmlns:xlink' => array(),
9860
+ ),
9861
+ 'tag_spec' => array(
9862
+ 'mandatory_ancestor' => 'svg',
9863
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
9864
+ ),
9865
+ ),
9866
+ ),
9867
+ 'tfoot' => array(
9868
+ array(
9869
+ 'attr_spec_list' => array(),
9870
+ 'tag_spec' => array(),
9871
+ ),
9872
+ ),
9873
+ 'th' => array(
9874
+ array(
9875
+ 'attr_spec_list' => array(
9876
+ 'abbr' => array(),
9877
+ 'align' => array(),
9878
+ 'bgcolor' => array(),
9879
+ 'colspan' => array(),
9880
+ 'headers' => array(),
9881
+ 'height' => array(),
9882
+ 'rowspan' => array(),
9883
+ 'scope' => array(),
9884
+ 'sorted' => array(),
9885
+ 'valign' => array(),
9886
+ 'width' => array(),
9887
+ ),
9888
+ 'tag_spec' => array(),
9889
+ ),
9890
+ ),
9891
+ 'thead' => array(
9892
+ array(
9893
+ 'attr_spec_list' => array(),
9894
+ 'tag_spec' => array(),
9895
+ ),
9896
+ ),
9897
+ 'time' => array(
9898
+ array(
9899
+ 'attr_spec_list' => array(
9900
+ 'datetime' => array(),
9901
+ ),
9902
+ 'tag_spec' => array(),
9903
+ ),
9904
+ ),
9905
+ 'title' => array(
9906
+ array(
9907
+ 'attr_spec_list' => array(),
9908
+ 'tag_spec' => array(
9909
+ 'spec_name' => 'title',
9910
+ ),
9911
+ ),
9912
+ array(
9913
+ 'attr_spec_list' => array(
9914
+ 'style' => array(
9915
+ 'blacklisted_value_regex' => '!important',
9916
+ ),
9917
+ 'xml:lang' => array(),
9918
+ 'xml:space' => array(),
9919
+ 'xmlns' => array(),
9920
+ 'xmlns:xlink' => array(),
9921
+ ),
9922
+ 'tag_spec' => array(
9923
+ 'mandatory_ancestor' => 'svg',
9924
+ 'spec_name' => 'svg title',
9925
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
9926
+ ),
9927
+ ),
9928
+ ),
9929
+ 'tr' => array(
9930
+ array(
9931
+ 'attr_spec_list' => array(
9932
+ 'align' => array(),
9933
+ 'bgcolor' => array(),
9934
+ 'height' => array(),
9935
+ 'valign' => array(),
9936
+ ),
9937
+ 'tag_spec' => array(),
9938
+ ),
9939
+ ),
9940
+ 'track' => array(
9941
+ array(
9942
+ 'attr_spec_list' => array(
9943
+ 'default' => array(
9944
+ 'value' => '',
9945
+ ),
9946
+ 'kind' => array(
9947
+ 'value_regex' => '(captions|descriptions|chapters|metadata)',
9948
+ ),
9949
+ 'label' => array(),
9950
+ 'src' => array(
9951
+ 'blacklisted_value_regex' => '__amp_source_origin',
9952
+ 'mandatory' => true,
9953
+ 'value_url' => array(
9954
+ 'allow_relative' => false,
9955
+ 'allowed_protocol' => array(
9956
+ 'https',
9957
+ ),
9958
+ ),
9959
+ ),
9960
+ 'srclang' => array(),
9961
+ ),
9962
+ 'tag_spec' => array(
9963
+ 'mandatory_parent' => 'audio',
9964
+ 'spec_name' => 'audio > track',
9965
+ ),
9966
+ ),
9967
+ array(
9968
+ 'attr_spec_list' => array(
9969
+ 'default' => array(
9970
+ 'value' => '',
9971
+ ),
9972
+ 'kind' => array(
9973
+ 'mandatory' => true,
9974
+ 'value_casei' => 'subtitles',
9975
+ ),
9976
+ 'label' => array(),
9977
+ 'src' => array(
9978
+ 'blacklisted_value_regex' => '__amp_source_origin',
9979
+ 'mandatory' => true,
9980
+ 'value_url' => array(
9981
+ 'allow_relative' => false,
9982
+ 'allowed_protocol' => array(
9983
+ 'https',
9984
+ ),
9985
+ ),
9986
+ ),
9987
+ 'srclang' => array(
9988
+ 'mandatory' => true,
9989
+ ),
9990
+ ),
9991
+ 'tag_spec' => array(
9992
+ 'mandatory_parent' => 'audio',
9993
+ 'spec_name' => 'audio > track[kind=subtitles]',
9994
+ ),
9995
+ ),
9996
+ array(
9997
+ 'attr_spec_list' => array(
9998
+ 'default' => array(
9999
+ 'value' => '',
10000
+ ),
10001
+ 'kind' => array(
10002
+ 'value_regex' => '(captions|descriptions|chapters|metadata)',
10003
+ ),
10004
+ 'label' => array(),
10005
+ 'src' => array(
10006
+ 'blacklisted_value_regex' => '__amp_source_origin',
10007
+ 'mandatory' => true,
10008
+ 'value_url' => array(
10009
+ 'allow_relative' => false,
10010
+ 'allowed_protocol' => array(
10011
+ 'https',
10012
+ ),
10013
+ ),
10014
+ ),
10015
+ 'srclang' => array(),
10016
+ ),
10017
+ 'tag_spec' => array(
10018
+ 'mandatory_parent' => 'video',
10019
+ 'spec_name' => 'video > track',
10020
+ ),
10021
+ ),
10022
+ array(
10023
+ 'attr_spec_list' => array(
10024
+ 'default' => array(
10025
+ 'value' => '',
10026
+ ),
10027
+ 'kind' => array(
10028
+ 'mandatory' => true,
10029
+ 'value_casei' => 'subtitles',
10030
+ ),
10031
+ 'label' => array(),
10032
+ 'src' => array(
10033
+ 'blacklisted_value_regex' => '__amp_source_origin',
10034
+ 'mandatory' => true,
10035
+ 'value_url' => array(
10036
+ 'allow_relative' => false,
10037
+ 'allowed_protocol' => array(
10038
+ 'https',
10039
+ ),
10040
+ ),
10041
+ ),
10042
+ 'srclang' => array(
10043
+ 'mandatory' => true,
10044
+ ),
10045
+ ),
10046
+ 'tag_spec' => array(
10047
+ 'mandatory_parent' => 'video',
10048
+ 'spec_name' => 'video > track[kind=subtitles]',
10049
+ ),
10050
+ ),
10051
+ array(
10052
+ 'attr_spec_list' => array(
10053
+ '[label]' => array(),
10054
+ '[src]' => array(),
10055
+ '[srclang]' => array(),
10056
+ 'default' => array(
10057
+ 'value' => '',
10058
+ ),
10059
+ 'kind' => array(
10060
+ 'value_regex' => '(captions|descriptions|chapters|metadata)',
10061
+ ),
10062
+ 'label' => array(),
10063
+ 'src' => array(
10064
+ 'blacklisted_value_regex' => '__amp_source_origin',
10065
+ 'mandatory' => true,
10066
+ 'value_url' => array(
10067
+ 'allow_relative' => false,
10068
+ 'allowed_protocol' => array(
10069
+ 'https',
10070
+ ),
10071
+ ),
10072
+ ),
10073
+ 'srclang' => array(),
10074
+ ),
10075
+ 'tag_spec' => array(
10076
+ 'mandatory_parent' => 'amp-audio',
10077
+ 'spec_name' => 'amp-audio > track',
10078
+ ),
10079
+ ),
10080
+ array(
10081
+ 'attr_spec_list' => array(
10082
+ '[label]' => array(),
10083
+ '[src]' => array(),
10084
+ '[srclang]' => array(),
10085
+ 'default' => array(
10086
+ 'value' => '',
10087
+ ),
10088
+ 'kind' => array(
10089
+ 'mandatory' => true,
10090
+ 'value_casei' => 'subtitles',
10091
+ ),
10092
+ 'label' => array(),
10093
+ 'src' => array(
10094
+ 'blacklisted_value_regex' => '__amp_source_origin',
10095
+ 'mandatory' => true,
10096
+ 'value_url' => array(
10097
+ 'allow_relative' => false,
10098
+ 'allowed_protocol' => array(
10099
+ 'https',
10100
+ ),
10101
+ ),
10102
+ ),
10103
+ 'srclang' => array(
10104
+ 'mandatory' => true,
10105
+ ),
10106
+ ),
10107
+ 'tag_spec' => array(
10108
+ 'mandatory_parent' => 'amp-audio',
10109
+ 'spec_name' => 'amp-audio > track[kind=subtitles]',
10110
+ ),
10111
+ ),
10112
+ array(
10113
+ 'attr_spec_list' => array(
10114
+ '[label]' => array(),
10115
+ '[src]' => array(),
10116
+ '[srclang]' => array(),
10117
+ 'default' => array(
10118
+ 'value' => '',
10119
+ ),
10120
+ 'kind' => array(
10121
+ 'value_regex' => '(captions|descriptions|chapters|metadata)',
10122
+ ),
10123
+ 'label' => array(),
10124
+ 'src' => array(
10125
+ 'blacklisted_value_regex' => '__amp_source_origin',
10126
+ 'mandatory' => true,
10127
+ 'value_url' => array(
10128
+ 'allow_relative' => false,
10129
+ 'allowed_protocol' => array(
10130
+ 'https',
10131
+ ),
10132
+ ),
10133
+ ),
10134
+ 'srclang' => array(),
10135
+ ),
10136
+ 'tag_spec' => array(
10137
+ 'mandatory_parent' => 'amp-video',
10138
+ 'spec_name' => 'amp-video > track',
10139
+ ),
10140
+ ),
10141
+ array(
10142
+ 'attr_spec_list' => array(
10143
+ '[label]' => array(),
10144
+ '[src]' => array(),
10145
+ '[srclang]' => array(),
10146
+ 'default' => array(
10147
+ 'value' => '',
10148
+ ),
10149
+ 'kind' => array(
10150
+ 'mandatory' => true,
10151
+ 'value_casei' => 'subtitles',
10152
+ ),
10153
+ 'label' => array(),
10154
+ 'src' => array(
10155
+ 'blacklisted_value_regex' => '__amp_source_origin',
10156
+ 'mandatory' => true,
10157
+ 'value_url' => array(
10158
+ 'allow_relative' => false,
10159
+ 'allowed_protocol' => array(
10160
+ 'https',
10161
+ ),
10162
+ ),
10163
+ ),
10164
+ 'srclang' => array(
10165
+ 'mandatory' => true,
10166
+ ),
10167
+ ),
10168
+ 'tag_spec' => array(
10169
+ 'mandatory_parent' => 'amp-video',
10170
+ 'spec_name' => 'amp-video > track[kind=subtitles]',
10171
+ ),
10172
+ ),
10173
+ array(
10174
+ 'attr_spec_list' => array(
10175
+ '[label]' => array(),
10176
+ '[src]' => array(),
10177
+ '[srclang]' => array(),
10178
+ 'default' => array(
10179
+ 'value' => '',
10180
+ ),
10181
+ 'kind' => array(
10182
+ 'mandatory' => true,
10183
+ 'value_casei' => 'subtitles',
10184
+ ),
10185
+ 'label' => array(),
10186
+ 'src' => array(
10187
+ 'blacklisted_value_regex' => '__amp_source_origin',
10188
+ 'mandatory' => true,
10189
+ 'value_url' => array(
10190
+ 'allow_relative' => false,
10191
+ 'allowed_protocol' => array(
10192
+ 'https',
10193
+ ),
10194
+ ),
10195
+ ),
10196
+ 'srclang' => array(
10197
+ 'mandatory' => true,
10198
+ ),
10199
+ ),
10200
+ 'tag_spec' => array(
10201
+ 'mandatory_parent' => 'amp-ima-video',
10202
+ 'spec_name' => 'amp-ima-video > track[kind=subtitles]',
10203
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-ima-video',
10204
+ ),
10205
+ ),
10206
+ ),
10207
+ 'tref' => array(
10208
+ array(
10209
+ 'attr_spec_list' => array(
10210
+ 'alignment-baseline' => array(),
10211
+ 'baseline-shift' => array(),
10212
+ 'clip' => array(),
10213
+ 'clip-path' => array(),
10214
+ 'clip-rule' => array(),
10215
+ 'color' => array(),
10216
+ 'color-interpolation' => array(),
10217
+ 'color-interpolation-filters' => array(),
10218
+ 'color-profile' => array(),
10219
+ 'color-rendering' => array(),
10220
+ 'cursor' => array(),
10221
+ 'direction' => array(),
10222
+ 'display' => array(),
10223
+ 'dominant-baseline' => array(),
10224
+ 'enable-background' => array(),
10225
+ 'externalresourcesrequired' => array(),
10226
+ 'fill' => array(),
10227
+ 'fill-opacity' => array(),
10228
+ 'fill-rule' => array(),
10229
+ 'filter' => array(),
10230
+ 'flood-color' => array(),
10231
+ 'flood-opacity' => array(),
10232
+ 'font-family' => array(),
10233
+ 'font-size' => array(),
10234
+ 'font-size-adjust' => array(),
10235
+ 'font-stretch' => array(),
10236
+ 'font-style' => array(),
10237
+ 'font-variant' => array(),
10238
+ 'font-weight' => array(),
10239
+ 'glyph-orientation-horizontal' => array(),
10240
+ 'glyph-orientation-vertical' => array(),
10241
+ 'image-rendering' => array(),
10242
+ 'kerning' => array(),
10243
+ 'letter-spacing' => array(),
10244
+ 'lighting-color' => array(),
10245
+ 'marker-end' => array(),
10246
+ 'marker-mid' => array(),
10247
+ 'marker-start' => array(),
10248
+ 'mask' => array(),
10249
+ 'opacity' => array(),
10250
+ 'overflow' => array(),
10251
+ 'pointer-events' => array(),
10252
+ 'requiredextensions' => array(),
10253
+ 'requiredfeatures' => array(),
10254
+ 'shape-rendering' => array(),
10255
+ 'stop-color' => array(),
10256
+ 'stop-opacity' => array(),
10257
+ 'stroke' => array(),
10258
+ 'stroke-dasharray' => array(),
10259
+ 'stroke-dashoffset' => array(),
10260
+ 'stroke-linecap' => array(),
10261
+ 'stroke-linejoin' => array(),
10262
+ 'stroke-miterlimit' => array(),
10263
+ 'stroke-opacity' => array(),
10264
+ 'stroke-width' => array(),
10265
+ 'style' => array(
10266
+ 'blacklisted_value_regex' => '!important',
10267
+ ),
10268
+ 'systemlanguage' => array(),
10269
+ 'text-anchor' => array(),
10270
+ 'text-decoration' => array(),
10271
+ 'text-rendering' => array(),
10272
+ 'unicode-bidi' => array(),
10273
+ 'vector-effect' => array(),
10274
+ 'visibility' => array(),
10275
+ 'word-spacing' => array(),
10276
+ 'writing-mode' => array(),
10277
+ 'xlink:actuate' => array(),
10278
+ 'xlink:arcrole' => array(),
10279
+ 'xlink:href' => array(
10280
+ 'alternative_names' => array(
10281
+ 'href',
10282
+ ),
10283
+ 'value_url' => array(
10284
+ 'allow_empty' => false,
10285
+ 'allow_relative' => true,
10286
+ 'allowed_protocol' => array(
10287
+ 'http',
10288
+ 'https',
10289
+ ),
10290
+ ),
10291
+ ),
10292
+ 'xlink:role' => array(),
10293
+ 'xlink:show' => array(),
10294
+ 'xlink:title' => array(),
10295
+ 'xlink:type' => array(),
10296
+ 'xml:lang' => array(),
10297
+ 'xml:space' => array(),
10298
+ 'xmlns' => array(),
10299
+ 'xmlns:xlink' => array(),
10300
+ ),
10301
+ 'tag_spec' => array(
10302
+ 'mandatory_ancestor' => 'svg',
10303
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
10304
+ ),
10305
+ ),
10306
+ ),
10307
+ 'tspan' => array(
10308
+ array(
10309
+ 'attr_spec_list' => array(
10310
+ 'alignment-baseline' => array(),
10311
+ 'baseline-shift' => array(),
10312
+ 'clip' => array(),
10313
+ 'clip-path' => array(),
10314
+ 'clip-rule' => array(),
10315
+ 'color' => array(),
10316
+ 'color-interpolation' => array(),
10317
+ 'color-interpolation-filters' => array(),
10318
+ 'color-profile' => array(),
10319
+ 'color-rendering' => array(),
10320
+ 'cursor' => array(),
10321
+ 'direction' => array(),
10322
+ 'display' => array(),
10323
+ 'dominant-baseline' => array(),
10324
+ 'dx' => array(),
10325
+ 'dy' => array(),
10326
+ 'enable-background' => array(),
10327
+ 'externalresourcesrequired' => array(),
10328
+ 'fill' => array(),
10329
+ 'fill-opacity' => array(),
10330
+ 'fill-rule' => array(),
10331
+ 'filter' => array(),
10332
+ 'flood-color' => array(),
10333
+ 'flood-opacity' => array(),
10334
+ 'font-family' => array(),
10335
+ 'font-size' => array(),
10336
+ 'font-size-adjust' => array(),
10337
+ 'font-stretch' => array(),
10338
+ 'font-style' => array(),
10339
+ 'font-variant' => array(),
10340
+ 'font-weight' => array(),
10341
+ 'glyph-orientation-horizontal' => array(),
10342
+ 'glyph-orientation-vertical' => array(),
10343
+ 'image-rendering' => array(),
10344
+ 'kerning' => array(),
10345
+ 'lengthadjust' => array(),
10346
+ 'letter-spacing' => array(),
10347
+ 'lighting-color' => array(),
10348
+ 'marker-end' => array(),
10349
+ 'marker-mid' => array(),
10350
+ 'marker-start' => array(),
10351
+ 'mask' => array(),
10352
+ 'opacity' => array(),
10353
+ 'overflow' => array(),
10354
+ 'pointer-events' => array(),
10355
+ 'requiredextensions' => array(),
10356
+ 'requiredfeatures' => array(),
10357
+ 'rotate' => array(),
10358
+ 'shape-rendering' => array(),
10359
+ 'stop-color' => array(),
10360
+ 'stop-opacity' => array(),
10361
+ 'stroke' => array(),
10362
+ 'stroke-dasharray' => array(),
10363
+ 'stroke-dashoffset' => array(),
10364
+ 'stroke-linecap' => array(),
10365
+ 'stroke-linejoin' => array(),
10366
+ 'stroke-miterlimit' => array(),
10367
+ 'stroke-opacity' => array(),
10368
+ 'stroke-width' => array(),
10369
+ 'style' => array(
10370
+ 'blacklisted_value_regex' => '!important',
10371
+ ),
10372
+ 'systemlanguage' => array(),
10373
+ 'text-anchor' => array(),
10374
+ 'text-decoration' => array(),
10375
+ 'text-rendering' => array(),
10376
+ 'textlength' => array(),
10377
+ 'unicode-bidi' => array(),
10378
+ 'vector-effect' => array(),
10379
+ 'visibility' => array(),
10380
+ 'word-spacing' => array(),
10381
+ 'writing-mode' => array(),
10382
+ 'x' => array(),
10383
+ 'xml:lang' => array(),
10384
+ 'xml:space' => array(),
10385
+ 'xmlns' => array(),
10386
+ 'xmlns:xlink' => array(),
10387
+ 'y' => array(),
10388
+ ),
10389
+ 'tag_spec' => array(
10390
+ 'mandatory_ancestor' => 'svg',
10391
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
10392
+ ),
10393
+ ),
10394
+ ),
10395
+ 'tt' => array(
10396
+ array(
10397
+ 'attr_spec_list' => array(),
10398
+ 'tag_spec' => array(),
10399
+ ),
10400
+ ),
10401
+ 'u' => array(
10402
+ array(
10403
+ 'attr_spec_list' => array(),
10404
+ 'tag_spec' => array(),
10405
+ ),
10406
+ ),
10407
+ 'ul' => array(
10408
+ array(
10409
+ 'attr_spec_list' => array(),
10410
+ 'tag_spec' => array(),
10411
+ ),
10412
+ ),
10413
+ 'use' => array(
10414
+ array(
10415
+ 'attr_spec_list' => array(
10416
+ 'alignment-baseline' => array(),
10417
+ 'baseline-shift' => array(),
10418
+ 'clip' => array(),
10419
+ 'clip-path' => array(),
10420
+ 'clip-rule' => array(),
10421
+ 'color' => array(),
10422
+ 'color-interpolation' => array(),
10423
+ 'color-interpolation-filters' => array(),
10424
+ 'color-profile' => array(),
10425
+ 'color-rendering' => array(),
10426
+ 'cursor' => array(),
10427
+ 'direction' => array(),
10428
+ 'display' => array(),
10429
+ 'dominant-baseline' => array(),
10430
+ 'enable-background' => array(),
10431
+ 'externalresourcesrequired' => array(),
10432
+ 'fill' => array(),
10433
+ 'fill-opacity' => array(),
10434
+ 'fill-rule' => array(),
10435
+ 'filter' => array(),
10436
+ 'flood-color' => array(),
10437
+ 'flood-opacity' => array(),
10438
+ 'font-family' => array(),
10439
+ 'font-size' => array(),
10440
+ 'font-size-adjust' => array(),
10441
+ 'font-stretch' => array(),
10442
+ 'font-style' => array(),
10443
+ 'font-variant' => array(),
10444
+ 'font-weight' => array(),
10445
+ 'glyph-orientation-horizontal' => array(),
10446
+ 'glyph-orientation-vertical' => array(),
10447
+ 'height' => array(),
10448
+ 'image-rendering' => array(),
10449
+ 'kerning' => array(),
10450
+ 'letter-spacing' => array(),
10451
+ 'lighting-color' => array(),
10452
+ 'marker-end' => array(),
10453
+ 'marker-mid' => array(),
10454
+ 'marker-start' => array(),
10455
+ 'mask' => array(),
10456
+ 'opacity' => array(),
10457
+ 'overflow' => array(),
10458
+ 'pointer-events' => array(),
10459
+ 'requiredextensions' => array(),
10460
+ 'requiredfeatures' => array(),
10461
+ 'shape-rendering' => array(),
10462
+ 'stop-color' => array(),
10463
+ 'stop-opacity' => array(),
10464
+ 'stroke' => array(),
10465
+ 'stroke-dasharray' => array(),
10466
+ 'stroke-dashoffset' => array(),
10467
+ 'stroke-linecap' => array(),
10468
+ 'stroke-linejoin' => array(),
10469
+ 'stroke-miterlimit' => array(),
10470
+ 'stroke-opacity' => array(),
10471
+ 'stroke-width' => array(),
10472
+ 'style' => array(
10473
+ 'blacklisted_value_regex' => '!important',
10474
+ ),
10475
+ 'systemlanguage' => array(),
10476
+ 'text-anchor' => array(),
10477
+ 'text-decoration' => array(),
10478
+ 'text-rendering' => array(),
10479
+ 'transform' => array(),
10480
+ 'unicode-bidi' => array(),
10481
+ 'vector-effect' => array(),
10482
+ 'visibility' => array(),
10483
+ 'width' => array(),
10484
+ 'word-spacing' => array(),
10485
+ 'writing-mode' => array(),
10486
+ 'x' => array(),
10487
+ 'xlink:actuate' => array(),
10488
+ 'xlink:arcrole' => array(),
10489
+ 'xlink:href' => array(
10490
+ 'alternative_names' => array(
10491
+ 'href',
10492
+ ),
10493
+ 'value_url' => array(
10494
+ 'allow_empty' => false,
10495
+ 'allow_relative' => true,
10496
+ 'allowed_protocol' => array(
10497
+ 'http',
10498
+ 'https',
10499
+ ),
10500
+ ),
10501
+ ),
10502
+ 'xlink:role' => array(),
10503
+ 'xlink:show' => array(),
10504
+ 'xlink:title' => array(),
10505
+ 'xlink:type' => array(),
10506
+ 'xml:lang' => array(),
10507
+ 'xml:space' => array(),
10508
+ 'xmlns' => array(),
10509
+ 'xmlns:xlink' => array(),
10510
+ 'y' => array(),
10511
+ ),
10512
+ 'tag_spec' => array(
10513
+ 'mandatory_ancestor' => 'svg',
10514
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
10515
+ ),
10516
+ ),
10517
+ ),
10518
+ 'var' => array(
10519
+ array(
10520
+ 'attr_spec_list' => array(),
10521
+ 'tag_spec' => array(),
10522
+ ),
10523
+ ),
10524
+ 'video' => array(
10525
+ array(
10526
+ 'attr_spec_list' => array(
10527
+ 'autoplay' => array(),
10528
+ 'controls' => array(),
10529
+ 'height' => array(),
10530
+ 'loop' => array(),
10531
+ 'muted' => array(),
10532
+ 'playsinline' => array(),
10533
+ 'poster' => array(),
10534
+ 'preload' => array(),
10535
+ 'src' => array(
10536
+ 'blacklisted_value_regex' => '__amp_source_origin',
10537
+ 'value_url' => array(
10538
+ 'allow_relative' => false,
10539
+ 'allowed_protocol' => array(
10540
+ 'data',
10541
+ 'https',
10542
+ ),
10543
+ ),
10544
+ ),
10545
+ 'width' => array(),
10546
+ ),
10547
+ 'tag_spec' => array(
10548
+ 'mandatory_ancestor' => 'noscript',
10549
+ 'mandatory_ancestor_suggested_alternative' => 'amp-video',
10550
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-video',
10551
+ ),
10552
+ ),
10553
+ ),
10554
+ 'view' => array(
10555
+ array(
10556
+ 'attr_spec_list' => array(
10557
+ 'externalresourcesrequired' => array(),
10558
+ 'preserveaspectratio' => array(),
10559
+ 'style' => array(
10560
+ 'blacklisted_value_regex' => '!important',
10561
+ ),
10562
+ 'viewbox' => array(),
10563
+ 'viewtarget' => array(),
10564
+ 'xml:lang' => array(),
10565
+ 'xml:space' => array(),
10566
+ 'xmlns' => array(),
10567
+ 'xmlns:xlink' => array(),
10568
+ 'zoomandpan' => array(),
10569
+ ),
10570
+ 'tag_spec' => array(
10571
+ 'mandatory_ancestor' => 'svg',
10572
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
10573
+ ),
10574
+ ),
10575
+ ),
10576
+ 'vkern' => array(
10577
+ array(
10578
+ 'attr_spec_list' => array(
10579
+ 'g1' => array(),
10580
+ 'g2' => array(),
10581
+ 'k' => array(),
10582
+ 'style' => array(
10583
+ 'blacklisted_value_regex' => '!important',
10584
+ ),
10585
+ 'u1' => array(),
10586
+ 'u2' => array(),
10587
+ 'xml:lang' => array(),
10588
+ 'xml:space' => array(),
10589
+ 'xmlns' => array(),
10590
+ 'xmlns:xlink' => array(),
10591
+ ),
10592
+ 'tag_spec' => array(
10593
+ 'mandatory_ancestor' => 'svg',
10594
+ 'spec_url' => 'https://www.ampproject.org/docs/reference/spec#svg',
10595
+ ),
10596
+ ),
10597
+ ),
10598
+ 'wbr' => array(
10599
+ array(
10600
+ 'attr_spec_list' => array(),
10601
+ 'tag_spec' => array(),
10602
+ ),
10603
+ ),
10604
+ 'xmp' => array(
10605
+ array(
10606
+ 'attr_spec_list' => array(),
10607
+ 'tag_spec' => array(),
10608
+ ),
10609
+ ),
10610
+ );
10611
+
10612
+ private static $layout_allowed_attrs = array(
10613
+ '[height]' => array(),
10614
+ '[width]' => array(),
10615
+ 'height' => array(),
10616
+ 'heights' => array(),
10617
+ 'layout' => array(),
10618
+ 'sizes' => array(),
10619
+ 'width' => array(),
10620
+ );
10621
+
10622
+
10623
+ private static $globally_allowed_attrs = array(
10624
+ '[aria-activedescendant]' => array(),
10625
+ '[aria-atomic]' => array(),
10626
+ '[aria-autocomplete]' => array(),
10627
+ '[aria-busy]' => array(),
10628
+ '[aria-checked]' => array(),
10629
+ '[aria-controls]' => array(),
10630
+ '[aria-describedby]' => array(),
10631
+ '[aria-disabled]' => array(),
10632
+ '[aria-dropeffect]' => array(),
10633
+ '[aria-expanded]' => array(),
10634
+ '[aria-flowto]' => array(),
10635
+ '[aria-grabbed]' => array(),
10636
+ '[aria-haspopup]' => array(),
10637
+ '[aria-hidden]' => array(),
10638
+ '[aria-invalid]' => array(),
10639
+ '[aria-label]' => array(),
10640
+ '[aria-labelledby]' => array(),
10641
+ '[aria-level]' => array(),
10642
+ '[aria-live]' => array(),
10643
+ '[aria-multiline]' => array(),
10644
+ '[aria-multiselectable]' => array(),
10645
+ '[aria-orientation]' => array(),
10646
+ '[aria-owns]' => array(),
10647
+ '[aria-posinset]' => array(),
10648
+ '[aria-pressed]' => array(),
10649
+ '[aria-readonly]' => array(),
10650
+ '[aria-relevant]' => array(),
10651
+ '[aria-required]' => array(),
10652
+ '[aria-selected]' => array(),
10653
+ '[aria-setsize]' => array(),
10654
+ '[aria-sort]' => array(),
10655
+ '[aria-valuemax]' => array(),
10656
+ '[aria-valuemin]' => array(),
10657
+ '[aria-valuenow]' => array(),
10658
+ '[aria-valuetext]' => array(),
10659
+ '[class]' => array(),
10660
+ '[hidden]' => array(),
10661
+ '[text]' => array(),
10662
+ 'about' => array(),
10663
+ 'accesskey' => array(),
10664
+ 'amp-access' => array(),
10665
+ 'amp-access-behavior' => array(),
10666
+ 'amp-access-hide' => array(),
10667
+ 'amp-access-id' => array(),
10668
+ 'amp-access-loader' => array(),
10669
+ 'amp-access-loading' => array(),
10670
+ 'amp-access-off' => array(),
10671
+ 'amp-access-on' => array(),
10672
+ 'amp-access-show' => array(),
10673
+ 'amp-access-style' => array(),
10674
+ 'amp-access-template' => array(),
10675
+ 'amp-fx' => array(
10676
+ 'value_casei' => 'parallax',
10677
+ ),
10678
+ 'aria-activedescendant' => array(),
10679
+ 'aria-atomic' => array(),
10680
+ 'aria-autocomplete' => array(),
10681
+ 'aria-busy' => array(),
10682
+ 'aria-checked' => array(),
10683
+ 'aria-controls' => array(),
10684
+ 'aria-current' => array(),
10685
+ 'aria-describedby' => array(),
10686
+ 'aria-disabled' => array(),
10687
+ 'aria-dropeffect' => array(),
10688
+ 'aria-expanded' => array(),
10689
+ 'aria-flowto' => array(),
10690
+ 'aria-grabbed' => array(),
10691
+ 'aria-haspopup' => array(),
10692
+ 'aria-hidden' => array(),
10693
+ 'aria-invalid' => array(),
10694
+ 'aria-label' => array(),
10695
+ 'aria-labelledby' => array(),
10696
+ 'aria-level' => array(),
10697
+ 'aria-live' => array(),
10698
+ 'aria-multiline' => array(),
10699
+ 'aria-multiselectable' => array(),
10700
+ 'aria-orientation' => array(),
10701
+ 'aria-owns' => array(),
10702
+ 'aria-posinset' => array(),
10703
+ 'aria-pressed' => array(),
10704
+ 'aria-readonly' => array(),
10705
+ 'aria-relevant' => array(),
10706
+ 'aria-required' => array(),
10707
+ 'aria-selected' => array(),
10708
+ 'aria-setsize' => array(),
10709
+ 'aria-sort' => array(),
10710
+ 'aria-valuemax' => array(),
10711
+ 'aria-valuemin' => array(),
10712
+ 'aria-valuenow' => array(),
10713
+ 'aria-valuetext' => array(),
10714
+ 'class' => array(
10715
+ 'blacklisted_value_regex' => '(^|\\W)i-amphtml-',
10716
+ ),
10717
+ 'content' => array(),
10718
+ 'datatype' => array(),
10719
+ 'dir' => array(),
10720
+ 'draggable' => array(),
10721
+ 'fallback' => array(
10722
+ 'value' => '',
10723
+ ),
10724
+ 'hidden' => array(
10725
+ 'value' => '',
10726
+ ),
10727
+ 'i-amp-access-id' => array(),
10728
+ 'id' => array(
10729
+ '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|$)',
10730
+ ),
10731
+ 'inlist' => array(),
10732
+ 'itemid' => array(),
10733
+ 'itemprop' => array(),
10734
+ 'itemref' => array(),
10735
+ 'itemscope' => array(),
10736
+ 'itemtype' => array(),
10737
+ 'lang' => array(),
10738
+ 'lightbox' => array(),
10739
+ 'on' => array(),
10740
+ 'overflow' => array(),
10741
+ 'placeholder' => array(
10742
+ 'value' => '',
10743
+ ),
10744
+ 'prefix' => array(),
10745
+ 'property' => array(),
10746
+ 'rel' => array(
10747
+ 'blacklisted_value_regex' => '(^|\\s)(canonical|components|dns-prefetch|import|manifest|preconnect|preload|prerender|serviceworker|stylesheet|subresource)(\\s|$)',
10748
+ ),
10749
+ 'resource' => array(),
10750
+ 'rev' => array(),
10751
+ 'role' => array(),
10752
+ 'subscriptions-action' => array(),
10753
+ 'subscriptions-actions' => array(
10754
+ 'value' => '',
10755
+ ),
10756
+ 'subscriptions-dialog' => array(
10757
+ 'value' => '',
10758
+ ),
10759
+ 'subscriptions-display' => array(),
10760
+ 'subscriptions-section' => array(
10761
+ 'value_regex_casei' => '(actions|content|content-not-granted)',
10762
+ ),
10763
+ 'subscriptions-service' => array(),
10764
+ 'tabindex' => array(),
10765
+ 'title' => array(),
10766
+ 'translate' => array(),
10767
+ 'typeof' => array(),
10768
+ 'validation-for' => array(),
10769
+ 'visible-when-invalid' => array(
10770
+ 'value_regex' => '(badInput|customError|patternMismatch|rangeOverflow|rangeUnderflow|stepMismatch|tooLong|typeMismatch|valueMissing)',
10771
+ ),
10772
+ 'vocab' => array(),
10773
+ );
10774
+
10775
+
10776
+ /**
10777
+ * Get allowed tags.
10778
+ *
10779
+ * @since 0.5
10780
+ * @return array Allowed tags.
10781
+ */
10782
+ public static function get_allowed_tags() {
10783
+ return self::$allowed_tags;
10784
+ }
10785
+
10786
+ /**
10787
+ * Get allowed tag.
10788
+ *
10789
+ * Get the rules for a single tag so that the entire data structure needn't be passed around.
10790
+ *
10791
+ * @since 0.7
10792
+ * @param string $node_name Tag name.
10793
+ * @return array|null Allowed tag, or null if the tag does not exist.
10794
+ */
10795
+ public static function get_allowed_tag( $node_name ) {
10796
+ if ( isset( self::$allowed_tags[ $node_name ] ) ) {
10797
+ return self::$allowed_tags[ $node_name ];
10798
+ }
10799
+ return null;
10800
+ }
10801
+
10802
+ /**
10803
+ * Get list of globally-allowed attributes.
10804
+ *
10805
+ * @since 0.5
10806
+ * @return array Allowed tag.
10807
+ */
10808
+ public static function get_allowed_attributes() {
10809
+ return self::$globally_allowed_attrs;
10810
+ }
10811
+
10812
+ /**
10813
+ * Get layout attributes.
10814
+ *
10815
+ * @since 0.5
10816
+ * @return array Allowed tag.
10817
+ */
10818
+ public static function get_layout_attributes() {
10819
+ return self::$layout_allowed_attrs;
10820
+ }
10821
+
10822
+ }
includes/vendor/vendor-files/class-amp-response-headers.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Response_Headers
4
+ *
5
+ * @since 1.0
6
+ * @package AMP
7
+ */
8
+
9
+ /**
10
+ * Class AMP_Response_Headers
11
+ */
12
+ class AMP_Response_Headers {
13
+
14
+ /**
15
+ * Headers sent (or attempted to be sent).
16
+ *
17
+ * @since 1.0
18
+ * @see AMP_Theme_Support::send_header()
19
+ * @var array[]
20
+ */
21
+ public static $headers_sent = array();
22
+
23
+ /**
24
+ * Send an HTTP response header.
25
+ *
26
+ * This largely exists to facilitate unit testing but it also provides a better interface for sending headers.
27
+ *
28
+ * @since 0.7.0
29
+ *
30
+ * @param string $name Header name.
31
+ * @param string $value Header value.
32
+ * @param array $args {
33
+ * Args to header().
34
+ *
35
+ * @type bool $replace Whether to replace a header previously sent. Default true.
36
+ * @type int $status_code Status code to send with the sent header.
37
+ * }
38
+ * @return bool Whether the header was sent.
39
+ */
40
+ public static function send_header( $name, $value, $args = array() ) {
41
+ $args = array_merge(
42
+ array(
43
+ 'replace' => true,
44
+ 'status_code' => null,
45
+ ),
46
+ $args
47
+ );
48
+
49
+ self::$headers_sent[] = array_merge( compact( 'name', 'value' ), $args );
50
+ if ( headers_sent() ) {
51
+ return false;
52
+ }
53
+
54
+ header(
55
+ sprintf( '%s: %s', $name, $value ),
56
+ $args['replace'],
57
+ $args['status_code']
58
+ );
59
+ return true;
60
+ }
61
+
62
+ /**
63
+ * Send Server-Timing header.
64
+ *
65
+ * @since 1.0
66
+ * @todo What is the ordering in Chrome dev tools? What are the colors about?
67
+ * @todo Is there a better name standardization?
68
+ * @todo Is there a way to indicate nested server timings, so an outer method's own time can be seen separately from the inner method's time?
69
+ *
70
+ * @param string $name Name.
71
+ * @param float $duration Duration. If negative, will be added to microtime( true ). Optional.
72
+ * @param string $description Description. Optional.
73
+ * @return bool Return value of send_header call.
74
+ */
75
+ public static function send_server_timing( $name, $duration = null, $description = null ) {
76
+ $value = $name;
77
+ if ( isset( $description ) ) {
78
+ $value .= sprintf( ';desc=%s', wp_json_encode( $description ) );
79
+ }
80
+ if ( isset( $duration ) ) {
81
+ if ( $duration < 0 ) {
82
+ $duration = microtime( true ) + $duration;
83
+ }
84
+ $value .= sprintf( ';dur=%f', $duration * 1000 );
85
+ }
86
+ return self::send_header( 'Server-Timing', $value, array( 'replace' => false ) );
87
+ }
88
+ }
includes/vendor/vendor-files/class-amp-theme-support.php ADDED
@@ -0,0 +1,1148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * Start time when init was called.
72
+ *
73
+ * @since 1.0
74
+ * @var float
75
+ */
76
+ public static $init_start_time;
77
+
78
+ /**
79
+ * Initialize.
80
+ *
81
+ * @since 0.7
82
+ */
83
+ public static function init() {
84
+ if ( ! start_non_amp_to_amp_conversion( ) ) { //if ( ! current_theme_supports( 'amp' ) ) { /*Changed */
85
+ return;
86
+ }
87
+ self::$init_start_time = microtime( true );
88
+
89
+ self::purge_amp_query_vars();
90
+ self::handle_xhr_request();
91
+
92
+ require_once AMP__DIR__ . '/includes/amp-post-template-actions.php';
93
+
94
+ // Validate theme support usage.
95
+ $support = get_theme_support( 'amp' );
96
+ if ( WP_DEBUG && is_array( $support ) ) {
97
+ $args = array_shift( $support );
98
+ if ( ! is_array( $args ) ) {
99
+ trigger_error( esc_html__( 'Expected AMP theme support arg to be array.', 'amp' ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
100
+ } elseif ( count( array_diff( array_keys( $args ), array( 'template_dir', 'available_callback', 'comments_live_list' ) ) ) !== 0 ) {
101
+ 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
102
+ }
103
+ }
104
+
105
+ add_action( 'widgets_init', array( __CLASS__, 'register_widgets' ) );
106
+
107
+ /*
108
+ * Note that wp action is use instead of template_redirect because some themes/plugins output
109
+ * the response at this action and then short-circuit with exit. So this is why the the preceding
110
+ * action to template_redirect--the wp action--is used instead.
111
+ */
112
+ add_action( 'wp', array( __CLASS__, 'finish_init' ), PHP_INT_MAX );
113
+ }
114
+
115
+ /**
116
+ * Finish initialization once query vars are set.
117
+ *
118
+ * @since 0.7
119
+ */
120
+ public static function finish_init() {
121
+ /*if ( ! is_amp_endpoint() ) {
122
+ // Add amphtml link when paired mode is available.
123
+ if ( self::is_paired_available() ) {
124
+ amp_add_frontend_actions(); // @todo This function is poor in how it requires a file that then does add_action().
125
+ if ( ! has_action( 'wp_head', 'amp_frontend_add_canonical' ) ) {
126
+ add_action( 'wp_head', 'amp_frontend_add_canonical' );
127
+ }
128
+ }
129
+ return;
130
+ }*/
131
+ //echo "called";die;
132
+ if ( amp_is_canonical() ) {
133
+ self::redirect_canonical_amp();
134
+ } else {
135
+ self::register_paired_hooks();
136
+ }
137
+
138
+ self::add_hooks();
139
+ self::$sanitizer_classes = amp_get_content_sanitizers();
140
+ self::$embed_handlers = self::register_content_embed_handlers();
141
+ }
142
+
143
+ /**
144
+ * Redirect to canonical URL if the AMP URL was loaded, since canonical is now AMP.
145
+ *
146
+ * @since 0.7
147
+ */
148
+ public static function redirect_canonical_amp() {
149
+ if ( false !== get_query_var( amp_get_slug(), false ) ) { // Because is_amp_endpoint() now returns true if amp_is_canonical().
150
+ $url = preg_replace( '#^(https?://.+?)(/.*)$#', '$1', home_url( '/' ) );
151
+ if ( isset( $_SERVER['REQUEST_URI'] ) ) {
152
+ $url .= wp_unslash( $_SERVER['REQUEST_URI'] );
153
+ }
154
+
155
+ $url = amp_remove_endpoint( $url );
156
+
157
+ wp_safe_redirect( $url, 302 ); // Temporary redirect because canonical may change in future.
158
+ exit;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Determines whether paired mode is available.
164
+ *
165
+ * When 'amp' theme support has not been added or canonical mode is enabled, then this returns false.
166
+ * Returns true when there is a template_dir defined in theme support, and if a defined available_callback
167
+ * returns true.
168
+ *
169
+ * @return bool Whether available.
170
+ */
171
+ public static function is_paired_available() {
172
+ $support = array();//get_theme_support( 'amp' );
173
+ /*if ( empty( $support ) || amp_is_canonical() ) {
174
+ return false;
175
+ }*/
176
+ if ( is_singular() && ! post_supports_amp( get_queried_object() ) ) {
177
+ return false;
178
+ }
179
+
180
+ $args = array_shift( $support );
181
+
182
+ if ( isset( $args['available_callback'] ) && is_callable( $args['available_callback'] ) ) {
183
+ return call_user_func( $args['available_callback'] );
184
+ }
185
+ return true;
186
+ }
187
+
188
+ /**
189
+ * Determine whether the user is in the Customizer preview iframe.
190
+ *
191
+ * @since 0.7
192
+ *
193
+ * @return bool Whether in Customizer preview iframe.
194
+ */
195
+ public static function is_customize_preview_iframe() {
196
+ global $wp_customize;
197
+ return is_customize_preview() && $wp_customize->get_messenger_channel();
198
+ }
199
+
200
+ /**
201
+ * Register hooks for paired mode.
202
+ */
203
+ public static function register_paired_hooks() {
204
+ foreach ( self::$template_types as $template_type ) {
205
+ add_filter( "{$template_type}_template_hierarchy", array( __CLASS__, 'filter_paired_template_hierarchy' ) );
206
+ }
207
+ add_filter( 'template_include', array( __CLASS__, 'filter_paired_template_include' ), 100 );
208
+ }
209
+
210
+ /**
211
+ * Register hooks.
212
+ */
213
+ public static function add_hooks() {
214
+
215
+ add_filter( 'amp_content_sanitizers', function( $sanitizers ) {
216
+ $sanitizers['AMP_Core_Theme_Sanitizer'] = array(
217
+ 'template' => get_template(),
218
+ 'stylesheet' => get_stylesheet(),
219
+ );
220
+ return $sanitizers;
221
+ } );
222
+
223
+ // Remove core actions which are invalid AMP.
224
+ remove_action( 'wp_head', 'wp_post_preview_js', 1 );
225
+ remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
226
+ remove_action( 'wp_print_styles', 'print_emoji_styles' );
227
+ remove_action( 'wp_head', 'wp_oembed_add_host_js' );
228
+
229
+ // Prevent MediaElement.js scripts/styles from being enqueued.
230
+ add_filter( 'wp_video_shortcode_library', function() {
231
+ return 'amp';
232
+ } );
233
+ add_filter( 'wp_audio_shortcode_library', function() {
234
+ return 'amp';
235
+ } );
236
+
237
+ /*
238
+ * Add additional markup required by AMP <https://www.ampproject.org/docs/reference/spec#required-markup>.
239
+ * Note that the meta[name=viewport] is not added here because a theme may want to define one with additional
240
+ * properties than included in the default configuration. If a theme doesn't include one, then the meta viewport
241
+ * will be added when output buffering is finished. Note that meta charset _is_ output here because the output
242
+ * buffer will need it to parse the document properly, and it must be exactly as is to be valid AMP. Nevertheless,
243
+ * in this case too we should defer to the theme as well to output the meta charset because it is possible the
244
+ * install is not on utf-8 and we may need to do a encoding conversion.
245
+ */
246
+ add_action( 'wp_print_styles', array( __CLASS__, 'print_amp_styles' ), 0 ); // Print boilerplate before theme and plugin stylesheets.
247
+ add_action( 'wp_head', 'amp_add_generator_metadata', 20 );
248
+
249
+ add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
250
+ add_action( 'wp_enqueue_scripts', array( __CLASS__, 'dequeue_customize_preview_scripts' ), 1000 );
251
+ add_filter( 'customize_partial_render', array( __CLASS__, 'filter_customize_partial_render' ) );
252
+
253
+ add_action( 'wp_footer', 'amp_print_analytics' );
254
+
255
+ /*
256
+ * Disable admin bar because admin-bar.css (28K) and Dashicons (48K) alone
257
+ * combine to surpass the 50K limit imposed for the amp-custom style.
258
+ */
259
+ add_filter( 'show_admin_bar', '__return_false', 100 );
260
+
261
+ /*
262
+ * Start output buffering at very low priority for sake of plugins and themes that use template_redirect
263
+ * instead of template_include.
264
+ */
265
+ add_action( 'template_redirect', array( __CLASS__, 'start_output_buffering' ), 0 );
266
+
267
+ // Commenting hooks.
268
+ add_filter( 'wp_list_comments_args', array( __CLASS__, 'set_comments_walker' ), PHP_INT_MAX );
269
+ add_filter( 'comment_form_defaults', array( __CLASS__, 'filter_comment_form_defaults' ) );
270
+ add_filter( 'comment_reply_link', array( __CLASS__, 'filter_comment_reply_link' ), 10, 4 );
271
+ add_filter( 'cancel_comment_reply_link', array( __CLASS__, 'filter_cancel_comment_reply_link' ), 10, 3 );
272
+ add_action( 'comment_form', array( __CLASS__, 'amend_comment_form' ), 100 );
273
+ remove_action( 'comment_form', 'wp_comment_form_unfiltered_html_nonce' );
274
+ add_filter( 'wp_kses_allowed_html', array( __CLASS__, 'whitelist_layout_in_wp_kses_allowed_html' ), 10 );
275
+
276
+ if ( AMP_Validation_Utils::should_validate_response() ) {
277
+ AMP_Validation_Utils::add_validation_hooks();
278
+ }
279
+
280
+ // @todo Add character conversion.
281
+ }
282
+
283
+ /**
284
+ * Remove query vars that come in requests such as for amp-live-list.
285
+ *
286
+ * WordPress should generally not respond differently to requests when these parameters
287
+ * are present. In some cases, when a query param such as __amp_source_origin is present
288
+ * then it would normally get included into pagination links generated by get_pagenum_link().
289
+ * The whitelist sanitizer empties out links that contain this string as it matches the
290
+ * blacklisted_value_regex. So by preemptively scrubbing any reference to these query vars
291
+ * we can ensure that WordPress won't end up referencing them in any way.
292
+ *
293
+ * @since 0.7
294
+ */
295
+ public static function purge_amp_query_vars() {
296
+ $query_vars = array(
297
+ '__amp_source_origin',
298
+ '_wp_amp_action_xhr_converted',
299
+ 'amp_latest_update_time',
300
+ );
301
+
302
+ // Scrub input vars.
303
+ foreach ( $query_vars as $query_var ) {
304
+ if ( ! isset( $_GET[ $query_var ] ) ) { // phpcs:ignore
305
+ continue;
306
+ }
307
+ self::$purged_amp_query_vars[ $query_var ] = wp_unslash( $_GET[ $query_var ] ); // phpcs:ignore
308
+ unset( $_REQUEST[ $query_var ], $_GET[ $query_var ] );
309
+ $scrubbed = true;
310
+ }
311
+
312
+ if ( isset( $scrubbed ) ) {
313
+ $build_query = function( $query ) use ( $query_vars ) {
314
+ $pattern = '/^(' . join( '|', $query_vars ) . ')(?==|$)/';
315
+ $pairs = array();
316
+ foreach ( explode( '&', $query ) as $pair ) {
317
+ if ( ! preg_match( $pattern, $pair ) ) {
318
+ $pairs[] = $pair;
319
+ }
320
+ }
321
+ return join( '&', $pairs );
322
+ };
323
+
324
+ // Scrub QUERY_STRING.
325
+ if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {
326
+ $_SERVER['QUERY_STRING'] = $build_query( $_SERVER['QUERY_STRING'] );
327
+ }
328
+
329
+ // Scrub REQUEST_URI.
330
+ if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
331
+ list( $path, $query ) = explode( '?', $_SERVER['REQUEST_URI'], 2 );
332
+
333
+ $pairs = $build_query( $query );
334
+ $_SERVER['REQUEST_URI'] = $path;
335
+ if ( ! empty( $pairs ) ) {
336
+ $_SERVER['REQUEST_URI'] .= "?{$pairs}";
337
+ }
338
+ }
339
+ }
340
+ $_SERVER['REQUEST_URI'] = amp_remove_endpoint( $_SERVER['REQUEST_URI'] );
341
+ }
342
+
343
+ /**
344
+ * Hook into a POST form submissions, such as the comment form or some other form submission.
345
+ *
346
+ * @since 0.7.0
347
+ */
348
+ public static function handle_xhr_request() {
349
+ $is_amp_xhr = (
350
+ ! empty( self::$purged_amp_query_vars['_wp_amp_action_xhr_converted'] )
351
+ &&
352
+ ! empty( self::$purged_amp_query_vars['__amp_source_origin'] )
353
+ &&
354
+ ( ! empty( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] )
355
+ );
356
+ if ( ! $is_amp_xhr ) {
357
+ return;
358
+ }
359
+
360
+ // Send AMP response header.
361
+ $origin = wp_validate_redirect( wp_sanitize_redirect( esc_url_raw( self::$purged_amp_query_vars['__amp_source_origin'] ) ) );
362
+ if ( $origin ) {
363
+ AMP_Response_Headers::send_header( 'AMP-Access-Control-Allow-Source-Origin', $origin, array( 'replace' => true ) );
364
+ }
365
+
366
+ // Intercept POST requests which redirect.
367
+ add_filter( 'wp_redirect', array( __CLASS__, 'intercept_post_request_redirect' ), PHP_INT_MAX );
368
+
369
+ // Add special handling for redirecting after comment submission.
370
+ add_filter( 'comment_post_redirect', array( __CLASS__, 'filter_comment_post_redirect' ), PHP_INT_MAX, 2 );
371
+
372
+ // Add die handler for AMP error display, most likely due to problem with comment.
373
+ add_filter( 'wp_die_handler', function() {
374
+ return array( __CLASS__, 'handle_wp_die' );
375
+ } );
376
+
377
+ }
378
+
379
+ /**
380
+ * Strip tags that are not allowed in amp-mustache.
381
+ *
382
+ * @since 0.7.0
383
+ *
384
+ * @param string $text Text to sanitize.
385
+ * @return string Sanitized text.
386
+ */
387
+ protected static function wp_kses_amp_mustache( $text ) {
388
+ $amp_mustache_allowed_html_tags = array( 'strong', 'b', 'em', 'i', 'u', 's', 'small', 'mark', 'del', 'ins', 'sup', 'sub' );
389
+ return wp_kses( $text, array_fill_keys( $amp_mustache_allowed_html_tags, array() ) );
390
+ }
391
+
392
+ /**
393
+ * 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.
394
+ *
395
+ * @since 0.7.0
396
+ *
397
+ * @param string $url Comment permalink to redirect to.
398
+ * @param WP_Comment $comment Posted comment.
399
+ * @return string URL.
400
+ */
401
+ public static function filter_comment_post_redirect( $url, $comment ) {
402
+ $theme_support = get_theme_support( 'amp' );
403
+
404
+ // Cause a page refresh if amp-live-list is not implemented for comments via add_theme_support( 'amp', array( 'comments_live_list' => true ) ).
405
+ if ( empty( $theme_support[0]['comments_live_list'] ) ) {
406
+ /*
407
+ * Add the comment ID to the URL to force AMP to refresh the page.
408
+ * This is ideally a temporary workaround to deal with https://github.com/ampproject/amphtml/issues/14170
409
+ */
410
+ $url = add_query_arg( 'comment', $comment->comment_ID, $url );
411
+
412
+ // Pass URL along to wp_redirect().
413
+ return $url;
414
+ }
415
+
416
+ // Create a success message to display to the user.
417
+ if ( '1' === (string) $comment->comment_approved ) {
418
+ $message = __( 'Your comment has been posted.', 'amp' );
419
+ } else {
420
+ $message = __( 'Your comment is awaiting moderation.', 'default' ); // Note core string re-use.
421
+ }
422
+
423
+ /**
424
+ * Filters the message when comment submitted success message when
425
+ *
426
+ * @since 0.7
427
+ */
428
+ $message = apply_filters( 'amp_comment_posted_message', $message, $comment );
429
+
430
+ // Message will be shown in template defined by AMP_Theme_Support::amend_comment_form().
431
+ wp_send_json( array(
432
+ 'message' => self::wp_kses_amp_mustache( $message ),
433
+ ) );
434
+ }
435
+
436
+ /**
437
+ * New error handler for AMP form submission.
438
+ *
439
+ * @since 0.7.0
440
+ * @see wp_die()
441
+ *
442
+ * @param WP_Error|string $error The error to handle.
443
+ * @param string|int $title Optional. Error title. If `$message` is a `WP_Error` object,
444
+ * error data with the key 'title' may be used to specify the title.
445
+ * If `$title` is an integer, then it is treated as the response
446
+ * code. Default empty.
447
+ * @param string|array|int $args {
448
+ * Optional. Arguments to control behavior. If `$args` is an integer, then it is treated
449
+ * as the response code. Default empty array.
450
+ *
451
+ * @type int $response The HTTP response code. Default 200 for Ajax requests, 500 otherwise.
452
+ * }
453
+ */
454
+ public static function handle_wp_die( $error, $title = '', $args = array() ) {
455
+ if ( is_int( $title ) ) {
456
+ $status_code = $title;
457
+ } elseif ( is_int( $args ) ) {
458
+ $status_code = $args;
459
+ } elseif ( is_array( $args ) && isset( $args['response'] ) ) {
460
+ $status_code = $args['response'];
461
+ } else {
462
+ $status_code = 500;
463
+ }
464
+ status_header( $status_code );
465
+
466
+ if ( is_wp_error( $error ) ) {
467
+ $error = $error->get_error_message();
468
+ }
469
+
470
+ // Message will be shown in template defined by AMP_Theme_Support::amend_comment_form().
471
+ wp_send_json( array(
472
+ 'error' => self::wp_kses_amp_mustache( $error ),
473
+ ) );
474
+ }
475
+
476
+ /**
477
+ * Intercept the response to a POST request.
478
+ *
479
+ * @since 0.7.0
480
+ * @see wp_redirect()
481
+ *
482
+ * @param string $location The location to redirect to.
483
+ */
484
+ public static function intercept_post_request_redirect( $location ) {
485
+
486
+ // Make sure relative redirects get made absolute.
487
+ $parsed_location = array_merge(
488
+ array(
489
+ 'scheme' => 'https',
490
+ 'host' => wp_parse_url( home_url(), PHP_URL_HOST ),
491
+ 'path' => isset( $_SERVER['REQUEST_URI'] ) ? strtok( wp_unslash( $_SERVER['REQUEST_URI'] ), '?' ) : '/',
492
+ ),
493
+ wp_parse_url( $location )
494
+ );
495
+
496
+ $absolute_location = '';
497
+ if ( 'https' === $parsed_location['scheme'] ) {
498
+ $absolute_location .= $parsed_location['scheme'] . ':';
499
+ }
500
+ $absolute_location .= '//' . $parsed_location['host'];
501
+ if ( isset( $parsed_location['port'] ) ) {
502
+ $absolute_location .= ':' . $parsed_location['port'];
503
+ }
504
+ $absolute_location .= $parsed_location['path'];
505
+ if ( isset( $parsed_location['query'] ) ) {
506
+ $absolute_location .= '?' . $parsed_location['query'];
507
+ }
508
+ if ( isset( $parsed_location['fragment'] ) ) {
509
+ $absolute_location .= '#' . $parsed_location['fragment'];
510
+ }
511
+
512
+ AMP_Response_Headers::send_header( 'AMP-Redirect-To', $absolute_location );
513
+ AMP_Response_Headers::send_header( 'Access-Control-Expose-Headers', 'AMP-Redirect-To' );
514
+
515
+ wp_send_json_success();
516
+ }
517
+
518
+ /**
519
+ * Register/override widgets.
520
+ *
521
+ * @global WP_Widget_Factory
522
+ * @return void
523
+ */
524
+ public static function register_widgets() {
525
+ global $wp_widget_factory;
526
+ foreach ( $wp_widget_factory->widgets as $registered_widget ) {
527
+ $registered_widget_class_name = get_class( $registered_widget );
528
+ if ( ! preg_match( '/^WP_Widget_(.+)$/', $registered_widget_class_name, $matches ) ) {
529
+ continue;
530
+ }
531
+ $amp_class_name = 'AMP_Widget_' . $matches[1];
532
+ if ( ! class_exists( $amp_class_name ) || is_a( $amp_class_name, $registered_widget_class_name ) ) {
533
+ continue;
534
+ }
535
+
536
+ unregister_widget( $registered_widget_class_name );
537
+ register_widget( $amp_class_name );
538
+ }
539
+ }
540
+
541
+ /**
542
+ * Register content embed handlers.
543
+ *
544
+ * This was copied from `AMP_Content::register_embed_handlers()` due to being a private method
545
+ * and due to `AMP_Content` not being well suited for use in AMP canonical.
546
+ *
547
+ * @see AMP_Content::register_embed_handlers()
548
+ * @global int $content_width
549
+ * @return AMP_Base_Embed_Handler[] Handlers.
550
+ */
551
+ public static function register_content_embed_handlers() {
552
+ global $content_width;
553
+
554
+ $embed_handlers = array();
555
+ foreach ( amp_get_content_embed_handlers() as $embed_handler_class => $args ) {
556
+
557
+ /**
558
+ * Embed handler.
559
+ *
560
+ * @type AMP_Base_Embed_Handler $embed_handler
561
+ */
562
+ $embed_handler = new $embed_handler_class( array_merge(
563
+ array(
564
+ 'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat.
565
+ ),
566
+ $args
567
+ ) );
568
+
569
+ if ( ! is_subclass_of( $embed_handler, 'AMP_Base_Embed_Handler' ) ) {
570
+ /* translators: %s is embed handler */
571
+ _doing_it_wrong( __METHOD__, esc_html( sprintf( __( 'Embed Handler (%s) must extend `AMP_Embed_Handler`', 'amp' ), $embed_handler_class ) ), '0.1' );
572
+ continue;
573
+ }
574
+
575
+ $embed_handler->register_embed();
576
+ $embed_handlers[] = $embed_handler;
577
+ }
578
+
579
+ return $embed_handlers;
580
+ }
581
+
582
+ /**
583
+ * Add the comments template placeholder marker
584
+ *
585
+ * @param array $args the args for the comments list..
586
+ * @return array Args to return.
587
+ */
588
+ public static function set_comments_walker( $args ) {
589
+ $amp_walker = new AMP_Comment_Walker();
590
+ $args['walker'] = $amp_walker;
591
+ return $args;
592
+ }
593
+
594
+ /**
595
+ * Adds the form submit success and fail templates.
596
+ */
597
+ public static function amend_comment_form() {
598
+ ?>
599
+ <?php if ( is_singular() && ! amp_is_canonical() ) : ?>
600
+ <input type="hidden" name="redirect_to" value="<?php echo esc_url( amp_get_permalink( get_the_ID() ) ); ?>">
601
+ <?php endif; ?>
602
+
603
+ <div submit-success>
604
+ <template type="amp-mustache">
605
+ <p>{{{message}}}</p>
606
+ </template>
607
+ </div>
608
+ <div submit-error>
609
+ <template type="amp-mustache">
610
+ <p class="amp-comment-submit-error">{{{error}}}</p>
611
+ </template>
612
+ </div>
613
+ <?php
614
+ }
615
+
616
+ /**
617
+ * Prepends template hierarchy with template_dir for AMP paired mode templates.
618
+ *
619
+ * @see get_query_template()
620
+ *
621
+ * @param array $templates Template hierarchy.
622
+ * @returns array Templates.
623
+ */
624
+ public static function filter_paired_template_hierarchy( $templates ) {
625
+ $support = array();//get_theme_support( 'amp' );
626
+ $args = array_shift( $support );
627
+ if ( isset( $args['template_dir'] ) ) {
628
+ $amp_templates = array();
629
+ foreach ( $templates as $template ) {
630
+ $amp_templates[] = $args['template_dir'] . '/' . $template;
631
+ }
632
+ $templates = $amp_templates;
633
+ }
634
+ return $templates;
635
+ }
636
+
637
+ /**
638
+ * Redirect to the non-canonical URL when the template to include is empty.
639
+ *
640
+ * This is a failsafe in case an index.php is not located in the AMP template_dir,
641
+ * and the available_callback fails to omit a given request from being available in AMP.
642
+ *
643
+ * @param string $template Template to include.
644
+ * @return string Template to include.
645
+ */
646
+ public static function filter_paired_template_include( $template ) {
647
+ if ( empty( $template ) || ! self::is_paired_available() ) {
648
+ wp_safe_redirect( self::get_current_canonical_url(), 302 ); // Temporary redirect because support may come later.
649
+ exit;
650
+ }
651
+ return $template;
652
+ }
653
+
654
+ /**
655
+ * Get canonical URL for current request.
656
+ *
657
+ * @see rel_canonical()
658
+ * @global WP $wp
659
+ * @global WP_Rewrite $wp_rewrite
660
+ * @link https://www.ampproject.org/docs/reference/spec#canon.
661
+ * @link https://core.trac.wordpress.org/ticket/18660
662
+ *
663
+ * @return string Canonical non-AMP URL.
664
+ */
665
+ public static function get_current_canonical_url() {
666
+ global $wp, $wp_rewrite;
667
+
668
+ $url = null;
669
+ if ( is_singular() ) {
670
+ $url = wp_get_canonical_url();
671
+ }
672
+
673
+ // For non-singular queries, make use of the request URI and public query vars to determine canonical URL.
674
+ if ( empty( $url ) ) {
675
+ $added_query_vars = $wp->query_vars;
676
+ if ( ! $wp_rewrite->permalink_structure || empty( $wp->request ) ) {
677
+ $url = home_url( '/' );
678
+ } else {
679
+ $url = home_url( user_trailingslashit( $wp->request ) );
680
+ parse_str( $wp->matched_query, $matched_query_vars );
681
+ foreach ( $wp->query_vars as $key => $value ) {
682
+
683
+ // Remove query vars that were matched in the rewrite rules for the request.
684
+ if ( isset( $matched_query_vars[ $key ] ) ) {
685
+ unset( $added_query_vars[ $key ] );
686
+ }
687
+ }
688
+ }
689
+ }
690
+
691
+ if ( ! empty( $added_query_vars ) ) {
692
+ $url = add_query_arg( $added_query_vars, $url );
693
+ }
694
+
695
+ return amp_remove_endpoint( $url );
696
+ }
697
+
698
+ /**
699
+ * Get the ID for the amp-state.
700
+ *
701
+ * @since 0.7
702
+ *
703
+ * @param int $post_id Post ID.
704
+ * @return string ID for amp-state.
705
+ */
706
+ public static function get_comment_form_state_id( $post_id ) {
707
+ return sprintf( 'commentform_post_%d', $post_id );
708
+ }
709
+
710
+ /**
711
+ * Filter comment form args to an element with [text] AMP binding wrap the title reply.
712
+ *
713
+ * @since 0.7
714
+ * @see comment_form()
715
+ *
716
+ * @param array $args Comment form args.
717
+ * @return array Filtered comment form args.
718
+ */
719
+ public static function filter_comment_form_defaults( $args ) {
720
+ $state_id = self::get_comment_form_state_id( get_the_ID() );
721
+
722
+ $text_binding = sprintf(
723
+ '%s.replyToName ? %s : %s',
724
+ $state_id,
725
+ str_replace(
726
+ '%s',
727
+ sprintf( '" + %s.replyToName + "', $state_id ),
728
+ wp_json_encode( $args['title_reply_to'] )
729
+ ),
730
+ wp_json_encode( $args['title_reply'] )
731
+ );
732
+
733
+ $args['title_reply_before'] .= sprintf(
734
+ '<span [text]="%s">',
735
+ esc_attr( $text_binding )
736
+ );
737
+ $args['cancel_reply_before'] = '</span>' . $args['cancel_reply_before'];
738
+ return $args;
739
+ }
740
+
741
+ /**
742
+ * Modify the comment reply link for AMP.
743
+ *
744
+ * @since 0.7
745
+ * @see get_comment_reply_link()
746
+ *
747
+ * @param string $link The HTML markup for the comment reply link.
748
+ * @param array $args An array of arguments overriding the defaults.
749
+ * @param WP_Comment $comment The object of the comment being replied.
750
+ * @return string Comment reply link.
751
+ */
752
+ public static function filter_comment_reply_link( $link, $args, $comment ) {
753
+
754
+ // Continue to show default link to wp-login when user is not logged-in.
755
+ if ( get_option( 'comment_registration' ) && ! is_user_logged_in() ) {
756
+ return $link;
757
+ }
758
+
759
+ $state_id = self::get_comment_form_state_id( get_the_ID() );
760
+ $tap_state = array(
761
+ $state_id => array(
762
+ 'replyToName' => $comment->comment_author,
763
+ 'values' => array(
764
+ 'comment_parent' => (string) $comment->comment_ID,
765
+ ),
766
+ ),
767
+ );
768
+
769
+ // @todo Figure out how to support add_below. Instead of moving the form, what about letting the form get a fixed position?
770
+ $link = sprintf(
771
+ '<a rel="nofollow" class="comment-reply-link" href="%s" on="%s" aria-label="%s">%s</a>',
772
+ esc_attr( '#' . $args['respond_id'] ),
773
+ esc_attr( sprintf( 'tap:AMP.setState( %s )', wp_json_encode( $tap_state ) ) ),
774
+ esc_attr( sprintf( $args['reply_to_text'], $comment->comment_author ) ),
775
+ $args['reply_text']
776
+ );
777
+ return $link;
778
+ }
779
+
780
+ /**
781
+ * Filters the cancel comment reply link HTML.
782
+ *
783
+ * @since 0.7
784
+ * @see get_cancel_comment_reply_link()
785
+ *
786
+ * @param string $formatted_link The HTML-formatted cancel comment reply link.
787
+ * @param string $link Cancel comment reply link URL.
788
+ * @param string $text Cancel comment reply link text.
789
+ * @return string Cancel reply link.
790
+ */
791
+ public static function filter_cancel_comment_reply_link( $formatted_link, $link, $text ) {
792
+ unset( $formatted_link, $link );
793
+ if ( empty( $text ) ) {
794
+ $text = __( 'Click here to cancel reply.', 'default' );
795
+ }
796
+
797
+ $state_id = self::get_comment_form_state_id( get_the_ID() );
798
+ $tap_state = array(
799
+ $state_id => array(
800
+ 'replyToName' => '',
801
+ 'values' => array(
802
+ 'comment_parent' => '0',
803
+ ),
804
+ ),
805
+ );
806
+
807
+ $respond_id = 'respond'; // Hard-coded in comment_form() and default value in get_comment_reply_link().
808
+ return sprintf(
809
+ '<a id="cancel-comment-reply-link" href="%s" %s [hidden]="%s" on="%s">%s</a>',
810
+ esc_url( remove_query_arg( 'replytocom' ) . '#' . $respond_id ),
811
+ isset( $_GET['replytocom'] ) ? '' : ' hidden', // phpcs:ignore
812
+ esc_attr( sprintf( '%s.values.comment_parent == "0"', self::get_comment_form_state_id( get_the_ID() ) ) ),
813
+ esc_attr( sprintf( 'tap:AMP.setState( %s )', wp_json_encode( $tap_state ) ) ),
814
+ esc_html( $text )
815
+ );
816
+ }
817
+
818
+ /**
819
+ * Print AMP boilerplate and custom styles.
820
+ */
821
+ public static function print_amp_styles() {
822
+ echo amp_get_boilerplate_code() . "\n"; // WPCS: XSS OK.
823
+ echo "<style amp-custom></style>\n"; // This will by populated by AMP_Style_Sanitizer.
824
+ }
825
+
826
+ /**
827
+ * Ensure markup required by AMP <https://www.ampproject.org/docs/reference/spec#required-markup>.
828
+ *
829
+ * Ensure meta[charset], meta[name=viewport], and link[rel=canonical]; a the whitelist sanitizer
830
+ * may have removed an illegal meta[http-equiv] or meta[name=viewport]. Core only outputs a
831
+ * canonical URL by default if a singular post.
832
+ *
833
+ * @since 0.7
834
+ * @todo All of this might be better placed inside of a sanitizer.
835
+ *
836
+ * @param DOMDocument $dom Doc.
837
+ */
838
+ public static function ensure_required_markup( DOMDocument $dom ) {
839
+ $head = $dom->getElementsByTagName( 'head' )->item( 0 );
840
+ if ( ! $head ) {
841
+ $head = $dom->createElement( 'head' );
842
+ $dom->documentElement->insertBefore( $head, $dom->documentElement->firstChild );
843
+ }
844
+ $meta_charset = null;
845
+ $meta_viewport = null;
846
+ foreach ( $head->getElementsByTagName( 'meta' ) as $meta ) {
847
+ /**
848
+ * Meta.
849
+ *
850
+ * @var DOMElement $meta
851
+ */
852
+ if ( $meta->hasAttribute( 'charset' ) && 'utf-8' === strtolower( $meta->getAttribute( 'charset' ) ) ) { // @todo Also look for meta[http-equiv="Content-Type"]?
853
+ $meta_charset = $meta;
854
+ } elseif ( 'viewport' === $meta->getAttribute( 'name' ) ) {
855
+ $meta_viewport = $meta;
856
+ }
857
+ }
858
+ if ( ! $meta_charset ) {
859
+ // Warning: This probably means the character encoding needs to be converted.
860
+ $meta_charset = AMP_DOM_Utils::create_node( $dom, 'meta', array(
861
+ 'charset' => 'utf-8',
862
+ ) );
863
+ $head->insertBefore( $meta_charset, $head->firstChild );
864
+ }
865
+ if ( ! $meta_viewport ) {
866
+ $meta_viewport = AMP_DOM_Utils::create_node( $dom, 'meta', array(
867
+ 'name' => 'viewport',
868
+ 'content' => 'width=device-width,minimum-scale=1',
869
+ ) );
870
+ $head->insertBefore( $meta_viewport, $meta_charset->nextSibling );
871
+ }
872
+ // Prevent schema.org duplicates.
873
+ $has_schema_org_metadata = false;
874
+ foreach ( $head->getElementsByTagName( 'script' ) as $script ) {
875
+ if ( 'application/ld+json' === $script->getAttribute( 'type' ) && false !== strpos( $script->nodeValue, 'schema.org' ) ) {
876
+ $has_schema_org_metadata = true;
877
+ break;
878
+ }
879
+ }
880
+ if ( ! $has_schema_org_metadata ) {
881
+ $script = $dom->createElement( 'script', wp_json_encode( amp_get_schemaorg_metadata() ) );
882
+ $script->setAttribute( 'type', 'application/ld+json' );
883
+ $head->appendChild( $script );
884
+ }
885
+ // Ensure rel=canonical link.
886
+ $rel_canonical = null;
887
+ foreach ( $head->getElementsByTagName( 'link' ) as $link ) {
888
+ if ( 'canonical' === $link->getAttribute( 'rel' ) ) {
889
+ $rel_canonical = $link;
890
+ break;
891
+ }
892
+ }
893
+ if ( ! $rel_canonical ) {
894
+ $rel_canonical = AMP_DOM_Utils::create_node( $dom, 'link', array(
895
+ 'rel' => 'canonical',
896
+ 'href' => self::get_current_canonical_url(),
897
+ ) );
898
+ $head->appendChild( $rel_canonical );
899
+ }
900
+ }
901
+
902
+ /**
903
+ * Dequeue Customizer assets which are not necessary outside the preview iframe.
904
+ *
905
+ * Prevent enqueueing customize-preview styles if not in customizer preview iframe.
906
+ * These are only needed for when there is live editing of content, such as selective refresh.
907
+ *
908
+ * @since 0.7
909
+ */
910
+ public static function dequeue_customize_preview_scripts() {
911
+
912
+ // Dequeue styles unnecessary unless in customizer preview iframe when editing (such as for edit shortcuts).
913
+ if ( ! self::is_customize_preview_iframe() ) {
914
+ wp_dequeue_style( 'customize-preview' );
915
+ foreach ( wp_styles()->registered as $handle => $dependency ) {
916
+ if ( in_array( 'customize-preview', $dependency->deps, true ) ) {
917
+ wp_dequeue_style( $handle );
918
+ }
919
+ }
920
+ }
921
+ }
922
+
923
+ /**
924
+ * Start output buffering.
925
+ *
926
+ * @since 0.7
927
+ * @see AMP_Theme_Support::finish_output_buffering()
928
+ */
929
+ public static function start_output_buffering() {
930
+ /*
931
+ * Disable the New Relic Browser agent on AMP responses.
932
+ * This prevents the New Relic from causing invalid AMP responses due the NREUM script it injects after the meta charset:
933
+ * https://docs.newrelic.com/docs/browser/new-relic-browser/troubleshooting/google-amp-validator-fails-due-3rd-party-script
934
+ * Sites with New Relic will need to specially configure New Relic for AMP:
935
+ * https://docs.newrelic.com/docs/browser/new-relic-browser/installation/monitor-amp-pages-new-relic-browser
936
+ */
937
+ if ( function_exists( 'newrelic_disable_autorum' ) ) {
938
+ newrelic_disable_autorum();
939
+ }
940
+
941
+ ob_start();
942
+
943
+ // Note that the following must be at 0 because wp_ob_end_flush_all() runs at shutdown:1.
944
+ add_action( 'shutdown', array( __CLASS__, 'finish_output_buffering' ), 0 );
945
+ }
946
+
947
+ /**
948
+ * Finish output buffering.
949
+ *
950
+ * @since 0.7
951
+ * @see AMP_Theme_Support::start_output_buffering()
952
+ */
953
+ public static function finish_output_buffering() {
954
+ AMP_Response_Headers::send_server_timing( 'amp_output_buffer', -self::$init_start_time, 'AMP Output Buffer' );
955
+ echo self::prepare_response( ob_get_clean() ); // WPCS: xss ok.
956
+ }
957
+
958
+ /**
959
+ * Filter rendered partial to convert to AMP.
960
+ *
961
+ * @see WP_Customize_Partial::render()
962
+ *
963
+ * @param string|mixed $partial Rendered partial.
964
+ * @return string|mixed Filtered partial.
965
+ * @global int $content_width
966
+ */
967
+ public static function filter_customize_partial_render( $partial ) {
968
+ global $content_width;
969
+ if ( is_string( $partial ) && preg_match( '/<\w/', $partial ) ) {
970
+ $dom = AMP_DOM_Utils::get_dom_from_content( $partial );
971
+ $args = array(
972
+ 'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat.
973
+ 'use_document_element' => false,
974
+ 'allow_dirty_styles' => true,
975
+ 'allow_dirty_scripts' => false,
976
+ );
977
+ AMP_Content_Sanitizer::sanitize_document( $dom, self::$sanitizer_classes, $args ); // @todo Include script assets in response?
978
+ $partial = AMP_DOM_Utils::get_content_from_dom( $dom );
979
+ }
980
+ return $partial;
981
+ }
982
+
983
+ /**
984
+ * Process response to ensure AMP validity.
985
+ *
986
+ * @since 0.7
987
+ *
988
+ * @param string $response HTML document response. By default it expects a complete document.
989
+ * @param array $args {
990
+ * Args to send to the preprocessor/sanitizer.
991
+ *
992
+ * @type callable $remove_invalid_callback Function to call whenever a node is removed due to being invalid.
993
+ * }
994
+ * @return string AMP document response.
995
+ * @global int $content_width
996
+ */
997
+ public static function prepare_response( $response, $args = array() ) {
998
+ global $content_width;
999
+
1000
+ /*
1001
+ * Check if the response starts with HTML markup.
1002
+ * Without this check, JSON responses will be erroneously corrupted,
1003
+ * being wrapped in HTML documents.
1004
+ */
1005
+ if ( '<' !== substr( ltrim( $response ), 0, 1 ) ) {
1006
+ return $response;
1007
+ }
1008
+
1009
+ // Account for case where ob_flush() was called prematurely.
1010
+ if ( false === strpos( $response, '<html' ) ) {
1011
+ $error = sprintf(
1012
+ '<div style="color:red; background: white; padding: 0.5em; position: fixed; z-index: 100000; bottom: 0; border: dashed 1px red;">%s</div>',
1013
+ wp_kses_post( __( '<strong>AMP Plugin Error</strong>: It appears that your WordPress install prematurely flushed the output buffer. You will need to disable AMP theme support until that is fixed.', 'amp' ) )
1014
+ );
1015
+ return $error . $response;
1016
+ }
1017
+
1018
+ $is_validation_debug_mode = ! empty( $_REQUEST[ AMP_Validation_Utils::DEBUG_QUERY_VAR ] ); // WPCS: csrf ok.
1019
+
1020
+ $args = array_merge(
1021
+ array(
1022
+ 'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat.
1023
+ 'use_document_element' => true,
1024
+ 'allow_dirty_styles' => self::is_customize_preview_iframe(), // Dirty styles only needed when editing (e.g. for edit shortcodes).
1025
+ 'allow_dirty_scripts' => is_customize_preview(), // Scripts are always needed to inject changeset UUID.
1026
+ 'disable_invalid_removal' => $is_validation_debug_mode,
1027
+ ),
1028
+ $args
1029
+ );
1030
+
1031
+ $dom_parse_start = microtime( true );
1032
+
1033
+ /*
1034
+ * Make sure that <meta charset> is present in output prior to parsing.
1035
+ * Note that the meta charset is supposed to appear within the first 1024 bytes.
1036
+ * See <https://www.w3.org/International/questions/qa-html-encoding-declarations>.
1037
+ */
1038
+ if ( ! preg_match( '#<meta[^>]+charset=#i', substr( $response, 0, 1024 ) ) ) {
1039
+ $response = preg_replace(
1040
+ '/(<head[^>]*>)/i',
1041
+ '$1' . sprintf( '<meta charset="%s">', esc_attr( get_bloginfo( 'charset' ) ) ),
1042
+ $response,
1043
+ 1
1044
+ );
1045
+ }
1046
+ $dom = AMP_DOM_Utils::get_dom( $response );
1047
+
1048
+ $xpath = new DOMXPath( $dom );
1049
+
1050
+ $head = $dom->getElementsByTagName( 'head' )->item( 0 );
1051
+
1052
+ if ( isset( $head ) ) {
1053
+ // Make sure scripts from the body get moved to the head.
1054
+ foreach ( $xpath->query( '//body//script[ @custom-element or @custom-template ]' ) as $script ) {
1055
+ $head->appendChild( $script );
1056
+ }
1057
+ }
1058
+
1059
+ // Ensure the mandatory amp attribute is present on the html element, as otherwise it will be stripped entirely.
1060
+ if ( ! $dom->documentElement->hasAttribute( 'amp' ) && ! $dom->documentElement->hasAttribute( '⚡️' ) ) {
1061
+ $dom->documentElement->setAttribute( 'amp', '' );
1062
+ }
1063
+
1064
+ AMP_Response_Headers::send_server_timing( 'amp_dom_parse', -$dom_parse_start, 'AMP DOM Parse' );
1065
+
1066
+ $assets = AMP_Content_Sanitizer::sanitize_document( $dom, self::$sanitizer_classes, $args );
1067
+
1068
+ $dom_serialize_start = microtime( true );
1069
+ self::ensure_required_markup( $dom );
1070
+
1071
+ // @todo If 'utf-8' is not the blog charset, then we'll need to do some character encoding conversation or "entityification".
1072
+ if ( 'utf-8' !== strtolower( get_bloginfo( 'charset' ) ) ) {
1073
+ /* translators: %s is the charset of the current site */
1074
+ 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
1075
+ }
1076
+
1077
+ if ( AMP_Validation_Utils::should_validate_response() ) {
1078
+ AMP_Validation_Utils::finalize_validation( $dom, array(
1079
+ 'remove_source_comments' => ! $is_validation_debug_mode,
1080
+ ) );
1081
+ }
1082
+
1083
+ $response = "<!DOCTYPE html>\n";
1084
+ $response .= AMP_DOM_Utils::get_content_from_dom_node( $dom, $dom->documentElement );
1085
+
1086
+ $amp_scripts = $assets['scripts'];
1087
+ foreach ( self::$embed_handlers as $embed_handler ) {
1088
+ $amp_scripts = array_merge(
1089
+ $amp_scripts,
1090
+ $embed_handler->get_scripts()
1091
+ );
1092
+ }
1093
+
1094
+ // Allow for embed handlers to override the default extension version by defining a different URL.
1095
+ foreach ( $amp_scripts as $handle => $value ) {
1096
+ if ( is_string( $value ) && wp_script_is( $handle, 'registered' ) ) {
1097
+ wp_scripts()->registered[ $handle ]->src = $value;
1098
+ }
1099
+ }
1100
+
1101
+ // Print all scripts, some of which may have already been printed and inject into head.
1102
+ ob_start();
1103
+ wp_print_scripts( array_keys( $amp_scripts ) );
1104
+ $script_tags = ob_get_clean();
1105
+ if ( ! empty( $script_tags ) ) {
1106
+ $response = preg_replace(
1107
+ '#(?=</head>)#',
1108
+ $script_tags,
1109
+ $response,
1110
+ 1
1111
+ );
1112
+ }
1113
+
1114
+ AMP_Response_Headers::send_server_timing( 'amp_dom_serialize', -$dom_serialize_start, 'AMP DOM Serialize' );
1115
+
1116
+ return $response;
1117
+ }
1118
+
1119
+ /**
1120
+ * Adds 'data-amp-layout' to the allowed <img> attributes for wp_kses().
1121
+ *
1122
+ * @since 0.7
1123
+ *
1124
+ * @param array $context Allowed tags and their allowed attributes.
1125
+ * @return array $context Filtered allowed tags and attributes.
1126
+ */
1127
+ public static function whitelist_layout_in_wp_kses_allowed_html( $context ) {
1128
+ if ( ! empty( $context['img']['width'] ) && ! empty( $context['img']['height'] ) ) {
1129
+ $context['img']['data-amp-layout'] = true;
1130
+ }
1131
+
1132
+ return $context;
1133
+ }
1134
+
1135
+ /**
1136
+ * Enqueue AMP assets if this is an AMP endpoint.
1137
+ *
1138
+ * @since 0.7
1139
+ *
1140
+ * @return void
1141
+ */
1142
+ public static function enqueue_assets() {
1143
+ wp_enqueue_script( 'amp-runtime' );
1144
+
1145
+ // Enqueue default styles expected by sanitizer.
1146
+ wp_enqueue_style( 'amp-default', amp_get_asset_url( 'css/amp-default.css' ), array(), AMP__VERSION );
1147
+ }
1148
+ }
includes/vendor/vendor-files/sanitizer/class-amp-core-theme-sanitizer.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Core_Theme_Sanitizer.
4
+ *
5
+ * @package AMP
6
+ * @since 1.0
7
+ */
8
+
9
+ /**
10
+ * Class AMP_Core_Theme_Sanitizer
11
+ *
12
+ * Fixes up common issues in core themes and others.
13
+ *
14
+ * @since 1.0
15
+ */
16
+ class AMP_Core_Theme_Sanitizer extends AMP_Base_Sanitizer {
17
+
18
+ /**
19
+ * Config for features needed by themes.
20
+ *
21
+ * @var array
22
+ */
23
+ public $theme_features = array(
24
+ 'twentyseventeen' => array(
25
+ 'force_svg_support' => array(),
26
+ 'force_fixed_background_support' => array(),
27
+ // @todo Header video probably needs special support in the plugin generally.
28
+ // @todo Header image is not styled properly. Needs layout=responsive and other things?
29
+ // @todo Dequeue scripts and replace with AMP functionality where possible.
30
+ ),
31
+ );
32
+
33
+ /**
34
+ * Fix up core themes to do things in the AMP way.
35
+ *
36
+ * @since 1.0
37
+ */
38
+ public function sanitize() {
39
+ $theme_features = array();
40
+
41
+ // Find theme features for core theme.
42
+ $theme_candidates = wp_array_slice_assoc( $this->args, array( 'stylesheet', 'template' ) );
43
+ foreach ( $theme_candidates as $theme_candidate ) {
44
+ if ( isset( $this->theme_features[ $theme_candidate ] ) ) {
45
+ $theme_features = $this->theme_features[ $theme_candidate ];
46
+ break;
47
+ }
48
+ }
49
+
50
+ // Allow specific theme features to be requested even if the theme is not in core.
51
+ if ( isset( $this->args['theme_features'] ) ) {
52
+ $theme_features = array_merge( $this->args['theme_features'], $theme_features );
53
+ }
54
+
55
+ foreach ( $theme_features as $theme_feature => $feature_args ) {
56
+ if ( method_exists( $this, $theme_feature ) ) {
57
+ call_user_func( array( $this, $theme_feature ), $feature_args );
58
+ }
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Force SVG support, replacing no-svg class name with svg class name.
64
+ *
65
+ * @link https://github.com/WordPress/wordpress-develop/blob/1af1f65a21a1a697fb5f33027497f9e5ae638453/src/wp-content/themes/twentyseventeen/assets/js/global.js#L211-L213
66
+ * @link https://caniuse.com/#feat=svg
67
+ */
68
+ public function force_svg_support() {
69
+ $this->dom->documentElement->setAttribute(
70
+ 'class',
71
+ preg_replace(
72
+ '/(^|\s)no-svg(\s|$)/',
73
+ ' svg ',
74
+ $this->dom->documentElement->getAttribute( 'class' )
75
+ )
76
+ );
77
+ }
78
+
79
+ /**
80
+ * Force support for fixed background-attachment.
81
+ *
82
+ * @link https://github.com/WordPress/wordpress-develop/blob/1af1f65a21a1a697fb5f33027497f9e5ae638453/src/wp-content/themes/twentyseventeen/assets/js/global.js#L215-L217
83
+ * @link https://caniuse.com/#feat=background-attachment
84
+ */
85
+ public function force_fixed_background_support() {
86
+ $this->dom->documentElement->setAttribute(
87
+ 'class',
88
+ $this->dom->documentElement->getAttribute( 'class' ) . ' background-fixed'
89
+ );
90
+ }
91
+ }
includes/vendor/vendor-files/sanitizer/class-amp-style-sanitizer.php ADDED
@@ -0,0 +1,1316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class AMP_Style_Sanitizer
4
+ *
5
+ * @package AMP
6
+ */
7
+
8
+ use \Sabberworm\CSS\RuleSet\DeclarationBlock;
9
+ use \Sabberworm\CSS\CSSList\CSSList;
10
+ use \Sabberworm\CSS\Property\Selector;
11
+ use \Sabberworm\CSS\RuleSet\RuleSet;
12
+ use \Sabberworm\CSS\Rule\Rule;
13
+ use \Sabberworm\CSS\Property\AtRule;
14
+ use \Sabberworm\CSS\CSSList\KeyFrame;
15
+ use \Sabberworm\CSS\RuleSet\AtRuleSet;
16
+ use \Sabberworm\CSS\Property\Import;
17
+ use \Sabberworm\CSS\CSSList\AtRuleBlockList;
18
+ use \Sabberworm\CSS\Value\RuleValueList;
19
+ use \Sabberworm\CSS\Value\URL;
20
+
21
+ /**
22
+ * Class AMP_Style_Sanitizer
23
+ *
24
+ * Collects inline styles and outputs them in the amp-custom stylesheet.
25
+ */
26
+ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer {
27
+
28
+ /**
29
+ * Array of flags used to control sanitization.
30
+ *
31
+ * @var array {
32
+ * @type string $remove_unused_rules Enum 'never', 'sometimes' (default), 'always'. If total CSS is greater than max_bytes, whether to strip selectors (and then empty rules) when they are not found to be used in doc. A validation error will be emitted when stripping happens since it is not completely safe in the case of dynamic content.
33
+ * @type string[] $dynamic_element_selectors Selectors for elements (or their ancestors) which contain dynamic content; selectors containing these will not be filtered.
34
+ * @type bool $use_document_element Whether the root of the document should be used rather than the body.
35
+ * @type bool $require_https_src Require HTTPS URLs.
36
+ * @type bool $allow_dirty_styles Allow dirty styles. This short-circuits the sanitize logic; it is used primarily in Customizer preview.
37
+ * @type callable $validation_error_callback Function to call when a validation error is encountered.
38
+ * }
39
+ */
40
+ protected $args;
41
+
42
+ /**
43
+ * Default args.
44
+ *
45
+ * @var array
46
+ */
47
+ protected $DEFAULT_ARGS = array(
48
+ 'remove_unused_rules' => 'sometimes',
49
+ 'dynamic_element_selectors' => array(
50
+ 'amp-list',
51
+ 'amp-live-list',
52
+ '[submit-error]',
53
+ '[submit-success]',
54
+ ),
55
+ );
56
+
57
+ /**
58
+ * Stylesheets.
59
+ *
60
+ * Values are the CSS stylesheets. Keys are MD5 hashes of the stylesheets,
61
+ *
62
+ * @since 0.7
63
+ * @var string[]
64
+ */
65
+ private $stylesheets = array();
66
+
67
+ /**
68
+ * List of stylesheet parts prior to selector/rule removal (tree shaking).
69
+ *
70
+ * Keys are MD5 hashes of stylesheets.
71
+ *
72
+ * @since 1.0
73
+ * @var array[] {
74
+ * @type array $stylesheet Array of stylesheet chunked, with declaration blocks being represented as arrays.
75
+ * @type DOMElement|DOMAttr $node Origin for styles.
76
+ * @type array $sources Sources for the node.
77
+ * @type bool $keyframes Whether an amp-keyframes.
78
+ * }
79
+ */
80
+ private $pending_stylesheets = array();
81
+
82
+ /**
83
+ * Spec for style[amp-custom] cdata.
84
+ *
85
+ * @since 1.0
86
+ * @var array
87
+ */
88
+ private $style_custom_cdata_spec;
89
+
90
+ /**
91
+ * The style[amp-custom] element.
92
+ *
93
+ * @var DOMElement
94
+ */
95
+ private $amp_custom_style_element;
96
+
97
+ /**
98
+ * Spec for style[amp-keyframes] cdata.
99
+ *
100
+ * @since 1.0
101
+ * @var array
102
+ */
103
+ private $style_keyframes_cdata_spec;
104
+
105
+ /**
106
+ * Regex for allowed font stylesheet URL.
107
+ *
108
+ * @var string
109
+ */
110
+ private $allowed_font_src_regex;
111
+
112
+ /**
113
+ * Base URL for styles.
114
+ *
115
+ * Full URL with trailing slash.
116
+ *
117
+ * @var string
118
+ */
119
+ private $base_url;
120
+
121
+ /**
122
+ * URL of the content directory.
123
+ *
124
+ * @var string
125
+ */
126
+ private $content_url;
127
+
128
+ /**
129
+ * Class names used in document.
130
+ *
131
+ * @since 1.0
132
+ * @var array
133
+ */
134
+ private $used_class_names = array();
135
+
136
+ /**
137
+ * XPath.
138
+ *
139
+ * @since 1.0
140
+ * @var DOMXPath
141
+ */
142
+ private $xpath;
143
+
144
+ /**
145
+ * Amount of time that was spent parsing CSS.
146
+ *
147
+ * @since 1.0
148
+ * @var float
149
+ */
150
+ private $parse_css_duration = 0.0;
151
+
152
+ /**
153
+ * AMP_Base_Sanitizer constructor.
154
+ *
155
+ * @since 0.7
156
+ *
157
+ * @param DOMDocument $dom Represents the HTML document to sanitize.
158
+ * @param array $args Args.
159
+ */
160
+ public function __construct( DOMDocument $dom, array $args = array() ) {
161
+ parent::__construct( $dom, $args );
162
+
163
+ foreach ( AMP_Allowed_Tags_Generated::get_allowed_tag( 'style' ) as $spec_rule ) {
164
+ if ( ! isset( $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) ) {
165
+ continue;
166
+ }
167
+ if ( 'style[amp-keyframes]' === $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) {
168
+ $this->style_keyframes_cdata_spec = $spec_rule[ AMP_Rule_Spec::CDATA ];
169
+ } elseif ( 'style amp-custom' === $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) {
170
+ $this->style_custom_cdata_spec = $spec_rule[ AMP_Rule_Spec::CDATA ];
171
+ }
172
+ }
173
+
174
+ $spec_name = 'link rel=stylesheet for fonts'; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
175
+ foreach ( AMP_Allowed_Tags_Generated::get_allowed_tag( 'link' ) as $spec_rule ) {
176
+ if ( isset( $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) && $spec_name === $spec_rule[ AMP_Rule_Spec::TAG_SPEC ]['spec_name'] ) {
177
+ $this->allowed_font_src_regex = '@^(' . $spec_rule[ AMP_Rule_Spec::ATTR_SPEC_LIST ]['href']['value_regex'] . ')$@';
178
+ break;
179
+ }
180
+ }
181
+
182
+ $guessurl = site_url();
183
+ if ( ! $guessurl ) {
184
+ $guessurl = wp_guess_url();
185
+ }
186
+ $this->base_url = $guessurl;
187
+ $this->content_url = WP_CONTENT_URL;
188
+ $this->xpath = new DOMXPath( $dom );
189
+ }
190
+
191
+ /**
192
+ * Get list of CSS styles in HTML content of DOMDocument ($this->dom).
193
+ *
194
+ * @since 0.4
195
+ * @deprecated As of 1.0, use get_stylesheets().
196
+ *
197
+ * @return array[] Mapping CSS selectors to array of properties, or mapping of keys starting with 'stylesheet:' with value being the stylesheet.
198
+ */
199
+ public function get_styles() {
200
+ return array();
201
+ }
202
+
203
+ /**
204
+ * Get stylesheets.
205
+ *
206
+ * @since 0.7
207
+ * @returns array Values are the CSS stylesheets. Keys are MD5 hashes of the stylesheets.
208
+ */
209
+ public function get_stylesheets() {
210
+ return $this->stylesheets;
211
+ }
212
+
213
+ /**
214
+ * Get list of all the class names used in the document.
215
+ *
216
+ * @since 1.0
217
+ * @return array Used class names.
218
+ */
219
+ private function get_used_class_names() {
220
+ if ( empty( $this->used_class_names ) ) {
221
+ $classes = ' ';
222
+ foreach ( $this->xpath->query( '//*/@class' ) as $class_attribute ) {
223
+ $classes .= ' ' . $class_attribute->nodeValue;
224
+ }
225
+ $this->used_class_names = array_unique( array_filter( preg_split( '/\s+/', trim( $classes ) ) ) );
226
+ }
227
+ return $this->used_class_names;
228
+ }
229
+
230
+ /**
231
+ * Sanitize CSS styles within the HTML contained in this instance's DOMDocument.
232
+ *
233
+ * @since 0.4
234
+ */
235
+ public function sanitize() {
236
+ $elements = array();
237
+
238
+ // Do nothing if inline styles are allowed.
239
+ if ( ! empty( $this->args['allow_dirty_styles'] ) ) {
240
+ return;
241
+ }
242
+
243
+ $this->parse_css_duration = 0.0;
244
+
245
+ /*
246
+ * Note that xpath is used to query the DOM so that the link and style elements will be
247
+ * in document order. DOMNode::compareDocumentPosition() is not yet implemented.
248
+ */
249
+ $xpath = $this->xpath;
250
+
251
+ $lower_case = 'translate( %s, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz" )'; // In XPath 2.0 this is lower-case().
252
+ $predicates = array(
253
+ sprintf( '( self::style and not( @amp-boilerplate ) and ( not( @type ) or %s = "text/css" ) )', sprintf( $lower_case, '@type' ) ),
254
+ sprintf( '( self::link and @href and %s = "stylesheet" )', sprintf( $lower_case, '@rel' ) ),
255
+ );
256
+
257
+ foreach ( $xpath->query( '//*[ ' . implode( ' or ', $predicates ) . ' ]' ) as $element ) {
258
+ $elements[] = $element;
259
+ }
260
+
261
+ /**
262
+ * Element.
263
+ *
264
+ * @var DOMElement $element
265
+ */
266
+ foreach ( $elements as $element ) {
267
+ $node_name = strtolower( $element->nodeName );
268
+ if ( 'style' === $node_name ) {
269
+ $this->process_style_element( $element );
270
+ } elseif ( 'link' === $node_name ) {
271
+ $this->process_link_element( $element );
272
+ }
273
+ }
274
+
275
+ $elements = array();
276
+ foreach ( $xpath->query( '//*[ @style ]' ) as $element ) {
277
+ $elements[] = $element;
278
+ }
279
+ foreach ( $elements as $element ) {
280
+ $this->collect_inline_styles( $element );
281
+ }
282
+
283
+ $this->finalize_styles();
284
+
285
+ $this->did_convert_elements = true;
286
+
287
+ if ( $this->parse_css_duration > 0.0 ) {
288
+ AMP_Response_Headers::send_server_timing( 'amp_parse_css', $this->parse_css_duration, 'AMP Parse CSS' );
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Generate a URL's fully-qualified file path.
294
+ *
295
+ * @since 0.7
296
+ * @see WP_Styles::_css_href()
297
+ *
298
+ * @param string $url The file URL.
299
+ * @param string[] $allowed_extensions Allowed file extensions.
300
+ * @return string|WP_Error Style's absolute validated filesystem path, or WP_Error when error.
301
+ */
302
+ public function get_validated_url_file_path( $url, $allowed_extensions = array() ) {
303
+ $needs_base_url = (
304
+ ! is_bool( $url )
305
+ &&
306
+ ! preg_match( '|^(https?:)?//|', $url )
307
+ &&
308
+ ! ( $this->content_url && 0 === strpos( $url, $this->content_url ) )
309
+ );
310
+ if ( $needs_base_url ) {
311
+ $url = $this->base_url . $url;
312
+ }
313
+
314
+ // Strip query and fragment from URL.
315
+ $url = preg_replace( ':[\?#].*$:', '', $url );
316
+
317
+ // Validate file extensions.
318
+ if ( ! empty( $allowed_extensions ) ) {
319
+ $pattern = sprintf( '/\.(%s)$/i', implode( '|', $allowed_extensions ) );
320
+ if ( ! preg_match( $pattern, $url ) ) {
321
+ /* translators: %s is the file URL */
322
+ return new WP_Error( 'amp_disallowed_file_extension', sprintf( __( 'Skipped file which does not have an allowed file extension (%s).', 'amp' ), $url ) );
323
+ }
324
+ }
325
+
326
+ $includes_url = includes_url( '/' );
327
+ $content_url = content_url( '/' );
328
+ $admin_url = get_admin_url( null, '/' );
329
+ $file_path = null;
330
+ if ( 0 === strpos( $url, $content_url ) ) {
331
+ $file_path = WP_CONTENT_DIR . substr( $url, strlen( $content_url ) - 1 );
332
+ } elseif ( 0 === strpos( $url, $includes_url ) ) {
333
+ $file_path = ABSPATH . WPINC . substr( $url, strlen( $includes_url ) - 1 );
334
+ } elseif ( 0 === strpos( $url, $admin_url ) ) {
335
+ $file_path = ABSPATH . 'wp-admin' . substr( $url, strlen( $admin_url ) - 1 );
336
+ }
337
+
338
+ if ( ! $file_path || false !== strpos( '../', $file_path ) || 0 !== validate_file( $file_path ) || ! file_exists( $file_path ) ) {
339
+ /* translators: %s is file URL */
340
+
341
+ //return new WP_Error( 'amp_file_path_not_found', sprintf( __( 'Unable to locate filesystem path for %s.', 'amp' ), $url ) );
342
+ }
343
+
344
+ return $file_path;
345
+ }
346
+
347
+ /**
348
+ * Process style element.
349
+ *
350
+ * @param DOMElement $element Style element.
351
+ */
352
+ private function process_style_element( DOMElement $element ) {
353
+
354
+ // @todo Any @keyframes rules could be removed from amp-custom and instead added to amp-keyframes.
355
+ $is_keyframes = $element->hasAttribute( 'amp-keyframes' );
356
+ $stylesheet = trim( $element->textContent );
357
+ $cdata_spec = $is_keyframes ? $this->style_keyframes_cdata_spec : $this->style_custom_cdata_spec;
358
+ if ( $stylesheet ) {
359
+
360
+ $stylesheet = $this->process_stylesheet( $stylesheet, $element, array(
361
+ 'allowed_at_rules' => $cdata_spec['css_spec']['allowed_at_rules'],
362
+ 'property_whitelist' => $cdata_spec['css_spec']['allowed_declarations'],
363
+ 'validate_keyframes' => $cdata_spec['css_spec']['validate_keyframes'],
364
+ ) );
365
+
366
+ $pending_stylesheet = array(
367
+ 'keyframes' => $is_keyframes,
368
+ 'stylesheet' => $stylesheet,
369
+ 'node' => $element,
370
+ );
371
+ if ( ! empty( $this->args['validation_error_callback'] ) ) {
372
+ $pending_stylesheet['sources'] = AMP_Validation_Utils::locate_sources( $element ); // Needed because node is removed below.
373
+ }
374
+ $this->pending_stylesheets[] = $pending_stylesheet;
375
+ }
376
+
377
+ if ( $element->hasAttribute( 'amp-custom' ) ) {
378
+ if ( ! $this->amp_custom_style_element ) {
379
+ $this->amp_custom_style_element = $element;
380
+ } else {
381
+ $element->parentNode->removeChild( $element ); // There can only be one. #highlander.
382
+ }
383
+ } else {
384
+
385
+ // Remove from DOM since we'll be adding it to amp-custom.
386
+ $element->parentNode->removeChild( $element );
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Process link element.
392
+ *
393
+ * @param DOMElement $element Link element.
394
+ */
395
+ private function process_link_element( DOMElement $element ) {
396
+ $href = $element->getAttribute( 'href' );
397
+
398
+ // Allow font URLs.
399
+ if ( $this->allowed_font_src_regex && preg_match( $this->allowed_font_src_regex, $href ) ) {
400
+ return;
401
+ }
402
+
403
+ $css_file_path = $this->get_validated_url_file_path( $href, array( 'css', 'less', 'scss', 'sass' ) );
404
+
405
+ if ( is_wp_error( $css_file_path ) ) {
406
+ $this->remove_invalid_child( $element, array(
407
+ 'message' => $css_file_path->get_error_message(),
408
+ ) );
409
+ return;
410
+ }
411
+ $stylesheet = '';
412
+ if ( $_SERVER["SERVER_ADDR"] == '127.0.0.1' || $_SERVER["SERVER_ADDR"] == '::1' ) {
413
+ $handle = fopen($css_file_path, "r");
414
+ $stylesheet .= fread($handle, filesize($css_file_path));
415
+ fclose($handle);
416
+ }else{
417
+ $stylesheet .= file_get_contents( $css_file_path ); // phpcs:ignore — It's a local filesystem path not a remote request.
418
+ }
419
+ // Load the CSS from the filesystem.
420
+ $stylesheet = file_get_contents( $css_file_path ); // phpcs:ignore -- It's a local filesystem path not a remote request.
421
+ if ( false === $stylesheet ) {
422
+ $this->remove_invalid_child( $element, array(
423
+ 'message' => __( 'Unable to load stylesheet from filesystem.', 'amp' ),
424
+ ) );
425
+ return;
426
+ }
427
+
428
+ // Honor the link's media attribute.
429
+ $media = $element->getAttribute( 'media' );
430
+ if ( $media && 'all' !== $media ) {
431
+ $stylesheet = sprintf( '@media %s { %s }', $media, $stylesheet );
432
+ }
433
+
434
+ $stylesheet = $this->process_stylesheet( $stylesheet, $element, array(
435
+ 'allowed_at_rules' => $this->style_custom_cdata_spec['css_spec']['allowed_at_rules'],
436
+ 'property_whitelist' => $this->style_custom_cdata_spec['css_spec']['allowed_declarations'],
437
+ 'stylesheet_url' => $href,
438
+ 'stylesheet_path' => $css_file_path,
439
+ ) );
440
+
441
+ $pending_stylesheet = array(
442
+ 'keyframes' => false,
443
+ 'stylesheet' => $stylesheet,
444
+ 'node' => $element,
445
+ );
446
+ if ( ! empty( $this->args['validation_error_callback'] ) ) {
447
+ $pending_stylesheet['sources'] = AMP_Validation_Utils::locate_sources( $element ); // Needed because node is removed below.
448
+ }
449
+ $this->pending_stylesheets[] = $pending_stylesheet;
450
+
451
+ // Remove now that styles have been processed.
452
+ $element->parentNode->removeChild( $element );
453
+ }
454
+
455
+ /**
456
+ * Process stylesheet.
457
+ *
458
+ * Sanitized invalid CSS properties and rules, removes rules which do not
459
+ * apply to the current document, and compresses the CSS to remove whitespace and comments.
460
+ *
461
+ * @since 1.0
462
+ *
463
+ * @param string $stylesheet Stylesheet.
464
+ * @param DOMElement|DOMAttr $node Element (link/style) or style attribute where the stylesheet came from.
465
+ * @param array $options {
466
+ * Options.
467
+ *
468
+ * @type bool $class_selector_tree_shaking Whether to perform tree shaking to delete rules that reference class names not extant in the current document.
469
+ * @type string[] $property_whitelist Exclusively-allowed properties.
470
+ * @type string[] $property_blacklist Disallowed properties.
471
+ * @type bool $convert_width_to_max_width Convert width to max-width.
472
+ * @type string $stylesheet_url Original URL for stylesheet when originating via link (or @import?).
473
+ * @type string $stylesheet_path Original filesystem path for stylesheet when originating via link (or @import?).
474
+ * @type array $allowed_at_rules Allowed @-rules.
475
+ * @type bool $validate_keyframes Whether keyframes should be validated.
476
+ * }
477
+ * @return array Processed stylesheet parts.
478
+ */
479
+ private function process_stylesheet( $stylesheet, $node, $options = array() ) {
480
+ $cache_impacting_options = wp_array_slice_assoc(
481
+ $options,
482
+ array( 'property_whitelist', 'property_blacklist', 'convert_width_to_max_width', 'stylesheet_url', 'allowed_at_rules' )
483
+ );
484
+
485
+ $cache_key = md5( $stylesheet . serialize( $cache_impacting_options ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
486
+
487
+ $cache_group = 'amp-parsed-stylesheet-v1';
488
+ if ( wp_using_ext_object_cache() ) {
489
+ $parsed = wp_cache_get( $cache_key, $cache_group );
490
+ } else {
491
+ $parsed = get_transient( $cache_key . $cache_group );
492
+ }
493
+ if ( ! $parsed || ! isset( $parsed['stylesheet'] ) || ! is_array( $parsed['stylesheet'] ) ) {
494
+ $parsed = $this->parse_stylesheet( $stylesheet, $options );
495
+ if ( wp_using_ext_object_cache() ) {
496
+ wp_cache_set( $cache_key, $parsed, $cache_group );
497
+ } else {
498
+ // The expiration is to ensure transient doesn't stick around forever since no LRU flushing like with external object cache.
499
+ set_transient( $cache_key . $cache_group, $parsed, MONTH_IN_SECONDS );
500
+ }
501
+ }
502
+
503
+ if ( ! empty( $this->args['validation_error_callback'] ) && ! empty( $parsed['validation_errors'] ) ) {
504
+ foreach ( $parsed['validation_errors'] as $validation_error ) {
505
+ call_user_func( $this->args['validation_error_callback'], array_merge( $validation_error, compact( 'node' ) ) );
506
+ }
507
+ }
508
+
509
+ return $parsed['stylesheet'];
510
+ }
511
+
512
+ /**
513
+ * Parse stylesheet.
514
+ *
515
+ * @since 1.0
516
+ *
517
+ * @param string $stylesheet_string Stylesheet.
518
+ * @param array $options Options. See definition in \AMP_Style_Sanitizer::process_stylesheet().
519
+ * @return array {
520
+ * Parsed stylesheet.
521
+ *
522
+ * @type array $stylesheet Stylesheet parts, where arrays are tuples for declaration blocks.
523
+ * @type array $validation_errors Validation errors.
524
+ * }
525
+ */
526
+ private function parse_stylesheet( $stylesheet_string, $options = array() ) {
527
+ $start_time = microtime( true );
528
+
529
+ $options = array_merge(
530
+ array(
531
+ 'allowed_at_rules' => array(),
532
+ 'convert_width_to_max_width' => false,
533
+ 'property_blacklist' => array(
534
+ // See <https://www.ampproject.org/docs/design/responsive/style_pages#disallowed-styles>.
535
+ 'behavior',
536
+ '-moz-binding',
537
+ ),
538
+ 'property_whitelist' => array(),
539
+ 'validate_keyframes' => false,
540
+ 'stylesheet_url' => null,
541
+ 'stylesheet_path' => null,
542
+ ),
543
+ $options
544
+ );
545
+
546
+ $stylesheet = array();
547
+ $validation_errors = array();
548
+ try {
549
+ $parser_settings = Sabberworm\CSS\Settings::create();
550
+ $css_parser = new Sabberworm\CSS\Parser( $stylesheet_string, $parser_settings );
551
+ $css_document = $css_parser->parse();
552
+
553
+ if ( ! empty( $options['stylesheet_url'] ) ) {
554
+ $this->real_path_urls(
555
+ array_filter(
556
+ $css_document->getAllValues(),
557
+ function ( $value ) {
558
+ return $value instanceof URL;
559
+ }
560
+ ),
561
+ $options['stylesheet_url']
562
+ );
563
+ }
564
+
565
+ $validation_errors = $this->process_css_list( $css_document, $options );
566
+
567
+ $output_format = Sabberworm\CSS\OutputFormat::createCompact();
568
+
569
+ $before_declaration_block = '/*AMP_WP_BEFORE_DECLARATION_BLOCK*/';
570
+ $between_selectors = '/*AMP_WP_BETWEEN_SELECTORS*/';
571
+ $after_declaration_block_selectors = '/*AMP_WP_BEFORE_DECLARATION_SELECTORS*/';
572
+ $after_declaration_block = '/*AMP_WP_AFTER_DECLARATION*/';
573
+
574
+ $output_format->set( 'BeforeDeclarationBlock', $before_declaration_block );
575
+ $output_format->set( 'SpaceBeforeSelectorSeparator', $between_selectors );
576
+ $output_format->set( 'AfterDeclarationBlockSelectors', $after_declaration_block_selectors );
577
+ $output_format->set( 'AfterDeclarationBlock', $after_declaration_block );
578
+
579
+ $stylesheet_string = $css_document->render( $output_format );
580
+
581
+ $pattern = '#';
582
+ $pattern .= '(' . preg_quote( $before_declaration_block, '#' ) . ')';
583
+ $pattern .= '(.+?)';
584
+ $pattern .= preg_quote( $after_declaration_block_selectors, '#' );
585
+ $pattern .= '(.+?)';
586
+ $pattern .= preg_quote( $after_declaration_block, '#' );
587
+ $pattern .= '#s';
588
+
589
+ $split_stylesheet = preg_split( $pattern, $stylesheet_string, -1, PREG_SPLIT_DELIM_CAPTURE );
590
+ $length = count( $split_stylesheet );
591
+ for ( $i = 0; $i < $length; $i++ ) {
592
+ if ( $before_declaration_block === $split_stylesheet[ $i ] ) {
593
+ $selectors = explode( $between_selectors . ',', $split_stylesheet[ ++$i ] );
594
+ $declaration = $split_stylesheet[ ++$i ];
595
+
596
+ $selectors_parsed = array();
597
+ foreach ( $selectors as $selector ) {
598
+ $classes = array();
599
+
600
+ // Remove :not() to eliminate false negatives, such as with `body:not(.title-tagline-hidden) .site-branding-text`.
601
+ $reduced_selector = preg_replace( '/:not\(.+?\)/', '', $selector );
602
+
603
+ // Remove attribute selectors to eliminate false negative, such as with `.social-navigation a[href*="example.com"]:before`.
604
+ $reduced_selector = preg_replace( '/\[\w.*?\]/', '', $reduced_selector );
605
+
606
+ if ( preg_match_all( '/(?<=\.)([a-zA-Z0-9_-]+)/', $reduced_selector, $matches ) ) {
607
+ $classes = $matches[0];
608
+ }
609
+ $selectors_parsed[ $selector ] = $classes;
610
+ }
611
+
612
+ $stylesheet[] = array(
613
+ $selectors_parsed,
614
+ $declaration,
615
+ );
616
+ } else {
617
+ $stylesheet[] = $split_stylesheet[ $i ];
618
+ }
619
+ }
620
+ } catch ( Exception $exception ) {
621
+ $validation_errors[] = array(
622
+ 'code' => 'css_parse_error',
623
+ 'message' => $exception->getMessage(),
624
+ );
625
+ }
626
+
627
+ $this->parse_css_duration += ( microtime( true ) - $start_time );
628
+
629
+ return compact( 'stylesheet', 'validation_errors' );
630
+ }
631
+
632
+ /**
633
+ * Process CSS list.
634
+ *
635
+ * @since 1.0
636
+ *
637
+ * @param CSSList $css_list CSS List.
638
+ * @param array $options Options.
639
+ * @return array Validation errors.
640
+ */
641
+ private function process_css_list( CSSList $css_list, $options ) {
642
+ $validation_errors = array();
643
+
644
+ foreach ( $css_list->getContents() as $css_item ) {
645
+ if ( $css_item instanceof DeclarationBlock && empty( $options['validate_keyframes'] ) ) {
646
+ $validation_errors = array_merge(
647
+ $validation_errors,
648
+ $this->process_css_declaration_block( $css_item, $css_list, $options )
649
+ );
650
+ } elseif ( $css_item instanceof AtRuleBlockList ) {
651
+ if ( in_array( $css_item->atRuleName(), $options['allowed_at_rules'], true ) ) {
652
+ $validation_errors = array_merge(
653
+ $validation_errors,
654
+ $this->process_css_list( $css_item, $options )
655
+ );
656
+ } else {
657
+ $validation_errors[] = array(
658
+ 'code' => 'illegal_css_at_rule',
659
+ /* translators: %s is the CSS at-rule name. */
660
+ 'message' => sprintf( __( 'CSS @%s rules are currently disallowed.', 'amp' ), $css_item->atRuleName() ),
661
+ );
662
+ $css_list->remove( $css_item );
663
+ }
664
+ } elseif ( $css_item instanceof Import ) {
665
+ $validation_errors[] = array(
666
+ 'code' => 'illegal_css_import_rule',
667
+ 'message' => __( 'CSS @import is currently disallowed.', 'amp' ),
668
+ );
669
+ $css_list->remove( $css_item );
670
+ } elseif ( $css_item instanceof AtRuleSet ) {
671
+ if ( in_array( $css_item->atRuleName(), $options['allowed_at_rules'], true ) ) {
672
+ $validation_errors = array_merge(
673
+ $validation_errors,
674
+ $this->process_css_declaration_block( $css_item, $css_list, $options )
675
+ );
676
+ } else {
677
+ $validation_errors[] = array(
678
+ 'code' => 'illegal_css_at_rule',
679
+ /* translators: %s is the CSS at-rule name. */
680
+ 'message' => sprintf( __( 'CSS @%s rules are currently disallowed.', 'amp' ), $css_item->atRuleName() ),
681
+ );
682
+ $css_list->remove( $css_item );
683
+ }
684
+ } elseif ( $css_item instanceof KeyFrame ) {
685
+ if ( in_array( 'keyframes', $options['allowed_at_rules'], true ) ) {
686
+ $validation_errors = array_merge(
687
+ $validation_errors,
688
+ $this->process_css_keyframes( $css_item, $options )
689
+ );
690
+ } else {
691
+ $validation_errors[] = array(
692
+ 'code' => 'illegal_css_at_rule',
693
+ /* translators: %s is the CSS at-rule name. */
694
+ 'message' => sprintf( __( 'CSS @%s rules are currently disallowed.', 'amp' ), $css_item->atRuleName() ),
695
+ );
696
+ }
697
+ } elseif ( $css_item instanceof AtRule ) {
698
+ $validation_errors[] = array(
699
+ 'code' => 'illegal_css_at_rule',
700
+ /* translators: %s is the CSS at-rule name. */
701
+ 'message' => sprintf( __( 'CSS @%s rules are currently disallowed.', 'amp' ), $css_item->atRuleName() ),
702
+ );
703
+ $css_list->remove( $css_item );
704
+ } else {
705
+ $validation_errors[] = array(
706
+ 'code' => 'unrecognized_css',
707
+ 'message' => __( 'Unrecognized CSS removed.', 'amp' ),
708
+ );
709
+ $css_list->remove( $css_item );
710
+ }
711
+ }
712
+ return $validation_errors;
713
+ }
714
+
715
+ /**
716
+ * Convert URLs in to non-relative real-paths.
717
+ *
718
+ * @param URL[] $urls URLs.
719
+ * @param string $stylesheet_url Stylesheet URL.
720
+ */
721
+ private function real_path_urls( $urls, $stylesheet_url ) {
722
+ $base_url = preg_replace( ':[^/]+(\?.*)?(#.*)?$:', '', $stylesheet_url );
723
+ if ( empty( $base_url ) ) {
724
+ return;
725
+ }
726
+ foreach ( $urls as $url ) {
727
+ $url_string = $url->getURL()->getString();
728
+ if ( 'data:' === substr( $url_string, 0, 5 ) ) {
729
+ continue;
730
+ }
731
+
732
+ $parsed_url = wp_parse_url( $url_string );
733
+ if ( ! empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) || '/' === substr( $parsed_url['path'], 0, 1 ) ) {
734
+ continue;
735
+ }
736
+
737
+ $relative_url = preg_replace( '#^\./#', '', $url->getURL()->getString() );
738
+
739
+ $real_url = $base_url . $relative_url;
740
+ do {
741
+ $real_url = preg_replace( '#[^/]+/../#', '', $real_url, -1, $count );
742
+ } while ( 0 !== $count );
743
+
744
+ $url->getURL()->setString( $real_url );
745
+ }
746
+ }
747
+
748
+ /**
749
+ * Process CSS rule set.
750
+ *
751
+ * @since 1.0
752
+ * @link https://www.ampproject.org/docs/design/responsive/style_pages#disallowed-styles
753
+ * @link https://www.ampproject.org/docs/design/responsive/style_pages#restricted-styles
754
+ *
755
+ * @param RuleSet $ruleset Ruleset.
756
+ * @param CSSList $css_list CSS List.
757
+ * @param array $options Options.
758
+ *
759
+ * @return array Validation errors.
760
+ */
761
+ private function process_css_declaration_block( RuleSet $ruleset, CSSList $css_list, $options ) {
762
+ $validation_errors = array();
763
+
764
+ // Remove disallowed properties.
765
+ if ( ! empty( $options['property_whitelist'] ) ) {
766
+ $properties = $ruleset->getRules();
767
+ foreach ( $properties as $property ) {
768
+ $vendorless_property_name = preg_replace( '/^-\w+-/', '', $property->getRule() );
769
+ if ( ! in_array( $vendorless_property_name, $options['property_whitelist'], true ) ) {
770
+ $validation_errors[] = array(
771
+ 'code' => 'illegal_css_property',
772
+ 'property_name' => $property->getRule(),
773
+ 'property_value' => $property->getValue(),
774
+ );
775
+ $ruleset->removeRule( $property->getRule() );
776
+ }
777
+ }
778
+ } else {
779
+ foreach ( $options['property_blacklist'] as $illegal_property_name ) {
780
+ $properties = $ruleset->getRules( $illegal_property_name );
781
+ foreach ( $properties as $property ) {
782
+ $validation_errors[] = array(
783
+ 'code' => 'illegal_css_property',
784
+ 'property_name' => $property->getRule(),
785
+ 'property_value' => $property->getValue(),
786
+ );
787
+ $ruleset->removeRule( $property->getRule() );
788
+ }
789
+ }
790
+ }
791
+
792
+ if ( $ruleset instanceof AtRuleSet && 'font-face' === $ruleset->atRuleName() ) {
793
+ $this->process_font_face_at_rule( $ruleset, $options );
794
+ }
795
+
796
+ $validation_errors = array_merge(
797
+ $validation_errors,
798
+ $this->transform_important_qualifiers( $ruleset, $css_list )
799
+ );
800
+
801
+ // Convert width to max-width when requested. See <https://github.com/Automattic/amp-wp/issues/494>.
802
+ if ( $options['convert_width_to_max_width'] ) {
803
+ $properties = $ruleset->getRules( 'width' );
804
+ foreach ( $properties as $property ) {
805
+ $width_property = new Rule( 'max-width' );
806
+ $width_property->setValue( $property->getValue() );
807
+ $ruleset->removeRule( $property );
808
+ $ruleset->addRule( $width_property, $property );
809
+ }
810
+ }
811
+
812
+ // Remove the ruleset if it is now empty.
813
+ if ( 0 === count( $ruleset->getRules() ) ) {
814
+ $css_list->remove( $ruleset );
815
+ }
816
+ // @todo Delete rules with selectors for -amphtml- class and i-amphtml- tags.
817
+ return $validation_errors;
818
+ }
819
+
820
+ /**
821
+ * Process @font-face by making src URLs non-relative and converting data: URLs into (assumed) file URLs.
822
+ *
823
+ * @since 1.0
824
+ *
825
+ * @param AtRuleSet $ruleset Ruleset for @font-face.
826
+ * @param array $options Options.
827
+ */
828
+ private function process_font_face_at_rule( AtRuleSet $ruleset, $options ) {
829
+ $src_properties = $ruleset->getRules( 'src' );
830
+ if ( empty( $src_properties ) ) {
831
+ return;
832
+ }
833
+
834
+ foreach ( $src_properties as $src_property ) {
835
+ $value = $src_property->getValue();
836
+ if ( ! ( $value instanceof RuleValueList ) ) {
837
+ continue;
838
+ }
839
+
840
+ /*
841
+ * The CSS Parser parses a src such as:
842
+ *
843
+ * url(data:application/font-woff;...) format('woff'),
844
+ * url('Genericons.ttf') format('truetype'),
845
+ * url('Genericons.svg#genericonsregular') format('svg')
846
+ *
847
+ * As a list of components consisting of:
848
+ *
849
+ * URL,
850
+ * RuleValueList( CSSFunction, URL ),
851
+ * RuleValueList( CSSFunction, URL ),
852
+ * CSSFunction
853
+ *
854
+ * Clearly the components here are not logically group