From 049ca612abff69ae0bfc3f417d82e0d2b776d063 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 17 May 2017 15:25:53 +0200 Subject: [PATCH 1/9] fix whitespace creation --- index.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index ae78ad4..4e3083d 100644 --- a/index.js +++ b/index.js @@ -131,12 +131,23 @@ function belCreateElement (tag, props, children) { } if (typeof node === 'string') { - if (/^[\n\r\s]+$/.test(node)) continue - if (el.lastChild && el.lastChild.nodeName === '#text') { - el.lastChild.nodeValue += node + // - if empty space, skip + // - if last node was a text node + // - if current node is a newline, push a space + // - else push the node value + // - else create a text node with the new text + if (/^[\r\s]+$/.test(node)) { continue + } else if (el.lastChild && el.lastChild.nodeName === '#text') { + if (/^[\n]+$/.test(node)) { + el.lastChild.nodeValue += ' ' + } else { + el.lastChild.nodeValue += node + } + continue + } else { + node = document.createTextNode(node) } - node = document.createTextNode(node) } if (node && node.nodeType) { From 27312afe5d191bb0b4260b7706dea0b9aa7280a8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 19 May 2017 02:58:23 +0200 Subject: [PATCH 2/9] fix browser rendering --- index.js | 43 +++++++++++++++++++++++++------------------ package.json | 6 +++--- test/api.js | 20 ++++++++++++++------ 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/index.js b/index.js index 4e3083d..ae20a17 100644 --- a/index.js +++ b/index.js @@ -115,7 +115,8 @@ function belCreateElement (tag, props, children) { function appendChild (childs) { if (!Array.isArray(childs)) return - for (var i = 0; i < childs.length; i++) { + var hadText = false + for (var i = 0, len = childs.length; i < len; i++) { var node = childs[i] if (Array.isArray(node)) { appendChild(node) @@ -130,27 +131,33 @@ function belCreateElement (tag, props, children) { node = node.toString() } + var lastChild = el.childNodes[el.childNodes.length - 1] if (typeof node === 'string') { - // - if empty space, skip - // - if last node was a text node - // - if current node is a newline, push a space - // - else push the node value - // - else create a text node with the new text - if (/^[\r\s]+$/.test(node)) { - continue - } else if (el.lastChild && el.lastChild.nodeName === '#text') { - if (/^[\n]+$/.test(node)) { - el.lastChild.nodeValue += ' ' - } else { - el.lastChild.nodeValue += node - } - continue + hadText = true + if (lastChild && lastChild.nodeName === '#text') { + lastChild.nodeValue += node } else { node = document.createTextNode(node) + el.appendChild(node) + lastChild = node + } + if (i === len - 1) { + hadText = false + var value = lastChild.nodeValue + .replace(/^\n[\s]+/, '') + .replace(/\n[\s]+$/, '') + if (value) lastChild.nodeValue = value + else el.removeChild(lastChild) + } + } else if (node && node.nodeType) { + if (hadText) { + hadText = false + var val = lastChild.nodeValue + .replace(/^\n[\s]+/, '') + .replace(/\n[\s]+$/, '') + if (val) lastChild.nodeValue = val + else el.removeChild(lastChild) } - } - - if (node && node.nodeType) { el.appendChild(node) } } diff --git a/package.json b/package.json index 4dc806a..14004ca 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "start": "wzrd test/index.js:bundle.js", - "test": "standard && node test/server.js && browserify test/index.js | testron", + "test": "standard && node test/server.js && browserify test/index.js | tape-run", "bench": "wzrd bench/index.js:bundle.js" }, "repository": { @@ -30,7 +30,8 @@ "dependencies": { "global": "^4.3.0", "hyperx": "^2.3.0", - "on-load": "^3.2.0" + "on-load": "^3.2.0", + "tape-run": "^3.0.0" }, "devDependencies": { "browser-process-hrtime": "^0.1.2", @@ -39,7 +40,6 @@ "morphdom": "^2.1.1", "standard": "^9.0.2", "tape": "^4.6.0", - "testron": "^1.2.0", "wzrd": "^1.4.0" } } diff --git a/test/api.js b/test/api.js index 8e4f3cf..a37638d 100644 --- a/test/api.js +++ b/test/api.js @@ -3,18 +3,26 @@ var bel = require('../') test('creates an element', function (t) { t.plan(3) - var button = bel`` - var result = bel`` + var button = bel` + + ` + + var result = bel` + + ` + function onselected (result) { t.equal(result, 'success') t.end() } + t.equal(result.tagName, 'UL') t.equal(result.querySelector('button').textContent, 'click me') + button.click() }) From 6d36508bc89bebc7e57bd90fb2cb01e53e2b6f93 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 19 May 2017 12:56:23 +0200 Subject: [PATCH 3/9] use pelo --- browser.js | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++ index.js | 173 +-------------------------------------------------- package.json | 3 +- 3 files changed, 174 insertions(+), 173 deletions(-) create mode 100644 browser.js diff --git a/browser.js b/browser.js new file mode 100644 index 0000000..265dd9c --- /dev/null +++ b/browser.js @@ -0,0 +1,171 @@ +var hyperx = require('hyperx') +var onload = require('on-load') + +var SVGNS = 'http://www.w3.org/2000/svg' +var XLINKNS = 'http://www.w3.org/1999/xlink' + +var BOOL_PROPS = { + autofocus: 1, + checked: 1, + defaultchecked: 1, + disabled: 1, + formnovalidate: 1, + indeterminate: 1, + readonly: 1, + required: 1, + selected: 1, + willvalidate: 1 +} +var COMMENT_TAG = '!--' +var SVG_TAGS = [ + 'svg', + 'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateColor', + 'animateMotion', 'animateTransform', 'circle', 'clipPath', 'color-profile', + 'cursor', 'defs', 'desc', 'ellipse', 'feBlend', 'feColorMatrix', + 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', + 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', + 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', + 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', + 'feSpotLight', 'feTile', 'feTurbulence', 'filter', 'font', 'font-face', + 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', + 'foreignObject', 'g', 'glyph', 'glyphRef', 'hkern', 'image', 'line', + 'linearGradient', 'marker', 'mask', 'metadata', 'missing-glyph', 'mpath', + 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', + 'set', 'stop', 'switch', 'symbol', 'text', 'textPath', 'title', 'tref', + 'tspan', 'use', 'view', 'vkern' +] + +function belCreateElement (tag, props, children) { + var el + + // If an svg tag, it needs a namespace + if (SVG_TAGS.indexOf(tag) !== -1) { + props.namespace = SVGNS + } + + // If we are using a namespace + var ns = false + if (props.namespace) { + ns = props.namespace + delete props.namespace + } + + // Create the element + if (ns) { + el = document.createElementNS(ns, tag) + } else if (tag === COMMENT_TAG) { + return document.createComment(props.comment) + } else { + el = document.createElement(tag) + } + + // If adding onload events + if (props.onload || props.onunload) { + var load = props.onload || function () {} + var unload = props.onunload || function () {} + onload(el, function belOnload () { + load(el) + }, function belOnunload () { + unload(el) + }, + // We have to use non-standard `caller` to find who invokes `belCreateElement` + belCreateElement.caller.caller.caller) + delete props.onload + delete props.onunload + } + + // Create the properties + for (var p in props) { + if (props.hasOwnProperty(p)) { + var key = p.toLowerCase() + var val = props[p] + // Normalize className + if (key === 'classname') { + key = 'class' + p = 'class' + } + // The for attribute gets transformed to htmlFor, but we just set as for + if (p === 'htmlFor') { + p = 'for' + } + // If a property is boolean, set itself to the key + if (BOOL_PROPS[key]) { + if (val === 'true') val = key + else if (val === 'false') continue + } + // If a property prefers being set directly vs setAttribute + if (key.slice(0, 2) === 'on') { + el[p] = val + } else { + if (ns) { + if (p === 'xlink:href') { + el.setAttributeNS(XLINKNS, p, val) + } else if (/^xmlns($|:)/i.test(p)) { + // skip xmlns definitions + } else { + el.setAttributeNS(null, p, val) + } + } else { + el.setAttribute(p, val) + } + } + } + } + + function appendChild (childs) { + if (!Array.isArray(childs)) return + var hadText = false + for (var i = 0, len = childs.length; i < len; i++) { + var node = childs[i] + if (Array.isArray(node)) { + appendChild(node) + continue + } + + if (typeof node === 'number' || + typeof node === 'boolean' || + typeof node === 'function' || + node instanceof Date || + node instanceof RegExp) { + node = node.toString() + } + + var lastChild = el.childNodes[el.childNodes.length - 1] + if (typeof node === 'string') { + hadText = true + if (lastChild && lastChild.nodeName === '#text') { + lastChild.nodeValue += node + } else { + node = document.createTextNode(node) + el.appendChild(node) + lastChild = node + } + if (i === len - 1) { + hadText = false + var value = lastChild.nodeValue + .replace(/^\n[\s]+/, '') + .replace(/\n[\s]+$/, '') + if (value) lastChild.nodeValue = value + else el.removeChild(lastChild) + } + } else if (node && node.nodeType) { + if (hadText) { + hadText = false + var val = lastChild.nodeValue + .replace(/^\n[\s]+/, '') + .replace(/\n[\s]+$/, '') + if (val) lastChild.nodeValue = val + else el.removeChild(lastChild) + } + el.appendChild(node) + } + } + } + appendChild(children) + + return el +} + +module.exports = hyperx(belCreateElement, {comments: true}) +module.exports.default = module.exports +module.exports.createElement = belCreateElement diff --git a/index.js b/index.js index ae20a17..a87d946 100644 --- a/index.js +++ b/index.js @@ -1,172 +1 @@ -var document = require('global/document') -var hyperx = require('hyperx') -var onload = require('on-load') - -var SVGNS = 'http://www.w3.org/2000/svg' -var XLINKNS = 'http://www.w3.org/1999/xlink' - -var BOOL_PROPS = { - autofocus: 1, - checked: 1, - defaultchecked: 1, - disabled: 1, - formnovalidate: 1, - indeterminate: 1, - readonly: 1, - required: 1, - selected: 1, - willvalidate: 1 -} -var COMMENT_TAG = '!--' -var SVG_TAGS = [ - 'svg', - 'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateColor', - 'animateMotion', 'animateTransform', 'circle', 'clipPath', 'color-profile', - 'cursor', 'defs', 'desc', 'ellipse', 'feBlend', 'feColorMatrix', - 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', - 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', - 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', - 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', - 'feSpotLight', 'feTile', 'feTurbulence', 'filter', 'font', 'font-face', - 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', - 'foreignObject', 'g', 'glyph', 'glyphRef', 'hkern', 'image', 'line', - 'linearGradient', 'marker', 'mask', 'metadata', 'missing-glyph', 'mpath', - 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', - 'set', 'stop', 'switch', 'symbol', 'text', 'textPath', 'title', 'tref', - 'tspan', 'use', 'view', 'vkern' -] - -function belCreateElement (tag, props, children) { - var el - - // If an svg tag, it needs a namespace - if (SVG_TAGS.indexOf(tag) !== -1) { - props.namespace = SVGNS - } - - // If we are using a namespace - var ns = false - if (props.namespace) { - ns = props.namespace - delete props.namespace - } - - // Create the element - if (ns) { - el = document.createElementNS(ns, tag) - } else if (tag === COMMENT_TAG) { - return document.createComment(props.comment) - } else { - el = document.createElement(tag) - } - - // If adding onload events - if (props.onload || props.onunload) { - var load = props.onload || function () {} - var unload = props.onunload || function () {} - onload(el, function belOnload () { - load(el) - }, function belOnunload () { - unload(el) - }, - // We have to use non-standard `caller` to find who invokes `belCreateElement` - belCreateElement.caller.caller.caller) - delete props.onload - delete props.onunload - } - - // Create the properties - for (var p in props) { - if (props.hasOwnProperty(p)) { - var key = p.toLowerCase() - var val = props[p] - // Normalize className - if (key === 'classname') { - key = 'class' - p = 'class' - } - // The for attribute gets transformed to htmlFor, but we just set as for - if (p === 'htmlFor') { - p = 'for' - } - // If a property is boolean, set itself to the key - if (BOOL_PROPS[key]) { - if (val === 'true') val = key - else if (val === 'false') continue - } - // If a property prefers being set directly vs setAttribute - if (key.slice(0, 2) === 'on') { - el[p] = val - } else { - if (ns) { - if (p === 'xlink:href') { - el.setAttributeNS(XLINKNS, p, val) - } else if (/^xmlns($|:)/i.test(p)) { - // skip xmlns definitions - } else { - el.setAttributeNS(null, p, val) - } - } else { - el.setAttribute(p, val) - } - } - } - } - - function appendChild (childs) { - if (!Array.isArray(childs)) return - var hadText = false - for (var i = 0, len = childs.length; i < len; i++) { - var node = childs[i] - if (Array.isArray(node)) { - appendChild(node) - continue - } - - if (typeof node === 'number' || - typeof node === 'boolean' || - typeof node === 'function' || - node instanceof Date || - node instanceof RegExp) { - node = node.toString() - } - - var lastChild = el.childNodes[el.childNodes.length - 1] - if (typeof node === 'string') { - hadText = true - if (lastChild && lastChild.nodeName === '#text') { - lastChild.nodeValue += node - } else { - node = document.createTextNode(node) - el.appendChild(node) - lastChild = node - } - if (i === len - 1) { - hadText = false - var value = lastChild.nodeValue - .replace(/^\n[\s]+/, '') - .replace(/\n[\s]+$/, '') - if (value) lastChild.nodeValue = value - else el.removeChild(lastChild) - } - } else if (node && node.nodeType) { - if (hadText) { - hadText = false - var val = lastChild.nodeValue - .replace(/^\n[\s]+/, '') - .replace(/\n[\s]+$/, '') - if (val) lastChild.nodeValue = val - else el.removeChild(lastChild) - } - el.appendChild(node) - } - } - } - appendChild(children) - - return el -} - -module.exports = hyperx(belCreateElement, {comments: true}) -module.exports.default = module.exports -module.exports.createElement = belCreateElement +module.exports = require('pelo') diff --git a/package.json b/package.json index 14004ca..ee372b6 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "4.6.1", "description": "A simple extension to native elements", "main": "index.js", + "browser": "browser.js", "scripts": { "start": "wzrd test/index.js:bundle.js", "test": "standard && node test/server.js && browserify test/index.js | tape-run", @@ -28,9 +29,9 @@ }, "homepage": "https://github.com/shama/bel", "dependencies": { - "global": "^4.3.0", "hyperx": "^2.3.0", "on-load": "^3.2.0", + "pelo": "0.0.1", "tape-run": "^3.0.0" }, "devDependencies": { From 8792f1d59d7263dbf2f5b994e6bbd1787e4718a2 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 19 May 2017 12:58:26 +0200 Subject: [PATCH 4/9] rm unused deps --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ee372b6..0cbe410 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,7 @@ "dependencies": { "hyperx": "^2.3.0", "on-load": "^3.2.0", - "pelo": "0.0.1", - "tape-run": "^3.0.0" + "pelo": "0.0.1" }, "devDependencies": { "browser-process-hrtime": "^0.1.2", @@ -41,6 +40,7 @@ "morphdom": "^2.1.1", "standard": "^9.0.2", "tape": "^4.6.0", + "tape-run": "^3.0.0", "wzrd": "^1.4.0" } } From 979588be2a61e5ef9b60523dc45a1adf897a5d0f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 19 May 2017 12:59:09 +0200 Subject: [PATCH 5/9] fixup! clean up files field --- package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/package.json b/package.json index 0cbe410..5d8f805 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,6 @@ "element", "diffhtml" ], - "files": [ - "index.js", - "create.js" - ], "author": "Kyle Robinson Young (http://dontkry.com)", "license": "MIT", "bugs": { From 0797eb1f12e9ff684444542ba492c7c2dc880bb7 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 19 May 2017 13:24:13 +0200 Subject: [PATCH 6/9] fixup! pull in pelo code --- index.js | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 +-- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index a87d946..b047eca 100644 --- a/index.js +++ b/index.js @@ -1 +1,61 @@ -module.exports = require('pelo') +// See https://github.com/shuhei/pelo/issues/5 + +var Module = require('module') + +function handleValue (value) { + if (Array.isArray(value)) { + // Suppose that each item is a result of html``. + return value.join('') + } + // Ignore event handlers. + // onclick=${(e) => doSomething(e)} + // will become + // onclick="" + if (typeof value === 'function') { + return '""' + } + if (value === null || value === undefined) { + return '' + } + if (value.__encoded) { + return value + } + var str = value.toString() + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +function stringify () { + var pieces = arguments[0] + var output = '' + for (var i = 0; i < pieces.length; i++) { + output += pieces[i] + if (i < pieces.length - 1) { + output += handleValue(arguments[i + 1]) + } + } + // HACK: Avoid double encoding by marking encoded string + // You cannot add properties to string literals + // eslint-disable-next-line no-new-wrappers + var wrapper = new String(output) + wrapper.__encoded = true + return wrapper +} + +function replace (moduleId) { + var originalRequire = Module.prototype.require + Module.prototype.require = function (id) { + if (id === moduleId) { + return stringify + } else { + return originalRequire.apply(this, arguments) + } + } +} +stringify.replace = replace + +module.exports = stringify diff --git a/package.json b/package.json index 5d8f805..3334272 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,7 @@ "homepage": "https://github.com/shama/bel", "dependencies": { "hyperx": "^2.3.0", - "on-load": "^3.2.0", - "pelo": "0.0.1" + "on-load": "^3.2.0" }, "devDependencies": { "browser-process-hrtime": "^0.1.2", From 23e7e3c873237f4458cf9451b34e9ecaadc006f5 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 19 May 2017 13:30:29 +0200 Subject: [PATCH 7/9] fixup! rm more global calls --- bench/create-trees.js | 1 - test/onload.js | 1 - 2 files changed, 2 deletions(-) diff --git a/bench/create-trees.js b/bench/create-trees.js index f5aa4e9..2e967c4 100644 --- a/bench/create-trees.js +++ b/bench/create-trees.js @@ -2,7 +2,6 @@ var bench = require('./bench') var bel = require('../') var vdom = require('virtual-dom') var h = vdom.h -var document = require('global/document') function raw (label, items) { var div = document.createElement('div') diff --git a/test/onload.js b/test/onload.js index 566c1aa..64a01b3 100644 --- a/test/onload.js +++ b/test/onload.js @@ -1,6 +1,5 @@ var test = require('tape') var bel = require('../') -var document = require('global/document') var morphdom = require('morphdom') test('fire onload and unload events', function (t) { From b8bd511c722f9e9cb7885e2fc766a5c1d9c267cf Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 19 May 2017 15:56:02 +0200 Subject: [PATCH 8/9] detect if running in Electron --- index.js | 22 +++++++--------------- package.json | 1 + 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/index.js b/index.js index b047eca..ffa6f7d 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,12 @@ // See https://github.com/shuhei/pelo/issues/5 +var isElectron = require('is-electron') +var browser = require('./browser') -var Module = require('module') +if (typeof window !== 'undefined' && isElectron()) { + module.exports = browser +} else { + module.exports = stringify +} function handleValue (value) { if (Array.isArray(value)) { @@ -45,17 +51,3 @@ function stringify () { wrapper.__encoded = true return wrapper } - -function replace (moduleId) { - var originalRequire = Module.prototype.require - Module.prototype.require = function (id) { - if (id === moduleId) { - return stringify - } else { - return originalRequire.apply(this, arguments) - } - } -} -stringify.replace = replace - -module.exports = stringify diff --git a/package.json b/package.json index 3334272..480bd61 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "homepage": "https://github.com/shama/bel", "dependencies": { "hyperx": "^2.3.0", + "is-electron": "^2.0.0", "on-load": "^3.2.0" }, "devDependencies": { From c12173536532d7c0eb66b4d73fb7f35a5e44ec8f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 19 May 2017 16:11:36 +0200 Subject: [PATCH 9/9] split off regexes --- browser.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/browser.js b/browser.js index 265dd9c..00b26e9 100644 --- a/browser.js +++ b/browser.js @@ -1,6 +1,9 @@ var hyperx = require('hyperx') var onload = require('on-load') +var headRegex = /^\n[\s]+/ +var tailRegex = /\n[\s]+$/ + var SVGNS = 'http://www.w3.org/2000/svg' var XLINKNS = 'http://www.w3.org/1999/xlink' @@ -143,18 +146,18 @@ function belCreateElement (tag, props, children) { if (i === len - 1) { hadText = false var value = lastChild.nodeValue - .replace(/^\n[\s]+/, '') - .replace(/\n[\s]+$/, '') - if (value) lastChild.nodeValue = value + .replace(headRegex, '') + .replace(tailRegex, '') + if (value !== '') lastChild.nodeValue = value else el.removeChild(lastChild) } } else if (node && node.nodeType) { if (hadText) { hadText = false var val = lastChild.nodeValue - .replace(/^\n[\s]+/, '') - .replace(/\n[\s]+$/, '') - if (val) lastChild.nodeValue = val + .replace(headRegex, '') + .replace(tailRegex, '') + if (val !== '') lastChild.nodeValue = val else el.removeChild(lastChild) } el.appendChild(node)