Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

cloudflare-video-element

Package Overview
Dependencies
Maintainers
2
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cloudflare-video-element - npm Package Compare versions

Comparing version 1.0.3 to 1.1.0

222

cloudflare-video-element.js

@@ -43,23 +43,59 @@ // https://developers.cloudflare.com/stream/viewing-videos/using-the-stream-player/using-the-player-api/

const templateShadowDOM = globalThis.document?.createElement('template');
templateShadowDOM.innerHTML = /*html*/`
<style>
:host {
display: inline-block;
min-width: 300px;
min-height: 150px;
position: relative;
}
iframe {
position: absolute;
top: 0;
left: 0;
}
:host(:not([controls])) {
pointer-events: none;
}
</style>
`;
function getTemplateHTML(attrs) {
const iframeAttrs = {
src: serializeIframeUrl(attrs),
frameborder: 0,
width: '100%',
height: '100%',
allow: 'accelerometer; fullscreen; autoplay; encrypted-media; gyroscope; picture-in-picture',
};
return /*html*/`
<style>
:host {
display: inline-block;
min-width: 300px;
min-height: 150px;
position: relative;
}
iframe {
position: absolute;
top: 0;
left: 0;
}
:host(:not([controls])) {
pointer-events: none;
}
</style>
<iframe${serializeAttributes(iframeAttrs)}></iframe>
`;
}
function serializeIframeUrl(attrs) {
if (!attrs.src) return;
const matches = attrs.src.match(MATCH_SRC);
const srcId = matches && matches[1];
const params = {
// ?controls=true is enabled by default in the iframe
controls: attrs.controls === '' ? null : 0,
autoplay: attrs.autoplay,
loop: attrs.loop,
muted: attrs.muted,
preload: attrs.preload,
poster: attrs.poster,
defaultTextTrack: attrs.defaulttexttrack,
primaryColor: attrs.primarycolor,
letterboxColor: attrs.letterboxcolor,
startTime: attrs.starttime,
'ad-url': attrs.adurl,
};
return `${EMBED_BASE}/${srcId}?${serialize(params)}`;
}
class CloudflareVideoElement extends (globalThis.HTMLElement ?? class {}) {
static getTemplateHTML = getTemplateHTML;
static shadowRootOptions = { mode: 'open' };
static observedAttributes = [

@@ -77,23 +113,18 @@ 'autoplay',

loadComplete = new PublicPromise();
#loadRequested;
#hasLoaded;
#noInit;
#options;
#isInit;
#readyState = 0;
constructor() {
super();
async load() {
if (this.#loadRequested) return;
this.attachShadow({ mode: 'open' });
this.shadowRoot.append(templateShadowDOM.content.cloneNode(true));
if (this.#hasLoaded) this.loadComplete = new PublicPromise();
this.#hasLoaded = true;
this.loadComplete = new PublicPromise();
}
// Wait 1 tick to allow other attributes to be set.
await (this.#loadRequested = Promise.resolve());
this.#loadRequested = null;
async load() {
if (this.#hasLoaded) {
this.loadComplete = new PublicPromise();
this.#noInit = true;
}
this.#hasLoaded = true;
this.#readyState = 0;

@@ -105,5 +136,2 @@ this.dispatchEvent(new Event('emptied'));

// Wait 1 tick to allow other attributes to be set.
await Promise.resolve();
if (!this.src) {

@@ -113,21 +141,6 @@ return;

this.#options = {
autoplay: this.autoplay,
controls: this.controls,
loop: this.loop,
muted: this.defaultMuted,
playsinline: this.playsInline,
preload: this.preload,
poster: this.poster,
defaultTextTrack: this.getAttribute('defaulttexttrack'),
primaryColor: this.getAttribute('primarycolor'),
letterboxColor: this.getAttribute('letterboxcolor'),
startTime: this.getAttribute('starttime'),
'ad-url': this.getAttribute('adurl'),
};
const matches = this.src.match(MATCH_SRC);
const srcId = matches && matches[1];
if (this.#noInit) {
if (this.#isInit) {

@@ -138,16 +151,21 @@ this.api = oldApi;

} else {
this.#isInit = true;
const src = `${EMBED_BASE}/${srcId}?${serialize(removeFalsy({
...this.#options,
// ?controls=true is enabled by default in the iframe
controls: this.#options.controls ? null : '0'
}))}`;
let serverRendered = this.shadowRoot;
let iframe = this.shadowRoot.querySelector('iframe');
if (!iframe) {
iframe = createEmbedIframe({ src });
this.shadowRoot.append(iframe);
if (!this.shadowRoot) {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = getTemplateHTML(namedNodeMapToObject(this.attributes));
}
const iframe = this.shadowRoot.querySelector('iframe');
const Stream = await loadScript(API_URL, API_GLOBAL);
if (serverRendered) {
// The Cloudflare Player API has a bug where it doesn't work with a SSR iframe
// because it loads too quickly and the `iframeReady` post message is lost.
// To work around this, we need to reload the iframe.
iframe.src = `${iframe.src}`;
}
this.api = Stream(iframe);

@@ -176,3 +194,3 @@

this.api.addEventListener('loadstart', () => {
Promise.resolve().then(() => {
this.dispatchEvent(new Event('loadcomplete'));

@@ -186,8 +204,8 @@ this.loadComplete.resolve();

async attributeChangedCallback(attrName, oldValue, newValue) {
if (oldValue === newValue) return;
// This is required to come before the await for resolving loadComplete.
switch (attrName) {
case 'src': {
if (oldValue !== newValue) {
this.load();
}
this.load();
return;

@@ -203,5 +221,3 @@ }

case 'loop': {
if (this.#options[attrName] !== this.hasAttribute(attrName)) {
this.api[attrName] = this.hasAttribute(attrName);
}
this.api[attrName] = this.hasAttribute(attrName);
break;

@@ -211,5 +227,3 @@ }

case 'preload': {
if (this.#options[attrName] !== this.getAttribute(attrName)) {
this.api[attrName] = this.getAttribute(attrName);
}
this.api[attrName] = this.getAttribute(attrName);
break;

@@ -390,2 +404,35 @@ }

function serializeAttributes(attrs) {
let html = '';
for (const key in attrs) {
const value = attrs[key];
if (value === '') html += ` ${key}`;
else html += ` ${key}="${value}"`;
}
return html;
}
function serialize(props) {
return String(new URLSearchParams(boolToBinary(props)));
}
function boolToBinary(props) {
let p = {};
for (let key in props) {
let val = props[key];
if (val === true || val === '') p[key] = 1;
else if (val === false) p[key] = 0;
else if (val != null) p[key] = val;
}
return p;
}
function namedNodeMapToObject(namedNodeMap) {
let obj = {};
for (let attr of namedNodeMap) {
obj[attr.name] = attr.value;
}
return obj;
}
const loadScriptCache = {};

@@ -426,33 +473,2 @@ async function loadScript(src, globalName) {

function createElement(tag, attrs = {}, ...children) {
const el = document.createElement(tag);
Object.keys(attrs).forEach(
(name) => attrs[name] != null && el.setAttribute(name, attrs[name])
);
el.append(...children);
return el;
}
const allow =
'accelerometer; fullscreen; autoplay; encrypted-media; gyroscope; picture-in-picture';
function createEmbedIframe({ src, ...props }) {
return createElement('iframe', {
src,
width: '100%',
height: '100%',
allow,
frameborder: 0,
...props,
});
}
function serialize(props) {
return String(new URLSearchParams(props));
}
function removeFalsy(obj) {
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v));
}
if (globalThis.customElements && !globalThis.customElements.get('cloudflare-video')) {

@@ -459,0 +475,0 @@ globalThis.customElements.define('cloudflare-video', CloudflareVideoElement);

{
"name": "cloudflare-video-element",
"version": "1.0.3",
"version": "1.1.0",
"description": "A custom element for the Cloudflare player with an API that matches the `<video>` API",
"type": "module",
"main": "cloudflare-video-element.js",
"repository": "luwes/cloudflare-video-element",
"author": "Wesley Luyten <me@wesleyluyten.com> (https://wesleyluyten.com)",
"author": "@muxinc",
"license": "MIT",
"homepage": "https://github.com/luwes/cloudflare-video-element#readme",
"homepage": "https://github.com/muxinc/media-elements#readme",
"bugs": {
"url": "https://github.com/luwes/cloudflare-video-element/issues"
"url": "https://github.com/muxinc/media-elements/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/muxinc/media-elements.git",
"directory": "packages/cloudflare-video-element"
},
"files": [
"dist"
],
"type": "module",
"main": "cloudflare-video-element.js",
"exports": {
".": "./cloudflare-video-element.js",
"./react": "./dist/react.js"
},
"scripts": {
"lint": "npx eslint@8 *.js -c ./node_modules/wet-run/.eslintrc.json",
"lint": "npx eslint@8 *.js",
"test": "wet run",
"dev": "wet serve"
"serve": "wet serve",
"build:react": "build-react",
"build": "run-s build:*"
},
"devDependencies": {
"npm-run-all": "^4.1.5",
"wet-run": "^1.2.2"

@@ -21,0 +35,0 @@ },

# `<cloudflare-video>`
[![Version](https://img.shields.io/npm/v/cloudflare-video-element?style=flat-square&color=success)](https://www.npmjs.com/package/cloudflare-video-element)
[![npm bundle size](https://img.shields.io/bundlephobia/minzip/cloudflare-video-element?style=flat-square&label=gzip)](https://bundlephobia.com/result?p=cloudflare-video-element)
[![NPM Version](https://img.shields.io/npm/v/cloudflare-video-element?style=flat-square&color=informational)](https://www.npmjs.com/package/cloudflare-video-element)
[![NPM Downloads](https://img.shields.io/npm/dm/cloudflare-video-element?style=flat-square&color=informational&label=npm)](https://www.npmjs.com/package/cloudflare-video-element)
[![jsDelivr hits (npm)](https://img.shields.io/jsdelivr/npm/hm/cloudflare-video-element?style=flat-square&color=%23FF5627)](https://www.jsdelivr.com/package/npm/cloudflare-video-element)
[![npm bundle size](https://img.shields.io/bundlephobia/minzip/cloudflare-video-element?style=flat-square&color=success&label=gzip)](https://bundlephobia.com/result?p=cloudflare-video-element)

@@ -6,0 +8,0 @@ A [custom element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc