Skip to content

Commit

Permalink
Reintroduced text blocks and added fixes + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tbranyen committed Jan 8, 2016
1 parent 0942257 commit 255b501
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 153 deletions.
253 changes: 141 additions & 112 deletions dist/diffhtml.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ module.exports = function(config) {
'node_modules/weakmap/weakmap.js',
'lib/**/*.js',
'test/assert.js',

{ pattern: 'test/unit/**/*.js', watched: false },
{ pattern: 'test/integration/**/*.js', watched: false }
],

preprocessors: {
'{lib/**/*, lib/worker/!source}.js': ['browserify'],
'lib/**/*.js': ['browserify'],
'test/unit/**/*.js': ['browserify'],
'test/integration/**/*.js': ['browserify'],
},
Expand All @@ -27,7 +28,6 @@ module.exports = function(config) {
coverageReporter: {
type: 'lcov',
dir: 'test/coverage',

instrumenters: { isparta: isparta },
instrumenter: { '**/*.js': 'isparta' }
},
Expand Down
3 changes: 2 additions & 1 deletion lib/element/make.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as svg from '../svg';
import makeNode from '../node/make';
import { components, upgrade } from './custom';
import { decodeEntities } from '../util/entities';

/**
* Takes in a virtual descriptor and creates an HTML element. Sets the element
Expand Down Expand Up @@ -53,7 +54,7 @@ export default function make(descriptor) {
// Set the text content, this should be refactored such that only text nodes
// should ever get assigned a value.
if (descriptor.nodeValue) {
element.textContent = descriptor.nodeValue;
element.textContent = decodeEntities(descriptor.nodeValue);
}

// Upgrade the element after creating it.
Expand Down
16 changes: 9 additions & 7 deletions lib/patches/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import * as sync from '../node/sync';
import { unprotectElement } from '../util/memory';
import { decodeEntities } from '../util/entities';

const blockTextElements = ['script', 'noscript', 'style', 'pre'];

/**
* Processes an Array of patches.
*
Expand All @@ -27,9 +29,7 @@ export default function process(element, patches) {
let textPromises = transition.makePromises('textChanged',
[element], null, descriptor.nodeValue);

let decoded = decodeEntities(descriptor.nodeValue);

element.nodeValue = decoded;
element.nodeValue = descriptor.nodeValue;

triggerTransition('textChanged', textPromises, promises => {});
}
Expand All @@ -42,6 +42,7 @@ export default function process(element, patches) {
fragment.appendChild(element);
}


return element;
};

Expand Down Expand Up @@ -184,6 +185,7 @@ export default function process(element, patches) {
'document root?');
}


// Append the element first, before doing the replacement.
patch.old.parentNode.insertBefore(patch.new, patch.old.nextSibling);

Expand Down Expand Up @@ -252,12 +254,12 @@ export default function process(element, patches) {
[patch.element], patch.element.nodeValue, patch.value);

triggerTransition('textChanged', textChangePromises, promises => {
let decoded = decodeEntities(patch.value);
patch.element.nodeValue = decodeEntities(patch.value);

patch.element.nodeValue = decoded;
let nodeName = patch.element.parentNode.nodeName.toLowerCase();

if (patch.element.parentNode) {
patch.element.parentNode.nodeValue = decoded;
if (blockTextElements[nodeName]) {
patch.element.parentNode.nodeValue = decodeEntities(patch.value);
}
});
}
Expand Down
24 changes: 14 additions & 10 deletions lib/transitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,21 @@ function triggerLifecycleEvent(stateName, args, elements) {
// Trigger custom element
var customElementFn = fnSignatures[stateName].customElementsFn;

elements.filter(Boolean).forEach(function(element) {
var customElement = components[element.nodeName.toLowerCase()] || empty;
var customElementMethodName = `${stateName}Callback`;

// Call the associated CustomElement's lifecycle callback, if it exists.
if (customElement.prototype[customElementMethodName]) {
customElementFn.apply(null, args)(
customElement.prototype[customElementMethodName].bind(element)
);
for (let i = 0; i < elements.length; i++) {
let element = elements[i];

if (element) {
let customElement = components[element.nodeName.toLowerCase()] || empty;
let customElementMethodName = `${stateName}Callback`;

// Call the associated CustomElement's lifecycle callback, if it exists.
if (customElement.prototype[customElementMethodName]) {
customElementFn.apply(null, args)(
customElement.prototype[customElementMethodName].bind(element)
);
}
}
});
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions lib/util/entities.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
let element = document.createElement('div');

/**
* Decode's HTML entities.
* Decodes HTML strings.
*
* @see http://stackoverflow.com/a/13091266
* @param stringing
* @return unescaped decoded HTML
* @return unescaped HTML
*/
export function decodeEntities(string) {
element.innerHTML = string;
Expand Down
43 changes: 37 additions & 6 deletions lib/util/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ export function makeParser() {
}
};

var kBlockTextElements = {
script: true,
noscript: true,
style: true,
pre: true,
template: true
};

/**
* TextNode to contain a text element in DOM tree.
* @param {string} value [description]
Expand Down Expand Up @@ -161,15 +169,13 @@ export function makeParser() {
* @param {string} data html
* @return {HTMLElement} root element
*/
parse: function(data, options) {
parse: function(data) {
let rootObject = {};
let root = HTMLElement(null, rootObject);
let currentParent = root;
let stack = [root];
let lastTextPos = -1;

options = options || {};

if (data.indexOf('<') === -1 && data) {
currentParent.childNodes[currentParent.childNodes.length] = TextNode(data);

Expand All @@ -183,7 +189,7 @@ export function makeParser() {
text = data.slice(lastTextPos, kMarkupPattern.lastIndex - match[0].length);

if (text.trim()) {
currentParent.childNodes[currentParent.childNodes.length] = TextNode(text);
currentParent.childNodes.push(TextNode(text));
}
}
}
Expand All @@ -210,10 +216,35 @@ export function makeParser() {
}
}

currentParent = currentParent.childNodes[currentParent.childNodes.push(
HTMLElement(match[2], attrs, match[3])) - 1];
currentParent = currentParent.childNodes[
currentParent.childNodes.push(
HTMLElement(match[2], attrs, match[3])
) - 1
];

stack.push(currentParent);

if (kBlockTextElements[match[2]]) {
// A little test to find next </script> or </style> ...
let closeMarkup = '</' + match[2] + '>';
let index = data.indexOf(closeMarkup, kMarkupPattern.lastIndex);
let length = match[2].length;

if (index === -1) {
lastTextPos = kMarkupPattern.lastIndex = data.length + 1;
}
else {
lastTextPos = kMarkupPattern.lastIndex = index + closeMarkup.length;
match[1] = true;
}

let newText = data.slice(match.index + match[0].length, index);

if (newText.trim()) {
currentParent.childNodes.push(TextNode(newText));
}

}
}
if (match[1] || match[4] || kSelfClosingElements[match[2]]) {
// </ or /> or <br> etc.
Expand Down
2 changes: 0 additions & 2 deletions lib/worker/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import { cleanMemory } from '../util/memory';
*/
export function completeWorkerRender(element, elementMeta) {
return function(ev) {
var nodes = ev.data.nodes;

var completeRender = function() {
// Reset internal caches for quicker lookups in the futures.
elementMeta._innerHTML = element.innerHTML;
Expand Down
11 changes: 7 additions & 4 deletions lib/worker/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,15 @@ export default function startup(worker) {
var isInner = data.isInner;
var newTree = null;

// Always unprotect allocations before the start of a render cycle.
if (oldTree) { unprotectElement(oldTree); }

// If an `oldTree` was provided by the UI thread, use that in place of the
// current `oldTree`.
if (data.oldTree) { oldTree = data.oldTree; }
if (data.oldTree) {
if (oldTree) {
unprotectElement(oldTree);
}

oldTree = data.oldTree;
}

// If the `newTree` was provided to the worker, use that instead of trying
// to create one from HTML source.
Expand Down
4 changes: 2 additions & 2 deletions test/integration/inner.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('Integration: innerHTML', function() {
describe('Text', function() {
it('can decode HTML entities', function() {
diff.innerHTML(this.fixture, '<div>&lt;</div>');
assert.equal(this.fixture.innerHTML, '<div>&lt;</div>');
assert.equal(this.fixture.innerText, '&lt;');
});

it('can be override existing content', function() {
Expand Down Expand Up @@ -82,7 +82,7 @@ describe('Integration: innerHTML', function() {
it('supports html5 entities', function() {
diff.innerHTML(this.fixture, '<div>&gla;</div>');

assert.equal(this.fixture.firstChild.innerHTML, '');
assert.equal(this.fixture.firstChild.innerText, '&gla;');
});
});

Expand Down
21 changes: 18 additions & 3 deletions test/integration/outer.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('Integration: outerHTML', function() {
});

it('cannot replace an element without a parent', function() {
assert.throws(function() {
assert.throws(() => {
diff.outerHTML(this.fixture, '<p></p>');
});
});
Expand Down Expand Up @@ -92,10 +92,25 @@ describe('Integration: outerHTML', function() {
assert.equal(this.fixture.firstChild, span, 'are the same element');
});

it('supports html5 entities', function() {
it('supports HTML5 entities', function() {
diff.outerHTML(this.fixture, '<div>&gla;</div>');

assert.equal(this.fixture.innerHTML, '⪥');
assert.equal(this.fixture.innerText, '&gla;');
});

it('will properly escape markup being injected into script tags', function() {
diff.outerHTML(this.fixture, `
<div>
<script test>
var test = "<p></p>";
</script>
</div>
`);

assert.equal(this.fixture.querySelector('p'), null);
assert.equal(this.fixture.firstChild.firstChild.textContent.trim(), `
var test = \"<p></p>\";
`.trim());
});
});

Expand Down
4 changes: 2 additions & 2 deletions test/playground.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<title>Playground</title>

<style>
<style type="text/css">
button { border: none; padding: 10px; -webkit-user-select: none; }
[id^=add] { background-color: green; color: #FFF; }
[id^=remove] { background-color: red; color: #FFF; }
Expand All @@ -30,7 +30,7 @@

function addWith() {
diff.innerHTML(sandbox, sandbox.innerHTML += `
<div>with worker</div>
<div>with worker &gla;</div>
`, { enableWorker: true });
}

Expand Down
17 changes: 17 additions & 0 deletions test/unit/util/entities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*import { decodeEntities } from '../../../lib/util/entities';
describe.skip('Unit: DecodeEntities', function() {
it.only('can decode an unencoded string', function() {
var string = decodeEntities(`
<p></p>
`);
assert.equal(string.trim(), '<p></p>');
});
it('can decode an HTML5 encoded string', function() {
var string = decodeEntities(`&gla;`);
assert.equal(string, '⪥');
});
});
*/

0 comments on commit 255b501

Please sign in to comment.