@the-w4/responsive-video-source 0.1.0
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/LICENSE +21 -0
- package/README.md +113 -0
- package/dist/index.cjs +133 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.global.js +143 -0
- package/dist/index.global.js.map +7 -0
- package/dist/index.global.min.js +10 -0
- package/dist/index.global.min.js.map +7 -0
- package/dist/index.mjs +113 -0
- package/dist/index.mjs.map +7 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 The W4
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# @the-w4/responsive-video-source
|
|
2
|
+
|
|
3
|
+
Switches a `<video>`'s `<source>` children based on a `data-resolution`
|
|
4
|
+
media-query attribute, so the right mp4/webm pair loads for the current
|
|
5
|
+
viewport instead of relying on the browser's (resolution-blind) native
|
|
6
|
+
`<source>` matching.
|
|
7
|
+
|
|
8
|
+
## The problem this solves
|
|
9
|
+
|
|
10
|
+
`<video>` lets you list multiple `<source>` elements, but the browser only
|
|
11
|
+
uses `type` to pick one — it has no concept of viewport size. If you want a
|
|
12
|
+
different clip (or just a different encode) on mobile vs. desktop, you need
|
|
13
|
+
JS. This package does that by reading a `data-resolution` attribute you put
|
|
14
|
+
on each `<source>`:
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<video autoplay muted loop playsinline>
|
|
18
|
+
<source src="/desktop.mp4" type="video/mp4" data-resolution="min-width: 1024px">
|
|
19
|
+
<source src="/desktop.webm" type="video/webm" data-resolution="min-width: 1024px">
|
|
20
|
+
|
|
21
|
+
<source src="/mobile.mp4" type="video/mp4" data-resolution="max-width: 1023px">
|
|
22
|
+
<source src="/mobile.webm" type="video/webm" data-resolution="max-width: 1023px">
|
|
23
|
+
</video>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Sources are grouped by their `data-resolution` value. When a group's query
|
|
27
|
+
matches, the video is given only that group's `<source>` elements (so the
|
|
28
|
+
browser still gets to pick mp4 vs. webm based on support within the matched
|
|
29
|
+
group) and reloaded. Listeners are added per unique breakpoint via
|
|
30
|
+
`matchMedia`, so switching happens automatically on resize/orientation
|
|
31
|
+
change — no manual resize polling.
|
|
32
|
+
|
|
33
|
+
**Videos with a partial data set are skipped.** If a `<video>` has some
|
|
34
|
+
`<source>` children with `data-resolution` and some without, the package
|
|
35
|
+
leaves it completely untouched (native behavior applies). Only videos where
|
|
36
|
+
*every* `<source>` carries the attribute are managed. A video with no
|
|
37
|
+
`data-resolution` attributes at all is also left alone.
|
|
38
|
+
|
|
39
|
+
The attribute value can be a bare media feature (`"min-width: 1024px"`,
|
|
40
|
+
wrapped in parens automatically) or a full query you write yourself
|
|
41
|
+
(`"(min-width: 1024px) and (orientation: landscape)"`, used as-is). Keep
|
|
42
|
+
breakpoints mutually exclusive (e.g. `min-width` / `max-width` pairs) —
|
|
43
|
+
if more than one group matches at once, the first one declared in the
|
|
44
|
+
markup wins.
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install @the-w4/responsive-video-source
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Usage with a bundler (e.g. @wordpress/scripts, webpack, Vite)
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
import { init } from '@the-w4/responsive-video-source';
|
|
56
|
+
|
|
57
|
+
const controller = init();
|
|
58
|
+
|
|
59
|
+
// later, if needed:
|
|
60
|
+
controller.refresh(); // re-check all managed videos now
|
|
61
|
+
controller.destroy(); // remove all matchMedia listeners
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`init(options)` accepts:
|
|
65
|
+
|
|
66
|
+
| option | default | description |
|
|
67
|
+
|-------------|----------------------|-----------------------------------------------|
|
|
68
|
+
| `root` | `document` | scope to search within |
|
|
69
|
+
| `selector` | `'video'` | selector for video elements |
|
|
70
|
+
| `attribute` | `'data-resolution'` | attribute holding the media-query value |
|
|
71
|
+
|
|
72
|
+
## Usage directly in WordPress (no build step)
|
|
73
|
+
|
|
74
|
+
Copy `dist/index.global.min.js` into your theme and enqueue it — it
|
|
75
|
+
auto-runs on `DOMContentLoaded` with the defaults shown above:
|
|
76
|
+
|
|
77
|
+
```php
|
|
78
|
+
wp_enqueue_script(
|
|
79
|
+
'responsive-video-source',
|
|
80
|
+
get_stylesheet_directory_uri() . '/js/responsive-video-source.min.js',
|
|
81
|
+
array(),
|
|
82
|
+
'0.1.0',
|
|
83
|
+
true
|
|
84
|
+
);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
To opt out of auto-init (e.g. you want a custom `root`/`selector`, or to
|
|
88
|
+
call `init()` yourself), add `data-no-autoinit` to the script tag. WordPress
|
|
89
|
+
doesn't expose a clean way to add arbitrary attributes to `wp_enqueue_script`
|
|
90
|
+
output, so either filter the `script_loader_tag` or just `init()` is also
|
|
91
|
+
fully exposed on `window.ResponsiveVideoSource` for manual control:
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
window.ResponsiveVideoSource.init({ root: document.querySelector('.hero') });
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Development
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm install
|
|
101
|
+
npm run build # writes dist/index.mjs, .cjs, .global.js, .global.min.js
|
|
102
|
+
npm test # jsdom-based unit tests
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Publishing
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npm login # if not already authenticated
|
|
109
|
+
npm publish # prepublishOnly runs build + test automatically
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This is a scoped package (`@the-w4/...`); `publishConfig.access` is already
|
|
113
|
+
set to `public` so it publishes for free under the `the-w4` npm scope.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/index.js
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
autoInit: () => autoInit,
|
|
23
|
+
init: () => init
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
var DEFAULT_ATTRIBUTE = "data-resolution";
|
|
27
|
+
var DEFAULT_SELECTOR = "video";
|
|
28
|
+
function toMediaQuery(value) {
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
return trimmed.startsWith("(") ? trimmed : `(${trimmed})`;
|
|
31
|
+
}
|
|
32
|
+
function getDirectSourceChildren(video) {
|
|
33
|
+
return Array.from(video.children).filter(
|
|
34
|
+
(el) => el.tagName === "SOURCE"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
function readSourceGroups(video, attribute) {
|
|
38
|
+
const sources = getDirectSourceChildren(video);
|
|
39
|
+
if (sources.length === 0) return null;
|
|
40
|
+
const withAttr = sources.filter((s) => s.hasAttribute(attribute));
|
|
41
|
+
if (withAttr.length !== sources.length) return null;
|
|
42
|
+
const order = [];
|
|
43
|
+
const groups = /* @__PURE__ */ new Map();
|
|
44
|
+
sources.forEach((s) => {
|
|
45
|
+
const key = s.getAttribute(attribute).trim();
|
|
46
|
+
if (!groups.has(key)) {
|
|
47
|
+
groups.set(key, []);
|
|
48
|
+
order.push(key);
|
|
49
|
+
}
|
|
50
|
+
groups.get(key).push({
|
|
51
|
+
src: s.getAttribute("src"),
|
|
52
|
+
type: s.getAttribute("type") || ""
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
return { order, groups };
|
|
56
|
+
}
|
|
57
|
+
function applySources(video, items) {
|
|
58
|
+
getDirectSourceChildren(video).forEach((s) => s.remove());
|
|
59
|
+
items.forEach(({ src, type }) => {
|
|
60
|
+
const source = document.createElement("source");
|
|
61
|
+
source.setAttribute("src", src);
|
|
62
|
+
if (type) source.setAttribute("type", type);
|
|
63
|
+
video.appendChild(source);
|
|
64
|
+
});
|
|
65
|
+
try {
|
|
66
|
+
video.load();
|
|
67
|
+
} catch (e) {
|
|
68
|
+
}
|
|
69
|
+
if (video.autoplay) {
|
|
70
|
+
try {
|
|
71
|
+
const playResult = video.play();
|
|
72
|
+
if (playResult && typeof playResult.catch === "function") {
|
|
73
|
+
playResult.catch(() => {
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
} catch (e) {
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function setupVideo(video, attribute) {
|
|
81
|
+
const data = readSourceGroups(video, attribute);
|
|
82
|
+
if (!data) return null;
|
|
83
|
+
const { order, groups } = data;
|
|
84
|
+
const queries = order.map((key) => ({
|
|
85
|
+
key,
|
|
86
|
+
mql: window.matchMedia(toMediaQuery(key))
|
|
87
|
+
}));
|
|
88
|
+
let currentKey = null;
|
|
89
|
+
function pickKey() {
|
|
90
|
+
const match = queries.find((q) => q.mql.matches);
|
|
91
|
+
return match ? match.key : null;
|
|
92
|
+
}
|
|
93
|
+
function evaluate() {
|
|
94
|
+
const key = pickKey();
|
|
95
|
+
if (key === null || key === currentKey) return;
|
|
96
|
+
currentKey = key;
|
|
97
|
+
applySources(video, groups.get(key));
|
|
98
|
+
}
|
|
99
|
+
const handler = () => evaluate();
|
|
100
|
+
queries.forEach((q) => q.mql.addEventListener("change", handler));
|
|
101
|
+
evaluate();
|
|
102
|
+
return {
|
|
103
|
+
evaluate,
|
|
104
|
+
destroy() {
|
|
105
|
+
queries.forEach((q) => q.mql.removeEventListener("change", handler));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function init(options = {}) {
|
|
110
|
+
const {
|
|
111
|
+
root = document,
|
|
112
|
+
selector = DEFAULT_SELECTOR,
|
|
113
|
+
attribute = DEFAULT_ATTRIBUTE
|
|
114
|
+
} = options;
|
|
115
|
+
const controllers = Array.from(root.querySelectorAll(selector)).map((video) => setupVideo(video, attribute)).filter(Boolean);
|
|
116
|
+
return {
|
|
117
|
+
refresh() {
|
|
118
|
+
controllers.forEach((c) => c.evaluate());
|
|
119
|
+
},
|
|
120
|
+
destroy() {
|
|
121
|
+
controllers.forEach((c) => c.destroy());
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function autoInit(options) {
|
|
126
|
+
const run = () => init(options);
|
|
127
|
+
if (document.readyState === "loading") {
|
|
128
|
+
document.addEventListener("DOMContentLoaded", run, { once: true });
|
|
129
|
+
} else {
|
|
130
|
+
run();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.js"],
|
|
4
|
+
"sourcesContent": ["const DEFAULT_ATTRIBUTE = 'data-resolution';\nconst DEFAULT_SELECTOR = 'video';\n\n/**\n * Wraps a bare media feature (\"min-width: 1024px\") in parens so it can be\n * passed to matchMedia(). Values that are already a full query (start with\n * \"(\") are left untouched so compound queries keep working.\n */\nfunction toMediaQuery(value) {\n const trimmed = value.trim();\n return trimmed.startsWith('(') ? trimmed : `(${trimmed})`;\n}\n\nfunction getDirectSourceChildren(video) {\n return Array.from(video.children).filter(\n (el) => el.tagName === 'SOURCE'\n );\n}\n\n/**\n * Groups a video's <source> elements by their data-resolution value.\n * Returns null if the video has no sources, or only some of them carry the\n * attribute (a \"partial\" data set) -- those videos are left untouched so\n * native browser behavior still applies.\n */\nfunction readSourceGroups(video, attribute) {\n const sources = getDirectSourceChildren(video);\n if (sources.length === 0) return null;\n\n const withAttr = sources.filter((s) => s.hasAttribute(attribute));\n if (withAttr.length !== sources.length) return null;\n\n const order = [];\n const groups = new Map();\n\n sources.forEach((s) => {\n const key = s.getAttribute(attribute).trim();\n if (!groups.has(key)) {\n groups.set(key, []);\n order.push(key);\n }\n groups.get(key).push({\n src: s.getAttribute('src'),\n type: s.getAttribute('type') || '',\n });\n });\n\n return { order, groups };\n}\n\nfunction applySources(video, items) {\n getDirectSourceChildren(video).forEach((s) => s.remove());\n\n items.forEach(({ src, type }) => {\n const source = document.createElement('source');\n source.setAttribute('src', src);\n if (type) source.setAttribute('type', type);\n video.appendChild(source);\n });\n\n try {\n video.load();\n } catch (e) {\n /* media APIs can be unimplemented in some test/SSR environments */\n }\n\n if (video.autoplay) {\n try {\n const playResult = video.play();\n if (playResult && typeof playResult.catch === 'function') {\n playResult.catch(() => {});\n }\n } catch (e) {\n /* autoplay can be rejected/unsupported; not our concern here */\n }\n }\n}\n\nfunction setupVideo(video, attribute) {\n const data = readSourceGroups(video, attribute);\n if (!data) return null;\n\n const { order, groups } = data;\n\n // One matchMedia listener per unique breakpoint, not per <source>.\n const queries = order.map((key) => ({\n key,\n mql: window.matchMedia(toMediaQuery(key)),\n }));\n\n let currentKey = null;\n\n function pickKey() {\n const match = queries.find((q) => q.mql.matches);\n return match ? match.key : null;\n }\n\n // If nothing matches (a gap between breakpoints), leave things as they\n // are rather than blanking the video out.\n function evaluate() {\n const key = pickKey();\n if (key === null || key === currentKey) return;\n currentKey = key;\n applySources(video, groups.get(key));\n }\n\n const handler = () => evaluate();\n queries.forEach((q) => q.mql.addEventListener('change', handler));\n\n evaluate();\n\n return {\n evaluate,\n destroy() {\n queries.forEach((q) => q.mql.removeEventListener('change', handler));\n },\n };\n}\n\n/**\n * Scans for <video> elements and switches their <source> children based on\n * a data-resolution media-query attribute. Videos where only some <source>\n * elements carry the attribute are skipped entirely.\n *\n * @param {Object} [options]\n * @param {Document|Element} [options.root=document] - scope to search within\n * @param {string} [options.selector='video'] - selector for video elements\n * @param {string} [options.attribute='data-resolution'] - attribute to read\n * @returns {{ refresh: () => void, destroy: () => void }}\n */\nexport function init(options = {}) {\n const {\n root = document,\n selector = DEFAULT_SELECTOR,\n attribute = DEFAULT_ATTRIBUTE,\n } = options;\n\n const controllers = Array.from(root.querySelectorAll(selector))\n .map((video) => setupVideo(video, attribute))\n .filter(Boolean);\n\n return {\n refresh() {\n controllers.forEach((c) => c.evaluate());\n },\n destroy() {\n controllers.forEach((c) => c.destroy());\n },\n };\n}\n\n/**\n * Convenience helper for the standalone <script> build: runs init() once\n * the DOM is ready, using the given options (or defaults).\n */\nexport function autoInit(options) {\n const run = () => init(options);\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', run, { once: true });\n } else {\n run();\n }\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface InitOptions {
|
|
2
|
+
/** Scope to search within. Defaults to `document`. */
|
|
3
|
+
root?: Document | Element;
|
|
4
|
+
/** Selector for video elements. Defaults to `'video'`. */
|
|
5
|
+
selector?: string;
|
|
6
|
+
/** Attribute holding the media-query value. Defaults to `'data-resolution'`. */
|
|
7
|
+
attribute?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Controller {
|
|
11
|
+
/** Re-runs the breakpoint check for every managed video. */
|
|
12
|
+
refresh(): void;
|
|
13
|
+
/** Removes all matchMedia listeners added by init(). */
|
|
14
|
+
destroy(): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function init(options?: InitOptions): Controller;
|
|
18
|
+
export function autoInit(options?: InitOptions): void;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
var ResponsiveVideoSource = (() => {
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.js
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
autoInit: () => autoInit,
|
|
24
|
+
init: () => init
|
|
25
|
+
});
|
|
26
|
+
var DEFAULT_ATTRIBUTE = "data-resolution";
|
|
27
|
+
var DEFAULT_SELECTOR = "video";
|
|
28
|
+
function toMediaQuery(value) {
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
return trimmed.startsWith("(") ? trimmed : `(${trimmed})`;
|
|
31
|
+
}
|
|
32
|
+
function getDirectSourceChildren(video) {
|
|
33
|
+
return Array.from(video.children).filter(
|
|
34
|
+
(el) => el.tagName === "SOURCE"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
function readSourceGroups(video, attribute) {
|
|
38
|
+
const sources = getDirectSourceChildren(video);
|
|
39
|
+
if (sources.length === 0) return null;
|
|
40
|
+
const withAttr = sources.filter((s) => s.hasAttribute(attribute));
|
|
41
|
+
if (withAttr.length !== sources.length) return null;
|
|
42
|
+
const order = [];
|
|
43
|
+
const groups = /* @__PURE__ */ new Map();
|
|
44
|
+
sources.forEach((s) => {
|
|
45
|
+
const key = s.getAttribute(attribute).trim();
|
|
46
|
+
if (!groups.has(key)) {
|
|
47
|
+
groups.set(key, []);
|
|
48
|
+
order.push(key);
|
|
49
|
+
}
|
|
50
|
+
groups.get(key).push({
|
|
51
|
+
src: s.getAttribute("src"),
|
|
52
|
+
type: s.getAttribute("type") || ""
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
return { order, groups };
|
|
56
|
+
}
|
|
57
|
+
function applySources(video, items) {
|
|
58
|
+
getDirectSourceChildren(video).forEach((s) => s.remove());
|
|
59
|
+
items.forEach(({ src, type }) => {
|
|
60
|
+
const source = document.createElement("source");
|
|
61
|
+
source.setAttribute("src", src);
|
|
62
|
+
if (type) source.setAttribute("type", type);
|
|
63
|
+
video.appendChild(source);
|
|
64
|
+
});
|
|
65
|
+
try {
|
|
66
|
+
video.load();
|
|
67
|
+
} catch (e) {
|
|
68
|
+
}
|
|
69
|
+
if (video.autoplay) {
|
|
70
|
+
try {
|
|
71
|
+
const playResult = video.play();
|
|
72
|
+
if (playResult && typeof playResult.catch === "function") {
|
|
73
|
+
playResult.catch(() => {
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
} catch (e) {
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function setupVideo(video, attribute) {
|
|
81
|
+
const data = readSourceGroups(video, attribute);
|
|
82
|
+
if (!data) return null;
|
|
83
|
+
const { order, groups } = data;
|
|
84
|
+
const queries = order.map((key) => ({
|
|
85
|
+
key,
|
|
86
|
+
mql: window.matchMedia(toMediaQuery(key))
|
|
87
|
+
}));
|
|
88
|
+
let currentKey = null;
|
|
89
|
+
function pickKey() {
|
|
90
|
+
const match = queries.find((q) => q.mql.matches);
|
|
91
|
+
return match ? match.key : null;
|
|
92
|
+
}
|
|
93
|
+
function evaluate() {
|
|
94
|
+
const key = pickKey();
|
|
95
|
+
if (key === null || key === currentKey) return;
|
|
96
|
+
currentKey = key;
|
|
97
|
+
applySources(video, groups.get(key));
|
|
98
|
+
}
|
|
99
|
+
const handler = () => evaluate();
|
|
100
|
+
queries.forEach((q) => q.mql.addEventListener("change", handler));
|
|
101
|
+
evaluate();
|
|
102
|
+
return {
|
|
103
|
+
evaluate,
|
|
104
|
+
destroy() {
|
|
105
|
+
queries.forEach((q) => q.mql.removeEventListener("change", handler));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function init(options = {}) {
|
|
110
|
+
const {
|
|
111
|
+
root = document,
|
|
112
|
+
selector = DEFAULT_SELECTOR,
|
|
113
|
+
attribute = DEFAULT_ATTRIBUTE
|
|
114
|
+
} = options;
|
|
115
|
+
const controllers = Array.from(root.querySelectorAll(selector)).map((video) => setupVideo(video, attribute)).filter(Boolean);
|
|
116
|
+
return {
|
|
117
|
+
refresh() {
|
|
118
|
+
controllers.forEach((c) => c.evaluate());
|
|
119
|
+
},
|
|
120
|
+
destroy() {
|
|
121
|
+
controllers.forEach((c) => c.destroy());
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function autoInit(options) {
|
|
126
|
+
const run = () => init(options);
|
|
127
|
+
if (document.readyState === "loading") {
|
|
128
|
+
document.addEventListener("DOMContentLoaded", run, { once: true });
|
|
129
|
+
} else {
|
|
130
|
+
run();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return __toCommonJS(index_exports);
|
|
134
|
+
})();
|
|
135
|
+
|
|
136
|
+
(function () {
|
|
137
|
+
if (typeof document === 'undefined') return;
|
|
138
|
+
var script = document.currentScript;
|
|
139
|
+
if (script && script.hasAttribute('data-no-autoinit')) return;
|
|
140
|
+
ResponsiveVideoSource.autoInit();
|
|
141
|
+
})();
|
|
142
|
+
|
|
143
|
+
//# sourceMappingURL=index.global.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.js"],
|
|
4
|
+
"sourcesContent": ["const DEFAULT_ATTRIBUTE = 'data-resolution';\nconst DEFAULT_SELECTOR = 'video';\n\n/**\n * Wraps a bare media feature (\"min-width: 1024px\") in parens so it can be\n * passed to matchMedia(). Values that are already a full query (start with\n * \"(\") are left untouched so compound queries keep working.\n */\nfunction toMediaQuery(value) {\n const trimmed = value.trim();\n return trimmed.startsWith('(') ? trimmed : `(${trimmed})`;\n}\n\nfunction getDirectSourceChildren(video) {\n return Array.from(video.children).filter(\n (el) => el.tagName === 'SOURCE'\n );\n}\n\n/**\n * Groups a video's <source> elements by their data-resolution value.\n * Returns null if the video has no sources, or only some of them carry the\n * attribute (a \"partial\" data set) -- those videos are left untouched so\n * native browser behavior still applies.\n */\nfunction readSourceGroups(video, attribute) {\n const sources = getDirectSourceChildren(video);\n if (sources.length === 0) return null;\n\n const withAttr = sources.filter((s) => s.hasAttribute(attribute));\n if (withAttr.length !== sources.length) return null;\n\n const order = [];\n const groups = new Map();\n\n sources.forEach((s) => {\n const key = s.getAttribute(attribute).trim();\n if (!groups.has(key)) {\n groups.set(key, []);\n order.push(key);\n }\n groups.get(key).push({\n src: s.getAttribute('src'),\n type: s.getAttribute('type') || '',\n });\n });\n\n return { order, groups };\n}\n\nfunction applySources(video, items) {\n getDirectSourceChildren(video).forEach((s) => s.remove());\n\n items.forEach(({ src, type }) => {\n const source = document.createElement('source');\n source.setAttribute('src', src);\n if (type) source.setAttribute('type', type);\n video.appendChild(source);\n });\n\n try {\n video.load();\n } catch (e) {\n /* media APIs can be unimplemented in some test/SSR environments */\n }\n\n if (video.autoplay) {\n try {\n const playResult = video.play();\n if (playResult && typeof playResult.catch === 'function') {\n playResult.catch(() => {});\n }\n } catch (e) {\n /* autoplay can be rejected/unsupported; not our concern here */\n }\n }\n}\n\nfunction setupVideo(video, attribute) {\n const data = readSourceGroups(video, attribute);\n if (!data) return null;\n\n const { order, groups } = data;\n\n // One matchMedia listener per unique breakpoint, not per <source>.\n const queries = order.map((key) => ({\n key,\n mql: window.matchMedia(toMediaQuery(key)),\n }));\n\n let currentKey = null;\n\n function pickKey() {\n const match = queries.find((q) => q.mql.matches);\n return match ? match.key : null;\n }\n\n // If nothing matches (a gap between breakpoints), leave things as they\n // are rather than blanking the video out.\n function evaluate() {\n const key = pickKey();\n if (key === null || key === currentKey) return;\n currentKey = key;\n applySources(video, groups.get(key));\n }\n\n const handler = () => evaluate();\n queries.forEach((q) => q.mql.addEventListener('change', handler));\n\n evaluate();\n\n return {\n evaluate,\n destroy() {\n queries.forEach((q) => q.mql.removeEventListener('change', handler));\n },\n };\n}\n\n/**\n * Scans for <video> elements and switches their <source> children based on\n * a data-resolution media-query attribute. Videos where only some <source>\n * elements carry the attribute are skipped entirely.\n *\n * @param {Object} [options]\n * @param {Document|Element} [options.root=document] - scope to search within\n * @param {string} [options.selector='video'] - selector for video elements\n * @param {string} [options.attribute='data-resolution'] - attribute to read\n * @returns {{ refresh: () => void, destroy: () => void }}\n */\nexport function init(options = {}) {\n const {\n root = document,\n selector = DEFAULT_SELECTOR,\n attribute = DEFAULT_ATTRIBUTE,\n } = options;\n\n const controllers = Array.from(root.querySelectorAll(selector))\n .map((video) => setupVideo(video, attribute))\n .filter(Boolean);\n\n return {\n refresh() {\n controllers.forEach((c) => c.evaluate());\n },\n destroy() {\n controllers.forEach((c) => c.destroy());\n },\n };\n}\n\n/**\n * Convenience helper for the standalone <script> build: runs init() once\n * the DOM is ready, using the given options (or defaults).\n */\nexport function autoInit(options) {\n const run = () => init(options);\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', run, { once: true });\n } else {\n run();\n }\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var ResponsiveVideoSource=(()=>{var l=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var g=(e,t)=>{for(var r in t)l(e,r,{get:t[r],enumerable:!0})},A=(e,t,r,u)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of y(t))!E.call(e,n)&&n!==r&&l(e,n,{get:()=>t[n],enumerable:!(u=p(t,n))||u.enumerable});return e};var L=e=>A(l({},"__esModule",{value:!0}),e);var M={};g(M,{autoInit:()=>D,init:()=>d});var S="data-resolution",b="video";function q(e){let t=e.trim();return t.startsWith("(")?t:`(${t})`}function h(e){return Array.from(e.children).filter(t=>t.tagName==="SOURCE")}function T(e,t){let r=h(e);if(r.length===0||r.filter(a=>a.hasAttribute(t)).length!==r.length)return null;let n=[],o=new Map;return r.forEach(a=>{let s=a.getAttribute(t).trim();o.has(s)||(o.set(s,[]),n.push(s)),o.get(s).push({src:a.getAttribute("src"),type:a.getAttribute("type")||""})}),{order:n,groups:o}}function w(e,t){h(e).forEach(r=>r.remove()),t.forEach(({src:r,type:u})=>{let n=document.createElement("source");n.setAttribute("src",r),u&&n.setAttribute("type",u),e.appendChild(n)});try{e.load()}catch{}if(e.autoplay)try{let r=e.play();r&&typeof r.catch=="function"&&r.catch(()=>{})}catch{}}function C(e,t){let r=T(e,t);if(!r)return null;let{order:u,groups:n}=r,o=u.map(c=>({key:c,mql:window.matchMedia(q(c))})),a=null;function s(){let c=o.find(m=>m.mql.matches);return c?c.key:null}function i(){let c=s();c===null||c===a||(a=c,w(e,n.get(c)))}let f=()=>i();return o.forEach(c=>c.mql.addEventListener("change",f)),i(),{evaluate:i,destroy(){o.forEach(c=>c.mql.removeEventListener("change",f))}}}function d(e={}){let{root:t=document,selector:r=b,attribute:u=S}=e,n=Array.from(t.querySelectorAll(r)).map(o=>C(o,u)).filter(Boolean);return{refresh(){n.forEach(o=>o.evaluate())},destroy(){n.forEach(o=>o.destroy())}}}function D(e){let t=()=>d(e);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}return L(M);})();
|
|
2
|
+
|
|
3
|
+
(function () {
|
|
4
|
+
if (typeof document === 'undefined') return;
|
|
5
|
+
var script = document.currentScript;
|
|
6
|
+
if (script && script.hasAttribute('data-no-autoinit')) return;
|
|
7
|
+
ResponsiveVideoSource.autoInit();
|
|
8
|
+
})();
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=index.global.min.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.js"],
|
|
4
|
+
"sourcesContent": ["const DEFAULT_ATTRIBUTE = 'data-resolution';\nconst DEFAULT_SELECTOR = 'video';\n\n/**\n * Wraps a bare media feature (\"min-width: 1024px\") in parens so it can be\n * passed to matchMedia(). Values that are already a full query (start with\n * \"(\") are left untouched so compound queries keep working.\n */\nfunction toMediaQuery(value) {\n const trimmed = value.trim();\n return trimmed.startsWith('(') ? trimmed : `(${trimmed})`;\n}\n\nfunction getDirectSourceChildren(video) {\n return Array.from(video.children).filter(\n (el) => el.tagName === 'SOURCE'\n );\n}\n\n/**\n * Groups a video's <source> elements by their data-resolution value.\n * Returns null if the video has no sources, or only some of them carry the\n * attribute (a \"partial\" data set) -- those videos are left untouched so\n * native browser behavior still applies.\n */\nfunction readSourceGroups(video, attribute) {\n const sources = getDirectSourceChildren(video);\n if (sources.length === 0) return null;\n\n const withAttr = sources.filter((s) => s.hasAttribute(attribute));\n if (withAttr.length !== sources.length) return null;\n\n const order = [];\n const groups = new Map();\n\n sources.forEach((s) => {\n const key = s.getAttribute(attribute).trim();\n if (!groups.has(key)) {\n groups.set(key, []);\n order.push(key);\n }\n groups.get(key).push({\n src: s.getAttribute('src'),\n type: s.getAttribute('type') || '',\n });\n });\n\n return { order, groups };\n}\n\nfunction applySources(video, items) {\n getDirectSourceChildren(video).forEach((s) => s.remove());\n\n items.forEach(({ src, type }) => {\n const source = document.createElement('source');\n source.setAttribute('src', src);\n if (type) source.setAttribute('type', type);\n video.appendChild(source);\n });\n\n try {\n video.load();\n } catch (e) {\n /* media APIs can be unimplemented in some test/SSR environments */\n }\n\n if (video.autoplay) {\n try {\n const playResult = video.play();\n if (playResult && typeof playResult.catch === 'function') {\n playResult.catch(() => {});\n }\n } catch (e) {\n /* autoplay can be rejected/unsupported; not our concern here */\n }\n }\n}\n\nfunction setupVideo(video, attribute) {\n const data = readSourceGroups(video, attribute);\n if (!data) return null;\n\n const { order, groups } = data;\n\n // One matchMedia listener per unique breakpoint, not per <source>.\n const queries = order.map((key) => ({\n key,\n mql: window.matchMedia(toMediaQuery(key)),\n }));\n\n let currentKey = null;\n\n function pickKey() {\n const match = queries.find((q) => q.mql.matches);\n return match ? match.key : null;\n }\n\n // If nothing matches (a gap between breakpoints), leave things as they\n // are rather than blanking the video out.\n function evaluate() {\n const key = pickKey();\n if (key === null || key === currentKey) return;\n currentKey = key;\n applySources(video, groups.get(key));\n }\n\n const handler = () => evaluate();\n queries.forEach((q) => q.mql.addEventListener('change', handler));\n\n evaluate();\n\n return {\n evaluate,\n destroy() {\n queries.forEach((q) => q.mql.removeEventListener('change', handler));\n },\n };\n}\n\n/**\n * Scans for <video> elements and switches their <source> children based on\n * a data-resolution media-query attribute. Videos where only some <source>\n * elements carry the attribute are skipped entirely.\n *\n * @param {Object} [options]\n * @param {Document|Element} [options.root=document] - scope to search within\n * @param {string} [options.selector='video'] - selector for video elements\n * @param {string} [options.attribute='data-resolution'] - attribute to read\n * @returns {{ refresh: () => void, destroy: () => void }}\n */\nexport function init(options = {}) {\n const {\n root = document,\n selector = DEFAULT_SELECTOR,\n attribute = DEFAULT_ATTRIBUTE,\n } = options;\n\n const controllers = Array.from(root.querySelectorAll(selector))\n .map((video) => setupVideo(video, attribute))\n .filter(Boolean);\n\n return {\n refresh() {\n controllers.forEach((c) => c.evaluate());\n },\n destroy() {\n controllers.forEach((c) => c.destroy());\n },\n };\n}\n\n/**\n * Convenience helper for the standalone <script> build: runs init() once\n * the DOM is ready, using the given options (or defaults).\n */\nexport function autoInit(options) {\n const run = () => init(options);\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', run, { once: true });\n } else {\n run();\n }\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["index_exports", "__export", "autoInit", "init", "DEFAULT_ATTRIBUTE", "DEFAULT_SELECTOR", "toMediaQuery", "value", "trimmed", "getDirectSourceChildren", "video", "el", "readSourceGroups", "attribute", "sources", "s", "order", "groups", "key", "applySources", "items", "src", "type", "source", "playResult", "setupVideo", "data", "queries", "currentKey", "pickKey", "match", "q", "evaluate", "handler", "options", "root", "selector", "controllers", "c", "run"]
|
|
7
|
+
}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// src/index.js
|
|
2
|
+
var DEFAULT_ATTRIBUTE = "data-resolution";
|
|
3
|
+
var DEFAULT_SELECTOR = "video";
|
|
4
|
+
function toMediaQuery(value) {
|
|
5
|
+
const trimmed = value.trim();
|
|
6
|
+
return trimmed.startsWith("(") ? trimmed : `(${trimmed})`;
|
|
7
|
+
}
|
|
8
|
+
function getDirectSourceChildren(video) {
|
|
9
|
+
return Array.from(video.children).filter(
|
|
10
|
+
(el) => el.tagName === "SOURCE"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
function readSourceGroups(video, attribute) {
|
|
14
|
+
const sources = getDirectSourceChildren(video);
|
|
15
|
+
if (sources.length === 0) return null;
|
|
16
|
+
const withAttr = sources.filter((s) => s.hasAttribute(attribute));
|
|
17
|
+
if (withAttr.length !== sources.length) return null;
|
|
18
|
+
const order = [];
|
|
19
|
+
const groups = /* @__PURE__ */ new Map();
|
|
20
|
+
sources.forEach((s) => {
|
|
21
|
+
const key = s.getAttribute(attribute).trim();
|
|
22
|
+
if (!groups.has(key)) {
|
|
23
|
+
groups.set(key, []);
|
|
24
|
+
order.push(key);
|
|
25
|
+
}
|
|
26
|
+
groups.get(key).push({
|
|
27
|
+
src: s.getAttribute("src"),
|
|
28
|
+
type: s.getAttribute("type") || ""
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
return { order, groups };
|
|
32
|
+
}
|
|
33
|
+
function applySources(video, items) {
|
|
34
|
+
getDirectSourceChildren(video).forEach((s) => s.remove());
|
|
35
|
+
items.forEach(({ src, type }) => {
|
|
36
|
+
const source = document.createElement("source");
|
|
37
|
+
source.setAttribute("src", src);
|
|
38
|
+
if (type) source.setAttribute("type", type);
|
|
39
|
+
video.appendChild(source);
|
|
40
|
+
});
|
|
41
|
+
try {
|
|
42
|
+
video.load();
|
|
43
|
+
} catch (e) {
|
|
44
|
+
}
|
|
45
|
+
if (video.autoplay) {
|
|
46
|
+
try {
|
|
47
|
+
const playResult = video.play();
|
|
48
|
+
if (playResult && typeof playResult.catch === "function") {
|
|
49
|
+
playResult.catch(() => {
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function setupVideo(video, attribute) {
|
|
57
|
+
const data = readSourceGroups(video, attribute);
|
|
58
|
+
if (!data) return null;
|
|
59
|
+
const { order, groups } = data;
|
|
60
|
+
const queries = order.map((key) => ({
|
|
61
|
+
key,
|
|
62
|
+
mql: window.matchMedia(toMediaQuery(key))
|
|
63
|
+
}));
|
|
64
|
+
let currentKey = null;
|
|
65
|
+
function pickKey() {
|
|
66
|
+
const match = queries.find((q) => q.mql.matches);
|
|
67
|
+
return match ? match.key : null;
|
|
68
|
+
}
|
|
69
|
+
function evaluate() {
|
|
70
|
+
const key = pickKey();
|
|
71
|
+
if (key === null || key === currentKey) return;
|
|
72
|
+
currentKey = key;
|
|
73
|
+
applySources(video, groups.get(key));
|
|
74
|
+
}
|
|
75
|
+
const handler = () => evaluate();
|
|
76
|
+
queries.forEach((q) => q.mql.addEventListener("change", handler));
|
|
77
|
+
evaluate();
|
|
78
|
+
return {
|
|
79
|
+
evaluate,
|
|
80
|
+
destroy() {
|
|
81
|
+
queries.forEach((q) => q.mql.removeEventListener("change", handler));
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function init(options = {}) {
|
|
86
|
+
const {
|
|
87
|
+
root = document,
|
|
88
|
+
selector = DEFAULT_SELECTOR,
|
|
89
|
+
attribute = DEFAULT_ATTRIBUTE
|
|
90
|
+
} = options;
|
|
91
|
+
const controllers = Array.from(root.querySelectorAll(selector)).map((video) => setupVideo(video, attribute)).filter(Boolean);
|
|
92
|
+
return {
|
|
93
|
+
refresh() {
|
|
94
|
+
controllers.forEach((c) => c.evaluate());
|
|
95
|
+
},
|
|
96
|
+
destroy() {
|
|
97
|
+
controllers.forEach((c) => c.destroy());
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function autoInit(options) {
|
|
102
|
+
const run = () => init(options);
|
|
103
|
+
if (document.readyState === "loading") {
|
|
104
|
+
document.addEventListener("DOMContentLoaded", run, { once: true });
|
|
105
|
+
} else {
|
|
106
|
+
run();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
autoInit,
|
|
111
|
+
init
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.js"],
|
|
4
|
+
"sourcesContent": ["const DEFAULT_ATTRIBUTE = 'data-resolution';\nconst DEFAULT_SELECTOR = 'video';\n\n/**\n * Wraps a bare media feature (\"min-width: 1024px\") in parens so it can be\n * passed to matchMedia(). Values that are already a full query (start with\n * \"(\") are left untouched so compound queries keep working.\n */\nfunction toMediaQuery(value) {\n const trimmed = value.trim();\n return trimmed.startsWith('(') ? trimmed : `(${trimmed})`;\n}\n\nfunction getDirectSourceChildren(video) {\n return Array.from(video.children).filter(\n (el) => el.tagName === 'SOURCE'\n );\n}\n\n/**\n * Groups a video's <source> elements by their data-resolution value.\n * Returns null if the video has no sources, or only some of them carry the\n * attribute (a \"partial\" data set) -- those videos are left untouched so\n * native browser behavior still applies.\n */\nfunction readSourceGroups(video, attribute) {\n const sources = getDirectSourceChildren(video);\n if (sources.length === 0) return null;\n\n const withAttr = sources.filter((s) => s.hasAttribute(attribute));\n if (withAttr.length !== sources.length) return null;\n\n const order = [];\n const groups = new Map();\n\n sources.forEach((s) => {\n const key = s.getAttribute(attribute).trim();\n if (!groups.has(key)) {\n groups.set(key, []);\n order.push(key);\n }\n groups.get(key).push({\n src: s.getAttribute('src'),\n type: s.getAttribute('type') || '',\n });\n });\n\n return { order, groups };\n}\n\nfunction applySources(video, items) {\n getDirectSourceChildren(video).forEach((s) => s.remove());\n\n items.forEach(({ src, type }) => {\n const source = document.createElement('source');\n source.setAttribute('src', src);\n if (type) source.setAttribute('type', type);\n video.appendChild(source);\n });\n\n try {\n video.load();\n } catch (e) {\n /* media APIs can be unimplemented in some test/SSR environments */\n }\n\n if (video.autoplay) {\n try {\n const playResult = video.play();\n if (playResult && typeof playResult.catch === 'function') {\n playResult.catch(() => {});\n }\n } catch (e) {\n /* autoplay can be rejected/unsupported; not our concern here */\n }\n }\n}\n\nfunction setupVideo(video, attribute) {\n const data = readSourceGroups(video, attribute);\n if (!data) return null;\n\n const { order, groups } = data;\n\n // One matchMedia listener per unique breakpoint, not per <source>.\n const queries = order.map((key) => ({\n key,\n mql: window.matchMedia(toMediaQuery(key)),\n }));\n\n let currentKey = null;\n\n function pickKey() {\n const match = queries.find((q) => q.mql.matches);\n return match ? match.key : null;\n }\n\n // If nothing matches (a gap between breakpoints), leave things as they\n // are rather than blanking the video out.\n function evaluate() {\n const key = pickKey();\n if (key === null || key === currentKey) return;\n currentKey = key;\n applySources(video, groups.get(key));\n }\n\n const handler = () => evaluate();\n queries.forEach((q) => q.mql.addEventListener('change', handler));\n\n evaluate();\n\n return {\n evaluate,\n destroy() {\n queries.forEach((q) => q.mql.removeEventListener('change', handler));\n },\n };\n}\n\n/**\n * Scans for <video> elements and switches their <source> children based on\n * a data-resolution media-query attribute. Videos where only some <source>\n * elements carry the attribute are skipped entirely.\n *\n * @param {Object} [options]\n * @param {Document|Element} [options.root=document] - scope to search within\n * @param {string} [options.selector='video'] - selector for video elements\n * @param {string} [options.attribute='data-resolution'] - attribute to read\n * @returns {{ refresh: () => void, destroy: () => void }}\n */\nexport function init(options = {}) {\n const {\n root = document,\n selector = DEFAULT_SELECTOR,\n attribute = DEFAULT_ATTRIBUTE,\n } = options;\n\n const controllers = Array.from(root.querySelectorAll(selector))\n .map((video) => setupVideo(video, attribute))\n .filter(Boolean);\n\n return {\n refresh() {\n controllers.forEach((c) => c.evaluate());\n },\n destroy() {\n controllers.forEach((c) => c.destroy());\n },\n };\n}\n\n/**\n * Convenience helper for the standalone <script> build: runs init() once\n * the DOM is ready, using the given options (or defaults).\n */\nexport function autoInit(options) {\n const run = () => init(options);\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', run, { once: true });\n } else {\n run();\n }\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@the-w4/responsive-video-source",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Switches <video> <source> elements based on a data-resolution media-query attribute, so the right mp4/webm loads per breakpoint.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"browser": "./dist/index.global.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.cjs",
|
|
15
|
+
"default": "./dist/index.mjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "node scripts/build.js",
|
|
25
|
+
"test": "node --test",
|
|
26
|
+
"prepublishOnly": "npm run build && npm test"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"video",
|
|
30
|
+
"responsive",
|
|
31
|
+
"source",
|
|
32
|
+
"media-query",
|
|
33
|
+
"matchmedia",
|
|
34
|
+
"resolution",
|
|
35
|
+
"mp4",
|
|
36
|
+
"webm"
|
|
37
|
+
],
|
|
38
|
+
"author": "Assaf Katz <assaf@thew4.co> (https://thew4.co)",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"esbuild": "^0.24.0",
|
|
45
|
+
"jsdom": "^25.0.0"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18"
|
|
49
|
+
}
|
|
50
|
+
}
|