Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active October 5, 2019 06:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westonruter/73412f3e35cdeaade808f3b8da9d9586 to your computer and use it in GitHub Desktop.
Save westonruter/73412f3e35cdeaade808f3b8da9d9586 to your computer and use it in GitHub Desktop.
Test AMP direct support the Pym.js messaging protocol (see https://github.com/ampproject/amphtml/issues/22714). To test: git clone git@gist.github.com:73412f3e35cdeaade808f3b8da9d9586.git pymjs-testing && wp plugin activate pymjs-testing; Create a new post using the content post_content.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Responsive iFrame Test With Graphic</title>
<meta name="description" content="">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="shortcut icon" href="http://media.npr.org/favicon.ico" />
<base href="https://blog.apps.npr.org/pym.js/examples/graphic/">
<style type="text/css">
/* base styles */
html { -webkit-text-size-adjust: none; /* prevent font scaling in landscape */ }
body {
margin: 0;
padding: 10px;
background-color: #F7F7F7;
font-family: Arial, Helvetica, sans-serif;
}
h1 {
margin: 0 0 22px 0;
font-size: 14px;
color: #333;
}
h3 {
font-weight: normal;
color: #333;
font-size: 16px;
margin: 0 0 11px 0;
}
a, a:link, a:visited {
color: #4774CC;
text-decoration: none;
}
a:hover, a:active { opacity: 0.7; }
.footnotes { margin-bottom: 20px; }
.footnotes h4 {
margin: 2px 0 7px 0;
color: #666;
font-size: 11px;
}
.footnotes p,
.footer p {
margin: 2px 0 0 0;
font-size: 11px;
line-height: 1.3;
color: #808080;
}
/* chart */
#graphic { width: 100%; }
#graphic img {
max-width: 100%;
height: auto;
}
#graphic svg { margin-bottom: 1em; }
#graphic .axis {
font-size: 12px;
fill: #666;
}
#graphic .axis path,
#graphic .axis line {
fill: none;
stroke: #666;
shape-rendering: crispEdges;
}
#graphic .axis.y path { display: none; }
#graphic .axis.y .tick line { display: none; }
#graphic .grid path { display: none; }
#graphic .grid .tick {
stroke: #ccc;
stroke-dasharray: 1px 3px;
stroke-width: 1px;
shape-rendering: crispEdges;
}
#graphic .line {
fill: none;
stroke-width: 3px;
stroke: #11605E;
}
</style>
</head>
<body>
<h1>Retail employment, in millions (2008-2014)</h1>
<div id="graphic"><img src="fallback.png" alt="[Chart]" /></div>
<div class="footnotes">
<h4>Note</h4>
<p>Figures for February and March 2014 are preliminary.</p>
</div>
<div class="footer">
<p>Source: Bureau of Labor Statistics</p>
<p>Credit: NPR</p>
</div>
<script src="js/lib/jquery.js" type="text/javascript"></script>
<script src="js/lib/d3.v3.min.js" type="text/javascript"></script>
<script src="js/lib/modernizr.svg.min.js" type="text/javascript"></script>
<script src="../../src/pym.js" type="text/javascript"></script>
<script src="js/graphic.js" type="text/javascript"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
<base href="https://blog.apps.npr.org/pym.js/examples/navigation/">
<style>
body { background-color: #F7F7F7; }
/* See https://github.com/nprapps/pym.js/issues/83 */
h2 { margin-top: 0; }
</style>
</head>
<body>
<div>
<h2>Scroll links</h2>
<p>These links are inside the iframe, but will scroll the parent:</p>
<ul>
<li><a href="javascript:pymChild.scrollParentTo('about');">About</a></li>
<li><a href="javascript:pymChild.scrollParentTo('custom-events');">Custom Events</a></li>
<li><a href="javascript:pymChild.scrollParentTo('requirements');">Requirements</a></li>
</ul>
<a href="javascript:pymChild.navigateParentTo('https://github.com/nprapps/pym.js');">Go to Github!</a> (see also with <a href="https://github.com/nprapps/pym.js" target="_top">target=_top</a>)
</div>
<script src="../../src/pym.js" type="text/javascript"></script>
<script>var pymChild = new pym.Child();</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Responsive iFrame Test With Quiz</title>
<meta name="description" content="">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="shortcut icon" href="http://media.npr.org/favicon.ico" />
<base href="https://blog.apps.npr.org/pym.js/examples/quiz/">
<style type="text/css">
/* base styles */
html { -webkit-text-size-adjust: none; /* prevent font scaling in landscape */ }
body {
margin: 0;
padding: 10px;
background-color: #F7F7F7;
font: 14px/1.4 Arial, Helvetica, sans-serif;
color: #333;
}
h1 {
margin: 0 0 22px 0;
font-size: 22px;
color: #333;
}
a, a:link, a:visited {
color: #4774CC;
text-decoration: none;
}
a:hover, a:active { opacity: 0.7; }
/* quiz */
.question {
}
.question .q {
font-weight: bold;
font-size: 16px;
}
.question ul {
margin: 0;
padding: 0;
list-style-type: none;
}
.question li {
cursor: pointer;
margin: 2px;
padding: 11px;
background-color: #7598C9;
color: #fff;
}
.question li:hover {
opacity: 0.7;
}
.question.answered li {
opacity: 0.3;
cursor: default;
}
.question.answered li.correct {
font-weight: bold;
color: #fff;
opacity: 1;
}
.answer {
margin: 22px 0 0 0;
font-style: italic;
display: none;
color: #666;
font-size: 12px;
}
</style>
</head>
<body>
<h1>Quiz: Lorem ipsum dolor sit amet</h1>
<div class="question">
<p class="q">Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua?</p>
<ul class="options">
<li>Ut enim ad minim veniam, quis nostrud exercitation</li>
<li class="correct">Ullamco laboris nisi ut aliquip ex ea commodo consequat</li>
<li>Duis aute irure dolor in reprehenderit in voluptate velit</li>
<li>Esse cillum dolore eu fugiat nulla pariatur. Excepteur sint</li>
</ul>
<p class="answer"><strong>Closing Statement:</strong> Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua? Lorem ipsum dolor sit amet. Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua? Lorem ipsum dolor sit amet.</p>
</div>
<script src="js/lib/jquery.js" type="text/javascript"></script>
<script src="../../src/pym.js" type="text/javascript"></script>
<script src="js/graphic.js" type="text/javascript"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Responsive iFrame Test With Table</title>
<meta name="description" content="">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="shortcut icon" href="http://media.npr.org/favicon.ico" />
<base href="https://blog.apps.npr.org/pym.js/examples/table/">
<style type="text/css">
/* base styles */
html { -webkit-text-size-adjust: none; /* prevent font scaling in landscape */ }
body {
margin: 0;
padding: 10px;
background-color: #F7F7F7;
font: 12px/1.4 Arial, Helvetica, sans-serif;
color: #333;
}
h1 {
margin: 0 0 15px 0;
font-size: 18px;
line-height: 1.3;
}
a, a:link, a:visited {
color: #4774CC;
text-decoration: none;
}
a:hover, a:active { opacity: 0.7; }
.footnotes { margin-bottom: 20px; }
.footnotes h4 {
margin: 2px 0 7px 0;
color: #666;
font-size: 11px;
}
.footnotes p,
.footer p {
margin: 2px 0 0 0;
font-size: 11px;
line-height: 1.3;
color: #808080;
}
table {
border-collapse: collapse;
padding: 0;
margin: 0 0 11px 0;
width: 100%;
}
table th {
text-align: left;
border-bottom: 2px solid #eee;
vertical-align: bottom;
padding: 0 10px 10px 10px;
text-align: right;
}
table td {
border-bottom: 1px solid #eee;
vertical-align: top;
padding: 10px;
text-align: right;
}
table th:nth-child(1),
table td:nth-child(1) {
text-align: left;
padding-left: 0;
font-weight: bold;
}
/* responsive table */
@media screen and (max-width: 480px) {
table,
tbody {
display: block;
width: 100%:
}
thead { display: none; }
table tr,
table th,
table td {
display: block;
padding: 0;
text-align: left;
white-space: normal;
}
table tr {
border-bottom: 1px solid #eee;
padding-bottom: 11px;
margin-bottom: 11px;
}
table th[data-title]:before,
table td[data-title]:before {
content: attr(data-title) ":\00A0";
font-weight: bold;
}
table td {
border: none;
margin-bottom: 6px;
color: #444;
}
table td:empty { display: none; }
table td:first-child {
font-size: 14px;
font-weight: bold;
margin-bottom: 6px;
color: #333;
}
table td:first-child:before { content: ''; }
}
</style>
</head>
<body>
<h1>Unemployment rate, by educational attainment (January-March 2014)</h1>
<table>
<thead>
<tr>
<th>Category</th>
<th>January</th>
<th>February</th>
<th>March</th>
</tr>
</thead>
<tr>
<td data-title="Category">Total (16 years and over)</td>
<td data-title="January">6.6</td>
<td data-title="February">6.7</td>
<td data-title="March">6.7</td>
</tr>
<tr>
<td data-title="Category">Less than a high school diploma</td>
<td data-title="January">9.6</td>
<td data-title="February">9.8</td>
<td data-title="March">9.6</td>
</tr>
<tr>
<td data-title="Category">High school graduates, no college</td>
<td data-title="January">6.5</td>
<td data-title="February">6.4</td>
<td data-title="March">6.3</td>
</tr>
<tr>
<td data-title="Category">Some college or associate degree</td>
<td data-title="January">6.0</td>
<td data-title="February">6.2</td>
<td data-title="March">6.1</td>
</tr>
<tr>
<td data-title="Category">Bachelor&rsquo;s degree and higher</td>
<td data-title="January">3.2</td>
<td data-title="February">3.4</td>
<td data-title="March">3.4</td>
</tr>
</table>
<div class="footnotes">
<h4>Note</h4>
<p>Figures for February and March 2014 are preliminary.</p>
</div>
<div class="footer">
<p>Source: Bureau of Labor Statistics</p>
<p>Credit: NPR</p>
</div>
<script src="../../src/pym.js" type="text/javascript"></script>
<script>var pymChild = new pym.Child();</script>
</body>
</html>
<!-- wp:heading -->
<h2>table</h2>
<!-- /wp:heading -->
<!-- wp:shortcode -->
[pymjs_iframe src="example-table.html"]
<!-- /wp:shortcode -->
<!-- wp:heading -->
<h2>graphic</h2>
<!-- /wp:heading -->
<!-- wp:shortcode -->
[pymjs_iframe src="example-graphic.html"]
<!-- /wp:shortcode -->
<!-- wp:heading -->
<h2>quiz</h2>
<!-- /wp:heading -->
<!-- wp:shortcode -->
[pymjs_iframe src="example-quiz.html"]
<!-- /wp:shortcode -->
<!-- wp:heading -->
<h2>(NOT SUPPORTED) follow-links</h2>
<!-- /wp:heading -->
<!-- wp:shortcode -->
[pymjs_iframe src="example-follow-links.html"]
<!-- /wp:shortcode -->
<!-- wp:heading -->
<h2>(NOT SUPPORTED) navigation</h2>
<!-- /wp:heading -->
<!-- wp:shortcode -->
[pymjs_iframe src="example-navigation.html"]
<!-- /wp:shortcode -->
// This code is injected in the iframe'd window to add AMP-compatibility for what Pym.js is doing.
window.addEventListener( 'load', () => {
function sendEmbedSize() {
window.parent.postMessage({
sentinel: 'amp',
type: 'embed-size',
height: document.body.scrollHeight
}, '*');
}
sendEmbedSize();
// Try to make sure elements created dynamically (e.g. graphics) are sized properly.
setTimeout( sendEmbedSize, 500 );
// Update embed size after clicks.
document.addEventListener( 'click', sendEmbedSize, { passive: true } );
} );
<?php
/**
* Plugin Name: Pym.js Testing with AMP
*
* @package Pymjs_Testing
* @author Weston Ruter, Google
* @license GPL-2.0-or-later
* @copyright 2019 Google Inc.
*
* @wordpress-plugin
* Plugin Name: Pym.js Testing with AMP
* Description: Demonstration of porting Pym.js examples over to use amp-iframe. To test, add the following shortcode to post content: <code>[pymjs_iframe src="example-table.html"]</code>.
* Plugin URI: https://gist.github.com/westonruter/73412f3e35cdeaade808f3b8da9d9586
* Version: 0.1.0
* Author: Weston Ruter, Google
* Author URI: https://weston.ruter.net/
* License: GNU General Public License v2 (or later)
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Pymjs_Testing;
const QUERY_VAR = 'pymjs_iframe';
const SHORTCODE = 'pymjs_iframe';
/**
* Return whether AMP endpoint.
*
* @return bool
*/
function is_amp() {
return function_exists( 'is_amp_endpoint' ) && is_amp_endpoint();
}
// Register pym.js.
add_action(
'wp_default_scripts',
function( \WP_Scripts $scripts ) {
$scripts->add( 'pymjs', 'https://pym.nprapps.org/pym.v1.js' );
}
);
if ( isset( $_GET[ QUERY_VAR ] ) ) { // phpcs:ignore
add_action( 'wp', __NAMESPACE__ . '\serve_pymjs_iframe' );
}
add_shortcode( SHORTCODE, __NAMESPACE__ . '\render_shortcode' );
/**
* Render shortcode.
*
* @param array $attr Shortcode attributes.
* @return string Embed, either Pym.js HTML or an <amp-iframe> in AMP.
*/
function render_shortcode( $attr ) {
$attr = shortcode_atts(
array(
'src' => 'example-table.html',
),
$attr,
SHORTCODE
);
// $src = add_query_arg( QUERY_VAR, $attr['src'], home_url( '/' ) );
$src = plugin_dir_url( __FILE__ ) . $attr['src']; // Depends on <https://github.com/ampproject/amphtml/issues/22714>.
ob_start();
if ( is_amp() ) {
?>
<amp-iframe src="<?php echo esc_url( $src ); ?>" layout="fixed-height" height="200" sandbox="allow-scripts allow-top-navigation" resizable>
<button overflow style="position:absolute; bottom:0;">Show more</button>
</amp-iframe>
<?php
} else {
wp_print_scripts( array( 'pymjs' ) );
$id = wp_unique_id( 'pymjs-iframe-' );
unset( $attr['src'] )
?>
<div id="<?php echo esc_attr( $id ); ?>"></div>
<script>
( function ( id, src, attr ) {
window.pymjsInstances = window.pymjsInstances || {};
window.pymjsInstances[ id ] = new pym.Parent( id, src, attr );
} )(
<?php echo wp_json_encode( $id ); ?>,
<?php echo wp_json_encode( $src ); ?>,
<?php echo wp_json_encode( $attr ); ?>
);
</script>
<?php
}
return ob_get_clean();
}
/**
* Serve iframe window to test Pym.js.
*/
function serve_pymjs_iframe() {
if ( ! isset( $_GET[ QUERY_VAR ] ) ) { // phpcs:ignore
wp_die( 'No file provided' );
}
$requested_file = basename( wp_unslash( $_GET[ QUERY_VAR ] ) ); // phpcs:ignore
$allowed_files = array_map( 'basename', glob( __DIR__ . '/*.html' ) );
if ( 0 !== validate_file( $requested_file, $allowed_files ) ) {
wp_die( 'File not allowed' );
}
$html = file_get_contents( __DIR__ . '/' . $requested_file ); // phpcs:ignore
$src = plugin_dir_url( __FILE__ ) . '/pymjs-amp-injected.js';
$html = preg_replace(
':(?=</body>):',
sprintf( '<!-- Injected for AMP-compat --><script async src="%s"></script>', esc_url( $src ) ),
$html
);
echo $html; // phpcs:ignore
exit;
}
@westonruter
Copy link
Author

Some notes:

  • If the iframe is in the viewport when the page loads, the resize will not be performed and instead an overflow button will be shown which the user can click to reveal the full iframe.
  • AMP doesn't allow iframes to be resized smaller than 100px in height. This impacts the follow-links example.
  • The navigation example does not work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment