New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

exokit

Package Overview
Dependencies
Maintainers
1
Versions
388
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

exokit - npm Package Compare versions

Comparing version 0.0.12 to 0.0.13

1669

index.js

@@ -20,3 +20,5 @@ const events = require('events');

const windowSymbol = Symbol();
const setWindowSymbol = Symbol();
const htmlTagsSymbol = Symbol();
const htmlElementsSymbol = Symbol();
const optionsSymbol = Symbol();

@@ -366,754 +368,876 @@ let id = 0;

const exokit = (s = '', options = {}) => {
options.url = options.url || 'http://127.0.0.1';
options.dataPath = options.dataPath || __dirname;
class Node extends EventEmitter {
constructor(nodeName = null) {
super();
const baseUrl = options.url;
const _normalizeUrl = src => new URL(src, baseUrl).href;
const _parseAttributes = attrs => {
const result = {};
for (let i = 0; i < attrs.length; i++) {
const attr = attrs[i];
result[attr.name] = attr.value;
}
return result;
};
const _formatAttributes = attributes => {
const result = [];
for (const name in attributes) {
const value = attributes[name];
result.push({
name,
value,
});
}
return result;
};
const _parseStyle = styleString => {
const style = {};
const split = styleString.split(/;\s*/);
for (let i = 0; i < split.length; i++) {
const split2 = split[i].split(/:\s*/);
if (split2.length === 2) {
style[split2[0]] = split2[1];
}
}
return style;
};
const _formatStyle = style => {
let styleString = '';
for (const k in style) {
styleString += (styleString.length > 0 ? ' ' : '') + k + ': ' + style[k] + ';';
}
return styleString;
};
const _hash = s => {
let result = 0;
for (let i = 0; i < s.length; i++) {
result += s.codePointAt(i);
}
return result;
};
this.nodeName = nodeName;
this.parentNode = null;
}
}
class HTMLElement extends Node {
constructor(tagName = 'div', attributes = {}, value = '') {
super(null);
class Node extends EventEmitter {
constructor(nodeName = null) {
super();
this.tagName = tagName;
this.attributes = attributes;
this.value = value;
this.childNodes = [];
this.nodeName = nodeName;
this.parentNode = null;
this._innerHTML = '';
}
this[windowSymbol] = null;
}
get attrs() {
return _formatAttributes(this.attributes);
}
set attrs(attrs) {
this.attributes = _parseAttributes(attrs);
}
[setWindowSymbol](window) {
this[windowSymbol] = window;
get children() {
return this.childNodes;
}
set children(children) {
this.childNodes = children;
}
this.emit('window', window);
}
getAttribute(name) {
return this.attributes[name];
}
Node.fromAST = (node, window, parentNode = null) => {
if (node.nodeName === '#text') {
const textNode = new TextNode(node.value);
textNode.parentNode = parentNode;
textNode[setWindowSymbol](window);
return textNode;
} else if (node.nodeName === '#comment') {
const commentNode = new CommentNode(node.value);
commentNode.parentNode = parentNode;
commentNode[setWindowSymbol](window);
return commentNode;
} else {
const {tagName, value} = node;
const attributes = node.attrs && _parseAttributes(node.attrs);
const HTMLElementTemplate = HTML_ELEMENTS[tagName];
const element = HTMLElementTemplate ?
new HTMLElementTemplate(
attributes,
value
)
:
new HTMLElement(
tagName,
attributes,
value
);
element.parentNode = parentNode;
element[setWindowSymbol](window);
if (node.childNodes) {
element.childNodes = node.childNodes.map(childNode => Node.fromAST(childNode, window, element));
}
return element;
}
};
class HTMLElement extends Node {
constructor(tagName = 'div', attributes = {}, value = '') {
super(null);
setAttribute(name, value) {
this.attributes[name] = value;
this.tagName = tagName;
this.attributes = attributes;
this.value = value;
this.childNodes = [];
this.emit('attribute', name, value);
}
this._innerHTML = '';
appendChild(childNode) {
this.childNodes.push(childNode);
childNode.parentNode = this;
}
removeChild(childNode) {
const index = this.childNodes.indexOf(childNode);
if (index !== -1) {
this.childNodes.splice(index, 1);
childNode.parentNode = null;
}
get attrs() {
return _formatAttributes(this.attributes);
}
insertBefore(childNode, nextSibling) {
const index = this.childNodes.indexOf(nextSibling);
if (index !== -1) {
this.childNodes.splice(index, 0, childNode);
childNode.parentNode = this;
}
set attrs(attrs) {
this.attributes = _parseAttributes(attrs);
}
insertAfter(childNode, nextSibling) {
const index = this.childNodes.indexOf(nextSibling);
if (index !== -1) {
this.childNodes.splice(index + 1, 0, childNode);
childNode.parentNode = this;
}
}
get children() {
return this.childNodes;
}
set children(children) {
this.childNodes = children;
}
getAttribute(name) {
return this.attributes[name];
}
setAttribute(name, value) {
this.attributes[name] = value;
this.emit('attribute', name, value);
}
appendChild(childNode) {
this.childNodes.push(childNode);
childNode.parentNode = this;
}
removeChild(childNode) {
const index = this.childNodes.indexOf(childNode);
if (index !== -1) {
this.childNodes.splice(index, 1);
childNode.parentNode = null;
getElementById(id) {
return this.traverse(node => {
if (
(node.getAttribute && node.getAttribute('id') === id) ||
(node.attrs && node.attrs.some(attr => attr.name === 'id' && attr.value === id))
) {
return node;
}
}
insertBefore(childNode, nextSibling) {
const index = this.childNodes.indexOf(nextSibling);
if (index !== -1) {
this.childNodes.splice(index, 0, childNode);
childNode.parentNode = this;
});
}
getElementByClassName(className) {
return this.traverse(node => {
if (
(node.getAttribute && node.getAttribute('class') === className) ||
(node.attrs && node.attrs.some(attr => attr.name === 'class' && attr.value === className))
) {
return node;
}
}
insertAfter(childNode, nextSibling) {
const index = this.childNodes.indexOf(nextSibling);
if (index !== -1) {
this.childNodes.splice(index + 1, 0, childNode);
childNode.parentNode = this;
});
}
getElementByTagName(tagName) {
return this.traverse(node => {
if (node.tagName === tagName) {
return node;
}
});
}
querySelector(selector) {
let match;
if (match = selector.match(/^#(.+)$/)) {
return this.getElementById(match[1]);
} else if (match = selector.match(/^\.(.+)$/)) {
return this.getElementByClassName(match[1]);
} else {
return this.getElementByTagName(selector);
}
getElementById(id) {
return this.traverse(node => {
if (
(node.getAttribute && node.getAttribute('id') === id) ||
(node.attrs && node.attrs.some(attr => attr.name === 'id' && attr.value === id))
) {
return node;
}
});
}
getElementByClassName(className) {
return this.traverse(node => {
if (
(node.getAttribute && node.getAttribute('class') === className) ||
(node.attrs && node.attrs.some(attr => attr.name === 'class' && attr.value === className))
) {
return node;
}
});
}
getElementByTagName(tagName) {
return this.traverse(node => {
if (node.tagName === tagName) {
return node;
}
});
}
querySelector(selector) {
let match;
if (match = selector.match(/^#(.+)$/)) {
return this.getElementById(match[1]);
} else if (match = selector.match(/^\.(.+)$/)) {
return this.getElementByClassName(match[1]);
} else {
return this.getElementByTagName(selector);
}
getElementsById(id) {
const result = [];
this.traverse(node => {
if (
(node.getAttribute && node.getAttribute('id') === id) ||
(node.attrs && node.attrs.some(attr => attr.name === 'id' && attr.value === id))
) {
result.push(node);
}
});
return result;
}
getElementsByClassName(className) {
const result = [];
this.traverse(node => {
if (
(node.getAttribute && node.getAttribute('class') === className) ||
(node.attrs && node.attrs.some(attr => attr.name === 'class' && attr.value === className))
) {
result.push(node);
}
});
return result;
}
getElementsByTagName(tagName) {
const result = [];
this.traverse(node => {
if (node.tagName === tagName) {
result.push(node);
}
});
return result;
}
querySelectorAll(selector) {
let match;
if (match = selector.match(/^#(.+)$/)) {
return this.getElementsById(match[1]);
} else if (match = selector.match(/^\.(.+)$/)) {
return this.getElementsByClassName(match[1]);
} else {
return this.getElementsByTagName(selector);
}
getElementsById(id) {
const result = [];
this.traverse(node => {
if (
(node.getAttribute && node.getAttribute('id') === id) ||
(node.attrs && node.attrs.some(attr => attr.name === 'id' && attr.value === id))
) {
result.push(node);
}
});
return result;
}
getElementsByClassName(className) {
const result = [];
this.traverse(node => {
if (
(node.getAttribute && node.getAttribute('class') === className) ||
(node.attrs && node.attrs.some(attr => attr.name === 'class' && attr.value === className))
) {
result.push(node);
}
});
return result;
}
getElementsByTagName(tagName) {
const result = [];
this.traverse(node => {
if (node.tagName === tagName) {
result.push(node);
}
});
return result;
}
querySelectorAll(selector) {
let match;
if (match = selector.match(/^#(.+)$/)) {
return this.getElementsById(match[1]);
} else if (match = selector.match(/^\.(.+)$/)) {
return this.getElementsByClassName(match[1]);
}
traverse(fn) {
const _recurse = node => {
const result = fn(node);
if (result !== undefined) {
return result;
} else {
return this.getElementsByTagName(selector);
}
}
traverse(fn) {
const _recurse = node => {
const result = fn(node);
if (result !== undefined) {
return result;
} else {
if (node.childNodes) {
for (let i = 0; i < node.childNodes.length; i++) {
const result = _recurse(node.childNodes[i]);
if (result !== undefined) {
return result;
}
if (node.childNodes) {
for (let i = 0; i < node.childNodes.length; i++) {
const result = _recurse(node.childNodes[i]);
if (result !== undefined) {
return result;
}
}
}
};
return _recurse(this);
}
}
};
return _recurse(this);
}
addEventListener() {
this.on.apply(this, arguments);
}
removeEventListener() {
this.removeListener.apply(this, arguments);
}
addEventListener() {
this.on.apply(this, arguments);
}
removeEventListener() {
this.removeListener.apply(this, arguments);
}
get offsetWidth() {
const style = _parseStyle(this.attributes['style'] || '');
const fontFamily = style['font-family'];
if (fontFamily) {
return _hash(fontFamily) * _hash(this.innerHTML);
} else {
return 0;
}
}
set offsetWidth(offsetWidth) {}
get offsetHeight() {
get offsetWidth() {
const style = _parseStyle(this.attributes['style'] || '');
const fontFamily = style['font-family'];
if (fontFamily) {
return _hash(fontFamily) * _hash(this.innerHTML);
} else {
return 0;
}
set offsetHeight(offsetHeight) {}
}
set offsetWidth(offsetWidth) {}
get offsetHeight() {
return 0;
}
set offsetHeight(offsetHeight) {}
get className() {
return this.attributes['class'] || '';
}
set className(className) {
this.attributes['class'] = className;
}
get className() {
return this.attributes['class'] || '';
}
set className(className) {
this.attributes['class'] = className;
}
get style() {
const style = _parseStyle(this.attributes['style'] || '');
Object.defineProperty(style, 'cssText', {
get: () => this.attributes['style'],
set: cssText => {
this.attributes['style'] = cssText;
},
});
return style;
}
set style(style) {
this.attributes['style'] = _formatStyle(style);
}
get style() {
const style = _parseStyle(this.attributes['style'] || '');
Object.defineProperty(style, 'cssText', {
get: () => this.attributes['style'],
set: cssText => {
this.attributes['style'] = cssText;
},
});
return style;
}
set style(style) {
this.attributes['style'] = _formatStyle(style);
}
get innerHTML() {
return parse5.serialize(this);
}
set innerHTML(innerHTML) {
const childNodes = parse5.parseFragment(innerHTML).childNodes.map(childNode => Node.fromAST(childNode, this[windowSymbol], this));
this.childNodes = childNodes;
get innerHTML() {
return parse5.serialize(this);
}
set innerHTML(innerHTML) {
const childNodes = parse5.parseFragment(innerHTML).childNodes.map(childNode => _fromAST(childNode, this[windowSymbol], this));
this.childNodes = childNodes;
_promiseSerial(childNodes.map(childNode => () => _runHtml(childNode, this[windowSymbol])))
.catch(err => {
console.warn(err);
});
_promiseSerial(childNodes.map(childNode => () => _runHtml(childNode, this[windowSymbol])))
.catch(err => {
console.warn(err);
});
this.emit('innerHTML', innerHTML);
}
this.emit('innerHTML', innerHTML);
}
class HTMLAnchorElement extends HTMLElement {
constructor(attributes = {}, value = '') {
super('a', attributes, value);
}
}
class HTMLAnchorElement extends HTMLElement {
constructor(attributes = {}, value = '') {
super('a', attributes, value);
}
get href() {
return this.getAttribute('href') || '';
}
set href(value) {
this.setAttribute('href', value);
}
get href() {
return this.getAttribute('href') || '';
}
class HTMLLoadableElement extends HTMLElement {
constructor(tagName, attributes = {}, value = '') {
super(tagName, attributes, value);
}
set href(value) {
this.setAttribute('href', value);
}
}
class HTMLLoadableElement extends HTMLElement {
constructor(tagName, attributes = {}, value = '') {
super(tagName, attributes, value);
}
get onload() {
return this.listeners('load')[0];
}
set onload(onload) {
if (typeof onload === 'function') {
this.addEventListener('load', onload);
} else {
const listeners = this.listeners('load');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('load', listeners[i]);
}
get onload() {
return this.listeners('load')[0];
}
set onload(onload) {
if (typeof onload === 'function') {
this.addEventListener('load', onload);
} else {
const listeners = this.listeners('load');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('load', listeners[i]);
}
}
}
get onerror() {
return this.listeners('error')[0];
}
set onerror(onerror) {
if (typeof onerror === 'function') {
this.addEventListener('error', onerror);
} else {
const listeners = this.listeners('error');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('error', listeners[i]);
}
get onerror() {
return this.listeners('error')[0];
}
set onerror(onerror) {
if (typeof onerror === 'function') {
this.addEventListener('error', onerror);
} else {
const listeners = this.listeners('error');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('error', listeners[i]);
}
}
}
class HTMLWindowElement extends HTMLLoadableElement {
constructor() {
super('window');
}
}
class HTMLWindowElement extends HTMLLoadableElement {
constructor() {
super('window');
}
postMessage(data) {
this.emit('message', new MessageEvent(data));
}
postMessage(data) {
this.emit('message', new MessageEvent(data));
}
get onmessage() {
return this.listeners('load')[0];
}
set onmessage(onmessage) {
if (typeof onmessage === 'function') {
this.addEventListener('message', onmessage);
} else {
const listeners = this.listeners('message');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('message', listeners[i]);
}
get onmessage() {
return this.listeners('load')[0];
}
set onmessage(onmessage) {
if (typeof onmessage === 'function') {
this.addEventListener('message', onmessage);
} else {
const listeners = this.listeners('message');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('message', listeners[i]);
}
}
}
class HTMLScriptElement extends HTMLLoadableElement {
constructor(attributes = {}, value = '') {
super('script', attributes, value);
}
class HTMLScriptElement extends HTMLLoadableElement {
constructor(attributes = {}, value = '') {
super('script', attributes, value);
this.readyState = null;
this.readyState = null;
this.on('attribute', (name, value) => {
if (name === 'src') {
this.readyState = null;
this.on('attribute', (name, value) => {
if (name === 'src') {
this.readyState = null;
const url = _normalizeUrl(value);
const url = value;
this[windowSymbol].fetch(url)
.then(res => {
if (res.status >= 200 && res.status < 300) {
return res.text();
} else {
return Promise.reject(new Error('script src got invalid status code: ' + res.status + ' : ' + url));
}
})
.then(jsString => {
_runJavascript(jsString, this[windowSymbol], url);
fetch(url)
.then(res => {
if (res.status >= 200 && res.status < 300) {
return res.text();
} else {
return Promise.reject(new Error('script src got invalid status code: ' + res.status + ' : ' + url));
}
})
.then(jsString => {
_runJavascript(jsString, this[windowSymbol], url);
this.readyState = 'complete';
this.readyState = 'complete';
this.emit('load');
})
.catch(err => {
this.readyState = 'complete';
this.emit('load');
})
.catch(err => {
this.readyState = 'complete';
this.emit('error', err);
});
}
});
this.on('innerHTML', innerHTML => {
_runJavascript(innerHTML, this[windowSymbol]);
this.emit('error', err);
});
}
this.readyState = 'complete';
process.nextTick(() => {
this.emit('load');
});
this.on('innerHTML', innerHTML => {
_runJavascript(innerHTML, this[windowSymbol]);
});
}
this.readyState = 'complete';
get src() {
return this.getAttribute('src') || '';
}
set src(value) {
this.setAttribute('src', value);
}
process.nextTick(() => {
this.emit('load');
});
});
}
set innerHTML(innerHTML) {
this.emit('innerHTML', innerHTML);
}
get src() {
return this.getAttribute('src') || '';
run() {
let running = false;
if (this.attributes.src) {
this.src = this.attributes.src;
running = true;
}
set src(value) {
this.setAttribute('src', value);
if (this.childNodes.length > 0) {
this.innerHTML = this.childNodes[0].value;
running = true;
}
return running;
}
}
class HTMLMediaElement extends HTMLLoadableElement {
constructor(tagName = null, attributes = {}, value = '') {
super(tagName, attributes, value);
}
set innerHTML(innerHTML) {
this.emit('innerHTML', innerHTML);
get src() {
this.getAttribute('src');
}
set src(value) {
this.setAttribute('src', value);
}
run() {
if (this.attributes.src) {
this.src = this.attributes.src;
return true;
} else {
return false;
}
}
}
const HTMLImageElement = (() => {
if (typeof nativeImage !== 'undefined') {
return class HTMLImageElement extends nativeImage {
constructor(attributes = {}, value = '') {
super();
EventEmitter.call(this);
this.tagName = 'image'
this.attributes = attributes;
this.value = value;
run() {
let running = false;
if (this.attributes.src) {
this.src = this.attributes.src;
running = true;
this._src = '';
}
if (this.childNodes.length > 0) {
this.innerHTML = this.childNodes[0].value;
running = true;
emit(event, data) {
return EventEmitter.prototype.emit.call(this, event, data);
}
return running;
}
}
class HTMLMediaElement extends HTMLLoadableElement {
constructor(tagName = null, attributes = {}, value = '') {
super(tagName, attributes, value);
}
on(event, cb) {
return EventEmitter.prototype.on.call(this, event, cb);
}
removeListener(event, cb) {
return EventEmitter.prototype.removeListener.call(this, event, cb);
}
get src() {
this.getAttribute('src');
}
set src(value) {
this.setAttribute('src', value);
}
addEventListener(event, cb) {
return HTMLElement.prototype.addEventListener.call(this, event, cb);
}
removeEventListener(event, cb) {
return HTMLElement.prototype.removeEventListener.call(this, event, cb);
}
run() {
if (this.attributes.src) {
this.src = this.attributes.src;
return true;
} else {
return false;
get src() {
return this._src;
}
}
}
const HTMLImageElement = (() => {
if (typeof nativeImage !== 'undefined') {
return class HTMLImageElement extends nativeImage {
constructor(attributes = {}, value = '') {
super();
EventEmitter.call(this);
this.tagName = 'image'
this.attributes = attributes;
this.value = value;
set src(src) {
this._src = src;
this._src = '';
}
const srcError = new Error();
emit(event, data) {
return EventEmitter.prototype.emit.call(this, event, data);
}
on(event, cb) {
return EventEmitter.prototype.on.call(this, event, cb);
}
removeListener(event, cb) {
return EventEmitter.prototype.removeListener.call(this, event, cb);
}
this[windowSymbol].fetch(src)
.then(res => {
if (res.status >= 200 && res.status < 300) {
return res.arrayBuffer();
} else {
return Promise.reject(new Error('img src got invalid status code: ' + res.status + ' : ' + url));
}
})
.then(arrayBuffer => {
if (this.load(arrayBuffer)) {
return Promise.resolve();
} else {
console.warn('failed to decode image src', srcError.stack);
return Promise.reject(new Error('failed to decode image'));
}
})
.then(() => {
this.emit('load');
})
.catch(err => {
this.emit('error', err);
});
}
addEventListener(event, cb) {
return HTMLElement.prototype.addEventListener.call(this, event, cb);
get onload() {
return this.listeners('load')[0];
}
set onload(onload) {
if (typeof onload === 'function') {
this.addEventListener('load', onload);
} else {
const listeners = this.listeners('load');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('load', listeners[i]);
}
}
removeEventListener(event, cb) {
return HTMLElement.prototype.removeEventListener.call(this, event, cb);
}
}
get src() {
return this._src;
get onerror() {
return this.listeners('error')[0];
}
set onerror(onerror) {
if (typeof onerror === 'function') {
this.addEventListener('error', onerror);
} else {
const listeners = this.listeners('error');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('error', listeners[i]);
}
}
set src(src) {
this._src = src;
}
};
} else {
return class HTMLImageElement extends HTMLMediaElement {
constructor() {
super('image');
fetch(src)
.then(res => {
if (res.status >= 200 && res.status < 300) {
return res.arrayBuffer();
} else {
return Promise.reject(new Error('img src got invalid status code: ' + res.status + ' : ' + url));
}
})
.then(arrayBuffer => {
if (this.load(arrayBuffer)) {
return Promise.resolve();
} else {
return Promise.reject(new Error('failed to decode image'));
}
})
.then(() => {
this.on('attribute', (name, value) => {
if (name === 'src') {
process.nextTick(() => { // XXX
this.emit('load');
})
.catch(err => {
this.emit('error', err);
});
}
get onload() {
return this.listeners('load')[0];
}
set onload(onload) {
if (typeof onload === 'function') {
this.addEventListener('load', onload);
} else {
const listeners = this.listeners('load');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('load', listeners[i]);
}
}
}
});
}
get onerror() {
return this.listeners('error')[0];
}
set onerror(onerror) {
if (typeof onerror === 'function') {
this.addEventListener('error', onerror);
} else {
const listeners = this.listeners('error');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('error', listeners[i]);
}
}
}
};
} else {
return class HTMLImageElement extends HTMLMediaElement {
constructor() {
super('image');
get width() {
return 0; // XXX
}
set width(width) {}
get height() {
return 0; // XXX
}
set height(height) {}
};
}
})();
class HTMLAudioElement extends HTMLMediaElement {
constructor(attributes = {}, value = '') {
super('audio', attributes, value);
this.on('attribute', (name, value) => {
if (name === 'src') {
process.nextTick(() => { // XXX
this.emit('load');
});
}
});
}
this.on('attribute', (name, value) => {
if (name === 'src') {
process.nextTick(() => { // XXX
this.emit('load');
this.emit('canplay');
});
}
});
}
get width() {
return 0; // XXX
}
set width(width) {}
get height() {
return 0; // XXX
}
set height(height) {}
};
get oncanplay() {
return this.listeners('canplay')[0];
}
set oncanplay(oncanplay) {
if (typeof oncanplay === 'function') {
this.addEventListener('canplay', oncanplay);
} else {
const listeners = this.listeners('canplay');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('canplay', listeners[i]);
}
}
})();
class HTMLAudioElement extends HTMLMediaElement {
constructor(attributes = {}, value = '') {
super('audio', attributes, value);
}
this.on('attribute', (name, value) => {
if (name === 'src') {
process.nextTick(() => { // XXX
this.emit('load');
this.emit('canplay');
});
}
});
}
get oncanplay() {
return this.listeners('canplay')[0];
}
set oncanplay(oncanplay) {
if (typeof oncanplay === 'function') {
this.addEventListener('canplay', oncanplay);
} else {
const listeners = this.listeners('canplay');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('canplay', listeners[i]);
}
get oncanplaythrough() {
return this.listeners('canplaythrough')[0];
}
set oncanplaythrough(oncanplaythrough) {
if (typeof oncanplaythrough === 'function') {
this.addEventListener('canplaythrough', oncanplaythrough);
} else {
const listeners = this.listeners('canplaythrough');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('canplaythrough', listeners[i]);
}
}
}
}
class HTMLVideoElement extends HTMLMediaElement {
constructor(attributes = {}, value = '') {
super('video', attributes, value);
get oncanplaythrough() {
return this.listeners('canplaythrough')[0];
}
set oncanplaythrough(oncanplaythrough) {
if (typeof oncanplaythrough === 'function') {
this.addEventListener('canplaythrough', oncanplaythrough);
} else {
const listeners = this.listeners('canplaythrough');
for (let i = 0; i < listeners.length; i++) {
this.removeEventListener('canplaythrough', listeners[i]);
}
this.on('attribute', (name, value) => {
if (name === 'src') {
process.nextTick(() => { // XXX
this.emit('load');
});
}
}
});
}
class HTMLVideoElement extends HTMLMediaElement {
constructor(attributes = {}, value = '') {
super('video', attributes, value);
}
class HTMLIframeElement extends HTMLMediaElement {
constructor(attributes = {}, value = '') {
super('iframe', attributes, value);
this.on('attribute', (name, value) => {
if (name === 'src') {
process.nextTick(() => { // XXX
this.emit('load');
const parentWindow = this[windowSymbol];
this.contentWindow = _parseWindow('', parentWindow[optionsSymbol], parentWindow, parentWindow.top);
this.contentDocument = this.contentWindow.document;
this.on('attribute', (name, value) => {
if (name === 'src') {
const url = value;
this[windowSymbol].fetch(url)
.then(res => {
if (res.status >= 200 && res.status < 300) {
return res.text();
} else {
return Promise.reject(new Error('iframe src got invalid status code: ' + res.status + ' : ' + url));
}
})
.then(htmlString => {
const contentDocument = _parseDocument(htmlString, this.contentWindow[optionsSymbol], this.contentWindow);
this.contentDocument = contentDocument;
contentDocument.once('readystatechange', () => {
this.emit('load');
});
})
.catch(err => {
this.emit('error', err);
});
}
});
}
}
});
}
class HTMLIframeElement extends HTMLMediaElement {
constructor(attributes = {}, value = '') {
super('iframe', attributes, value);
}
class HTMLCanvasElement extends HTMLElement {
constructor(attributes = {}, value = '') {
super('canvas', attributes, value);
this.contentWindow = null;
this.contentDocument = null;
this._context = null;
this.on('window', window => {
const contentWindow = _parseWindow('', this[windowSymbol], this[windowSymbol].top);
this.contentWindow = contentWindow;
this.on('attribute', (name, value) => {
if (name === 'width') {
// XXX
} else if (name === 'height') {
// XXX
}
});
}
const {document: contentDocument} = contentWindow;
this.contentDocument = contentDocument;
});
this.on('attribute', (name, value) => {
if (name === 'src') {
const url = _normalizeUrl(value);
get width() {
this.getAttribute('width');
}
set width(value) {
this.setAttribute('width', value);
}
fetch(url)
.then(res => {
if (res.status >= 200 && res.status < 300) {
return res.text();
} else {
return Promise.reject(new Error('iframe src got invalid status code: ' + res.status + ' : ' + url));
}
})
.then(htmlString => {
const contentDocument = _parseDocument(htmlString, this.contentWindow);
this.contentDocument = contentDocument;
get height() {
this.getAttribute('height');
}
set height(value) {
this.setAttribute('height', value);
}
contentDocument.once('readystatechange', () => {
this.emit('load');
});
})
.catch(err => {
this.emit('error', err);
});
}
});
getContext(contextType) {
if (this._context === null) {
if (contextType === '2d') {
this._context = new CanvasRenderingContext2D(this.width, this.height);
} else if (contextType === 'webgl') {
this._context = new WebGLContext();
}
}
return this._context;
}
class HTMLCanvasElement extends HTMLElement {
constructor(attributes = {}, value = '') {
super('canvas', attributes, value);
}
class TextNode extends Node {
constructor(value) {
super('#text');
this._context = null;
this.value = value;
}
}
class CommentNode extends Node {
constructor(value) {
super('#comment');
this.on('attribute', (name, value) => {
if (name === 'width') {
// XXX
} else if (name === 'height') {
// XXX
}
});
}
this.value = value;
}
}
get width() {
this.getAttribute('width');
const _fromAST = (node, window, parentNode = null) => {
if (node.nodeName === '#text') {
const textNode = new window[htmlElementsSymbol].TextNode(node.value);
textNode.parentNode = parentNode;
return textNode;
} else if (node.nodeName === '#comment') {
const commentNode = new window[htmlElementsSymbol].CommentNode(node.value);
commentNode.parentNode = parentNode;
return commentNode;
} else {
const {tagName, value} = node;
const attributes = node.attrs && _parseAttributes(node.attrs);
const HTMLElementTemplate = window[htmlTagsSymbol][tagName];
const element = HTMLElementTemplate ?
new HTMLElementTemplate(
attributes,
value
)
:
new window[htmlElementsSymbol].HTMLElement(
tagName,
attributes,
value
);
element.parentNode = parentNode;
if (node.childNodes) {
element.childNodes = node.childNodes.map(childNode => _fromAST(childNode, window, element));
}
set width(value) {
this.setAttribute('width', value);
return element;
}
};
const _parseAttributes = attrs => {
const result = {};
for (let i = 0; i < attrs.length; i++) {
const attr = attrs[i];
result[attr.name] = attr.value;
}
return result;
};
const _formatAttributes = attributes => {
const result = [];
for (const name in attributes) {
const value = attributes[name];
result.push({
name,
value,
});
}
return result;
};
const _parseStyle = styleString => {
const style = {};
const split = styleString.split(/;\s*/);
for (let i = 0; i < split.length; i++) {
const split2 = split[i].split(/:\s*/);
if (split2.length === 2) {
style[split2[0]] = split2[1];
}
}
return style;
};
const _formatStyle = style => {
let styleString = '';
for (const k in style) {
styleString += (styleString.length > 0 ? ' ' : '') + k + ': ' + style[k] + ';';
}
return styleString;
};
const _hash = s => {
let result = 0;
for (let i = 0; i < s.length; i++) {
result += s.codePointAt(i);
}
return result;
};
const _promiseSerial = async promiseFns => {
for (let i = 0; i < promiseFns.length; i++) {
await promiseFns[i]();
}
};
const _loadPromise = el => new Promise((accept, reject) => {
el.on('load', () => {
accept();
});
el.on('error', err => {
reject(err);
});
});
const _runHtml = async (element, window) => {
if (element instanceof HTMLElement) {
const scripts = element.querySelectorAll('script');
for (let i = 0; i < scripts.length; i++) {
const script = scripts[i];
if (script.run()) {
if (script.attributes.async) {
_loadPromise(script)
.catch(err => {
console.warn(err);
});
} else {
try {
await _loadPromise(script);
} catch(err) {
console.warn(err);
}
}
}
}
get height() {
this.getAttribute('height');
const images = element.querySelectorAll('image');
for (let i = 0; i < images.length; i++) {
const image = images[i];
if (image.run()) {
await _loadPromise(image);
}
}
set height(value) {
this.setAttribute('height', value);
}
getContext(contextType) {
if (this._context === null) {
if (contextType === '2d') {
this._context = new CanvasRenderingContext2D(this.width, this.height);
} else if (contextType === 'webgl') {
this._context = new WebGLContext();
}
const audios = element.querySelectorAll('audio');
for (let i = 0; i < audios.length; i++) {
const audio = audios[i];
if (audio.run()) {
await _loadPromise(audioEl);
}
return this._context;
}
}
class TextNode extends Node {
constructor(value) {
super('#text');
this.value = value;
const videos = element.querySelectorAll('video');
for (let i = 0; i < videos.length; i++) {
const video = videos[i];
if (video.run()) {
await _loadPromise(videoEl);
}
}
}
class CommentNode extends Node {
constructor(value) {
super('#comment');
};
const _runJavascript = (jsString, window, filename = 'script') => {
try {
vm.runInContext(jsString, window, {
filename,
});
} catch (err) {
console.warn(err);
}
};
const _makeWindow = (options = {}, parent = null, top = null) => {
const _normalizeUrl = src => new URL(src, options.baseUrl).href;
this.value = value;
const window = new HTMLWindowElement();
window.window = window;
window.self = window;
window.parent = parent || window;
window.top = top || window;
window.innerWidth = 1280;
window.innerHeight = 1024;
window.console = console;
window.setTimeout = setTimeout;
window.clearTimeout = clearTimeout;
window.setInterval = setInterval;
window.clearInterval = clearInterval;
window.Date = Date;
window.performance = performance;
window.location = url.parse(options.baseUrl);
let vrDisplays = [];
window.navigator = {
userAgent: 'exokit',
setVRMode: vrMode => {
for (let i = 0; i < vrDisplays.length; i++) {
vrDisplays[i].destroy();
}
if (vrMode === 'vr') {
vrDisplays = [new VRDisplay(window)];
} else if (vrMode === 'ar') {
vrDisplays = [new ARDisplay(window)];
}
},
getVRDisplays: () => vrDisplays,
};
window.localStorage = new LocalStorage(path.join(options.dataPath, '.localStorage'));
window.document = null;
window.URL = URL;
window[htmlElementsSymbol] = {
Node: (Old => class Node extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(Node),
HTMLElement: (Old => class HTMLElement extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(HTMLElement),
HTMLAnchorElement: (Old => class HTMLAnchorElement extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(HTMLAnchorElement),
HTMLScriptElement: (Old => class HTMLScriptElement extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(HTMLScriptElement),
HTMLImageElement: (Old => class HTMLImageElement extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(HTMLImageElement),
HTMLAudioElement: (Old => class HTMLAudioElement extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(HTMLAudioElement),
HTMLVideoElement: (Old => class HTMLVideoElement extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(HTMLVideoElement),
HTMLIframeElement: (Old => class HTMLIframeElement extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(HTMLIframeElement),
HTMLCanvasElement: (Old => class HTMLCanvasElement extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(HTMLCanvasElement),
TextNode: (Old => class TextNode extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(TextNode),
CommentNode: (Old => class CommentNode extends Old { constructor() { super(...arguments); this[windowSymbol] = window; } })(CommentNode),
};
window[htmlTagsSymbol] = {
a: window[htmlElementsSymbol].HTMLAnchorElement,
script: window[htmlElementsSymbol].HTMLScriptElement,
img: window[htmlElementsSymbol].HTMLImageElement,
audio: window[htmlElementsSymbol].HTMLAudioElement,
video: window[htmlElementsSymbol].HTMLVideoElement,
iframe: window[htmlElementsSymbol].HTMLIframeElement,
canvas: window[htmlElementsSymbol].HTMLCanvasElement,
};
window[optionsSymbol] = options;
window.Image = window[htmlElementsSymbol].HTMLImageElement;
window.HTMLElement = window[htmlElementsSymbol].HTMLElement;
window.HTMLAnchorElement = window[htmlElementsSymbol].HTMLAnchorElement;
window.HTMLScriptElement = window[htmlElementsSymbol].HTMLScriptElement;
window.HTMLImageElement = window[htmlElementsSymbol].HTMLImageElement;
window.HTMLAudioElement = window[htmlElementsSymbol].HTMLAudioElement;
window.HTMLVideoElement = window[htmlElementsSymbol].HTMLVideoElement;
window.HTMLIframeElement = window[htmlElementsSymbol].HTMLIframeElement;
window.HTMLCanvasElement = window[htmlElementsSymbol].HTMLCanvasElement;
window.ImageData = ImageData;
window.ImageBitmap = ImageBitmap;
window.Path2D = Path2D;
window.CanvasRenderingContext2D = CanvasRenderingContext2D;
window.VRFrameData = VRFrameData;
window.btoa = s => new Buffer(s, 'binary').toString('base64');
window.atob = s => new Buffer(s, 'base64').toString('binary');
window.fetch = (url, options) => {
const blob = urls.get(url);
if (blob) {
return Promise.resolve(new Response(blob));
} else {
return fetch(_normalizeUrl(url), options);
}
}
const HTML_ELEMENTS = {
a: HTMLAnchorElement,
script: HTMLScriptElement,
img: HTMLImageElement,
audio: HTMLAudioElement,
video: HTMLVideoElement,
iframe: HTMLIframeElement,
canvas: HTMLCanvasElement,
};
class Worker extends WindowWorker {
window.XMLHttpRequest = XMLHttpRequest;
window.WebSocket = WebSocket;
window.Worker = class Worker extends WindowWorker {
constructor(src, options = {}) {
options.baseUrl = options.baseUrl || baseUrl;
options.baseUrl = baseUrl;

@@ -1126,217 +1250,84 @@ if (src instanceof Blob) {

}
}
};
window.Blob = Blob;
window.AudioContext = AudioContext;
window.Path2D = Path2D;
window.createImageBitmap = image => Promise.resolve(ImageBitmap.createImageBitmap(image));
const rafCbs = [];
const _makeWindow = (parent, top) => {
const window = new HTMLWindowElement();
window.window = window;
window.self = window;
window.parent = parent || window;
window.top = top || window;
window.innerWidth = 1280;
window.innerHeight = 1024;
window.console = console;
window.setTimeout = setTimeout;
window.clearTimeout = clearTimeout;
window.setInterval = setInterval;
window.clearInterval = clearInterval;
window.Date = Date;
window.performance = performance;
window.location = url.parse(baseUrl);
let vrDisplays = [];
window.navigator = {
userAgent: 'exokit',
setVRMode: vrMode => {
for (let i = 0; i < vrDisplays.length; i++) {
vrDisplays[i].destroy();
}
if (vrMode === 'vr') {
vrDisplays = [new VRDisplay(window)];
} else if (vrMode === 'ar') {
vrDisplays = [new ARDisplay(window)];
}
},
getVRDisplays: () => vrDisplays,
};
window.localStorage = new LocalStorage(path.join(options.dataPath, '.localStorage'));
window.document = null;
window.URL = URL;
window.Image = HTMLImageElement;
window.HTMLScriptElement = HTMLScriptElement;
window.HTMLImageElement = HTMLImageElement;
window.HTMLAudioElement = HTMLAudioElement;
window.HTMLVideoElement = HTMLVideoElement;
window.HTMLIframeElement = HTMLIframeElement;
window.HTMLCanvasElement = HTMLCanvasElement;
window.ImageData = ImageData;
window.ImageBitmap = ImageBitmap;
window.Path2D = Path2D;
window.CanvasRenderingContext2D = CanvasRenderingContext2D;
window.VRFrameData = VRFrameData;
window.btoa = s => new Buffer(s, 'binary').toString('base64');
window.atob = s => new Buffer(s, 'base64').toString('binary');
window.fetch = (url, options) => {
const blob = urls.get(url);
if (blob) {
return Promise.resolve(new Response(blob));
} else {
return fetch(_normalizeUrl(url), options);
}
};
window.XMLHttpRequest = XMLHttpRequest;
window.WebSocket = WebSocket;
window.Worker = Worker;
window.Blob = Blob;
window.AudioContext = AudioContext;
window.Path2D = Path2D;
window.createImageBitmap = image => Promise.resolve(ImageBitmap.createImageBitmap(image));
window.requestAnimationFrame = fn => {
rafCbs.push(fn);
};
window.clearAnimationFrame = fn => {
const index = rafCbs.indexOf(fn);
if (index !== -1) {
rafCbs.splice(index, 1);
}
};
window.tickAnimationFrame = () => {
const localRafCbs = rafCbs.slice();
rafCbs.length = 0;
for (let i = 0; i < localRafCbs.length; i++) {
localRafCbs[i]();
}
};
window.alignFrame = (viewMatrix, projectionMatrix) => {
window.emit('alignframe', viewMatrix, projectionMatrix);
};
vm.createContext(window);
return window;
window.requestAnimationFrame = fn => {
rafCbs.push(fn);
};
const _parseDocument = (s, window) => {
const document = Node.fromAST(parse5.parse(s), window);
const html = document.childNodes.find(element => element.tagName === 'html');
const head = html.childNodes.find(element => element.tagName === 'head');
const body = html.childNodes.find(element => element.tagName === 'body');
document.documentElement = document;
document.readyState = null;
document.head = head;
document.body = body;
document.location = url.parse(baseUrl);
document.createElement = tagName => {
const HTMLElementTemplate = HTML_ELEMENTS[tagName];
const el = HTMLElementTemplate ? new HTMLElementTemplate() : new HTMLElement(tagName);
el[setWindowSymbol](window);
return el;
};
document.createElementNS = (namespace, tagName) => document.createElement(tagName);
document.createDocumentFragment = () => document.createElement();
document.createTextNode = text => new TextNode(text);
document.createComment = comment => new CommentNode(comment);
document.styleSheets = [];
document.write = htmlString => {
const childNodes = parse5.parseFragment(htmlString).childNodes.map(childNode => Node.fromAST(childNode, window, this));
for (let i = 0; i < childNodes.length; i++) {
document.body.appendChild(childNodes[i]);
}
};
window.document = document;
process.nextTick(async () => {
document.readyState = 'complete';
try {
await _runHtml(document, window);
} catch(err) {
console.warn(err);
}
document.emit('readystatechange');
});
return document;
window.clearAnimationFrame = fn => {
const index = rafCbs.indexOf(fn);
if (index !== -1) {
rafCbs.splice(index, 1);
}
};
const _parseWindow = (s, parent, top) => {
const window = _makeWindow(parent, top);
const document = _parseDocument(s, window);
window.document = document;
window.tickAnimationFrame = () => {
const localRafCbs = rafCbs.slice();
rafCbs.length = 0;
for (let i = 0; i < localRafCbs.length; i++) {
localRafCbs[i]();
}
};
window.alignFrame = (viewMatrix, projectionMatrix) => {
window.emit('alignframe', viewMatrix, projectionMatrix);
};
vm.createContext(window);
return window;
};
const _parseDocument = (s, options, window) => {
const document = _fromAST(parse5.parse(s), window);
const html = document.childNodes.find(element => element.tagName === 'html');
const head = html.childNodes.find(element => element.tagName === 'head');
const body = html.childNodes.find(element => element.tagName === 'body');
return window;
document.documentElement = document;
document.readyState = null;
document.head = head;
document.body = body;
document.location = url.parse(options.baseUrl);
document.createElement = tagName => {
const HTMLElementTemplate = window[htmlTagsSymbol][tagName];
return HTMLElementTemplate ? new HTMLElementTemplate() : new window[htmlElementsSymbol].HTMLElement(tagName);
};
const window = _parseWindow(s);
const {document} = window;
const _promiseSerial = async promiseFns => {
for (let i = 0; i < promiseFns.length; i++) {
await promiseFns[i]();
document.createElementNS = (namespace, tagName) => document.createElement(tagName);
document.createDocumentFragment = () => document.createElement();
document.createTextNode = text => new TextNode(text);
document.createComment = comment => new CommentNode(comment);
document.styleSheets = [];
document.write = htmlString => {
const childNodes = parse5.parseFragment(htmlString).childNodes.map(childNode => _fromAST(childNode, window, this));
for (let i = 0; i < childNodes.length; i++) {
document.body.appendChild(childNodes[i]);
}
};
const _loadPromise = el => new Promise((accept, reject) => {
el.on('load', () => {
accept();
});
el.on('error', err => {
reject(err);
});
});
const _runHtml = async (element, window) => {
if (element instanceof HTMLElement) {
const scripts = element.querySelectorAll('script');
for (let i = 0; i < scripts.length; i++) {
const script = scripts[i];
if (script.run()) {
if (script.attributes.async) {
_loadPromise(script)
.catch(err => {
console.warn(err);
});
} else {
try {
await _loadPromise(script);
} catch(err) {
console.warn(err);
}
}
}
}
window.document = document;
const images = element.querySelectorAll('image');
for (let i = 0; i < images.length; i++) {
const image = images[i];
if (image.run()) {
await _loadPromise(image);
}
}
process.nextTick(async () => {
document.readyState = 'complete';
const audios = element.querySelectorAll('audio');
for (let i = 0; i < audios.length; i++) {
const audio = audios[i];
if (audio.run()) {
await _loadPromise(audioEl);
}
}
const videos = element.querySelectorAll('video');
for (let i = 0; i < videos.length; i++) {
const video = videos[i];
if (video.run()) {
await _loadPromise(videoEl);
}
}
}
};
const _runJavascript = (jsString, window, filename = 'script') => {
try {
vm.runInContext(jsString, window, {
filename,
});
} catch (err) {
await _runHtml(document, window);
} catch(err) {
console.warn(err);
}
};
document.emit('readystatechange');
});
return document;
};
const _parseWindow = (s, options, parent, top) => {
const window = _makeWindow(options, parent, top);
const document = _parseDocument(s, options, window);
window.document = document;
return window;
};
const exokit = (s = '', options = {}) => {
options.baseUrl = options.baseUrl || 'http://127.0.0.1';
options.dataPath = options.dataPath || __dirname;
return _parseWindow(s, options);
};
exokit.fetch = src => fetch(src)

@@ -1353,3 +1344,3 @@ .then(res => {

return exokit(htmlString, {
url: (parsedUrl.protocol || 'http:') + '//' + (parsedUrl.host || '127.0.0.1'),
baseUrl: (parsedUrl.protocol || 'http:') + '//' + (parsedUrl.host || '127.0.0.1'),
});

@@ -1356,0 +1347,0 @@ });

{
"name": "exokit",
"version": "0.0.12",
"version": "0.0.13",
"main": "index.js",

@@ -5,0 +5,0 @@ "dependencies": {

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