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

youtube-video-element

Package Overview
Dependencies
Maintainers
4
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

youtube-video-element - npm Package Compare versions

Comparing version 1.0.1 to 1.1.0

33

package.json
{
"name": "youtube-video-element",
"version": "1.0.1",
"version": "1.1.0",
"description": "Custom element (web component) for the YouTube player.",
"type": "module",
"main": "youtube-video-element.js",
"files": [],
"scripts": {
"lint": "npx eslint@8 *.js -c ./node_modules/wet-run/.eslintrc.json",
"test": "wet run",
"dev": "wet serve"
},
"repository": "muxinc/youtube-video-element",
"author": "@muxinc",

@@ -20,3 +11,25 @@ "license": "MIT",

},
"repository": {
"type": "git",
"url": "git+https://github.com/muxinc/media-elements.git",
"directory": "packages/youtube-video-element"
},
"files": [
"dist"
],
"type": "module",
"main": "youtube-video-element.js",
"exports": {
".": "./youtube-video-element.js",
"./react": "./dist/react.js"
},
"scripts": {
"lint": "npx eslint@8 *.js",
"test": "wet run",
"serve": "wet serve",
"build:react": "build-react",
"build": "run-s build:*"
},
"devDependencies": {
"npm-run-all": "^4.1.5",
"wet-run": "^1.2.2"

@@ -23,0 +36,0 @@ },

@@ -10,23 +10,58 @@ // https://developers.google.com/youtube/iframe_api_reference

const templateShadowDOM = globalThis.document?.createElement('template');
if (templateShadowDOM) {
templateShadowDOM.innerHTML = /*html*/`
<style>
:host {
display: inline-block;
line-height: 0;
position: relative;
min-width: 300px;
min-height: 150px;
}
iframe {
position: absolute;
top: 0;
left: 0;
}
</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;
line-height: 0;
position: relative;
min-width: 300px;
min-height: 150px;
}
iframe {
position: absolute;
top: 0;
left: 0;
}
</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,
mute: attrs.muted,
playsinline: attrs.playsinline,
preload: attrs.preload ?? 'metadata',
// origin: globalThis.location?.origin,
enablejsapi: 1,
showinfo: 0,
rel: 0,
iv_load_policy: 3,
modestbranding: 1,
};
return `${EMBED_BASE}/${srcId}?${serialize(params)}`;
}
class YoutubeVideoElement extends (globalThis.HTMLElement ?? class {}) {
static getTemplateHTML = getTemplateHTML;
static shadowRootOptions = { mode: 'open' };
static observedAttributes = [

@@ -44,3 +79,5 @@ 'autoplay',

#options;
loadComplete = new PublicPromise();
#loadRequested;
#hasLoaded;
#readyState = 0;

@@ -51,18 +88,19 @@ #seeking = false;

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

@@ -74,8 +112,5 @@ this.dispatchEvent(new Event('emptied'));

// Wait 1 tick to allow other attributes to be set.
await Promise.resolve();
oldApi?.destroy();
if (!this.src) {
// Removes the <iframe> containing the player.
oldApi?.destroy();
return;

@@ -86,26 +121,8 @@ }

this.#options = {
autoplay: this.autoplay,
controls: this.controls,
loop: this.loop,
mute: this.defaultMuted,
playsinline: this.playsInline,
preload: this.preload ?? 'metadata',
origin: location.origin,
enablejsapi: 1,
showinfo: 0,
rel: 0,
iv_load_policy: 3,
modestbranding: 1,
};
let iframe = this.shadowRoot.querySelector('iframe');
let attrs = namedNodeMapToObject(this.attributes);
const matches = this.src.match(MATCH_SRC);
const metaId = matches && matches[1];
const src = `${EMBED_BASE}/${metaId}?${serialize(
boolToBinary(this.#options)
)}`;
let iframe = this.shadowRoot.querySelector('iframe');
if (!iframe) {
iframe = createEmbedIframe({ src });
this.shadowRoot.append(iframe);
if (!iframe?.src || iframe.src !== serializeIframeUrl(attrs)) {
this.shadowRoot.innerHTML = getTemplateHTML(attrs);
iframe = this.shadowRoot.querySelector('iframe');
}

@@ -222,14 +239,8 @@

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

@@ -239,6 +250,3 @@ case 'controls':

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

@@ -305,4 +313,3 @@ }

if (this.autoplay == val) return;
if (val) this.setAttribute('autoplay', '');
else this.removeAttribute('autoplay');
this.toggleAttribute('autoplay', Boolean(val));
}

@@ -326,4 +333,3 @@

if (this.controls == val) return;
if (val) this.setAttribute('controls', '');
else this.removeAttribute('controls');
this.toggleAttribute('controls', Boolean(val));
}

@@ -351,4 +357,3 @@

if (this.defaultMuted == val) return;
if (val) this.setAttribute('muted', '');
else this.removeAttribute('muted');
this.toggleAttribute('muted', Boolean(val));
}

@@ -366,4 +371,3 @@

if (this.loop == val) return;
if (val) this.setAttribute('loop', '');
else this.removeAttribute('loop');
this.toggleAttribute('loop', Boolean(val));
}

@@ -400,4 +404,3 @@

if (this.playsInline == val) return;
if (val) this.setAttribute('playsinline', '');
else this.removeAttribute('playsinline');
this.toggleAttribute('playsinline', Boolean(val));
}

@@ -427,2 +430,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 = {};

@@ -488,43 +524,2 @@ async function loadScript(src, globalName, readyFnName) {

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; autoplay; fullscreen; 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 Object.keys(props)
.map((key) => {
if (props[key] == null) return '';
return `${key}=${encodeURIComponent(props[key])}`;
})
.join('&');
}
function boolToBinary(props) {
let p = { ...props };
for (let key in p) {
if (p[key] === false) p[key] = 0;
else if (p[key] === true) p[key] = 1;
}
return p;
}
/**

@@ -531,0 +526,0 @@ * Creates a fake `TimeRanges` object.

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