├── .github
└── pull_request_template.md
├── .gitignore
├── CONTRIBUTING.md
├── README.md
├── SUPPORT.md
├── css
└── editor.css
├── docs
├── img
│ ├── block-in-editor.png
│ ├── block.png
│ ├── pym-example-desktop.png
│ ├── pym-example-phone.png
│ ├── pym-shortcode-in-post.png
│ └── responsive-iframe-npr.png
├── maintainer-notes.md
├── readme.md
├── release-checklist.md
└── upgrade-testing.md
├── inc
├── amp.php
├── block.php
├── class-pymsrc-output.php
├── info-page.php
├── settings-page.php
└── shortcode.php
├── js
├── block.js
├── pym.js
└── pym.v1.min.js
├── license.txt
├── pym-shortcode.php
├── readme.txt
└── release.sh
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Changes
2 |
3 | This pull request makes the following changes:
4 |
5 | -
6 |
7 | ## Why
8 |
9 |
10 |
11 | For #
12 |
13 | ## Testing/Questions
14 |
15 | Features that this PR affects:
16 |
17 | -
18 |
19 |
20 | Questions that need to be answered before merging:
21 |
22 | - [ ] Is this PR targeting the correct branch in this repository?
23 | - [ ] Does this work for the test cases provided in https://github.com/INN/pym-shortcode/blob/master/docs/maintainer-notes.md#testing-before-release ?
24 | - [ ]
25 |
26 | Steps to test this PR:
27 |
28 | 1.
29 |
30 | ## Additional information
31 |
32 | INN Member/Labs Client requesting: (if applicable)
33 |
34 | - [ ] Contributor has read INN's [GitHub code of conduct](https://github.com/INN/.github/blob/master/CODE_OF_CONDUCT.md)
35 | - [ ] Contributor would like to be mentioned in the release notes as: (fill in this blank)
36 | - [ ] Contributor agrees to the license terms of this repository.
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | release/
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We welcome contributions and suggestions to help us improve this project. Please start by [reading our contribution guidelines](https://github.com/INN/docs/blob/master/how-to-work-with-us/contributing.md).
4 |
5 | ### Workflow:
6 |
7 | 1. [Fork this repo](https://help.github.com/articles/fork-a-repo)
8 | 2. Create a branch (git checkout -b my-branch)
9 | 3. Stage and commit your changes (git commit -am 'description of my changes')
10 | 4. Push the changes to your fork (git push origin my-branch)
11 | 5. [Submit a pull request to the parent repo](https://help.github.com/articles/creating-a-pull-request). Please read our [guide to submitting pull requests](https://github.com/inn/docs/blob/master/how-to-work-with-us/pull-requests.md) to see what we expect in a good pull request message.
12 | 6. Pull request should be assigned to:
13 | - [@benlk](http://github.com/benlk) (primary)
14 |
15 | We have [a helpful how-to](https://github.com/INN/docs/blob/master/how-to-work-with-us/via-github.md) that walks through this process in more detail if you're new to using Git.
16 |
17 | Additionally, you can [create issues](https://github.com/INN/pym-shortcode/issues) on this repo to suggest changes or improvements.
18 |
19 | And of course you can always email us: [support@inn.org](mailto:support@inn.org).
20 |
21 | ### Standards
22 |
23 | - Follow all standards from the INN Labs [coding style guide](https://github.com/INN/docs/tree/master/style-guides/code).
24 | - Use [markdown syntax](http://daringfireball.net/projects/markdown/syntax) for all text documents.
25 | - Pull requests for new functionality should be accompanied by tests wherever possible.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pym.js Embeds
2 |
3 | This plugin allows the use of NPR's [Pym.js](http://blog.apps.npr.org/pym.js/) responsive iframe script on WordPress sites, through the use of shortcodes and Gutenberg blocks.
4 |
5 | For detailed examples, ➡️ [read the docs!](./docs/) ⬅️
6 |
7 | For a detailed changelog, [read readme.txt](./readme.txt)!
8 |
9 | For the most-recent release, [see the list of releases](https://github.com/INN/pym-shortcode/releases).
10 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # Support resources
2 |
3 | This plugin's [documentation is on GitHub](https://github.com/INN/pym-shortcode/tree/master/docs).
4 |
5 | If you have questions about about this plugin, please contact us at support@inn.org.
6 |
--------------------------------------------------------------------------------
/css/editor.css:
--------------------------------------------------------------------------------
1 | /**
2 | * The following styles get applied inside the editor only.
3 | *
4 | * Replace them with your own styles or remove the file completely.
5 | */
6 |
7 | .wp-block-pym-shortcode-pym {
8 | background-color: #f8f9f9;
9 | padding: 14px;
10 | }
11 |
12 | .wp-block-pym-shortcode-pym .components-base-control__field {
13 | display: flex;
14 | flex-direction: row;
15 | }
16 | .wp-block-pym-shortcode-pym .components-base-control__field label {
17 | display: flex;
18 | align-items: center;
19 | margin-right: 10px;
20 | white-space: nowrap;
21 | font-weight: 600;
22 | flex-shrink: 0;
23 |
24 | margin-bottom: 0;
25 |
26 | font-size: 13px;
27 | font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;
28 | }
29 | .wp-block-pym-shortcode-pym .dashicon {
30 | margin-right: 10px;
31 | }
32 | .wp-block-pym-shortcode-pym .components-base-control__field input {
33 | flex-grow: 1;
34 | }
35 |
--------------------------------------------------------------------------------
/docs/img/block-in-editor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/pym-shortcode/8121d61e66ed6253cfa03d0672aeff6f46167bff/docs/img/block-in-editor.png
--------------------------------------------------------------------------------
/docs/img/block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/pym-shortcode/8121d61e66ed6253cfa03d0672aeff6f46167bff/docs/img/block.png
--------------------------------------------------------------------------------
/docs/img/pym-example-desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/pym-shortcode/8121d61e66ed6253cfa03d0672aeff6f46167bff/docs/img/pym-example-desktop.png
--------------------------------------------------------------------------------
/docs/img/pym-example-phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/pym-shortcode/8121d61e66ed6253cfa03d0672aeff6f46167bff/docs/img/pym-example-phone.png
--------------------------------------------------------------------------------
/docs/img/pym-shortcode-in-post.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/pym-shortcode/8121d61e66ed6253cfa03d0672aeff6f46167bff/docs/img/pym-shortcode-in-post.png
--------------------------------------------------------------------------------
/docs/img/responsive-iframe-npr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/pym-shortcode/8121d61e66ed6253cfa03d0672aeff6f46167bff/docs/img/responsive-iframe-npr.png
--------------------------------------------------------------------------------
/docs/maintainer-notes.md:
--------------------------------------------------------------------------------
1 | # Notes for Plugin maintainers
2 |
3 | ## Updating Pym.js
4 |
5 | `js/pym.v1.min.js` in this plugin should be kept up-to-date with the current version of https://pym.nprapps.org/pym.v1.min.js.
6 |
7 | `js/pym.js` is specifically `Pym.js` version 1.1.0, which this plugin was introduced with, before we had solid plans for an update strategy. It will not be updated.
8 |
9 | To update:
10 |
11 | - save https://pym.nprapps.org/pym.v1.min.js as `js/pym.v1.min.js` in this plugin, or download https://github.com/nprapps/pym.js/blob/master/dist/pym.v1.min.js from the relevant most-recent tagged release.
12 | - update this plugin's version number to the `Pym.js` version number
13 |
14 | NPR Visuals Team's [stated intention](https://github.com/nprapps/pym.js/tree/master#versioning) is that versions of `Pym.js` will be backwards-compatible for `0.x` and `0.0.x` releases, so we can copy those in directly. When a `x.0.0` release comes around, we'll need to figure out a plan for that. See discussion in https://github.com/INN/pym-shortcode/issues/12
15 |
16 | ## Updating the plugin
17 |
18 | The plugin's `A.B.C` version number should match the version number of the bundled copy of `Pym.js`. We started doing this in plugin release 1.1.2.
19 |
20 | The plugin's [version history](https://github.com/INN/pym-shortcode/releases) looks like this:
21 |
22 | - 0.1: initial release
23 | - 1.1.2
24 | - 1.2.0
25 | - 1.2.0.1: a fix in this plugin
26 | - 1.2.0.2: a fix in this plugin
27 | - 1.3.1
28 | - 1.3.2
29 | - 1.3.2.1: Gutenberg and settings page
30 | - 1.3.2.2: WordPress 5.0 support
31 | - 1.3.2.3: AMP support
32 | - 1.3.2.4: minor bugfix for WP 5.4
33 |
34 | ## Release checklist
35 |
36 | See [release-checklist.md](./release-checklist.md) for the full list.
37 |
38 | ## Testing before release
39 |
40 | You should make a copy of this document to keep track of checking off the checkboxes. A Github comment is a fine place to do that, as in https://github.com/INN/pym-shortcode/issues/68#issuecomment-593634311.
41 |
42 | See also https://github.com/INN/docs/blob/master/projects/wordpress-plugins/release.sh.md
43 |
44 | Run the following tests both with and without [the AMP plugin](https://wordpress.org/plugins/amp/) activated:
45 | - [ ] with
46 | - [ ] without
47 |
48 | Plugin settings:
49 |
50 | - [ ] Does the plugin settings page work? `/wp-admin/options-general.php?page=pym-embed-settings`
51 | - [ ] Does the plugin info page work? `/wp-admin/tools.php?page=pym-embeds-info`
52 |
53 | Shortcode tests:
54 |
55 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html"]`
56 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" pymsrc="https://pym.nprapps.org/pym.v1.min.js"]`
57 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" pymoptions=" xdomain: '\\*\.npr\.org' "]`
58 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" class="one two three four float-left mw_50"]`
59 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" align=""]`
60 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" align="none"]`
61 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" align="left"]`
62 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" align="center"]`
63 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" align="right"]`
64 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" align="wide"]`
65 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" align="full"]`
66 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" id="extremely_specific_id"]`
67 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" pymsrc="https://pym.nprapps.org/pym.v1.min.js" pymoptions=""]`
68 | - [ ] `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" pymsrc="https://pym.nprapps.org/pym.v1.min.js" pymoptions=""]`
69 |
70 | Gutenberg tests:
71 |
72 | - [ ] the block, when inserted, prompts users for a URL
73 | - [ ] the block's alignment, custom classes, custom ID, and other options are respected.
74 | - [ ] the block uses the default pymsrc URL if the pymsrc attribute is not set
75 | - [ ] on a site with Gutenberg not installed, the plugin functions
76 | - [ ] on a 4.9 site with Gutenberg installed, the plugin functions
77 | - [ ] on a 5.0 site, the plugin functions
78 |
--------------------------------------------------------------------------------
/docs/readme.md:
--------------------------------------------------------------------------------
1 | # Pym.js Embeds for WordPress
2 |
3 | Pym.js Embeds provides shortcode and Gutenberg block wrappers for embedding responsive iframes using [Pym.js](http://blog.apps.npr.org/pym.js/), developed by the NPR Visuals Team. Embedded content resizes vertically to match its container's width.
4 |
5 | Contents:
6 |
7 | 1. [Plugin Installation](#plugin-installation)
8 | 2. [The Pym.js Shortcode](#the-pymjs-shortcode)
9 | 3. [The Pym.js Block](#the-pym-block)
10 | 4. [Embed Options](#embed-options)
11 | 1. [src: the only required argument](#src-the-child-url-required-argument)
12 | 2. [pymsrc](#pymsrc-the-url-for-pymjs)
13 | 3. [pymoptions](#pymoptions-settings-for-pymjs)
14 | 4. [class](#class-to-add-html-classes-to-the-pymjs-parent-element)
15 | 5. [align](#align-for-wordpress-alignment-support)
16 | 6. [id](#id-to-set-the-pym-parent-elements-id)
17 | 5. [Plugin Options](#plugin-options)
18 | 5. [Frequently Asked Questions](#frequently-asked-questions)
19 | 1. [Why would I want to use Pym.js in the first place?](#why-would-i-want-to-use-pym-in-the-first-place)
20 | 2. [Why is a WordPress plugin needed to use Pym.js?](#why-is-a-wordpress-plugin-needed-to-use-pymjs)
21 | 3. [When would I use a Pym.js solution versus embed code without using Pym.js?](#when-would-i-use-a-pymjs-solution-versus-embed-code-without-using-pymjs)
22 | 4. [Is Pym.js or this plugin dependent on jQuery or any other library?](#is-pymjs-or-this-plugin-dependent-on-jquery-or-any-other-library)
23 | 5. [Where should I put the graphic files I want to embed using `Pym.js`?](#where-should-i-put-the-graphic-files-i-want-to-embed-using-pymjs)
24 | 6. [What is the URL for pym.v1.min.js?](#what-is-the-url-for-pymv1minjs)
25 | 7. [What is the difference between `Pym.js` and `pym.v1.min.js`?](what-is-the-difference-between-pymjs-and-pymv1minjs)
26 | 8. [Why would I want to change the Pym.js source URL?](#why-would-i-want-to-change-the-pymjs-source-url)
27 | 9. [I've set a different pymsrc option, but now I'm seeing a message in the console](#ive-set-a-different-pymsrc-option-but-now-im-seeing-a-message-in-the-console)
28 | 10. [How do I serve Pym.js if the embedded page uses HTTPS and my site does not?](#how-do-i-serve-pymjs-if-the-embedded-page-uses-https-and-my-site-does-not)
29 | 11. [How do I know if there's an HTTPS problem with a given embedded iframe?](#how-do-i-know-if-theres-an-https-problem-with-a-given-embedded-iframe)
30 | 12. [What license is this plugin licensed under?](#what-license-is-this-plugin-licensed-under)
31 | 13. [How do I contribute to this plugin?](#how-do-i-contribute-to-this-plugin)
32 | 14. [How do I get support for this plugin?](#how-do-i-get-support-for-this-plugin)
33 | 6. [Other Pym.js Resources](#other-pymjs-resources)
34 |
35 | ## Plugin Installation
36 |
37 | 1. In the WordPress Dashboard go to **Plugins**, then click the **Add Plugins** button and search the WordPress Plugins Directory for Pym.js Embeds. Alternatively, you can download the zip file from this Github repo and upload it manually to your WordPress site.
38 | 2. Activate the plugin through the 'Plugins' screen in WordPress.
39 | 3. Nothing to configure, just begin using Pym.js Embeds!
40 |
41 | ## The Pym.js Shortcode
42 |
43 | In a WordPress post or page, use the Pym.js Shortcode like this:
44 |
45 | `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html"]`
46 |
47 | Example in a post:
48 |
49 | 
50 |
51 | ## The Pym.js Block
52 |
53 | Example in a post:
54 |
55 | 
56 |
57 | For the block, all options available via shortcode arguments are available through the block's Advanced Options panel.
58 |
59 | ## Embed Options
60 |
61 | ```
62 | [pym src="" pymsrc="" pymoptions="" class="" align="" id="" ]
63 | ```
64 |
65 | ### `src`, the child URL (required argument)
66 |
67 | `src` is the URL of the page that is to be embedded. In these examples, we use https://blog.apps.npr.org/pym.js/examples/table/child.html, the source code for which can be found at https://github.com/nprapps/pym.js/tree/master/examples/table .
68 |
69 | For the shortcode, `src` is the only required parameter.
70 |
71 | ```
72 | [pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html"]
73 | ```
74 |
75 | Here's what the setting looks like in a block:
76 |
77 | 
78 |
79 | ### `pymsrc`, the URL for Pym.js
80 |
81 | `pymsrc` is optional; only set this if you need to specify a different source for `Pym.js` than the default. The default `Pym.js` source URL is `js/pym.v1.min.js` in this plugin's directory on your server. [NPR recommends](http://blog.apps.npr.org/pym.js/#get-pym-cdn) that you use the CDN version of `Pym.js` in most cases, which is available at `https://pym.nprapps.org/pym.v1.min.js`. An example shortcode using this option is as follows:
82 |
83 | ```
84 | [pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" pymsrc="https://pym.nprapps.org/pym.v1.min.js"]
85 | ```
86 |
87 | ### `pymoptions`, settings for Pym.js
88 |
89 | `pymoptions` is optional; this should be a javascript object without the surrounding `{}`, and is given in the event that options need to be passed to the `pymParent`. NPR gives [this example](http://blog.apps.npr.org/pym.js/#examples) javascript:
90 |
91 | ```js
92 | pym.Parent('example', 'https://blog.apps.npr.org/pym.js/examples/table/child.html', { xdomain: '*\.npr\.org' });
93 | ```
94 |
95 | To do the same thing with this Pym.js Shortcode, you would write:
96 |
97 | ```
98 | [pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" pymoptions=" xdomain: '\\*\.npr\.org' "]
99 | ```
100 |
101 | For a full list of options, see the Pym.js [API documentation for the `config` parameter](http://blog.apps.npr.org/pym.js/api/pym.js/1.3.2/module-pym.Parent.html).
102 |
103 | ### `class`, to add HTML classes to the Pym.js parent element
104 |
105 | `class` is optional; this should be a valid HTML class name. It will be added to the element's default class, `'pym'`. You would want to use this if, for example, you wanted to [use a size-based class name to determine the size of the embed on your site](https://github.com/INN/pym-shortcode/issues/23). The class `'pym'` will always be output on container elements created by the Pym.js Shortcode. This class was introduced in version 1.2.2.
106 |
107 | For example, the shortcode `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" class="one two three four float-left mw_50"]` results in the following output:
108 |
109 | ```html
110 |
111 | ...
112 |
113 | ```
114 |
115 | If you do not want the class `'pym'` output on container elements, [add a filter](https://codex.wordpress.org/Plugin_API/Filter_Reference) to the hook `pym_shortcode_default_class` that returns an empty string.
116 |
117 | ### `align`, for WordPress alignment support
118 |
119 | `align` is optional; this should be one of the [WordPress-provided generated alignment types](https://codex.wordpress.org/CSS#WordPress_Generated_Classes): `left`, `right`, `center`, `none`. If your theme supports the `wide` or `full` values, you can use those too, as the value provided here will be prefixed with `align` and output as a CSS class on the Pym.js parent, so that the shortcode `[pym align="foo"]` results in the output `
...`
120 |
121 | In the Gutenberg editor, the alignment options are provided by the alignment controls that appear when the block is selected. The default choice is "none", with no option selected, and the other options are to align it left, center, or right. If your theme [declares support for the "wide" alignment](https://wordpress.org/gutenberg/handbook/extensibility/theme-support/#wide-alignment), you'll also see options for "wide" and "full" widths. The appearance of these alignment options on the page will depend on your site's theme.
122 |
123 | 
124 |
125 | ### `id`, to set the Pym.js parent element's ID
126 |
127 | `id` is optional; this should be a valid HTML element ID name. It will be used as the ID of your `pymParent` iframe on the parent page. You would want to use this if, for example, [your embedded page contained navigation to another page, requiring the second page to know the pymParent element ID](https://github.com/INN/pym-shortcode/issues/20).
128 |
129 | For example, the shortcode `[pym src="https://blog.apps.npr.org/pym.js/examples/table/child.html" id="extremely_specific_id"]` results in the following output:
130 |
131 | ```html
132 |
133 | ...
134 |
135 | ```
136 |
137 | ## Plugin Options
138 |
139 | The Pym.js Embed Settings page can be found under WordPress' "Settings" menu.
140 |
141 | The settings page provides two options: the default pymsrc and the option to override all pymsrc arguments.
142 |
143 | We **strongly recommend** that you set the default pymsrc to `https://pym.nprapps.org/pym.v1.min.js` and check the box to enable the pymsrc override.
144 |
145 | ### Default pymsrc
146 |
147 | As explained in the documentation for [the pymsrc embed option](#pymsrc-the-url-for-pymjs), the default copy of `Pym.js` used by this plugin is the copy bundled with this plugin. NPR recommends, and we recommend, that you use the copy of `Pym.js` provided by NPR's CDN. However, this plugin cannot force you to do so; the WordPress.org plugin guidelines generally [prohibit plugin use of third-party scripts without user consent](https://developer.wordpress.org/plugins/wordpress-org/detailed-plugin-guidelines/#7-plugins-may-not-track-users-without-their-consent), and frown upon plugins that use CDNs by default. Therefore, we give you the option to use NPR's CDN, or your newsroom's CDN, and ship the plugin in a state that defaults to no CDN.
148 |
149 | Shortcodes and blocks have the the option to specify an alternate source for `Pym.js` at a per-embed level, which allows you to opt into using the CDN version of the script on a per-embed level. This is less than optimal; every time you create a `Pym.js` embed on your site, you will need to check that the pymsrc option is set.
150 |
151 | To save time and effort, set the "Default pymsrc" option in the plugin settings to NPR's CDN copy of `Pym.js`: `https://pym.nprapps.org/pym.v1.min.js`
152 |
153 | ### Override pymsrc
154 |
155 | Anyone who can edit a post can set the pymsrc URL on a shortcode or a block, and the pymsrc URL can be *any* resource. Browsers will try to load it as JavaScript, even if the set URL is for an image, a 404 page, or a non-`Pym.js` library. This is not good.
156 |
157 | We **strongly recommend** that you check this box, to force all `Pym.js` Embed shortcodes and blocks to use the plugin's default pymsrc.
158 |
159 | This box is not checked by default, because defaulting to overriding the pymsrc URL would potentially break existing shortcodes on sites that used this plugin before release version 1.3.2.1. If you used this plugin to create `[pym]` shortcodes before release 1.3.2.1, and wish to test your embeds before enabling the override, read [this testing advice](./upgrade-testing.md).
160 |
161 | ## Frequently Asked Questions
162 |
163 | ### Why would I want to use Pym.js in the first place?
164 |
165 | Using iframes in a responsive page can be frustrating. It’s easy enough to make an iframe’s width span 100% of its container, but sizing its height is tricky — especially if the content of the iframe changes height depending on page width (for example, because of text wrapping or media queries) or events within the iframe. For more information, see [NPR's documentation for `Pym.js`](http://blog.apps.npr.org/pym.js).
166 |
167 | ### Why is a WordPress plugin needed to use `Pym.js`?
168 |
169 | Normally WordPress strips out JavaScript inserted in posts and pages, so the usual HTML `Pym.js` embed code's `',
138 | wp_json_encode( __( 'Hi Pym.js user! It looks like your post has multiple values for pymsrc for the blocks and shortcodes in use on this page. This may be causing problems for your Pym.js embeds. For more details, see https://github.com/INN/pym-shortcode/tree/master/docs#ive-set-a-different-pymsrc-option-but-now-im-seeing-a-message-in-the-console', 'pym_shortcode' ) ),
139 | wp_json_encode( $this->sources )
140 | );
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/inc/info-page.php:
--------------------------------------------------------------------------------
1 | %1$s',
38 | esc_html( get_admin_page_title() )
39 | );
40 |
41 | printf(
42 | '
',
154 | __( 'The Pym.js JavaScript library can be provided from many sources. By default, shortcodes and blocks will use a copy of Pym.js hosted on your website to power embeds. For more information about changing the Pym.js source URL, referred to as \'pymsrc\', please read this plugin\'s documentation.', 'pym-embeds' )
155 | );
156 | }
157 |
158 | /**
159 | * Sanitization callback for the option_value.
160 | *
161 | * If the default_pymsrc isn't a valid URL or is the pym_pymsrc_local_url, blank it.
162 | * If the override_pymsrc isn't 'on' indicating it's checked, blank it.
163 | *
164 | * @since 1.3.2.1
165 | * @param Mixed $value The setting.
166 | * @return Array The sanitized setting
167 | * @uses pym_pymsrc_local_url
168 | * @uses pym_plugin_version
169 | */
170 | function sanitize_callback( $value ) {
171 | $new_value = array();
172 |
173 | $proposed_pymsrc = wp_http_validate_url( $value['default_pymsrc'] );
174 | if ( pym_pymsrc_local_url() === $proposed_pymsrc ) {
175 | $proposed_pymsrc = null;
176 | }
177 | $new_value['default_pymsrc'] = ! empty( $proposed_pymsrc ) ? $proposed_pymsrc : null;
178 |
179 | $new_value['override_pymsrc'] = ( isset( $value['override_pymsrc'] ) && 'on' === $value['override_pymsrc'] ) ? 'on': null;
180 |
181 | $new_value['version'] = pym_plugin_version();
182 |
183 | return $new_value;
184 | }
185 |
186 | /**
187 | * The field for the default Pymsrc
188 | *
189 | * @param Array $args The arguments passed to the settings field callback.
190 | */
191 | function field_default_pymsrc( $args ) {
192 | $settings = get_option( option_key() );
193 | $id = isset( $args['label_for'] ) ? $args['label_for'] : 'default_pymsrc';
194 | $id = option_key() . '[' . $id . ']';
195 |
196 | $url = isset( $settings['default_pymsrc'] ) ? $settings['default_pymsrc'] : '';
197 | printf(
198 | '',
199 | esc_attr( $id ),
200 | esc_attr( $url )
201 | );
202 |
203 | printf(
204 | '',
205 | esc_attr( $id ),
206 | wp_kses_post( __( 'This URL is where Pym.js will be loaded from for all embeds that do not set a pymsrc in the shortcode attributes or block settings. NPR and the Pym.js Embed plugin maintainers recommend that you use the NPR-provided CDN for this purpose: https://pym.nprapps.org/pym.v1.min.js', 'pym-embeds' ) )
207 | );
208 | printf(
209 | '',
210 | esc_attr( $id ),
211 | sprintf(
212 | // translators: %1$s is a bare URL.
213 | wp_kses_post( __( 'If no pymsrc URL is set here, then the plugin-provided copy of Pym.js will be used as the default: %1$s', 'pym-embeds' ) ),
214 | esc_html( pym_pymsrc_local_url() )
215 | )
216 | );
217 | }
218 |
219 | /**
220 | * The checkbox to force all embeds to use the default
221 | *
222 | * @param Array $args The arguments passed to the settings field callback.
223 | */
224 | function field_override_pymsrc( $args ) {
225 | $settings = get_option( option_key() );
226 | $id = isset( $args['label_for'] ) ? $args['label_for'] : 'override_pymsrc';
227 | $id = option_key() . '[' . $id . ']';
228 |
229 | $value = isset( $settings['override_pymsrc'] ) ? $settings['override_pymsrc'] : '';
230 | printf(
231 | '',
232 | esc_attr( $id ),
233 | checked( $value, 'on', false )
234 | );
235 |
236 | printf(
237 | '',
238 | esc_attr( $id ),
239 | esc_html__( 'Checking this box means that every Pym.js embed will use the default pymsrc URL, ignoring the pymsrc URL set in the embed\'s shortcode attributes or block settings. We recommend that you check this box after setting the default pymsrc URL to the CDN-provided copy of the library.', 'pym-embeds' )
240 | );
241 | }
242 |
--------------------------------------------------------------------------------
/inc/shortcode.php:
--------------------------------------------------------------------------------
1 |
',
64 | esc_attr( $actual_id ),
65 | esc_attr( $actual_classes )
66 | );
67 |
68 | // What's the pymsrc for this shortcode?
69 | if (
70 | pym_maybe_override_pymsrc() // if the box to override has been checked...
71 | || empty( $atts['pymsrc'] ) // if there is no specified pymsrc...
72 | || ! wp_http_validate_url( $atts['pymsrc'] ) // if the specified pymsrc is not a safe URL...
73 | ) {
74 | $pymsrc = pym_pymsrc_default_url(); // use the default URL.
75 | } else {
76 | $pymsrc = $atts['pymsrc'];
77 | }
78 |
79 | // If this is the first Pym.js element on the page,
80 | // register the default pymsrc script tag for output.
81 | //
82 | // Or, if the pymsrc is set, register that specific pymsrc for output.
83 | if ( 0 === $pym_id || ! empty( $atts['pymsrc'] ) ) {
84 | $pymsrc_output = Pymsrc_Output::get_instance();
85 | $pymsrc_output->add( $pymsrc );
86 | }
87 |
88 | // Output the parent's scripts.
89 | pym_shortcode_script_footer_enqueue( array(
90 | 'pym_id' => $pym_id,
91 | 'actual_id' => $actual_id,
92 | 'src' => $src,
93 | 'pymoptions' => $pymoptions,
94 | // The following options are not necessary for the default
95 | // function pym_shortcode_script_footer_enqueue, but are provided
96 | // in case someone wants to do other things with their own version
97 | // of this function.
98 | // @link https://github.com/INN/pym-shortcode/issues/19
99 | 'actual_classes' => $actual_classes,
100 | 'pymsrc' => $pymsrc,
101 | ) );
102 |
103 | // What is output to the page:
104 | $ret = ob_get_clean();
105 | return $ret;
106 | }
107 | add_shortcode( 'pym', 'pym_shortcode' );
108 |
109 | /**
110 | * Given the necessary arguments for creating an embed's activation javascript, enqueue that script in the footer
111 | *
112 | * This function is pluggable. https://codex.wordpress.org/Pluggable_Functions
113 | * If your site requires a different activation script than the one provided
114 | * by this function, create a function in your site's theme or in a plugin
115 | * with this plugin's name, accepting the arguments passed to this function.
116 | *
117 | * @link https://github.com/INN/pym-shortcode/issues/19
118 | *
119 | * @param Array $args Has the following indices:
120 | * - 'pym_id' Which Pym.js embed instance this is on the page, provided for
121 | * informational purposes. In this function, the pym_id value is
122 | * used as the variable name in `var pym_id = new pym.Parent(...);`
123 | * - 'actual_id' the element ID used for the Pym.js container element,
124 | * which is at this point set on the page and not changeable from
125 | * this function. This is the first argument for `new pym.Parent()`.
126 | * - 'src' the URL for the Pym.js child page. This is the second argument
127 | * for `new pym.Parent()`.
128 | * - 'pymoptions' The third argument for `pym.Parent()` See the xdomain
129 | * argument in http://blog.apps.npr.org/pym.js/#example-block
130 | * - 'actual_classes' The classes used on the Pym.js container element,
131 | * provided to this function for informational purposes.
132 | * - 'pymsrc' The URL from which Pym.js is to be loaded for this emebed,
133 | * based on the shortcode/block options and the plugin settings.
134 | *
135 | * @since 1.3.2.1
136 | */
137 | if ( ! function_exists( 'pym_shortcode_script_footer_enqueue' ) ) {
138 | function pym_shortcode_script_footer_enqueue( $args = array() ) {
139 | add_action(
140 | 'wp_footer',
141 | function() use ( $args ) {
142 | // Output the parent's scripts.
143 | echo '';
152 | echo PHP_EOL; // for pretty printing of scripts in the footer.
153 | },
154 | 20 // So that this comes after the pymsrc tag is output at priority 10.
155 | );
156 | }
157 | }
158 |
159 | /**
160 | * Whether to force use of the default pymsrc URL.
161 | *
162 | * @since 1.3.2.1
163 | * @uses \INN\PymEmbeds\Settings\option_key()
164 | * @return bool Whether to force use of the default URL.
165 | */
166 | function pym_maybe_override_pymsrc() {
167 | $settings = get_option( \INN\PymEmbeds\Settings\option_key() );
168 | if ( isset( $settings['override_pymsrc'] ) && 'on' === $settings['override_pymsrc'] ) {
169 | return true;
170 | }
171 | return false;
172 | }
173 |
174 | /**
175 | * The default URL for pymsrc, as defined in plugin settings
176 | *
177 | * @since 1.3.2.1
178 | * @uses pym_pymsrc_local_url
179 | */
180 | function pym_pymsrc_default_url() {
181 | $settings = get_option( \INN\PymEmbeds\Settings\option_key() );
182 | if ( isset ( $settings['default_pymsrc'] ) ) {
183 | if ( wp_http_validate_url( $settings['default_pymsrc'] ) ) {
184 | return $settings['default_pymsrc'];
185 | }
186 | }
187 | return pym_pymsrc_local_url();
188 | }
189 |
190 | /**
191 | * The plugin-provided pymsrc url
192 | *
193 | * @since 1.3.2.1
194 | * @return string The URL for /wp-content/plugins/pym-shortcode/js/pym.v1.min.js
195 | */
196 | function pym_pymsrc_local_url() {
197 | return plugins_url( '/js/pym.v1.min.js', dirname( __FILE__ ) );
198 | }
199 |
--------------------------------------------------------------------------------
/js/block.js:
--------------------------------------------------------------------------------
1 | ( function( wp ) {
2 | /**
3 | * Registers a new block provided a unique name and an object defining its behavior.
4 | * @see https://github.com/WordPress/gutenberg/tree/master/blocks#api
5 | */
6 | var registerBlockType = wp.blocks.registerBlockType;
7 |
8 | /**
9 | * Returns a new element of given type. Element is an abstraction layer atop React.
10 | * @see https://github.com/WordPress/gutenberg/tree/master/element#element
11 | */
12 | var el = wp.element.createElement;
13 |
14 | /**
15 | * Rendering
16 | */
17 | var ServerSideRender = wp.components.ServerSideRender;
18 |
19 | /**
20 | * Text tools
21 | */
22 | var TextControl = wp.components.TextControl;
23 | var PanelBody = wp.components.PanelBody;
24 |
25 | /**
26 | * The sidebar controls I think?
27 | * @todo check definition
28 | */
29 | var InspectorControls = wp.editor.InspectorControls;
30 | var InspectorAdvancedControls = wp.editor.InspectorAdvancedControls;
31 |
32 | /**
33 | * Retrieves the translation of text.
34 | * @see https://github.com/WordPress/gutenberg/tree/master/i18n#api
35 | */
36 | var __ = wp.i18n.__;
37 |
38 | /**
39 | * Literally just for a fancy dashicon
40 | * @see https://github.com/WordPress/gutenberg/blob/master/packages/components/src/dashicon/README.md
41 | */
42 | var dashicon = wp.components.Dashicon;
43 |
44 | /**
45 | * Every block starts by registering a new block type definition.
46 | * @see https://wordpress.org/gutenberg/handbook/block-api/
47 | */
48 | registerBlockType( 'pym-shortcode/pym', {
49 | /**
50 | * This is the display title for your block, which can be translated with `i18n` functions.
51 | * The block inserter will show this name.
52 | */
53 | title: __( 'Pym.js Embed' ),
54 |
55 | /**
56 | * An icon property should be specified to make it easier to identify a block.
57 | * These can be any of WordPress’ Dashicons, or a custom svg element.
58 | */
59 | icon: 'analytics',
60 |
61 | /**
62 | * Blocks are grouped into categories to help users browse and discover them.
63 | * The categories provided by core are `common`, `embed`, `formatting`, `layout` and `widgets`.
64 | */
65 | category: 'embed',
66 |
67 | /**
68 | * Gutenberg features supported by this block
69 | * @link https://wordpress.org/gutenberg/handbook/block-api/#supports-optional
70 | */
71 | supports: {
72 | html: false, // Removes support for an HTML mode.
73 | align: true, // supports alignment
74 | alignWide: true, // supports the extra slignment
75 | anchor: false, // see https://github.com/INN/pym-shortcode/issues/36
76 | customClassName: true,
77 | className: true,
78 | inserter: true,
79 | multiple: true,
80 | },
81 |
82 | /**
83 | * Describe the block for the block inspector
84 | */
85 | description: __( 'Embed a webpage using NPR\'s Pym.js' ),
86 |
87 | /**
88 | * Make the block easier to find by including keywords
89 | */
90 | keywords: [ __( 'NPR' ) ],
91 |
92 | /**
93 | * The edit function describes the structure of your block in the context of the editor.
94 | * This represents what the editor will render when the block is used.
95 | * @see https://wordpress.org/gutenberg/handbook/block-edit-save/#edit
96 | *
97 | * @param {Object} [props] Properties passed from the editor.
98 | * @return {Element} Element to render.
99 | */
100 | edit: function( props ) {
101 | return [
102 | // https://gist.github.com/pento/cf38fd73ce0f13fcf0f0ae7d6c4b685d#file-php-block-js-L59
103 | el(
104 | 'div',
105 | {
106 | // attributes
107 | className: props.className,
108 | },
109 | // children follow
110 | el( TextControl, {
111 | label: [
112 | el(
113 | dashicon,
114 | {
115 | icon: 'analytics'
116 | },
117 | ),
118 | __( 'Pym.js Child URL' )
119 | ],
120 | value: props.attributes.src,
121 | placeholder: __( 'What is the URL of your Pym.js child page?' ),
122 | onChange: ( value ) => { props.setAttributes( { src: value } ); },
123 | } )
124 | ),
125 | el( InspectorControls, {},
126 | el(
127 | PanelBody, // inserting a PanelBody here for the presentational classes, so that it matches the display of InspectorAdvancedControls children
128 | {
129 | initialOpen: true,
130 | },
131 | el( TextControl, {
132 | label: __( 'Pym.js Child URL' ),
133 | value: props.attributes.src,
134 | placeholder: __( 'What is the URL of your Pym.js child page?' ),
135 | onChange: ( value ) => { props.setAttributes( { src: value } ); },
136 | } )
137 | )
138 | ),
139 | el( InspectorAdvancedControls, {},
140 | el( TextControl, {
141 | label: __( 'Parent Element ID (optional)' ),
142 | value: props.attributes.id,
143 | onChange: ( value ) => { props.setAttributes( { id: value } ); },
144 | help: __( 'The Pym.js block will automatically generate an ID for the parent element and use that to initiate the Pym.js embed. If your child page\'s code requires its parent to have a specific element ID, set that here.' ),
145 | } ),
146 | el( TextControl, {
147 | label: __( 'Pym.js Source URL (optional)' ),
148 | value: props.attributes.pymsrc,
149 | onChange: ( value ) => { props.setAttributes( { pymsrc: value } ); },
150 | } ),
151 | el( TextControl, {
152 | label: __( 'Pym.js Options' ),
153 | value: props.attributes.pymoptions,
154 | onChange: ( value ) => { props.setAttributes( { pymoptions: value } ); },
155 | // @todo make this translatable https://github.com/WordPress/gutenberg/blob/master/packages/i18n/README.md
156 | help: [
157 | 'For more about this control, see ',
158 | el(
159 | 'a',
160 | {
161 | href: 'http://blog.apps.npr.org/pym.js/',
162 | },
163 | 'the Usage section of the Pym.js docs',
164 | ),
165 | '.'
166 | ],
167 | } )
168 | ),
169 | ];
170 | },
171 |
172 | /**
173 | * The save function defines the way in which the different attributes should be combined
174 | * into the final markup, which is then serialized by Gutenberg into `post_content`.
175 | * @see https://wordpress.org/gutenberg/handbook/block-edit-save/#save
176 | *
177 | * Though this block has a render callback, we save the URL of the embed in the post_content
178 | * just in case this plugin is ever deactivated.
179 | *
180 | * @return {Element} Element to render.
181 | */
182 | save: function( props ) {
183 | return wp.element.createElement(
184 | 'a',
185 | {
186 | href: props.attributes.src,
187 | },
188 | props.attributes.src
189 | );
190 | },
191 |
192 | /**
193 | * @todo provide transformation from shortcode
194 | * @todo provide transformation to plain embed
195 | *
196 | * @link https://wordpress.org/gutenberg/handbook/block-api/#transforms-optional
197 | */
198 | transforms: {
199 | from: [
200 | {
201 | type: 'shortcode',
202 | tag: 'pym',
203 | attributes: {
204 | src: {
205 | type: 'string',
206 | shortcode: function( named ) {
207 | return named.src ? named.src : '';
208 | },
209 | },
210 | pymsrc: {
211 | type: 'string',
212 | shortcode: function( named ) {
213 | return named.pymsrc ? named.pymsrc : '';
214 | },
215 | },
216 | pymoptions: {
217 | type: 'string',
218 | shortcode: function( named ) {
219 | return named.pymoptions ? named.pymoptions : '';
220 | },
221 | },
222 | id: {
223 | type: 'string',
224 | shortcode: function( named ) {
225 | return named.id ? named.id : '';
226 | },
227 | },
228 | className: {
229 | type: 'string',
230 | shortcode: function( named ) {
231 | return named.class ? named.class : '';
232 | },
233 | },
234 | align: {
235 | type: 'string',
236 | shortcode: function( named ) {
237 | var align = named.align ? named.align : 'alignnone';
238 | return align.replace( 'align', '' );
239 | },
240 | },
241 | },
242 | },
243 | ]
244 | // @todo provide "to" transformations for embed, plain HTML, etc
245 | },
246 | } );
247 | } )(
248 | window.wp
249 | );
250 |
--------------------------------------------------------------------------------
/js/pym.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Pym.js is library that resizes an iframe based on the width of the parent and the resulting height of the child.
3 | * Check out the docs at http://blog.apps.npr.org/pym.js/ or the readme at README.md for usage.
4 | */
5 |
6 | /* global module */
7 |
8 | (function(factory) {
9 | if (typeof define === 'function' && define.amd) {
10 | define(factory);
11 | } else if (typeof module !== 'undefined' && module.exports) {
12 | module.exports = factory();
13 | } else {
14 | window.pym = factory.call(this);
15 | }
16 | })(function() {
17 | var MESSAGE_DELIMITER = 'xPYMx';
18 |
19 | var lib = {};
20 |
21 | /**
22 | * Generic function for parsing URL params.
23 | * Via http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
24 | *
25 | * @method _getParameterByName
26 | * @param {String} name The name of the paramter to get from the URL.
27 | */
28 | var _getParameterByName = function(name) {
29 | var regex = new RegExp("[\\?&]" + name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]') + '=([^]*)');
30 | var results = regex.exec(location.search);
31 |
32 | if (results === null) {
33 | return '';
34 | }
35 |
36 | return decodeURIComponent(results[1].replace(/\+/g, " "));
37 | };
38 |
39 | /**
40 | * Check the message to make sure it comes from an acceptable xdomain.
41 | * Defaults to '*' but can be overriden in config.
42 | *
43 | * @method _isSafeMessage
44 | * @param {Event} e The message event.
45 | * @param {Object} settings Configuration.
46 | */
47 | var _isSafeMessage = function(e, settings) {
48 | if (settings.xdomain !== '*') {
49 | // If origin doesn't match our xdomain, return.
50 | if (!e.origin.match(new RegExp(settings.xdomain + '$'))) { return; }
51 | }
52 |
53 | return true;
54 | };
55 |
56 | /**
57 | * Construct a message to send between frames.
58 | *
59 | * NB: We use string-building here because JSON message passing is
60 | * not supported in all browsers.
61 | *
62 | * @method _makeMessage
63 | * @param {String} id The unique id of the message recipient.
64 | * @param {String} messageType The type of message to send.
65 | * @param {String} message The message to send.
66 | */
67 | var _makeMessage = function(id, messageType, message) {
68 | var bits = ['pym', id, messageType, message];
69 |
70 | return bits.join(MESSAGE_DELIMITER);
71 | };
72 |
73 | /**
74 | * Construct a regex to validate and parse messages.
75 | *
76 | * @method _makeMessageRegex
77 | * @param {String} id The unique id of the message recipient.
78 | */
79 | var _makeMessageRegex = function(id) {
80 | var bits = ['pym', id, '(\\S+)', '(.+)'];
81 |
82 | return new RegExp('^' + bits.join(MESSAGE_DELIMITER) + '$');
83 | };
84 |
85 | /**
86 | * Initialize Pym for elements on page that have data-pym attributes.
87 | *
88 | * @method _autoInit
89 | */
90 | var _autoInit = function() {
91 | var elements = document.querySelectorAll(
92 | '[data-pym-src]:not([data-pym-auto-initialized])'
93 | );
94 |
95 | var length = elements.length;
96 |
97 | for (var idx = 0; idx < length; ++idx) {
98 | var element = elements[idx];
99 |
100 | /*
101 | * Mark automatically-initialized elements so they are not
102 | * re-initialized if the user includes pym.js more than once in the
103 | * same document.
104 | */
105 | element.setAttribute('data-pym-auto-initialized', '');
106 |
107 | // Ensure elements have an id
108 | if (element.id === '') {
109 | element.id = 'pym-' + idx;
110 | }
111 |
112 | var src = element.getAttribute('data-pym-src');
113 | var xdomain = element.getAttribute('data-pym-xdomain');
114 | var config = {};
115 |
116 | if (xdomain) {
117 | config.xdomain = xdomain;
118 | }
119 |
120 | new lib.Parent(element.id, src, config);
121 | }
122 | };
123 |
124 | /**
125 | * The Parent half of a response iframe.
126 | *
127 | * @class Parent
128 | * @param {String} id The id of the div into which the iframe will be rendered.
129 | * @param {String} url The url of the iframe source.
130 | * @param {Object} config Configuration to override the default settings.
131 | */
132 | lib.Parent = function(id, url, config) {
133 | this.id = id;
134 | this.url = url;
135 | this.el = document.getElementById(id);
136 | this.iframe = null;
137 |
138 | this.settings = {
139 | xdomain: '*'
140 | };
141 |
142 | this.messageRegex = _makeMessageRegex(this.id);
143 | this.messageHandlers = {};
144 |
145 | // ensure a config object
146 | config = (config || {});
147 |
148 | /**
149 | * Construct the iframe.
150 | *
151 | * @memberof Parent.prototype
152 | * @method _constructIframe
153 | */
154 | this._constructIframe = function() {
155 | // Calculate the width of this element.
156 | var width = this.el.offsetWidth.toString();
157 |
158 | // Create an iframe element attached to the document.
159 | this.iframe = document.createElement('iframe');
160 |
161 | // Save fragment id
162 | var hash = '';
163 | var hashIndex = this.url.indexOf('#');
164 |
165 | if (hashIndex > -1) {
166 | hash = this.url.substring(hashIndex, this.url.length);
167 | this.url = this.url.substring(0, hashIndex);
168 | }
169 |
170 | // If the URL contains querystring bits, use them.
171 | // Otherwise, just create a set of valid params.
172 | if (this.url.indexOf('?') < 0) {
173 | this.url += '?';
174 | } else {
175 | this.url += '&';
176 | }
177 |
178 | // Append the initial width as a querystring parameter, and the fragment id
179 | this.iframe.src = this.url +
180 | 'initialWidth=' + width +
181 | '&childId=' + this.id +
182 | '&parentUrl=' + encodeURIComponent(window.location.href) +
183 | hash;
184 |
185 | // Set some attributes to this proto-iframe.
186 | this.iframe.setAttribute('width', '100%');
187 | this.iframe.setAttribute('scrolling', 'no');
188 | this.iframe.setAttribute('marginheight', '0');
189 | this.iframe.setAttribute('frameborder', '0');
190 |
191 | if (this.settings.title) {
192 | this.iframe.setAttribute('title', this.settings.title);
193 | }
194 |
195 | // Append the iframe to our element.
196 | this.el.appendChild(this.iframe);
197 |
198 | // Add an event listener that will handle redrawing the child on resize.
199 | window.addEventListener('resize', this._onResize);
200 | };
201 |
202 | /**
203 | * Send width on resize.
204 | *
205 | * @memberof Parent.prototype
206 | * @method _onResize
207 | */
208 | this._onResize = function() {
209 | this.sendWidth();
210 | }.bind(this);
211 |
212 | /**
213 | * Fire all event handlers for a given message type.
214 | *
215 | * @memberof Parent.prototype
216 | * @method _fire
217 | * @param {String} messageType The type of message.
218 | * @param {String} message The message data.
219 | */
220 | this._fire = function(messageType, message) {
221 | if (messageType in this.messageHandlers) {
222 | for (var i = 0; i < this.messageHandlers[messageType].length; i++) {
223 | this.messageHandlers[messageType][i].call(this, message);
224 | }
225 | }
226 | };
227 |
228 | /**
229 | * Remove this parent from the page and unbind it's event handlers.
230 | *
231 | * @memberof Parent.prototype
232 | * @method remove
233 | */
234 | this.remove = function() {
235 | window.removeEventListener('message', this._processMessage);
236 | window.removeEventListener('resize', this._onResize);
237 |
238 | this.el.removeChild(this.iframe);
239 | };
240 |
241 | /**
242 | * @callback Parent~onMessageCallback
243 | * @param {String} message The message data.
244 | */
245 |
246 | /**
247 | * Process a new message from the child.
248 | *
249 | * @memberof Parent.prototype
250 | * @method _processMessage
251 | * @param {Event} e A message event.
252 | */
253 | this._processMessage = function(e) {
254 | // First, punt if this isn't from an acceptable xdomain.
255 | if (!_isSafeMessage(e, this.settings)) {
256 | return;
257 | }
258 |
259 | // Discard object messages, we only care about strings
260 | if (typeof e.data !== 'string') {
261 | return;
262 | }
263 |
264 | // Grab the message from the child and parse it.
265 | var match = e.data.match(this.messageRegex);
266 |
267 | // If there's no match or too many matches in the message, punt.
268 | if (!match || match.length !== 3) {
269 | return false;
270 | }
271 |
272 | var messageType = match[1];
273 | var message = match[2];
274 |
275 | this._fire(messageType, message);
276 | }.bind(this);
277 |
278 | /**
279 | * Resize iframe in response to new height message from child.
280 | *
281 | * @memberof Parent.prototype
282 | * @method _onHeightMessage
283 | * @param {String} message The new height.
284 | */
285 | this._onHeightMessage = function(message) {
286 | /*
287 | * Handle parent height message from child.
288 | */
289 | var height = parseInt(message);
290 |
291 | this.iframe.setAttribute('height', height + 'px');
292 | };
293 |
294 | /**
295 | * Navigate parent to a new url.
296 | *
297 | * @memberof Parent.prototype
298 | * @method _onNavigateToMessage
299 | * @param {String} message The url to navigate to.
300 | */
301 | this._onNavigateToMessage = function(message) {
302 | /*
303 | * Handle parent scroll message from child.
304 | */
305 | document.location.href = message;
306 | };
307 |
308 | /**
309 | * Bind a callback to a given messageType from the child.
310 | *
311 | * Reserved message names are: "height", "scrollTo" and "navigateTo".
312 | *
313 | * @memberof Parent.prototype
314 | * @method onMessage
315 | * @param {String} messageType The type of message being listened for.
316 | * @param {Parent~onMessageCallback} callback The callback to invoke when a message of the given type is received.
317 | */
318 | this.onMessage = function(messageType, callback) {
319 | if (!(messageType in this.messageHandlers)) {
320 | this.messageHandlers[messageType] = [];
321 | }
322 |
323 | this.messageHandlers[messageType].push(callback);
324 | };
325 |
326 | /**
327 | * Send a message to the the child.
328 | *
329 | * @memberof Parent.prototype
330 | * @method sendMessage
331 | * @param {String} messageType The type of message to send.
332 | * @param {String} message The message data to send.
333 | */
334 | this.sendMessage = function(messageType, message) {
335 | this.el.getElementsByTagName('iframe')[0].contentWindow.postMessage(_makeMessage(this.id, messageType, message), '*');
336 | };
337 |
338 | /**
339 | * Transmit the current iframe width to the child.
340 | *
341 | * You shouldn't need to call this directly.
342 | *
343 | * @memberof Parent.prototype
344 | * @method sendWidth
345 | */
346 | this.sendWidth = function() {
347 | var width = this.el.offsetWidth.toString();
348 |
349 | this.sendMessage('width', width);
350 | };
351 |
352 | // Add any overrides to settings coming from config.
353 | for (var key in config) {
354 | this.settings[key] = config[key];
355 | }
356 |
357 | // Bind required message handlers
358 | this.onMessage('height', this._onHeightMessage);
359 | this.onMessage('navigateTo', this._onNavigateToMessage);
360 |
361 | // Add a listener for processing messages from the child.
362 | window.addEventListener('message', this._processMessage, false);
363 |
364 | // Construct the iframe in the container element.
365 | this._constructIframe();
366 |
367 | return this;
368 | };
369 |
370 | /**
371 | * The Child half of a responsive iframe.
372 | *
373 | * @class Child
374 | * @param {Object} config Configuration to override the default settings.
375 | */
376 | lib.Child = function(config) {
377 | this.parentWidth = null;
378 | this.id = null;
379 | this.parentUrl = null;
380 |
381 | this.settings = {
382 | renderCallback: null,
383 | xdomain: '*',
384 | polling: 0
385 | };
386 |
387 | this.messageRegex = null;
388 | this.messageHandlers = {};
389 |
390 | // Ensure a config object
391 | config = (config || {});
392 |
393 | /**
394 | * Bind a callback to a given messageType from the child.
395 | *
396 | * Reserved message names are: "width".
397 | *
398 | * @memberof Child.prototype
399 | * @method onMessage
400 | * @param {String} messageType The type of message being listened for.
401 | * @param {Child~onMessageCallback} callback The callback to invoke when a message of the given type is received.
402 | */
403 | this.onMessage = function(messageType, callback) {
404 | if (!(messageType in this.messageHandlers)) {
405 | this.messageHandlers[messageType] = [];
406 | }
407 |
408 | this.messageHandlers[messageType].push(callback);
409 | };
410 |
411 | /**
412 | * @callback Child~onMessageCallback
413 | * @param {String} message The message data.
414 | */
415 |
416 | /**
417 | * Fire all event handlers for a given message type.
418 | *
419 | * @memberof Parent.prototype
420 | * @method _fire
421 | * @param {String} messageType The type of message.
422 | * @param {String} message The message data.
423 | */
424 | this._fire = function(messageType, message) {
425 | /*
426 | * Fire all event handlers for a given message type.
427 | */
428 | if (messageType in this.messageHandlers) {
429 | for (var i = 0; i < this.messageHandlers[messageType].length; i++) {
430 | this.messageHandlers[messageType][i].call(this, message);
431 | }
432 | }
433 | };
434 |
435 | /**
436 | * Process a new message from the parent.
437 | *
438 | * @memberof Child.prototype
439 | * @method _processMessage
440 | * @param {Event} e A message event.
441 | */
442 | this._processMessage = function(e) {
443 | /*
444 | * Process a new message from parent frame.
445 | */
446 | // First, punt if this isn't from an acceptable xdomain.
447 | if (!_isSafeMessage(e, this.settings)) {
448 | return;
449 | }
450 |
451 | // Discard object messages, we only care about strings
452 | if (typeof e.data !== 'string') {
453 | return;
454 | }
455 |
456 | // Get the message from the parent.
457 | var match = e.data.match(this.messageRegex);
458 |
459 | // If there's no match or it's a bad format, punt.
460 | if (!match || match.length !== 3) { return; }
461 |
462 | var messageType = match[1];
463 | var message = match[2];
464 |
465 | this._fire(messageType, message);
466 | }.bind(this);
467 |
468 | /**
469 | * Resize iframe in response to new width message from parent.
470 | *
471 | * @memberof Child.prototype
472 | * @method _onWidthMessage
473 | * @param {String} message The new width.
474 | */
475 | this._onWidthMessage = function(message) {
476 | /*
477 | * Handle width message from the child.
478 | */
479 | var width = parseInt(message);
480 |
481 | // Change the width if it's different.
482 | if (width !== this.parentWidth) {
483 | this.parentWidth = width;
484 |
485 | // Call the callback function if it exists.
486 | if (this.settings.renderCallback) {
487 | this.settings.renderCallback(width);
488 | }
489 |
490 | // Send the height back to the parent.
491 | this.sendHeight();
492 | }
493 | };
494 |
495 | /**
496 | * Send a message to the the Parent.
497 | *
498 | * @memberof Child.prototype
499 | * @method sendMessage
500 | * @param {String} messageType The type of message to send.
501 | * @param {String} message The message data to send.
502 | */
503 | this.sendMessage = function(messageType, message) {
504 | /*
505 | * Send a message to the parent.
506 | */
507 | window.parent.postMessage(_makeMessage(this.id, messageType, message), '*');
508 | };
509 |
510 | /**
511 | * Transmit the current iframe height to the parent.
512 | *
513 | * Call this directly in cases where you manually alter the height of the iframe contents.
514 | *
515 | * @memberof Child.prototype
516 | * @method sendHeight
517 | */
518 | this.sendHeight = function() {
519 | // Get the child's height.
520 | var height = document.getElementsByTagName('body')[0].offsetHeight.toString();
521 |
522 | // Send the height to the parent.
523 | this.sendMessage('height', height);
524 | }.bind(this);
525 | this.sendHeightToParent = function() {
526 | // Get the child's height.
527 | var height = document.getElementsByTagName('body')[0].offsetHeight.toString();
528 |
529 | // Send the height to the parent.
530 | this.sendMessage('height', height);
531 | }.bind(this);
532 |
533 | /**
534 | * Scroll parent to a given element id.
535 | *
536 | * @memberof Child.prototype
537 | * @method scrollParentTo
538 | * @param {String} hash The id of the element to scroll to.
539 | */
540 | this.scrollParentTo = function(hash) {
541 | this.sendMessage('navigateTo', '#' + hash);
542 | };
543 |
544 | /**
545 | * Navigate parent to a given url.
546 | *
547 | * @memberof Parent.prototype
548 | * @method navigateParentTo
549 | * @param {String} url The url to navigate to.
550 | */
551 | this.navigateParentTo = function(url) {
552 | this.sendMessage('navigateTo', url);
553 | };
554 |
555 | // Identify what ID the parent knows this child as.
556 | this.id = _getParameterByName('childId') || config.id;
557 | this.messageRegex = new RegExp('^pym' + MESSAGE_DELIMITER + this.id + MESSAGE_DELIMITER + '(\\S+)' + MESSAGE_DELIMITER + '(.+)$');
558 |
559 | // Get the initial width from a URL parameter.
560 | var width = parseInt(_getParameterByName('initialWidth'));
561 |
562 | // Get the url of the parent frame
563 | this.parentUrl = _getParameterByName('parentUrl');
564 |
565 | // Bind the required message handlers
566 | this.onMessage('width', this._onWidthMessage);
567 |
568 | // Initialize settings with overrides.
569 | for (var key in config) {
570 | this.settings[key] = config[key];
571 | }
572 |
573 | // Set up a listener to handle any incoming messages.
574 | window.addEventListener('message', this._processMessage, false);
575 |
576 | // If there's a callback function, call it.
577 | if (this.settings.renderCallback) {
578 | this.settings.renderCallback(width);
579 | }
580 |
581 | // Send the initial height to the parent.
582 | this.sendHeight();
583 |
584 | // If we're configured to poll, create a setInterval to handle that.
585 | if (this.settings.polling) {
586 | window.setInterval(this.sendHeight, this.settings.polling);
587 | }
588 |
589 | return this;
590 | };
591 |
592 | // Initialize elements with pym data attributes
593 | _autoInit();
594 |
595 | return lib;
596 | });
597 |
--------------------------------------------------------------------------------
/js/pym.v1.min.js:
--------------------------------------------------------------------------------
1 | /*! pym.js - v1.3.2 - 2018-02-13 */
2 |
3 | !function(a){"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&module.exports?module.exports=a():window.pym=a.call(this)}(function(){var a={},b=function(a){var b=document.createEvent("Event");b.initEvent("pym:"+a,!0,!0),document.dispatchEvent(b)},c=function(a){var b=new RegExp("[\\?&]"+a.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]")+"=([^]*)"),c=b.exec(location.search);return null===c?"":decodeURIComponent(c[1].replace(/\+/g," "))},d=function(a,b){if(("*"===b.xdomain||a.origin.match(new RegExp(b.xdomain+"$")))&&"string"==typeof a.data)return!0},e=function(a){var b=/^(?:(?:https?|mailto|ftp):|[^&:\/?#]*(?:[\/?#]|$))/gi;if(a.match(b))return!0},f=function(a,b,c){return["pym",a,b,c].join("xPYMx")},g=function(a){var b=["pym",a,"(\\S+)","(.*)"];return new RegExp("^"+b.join("xPYMx")+"$")},h=Date.now||function(){return(new Date).getTime()},i=function(a,b,c){var d,e,f,g=null,i=0;c||(c={});var j=function(){i=!1===c.leading?0:h(),g=null,f=a.apply(d,e),g||(d=e=null)};return function(){var k=h();i||!1!==c.leading||(i=k);var l=b-(k-i);return d=this,e=arguments,l<=0||l>b?(g&&(clearTimeout(g),g=null),i=k,f=a.apply(d,e),g||(d=e=null)):g||!1===c.trailing||(g=setTimeout(j,l)),f}},j=function(){for(var b=a.autoInitInstances.length,c=b-1;c>=0;c--){var d=a.autoInitInstances[c];d.el.getElementsByTagName("iframe").length&&d.el.getElementsByTagName("iframe")[0].contentWindow||a.autoInitInstances.splice(c,1)}};return a.autoInitInstances=[],a.autoInit=function(c){var d=document.querySelectorAll("[data-pym-src]:not([data-pym-auto-initialized])"),e=d.length;j();for(var f=0;f-1&&(b=this.url.substring(c,this.url.length),this.url=this.url.substring(0,c)),this.url.indexOf("?")<0?this.url+="?":this.url+="&",this.iframe.src=this.url+"initialWidth="+a+"&childId="+this.id,this.settings.optionalparams&&(this.iframe.src+="&parentTitle="+encodeURIComponent(document.title),this.iframe.src+="&"+this.settings.parenturlparam+"="+encodeURIComponent(this.settings.parenturlvalue)),this.iframe.src+=b,this.iframe.setAttribute("width","100%"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("marginheight","0"),this.iframe.setAttribute("frameborder","0"),this.settings.title&&this.iframe.setAttribute("title",this.settings.title),void 0!==this.settings.allowfullscreen&&!1!==this.settings.allowfullscreen&&this.iframe.setAttribute("allowfullscreen",""),void 0!==this.settings.sandbox&&"string"==typeof this.settings.sandbox&&this.iframe.setAttribute("sandbox",this.settings.sandbox),this.settings.id&&(document.getElementById(this.settings.id)||this.iframe.setAttribute("id",this.settings.id)),this.settings.name&&this.iframe.setAttribute("name",this.settings.name);this.el.firstChild;)this.el.removeChild(this.el.firstChild);this.el.appendChild(this.iframe),window.addEventListener("resize",this._onResize),this.settings.trackscroll&&window.addEventListener("scroll",this._throttleOnScroll)},this._onResize=function(){this.sendWidth(),this.settings.trackscroll&&this.sendViewportAndIFramePosition()}.bind(this),this._onScroll=function(){this.sendViewportAndIFramePosition()}.bind(this),this._fire=function(a,b){if(a in this.messageHandlers)for(var c=0;c
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/pym-shortcode.php:
--------------------------------------------------------------------------------
1 | Pym.js Embed Settings**, decide whether you'd like to change the plugin's behavior to use a non-default source URL for `Pym.js`, and whether you'd like to prevent post authors from setting embed-specific URLs for `Pym.js`
25 | 4. Begin embedding content!
26 |
27 | == Frequently Asked Questions ==
28 |
29 | For answers to frequently asked questions, [see this plugin's documentation on GitHub](https://github.com/INN/pym-shortcode/tree/master/docs).
30 |
31 | For support resources, [see this plugin's documentation on GitHub](https://github.com/INN/pym-shortcode/tree/master/docs).
32 |
33 | == Screenshots ==
34 |
35 | Embeddable table from NPR:
36 |
37 | 
38 |
39 | Pym.js Shortcode in a WordPress post:
40 |
41 | 
42 |
43 | Desktop view of the WordPress post with the NPR embed using Pym.js Shortcode:
44 |
45 | 
46 |
47 | Mobile view of the WordPress post with the NPR embed using Pym.js Shortcode:
48 |
49 | 
50 |
51 | == Changelog ==
52 |
53 | = 1.3.2.4 =
54 |
55 | - Now tested up to WordPress 5.4 and Gutenberg 7.8.
56 | - Fixes a presentational error in the Pym.js Embeds Block's block inspector control within the editor. PR [#74](https://github.com/INN/pym-shortcode/pull/74) for issue [#72](https://github.com/INN/pym-shortcode/issues/72).
57 |
58 | = 1.3.2.3 =
59 |
60 | New features:
61 |
62 | - Adds compatibility with [the official WordPress AMP Plugin](https://wordpress.org/plugins/amp/). On AMP endpoints, markup for Pym.js-based embeds is converted to `amp-iframe` tags. If you're not using the AMP Plugin, your site won't be affected. And if you're not viewing a page on an AMP endpoint, the page won't be affected. PR [#62](https://github.com/INN/pym-shortcode/pull/62) by Claudiu Lodromanean, [originally for Automattic's Newspack](https://github.com/Automattic/newspack-plugin/pull/276).
63 |
64 | Other updates:
65 |
66 | - Adds credit to GitHub user [eidietrich](https://github.com/eidietrich) for [PR #55](https://github.com/INN/pym-shortcode/pull/55) in the 1.3.2.2 release notes.
67 | - Fixes a 'nwesroom' typo. [PR #66](https://github.com/INN/pym-shortcode/pull/66) for issue [#65](https://github.com/INN/pym-shortcode/issues/65).
68 |
69 | = 1.3.2.2 =
70 |
71 | - Plugin is now tested against WordPress 5.0 beta 3.
72 | - Adds support for WordPress 5.0.
73 | - Fixes bug where the Pym.js Embeds block did not work in WordPress 5.0. [PR #58](https://github.com/INN/pym-shortcode/pulls/58) for [issue #57](https://github.com/INN/pym-shortcode/issues/57).
74 | - Adds advice for where to host graphics files. [PR #55](https://github.com/INN/pym-shortcode/pull/55) from Github user [eidietrich](https://github.com/eidietrich).
75 |
76 | = 1.3.2.1 =
77 |
78 | **This is a major update! Please read the release notes.**
79 |
80 | Following the practice begun at plugin version 1.1.2 of [having the plugin version number match the version number of the bundled copy of `Pym.js`](https://github.com/INN/pym-shortcode/blob/master/docs/maintainer-notes.md), the first three numbers in this plugin's version do not change with this release because the `Pym.js` version has not changed. We've tacked a `.1` on to the end to denote this release. Please read the release notes and test your site as appropriate before upgrading in production.
81 |
82 | We wish to thank all who helped us test [the release candidate](https://github.com/INN/pym-shortcode/releases/tag/v1.3.2.1-rc1) for this version, including Mike Janssen at [Current.org](https://current.org/) and Alyson Hurt at the NPR Visuals Team.
83 |
84 | New features:
85 |
86 | * Plugin renamed from "Pym Shortcode" to "Pym.js Embeds".
87 | * Adds a "Pym.js Embed" block for use in Gutenberg. [PR #34](https://github.com/INN/pym-shortcode/pull/34) for issue [#28](https://github.com/INN/pym-shortcode/issues/28).
88 | * If a block is created using this plugin and Gutenberg, and Gutenberg is then disabled, the block will show a link to the embedded graphic.
89 | * Through the settings page, you can now serve Pym.js using your newsroom's CDN or NPR's CDN! [PR #45]() for [issue #31](https://github.com/INN/pym-shortcode/issues/31).
90 | * Adds a settings page, available to [those users with the `manage_options` capability](https://codex.wordpress.org/Roles_and_Capabilities#Capability_vs._Role_Table), with the following options:
91 | * Change the default pymsrc URL. [PR #45](https://github.com/INN/pym-shortcode/pull/45) for [issue #8](https://github.com/INN/pym-shortcode/issues/8).
92 | * Override block and shortcode pymsrc URLs with the default pymsrc URL. [PR #45](https://github.com/INN/pym-shortcode/pull/45) for [issue #8](https://github.com/INN/pym-shortcode/issues/8).
93 | * Adds an informational page, available to all who can make posts, that lists the plugin's default source URL for `Pym.js`. This is to make the process of building new interactives easier.
94 | * Shortcode now gains an explicit `align=""` parameter, so that WordPress's generated [alignment CSS classes](https://codex.wordpress.org/CSS#WordPress_Generated_Classes) can be used on embeds. By enabling this in the shortcode, the Gutenberg Block also gains support for alignment. [PR #34](https://github.com/INN/pym-shortcode/pull/34). Prior to this release, the alignment classes could be added via the `class=""` parameter.
95 | * Script tags for embeds are no longer output by `the_content()`, instead being output during `wp_footer()` by [closures](https://secure.php.net/manual/en/functions.anonymous.php) hooked on the `'wp_footer'` action. [PR #34](https://github.com/INN/pym-shortcode/pull/34) for issues [#33](https://github.com/INN/pym-shortcode/issues/33) and [#35](https://github.com/INN/pym-shortcode/issues/35).
96 | * The script tag used to run `new pym.Parent` is now configurable. By replacing the [pluggable function](https://codex.wordpress.org/Pluggable_Functions) `pym_shortcode_script_footer_enqueue()` with your own function, you can now use alternate forms of embed code that may be required for PJAX sites or custom versions of Pym.js. This resolves issue [#19](https://github.com/INN/pym-shortcode/issues/19).
97 | * Adds "Requires PHP: 5.3" metadata to the plugin's `readme.txt`, since we're now using PHP namespaces for some code.
98 | * Adds documentation for how to test the plugin:
99 | * tests to run before enabling the "override pymsrc" option in production
100 | * tests to run for site compatibility with Gutenberg
101 |
102 | Changes:
103 |
104 | * The source URL for `pymjs`, known as the pymsrc URL, is now passed through [wp_http_validate_url](https://developer.wordpress.org/reference/functions/wp_http_validate_url/). [PR #45](https://github.com/INN/pym-shortcode/pull/45) for [issue #8](https://github.com/INN/pym-shortcode/issues/8).
105 | * The source URL for `pym.js` is no longer output by `the_content()`, instead being output during `wp_footer` by an action dedicated to the task. If different shortcodes and/or blocks on the page specify different source URLs for Pym.js, all are output (after removing duplicates), but a message is logged in the browser console. If `WP_DEBUG` is set, this message is also logged to the server log, with the post ID specified. [PR #34](https://github.com/INN/pym-shortcode/pull/34) for issues [#33](https://github.com/INN/pym-shortcode/issues/33) and [#35](https://github.com/INN/pym-shortcode/issues/35). See https://github.com/INN/pym-shortcode/tree/master/docs#ive-set-a-different-pymsrc-option-but-now-im-seeing-a-message-in-the-console
106 | * `docs/updating-pym.md` becomes `docs/maintainer-notes.md`
107 | * Script tags for embeds are no longer output by `the_content()`, instead being output during `wp_footer()` by [closures](https://secure.php.net/manual/en/functions.anonymous.php) hooked on the `'wp_footer'` action. [PR #34](https://github.com/INN/pym-shortcode/pull/34) for issues [#33](https://github.com/INN/pym-shortcode/issues/33) and [#35](https://github.com/INN/pym-shortcode/issues/35).
108 |
109 | Removed:
110 |
111 | * Script tags for embeds are no longer output by `the_content()`, instead being output during `wp_footer()` by [closures](https://secure.php.net/manual/en/functions.anonymous.php) hooked on the `'wp_footer'` action. [PR #34](https://github.com/INN/pym-shortcode/pull/34) for issues [#33](https://github.com/INN/pym-shortcode/issues/33) and [#35](https://github.com/INN/pym-shortcode/issues/35).
112 |
113 | = 1.3.2 =
114 |
115 | * *RECOMMENDED UPDATE* : Pym.js users, NPR has released an update that closes a potential security hole. We recommend everyone update to 1.3.2.
116 | * Update to Pym.js version 1.3.2: https://github.com/nprapps/pym.js/releases/tag/v1.3.2 (Changelog at https://github.com/nprapps/pym.js/blob/v1.3.2/CHANGELOG)
117 |
118 | = 1.3.1 =
119 |
120 | * Update to Pym.js version 1.3.1: https://github.com/nprapps/pym.js/releases/tag/v1.3.1 (Changelog at https://github.com/nprapps/pym.js/blob/v1.3.1/CHANGELOG)
121 | * (we skipped pym.js version 1.3.0: https://github.com/nprapps/pym.js/releases/tag/v1.3.0)
122 |
123 | = 1.2.2 =
124 |
125 | * Update to Pym.js version 1.2.2: https://github.com/nprapps/pym.js/releases/tag/v1.2.2 (Changelog at https://github.com/nprapps/pym.js/blob/master/CHANGELOG )
126 | * (we skipped Pym.js version 1.2.1: https://github.com/nprapps/pym.js/releases/tag/v1.2.1 )
127 | * Add `id=""` attribute to allow setting custom IDs on embeds. [#21](https://github.com/INN/pym-shortcode/issues/21)
128 | * Add `class=""` attribute to allow setting custom classes on embeds. [#22](https://github.com/INN/pym-shortcode/issues/22) and [#23](https://github.com/INN/pym-shortcode/issues/23).
129 | * Add a default class name `pym` to all embed-containing div elements output by this plugin, and a filter 'pym_shortcode_default_class' to allow changing it.
130 |
131 | = 1.2.0.2 =
132 |
133 | * Fix encoding error on pym.v1.min.js, [thanks to lchheng](https://github.com/INN/pym-shortcode/pull/18)
134 |
135 | = 1.2.0.1 =
136 |
137 | * Add attribution for lchheng's [pymsrc fix](https://github.com/INN/pym-shortcode/pull/17).
138 |
139 | = 1.2.0 =
140 |
141 | * Update to Pym.js version 1.2.0: https://github.com/nprapps/pym.js/releases/tag/v1.2.0 (Changelog at https://github.com/nprapps/pym.js/blob/v1.2.0/CHANGELOG )
142 | * Fixes a bug where the `pymsrc` attribute might have been ignored, for real this time. [Thanks, lchheng!](https://github.com/INN/pym-shortcode/pull/17)
143 |
144 | = 1.1.2 =
145 |
146 | * Update to Pym.js version 1.1.2: https://github.com/nprapps/pym.js/releases/tag/v1.1.2
147 | * Switch the new default url of `Pym.js` in this plugin to `js/pym.v1.min.js`, leaving the existing `js/pym.js` where it is.
148 | * Provide additional notes in [the documentation](https://github.com/INN/pym-shortcode/tree/master/docs) for maintainers on updating `Pym.js` in this plugin
149 | * Fixes a bug where the `pymsrc` attribute might have been ignored
150 | * Fixes and corrections to documentation.
151 |
152 | = 1.0 =
153 |
154 | * First release of the plugin
155 |
156 | == Pym.js Resources from NPR ==
157 |
158 | You may also want to look at NPR's Pym.js resources:
159 |
160 | * [Pym.js homepage](http://blog.apps.npr.org/pym.js/)
161 | * [Pym.js repo on GutHub/nprapps](https://github.com/nprapps/pym.js/)
162 |
--------------------------------------------------------------------------------
/release.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # This file is a WordPress.org plugin release script.
4 | # For more about how it works, see the documentation at
5 | # https://github.com/INN/docs/blob/master/projects/wordpress-plugins/release.sh.md
6 | #
7 | RELEASE_DIR=release;
8 | SVN_PATH=$RELEASE_DIR/svn;
9 | SVN_REPO="https://plugins.svn.wordpress.org/pym-shortcode/";
10 | BLACKLIST=(
11 | .\*
12 | release.sh
13 | mkdocs.yml
14 | phpunit.xml
15 | requirements.txt
16 | composer.lock
17 | composer.json
18 | package.json
19 | bower.json
20 | Gruntfile.js
21 | release/\*
22 | tests/\*
23 | node_modules/\*
24 | ./\*\*/.\*
25 | );
26 |
27 | function ensure_release_dir() {
28 | local release_dir="$1";
29 | if [[ ! -d $release_dir ]]
30 | then
31 | echo "Creating $release_dir directory";
32 | mkdir -p $release_dir;
33 | fi
34 | }
35 |
36 | function is_master_branch() {
37 | local remotes=`git ls-remote --quiet`;
38 | local current=`get_current_git_commit`;
39 | local is_master=`echo "$remotes" | grep "refs/heads/master" | grep $current | awk '{print $2;}'`;
40 | echo "$is_master";
41 | }
42 |
43 | function is_git_tag() {
44 | local remotes=`git ls-remote --quiet`;
45 | local current=`get_current_git_commit`;
46 | local is_tag=`echo "$remotes" | grep "refs/tags" | grep $current | awk '{print $2;}' | sed -e 's/^refs\/tags\///;s/\^{}$//'`;
47 | echo "$is_tag";
48 | }
49 |
50 | function get_current_git_commit() {
51 | echo `git rev-parse HEAD`;
52 | }
53 |
54 | function check_repo_state() {
55 | #
56 | # Check the state of this git repo. If no tag is checked out
57 | # and we're not on the master branch, bail.
58 | #
59 | local is_master=`is_master_branch`;
60 | local is_tag=`is_git_tag`;
61 |
62 | if [[ "$is_master" == "" && "$is_tag" == "" ]]
63 | then
64 | echo "Bad release state for git repo!";
65 | echo "Make sure you've checked out a tag or the master branch before releasing.";
66 | exit 1;
67 | else
68 | echo "Repository is ready for deployment...";
69 | fi
70 | }
71 |
72 | function confirm_deployment() {
73 | # make sure we know what we're doing
74 | local is_master=`is_master_branch`;
75 | local is_tag=`is_git_tag`;
76 | local which_text="[master] and [$is_tag]";
77 |
78 | if [[ $is_master == "" ]]
79 | then
80 | which_text="[$is_tag]";
81 | fi
82 |
83 | if [[ $is_tag == "" ]]
84 | then
85 | which_text="[master]";
86 | fi
87 |
88 | read -p "Really release plugin from $which_text? [y/N] " -r;
89 | if [[ ! $REPLY =~ ^[Yy]$ ]]
90 | then
91 | echo "No changes made. Exiting...";
92 | exit 0;
93 | fi
94 | }
95 |
96 | function init_update_svn_repo() {
97 | local svn_path="$1";
98 | local svn_repo="$2";
99 |
100 | if [[ "$svn_path" == "" || "$svn_repo" == "" ]]
101 | then
102 | echo "The svn_path and svn_repo arguments are required.";
103 | exit 1;
104 | fi
105 |
106 | # init and update svn repo
107 | if [[ ! -d $svn_path ]]
108 | then
109 | echo " - checking out svn repo";
110 | OUT=`mkdir -p $svn_path && svn checkout $svn_repo $svn_path`
111 | if [[ $? -ne 0 ]]
112 | then
113 | echo "$OUT";
114 | exit 1;
115 | fi
116 | else
117 | echo " - updating svn repo";
118 | OUT=`cd $svn_path && svn update`;
119 | if [[ $? -ne 0 ]]
120 | then
121 | echo "$OUT";
122 | exit 1;
123 | fi
124 | fi
125 | }
126 |
127 | function create_release_zip() {
128 | # Build a release zip file
129 | echo "Creating release/wp-release.zip";
130 |
131 | OUT=`rm -f release/wp-release.zip`;
132 | OUT=`zip -x "${BLACKLIST[@]}" -q -r release/wp-release.zip .`;
133 |
134 | if [[ $? -ne 0 ]]
135 | then
136 | echo "$OUT";
137 | exit 1;
138 | fi
139 | }
140 |
141 | function install_update_dependencies() {
142 | echo "Checking for third-party/vendor dependencies...";
143 | # If composer.json exists, run composer install
144 | if [[ -f composer.json ]]
145 | then
146 | echo " - installing composer dependencies";
147 | composer install --no-dev;
148 | fi
149 |
150 | # If package.json exists, run npm install
151 | if [[ -f package.json ]]
152 | then
153 | echo " - installing npm dependencies";
154 | npm install;
155 | fi
156 |
157 | # If bower.json exists, run bower install
158 | if [[ -f bower.json ]]
159 | then
160 | echo " - installing bower dependencies";
161 | bower install;
162 | fi
163 | }
164 |
165 | function write_trunk() {
166 | local svn_path="$1";
167 | local is_master=`is_master_branch`;
168 |
169 | if [[ $is_master != "" ]]
170 | then
171 | trunk_path=$svn_path/trunk;
172 | echo "Writing to $trunk_path";
173 |
174 | # overwrite with unzip
175 | OUT=`rm -rf $trunk_path && unzip -o release/wp-release.zip -d $trunk_path`;
176 | if [[ $? -ne 0 ]]
177 | then
178 | echo "$OUT";
179 | exit 1;
180 | fi;
181 |
182 | # stage all changes (adds and removes)
183 | OUT=`cd $trunk_path && svn st | grep '^\?' | awk '{print \$2}' | xargs svn add` # add all
184 | if [[ $? -ne 0 ]]
185 | then
186 | echo "$OUT";
187 | exit 1;
188 | fi
189 |
190 | OUT=`cd $trunk_path && svn st | grep '^\!' | awk '{print \$2}' | xargs svn rm` # remove all
191 | if [[ $? -ne 0 ]]
192 | then
193 | echo "$OUT";
194 | exit 1;
195 | fi
196 |
197 | # make sure something has changed besides the autoloader hashes
198 | CHANGES=`cd $trunk_path && svn st | grep -v 'autoload\(_real\)\?\.php'`
199 | if [[ $CHANGES == "" ]]
200 | then
201 | echo "There are no changes to commit for trunk.";
202 | OUT=`cd $trunk_path && svn revert --recursive .`;
203 | if [[ $? -ne 0 ]]
204 | then
205 | echo "$OUT";
206 | exit 1;
207 | fi
208 | else
209 | echo "Committing $trunk_path (slow) ...";
210 | CURRENT=`get_current_git_commit`;
211 | OUT=`cd $trunk_path && svn commit -m "update trunk to git $CURRENT"`;
212 | if [[ $? -ne 0 ]]
213 | then
214 | echo "$OUT";
215 | exit 1;
216 | fi
217 | fi
218 | fi
219 | }
220 |
221 | function write_tag() {
222 | local svn_path="$1";
223 | local is_tag=`is_git_tag`;
224 |
225 | if [[ $is_tag != "" ]]
226 | then
227 | WP_TAG=`echo $is_tag | sed -e 's/^v//'`;
228 | TAG_PATH=$svn_path/tags/$WP_TAG;
229 | echo "Writing to $TAG_PATH";
230 |
231 | # overwrite with unzip
232 | OUT=`rm -rf $TAG_PATH && unzip -o release/wp-release.zip -d $TAG_PATH`;
233 | if [[ $? -ne 0 ]]
234 | then
235 | echo "$OUT";
236 | exit 1;
237 | fi
238 |
239 | # TODO: set version numbers and/or ensure version numbers in plugin files are correct
240 |
241 | # stage all changes (adds and removes)
242 | OUT=`cd $svn_path/tags && svn st | grep '^\?' | awk '{print \$2}' | xargs svn add`; # add all
243 | if [[ $? -ne 0 ]]
244 | then
245 | echo "$OUT";
246 | exit 1;
247 | fi
248 | OUT=`cd $svn_path/tags && svn st | grep '^\!' | awk '{print \$2}' | xargs svn rm`; # remove all
249 | if [[ $? -ne 0 ]]
250 | then
251 | echo "$OUT";
252 | exit 1;
253 | fi
254 |
255 | # make sure something has changed besides the autoloader hashes
256 | CHANGES=`cd $svn_path/tags && svn st | grep -v 'autoload\(_real\)\?\.php'`;
257 | if [[ $CHANGES == "" ]]
258 | then
259 | echo "There are no changes to commit for $TAG_PATH";
260 | OUT=`cd $svn_path/tags && svn revert --recursive .`;
261 | if [[ $? -ne 0 ]]
262 | then
263 | echo "$OUT";
264 | exit 1;
265 | fi
266 | else
267 | echo "Committing $TAG_PATH (slow) ...";
268 | CURRENT=`get_current_git_commit`;
269 | OUT=`cd $svn_path/tags && svn commit -m "update $WP_TAG to git $CURRENT"`;
270 | if [[ $? -ne 0 ]]
271 | then
272 | echo "$OUT";
273 | exit 1;
274 | fi
275 | fi
276 | fi
277 | }
278 |
279 | function help_text() {
280 | echo "Usage: ./release.sh [--dry_run, --help]";
281 | echo "";
282 | echo "--dry_run: Create the release directory and release zip,
283 | but bon't actually commit to the SVN repository."
284 | echo "";
285 | echo "--help: Display this help screen and exit.";
286 | echo "";
287 | echo "For more information about this script, see:
288 | https://github.com/INN/docs/blob/master/projects/wordpress-plugins/release.sh.md";
289 | echo "";
290 | exit 0;
291 | }
292 |
293 | # Parse args
294 | if [[ $@ =~ "help" || $@ =~ "--help" ]]
295 | then
296 | help_text;
297 | fi
298 |
299 | if [[ $@ =~ "dry_run" || $@ =~ "--dry_run" ]]
300 | then
301 | dry_run=1;
302 | else
303 | dry_run=0;
304 | fi
305 |
306 | # Start the release process
307 | ensure_release_dir "$RELEASE_DIR";
308 | check_repo_state;
309 | confirm_deployment;
310 |
311 | if [[ $dry_run == 0 ]]
312 | then
313 | init_update_svn_repo "$SVN_PATH" "$SVN_REPO";
314 | fi
315 |
316 | install_update_dependencies;
317 | create_release_zip;
318 |
319 | if [[ $dry_run == 0 ]]
320 | then
321 | write_trunk "$SVN_PATH";
322 | write_tag "$SVN_PATH";
323 | fi
324 |
325 | echo "Release process finished."
326 |
--------------------------------------------------------------------------------