AMP for WordPress - Version 0.4

Version Description

  • Breaking change: The new template has changes to markup, class names, and styles that may not work with existing customizations. If you want to stay on the old template for now, you can use the following code snippet:
if ( function_exists( 'amp_backcompat_use_v03_templates' ) ) {
    amp_backcompat_use_v03_templates();
}

For more details, please see https://wordpress.org/support/topic/v0-4-whats-new-and-possible-breaking-changes/

Download this release

Release Info

Developer batmoo
Plugin Icon 128x128 AMP for WordPress
Version 0.4
Comparing to
See all releases

Code changes from version 0.3.3 to 0.4

Files changed (38) hide show
  1. amp.php +44 -2
  2. assets/images/placeholder-icon.png +0 -0
  3. assets/js/amp-customizer-design-preview.js +40 -0
  4. assets/js/amp-customizer-preview.js +15 -0
  5. back-compat/back-compat.php +12 -0
  6. back-compat/templates-v0-3/header-bar.php +13 -0
  7. back-compat/templates-v0-3/meta-author.php +9 -0
  8. back-compat/templates-v0-3/meta-taxonomy.php +15 -0
  9. back-compat/templates-v0-3/meta-time.php +12 -0
  10. back-compat/templates-v0-3/single.php +24 -0
  11. back-compat/templates-v0-3/style.php +242 -0
  12. includes/admin/class-amp-customizer.php +170 -0
  13. includes/admin/functions.php +71 -0
  14. includes/amp-helper-functions.php +6 -0
  15. includes/amp-post-template-actions.php +20 -0
  16. includes/class-amp-content.php +10 -0
  17. includes/class-amp-post-template.php +110 -1
  18. includes/embeds/class-amp-youtube-embed.php +12 -2
  19. includes/sanitizers/class-amp-base-sanitizer.php +4 -0
  20. includes/sanitizers/class-amp-blacklist-sanitizer.php +19 -4
  21. includes/sanitizers/class-amp-img-sanitizer.php +1 -1
  22. includes/sanitizers/class-amp-style-sanitizer.php +103 -0
  23. includes/settings/class-amp-customizer-design-settings.php +156 -0
  24. includes/settings/class-amp-customizer-settings.php +13 -0
  25. includes/utils/class-amp-dom-utils.php +45 -2
  26. readme.md +5 -0
  27. readme.txt +44 -6
  28. screenshot-1.png +0 -0
  29. screenshot-2.png +0 -0
  30. templates/featured-image.php +18 -0
  31. templates/footer.php +9 -0
  32. templates/header-bar.php +4 -4
  33. templates/meta-author.php +9 -8
  34. templates/meta-comments-link.php +11 -0
  35. templates/meta-taxonomy.php +15 -11
  36. templates/meta-time.php +2 -2
  37. templates/single.php +29 -12
  38. templates/style.php +280 -145
amp.php CHANGED
@@ -5,7 +5,7 @@
5
  * Plugin URI: https://github.com/automattic/amp-wp
6
  * Author: Automattic
7
  * Author URI: https://automattic.com
8
- * Version: 0.3.3
9
  * Text Domain: amp
10
  * Domain Path: /languages/
11
  * License: GPLv2 or later
@@ -13,17 +13,33 @@
13
 
14
  define( 'AMP__FILE__', __FILE__ );
15
  define( 'AMP__DIR__', dirname( __FILE__ ) );
 
16
 
 
17
  require_once( AMP__DIR__ . '/includes/amp-helper-functions.php' );
 
 
 
18
 
19
  register_activation_hook( __FILE__, 'amp_activate' );
20
  function amp_activate() {
21
- amp_init();
 
 
22
  flush_rewrite_rules();
23
  }
24
 
25
  register_deactivation_hook( __FILE__, 'amp_deactivate' );
26
  function amp_deactivate() {
 
 
 
 
 
 
 
 
 
27
  flush_rewrite_rules();
28
  }
29
 
@@ -114,3 +130,29 @@ function amp_render() {
114
  $template->load();
115
  exit;
116
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  * Plugin URI: https://github.com/automattic/amp-wp
6
  * Author: Automattic
7
  * Author URI: https://automattic.com
8
+ * Version: 0.4
9
  * Text Domain: amp
10
  * Domain Path: /languages/
11
  * License: GPLv2 or later
13
 
14
  define( 'AMP__FILE__', __FILE__ );
15
  define( 'AMP__DIR__', dirname( __FILE__ ) );
16
+ define( 'AMP__VERSION', '0.4' );
17
 
18
+ require_once( AMP__DIR__ . '/back-compat/back-compat.php' );
19
  require_once( AMP__DIR__ . '/includes/amp-helper-functions.php' );
20
+ require_once( AMP__DIR__ . '/includes/admin/functions.php' );
21
+ require_once( AMP__DIR__ . '/includes/settings/class-amp-customizer-settings.php' );
22
+ require_once( AMP__DIR__ . '/includes/settings/class-amp-customizer-design-settings.php' );
23
 
24
  register_activation_hook( __FILE__, 'amp_activate' );
25
  function amp_activate() {
26
+ if ( ! did_action( 'amp_init' ) ) {
27
+ amp_init();
28
+ }
29
  flush_rewrite_rules();
30
  }
31
 
32
  register_deactivation_hook( __FILE__, 'amp_deactivate' );
33
  function amp_deactivate() {
34
+ // We need to manually remove the amp endpoint
35
+ global $wp_rewrite;
36
+ foreach ( $wp_rewrite->endpoints as $index => $endpoint ) {
37
+ if ( AMP_QUERY_VAR === $endpoint[1] ) {
38
+ unset( $wp_rewrite->endpoints[ $index ] );
39
+ break;
40
+ }
41
+ }
42
+
43
  flush_rewrite_rules();
44
  }
45
 
130
  $template->load();
131
  exit;
132
  }
133
+
134
+ /**
135
+ * Bootstraps the AMP customizer.
136
+ *
137
+ * If the AMP customizer is enabled, initially drop the core widgets and menus panels. If the current
138
+ * preview page isn't flagged as an AMP template, the core panels will be re-added and the AMP panel
139
+ * hidden.
140
+ *
141
+ * @internal This callback must be hooked before priority 10 on 'plugins_loaded' to properly unhook
142
+ * the core panels.
143
+ *
144
+ * @since 0.4
145
+ */
146
+ function _amp_bootstrap_customizer() {
147
+ /**
148
+ * Filter whether to enable the AMP template customizer functionality.
149
+ *
150
+ * @param bool $enable Whether to enable the AMP customizer. Default true.
151
+ */
152
+ $amp_customizer_enabled = apply_filters( 'amp_customizer_is_enabled', true );
153
+
154
+ if ( true === $amp_customizer_enabled ) {
155
+ amp_init_customizer();
156
+ }
157
+ }
158
+ add_action( 'plugins_loaded', '_amp_bootstrap_customizer', 9 );
assets/images/placeholder-icon.png CHANGED
Binary file
assets/js/amp-customizer-design-preview.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( $ ) {
2
+ 'use strict';
3
+
4
+ // Nav bar text color.
5
+ wp.customize( 'amp_customizer[header_color]', function( value ) {
6
+ value.bind( function( to ) {
7
+ $( '.amp-wp-header a' ).css( 'color', to );
8
+ $( '.amp-wp-header div' ).css( 'color', to );
9
+ $( '.amp-wp-header .amp-wp-site-icon' ).css( 'border-color', to ).css( 'background-color', to );
10
+ } );
11
+ } );
12
+
13
+ // Nav bar background color.
14
+ wp.customize( 'amp_customizer[header_background_color]', function( value ) {
15
+ value.bind( function( to ) {
16
+ $( 'html, .amp-wp-header' ).css( 'background-color', to );
17
+ $( '.amp-wp-article a, .amp-wp-article a:visited, .amp-wp-footer a, .amp-wp-footer a:visited' ).css( 'color', to );
18
+ $( 'blockquote, .amp-wp-byline amp-img' ).css( 'border-color', to );
19
+ } );
20
+ } );
21
+
22
+ // AMP background color scheme.
23
+ wp.customize( 'amp_customizer[color_scheme]', function( value ) {
24
+ value.bind( function( to ) {
25
+ var colors = amp_customizer_design.color_schemes[ to ];
26
+
27
+ if ( ! colors ) {
28
+ console.error( 'Selected color scheme "%s" not registered.', to );
29
+ return;
30
+ }
31
+
32
+ $( 'body' ).css( 'background-color', colors.theme_color );
33
+ $( 'body, a:hover, a:active, a:focus, blockquote, .amp-wp-article, .amp-wp-title' ).css( 'color', colors.text_color );
34
+ $( '.amp-wp-meta, .wp-caption .wp-caption-text, .amp-wp-tax-category, .amp-wp-tax-tag, .amp-wp-footer p' ).css( 'color', colors.muted_text_color );
35
+ $( '.wp-caption .wp-caption-text, .amp-wp-comments-link a, .amp-wp-footer' ).css( 'border-color', colors.border_color );
36
+ $( '.amp-wp-iframe-placeholder, amp-carousel, amp-iframe, amp-youtube, amp-instagram, amp-vine' ).css( 'background-color', colors.border_color );
37
+ } );
38
+ } );
39
+
40
+ } )( jQuery );
assets/js/amp-customizer-preview.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( $ ) {
2
+ 'use strict';
3
+
4
+ // Don't allow navigation away from the AMP page in the preview.
5
+ // The Customizer breaks pretty spectacularly when that happens as it's only meant to work for AMP pages.
6
+ $( 'a' ).click( function( e ) {
7
+ var href = this.getAttribute( 'href' );
8
+ if ( href && href.indexOf( '#' ) === 0 ) {
9
+ return true;
10
+ }
11
+
12
+ return false;
13
+ } );
14
+
15
+ } )( jQuery );
back-compat/back-compat.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // If you want to use the template that shipped with v0.3 and earlier, you can use this to force that.
4
+ // Note that this may not stick around forever, so use caution and `function_exists`.
5
+ function amp_backcompat_use_v03_templates() {
6
+ add_filter( 'amp_customizer_is_enabled', '__return_false' );
7
+ add_filter( 'amp_post_template_dir', '_amp_backcompat_use_v03_templates_callback', 0 ); // early in case there are other overrides
8
+ }
9
+
10
+ function _amp_backcompat_use_v03_templates_callback( $templates ) {
11
+ return AMP__DIR__ . '/back-compat/templates-v0-3';
12
+ }
back-compat/templates-v0-3/header-bar.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php $site_icon_url = $this->get( 'site_icon_url' ); ?>
2
+
3
+ <nav class="amp-wp-title-bar">
4
+ <div>
5
+ <a href="<?php echo esc_url( $this->get( 'home_url' ) ); ?>">
6
+ <?php if ( $site_icon_url ) : ?>
7
+ <amp-img src="<?php echo esc_url( $site_icon_url ); ?>" width="32" height="32" class="amp-wp-site-icon"></amp-img>
8
+ <?php endif; ?>
9
+
10
+ <?php echo esc_html( $this->get( 'blog_name' ) ); ?>
11
+ </a>
12
+ </div>
13
+ </nav>
back-compat/templates-v0-3/meta-author.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php $post_author = $this->get( 'post_author' ); ?>
2
+ <li class="amp-wp-byline">
3
+ <?php if ( function_exists( 'get_avatar_url' ) ) : ?>
4
+ <amp-img src="<?php echo esc_url( get_avatar_url( $post_author->user_email, array(
5
+ 'size' => 24,
6
+ ) ) ); ?>" width="24" height="24" layout="fixed"></amp-img>
7
+ <?php endif; ?>
8
+ <span class="amp-wp-author"><?php echo esc_html( $post_author->display_name ); ?></span>
9
+ </li>
back-compat/templates-v0-3/meta-taxonomy.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php $categories = get_the_category_list( _x( ', ', 'Used between list items, there is a space after the comma.', 'amp' ) ); ?>
2
+ <?php if ( $categories ) : ?>
3
+ <li class="amp-wp-tax-category">
4
+ <span class="screen-reader-text">Categories:</span>
5
+ <?php echo $categories; ?>
6
+ </li>
7
+ <?php endif; ?>
8
+
9
+ <?php $tags = get_the_tag_list( '', _x( ', ', 'Used between list items, there is a space after the comma.', 'amp' ) ); ?>
10
+ <?php if ( $tags && ! is_wp_error( $tags ) ) : ?>
11
+ <li class="amp-wp-tax-tag">
12
+ <span class="screen-reader-text">Tags:</span>
13
+ <?php echo $tags; ?>
14
+ </li>
15
+ <?php endif; ?>
back-compat/templates-v0-3/meta-time.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <li class="amp-wp-posted-on">
2
+ <time datetime="<?php echo esc_attr( date( 'c', $this->get( 'post_publish_timestamp' ) ) ); ?>">
3
+ <?php
4
+ echo esc_html(
5
+ sprintf(
6
+ _x( '%s ago', '%s = human-readable time difference', 'amp' ),
7
+ human_time_diff( $this->get( 'post_publish_timestamp' ), current_time( 'timestamp' ) )
8
+ )
9
+ );
10
+ ?>
11
+ </time>
12
+ </li>
back-compat/templates-v0-3/single.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html amp <?php language_attributes(); ?>>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
6
+ <?php do_action( 'amp_post_template_head', $this ); ?>
7
+
8
+ <style amp-custom>
9
+ <?php $this->load_parts( array( 'style' ) ); ?>
10
+ <?php do_action( 'amp_post_template_css', $this ); ?>
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <?php $this->load_parts( array( 'header-bar' ) ); ?>
15
+ <div class="amp-wp-content">
16
+ <h1 class="amp-wp-title"><?php echo wp_kses_data( $this->get( 'post_title' ) ); ?></h1>
17
+ <ul class="amp-wp-meta">
18
+ <?php $this->load_parts( apply_filters( 'amp_post_template_meta_parts', array( 'meta-author', 'meta-time', 'meta-taxonomy' ) ) ); ?>
19
+ </ul>
20
+ <?php echo $this->get( 'post_amp_content' ); // amphtml content; no kses ?>
21
+ </div>
22
+ <?php do_action( 'amp_post_template_footer', $this ); ?>
23
+ </body>
24
+ </html>
back-compat/templates-v0-3/style.php ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Merriweather fonts */
2
+ @font-face {
3
+ font-family:'Merriweather';
4
+ src:url('https://s1.wp.com/i/fonts/merriweather/merriweather-regular-webfont.woff2') format('woff2'),
5
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-regular-webfont.woff') format('woff'),
6
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-regular-webfont.ttf') format('truetype'),
7
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-regular-webfont.svg#merriweatherregular') format('svg');
8
+ font-weight:400;
9
+ font-style:normal;
10
+ }
11
+
12
+ @font-face {
13
+ font-family:'Merriweather';
14
+ src:url('https://s1.wp.com/i/fonts/merriweather/merriweather-italic-webfont.woff2') format('woff2'),
15
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-italic-webfont.woff') format('woff'),
16
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-italic-webfont.ttf') format('truetype'),
17
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-italic-webfont.svg#merriweatheritalic') format('svg');
18
+ font-weight:400;
19
+ font-style:italic;
20
+ }
21
+
22
+ @font-face {
23
+ font-family:'Merriweather';
24
+ src:url('https://s1.wp.com/i/fonts/merriweather/merriweather-bold-webfont.woff2') format('woff2'),
25
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-bold-webfont.woff') format('woff'),
26
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-bold-webfont.ttf') format('truetype'),
27
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-bold-webfont.svg#merriweatherbold') format('svg');
28
+ font-weight:700;
29
+ font-style:normal;
30
+ }
31
+
32
+ @font-face {
33
+ font-family:'Merriweather';
34
+ src:url('https://s1.wp.com/i/fonts/merriweather/merriweather-bolditalic-webfont.woff2') format('woff2'),
35
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-bolditalic-webfont.woff') format('woff'),
36
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-bolditalic-webfont.ttf') format('truetype'),
37
+ url('https://s1.wp.com/i/fonts/merriweather/merriweather-bolditalic-webfont.svg#merriweatherbold_italic') format('svg');
38
+ font-weight:700;
39
+ font-style:italic;
40
+ }
41
+
42
+ /* Generic WP styling */
43
+ amp-img.alignright { float: right; margin: 0 0 1em 1em; }
44
+ amp-img.alignleft { float: left; margin: 0 1em 1em 0; }
45
+ amp-img.aligncenter { display: block; margin-left: auto; margin-right: auto; }
46
+ .alignright { float: right; }
47
+ .alignleft { float: left; }
48
+ .aligncenter { display: block; margin-left: auto; margin-right: auto; }
49
+
50
+ .wp-caption.alignleft { margin-right: 1em; }
51
+ .wp-caption.alignright { margin-left: 1em; }
52
+
53
+ .amp-wp-enforced-sizes {
54
+ /** Our sizes fallback is 100vw, and we have a padding on the container; the max-width here prevents the element from overflowing. **/
55
+ max-width: 100%;
56
+ }
57
+
58
+ .amp-wp-unknown-size img {
59
+ /** Worst case scenario when we can't figure out dimensions for an image. **/
60
+ /** Force the image into a box of fixed dimensions and use object-fit to scale. **/
61
+ object-fit: contain;
62
+ }
63
+
64
+ /* Template Styles */
65
+ .amp-wp-content, .amp-wp-title-bar div {
66
+ <?php $content_max_width = absint( $this->get( 'content_max_width' ) ); ?>
67
+ <?php if ( $content_max_width > 0 ) : ?>
68
+ max-width: <?php echo sprintf( '%dpx', $content_max_width ); ?>;
69
+ margin: 0 auto;
70
+ <?php endif; ?>
71
+ }
72
+
73
+ body {
74
+ font-family: 'Merriweather', Serif;
75
+ font-size: 16px;
76
+ line-height: 1.8;
77
+ background: #fff;
78
+ color: #3d596d;
79
+ padding-bottom: 100px;
80
+ }
81
+
82
+ .amp-wp-content {
83
+ padding: 16px;
84
+ overflow-wrap: break-word;
85
+ word-wrap: break-word;
86
+ font-weight: 400;
87
+ color: #3d596d;
88
+ }
89
+
90
+ .amp-wp-title {
91
+ margin: 36px 0 0 0;
92
+ font-size: 36px;
93
+ line-height: 1.258;
94
+ font-weight: 700;
95
+ color: #2e4453;
96
+ }
97
+
98
+ .amp-wp-meta {
99
+ margin-bottom: 16px;
100
+ }
101
+
102
+ p,
103
+ ol,
104
+ ul,
105
+ figure {
106
+ margin: 0 0 24px 0;
107
+ }
108
+
109
+ a,
110
+ a:visited {
111
+ color: #0087be;
112
+ }
113
+
114
+ a:hover,
115
+ a:active,
116
+ a:focus {
117
+ color: #33bbe3;
118
+ }
119
+
120
+
121
+ /* UI Fonts */
122
+ .amp-wp-meta,
123
+ nav.amp-wp-title-bar,
124
+ .wp-caption-text {
125
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen-Sans", "Ubuntu", "Cantarell", "Helvetica Neue", sans-serif;
126
+ font-size: 15px;
127
+ }
128
+
129
+
130
+ /* Meta */
131
+ ul.amp-wp-meta {
132
+ padding: 24px 0 0 0;
133
+ margin: 0 0 24px 0;
134
+ }
135
+
136
+ ul.amp-wp-meta li {
137
+ list-style: none;
138
+ display: inline-block;
139
+ margin: 0;
140
+ line-height: 24px;
141
+ white-space: nowrap;
142
+ overflow: hidden;
143
+ text-overflow: ellipsis;
144
+ max-width: 300px;
145
+ }
146
+
147
+ ul.amp-wp-meta li:before {
148
+ content: "\2022";
149
+ margin: 0 8px;
150
+ }
151
+
152
+ ul.amp-wp-meta li:first-child:before {
153
+ display: none;
154
+ }
155
+
156
+ .amp-wp-meta,
157
+ .amp-wp-meta a {
158
+ color: #4f748e;
159
+ }
160
+
161
+ .amp-wp-meta .screen-reader-text {
162
+ /* from twentyfifteen */
163
+ clip: rect(1px, 1px, 1px, 1px);
164
+ height: 1px;
165
+ overflow: hidden;
166
+ position: absolute;
167
+ width: 1px;
168
+ }
169
+
170
+ .amp-wp-byline amp-img {
171
+ border-radius: 50%;
172
+ border: 0;
173
+ background: #f3f6f8;
174
+ position: relative;
175
+ top: 6px;
176
+ margin-right: 6px;
177
+ }
178
+
179
+ /* Titlebar */
180
+ nav.amp-wp-title-bar {
181
+ background: <?php echo esc_html( $this->get_customizer_setting( 'navbar_background', self::DEFAULT_NAVBAR_BACKGROUND ) ); // not ideal for escaping here, but better than nothing? ?>;
182
+ padding: 0 16px;
183
+ }
184
+
185
+ nav.amp-wp-title-bar div {
186
+ line-height: 54px;
187
+ color: <?php echo esc_html( $this->get_customizer_setting( 'navbar_color', self::DEFAULT_NAVBAR_COLOR ) ); ?>;
188
+ }
189
+
190
+ nav.amp-wp-title-bar a {
191
+ color: <?php echo esc_html( $this->get_customizer_setting( 'navbar_color', self::DEFAULT_NAVBAR_COLOR ) ); ?>;
192
+ text-decoration: none;
193
+ }
194
+
195
+ nav.amp-wp-title-bar .amp-wp-site-icon {
196
+ /** site icon is 32px **/
197
+ float: left;
198
+ margin: 11px 8px 0 0;
199
+ border-radius: 50%;
200
+ }
201
+
202
+ /* Captions */
203
+ .wp-caption-text {
204
+ padding: 8px 16px;
205
+ font-style: italic;
206
+ }
207
+
208
+
209
+ /* Quotes */
210
+ blockquote {
211
+ padding: 16px;
212
+ margin: 8px 0 24px 0;
213
+ border-left: 2px solid #87a6bc;
214
+ color: #4f748e;
215
+ background: #e9eff3;
216
+ }
217
+
218
+ blockquote p:last-child {
219
+ margin-bottom: 0;
220
+ }
221
+
222
+ /* Other Elements */
223
+ amp-carousel {
224
+ background: #000;
225
+ }
226
+
227
+ amp-iframe,
228
+ amp-youtube,
229
+ amp-instagram,
230
+ amp-vine {
231
+ background: #f3f6f8;
232
+ }
233
+
234
+ amp-carousel > amp-img > img {
235
+ object-fit: contain;
236
+ }
237
+
238
+ .amp-wp-iframe-placeholder {
239
+ background: #f3f6f8 url( <?php echo esc_url( $this->get( 'placeholder_image_url' ) ); ?> ) no-repeat center 40%;
240
+ background-size: 48px 48px;
241
+ min-height: 48px;
242
+ }
includes/admin/class-amp-customizer.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AMP class that implements a template style editor in the Customizer.
4
+ *
5
+ * A direct, formed link to the AMP editor in the Customizer is added via
6
+ * {@see amp_customizer_editor_link()} as a submenu to the Appearance menu.
7
+ *
8
+ * @since 0.4
9
+ */
10
+ class AMP_Template_Customizer {
11
+ /**
12
+ * AMP template editor panel ID.
13
+ *
14
+ * @since 0.4
15
+ * @var string
16
+ */
17
+ const PANEL_ID = 'amp_panel';
18
+
19
+ /**
20
+ * Customizer instance.
21
+ *
22
+ * @since 0.4
23
+ * @access protected
24
+ * @var WP_Customize_Manager $wp_customize
25
+ */
26
+ protected $wp_customize;
27
+
28
+ /**
29
+ * Initialize the template Customizer feature class.
30
+ *
31
+ * @static
32
+ * @since 0.4
33
+ * @access public
34
+ *
35
+ * @param WP_Customize_Manager $wp_customize Customizer instance.
36
+ */
37
+ public static function init( $wp_customize ) {
38
+ $self = new self();
39
+
40
+ $self->wp_customize = $wp_customize;
41
+
42
+ do_action( 'amp_customizer_init', $self );
43
+
44
+ // Settings need to be registered for regular customize requests as well (since save is handled there)
45
+ $self->register_settings();
46
+
47
+ // Our custom panels only need to go for AMP Customizer requests though
48
+ if ( self::is_amp_customizer() ) {
49
+ if ( empty( $_GET['url'] ) ) {
50
+ $wp_customize->set_preview_url( amp_admin_get_preview_permalink() );
51
+ }
52
+
53
+ $self->_unregister_core_ui();
54
+ $self->register_ui();
55
+ } elseif ( is_customize_preview() ) {
56
+ // Delay preview-specific actions until we're sure we're rendering an AMP page, since it's too early for `is_amp_endpoint()` here.
57
+ add_action( 'pre_amp_render_post', array( $self, 'init_preview' ) );
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Filters the core components to unhook the nav_menus and widgets panels.
63
+ *
64
+ * @since 0.4
65
+ * @access private
66
+ *
67
+ * @return array Array of core Customizer components to keep active.
68
+ */
69
+ public static function _unregister_core_panels( $panels ) {
70
+ if ( self::is_amp_customizer() ) {
71
+ $panels = array();
72
+ }
73
+ return $panels;
74
+ }
75
+
76
+ /**
77
+ * Removes all non-AMP sections and panels.
78
+ *
79
+ * Provides a clean, standalone instance-like experience by removing all non-AMP registered panels and sections.
80
+ *
81
+ * @since 0.4
82
+ * @access private
83
+ */
84
+ private function _unregister_core_ui() {
85
+ $panels = $this->wp_customize->panels();
86
+ $sections = $this->wp_customize->sections();
87
+
88
+ foreach ( $panels as $panel_id => $object ) {
89
+ $this->wp_customize->remove_panel( $panel_id );
90
+ }
91
+
92
+ foreach ( $sections as $section_id => $object ) {
93
+ $this->wp_customize->remove_section( $section_id );
94
+ }
95
+ }
96
+
97
+ public function init_preview() {
98
+ // Preview needs controls registered too for postMessage communication.
99
+ $this->register_ui();
100
+
101
+ add_action( 'amp_post_template_footer', array( $this, 'add_preview_scripts' ) );
102
+ }
103
+
104
+ /**
105
+ * Sets up the AMP Customizer preview.
106
+ */
107
+ public function register_ui() {
108
+ add_action( 'customize_controls_enqueue_scripts', array( $this, 'add_customizer_scripts' ) );
109
+ add_filter( 'customize_previewable_devices', array( $this, 'force_mobile_preview' ) );
110
+
111
+ $this->wp_customize->add_panel( self::PANEL_ID, array(
112
+ 'type' => 'amp',
113
+ 'title' => __( 'AMP', 'amp' ),
114
+ 'description' => sprintf( __( '<a href="%s" target="_blank">The AMP Project</a> is a Google-led initiative that dramatically improves loading speeds on phones and tablets. You can use the Customizer to preview changes to your AMP template before publishing them.', 'amp' ), 'https://ampproject.org' ),
115
+ ) );
116
+
117
+ do_action( 'amp_customizer_register_ui', $this->wp_customize );
118
+ }
119
+
120
+ /**
121
+ * Registers settings for customizing AMP templates.
122
+ *
123
+ * @since 0.4
124
+ * @access public
125
+ */
126
+ public function register_settings() {
127
+ do_action( 'amp_customizer_register_settings', $this->wp_customize );
128
+ }
129
+
130
+ public function add_customizer_scripts() {
131
+ wp_enqueue_script( 'wp-util' ); // fix `wp.template is not a function`
132
+ do_action( 'amp_customizer_enqueue_scripts' );
133
+ }
134
+
135
+ /**
136
+ * Enqueues scripts and fires the 'wp_footer' action so we can output customizer scripts.
137
+ *
138
+ * This breaks AMP validation in the customizer but is necessary for the live preview.
139
+ *
140
+ * @since 0.4
141
+ * @access public
142
+ */
143
+ public function add_preview_scripts() {
144
+ wp_enqueue_script(
145
+ 'amp-customizer',
146
+ amp_get_asset_url( 'js/amp-customizer-preview.js' ),
147
+ array( 'jquery', 'customize-preview', 'wp-util' ),
148
+ $version = false,
149
+ $footer = true
150
+ );
151
+
152
+ do_action( 'amp_customizer_enqueue_preview_scripts', $this->wp_customize );
153
+
154
+ /** This action is documented in wp-includes/general-template.php */
155
+ do_action( 'wp_footer' );
156
+ }
157
+
158
+ public function force_mobile_preview( $devices ) {
159
+ if ( isset( $devices[ 'mobile' ] ) ) {
160
+ $devices['mobile']['default'] = true;
161
+ unset( $devices['desktop']['default'] );
162
+ }
163
+
164
+ return $devices;
165
+ }
166
+
167
+ public static function is_amp_customizer() {
168
+ return ! empty( $_REQUEST[ AMP_CUSTOMIZER_QUERY_VAR ] );
169
+ }
170
+ }
includes/admin/functions.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Callbacks for adding AMP-related things to the admin.
3
+
4
+ define( 'AMP_CUSTOMIZER_QUERY_VAR', 'customize_amp' );
5
+
6
+ /**
7
+ * Sets up the AMP template editor for the Customizer.
8
+ */
9
+ function amp_init_customizer() {
10
+ require_once( AMP__DIR__ . '/includes/admin/class-amp-customizer.php' );
11
+
12
+ // Drop core panels (menus, widgets) from the AMP customizer
13
+ add_filter( 'customize_loaded_components', array( 'AMP_Template_Customizer', '_unregister_core_panels' ) );
14
+
15
+ // Fire up the AMP Customizer
16
+ add_action( 'customize_register', array( 'AMP_Template_Customizer', 'init' ), 500 );
17
+
18
+ // Add some basic design settings + controls to the Customizer
19
+ add_action( 'amp_init', array( 'AMP_Customizer_Design_Settings', 'init' ) );
20
+
21
+ // Add a link to the Customizer
22
+ add_action( 'admin_menu', 'amp_add_customizer_link' );
23
+ }
24
+
25
+ /**
26
+ * Registers a submenu page to access the AMP template editor panel in the Customizer.
27
+ */
28
+ function amp_add_customizer_link() {
29
+ // Teensy little hack on menu_slug, but it works. No redirect!
30
+ $menu_slug = add_query_arg( array(
31
+ 'autofocus[panel]' => AMP_Template_Customizer::PANEL_ID,
32
+ 'return' => rawurlencode( admin_url() ),
33
+ AMP_CUSTOMIZER_QUERY_VAR => true,
34
+ ), 'customize.php' );
35
+
36
+ // Add the theme page.
37
+ $page = add_theme_page(
38
+ __( 'AMP', 'amp' ),
39
+ __( 'AMP', 'amp' ),
40
+ 'edit_theme_options',
41
+ $menu_slug
42
+ );
43
+ }
44
+
45
+ function amp_admin_get_preview_permalink() {
46
+ /**
47
+ * Filter the post type to retrieve the latest of for use in the AMP template customizer.
48
+ *
49
+ * @param string $post_type Post type slug. Default 'post'.
50
+ */
51
+ $post_type = (string) apply_filters( 'amp_customizer_post_type', 'post' );
52
+
53
+ if ( ! post_type_supports( $post_type, 'amp' ) ) {
54
+ return;
55
+ }
56
+
57
+ $post_ids = get_posts( array(
58
+ 'post_status' => 'publish',
59
+ 'post_type' => $post_type,
60
+ 'posts_per_page' => 1,
61
+ 'fields' => 'ids',
62
+ ) );
63
+
64
+ if ( empty( $post_ids ) ) {
65
+ return false;
66
+ }
67
+
68
+ $post_id = $post_ids[0];
69
+
70
+ return amp_get_permalink( $post_id );
71
+ }
includes/amp-helper-functions.php CHANGED
@@ -1,6 +1,12 @@
1
  <?php
2
 
3
  function amp_get_permalink( $post_id ) {
 
 
 
 
 
 
4
  if ( '' != get_option( 'permalink_structure' ) ) {
5
  $amp_url = trailingslashit( get_permalink( $post_id ) ) . user_trailingslashit( AMP_QUERY_VAR, 'single_amp' );
6
  } else {
1
  <?php
2
 
3
  function amp_get_permalink( $post_id ) {
4
+ $pre_url = apply_filters( 'amp_pre_get_permalink', false, $post_id );
5
+
6
+ if ( false !== $pre_url ) {
7
+ return $pre_url;
8
+ }
9
+
10
  if ( '' != get_option( 'permalink_structure' ) ) {
11
  $amp_url = trailingslashit( get_permalink( $post_id ) ) . user_trailingslashit( AMP_QUERY_VAR, 'single_amp' );
12
  } else {
includes/amp-post-template-actions.php CHANGED
@@ -25,6 +25,14 @@ function amp_post_template_add_scripts( $amp_template ) {
25
  <?php
26
  }
27
 
 
 
 
 
 
 
 
 
28
  add_action( 'amp_post_template_head', 'amp_post_template_add_boilerplate_css' );
29
  function amp_post_template_add_boilerplate_css( $amp_template ) {
30
  ?>
@@ -43,6 +51,18 @@ function amp_post_template_add_schemaorg_metadata( $amp_template ) {
43
  <?php
44
  }
45
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  add_action( 'amp_post_template_data', 'amp_post_template_add_analytics_script' );
47
  function amp_post_template_add_analytics_script( $data ) {
48
  if ( ! empty( $data['amp_analytics'] ) ) {
25
  <?php
26
  }
27
 
28
+ add_action( 'amp_post_template_head', 'amp_post_template_add_fonts' );
29
+ function amp_post_template_add_fonts( $amp_template ) {
30
+ $font_urls = $amp_template->get( 'font_urls', array() );
31
+ foreach( $font_urls as $slug => $url ) : ?>
32
+ <link rel="stylesheet" href="<?php echo esc_url( $url ); ?>">
33
+ <?php endforeach;
34
+ }
35
+
36
  add_action( 'amp_post_template_head', 'amp_post_template_add_boilerplate_css' );
37
  function amp_post_template_add_boilerplate_css( $amp_template ) {
38
  ?>
51
  <?php
52
  }
53
 
54
+ add_action( 'amp_post_template_css', 'amp_post_template_add_styles', 99 );
55
+ function amp_post_template_add_styles( $amp_template ) {
56
+ $styles = $amp_template->get( 'post_amp_styles' );
57
+ if ( ! empty( $styles ) ) {
58
+ echo '/* Inline styles */' . PHP_EOL;
59
+ foreach ( $styles as $selector => $declarations ) {
60
+ $declarations = implode( ";", $declarations ) . ";";
61
+ printf( '%1$s{%2$s}', $selector, $declarations );
62
+ }
63
+ }
64
+ }
65
+
66
  add_action( 'amp_post_template_data', 'amp_post_template_add_analytics_script' );
67
  function amp_post_template_add_analytics_script( $data ) {
68
  if ( ! empty( $data['amp_analytics'] ) ) {
includes/class-amp-content.php CHANGED
@@ -8,6 +8,7 @@ class AMP_Content {
8
  private $content;
9
  private $amp_content = '';
10
  private $amp_scripts = array();
 
11
  private $args = array();
12
  private $embed_handler_classes = array();
13
  private $sanitizer_classes = array();
@@ -29,6 +30,10 @@ class AMP_Content {
29
  return $this->amp_scripts;
30
  }
31
 
 
 
 
 
32
  private function transform() {
33
  $content = $this->content;
34
 
@@ -47,6 +52,10 @@ class AMP_Content {
47
  $this->amp_scripts = array_merge( $this->amp_scripts, $scripts );
48
  }
49
 
 
 
 
 
50
  private function register_embed_handlers() {
51
  $embed_handlers = array();
52
 
@@ -85,6 +94,7 @@ class AMP_Content {
85
 
86
  $sanitizer->sanitize();
87
  $this->add_scripts( $sanitizer->get_scripts() );
 
88
  }
89
 
90
  return AMP_DOM_Utils::get_content_from_dom( $dom );
8
  private $content;
9
  private $amp_content = '';
10
  private $amp_scripts = array();
11
+ private $amp_styles = array();
12
  private $args = array();
13
  private $embed_handler_classes = array();
14
  private $sanitizer_classes = array();
30
  return $this->amp_scripts;
31
  }
32
 
33
+ public function get_amp_styles() {
34
+ return $this->amp_styles;
35
+ }
36
+
37
  private function transform() {
38
  $content = $this->content;
39
 
52
  $this->amp_scripts = array_merge( $this->amp_scripts, $scripts );
53
  }
54
 
55
+ private function add_styles( $styles ) {
56
+ $this->amp_styles = array_merge( $this->amp_styles, $styles );
57
+ }
58
+
59
  private function register_embed_handlers() {
60
  $embed_handlers = array();
61
 
94
 
95
  $sanitizer->sanitize();
96
  $this->add_scripts( $sanitizer->get_scripts() );
97
+ $this->add_styles( $sanitizer->get_styles() );
98
  }
99
 
100
  return AMP_DOM_Utils::get_content_from_dom( $dom );
includes/class-amp-post-template.php CHANGED
@@ -6,6 +6,7 @@ require_once( AMP__DIR__ . '/includes/utils/class-amp-string-utils.php' );
6
 
7
  require_once( AMP__DIR__ . '/includes/class-amp-content.php' );
8
 
 
9
  require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-blacklist-sanitizer.php' );
10
  require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-img-sanitizer.php' );
11
  require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-video-sanitizer.php' );
@@ -23,6 +24,10 @@ class AMP_Post_Template {
23
  const SITE_ICON_SIZE = 32;
24
  const CONTENT_MAX_WIDTH = 600;
25
 
 
 
 
 
26
  private $template_dir;
27
  private $data;
28
 
@@ -45,13 +50,24 @@ class AMP_Post_Template {
45
  'canonical_url' => get_permalink( $post_id ),
46
  'home_url' => home_url(),
47
  'blog_name' => get_bloginfo( 'name' ),
 
48
 
49
  'site_icon_url' => apply_filters( 'amp_site_icon_url', function_exists( 'get_site_icon_url' ) ? get_site_icon_url( self::SITE_ICON_SIZE ) : '' ),
50
  'placeholder_image_url' => amp_get_asset_url( 'images/placeholder-icon.png' ),
51
 
 
 
 
 
52
  'amp_runtime_script' => 'https://cdn.ampproject.org/v0.js',
53
  'amp_component_scripts' => array(),
54
 
 
 
 
 
 
 
55
  /**
56
  * Add amp-analytics tags.
57
  *
@@ -63,10 +79,11 @@ class AMP_Post_Template {
63
  * @param object $post The current post.
64
  */
65
  'amp_analytics' => apply_filters( 'amp_post_template_analytics', array(), $this->post ),
66
- );
67
 
68
  $this->build_post_content();
69
  $this->build_post_data();
 
70
 
71
  $this->data = apply_filters( 'amp_post_template_data', $this->data, $this->post );
72
  }
@@ -81,6 +98,15 @@ class AMP_Post_Template {
81
  return $default;
82
  }
83
 
 
 
 
 
 
 
 
 
 
84
  public function load() {
85
  $this->load_parts( array( 'single' ) );
86
  }
@@ -160,6 +186,25 @@ class AMP_Post_Template {
160
  }
161
 
162
  $this->add_data_by_key( 'metadata', apply_filters( 'amp_post_template_metadata', $metadata, $this->post ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  }
164
 
165
  private function build_post_content() {
@@ -173,6 +218,7 @@ class AMP_Post_Template {
173
  'AMP_Gallery_Embed_Handler' => array(),
174
  ), $this->post ),
175
  apply_filters( 'amp_content_sanitizers', array(
 
176
  'AMP_Blacklist_Sanitizer' => array(),
177
  'AMP_Img_Sanitizer' => array(),
178
  'AMP_Video_Sanitizer' => array(),
@@ -188,6 +234,68 @@ class AMP_Post_Template {
188
 
189
  $this->add_data_by_key( 'post_amp_content', $amp_content->get_amp_content() );
190
  $this->merge_data_for_key( 'amp_component_scripts', $amp_content->get_amp_scripts() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  }
192
 
193
  /**
@@ -248,6 +356,7 @@ class AMP_Post_Template {
248
  return;
249
  }
250
 
 
251
  include( $file );
252
  }
253
 
6
 
7
  require_once( AMP__DIR__ . '/includes/class-amp-content.php' );
8
 
9
+ require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-style-sanitizer.php' );
10
  require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-blacklist-sanitizer.php' );
11
  require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-img-sanitizer.php' );
12
  require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-video-sanitizer.php' );
24
  const SITE_ICON_SIZE = 32;
25
  const CONTENT_MAX_WIDTH = 600;
26
 
27
+ // Needed for 0.3 back-compat
28
+ const DEFAULT_NAVBAR_BACKGROUND = '#0a89c0';
29
+ const DEFAULT_NAVBAR_COLOR = '#fff';
30
+
31
  private $template_dir;
32
  private $data;
33
 
50
  'canonical_url' => get_permalink( $post_id ),
51
  'home_url' => home_url(),
52
  'blog_name' => get_bloginfo( 'name' ),
53
+ 'body_class' => '',
54
 
55
  'site_icon_url' => apply_filters( 'amp_site_icon_url', function_exists( 'get_site_icon_url' ) ? get_site_icon_url( self::SITE_ICON_SIZE ) : '' ),
56
  'placeholder_image_url' => amp_get_asset_url( 'images/placeholder-icon.png' ),
57
 
58
+ 'featured_image' => false,
59
+ 'comments_link_url' => false,
60
+ 'comments_link_text' => false,
61
+
62
  'amp_runtime_script' => 'https://cdn.ampproject.org/v0.js',
63
  'amp_component_scripts' => array(),
64
 
65
+ 'customizer_settings' => array(),
66
+
67
+ 'font_urls' => array(
68
+ 'merriweather' => 'https://fonts.googleapis.com/css?family=Merriweather:400,400italic,700,700italic',
69
+ ),
70
+
71
  /**
72
  * Add amp-analytics tags.
73
  *
79
  * @param object $post The current post.
80
  */
81
  'amp_analytics' => apply_filters( 'amp_post_template_analytics', array(), $this->post ),
82
+ );
83
 
84
  $this->build_post_content();
85
  $this->build_post_data();
86
+ $this->build_customizer_settings();
87
 
88
  $this->data = apply_filters( 'amp_post_template_data', $this->data, $this->post );
89
  }
98
  return $default;
99
  }
100
 
101
+ public function get_customizer_setting( $name, $default = null ) {
102
+ $settings = $this->get( 'customizer_settings' );
103
+ if ( ! empty( $settings[ $name ] ) ) {
104
+ return $settings[ $name ];
105
+ }
106
+
107
+ return $default;
108
+ }
109
+
110
  public function load() {
111
  $this->load_parts( array( 'single' ) );
112
  }
186
  }
187
 
188
  $this->add_data_by_key( 'metadata', apply_filters( 'amp_post_template_metadata', $metadata, $this->post ) );
189
+
190
+ $this->build_post_featured_image();
191
+ $this->build_post_commments_data();
192
+ }
193
+
194
+ private function build_post_commments_data() {
195
+ if ( ! post_type_supports( $this->post->post_type, 'comments' ) ) {
196
+ return;
197
+ }
198
+
199
+ $comments_link_url = get_comments_link( $this->ID );
200
+ $comments_link_text = comments_open( $this->ID )
201
+ ? __( 'Leave a Comment', 'amp' )
202
+ : __( 'View Comments', 'amp' );
203
+
204
+ $this->add_data( array(
205
+ 'comments_link_url' => $comments_link_url,
206
+ 'comments_link_text' => $comments_link_text,
207
+ ) );
208
  }
209
 
210
  private function build_post_content() {
218
  'AMP_Gallery_Embed_Handler' => array(),
219
  ), $this->post ),
220
  apply_filters( 'amp_content_sanitizers', array(
221
+ 'AMP_Style_Sanitizer' => array(),
222
  'AMP_Blacklist_Sanitizer' => array(),
223
  'AMP_Img_Sanitizer' => array(),
224
  'AMP_Video_Sanitizer' => array(),
234
 
235
  $this->add_data_by_key( 'post_amp_content', $amp_content->get_amp_content() );
236
  $this->merge_data_for_key( 'amp_component_scripts', $amp_content->get_amp_scripts() );
237
+ $this->add_data_by_key( 'post_amp_styles', $amp_content->get_amp_styles() );
238
+ }
239
+
240
+ private function build_post_featured_image() {
241
+ $post_id = $this->ID;
242
+ $featured_html = get_the_post_thumbnail( $post_id, 'large' );
243
+
244
+ // Skip featured image if no featured image is available.
245
+ if ( ! $featured_html ) {
246
+ return;
247
+ }
248
+
249
+ $featured_id = get_post_thumbnail_id( $post_id );
250
+
251
+ // If an image with the same ID as the featured image exists in the content, skip the featured image markup.
252
+ // Prevents duplicate images, which is especially problematic for photo blogs.
253
+ // A bit crude but it's fast and should cover most cases.
254
+ $post_content = $this->post->post_content;
255
+ if ( false !== strpos( $post_content, 'wp-image-' . $featured_id )
256
+ || false !== strpos( $post_content, 'attachment_' . $featured_id ) ) {
257
+ return;
258
+ }
259
+
260
+ $featured_image = get_post( $featured_id );
261
+
262
+ remove_filter( 'the_content', 'wpautop' ); // We don't want our image wrapped in a <p>
263
+ $featured_amp_content = new AMP_Content(
264
+ $featured_html,
265
+ array(),
266
+ array(
267
+ 'AMP_Img_Sanitizer' => array(),
268
+ ),
269
+ array(
270
+ 'content_max_width' => $this->get( 'content_max_width' ),
271
+ )
272
+ );
273
+ add_filter( 'the_content', 'wpautop' );
274
+
275
+ $this->add_data_by_key( 'featured_image', array(
276
+ 'amp_html' => $featured_amp_content->get_amp_content(),
277
+ 'caption' => $featured_image->post_excerpt,
278
+ ) );
279
+ }
280
+
281
+ private function build_customizer_settings() {
282
+ $settings = AMP_Customizer_Settings::get_settings();
283
+
284
+ /**
285
+ * Filter AMP Customizer settings.
286
+ *
287
+ * Inject your Customizer settings here to make them accessible via the getter in your custom style.php template.
288
+ *
289
+ * Example:
290
+ *
291
+ * echo esc_html( $this->get_customizer_setting( 'your_setting_key', 'your_default_value' ) );
292
+ *
293
+ * @since 0.4
294
+ *
295
+ * @param array $settings Array of AMP Customizer settings.
296
+ * @param WP_Post $post Current post object.
297
+ */
298
+ $this->add_data_by_key( 'customizer_settings', apply_filters( 'amp_post_template_customizer_settings', $settings, $this->post ) );
299
  }
300
 
301
  /**
356
  return;
357
  }
358
 
359
+ do_action( 'amp_post_template_include_' . $template_type, $this );
360
  include( $file );
361
  }
362
 
includes/embeds/class-amp-youtube-embed.php CHANGED
@@ -5,7 +5,8 @@ require_once( AMP__DIR__ . '/includes/embeds/class-amp-base-embed-handler.php' )
5
  // Much of this class is borrowed from Jetpack embeds
6
  class AMP_YouTube_Embed_Handler extends AMP_Base_Embed_Handler {
7
  const SHORT_URL_HOST = 'youtu.be';
8
- const URL_PATTERN = '#https?://(?:www\.)?(?:youtube.com/(?:v/|e/|embed/|playlist|watch[/\#?])|youtu\.be/).*#i';
 
9
  const RATIO = 0.5625;
10
 
11
  protected $DEFAULT_WIDTH = 600;
@@ -104,7 +105,7 @@ class AMP_YouTube_Embed_Handler extends AMP_Base_Embed_Handler {
104
  parse_str( $parsed_url['query'], $query_args );
105
 
106
  if ( isset( $query_args['v'] ) ) {
107
- $video_id = $query_args['v'];
108
  }
109
  }
110
 
@@ -119,4 +120,13 @@ class AMP_YouTube_Embed_Handler extends AMP_Base_Embed_Handler {
119
 
120
  return $video_id;
121
  }
 
 
 
 
 
 
 
 
 
122
  }
5
  // Much of this class is borrowed from Jetpack embeds
6
  class AMP_YouTube_Embed_Handler extends AMP_Base_Embed_Handler {
7
  const SHORT_URL_HOST = 'youtu.be';
8
+ // Only handling single videos. Playlists are handled elsewhere.
9
+ const URL_PATTERN = '#https?://(?:www\.)?(?:youtube.com/(?:v/|e/|embed/|watch[/\#?])|youtu\.be/).*#i';
10
  const RATIO = 0.5625;
11
 
12
  protected $DEFAULT_WIDTH = 600;
105
  parse_str( $parsed_url['query'], $query_args );
106
 
107
  if ( isset( $query_args['v'] ) ) {
108
+ $video_id = $this->sanitize_v_arg( $query_args['v'] );
109
  }
110
  }
111
 
120
 
121
  return $video_id;
122
  }
123
+
124
+ private function sanitize_v_arg( $value ) {
125
+ // Deal with broken params like `?v=123?rel=0`
126
+ if ( false !== strpos( $value, '?' ) ) {
127
+ $value = strtok( $value, '?' );
128
+ }
129
+
130
+ return $value;
131
+ }
132
  }
includes/sanitizers/class-amp-base-sanitizer.php CHANGED
@@ -20,6 +20,10 @@ abstract class AMP_Base_Sanitizer {
20
  return array();
21
  }
22
 
 
 
 
 
23
  protected function get_body_node() {
24
  return $this->dom->getElementsByTagName( 'body' )->item( 0 );
25
  }
20
  return array();
21
  }
22
 
23
+ public function get_styles() {
24
+ return array();
25
+ }
26
+
27
  protected function get_body_node() {
28
  return $this->dom->getElementsByTagName( 'body' )->item( 0 );
29
  }
includes/sanitizers/class-amp-blacklist-sanitizer.php CHANGED
@@ -37,9 +37,11 @@ class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
37
  // Some nodes may contain valid content but are themselves invalid.
38
  // Remove the node but preserve the children.
39
  if ( 'font' === $node_name ) {
40
- $this->replace_node_with_children( $node );
 
41
  } elseif ( 'a' === $node_name && false === $this->validate_a_node( $node ) ) {
42
- $this->replace_node_with_children( $node );
 
43
  }
44
 
45
  if ( $node->hasAttributes() ) {
@@ -144,21 +146,28 @@ class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
144
  }
145
 
146
  $valid_protocols = array( 'http', 'https', 'mailto', 'sms', 'tel', 'viber', 'whatsapp' );
 
147
  $protocol = strtok( $href, ':' );
 
148
  if ( false === filter_var( $href, FILTER_VALIDATE_URL )
149
- || ! in_array( $protocol, $valid_protocols ) ) {
 
 
 
 
150
  return false;
151
  }
152
 
153
  return true;
154
  }
155
 
156
- private function replace_node_with_children( $node ) {
157
  // If the node has children and also has a parent node,
158
  // clone and re-add all the children just before current node.
159
  if ( $node->hasChildNodes() && $node->parentNode ) {
160
  foreach ( $node->childNodes as $child_node ) {
161
  $new_child = $child_node->cloneNode( true );
 
162
  $node->parentNode->insertBefore( $new_child, $node );
163
  }
164
  }
@@ -208,6 +217,9 @@ class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
208
  'embed',
209
  'embedvideo',
210
 
 
 
 
211
  // These are converted into amp-* versions
212
  //'img',
213
  //'video',
@@ -220,6 +232,9 @@ class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
220
  return $this->merge_defaults_with_args( 'add_blacklisted_attributes', array(
221
  'style',
222
  'size',
 
 
 
223
  ) );
224
  }
225
  }
37
  // Some nodes may contain valid content but are themselves invalid.
38
  // Remove the node but preserve the children.
39
  if ( 'font' === $node_name ) {
40
+ $this->replace_node_with_children( $node, $bad_attributes, $bad_protocols );
41
+ return;
42
  } elseif ( 'a' === $node_name && false === $this->validate_a_node( $node ) ) {
43
+ $this->replace_node_with_children( $node, $bad_attributes, $bad_protocols );
44
+ return;
45
  }
46
 
47
  if ( $node->hasAttributes() ) {
146
  }
147
 
148
  $valid_protocols = array( 'http', 'https', 'mailto', 'sms', 'tel', 'viber', 'whatsapp' );
149
+ $special_protocols = array( 'tel', 'sms' ); // these ones don't valid with `filter_var+FILTER_VALIDATE_URL`
150
  $protocol = strtok( $href, ':' );
151
+
152
  if ( false === filter_var( $href, FILTER_VALIDATE_URL )
153
+ && ! in_array( $protocol, $special_protocols ) ) {
154
+ return false;
155
+ }
156
+
157
+ if ( ! in_array( $protocol, $valid_protocols ) ) {
158
  return false;
159
  }
160
 
161
  return true;
162
  }
163
 
164
+ private function replace_node_with_children( $node, $bad_attributes, $bad_protocols ) {
165
  // If the node has children and also has a parent node,
166
  // clone and re-add all the children just before current node.
167
  if ( $node->hasChildNodes() && $node->parentNode ) {
168
  foreach ( $node->childNodes as $child_node ) {
169
  $new_child = $child_node->cloneNode( true );
170
+ $this->strip_attributes_recursive( $new_child, $bad_attributes, $bad_protocols );
171
  $node->parentNode->insertBefore( $new_child, $node );
172
  }
173
  }
217
  'embed',
218
  'embedvideo',
219
 
220
+ // Other weird ones
221
+ 'comments-count',
222
+
223
  // These are converted into amp-* versions
224
  //'img',
225
  //'video',
232
  return $this->merge_defaults_with_args( 'add_blacklisted_attributes', array(
233
  'style',
234
  'size',
235
+ 'clear',
236
+ 'align',
237
+ 'valign',
238
  ) );
239
  }
240
  }
includes/sanitizers/class-amp-img-sanitizer.php CHANGED
@@ -28,7 +28,7 @@ class AMP_Img_Sanitizer extends AMP_Base_Sanitizer {
28
  $node = $nodes->item( $i );
29
  $old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
30
 
31
- if ( ! array_key_exists( 'src', $old_attributes ) ) {
32
  $node->parentNode->removeChild( $node );
33
  continue;
34
  }
28
  $node = $nodes->item( $i );
29
  $old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
30
 
31
+ if ( empty( $old_attributes['src'] ) ) {
32
  $node->parentNode->removeChild( $node );
33
  continue;
34
  }
includes/sanitizers/class-amp-style-sanitizer.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-base-sanitizer.php' );
4
+
5
+ /**
6
+ * Collects inline styles and outputs them in the amp-custom stylesheet.
7
+ */
8
+ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer {
9
+ private $styles = array();
10
+
11
+ public function get_styles() {
12
+ return $this->styles;
13
+ }
14
+
15
+ public function sanitize() {
16
+ $body = $this->get_body_node();
17
+ $this->collect_styles_recursive( $body );
18
+ }
19
+
20
+ private function collect_styles_recursive( $node ) {
21
+ if ( $node->nodeType !== XML_ELEMENT_NODE ) {
22
+ return;
23
+ }
24
+
25
+ if ( $node->hasAttributes() && $node instanceof DOMElement ) {
26
+ $style = $node->getAttribute( 'style' );
27
+ $class = $node->getAttribute( 'class' );
28
+
29
+ if ( $style ) {
30
+ $style = $this->process_style( $style );
31
+ if ( ! empty( $style ) ) {
32
+ $class_name = $this->generate_class_name( $style );
33
+ $new_class = trim( $class . ' ' . $class_name );
34
+
35
+ $node->setAttribute( 'class', $new_class );
36
+ $this->styles[ '.' . $class_name ] = $style;
37
+ }
38
+
39
+ $node->removeAttribute( 'style' );
40
+ }
41
+ }
42
+
43
+ $length = $node->childNodes->length;
44
+ for ( $i = $length - 1; $i >= 0; $i -- ) {
45
+ $child_node = $node->childNodes->item( $i );
46
+
47
+ $this->collect_styles_recursive( $child_node );
48
+ }
49
+ }
50
+
51
+ private function process_style( $string ) {
52
+ // Filter properties
53
+ $string = safecss_filter_attr( esc_html( $string ) );
54
+
55
+ if ( ! $string ) {
56
+ return array();
57
+ }
58
+
59
+ // Normalize order
60
+ $styles = array_map( 'trim', explode( ';', $string ) );
61
+ sort( $styles );
62
+
63
+ $processed_styles = array();
64
+
65
+ // Normalize whitespace and filter rules
66
+ foreach ( $styles as $index => $rule ) {
67
+ $arr2 = array_map( 'trim', explode( ':', $rule, 2 ) );
68
+ if ( 2 !== count( $arr2 ) ) {
69
+ continue;
70
+ }
71
+
72
+ list( $property, $value ) = $this->filter_style( $arr2[0], $arr2[1] );
73
+ if ( empty( $property ) || empty( $value ) ) {
74
+ continue;
75
+ }
76
+
77
+ $processed_styles[ $index ] = $property . ':' . $value;
78
+ }
79
+
80
+ return $processed_styles;
81
+ }
82
+
83
+ private function filter_style( $property, $value ) {
84
+ // Handle overflow rule
85
+ // https://www.ampproject.org/docs/reference/spec.html#properties
86
+ if ( 0 === strpos( $property, 'overflow' )
87
+ && ( false !== strpos( $value, 'auto' ) || false !== strpos( $value, 'scroll' ) )
88
+ ) {
89
+ return false;
90
+ }
91
+
92
+ if ( 'width' === $property ) {
93
+ $property = 'max-width';
94
+ }
95
+
96
+ return array( $property, $value );
97
+ }
98
+
99
+ private function generate_class_name( $data ) {
100
+ $string = maybe_serialize( $data );
101
+ return 'amp-wp-inline-' . md5( $string );
102
+ }
103
+ }
includes/settings/class-amp-customizer-design-settings.php ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class AMP_Customizer_Design_Settings {
4
+ const DEFAULT_HEADER_COLOR = '#fff';
5
+ const DEFAULT_HEADER_BACKGROUND_COLOR = '#0a89c0';
6
+ const DEFAULT_COLOR_SCHEME = 'light';
7
+
8
+ public static function init() {
9
+ add_action( 'amp_customizer_init', array( __CLASS__, 'init_customizer' ) );
10
+
11
+ add_filter( 'amp_customizer_get_settings', array( __CLASS__, 'append_settings' ) );
12
+ }
13
+
14
+ public static function init_customizer() {
15
+ add_action( 'amp_customizer_register_settings', array( __CLASS__, 'register_customizer_settings' ) );
16
+ add_action( 'amp_customizer_register_ui', array( __CLASS__, 'register_customizer_ui' ) );
17
+ add_action( 'amp_customizer_enqueue_preview_scripts', array( __CLASS__, 'enqueue_customizer_preview_scripts' ) );
18
+ }
19
+
20
+ public static function register_customizer_settings( $wp_customize ) {
21
+ // Header text color setting
22
+ $wp_customize->add_setting( 'amp_customizer[header_color]', array(
23
+ 'type' => 'option',
24
+ 'default' => self::DEFAULT_HEADER_COLOR,
25
+ 'sanitize_callback' => 'sanitize_hex_color',
26
+ 'transport' => 'postMessage'
27
+ ) );
28
+
29
+ // Header background color
30
+ $wp_customize->add_setting( 'amp_customizer[header_background_color]', array(
31
+ 'type' => 'option',
32
+ 'default' => self::DEFAULT_HEADER_BACKGROUND_COLOR,
33
+ 'sanitize_callback' => 'sanitize_hex_color',
34
+ 'transport' => 'postMessage'
35
+ ) );
36
+
37
+ // Background color scheme
38
+ $wp_customize->add_setting( 'amp_customizer[color_scheme]', array(
39
+ 'type' => 'option',
40
+ 'default' => self::DEFAULT_COLOR_SCHEME,
41
+ 'sanitize_callback' => array( __CLASS__ , 'sanitize_color_scheme' ),
42
+ 'transport' => 'postMessage'
43
+ ) );
44
+ }
45
+
46
+ public function register_customizer_ui( $wp_customize ) {
47
+ $wp_customize->add_section( 'amp_design', array(
48
+ 'title' => __( 'Design', 'amp' ),
49
+ 'panel' => AMP_Template_Customizer::PANEL_ID,
50
+ ) );
51
+
52
+ // Header text color control.
53
+ $wp_customize->add_control(
54
+ new WP_Customize_Color_Control( $wp_customize, 'amp_header_color', array(
55
+ 'settings' => 'amp_customizer[header_color]',
56
+ 'label' => __( 'Header Text Color', 'amp' ),
57
+ 'section' => 'amp_design',
58
+ 'priority' => 10
59
+ ) )
60
+ );
61
+
62
+ // Header background color control.
63
+ $wp_customize->add_control(
64
+ new WP_Customize_Color_Control( $wp_customize, 'amp_header_background_color', array(
65
+ 'settings' => 'amp_customizer[header_background_color]',
66
+ 'label' => __( 'Header Background & Link Color', 'amp' ),
67
+ 'section' => 'amp_design',
68
+ 'priority' => 20
69
+ ) )
70
+ );
71
+
72
+ // Background color scheme
73
+ $wp_customize->add_control( 'amp_color_scheme', array(
74
+ 'settings' => 'amp_customizer[color_scheme]',
75
+ 'label' => __( 'Color Scheme', 'amp' ),
76
+ 'section' => 'amp_design',
77
+ 'type' => 'radio',
78
+ 'priority' => 30,
79
+ 'choices' => self::get_color_scheme_names(),
80
+ ));
81
+ }
82
+
83
+ public static function enqueue_customizer_preview_scripts() {
84
+ wp_enqueue_script(
85
+ 'amp-customizer-design-preview',
86
+ amp_get_asset_url( 'js/amp-customizer-design-preview.js' ),
87
+ array( 'amp-customizer' ),
88
+ false,
89
+ true
90
+ );
91
+ wp_localize_script( 'amp-customizer-design-preview', 'amp_customizer_design', array(
92
+ 'color_schemes' => self::get_color_schemes(),
93
+ ) );
94
+ }
95
+
96
+ public static function append_settings( $settings ) {
97
+ $settings = wp_parse_args( $settings, array(
98
+ 'header_color' => self::DEFAULT_HEADER_COLOR,
99
+ 'header_background_color' => self::DEFAULT_HEADER_BACKGROUND_COLOR,
100
+ 'color_scheme' => self::DEFAULT_COLOR_SCHEME,
101
+ ) );
102
+
103
+ $theme_colors = self::get_colors_for_color_scheme( $settings['color_scheme'] );
104
+
105
+ return array_merge( $settings, $theme_colors, array(
106
+ 'link_color' => $settings['header_background_color'],
107
+ ) );
108
+ }
109
+
110
+ protected static function get_color_scheme_names() {
111
+ return array(
112
+ 'light' => __( 'Light', 'amp'),
113
+ 'dark' => __( 'Dark', 'amp' ),
114
+ );
115
+ }
116
+
117
+ protected static function get_color_schemes() {
118
+ return array(
119
+ 'light' => array(
120
+ // Convert colors to greyscale for light theme color; see http://goo.gl/2gDLsp
121
+ 'theme_color' => '#fff',
122
+ 'text_color' => '#353535',
123
+ 'muted_text_color' => '#696969',
124
+ 'border_color' => '#c2c2c2',
125
+ ),
126
+ 'dark' => array(
127
+ // Convert and invert colors to greyscale for dark theme color; see http://goo.gl/uVB2cO
128
+ 'theme_color' => '#0a0a0a',
129
+ 'text_color' => '#dedede',
130
+ 'muted_text_color' => '#b1b1b1',
131
+ 'border_color' => '#707070',
132
+ )
133
+ );
134
+ }
135
+
136
+ protected static function get_colors_for_color_scheme( $scheme ) {
137
+ $color_schemes = self::get_color_schemes();
138
+
139
+ if ( isset( $color_schemes[ $scheme ] ) ) {
140
+ return $color_schemes[ $scheme ];
141
+ }
142
+
143
+ return $color_schemes[ self::DEFAULT_COLOR_SCHEME ];
144
+ }
145
+
146
+ public static function sanitize_color_scheme( $value ) {
147
+ $schemes = self::get_color_scheme_names();
148
+ $scheme_slugs = array_keys( $schemes );
149
+
150
+ if ( ! in_array( $value, $scheme_slugs ) ) {
151
+ $value = self::DEFAULT_COLOR_SCHEME;
152
+ }
153
+
154
+ return $value;
155
+ }
156
+ }
includes/settings/class-amp-customizer-settings.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class AMP_Customizer_Settings {
4
+ private static function get_stored_options() {
5
+ return get_option( 'amp_customizer', array() );
6
+ }
7
+
8
+ public static function get_settings() {
9
+ $settings = self::get_stored_options();
10
+
11
+ return apply_filters( 'amp_customizer_get_settings', $settings );
12
+ }
13
+ }
includes/utils/class-amp-dom-utils.php CHANGED
@@ -25,8 +25,14 @@ class AMP_DOM_Utils {
25
  // Only want children of the body tag, since we have a subset of HTML.
26
  $out = '';
27
  $body = $dom->getElementsByTagName( 'body' )->item( 0 );
 
 
 
 
 
 
28
  foreach ( $body->childNodes as $node ) {
29
- $out .= $dom->saveXML( $node, LIBXML_NOEMPTYTAG );
30
  }
31
  return $out;
32
  }
@@ -52,6 +58,43 @@ class AMP_DOM_Utils {
52
  }
53
 
54
  public static function is_node_empty( $node ) {
55
- return 0 === $node->childNodes->length && empty( $node->textContent );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  }
57
  }
25
  // Only want children of the body tag, since we have a subset of HTML.
26
  $out = '';
27
  $body = $dom->getElementsByTagName( 'body' )->item( 0 );
28
+
29
+ // AMP elements always need closing tags.
30
+ // To force them, we can't use saveHTML (node support is 5.3+) and LIBXML_NOEMPTYTAG results in issues with self-closing tags like `br` and `hr`.
31
+ // So, we're manually forcing closing tags.
32
+ self::recursive_force_closing_tags( $dom, $body );
33
+
34
  foreach ( $body->childNodes as $node ) {
35
+ $out .= $dom->saveXML( $node );
36
  }
37
  return $out;
38
  }
58
  }
59
 
60
  public static function is_node_empty( $node ) {
61
+ return false === $node->hasChildNodes()
62
+ && empty( $node->textContent );
63
+ }
64
+
65
+ public static function recursive_force_closing_tags( $dom, $node ) {
66
+ if ( XML_ELEMENT_NODE !== $node->nodeType ) {
67
+ return;
68
+ }
69
+
70
+ if ( self::is_self_closing_tag( $node->nodeName ) ) {
71
+ return;
72
+ }
73
+
74
+ if ( self::is_node_empty( $node ) ) {
75
+ $text_node = $dom->createTextNode( '' );
76
+ $node->appendChild( $text_node );
77
+ return;
78
+ }
79
+
80
+ $num_children = $node->childNodes->length;
81
+ for ( $i = $num_children - 1; $i >= 0; $i-- ) {
82
+ $child = $node->childNodes->item( $i );
83
+ self::recursive_force_closing_tags( $dom, $child );
84
+ }
85
+ }
86
+
87
+ private static function is_self_closing_tag( $tag ) {
88
+ // This function is called a lot; the static var prevents having to re-create the array every time.
89
+ static $self_closing_tags;
90
+ if ( ! isset( $self_closing_tags ) ) {
91
+ // https://www.w3.org/TR/html5/syntax.html#serializing-html-fragments
92
+ // Not all are valid AMP, but we include them for completeness.
93
+ $self_closing_tags = array(
94
+ 'area', 'base', 'basefont', 'bgsound', 'br', 'col', 'embed', 'frame', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr',
95
+ );
96
+ }
97
+
98
+ return in_array( $tag, $self_closing_tags );
99
  }
100
  }
readme.md CHANGED
@@ -575,6 +575,11 @@ We may provide better ways to handle this in the future.
575
 
576
  Jetpack integration is baked in. More support for things like Related Posts to come.
577
 
 
 
 
 
 
578
  ### Yoast SEO
579
 
580
  If you're using [Yoast SEO](https://wordpress.org/plugins/wordpress-seo/), check out the companion plugin here: https://github.com/Yoast/yoastseo-amp
575
 
576
  Jetpack integration is baked in. More support for things like Related Posts to come.
577
 
578
+ ### Parse.ly
579
+
580
+ [Parse.ly's WordPress plugin](https://wordpress.org/plugins/wp-parsely/) automatically tracks AMP pages when enabled along with this plugin.
581
+
582
+
583
  ### Yoast SEO
584
 
585
  If you're using [Yoast SEO](https://wordpress.org/plugins/wordpress-seo/), check out the companion plugin here: https://github.com/Yoast/yoastseo-amp
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: batmoo, joen, automattic, potatomaster
3
  Tags: amp, mobile
4
  Requires at least: 4.4
5
  Tested up to: 4.6
6
- Stable tag: 0.3.3
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -25,20 +25,46 @@ Follow along with or contribute to the development of this plugin at https://git
25
 
26
  1. Upload the folder to the `/wp-content/plugins/` directory
27
  1. Activate the plugin through the 'Plugins' menu in WordPress
 
28
 
29
  == Frequently Asked Questions ==
30
 
31
  = How do I customize the AMP output for my site? =
32
 
33
- You can find details about customization options at https://github.com/Automattic/amp-wp/blob/master/readme.md
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  == Changelog ==
36
 
37
- = 0.4 (in-progress) =
38
 
39
- - Pages
40
- - Customizer
41
- - Updated Template
 
 
 
 
 
 
 
42
 
43
  = 0.3.3 (Aug 18, 2016) =
44
 
@@ -100,6 +126,18 @@ You can find details about customization options at https://github.com/Automatti
100
 
101
  == Upgrade Notice ==
102
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  = 0.3.1 =
104
 
105
  * Breaking change: `AMP_QUERY_VAR` is now defined right before `amp_init`.
3
  Tags: amp, mobile
4
  Requires at least: 4.4
5
  Tested up to: 4.6
6
+ Stable tag: 0.4
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
25
 
26
  1. Upload the folder to the `/wp-content/plugins/` directory
27
  1. Activate the plugin through the 'Plugins' menu in WordPress
28
+ 1. You may need to refresh your permalinks by going to `Settings > Permalinks` and tapping the `Save` button.
29
 
30
  == Frequently Asked Questions ==
31
 
32
  = How do I customize the AMP output for my site? =
33
 
34
+ You can tweak a few things like colours from the AMP Customizer. From the Dashboard, go to `Appearance > AMP`.
35
+
36
+ For deeper level customizations, please see the readme at https://github.com/Automattic/amp-wp/blob/master/readme.md
37
+
38
+ = What about ads and shortcodes and such? =
39
+
40
+ Check out https://github.com/Automattic/amp-wp/blob/master/readme.md#handling-media
41
+
42
+ = What about analytics? =
43
+
44
+ Many plugins are adding AMP support already. If you handling analytics yourself, please see https://github.com/Automattic/amp-wp/blob/master/readme.md#analytics
45
+
46
+ = Google Webmaster Tools is reporting validation errors for my site. How do I fix them? =
47
+
48
+ The best place to start is to open a new discussion in the [support forum](https://wordpress.org/support/plugin/amp) with details on what the specific validation error is.
49
+
50
+ = Why aren't Pages supported yet =
51
+
52
+ A wise green Yoda once said, "Patience you must have, my young padawan." We're working on it :)
53
 
54
  == Changelog ==
55
 
56
+ = 0.4 (2016-10-06) =
57
 
58
+ - New template: spiffy, shiny, and has the fresh theme smell (props allancole and the Automattic Theme Team).
59
+ - AMP Customizer: Pick your colours and make the template your own (props DrewAPicture and 10up)
60
+ - Fix: support for inline styles (props coreymckrill).
61
+ - Fix: no more fatal errors when tags not supported by post type (props david-binda)
62
+ - Fix: no more unnecessary `<br>` tags.
63
+ - Fix: sanitize children of removed nodes (like empty `<a>` tags) (props Maxime2).
64
+ - Fix: no more broken YouTube URLs with multiple ?s.
65
+ - Fix: properly handle tel and sms schemes (h/t soundstrategies).
66
+ - Fix: remove amp endpoint on deactivate.
67
+ - New filter: `amp_pre_get_permalink` if you want a completely custom AMP permalink.
68
 
69
  = 0.3.3 (Aug 18, 2016) =
70
 
126
 
127
  == Upgrade Notice ==
128
 
129
+ = 0.4 =
130
+
131
+ * Breaking change: The new template has changes to markup, class names, and styles that may not work with existing customizations. If you want to stay on the old template for now, you can use the following code snippet:
132
+
133
+ ```
134
+ if ( function_exists( 'amp_backcompat_use_v03_templates' ) ) {
135
+ amp_backcompat_use_v03_templates();
136
+ }
137
+ ```
138
+
139
+ For more details, please see https://wordpress.org/support/topic/v0-4-whats-new-and-possible-breaking-changes/
140
+
141
  = 0.3.1 =
142
 
143
  * Breaking change: `AMP_QUERY_VAR` is now defined right before `amp_init`.
screenshot-1.png CHANGED
Binary file
screenshot-2.png CHANGED
Binary file
templates/featured-image.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $featured_image = $this->get( 'featured_image' );
3
+
4
+ if ( empty( $featured_image ) ) {
5
+ return;
6
+ }
7
+
8
+ $amp_html = $featured_image['amp_html'];
9
+ $caption = $featured_image['caption'];
10
+ ?>
11
+ <figure class="amp-wp-article-featured-image wp-caption">
12
+ <?php echo $amp_html; // amphtml content; no kses ?>
13
+ <?php if ( $caption ) : ?>
14
+ <p class="wp-caption-text">
15
+ <?php echo wp_kses_data( $caption ); ?>
16
+ </p>
17
+ <?php endif; ?>
18
+ </figure>
templates/footer.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <footer class="amp-wp-footer">
2
+ <div>
3
+ <h2><?php echo esc_html( $this->get( 'blog_name' ) ); ?></h2>
4
+ <p>
5
+ <a href="<?php echo esc_url( __( 'https://wordpress.org/', 'amp' ) ); ?>"><?php printf( __( 'Powered by %s', 'amp' ), 'WordPress' ); ?></a>
6
+ </p>
7
+ <a href="#top" class="back-to-top"><?php _e( 'Back to top', 'amp' ); ?></a>
8
+ </div>
9
+ </footer>
templates/header-bar.php CHANGED
@@ -1,11 +1,11 @@
1
- <nav class="amp-wp-title-bar">
2
  <div>
3
  <a href="<?php echo esc_url( $this->get( 'home_url' ) ); ?>">
4
- <?php $site_icon_url = $this->get( 'site_icon_url' ); ?>
5
- <?php if ( $site_icon_url ) : ?>
6
  <amp-img src="<?php echo esc_url( $site_icon_url ); ?>" width="32" height="32" class="amp-wp-site-icon"></amp-img>
7
  <?php endif; ?>
8
  <?php echo esc_html( $this->get( 'blog_name' ) ); ?>
9
  </a>
10
  </div>
11
- </nav>
1
+ <header id="#top" class="amp-wp-header">
2
  <div>
3
  <a href="<?php echo esc_url( $this->get( 'home_url' ) ); ?>">
4
+ <?php $site_icon_url = $this->get( 'site_icon_url' );
5
+ if ( $site_icon_url ) : ?>
6
  <amp-img src="<?php echo esc_url( $site_icon_url ); ?>" width="32" height="32" class="amp-wp-site-icon"></amp-img>
7
  <?php endif; ?>
8
  <?php echo esc_html( $this->get( 'blog_name' ) ); ?>
9
  </a>
10
  </div>
11
+ </header>
templates/meta-author.php CHANGED
@@ -1,9 +1,10 @@
1
  <?php $post_author = $this->get( 'post_author' ); ?>
2
- <li class="amp-wp-byline">
3
- <?php if ( function_exists( 'get_avatar_url' ) ) : ?>
4
- <amp-img src="<?php echo esc_url( get_avatar_url( $post_author->user_email, array(
5
- 'size' => 24,
6
- ) ) ); ?>" width="24" height="24" layout="fixed"></amp-img>
7
- <?php endif; ?>
8
- <span class="amp-wp-author"><?php echo esc_html( $post_author->display_name ); ?></span>
9
- </li>
 
1
  <?php $post_author = $this->get( 'post_author' ); ?>
2
+ <?php if ( $post_author ) : ?>
3
+ <?php $author_avatar_url = get_avatar_url( $post_author->user_email, array( 'size' => 24 ) ); ?>
4
+ <div class="amp-wp-meta amp-wp-byline">
5
+ <?php if ( function_exists( 'get_avatar_url' ) ) : ?>
6
+ <amp-img src="<?php echo esc_url( $author_avatar_url ); ?>" width="24" height="24" layout="fixed"></amp-img>
7
+ <?php endif; ?>
8
+ <span class="amp-wp-author author vcard"><?php echo esc_html( $post_author->display_name ); ?></span>
9
+ </div>
10
+ <?php endif; ?>
templates/meta-comments-link.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $comments_link_url = $this->get( 'comments_link_url' );
3
+ ?>
4
+ <?php if ( $comments_link_url ) : ?>
5
+ <?php $comments_link_text = $this->get( 'comments_link_text' ); ?>
6
+ <div class="amp-wp-meta amp-wp-comments-link">
7
+ <a href="<?php echo esc_url( $comments_link_url ); ?>">
8
+ <?php echo esc_html( $comments_link_text ); ?>
9
+ </a>
10
+ </div>
11
+ <?php endif; ?>
templates/meta-taxonomy.php CHANGED
@@ -1,15 +1,19 @@
1
- <?php $categories = get_the_category_list( _x( ', ', 'Used between list items, there is a space after the comma.', 'amp' ) ); ?>
2
  <?php if ( $categories ) : ?>
3
- <li class="amp-wp-tax-category">
4
- <span class="screen-reader-text">Categories:</span>
5
- <?php echo $categories; ?>
6
- </li>
7
  <?php endif; ?>
8
 
9
- <?php $tags = get_the_tag_list( '', _x( ', ', 'Used between list items, there is a space after the comma.', 'amp' ) ); ?>
10
- <?php if ( $tags ) : ?>
11
- <li class="amp-wp-tax-tag">
12
- <span class="screen-reader-text">Tags:</span>
13
- <?php echo $tags; ?>
14
- </li>
 
 
 
 
 
15
  <?php endif; ?>
1
+ <?php $categories = get_the_category_list( _x( ', ', 'Used between list items, there is a space after the comma.', 'amp' ), '', $this->ID ); ?>
2
  <?php if ( $categories ) : ?>
3
+ <div class="amp-wp-meta amp-wp-tax-category">
4
+ <?php printf( esc_html__( 'Categories: %s', 'amp' ), $categories ); ?>
5
+ </div>
 
6
  <?php endif; ?>
7
 
8
+ <?php
9
+ $tags = get_the_tag_list(
10
+ '',
11
+ _x( ', ', 'Used between list items, there is a space after the comma.', 'amp' ),
12
+ '',
13
+ $this->ID
14
+ ); ?>
15
+ <?php if ( $tags && ! is_wp_error( $tags ) ) : ?>
16
+ <div class="amp-wp-meta amp-wp-tax-tag">
17
+ <?php printf( esc_html__( 'Tags: %s', 'amp' ), $tags ); ?>
18
+ </div>
19
  <?php endif; ?>
templates/meta-time.php CHANGED
@@ -1,4 +1,4 @@
1
- <li class="amp-wp-posted-on">
2
  <time datetime="<?php echo esc_attr( date( 'c', $this->get( 'post_publish_timestamp' ) ) ); ?>">
3
  <?php
4
  echo esc_html(
@@ -9,4 +9,4 @@
9
  );
10
  ?>
11
  </time>
12
- </li>
1
+ <div class="amp-wp-meta amp-wp-posted-on">
2
  <time datetime="<?php echo esc_attr( date( 'c', $this->get( 'post_publish_timestamp' ) ) ); ?>">
3
  <?php
4
  echo esc_html(
9
  );
10
  ?>
11
  </time>
12
+ </div>
templates/single.php CHANGED
@@ -1,24 +1,41 @@
1
  <!doctype html>
2
- <html amp>
3
  <head>
4
  <meta charset="utf-8">
5
  <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
6
  <?php do_action( 'amp_post_template_head', $this ); ?>
7
-
8
  <style amp-custom>
9
- <?php $this->load_parts( array( 'style' ) ); ?>
10
- <?php do_action( 'amp_post_template_css', $this ); ?>
11
  </style>
12
  </head>
13
- <body>
 
 
14
  <?php $this->load_parts( array( 'header-bar' ) ); ?>
15
- <div class="amp-wp-content">
16
- <h1 class="amp-wp-title"><?php echo wp_kses_data( $this->get( 'post_title' ) ); ?></h1>
17
- <ul class="amp-wp-meta">
18
- <?php $this->load_parts( apply_filters( 'amp_post_template_meta_parts', array( 'meta-author', 'meta-time', 'meta-taxonomy' ) ) ); ?>
19
- </ul>
20
- <?php echo $this->get( 'post_amp_content' ); // amphtml content; no kses ?>
21
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  <?php do_action( 'amp_post_template_footer', $this ); ?>
 
23
  </body>
24
  </html>
1
  <!doctype html>
2
+ <html amp <?php language_attributes(); ?>>
3
  <head>
4
  <meta charset="utf-8">
5
  <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
6
  <?php do_action( 'amp_post_template_head', $this ); ?>
 
7
  <style amp-custom>
8
+ <?php $this->load_parts( array( 'style' ) ); ?>
9
+ <?php do_action( 'amp_post_template_css', $this ); ?>
10
  </style>
11
  </head>
12
+
13
+ <body class="<?php echo esc_attr( $this->get( 'body_class' ) ); ?>">
14
+
15
  <?php $this->load_parts( array( 'header-bar' ) ); ?>
16
+
17
+ <article class="amp-wp-article">
18
+
19
+ <header class="amp-wp-article-header">
20
+ <h1 class="amp-wp-title"><?php echo wp_kses_data( $this->get( 'post_title' ) ); ?></h1>
21
+ <?php $this->load_parts( apply_filters( 'amp_post_article_header_meta', array( 'meta-author', 'meta-time' ) ) ); ?>
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' ); // amphtml content; no kses ?>
28
+ </div>
29
+
30
+ <footer class="amp-wp-article-footer">
31
+ <?php $this->load_parts( apply_filters( 'amp_post_article_footer_meta', array( 'meta-taxonomy', 'meta-comments-link' ) ) ); ?>
32
+ </footer>
33
+
34
+ </article>
35
+
36
+ <?php $this->load_parts( array( 'footer' ) ); ?>
37
+
38
  <?php do_action( 'amp_post_template_footer', $this ); ?>
39
+
40
  </body>
41
  </html>
templates/style.php CHANGED
@@ -1,58 +1,36 @@
1
- /* Merriweather fonts */
2
- @font-face {
3
- font-family:'Merriweather';
4
- src:url('https://s1.wp.com/i/fonts/merriweather/merriweather-regular-webfont.woff2') format('woff2'),
5
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-regular-webfont.woff') format('woff'),
6
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-regular-webfont.ttf') format('truetype'),
7
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-regular-webfont.svg#merriweatherregular') format('svg');
8
- font-weight:400;
9
- font-style:normal;
10
- }
11
-
12
- @font-face {
13
- font-family:'Merriweather';
14
- src:url('https://s1.wp.com/i/fonts/merriweather/merriweather-italic-webfont.woff2') format('woff2'),
15
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-italic-webfont.woff') format('woff'),
16
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-italic-webfont.ttf') format('truetype'),
17
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-italic-webfont.svg#merriweatheritalic') format('svg');
18
- font-weight:400;
19
- font-style:italic;
20
- }
21
-
22
- @font-face {
23
- font-family:'Merriweather';
24
- src:url('https://s1.wp.com/i/fonts/merriweather/merriweather-bold-webfont.woff2') format('woff2'),
25
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-bold-webfont.woff') format('woff'),
26
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-bold-webfont.ttf') format('truetype'),
27
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-bold-webfont.svg#merriweatherbold') format('svg');
28
- font-weight:700;
29
- font-style:normal;
30
- }
31
-
32
- @font-face {
33
- font-family:'Merriweather';
34
- src:url('https://s1.wp.com/i/fonts/merriweather/merriweather-bolditalic-webfont.woff2') format('woff2'),
35
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-bolditalic-webfont.woff') format('woff'),
36
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-bolditalic-webfont.ttf') format('truetype'),
37
- url('https://s1.wp.com/i/fonts/merriweather/merriweather-bolditalic-webfont.svg#merriweatherbold_italic') format('svg');
38
- font-weight:700;
39
- font-style:italic;
40
  }
41
 
42
- /* Generic WP styling */
43
- amp-img.alignright { float: right; margin: 0 0 1em 1em; }
44
- amp-img.alignleft { float: left; margin: 0 1em 1em 0; }
45
- amp-img.aligncenter { display: block; margin-left: auto; margin-right: auto; }
46
- .alignright { float: right; }
47
- .alignleft { float: left; }
48
- .aligncenter { display: block; margin-left: auto; margin-right: auto; }
49
 
50
- .wp-caption.alignleft { margin-right: 1em; }
51
- .wp-caption.alignright { margin-left: 1em; }
 
 
 
52
 
53
  .amp-wp-enforced-sizes {
54
  /** Our sizes fallback is 100vw, and we have a padding on the container; the max-width here prevents the element from overflowing. **/
55
  max-width: 100%;
 
56
  }
57
 
58
  .amp-wp-unknown-size img {
@@ -62,175 +40,249 @@ amp-img.aligncenter { display: block; margin-left: auto; margin-right: auto; }
62
  }
63
 
64
  /* Template Styles */
65
- .amp-wp-content, .amp-wp-title-bar div {
66
- <?php $content_max_width = absint( $this->get( 'content_max_width' ) ); ?>
 
67
  <?php if ( $content_max_width > 0 ) : ?>
68
- max-width: <?php echo sprintf( '%dpx', $content_max_width ); ?>;
69
  margin: 0 auto;
 
70
  <?php endif; ?>
71
  }
72
 
73
- body {
74
- font-family: 'Merriweather', Serif;
75
- font-size: 16px;
76
- line-height: 1.8;
77
- background: #fff;
78
- color: #3d596d;
79
- padding-bottom: 100px;
80
  }
81
 
82
- .amp-wp-content {
83
- padding: 16px;
84
- overflow-wrap: break-word;
85
- word-wrap: break-word;
86
- font-weight: 400;
87
- color: #3d596d;
88
- }
89
-
90
- .amp-wp-title {
91
- margin: 36px 0 0 0;
92
- font-size: 36px;
93
- line-height: 1.258;
94
- font-weight: 700;
95
- color: #2e4453;
96
- }
97
-
98
- .amp-wp-meta {
99
- margin-bottom: 16px;
100
  }
101
 
102
  p,
103
  ol,
104
  ul,
105
  figure {
106
- margin: 0 0 24px 0;
 
107
  }
108
 
109
  a,
110
  a:visited {
111
- color: #0087be;
112
  }
113
 
114
  a:hover,
115
  a:active,
116
  a:focus {
117
- color: #33bbe3;
118
  }
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
  /* UI Fonts */
 
122
  .amp-wp-meta,
123
- nav.amp-wp-title-bar,
124
- .wp-caption-text {
 
 
 
 
 
 
125
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen-Sans", "Ubuntu", "Cantarell", "Helvetica Neue", sans-serif;
126
- font-size: 15px;
127
  }
128
 
 
129
 
130
- /* Meta */
131
- ul.amp-wp-meta {
132
- padding: 24px 0 0 0;
133
- margin: 0 0 24px 0;
134
  }
135
 
136
- ul.amp-wp-meta li {
137
- list-style: none;
138
- display: inline-block;
139
- margin: 0;
140
- line-height: 24px;
141
- white-space: nowrap;
142
- overflow: hidden;
143
- text-overflow: ellipsis;
144
- max-width: 300px;
145
  }
146
 
147
- ul.amp-wp-meta li:before {
148
- content: "\2022";
149
- margin: 0 8px;
150
  }
151
 
152
- ul.amp-wp-meta li:first-child:before {
153
- display: none;
 
 
 
 
 
 
 
 
154
  }
155
 
156
- .amp-wp-meta,
157
- .amp-wp-meta a {
158
- color: #4f748e;
 
 
 
 
 
 
159
  }
160
 
161
- .amp-wp-meta .screen-reader-text {
162
- /* from twentyfifteen */
163
- clip: rect(1px, 1px, 1px, 1px);
164
- height: 1px;
165
- overflow: hidden;
166
- position: absolute;
167
- width: 1px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  }
169
 
170
  .amp-wp-byline amp-img {
 
171
  border-radius: 50%;
172
- border: 0;
173
- background: #f3f6f8;
174
  position: relative;
175
- top: 6px;
176
  margin-right: 6px;
177
  }
178
 
 
 
 
 
 
179
 
180
- /* Titlebar */
181
- nav.amp-wp-title-bar {
182
- background: #0a89c0;
183
- padding: 0 16px;
 
 
 
 
184
  }
185
 
186
- nav.amp-wp-title-bar div {
187
- line-height: 54px;
188
- color: #fff;
 
189
  }
190
 
191
- nav.amp-wp-title-bar a {
192
- color: #fff;
193
- text-decoration: none;
194
  }
195
 
196
- nav.amp-wp-title-bar .amp-wp-site-icon {
197
- /** site icon is 32px **/
198
- float: left;
199
- margin: 11px 8px 0 0;
200
- border-radius: 50%;
 
201
  }
202
 
 
 
 
203
 
204
  /* Captions */
205
- .wp-caption-text {
206
- padding: 8px 16px;
207
- font-style: italic;
208
  }
209
 
 
 
 
210
 
211
- /* Quotes */
212
- blockquote {
213
- padding: 16px;
214
- margin: 8px 0 24px 0;
215
- border-left: 2px solid #87a6bc;
216
- color: #4f748e;
217
- background: #e9eff3;
218
  }
219
 
220
- blockquote p:last-child {
221
- margin-bottom: 0;
 
 
 
 
 
222
  }
223
 
224
- /* Other Elements */
 
225
  amp-carousel {
226
- background: #000;
 
227
  }
228
-
229
  amp-iframe,
230
  amp-youtube,
231
  amp-instagram,
232
  amp-vine {
233
- background: #f3f6f8;
 
 
 
 
 
234
  }
235
 
236
  amp-carousel > amp-img > img {
@@ -238,7 +290,90 @@ amp-carousel > amp-img > img {
238
  }
239
 
240
  .amp-wp-iframe-placeholder {
241
- background: #f3f6f8 url( <?php echo esc_url( $this->get( 'placeholder_image_url' ) ); ?> ) no-repeat center 40%;
242
  background-size: 48px 48px;
243
  min-height: 48px;
244
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Get content width
3
+ $content_max_width = absint( $this->get( 'content_max_width' ) );
4
+
5
+ // Get template colors
6
+ $theme_color = $this->get_customizer_setting( 'theme_color' );
7
+ $text_color = $this->get_customizer_setting( 'text_color' );
8
+ $muted_text_color = $this->get_customizer_setting( 'muted_text_color' );
9
+ $border_color = $this->get_customizer_setting( 'border_color' );
10
+ $link_color = $this->get_customizer_setting( 'link_color' );
11
+ $header_background_color = $this->get_customizer_setting( 'header_background_color' );
12
+ $header_color = $this->get_customizer_setting( 'header_color' );
13
+ ?>
14
+ /* Generic WP styling */
15
+
16
+ .alignright {
17
+ float: right;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
19
 
20
+ .alignleft {
21
+ float: left;
22
+ }
 
 
 
 
23
 
24
+ .aligncenter {
25
+ display: block;
26
+ margin-left: auto;
27
+ margin-right: auto;
28
+ }
29
 
30
  .amp-wp-enforced-sizes {
31
  /** Our sizes fallback is 100vw, and we have a padding on the container; the max-width here prevents the element from overflowing. **/
32
  max-width: 100%;
33
+ margin: 0 auto;
34
  }
35
 
36
  .amp-wp-unknown-size img {
40
  }
41
 
42
  /* Template Styles */
43
+
44
+ .amp-wp-content,
45
+ .amp-wp-title-bar div {
46
  <?php if ( $content_max_width > 0 ) : ?>
 
47
  margin: 0 auto;
48
+ max-width: <?php echo sprintf( '%dpx', $content_max_width ); ?>;
49
  <?php endif; ?>
50
  }
51
 
52
+ html {
53
+ background: <?php echo sanitize_hex_color( $header_background_color ); ?>;
 
 
 
 
 
54
  }
55
 
56
+ body {
57
+ background: <?php echo sanitize_hex_color( $theme_color ); ?>;
58
+ color: <?php echo sanitize_hex_color( $text_color ); ?>;
59
+ font-family: 'Merriweather', 'Times New Roman', Times, Serif;
60
+ font-weight: 300;
61
+ line-height: 1.75em;
 
 
 
 
 
 
 
 
 
 
 
 
62
  }
63
 
64
  p,
65
  ol,
66
  ul,
67
  figure {
68
+ margin: 0 0 1em;
69
+ padding: 0;
70
  }
71
 
72
  a,
73
  a:visited {
74
+ color: <?php echo sanitize_hex_color( $link_color ); ?>;
75
  }
76
 
77
  a:hover,
78
  a:active,
79
  a:focus {
80
+ color: <?php echo sanitize_hex_color( $text_color ); ?>;
81
  }
82
 
83
+ /* Quotes */
84
+
85
+ blockquote {
86
+ color: <?php echo sanitize_hex_color( $text_color ); ?>;
87
+ background: rgba(127,127,127,.125);
88
+ border-left: 2px solid <?php echo sanitize_hex_color( $link_color ); ?>;
89
+ margin: 8px 0 24px 0;
90
+ padding: 16px;
91
+ }
92
+
93
+ blockquote p:last-child {
94
+ margin-bottom: 0;
95
+ }
96
 
97
  /* UI Fonts */
98
+
99
  .amp-wp-meta,
100
+ .amp-wp-header div,
101
+ .amp-wp-title,
102
+ .wp-caption-text,
103
+ .amp-wp-tax-category,
104
+ .amp-wp-tax-tag,
105
+ .amp-wp-comments-link,
106
+ .amp-wp-footer p,
107
+ .back-to-top {
108
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen-Sans", "Ubuntu", "Cantarell", "Helvetica Neue", sans-serif;
 
109
  }
110
 
111
+ /* Header */
112
 
113
+ .amp-wp-header {
114
+ background-color: <?php echo sanitize_hex_color( $header_background_color ); ?>;
 
 
115
  }
116
 
117
+ .amp-wp-header div {
118
+ color: <?php echo sanitize_hex_color( $header_color ); ?>;
119
+ font-size: 1em;
120
+ font-weight: 400;
121
+ margin: 0 auto;
122
+ max-width: calc(840px - 32px);
123
+ padding: .875em 16px;
124
+ position: relative;
 
125
  }
126
 
127
+ .amp-wp-header a {
128
+ color: <?php echo sanitize_hex_color( $header_color ); ?>;
129
+ text-decoration: none;
130
  }
131
 
132
+ /* Site Icon */
133
+
134
+ .amp-wp-header .amp-wp-site-icon {
135
+ /** site icon is 32px **/
136
+ background-color: <?php echo sanitize_hex_color( $header_color ); ?>;
137
+ border: 1px solid <?php echo sanitize_hex_color( $header_color ); ?>;
138
+ border-radius: 50%;
139
+ position: absolute;
140
+ right: 18px;
141
+ top: 10px;
142
  }
143
 
144
+ /* Article */
145
+
146
+ .amp-wp-article {
147
+ color: <?php echo sanitize_hex_color( $text_color ); ?>;
148
+ font-weight: 400;
149
+ margin: 1.5em auto;
150
+ max-width: 840px;
151
+ overflow-wrap: break-word;
152
+ word-wrap: break-word;
153
  }
154
 
155
+ /* Article Header */
156
+
157
+ .amp-wp-article-header {
158
+ align-items: center;
159
+ align-content: stretch;
160
+ display: flex;
161
+ flex-wrap: wrap;
162
+ justify-content: space-between;
163
+ margin: 1.5em 16px 1.5em;
164
+ }
165
+
166
+ .amp-wp-title {
167
+ color: <?php echo sanitize_hex_color( $text_color ); ?>;
168
+ display: block;
169
+ flex: 1 0 100%;
170
+ font-weight: 900;
171
+ margin: 0 0 .625em;
172
+ width: 100%;
173
+ }
174
+
175
+ /* Article Meta */
176
+
177
+ .amp-wp-meta {
178
+ color: <?php echo sanitize_hex_color( $muted_text_color ); ?>;
179
+ display: inline-block;
180
+ flex: 2 1 50%;
181
+ font-size: .875em;
182
+ line-height: 1.5em;
183
+ margin: 0;
184
+ padding: 0;
185
+ }
186
+
187
+ .amp-wp-article-header .amp-wp-meta:last-of-type {
188
+ text-align: right;
189
+ }
190
+
191
+ .amp-wp-article-header .amp-wp-meta:first-of-type {
192
+ text-align: left;
193
+ }
194
+
195
+ .amp-wp-byline amp-img,
196
+ .amp-wp-byline .amp-wp-author {
197
+ display: inline-block;
198
+ vertical-align: middle;
199
  }
200
 
201
  .amp-wp-byline amp-img {
202
+ border: 1px solid <?php echo sanitize_hex_color( $link_color ); ?>;
203
  border-radius: 50%;
 
 
204
  position: relative;
 
205
  margin-right: 6px;
206
  }
207
 
208
+ .amp-wp-posted-on {
209
+ text-align: right;
210
+ }
211
+
212
+ /* Featured image */
213
 
214
+ .amp-wp-article-featured-image {
215
+ margin: 0 0 1em;
216
+ }
217
+ .amp-wp-article-featured-image amp-img {
218
+ margin: 0 auto;
219
+ }
220
+ .amp-wp-article-featured-image.wp-caption .wp-caption-text {
221
+ margin: 0 18px;
222
  }
223
 
224
+ /* Article Content */
225
+
226
+ .amp-wp-article-content {
227
+ margin: 0 16px;
228
  }
229
 
230
+ .amp-wp-article-content ul,
231
+ .amp-wp-article-content ol {
232
+ margin-left: 1em;
233
  }
234
 
235
+ .amp-wp-article-content amp-img {
236
+ margin: 0 auto;
237
+ }
238
+
239
+ .amp-wp-article-content amp-img.alignright {
240
+ margin: 0 0 1em 16px;
241
  }
242
 
243
+ .amp-wp-article-content amp-img.alignleft {
244
+ margin: 0 16px 1em 0;
245
+ }
246
 
247
  /* Captions */
248
+
249
+ .wp-caption {
250
+ padding: 0;
251
  }
252
 
253
+ .wp-caption.alignleft {
254
+ margin-right: 16px;
255
+ }
256
 
257
+ .wp-caption.alignright {
258
+ margin-left: 16px;
 
 
 
 
 
259
  }
260
 
261
+ .wp-caption .wp-caption-text {
262
+ border-bottom: 1px solid <?php echo sanitize_hex_color( $border_color ); ?>;
263
+ color: <?php echo sanitize_hex_color( $muted_text_color ); ?>;
264
+ font-size: .875em;
265
+ line-height: 1.5em;
266
+ margin: 0;
267
+ padding: .66em 10px .75em;
268
  }
269
 
270
+ /* AMP Media */
271
+
272
  amp-carousel {
273
+ background: <?php echo sanitize_hex_color( $border_color ); ?>;
274
+ margin: 0 -16px 1.5em;
275
  }
 
276
  amp-iframe,
277
  amp-youtube,
278
  amp-instagram,
279
  amp-vine {
280
+ background: <?php echo sanitize_hex_color( $border_color ); ?>;
281
+ margin: 0 -16px 1.5em;
282
+ }
283
+
284
+ .amp-wp-article-content amp-carousel amp-img {
285
+ border: none;
286
  }
287
 
288
  amp-carousel > amp-img > img {
290
  }
291
 
292
  .amp-wp-iframe-placeholder {
293
+ background: <?php echo sanitize_hex_color( $border_color ); ?> url( <?php echo esc_url( $this->get( 'placeholder_image_url' ) ); ?> ) no-repeat center 40%;
294
  background-size: 48px 48px;
295
  min-height: 48px;
296
  }
297
+
298
+ /* Article Footer Meta */
299
+
300
+ .amp-wp-article-footer .amp-wp-meta {
301
+ display: block;
302
+ }
303
+
304
+ .amp-wp-tax-category,
305
+ .amp-wp-tax-tag {
306
+ color: <?php echo sanitize_hex_color( $muted_text_color ); ?>;
307
+ font-size: .875em;
308
+ line-height: 1.5em;
309
+ margin: 1.5em 16px;
310
+ }
311
+
312
+ .amp-wp-comments-link {
313
+ color: <?php echo sanitize_hex_color( $muted_text_color ); ?>;
314
+ font-size: .875em;
315
+ line-height: 1.5em;
316
+ text-align: center;
317
+ margin: 2.25em 0 1.5em;
318
+ }
319
+
320
+ .amp-wp-comments-link a {
321
+ border-style: solid;
322
+ border-color: <?php echo sanitize_hex_color( $border_color ); ?>;
323
+ border-width: 1px 1px 2px;
324
+ border-radius: 4px;
325
+ background-color: transparent;
326
+ color: <?php echo sanitize_hex_color( $link_color ); ?>;
327
+ cursor: pointer;
328
+ display: block;
329
+ font-size: 14px;
330
+ font-weight: 600;
331
+ line-height: 18px;
332
+ margin: 0 auto;
333
+ max-width: 200px;
334
+ padding: 11px 16px;
335
+ text-decoration: none;
336
+ width: 50%;
337
+ -webkit-transition: background-color 0.2s ease;
338
+ transition: background-color 0.2s ease;
339
+ }
340
+
341
+ /* AMP Footer */
342
+
343
+ .amp-wp-footer {
344
+ border-top: 1px solid <?php echo sanitize_hex_color( $border_color ); ?>;
345
+ margin: calc(1.5em - 1px) 0 0;
346
+ }
347
+
348
+ .amp-wp-footer div {
349
+ margin: 0 auto;
350
+ max-width: calc(840px - 32px);
351
+ padding: 1.25em 16px 1.25em;
352
+ position: relative;
353
+ }
354
+
355
+ .amp-wp-footer h2 {
356
+ font-size: 1em;
357
+ line-height: 1.375em;
358
+ margin: 0 0 .5em;
359
+ }
360
+
361
+ .amp-wp-footer p {
362
+ color: <?php echo sanitize_hex_color( $muted_text_color ); ?>;
363
+ font-size: .8em;
364
+ line-height: 1.5em;
365
+ margin: 0 85px 0 0;
366
+ }
367
+
368
+ .amp-wp-footer a {
369
+ text-decoration: none;
370
+ }
371
+
372
+ .back-to-top {
373
+ bottom: 1.275em;
374
+ font-size: .8em;
375
+ font-weight: 600;
376
+ line-height: 2em;
377
+ position: absolute;
378
+ right: 16px;
379
+ }