Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions cjs/hyper/render.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use strict';
const WeakMap = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/weakmap'));
const unique = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/template-literal'));

const {OWNER_SVG_ELEMENT} = require('../shared/constants.js');
const {Tagger} = require('../objects/Updates.js');
const {reArguments} = require('../shared/utils.js');

// a weak collection of contexts that
// are already known to hyperHTML
Expand All @@ -13,12 +13,13 @@ const bewitched = new WeakMap;
// the main tag function in charge of fully upgrading
// or simply updating, contexts used as hyperHTML targets.
// The `this` context is either a regular DOM node or a fragment.
function render(template) {
function render() {
const wicked = bewitched.get(this);
if (wicked && wicked.template === unique(template)) {
wicked.tagger.apply(null, arguments);
const args = reArguments.apply(null, arguments);
if (wicked && wicked.template === args[0]) {
wicked.tagger.apply(null, args);
} else {
upgrade.apply(this, arguments);
upgrade.apply(this, args);
}
return this;
}
Expand All @@ -27,13 +28,13 @@ function render(template) {
// parse it once, if unknown, to map all interpolations
// as single DOM callbacks, relate such template
// to the current context, and render it after cleaning the context up
function upgrade(template) {
template = unique(template);
function upgrade() {
const args = reArguments.apply(null, arguments);
const type = OWNER_SVG_ELEMENT in this ? 'svg' : 'html';
const tagger = new Tagger(type);
bewitched.set(this, {tagger, template});
bewitched.set(this, {tagger, template: args[0]});
this.textContent = '';
this.appendChild(tagger.apply(null, arguments));
this.appendChild(tagger.apply(null, args));
}

Object.defineProperty(exports, '__esModule', {value: true}).default = render;
20 changes: 10 additions & 10 deletions cjs/hyper/wire.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';
const WeakMap = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/weakmap'));
const unique = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/template-literal'));
const trim = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/trim'));

const {ELEMENT_NODE} = require('../shared/constants.js');
const Wire = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('../classes/Wire.js'));

const {Tagger} = require('../objects/Updates.js');
const {reArguments} = require('../shared/utils.js');

// all wires used per each context
const wires = new WeakMap;
Expand All @@ -17,7 +17,7 @@ const wires = new WeakMap;
// This provides the ability to have a unique DOM structure
// related to a unique JS object through a reusable template literal.
// A wire can specify a type, as svg or html, and also an id
// via html:id or :id convention. Such :id allows same JS objects
// via the html:id or :id convention. Such :id allows same JS objects
// to be associated to different DOM structures accordingly with
// the used template literal without losing previously rendered parts.
const wire = (obj, type) => obj == null ?
Expand All @@ -32,22 +32,22 @@ const wire = (obj, type) => obj == null ?
// in charge of updating its content like a bound element would do.
const content = type => {
let wire, tagger, template;
return function (statics) {
statics = unique(statics);
if (template !== statics) {
template = statics;
return function () {
const args = reArguments.apply(null, arguments);
if (template !== args[0]) {
template = args[0];
tagger = new Tagger(type);
wire = wireContent(tagger.apply(tagger, arguments));
wire = wireContent(tagger.apply(tagger, args));
} else {
tagger.apply(tagger, arguments);
tagger.apply(tagger, args);
}
return wire;
};
};

// wires are weakly created through objects.
// Each object can have multiple wires associated
// and this is thanks to the type + :id feature.
// thanks to the type + :id feature.
const weakly = (obj, type) => {
const i = type.indexOf(':');
let wire = wires.get(obj);
Expand All @@ -61,7 +61,7 @@ const weakly = (obj, type) => {
return wire[id] || (wire[id] = content(type));
};

// a document fragment loses its nodes as soon
// A document fragment loses its nodes as soon
// as it's appended into another node.
// This would easily lose wired content
// so that on a second render call, the parent
Expand Down
160 changes: 160 additions & 0 deletions cjs/objects/Basic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
'use strict';
const CustomEvent = (m => m.__esModule ? m.default : m)(require('@ungap/custom-event'));
const WeakSet = (m => m.__esModule ? m.default : m)(require('@ungap/essential-weakset'));
const disconnected = (m => m.__esModule ? m.default : m)(require('disconnected'));
const domdiff = (m => m.__esModule ? m.default : m)(require('domdiff'));
const domtagger = (m => m.__esModule ? m.default : m)(require('domtagger'));
const hyperStyle = (m => m.__esModule ? m.default : m)(require('hyperhtml-style'));

const CONNECTED = 'connected';
const DISCONNECTED = 'dis' + CONNECTED;

const slice = [].slice;
const observe = disconnected({Event: CustomEvent, WeakSet});

exports.Tagger = Tagger;
exports.observe = observe;

// list of attributes that should not be directly assigned
const readOnly = /^(?:form|list)$/i;

function Tagger(type) {
this.type = type;
return domtagger(this);
}

Tagger.prototype = {

attribute(node, name, original) {
const isSVG = 'ownerSVGElement' in node;
let oldValue;
if (name === 'style')
return hyperStyle(node);
else if ('on' === name.slice(0, 2)) {
let type = name.slice(2);
if (type === CONNECTED || type === DISCONNECTED)
observe(node);
else if (name.toLowerCase()
in node)
type = type.toLowerCase();
return newValue => {
if (oldValue !== newValue) {
if (oldValue)
node.removeEventListener(type, oldValue, false);
oldValue = newValue;
if (newValue)
node.addEventListener(type, newValue, false);
}
};
}
else if (
name === 'data' ||
(!isSVG && name in node && !readOnly.test(name))
) {
return newValue => {
if (oldValue !== newValue) {
oldValue = newValue;
if (node[name] !== newValue) {
node[name] = newValue;
if (newValue == null) {
node.removeAttribute(name);
}
}
}
};
}
else {
let owner = false;
const attribute = original.cloneNode(true);
return newValue => {
if (oldValue !== newValue) {
oldValue = newValue;
if (attribute.value !== newValue) {
if (newValue == null) {
if (owner) {
owner = false;
node.removeAttributeNode(attribute);
}
attribute.value = newValue;
} else {
attribute.value = newValue;
if (!owner) {
owner = true;
node.setAttributeNode(attribute);
}
}
}
}
};
}
},

any(node, childNodes) {
const diffOptions = {before: node};
let fastPath = false;
let oldValue;
const anyContent = value => {
switch (typeof value) {
case 'string':
case 'number':
case 'boolean':
if (fastPath) {
if (oldValue !== value) {
oldValue = value;
childNodes[0].textContent = value;
}
} else {
fastPath = true;
oldValue = value;
childNodes = domdiff(
node.parentNode,
childNodes,
[node.ownerDocument.createTextNode(value)],
diffOptions
);
}
break;
case 'function':
anyContent(value(node));
break;
case 'object':
case 'undefined':
if (value == null) {
fastPath = false;
childNodes = domdiff(
node.parentNode,
childNodes,
[],
diffOptions
);
break;
}
default:
fastPath = false;
oldValue = value;
if ('ELEMENT_NODE' in value) {
childNodes = domdiff(
node.parentNode,
childNodes,
value.nodeType === 11 ?
slice.call(value.childNodes) :
[value],
diffOptions
);
}
break;
}
};
return anyContent;
},

text(node) {
let oldValue;
return value => {
if (oldValue !== value) {
oldValue = value;
node.textContent = value == null ? '' : value;
}
};
}
};
7 changes: 3 additions & 4 deletions cjs/objects/Updates.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ const createContent = (m => m.__esModule ? /* istanbul ignore next */ m.default
const disconnected = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('disconnected'));
const domdiff = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('domdiff'));
const domtagger = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('domtagger'));
const hyperStyle = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('hyperhtml-style'));

const {
CONNECTED, DISCONNECTED, DOCUMENT_FRAGMENT_NODE, OWNER_SVG_ELEMENT
} = require('../shared/constants.js');

const Component = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('../classes/Component.js'));
const Wire = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('../classes/Wire.js'));
const Style = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('./Style.js'));
const Intent = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('./Intent.js'));
const { slice, text } = require('../shared/utils.js');

Expand Down Expand Up @@ -89,9 +89,8 @@ Tagger.prototype = {
let oldValue;
// if the attribute is the style one
// handle it differently from others
if (name === 'style') {
return Style(node, original, isSVG);
}
if (name === 'style')
return hyperStyle(node, original, isSVG);
// the name is an event one,
// add/remove event listeners accordingly
else if (/^on/.test(name)) {
Expand Down
79 changes: 79 additions & 0 deletions cjs/objects/hyperstyle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
'use strict';
var hyperStyle = (function (){'use strict';
// from https://github.com/developit/preact/blob/33fc697ac11762a1cb6e71e9847670d047af7ce5/src/varants.js
var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
var hyphen = /([^A-Z])([A-Z]+)/g;
return function hyperStyle(node) {
return 'ownerSVGElement' in node ? svg(node) : update(node.style, false);
};
function ized($0, $1, $2) {
return $1 + '-' + $2.toLowerCase();
}
function svg(node) {
node.setAttribute('style', '');
return update(node.getAttributeNode('style'), true);
}
function toStyle(object) {
var key, css = [];
for (key in object) {
css.push(key.replace(hyphen, ized), ':', object[key], ';');
}
return css.join('');
}
function update(style, isSVG) {
var oldType, oldValue;
return function (newValue) {
var info, key, styleValue, value;
switch (typeof newValue) {
case 'object':
if (newValue) {
if (oldType === 'object') {
if (!isSVG) {
if (oldValue !== newValue) {
for (key in oldValue) {
if (!(key in newValue)) {
style[key] = '';
}
}
}
}
} else {
if (isSVG)
style.value = '';
else
style.cssText = '';
}
info = isSVG ? {} : style;
for (key in newValue) {
value = newValue[key];
styleValue = typeof value === 'number' &&
!IS_NON_DIMENSIONAL.test(key) ?
(value + 'px') : value;
if (!isSVG && /^--/.test(key))
info.setProperty(key, styleValue);
else
info[key] = styleValue;
}
oldType = 'object';
if (isSVG)
style.value = toStyle((oldValue = info));
else
oldValue = newValue;
break;
}
default:
if (oldValue != newValue) {
oldType = 'string';
oldValue = newValue;
if (isSVG)
style.value = newValue || '';
else
style.cssText = newValue || '';
}
break;
}
};
}
}());

Object.defineProperty(exports, '__esModule', {value: true}).default = hyperStyle;
Loading