@missing-elements/h5p-embed 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -90
- package/bin/h5p-embed.mjs +4 -4
- package/lib/build.mjs +10 -6
- package/package.json +4 -4
- package/site/embed.js +14 -17
package/README.md
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
# @missing-elements/h5p-embed
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
a
|
|
5
|
-
with signed-in users — embeds a package with an iframe and one script line. No H5P server, no
|
|
6
|
-
backend, and no third-party service between your learners and your content.
|
|
3
|
+
Writes the H5P embed page as a static site for a player domain of your own. Any site then embeds
|
|
4
|
+
a package with an iframe and one script line. No H5P server, no backend, no third-party service.
|
|
7
5
|
|
|
8
6
|
```bash
|
|
9
|
-
npx @missing-elements/h5p-embed h5p-player
|
|
7
|
+
npx @missing-elements/h5p-embed h5p-player # the folder defaults to ./h5p-player
|
|
10
8
|
```
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
Wrote h5p-player/ (11.1 MB): the embed page, the player, the H5P runtime, the library pack.
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Deploy the folder to any static host, then embed:
|
|
10
|
+
Deploy it to any static host, then embed:
|
|
17
11
|
|
|
18
12
|
```html
|
|
19
13
|
<iframe src="https://h5p-player.example.net/?src=https://cdn.example.org/course.h5p"
|
|
@@ -21,35 +15,28 @@ Deploy the folder to any static host, then embed:
|
|
|
21
15
|
<script src="https://h5p-player.example.net/resizer.js"></script>
|
|
22
16
|
```
|
|
23
17
|
|
|
24
|
-
The script
|
|
25
|
-
|
|
18
|
+
The script sizes the iframe to the content; a page that already loads h5p.org's `h5p-resizer.js`
|
|
19
|
+
needs no second one.
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
domain that holds nothing else, a package cannot reach your site's page, cookies, storage or APIs.
|
|
31
|
-
Use a separate registrable domain — `h5p-player.example.net`, not `h5p.example.com` — because a
|
|
32
|
-
subdomain is the same site and receives cookies set for `.example.com`. Put no accounts, no
|
|
33
|
-
cookies and nothing else on it.
|
|
21
|
+
**Use a separate registrable domain** — `h5p-player.example.net`, not `h5p.example.com` — and put
|
|
22
|
+
nothing else on it: no accounts, no cookies. A package is JavaScript and runs as that origin, and
|
|
23
|
+
a subdomain shares your site's cookies.
|
|
34
24
|
|
|
35
25
|
## Options
|
|
36
26
|
|
|
37
27
|
| Option | Effect |
|
|
38
28
|
|---|---|
|
|
39
|
-
| `--packages <origins>` | Play packages
|
|
40
|
-
| `--ancestors <origins>` | Only these sites may frame the page
|
|
41
|
-
| `--default-libraries <sources>` | The `libraries` value for addresses that name none
|
|
42
|
-
| `--no-libraries` | Leave out the 9.
|
|
43
|
-
| `--force` | Write into a folder
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
list fails as an ordinary network error, with nothing naming the list as the cause.
|
|
51
|
-
|
|
52
|
-
A player domain for one organisation is best locked to its own hosts:
|
|
29
|
+
| `--packages <origins>` | Play packages and fetch library bundles only from these origins (the site's own is always allowed). Add `https://cdn.jsdelivr.net` for `libraries=pack` on a site built with `--no-libraries`. Default: any https host |
|
|
30
|
+
| `--ancestors <origins>` | Only these sites may frame the page (`frame-ancestors`, header only). Default: any site |
|
|
31
|
+
| `--default-libraries <sources>` | The `libraries` value for addresses that name none: `pack` or URLs. Checked against `--packages`. Default: none, so exports without libraries are refused unless the address asks |
|
|
32
|
+
| `--no-libraries` | Leave out the 9.8 MB library pack; `libraries=pack` then fetches it from jsDelivr, where allowed |
|
|
33
|
+
| `--force` | Write into a non-empty folder, replacing only this tool's files |
|
|
34
|
+
| `-h`, `--help` / `-v`, `--version` | |
|
|
35
|
+
|
|
36
|
+
Origins are `https://host.example`, comma or space separated, no path or trailing slash;
|
|
37
|
+
`http://localhost` is accepted for local trials. **List every host a package URL redirects
|
|
38
|
+
through:** the browser's policy applies to each hop, and a redirect off the list fails as a plain
|
|
39
|
+
network error.
|
|
53
40
|
|
|
54
41
|
```bash
|
|
55
42
|
npx @missing-elements/h5p-embed h5p-player \
|
|
@@ -62,40 +49,31 @@ npx @missing-elements/h5p-embed h5p-player \
|
|
|
62
49
|
| Host | Policy and caching |
|
|
63
50
|
|---|---|
|
|
64
51
|
| Netlify, Cloudflare Pages | `_headers`, written beside the page |
|
|
65
|
-
| Vercel | `vercel.json`, written beside the page
|
|
66
|
-
| GitHub Pages, S3, nginx,
|
|
52
|
+
| Vercel | `vercel.json`, written beside the page |
|
|
53
|
+
| GitHub Pages, S3, nginx, others | a `<meta>` policy in the page; `--ancestors` needs a `Content-Security-Policy: frame-ancestors …` header set on the server |
|
|
67
54
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
over https; a frame in a page on plain http has no Service Worker.
|
|
71
|
-
|
|
72
|
-
`h5p-sw.js` should be served with `Cache-Control: no-cache`, as the header files say, so an update
|
|
73
|
-
reaches learners on their next visit. To update, run the command again with `--force` and
|
|
74
|
-
redeploy.
|
|
55
|
+
It works at a domain's root or under a path, over https. Serve `h5p-sw.js` with
|
|
56
|
+
`Cache-Control: no-cache`. To update, run the command again with `--force` and redeploy.
|
|
75
57
|
|
|
76
58
|
## The address
|
|
77
59
|
|
|
78
60
|
| Parameter | Effect |
|
|
79
61
|
|---|---|
|
|
80
|
-
| `src=<url>` | The package, required. Encode `&`, `#`, `+`, `%` and spaces
|
|
81
|
-
| `libraries=pack`,
|
|
82
|
-
| `frame`, `copyright`, `export`, `icon`, `reporting` | H5P's action bar
|
|
62
|
+
| `src=<url>` | The package, required. Encode `&`, `#`, `+`, `%` and spaces |
|
|
63
|
+
| `libraries=pack`, `<url>` or `none` | Libraries for an export without them; several are tried in order. `pack` is this domain's copy, or jsDelivr's without one; `hub` means `pack`; `none` turns off `--default-libraries` |
|
|
64
|
+
| `frame`, `copyright`, `export`, `icon`, `reporting` | H5P's action bar and its buttons |
|
|
83
65
|
| `fullscreen=off` | No fullscreen button |
|
|
84
66
|
| `preload=auto` | Start fetching media at once |
|
|
85
|
-
| `activity-id=<IRI>` | The object id
|
|
86
|
-
| `custom-css=<url>` | A stylesheet
|
|
87
|
-
| `xapi=<origin>` | Relay statements to the embedding page
|
|
88
|
-
|
|
89
|
-
Not available on the address: a custom script, and a learner's name. A script is a capability on
|
|
90
|
-
the player's origin that a link should not hand out, and a name has no place in a URL.
|
|
67
|
+
| `activity-id=<IRI>` | The xAPI object id, instead of the package URL |
|
|
68
|
+
| `custom-css=<url>` | A stylesheet loaded into the content |
|
|
69
|
+
| `xapi=<origin>` | Relay statements to the embedding page |
|
|
91
70
|
|
|
92
|
-
|
|
93
|
-
|
|
71
|
+
A custom script and a learner's name cannot be set on the address. Opened unframed, the page asks
|
|
72
|
+
before playing a package from another origin unless `--packages` names it.
|
|
94
73
|
|
|
95
|
-
##
|
|
74
|
+
## Messages
|
|
96
75
|
|
|
97
|
-
|
|
98
|
-
and no other:
|
|
76
|
+
With `&xapi=<embedding page's origin>` the frame posts statements to that origin only:
|
|
99
77
|
|
|
100
78
|
```js
|
|
101
79
|
const frame = document.querySelector('iframe')
|
|
@@ -107,48 +85,34 @@ addEventListener('message', ({ source, origin, data }) => {
|
|
|
107
85
|
})
|
|
108
86
|
```
|
|
109
87
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
carries `context.revision`, a fingerprint of the package build.
|
|
88
|
+
A package can post any statement, so treat relayed results as the learner's report, not proof
|
|
89
|
+
for a grade. Each carries `context.revision`, the package build's fingerprint.
|
|
113
90
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
Besides the heights and the relayed statements, the frame posts two messages to its parent,
|
|
117
|
-
whatever `xapi=` says. Neither carries anything the embedding page did not hand over itself.
|
|
91
|
+
Whatever `xapi=` says, the frame also posts to its parent:
|
|
118
92
|
|
|
119
93
|
```js
|
|
120
|
-
// Once, when the content is up: what the player learnt about the package.
|
|
121
94
|
{ context: 'h5p-offline-player', action: 'report',
|
|
122
|
-
source, // { type: 'range-http' | 'chunked' | 'file', size }
|
|
123
|
-
metadata, // { title, license, licenseVersion, authors
|
|
124
|
-
libraryBundle, // { url, origin, fromCache }
|
|
125
|
-
elapsedMs }
|
|
126
|
-
|
|
127
|
-
// Instead, when the load fails, or the page refuses the address (code: 'refused', or 'no-src').
|
|
128
|
-
{ context: 'h5p-offline-player', action: 'error', code, message }
|
|
95
|
+
source, // { type: 'range-http' | 'chunked' | 'file', size }
|
|
96
|
+
metadata, // { title, license, licenseVersion, authors, mainLibrary }, from h5p.json; show as text
|
|
97
|
+
libraryBundle, // { url, origin, fromCache }, or null
|
|
98
|
+
elapsedMs }
|
|
99
|
+
{ context: 'h5p-offline-player', action: 'error', code, message } // code 'refused', 'no-src' or the load's
|
|
129
100
|
```
|
|
130
101
|
|
|
131
|
-
The strings in `metadata` are the package's own; show them as text.
|
|
132
|
-
|
|
133
102
|
## As a library
|
|
134
103
|
|
|
135
104
|
```js
|
|
136
105
|
import { buildSite } from '@missing-elements/h5p-embed'
|
|
137
|
-
|
|
138
106
|
await buildSite({ out: 'public/player', packages: ['https://cdn.example.org'] })
|
|
139
107
|
```
|
|
140
108
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
| `packages` | The origins packages may come from, besides the page's own; `null` for any |
|
|
149
|
-
| `defaultLibraries` | The `libraries` value for addresses that name none |
|
|
150
|
-
| `runtime` | The `runtime` export of `@missing-elements/h5p-runtime`, for a bundled element |
|
|
151
|
-
| `askInOwnFrame` | `false` to skip the click a package from elsewhere otherwise waits for when a page of the same origin frames this one — for a site whose own preview frames it for a package the visitor just chose. Default `true` |
|
|
109
|
+
`buildSite` takes `out`, `libraries` (default `true`), `packages`, `ancestors`, `defaultLibraries`
|
|
110
|
+
and `force`, as the flags. `@missing-elements/h5p-embed/embed.js` exports `startEmbed()`, the
|
|
111
|
+
page's script, which reads the page's `<h5p-player>`, `#loader` and `#notice` (see
|
|
112
|
+
`site/index.html` and `site/embed.css`). Its options: `librariesPack` (the URL `libraries=pack`
|
|
113
|
+
names), `packages` (allowed origins, `null` for any), `defaultLibraries`, `runtime` (the
|
|
114
|
+
`@missing-elements/h5p-runtime` export, for a bundled element) and `askInOwnFrame` (default
|
|
115
|
+
`true`; `false` skips the click when a same-origin page frames it).
|
|
152
116
|
|
|
153
117
|
```js
|
|
154
118
|
import '@missing-elements/h5p-offline-player'
|
|
@@ -160,10 +124,18 @@ import { startEmbed } from '@missing-elements/h5p-embed/embed.js'
|
|
|
160
124
|
startEmbed({ runtime, librariesPack, defaultLibraries: 'pack' })
|
|
161
125
|
```
|
|
162
126
|
|
|
127
|
+
## Privacy
|
|
128
|
+
|
|
129
|
+
The site sets no cookies and sends nothing anywhere of its own. Learners' browsers contact your
|
|
130
|
+
player domain, the package hosts and, only for `pack` on a site built with `--no-libraries`,
|
|
131
|
+
`cdn.jsdelivr.net`. What the player stores and
|
|
132
|
+
sends, for a privacy notice or a GDPR review:
|
|
133
|
+
[Privacy and data protection](https://github.com/missing-elements/h5p-offline-player/blob/main/docs/privacy.md).
|
|
134
|
+
|
|
163
135
|
## Licences
|
|
164
136
|
|
|
165
|
-
|
|
166
|
-
[`@missing-elements/h5p-runtime`](https://www.npmjs.com/package/@missing-elements/h5p-runtime)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
137
|
+
MIT. The written folder also carries
|
|
138
|
+
[`@missing-elements/h5p-runtime`](https://www.npmjs.com/package/@missing-elements/h5p-runtime)
|
|
139
|
+
under the GPL-3.0, as `frame-assets/` with its `LICENSE.txt` and `NOTICE.txt`, and, unless
|
|
140
|
+
`--no-libraries`, the hub's libraries under their own licences, listed in `libraries.txt`. The
|
|
141
|
+
folder's `NOTICE.txt` says which file is what.
|
package/bin/h5p-embed.mjs
CHANGED
|
@@ -12,15 +12,15 @@ The folder defaults to ./h5p-player.
|
|
|
12
12
|
Options:
|
|
13
13
|
--packages <origins> play packages, and fetch library bundles, only from these origins
|
|
14
14
|
(comma or space separated; this site's own is always allowed). Add
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
any host a package URL redirects to, and https://cdn.jsdelivr.net for
|
|
16
|
+
libraries=pack on a site built with --no-libraries. Default: any https host.
|
|
17
17
|
--ancestors <origins> only these sites may frame the page (frame-ancestors, sent as a header).
|
|
18
18
|
Default: any site.
|
|
19
19
|
--default-libraries <sources>
|
|
20
20
|
the libraries= value for addresses that name none, so a snippet without
|
|
21
|
-
it still plays an export with no libraries: pack
|
|
21
|
+
it still plays an export with no libraries: pack or URLs, as the
|
|
22
22
|
parameter. Default: none, such exports are refused unless the address asks
|
|
23
|
-
--no-libraries leave out the 9.
|
|
23
|
+
--no-libraries leave out the 9.8 MB library pack; libraries=pack then fetches it from jsDelivr
|
|
24
24
|
--force write into a folder that is not empty, replacing only this tool's files
|
|
25
25
|
-h, --help show this
|
|
26
26
|
-v, --version print the version`
|
package/lib/build.mjs
CHANGED
|
@@ -51,6 +51,9 @@ export function parseOrigin(value) {
|
|
|
51
51
|
return url.origin
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/** Where the element's `libraries="pack"` fetches from when a site carries no copy of its own. */
|
|
55
|
+
export const PACK_CDN_ORIGIN = 'https://cdn.jsdelivr.net'
|
|
56
|
+
|
|
54
57
|
/**
|
|
55
58
|
* A `libraries` value for addresses that name none, checked against the site it goes into: the
|
|
56
59
|
* tokens the page understands (`pack`, `hub`, `none`, https URLs), and with a host list, nothing
|
|
@@ -69,13 +72,14 @@ export function parseDefaultLibraries(value, { packages = null, libraries = true
|
|
|
69
72
|
if (tokens.length > 1) throw new EmbedError('--default-libraries none stands alone.')
|
|
70
73
|
return null
|
|
71
74
|
}
|
|
72
|
-
// An exact match on a parsed origin, not a substring of a URL.
|
|
73
|
-
|
|
75
|
+
// An exact match on a parsed origin, not a substring of a URL. Without its own copy, `pack` is
|
|
76
|
+
// the copy on jsDelivr that the element names; `hub` has meant `pack` since player 0.6.
|
|
77
|
+
const cdnAllowed = !packages || packages.some((origin) => origin === PACK_CDN_ORIGIN)
|
|
74
78
|
for (const token of tokens) {
|
|
75
|
-
if (token === 'hub') {
|
|
76
|
-
if (!
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
if (token === 'pack' || token === 'hub') {
|
|
80
|
+
if (!libraries && !cdnAllowed) {
|
|
81
|
+
throw new EmbedError(`--default-libraries names ${token}, but the site has no pack (--no-libraries) and --packages does not list ${PACK_CDN_ORIGIN}.`)
|
|
82
|
+
}
|
|
79
83
|
} else {
|
|
80
84
|
let url
|
|
81
85
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@missing-elements/h5p-embed",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Self-hosted H5P embeds: writes the H5P embed page as a static site for a player domain of your own, then any site frames it with an iframe and one script line. No H5P server, no third-party service; optional allowlists for package hosts and embedding sites.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"cli"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@missing-elements/h5p-libraries": "^0.1.
|
|
47
|
-
"@missing-elements/h5p-
|
|
48
|
-
"@missing-elements/h5p-
|
|
46
|
+
"@missing-elements/h5p-libraries": "^0.1.1",
|
|
47
|
+
"@missing-elements/h5p-offline-player": "^0.6.1",
|
|
48
|
+
"@missing-elements/h5p-runtime": "^0.1.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "^26.6.4",
|
package/site/embed.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `h5p-embed` was told when it wrote the site, and the demo's `/embed` with the demo's copy of the
|
|
6
6
|
* library pack.
|
|
7
7
|
*
|
|
8
|
-
* ?src=<package url>[&libraries=pack
|
|
8
|
+
* ?src=<package url>[&libraries=pack|<url> …|none][&preload=auto][&xapi=<parent origin>]
|
|
9
9
|
* [&frame][©right][&export][&icon][&reporting][&fullscreen=off]
|
|
10
10
|
* [&activity-id=<IRI>][&custom-css=<stylesheet url>]
|
|
11
11
|
*
|
|
@@ -18,17 +18,17 @@
|
|
|
18
18
|
* package — Embed My's preview — shows.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
/** Where `libraries=
|
|
22
|
-
const
|
|
21
|
+
/** Where the element's `libraries="pack"` fetches the library pack from, for a site without its own copy. */
|
|
22
|
+
const PACK_CDN_ORIGIN = 'https://cdn.jsdelivr.net'
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* @param {object} [options]
|
|
26
26
|
* @param {string | null} [options.librariesPack] the URL of a copy of `@missing-elements/h5p-libraries`
|
|
27
|
-
* on this site, which `libraries=pack` names; without one, `pack`
|
|
27
|
+
* on this site, which `libraries=pack` names; without one, `pack` is the element's copy on jsDelivr
|
|
28
28
|
* @param {string[] | null} [options.packages] the origins packages and library bundles may come
|
|
29
29
|
* from, besides this page's own; `null` plays any, asking first when the storage is this origin's
|
|
30
30
|
* @param {string | null} [options.defaultLibraries] the `libraries` value for an address that has
|
|
31
|
-
* none — `pack
|
|
31
|
+
* none — `pack` or URLs, as the parameter — so a snippet without `&libraries=` still plays
|
|
32
32
|
* an export that carries no libraries; `&libraries=none` turns it off for one address
|
|
33
33
|
* @param {object | null} [options.runtime] the `runtime` export of `@missing-elements/h5p-runtime`,
|
|
34
34
|
* for a page that bundles the element; without it the element looks for `frame-assets/` beside itself
|
|
@@ -215,24 +215,21 @@ export function startEmbed({ librariesPack = null, packages = null, defaultLibra
|
|
|
215
215
|
|
|
216
216
|
/**
|
|
217
217
|
* The `libraries` value with `pack` resolved to this site's copy, or an error to show. With a
|
|
218
|
-
* list of hosts, every bundle's origin has to be on it,
|
|
219
|
-
* `h5p-embed` wrote says the same, this only says it in words.
|
|
218
|
+
* list of hosts, every bundle's origin has to be on it, jsDelivr's included when the pack comes
|
|
219
|
+
* from there; the CSP that `h5p-embed` wrote says the same, this only says it in words.
|
|
220
220
|
*/
|
|
221
221
|
const librarySources = (value) => {
|
|
222
222
|
const sources = []
|
|
223
223
|
if (value === 'none') return { value: '' }
|
|
224
224
|
for (const token of value.split(/\s+/).filter(Boolean)) {
|
|
225
|
-
if (token === 'pack') {
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
225
|
+
if (token === 'pack' || token === 'hub') {
|
|
226
|
+
// This site's copy of the pack; without one, the element's own `pack`, the copy on
|
|
227
|
+
// jsDelivr, where that may be reached, so a snippet written for `pack` keeps playing after
|
|
228
|
+
// a rebuild with --no-libraries. `hub` has meant `pack` since player 0.6: the one H5P hub
|
|
229
|
+
// host that answers a browser serves a catalogue older than current exports need.
|
|
230
230
|
if (librariesPack) sources.push(librariesPack)
|
|
231
|
-
if (permitted(
|
|
232
|
-
else
|
|
233
|
-
} else if (token === 'hub') {
|
|
234
|
-
if (!permitted(HUB_ORIGIN)) return { error: 'This player does not fetch libraries from the H5P hub.' }
|
|
235
|
-
sources.push(token)
|
|
231
|
+
else if (permitted(PACK_CDN_ORIGIN)) sources.push('pack')
|
|
232
|
+
else return { error: 'This player was set up without the library pack, so libraries=pack is not available here.' }
|
|
236
233
|
} else {
|
|
237
234
|
const origin = urlOrigin(token)
|
|
238
235
|
if (!origin || !permitted(origin)) return { error: `This player does not fetch libraries from ${origin ?? token}.` }
|