Skip to content

Commit

Permalink
Merge 80d2e13 into fbdacb0
Browse files Browse the repository at this point in the history
  • Loading branch information
tbranyen committed Jun 2, 2016
2 parents fbdacb0 + 80d2e13 commit 23eafcb
Show file tree
Hide file tree
Showing 14 changed files with 422 additions and 75 deletions.
245 changes: 213 additions & 32 deletions dist/diffhtml.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions lib/element/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import makeElement from '../element/make';
* @return {Object} containing the uuid and DOM node
*/
export default function get(descriptor) {
let uuid = descriptor.uuid;
let element = makeElement(descriptor);
const uuid = descriptor.uuid;
const element = makeElement(descriptor);

return { uuid, element };
}
4 changes: 2 additions & 2 deletions lib/element/make.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ export default function make(descriptor) {

// If not a dynamic type, set as an attribute, since it's a valid
// attribute value.
if (!isObject && !isFunction) {
if (attr.name && !isObject && !isFunction) {
element.setAttribute(attr.name, attr.value);
}
else if (typeof attr.value !== 'string') {
else if (attr.name && typeof attr.value !== 'string') {
// Necessary to track the attribute/prop existence.
element.setAttribute(attr.name, '');

Expand Down
1 change: 0 additions & 1 deletion lib/html.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { parse } from './util/parser';

// Make a parser.
const isPropEx = /(=|'|")/;

/**
Expand Down
3 changes: 3 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import { TransitionStateError, DOMException } from './errors';
export { html } from './html';
import { html } from './html';

// Export the VTree/Attribute helpers
export { createElement, createAttribute } from './util/transform';

/**
* Used to diff the outerHTML contents of the passed element with the markup
* contents. Very useful for applying a global diff on the
Expand Down
10 changes: 8 additions & 2 deletions lib/node/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export default function patchNode(element, newHTML, options) {

if (typeof newHTML === 'string') {
let childNodes = parse(newHTML).childNodes;
newTree = options.inner ? childNodes : childNodes[0];
newTree = isInner ? childNodes : childNodes[0];
}
else if (newHTML.ownerDocument) {
newTree = makeNode(newHTML);
Expand All @@ -94,7 +94,6 @@ export default function patchNode(element, newHTML, options) {
newTree = {
childNodes,
attributes: elementMeta.oldTree.attributes,
uuid: elementMeta.oldTree.uuid,
nodeName: elementMeta.oldTree.nodeName,
nodeValue: elementMeta.oldTree.nodeValue
};
Expand All @@ -104,6 +103,13 @@ export default function patchNode(element, newHTML, options) {
let patches = syncNode(elementMeta.oldTree, newTree);
let invokeRender = completeRender(element, elementMeta);

if (Array.isArray(newTree)) {
newTree.forEach(unprotectElement);
}
else if (newTree) {
newTree.childNodes.forEach(unprotectElement);
}

// Process the data immediately and wait until all transition callbacks
// have completed.
let promises = processPatches(element, patches);
Expand Down
86 changes: 66 additions & 20 deletions lib/node/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ export default function sync(oldTree, newTree, patches) {
throw new Error('Missing existing tree to sync');
}

// Flatten out childNodes that are arrays.
if (newTree && newTree.childNodes) {
let hasArray = newTree.childNodes.some(node => Array.isArray(node));

if (hasArray) {
let newChildNodes = [];

newTree.childNodes.forEach(childNode => {
if (Array.isArray(childNode)) {
newChildNodes.push.apply(newChildNodes, childNode);
}
else {
newChildNodes.push(childNode);
}
});

newTree.childNodes = newChildNodes;
}
}

let oldNodeValue = oldTree.nodeValue;
let oldChildNodes = oldTree.childNodes;
let oldChildNodesLength = oldChildNodes ? oldChildNodes.length : 0;
Expand Down Expand Up @@ -78,9 +98,16 @@ export default function sync(oldTree, newTree, patches) {

return patches;
}
// These elements never changed.
else if (oldTree.uuid === newTree.uuid) {
skipAttributeCompare = true;
//return patches;
}

let areTextNodes = oldIsTextNode && newIsTextNode;

// If the top level nodeValue has changed we should reflect it.
if (oldIsTextNode && newIsTextNode && oldNodeValue !== nodeValue) {
if (areTextNodes && oldNodeValue !== nodeValue) {
patches.push({
__do__: CHANGE_TEXT,
element: oldTree,
Expand All @@ -92,6 +119,25 @@ export default function sync(oldTree, newTree, patches) {
return patches;
}

// Ensure keys exist for all the old & new elements.
let noOldKeys = !oldChildNodes.some(oldChildNode => oldChildNode.key);
let newKeys = null;
let oldKeys = null;

if (!noOldKeys) {
newKeys = new Set(
childNodes
.map(childNode => String(childNode.key))
.filter(Boolean)
);

oldKeys = new Set(
oldChildNodes
.map(childNode => String(childNode.key))
.filter(Boolean)
);
}

// Most common additive elements.
if (childNodesLength > oldChildNodesLength) {
// Store elements in a DocumentFragment to increase performance and be
Expand All @@ -116,9 +162,6 @@ export default function sync(oldTree, newTree, patches) {
});
}

// Ensure keys exist for all the old & new elements.
let noOldKeys = !oldChildNodes.some(oldChildNode => oldChildNode.key);

// Remove these elements.
if (oldChildNodesLength > childNodesLength) {
// For now just splice out the end items.
Expand All @@ -133,18 +176,6 @@ export default function sync(oldTree, newTree, patches) {
// This is an expensive operation so we do the above check to ensure that a
// key was specified.
else {
let newKeys = new Set(
childNodes
.map(childNode => String(childNode.key))
.filter(Boolean)
);

let oldKeys = new Set(
oldChildNodes
.map(childNode => String(childNode.key))
.filter(Boolean)
);

let keysToRemove = {};
let truthy = 1;

Expand Down Expand Up @@ -232,7 +263,7 @@ export default function sync(oldTree, newTree, patches) {
// Synchronize attributes
let attributes = newTree.attributes;

if (attributes && !skipAttributeCompare) {
if (!skipAttributeCompare && attributes) {
let oldLength = oldTree.attributes.length;
let newLength = attributes.length;

Expand Down Expand Up @@ -290,22 +321,37 @@ export default function sync(oldTree, newTree, patches) {
let toModify = attributes;

for (let i = 0; i < toModify.length; i++) {
let oldAttrName = oldTree.attributes[i] && oldTree.attributes[i].name;
let oldAttrValue = oldTree.attributes[i] && oldTree.attributes[i].value;
let newAttrName = attributes[i] && attributes[i].name;
let newAttrValue = attributes[i] && attributes[i].value;

// Only push in a change if the attribute or value changes.
if (oldAttrValue !== newAttrValue) {
let change = {
__do__: MODIFY_ATTRIBUTE,
element: oldTree,
name: toModify[i].name,
name: oldTree.attributes[i].name,
value: toModify[i].value,
};

// Replace the attribute in the virtual node.
let attr = oldTree.attributes[i];
attr.name = toModify[i].name;
attr.value = toModify[i].value;

// If the attribute names change, this is a removal.
if (oldAttrName === newAttrName) {
attr.value = toModify[i].value;
}
else {
change.name = newAttrName ? newAttrName : oldAttrName ;
change.value = toModify[i].value ? toModify[i].value : undefined;
attr.name = newAttrName;
attr.value = change.value;

if (!newAttrName && !newAttrName) {
pools.attributeObject.unprotect(toModify[i]);
}
}

// Add the change to the series of patches.
patches.push(change);
Expand Down
6 changes: 4 additions & 2 deletions lib/patches/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const slice = Array.prototype.slice;
* @param patches - Array that contains patch objects.
*/
export default function process(element, patches) {
//console.log(JSON.parse(JSON.stringify(patches)));
const elementMeta = TreeCache.get(element);
const promises = [];
const triggerTransition = transition.buildTrigger(promises);
Expand Down Expand Up @@ -314,7 +315,9 @@ export default function process(element, patches) {
// If not a dynamic type, set as an attribute, since it's a valid
// attribute value.
if (!isObject && !isFunction) {
el.setAttribute(patch.name, patch.value);
if (patch.name) {
el.setAttribute(patch.name, patch.value);
}
}
else if (typeof patch.value !== 'string') {
// Necessary to track the attribute/prop existence.
Expand All @@ -324,7 +327,6 @@ export default function process(element, patches) {
el[patch.name] = patch.value;
}

// Support live updating of the value attribute.
// Support live updating of the value attribute.
if (patch.name === 'value' || patch.name === 'checked') {
el[patch.name] = patch.value;
Expand Down
32 changes: 22 additions & 10 deletions lib/util/memory.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { pools } from '../util/pools';
import { count, pools } from '../util/pools';
import makeNode from '../node/make';

/**
Expand All @@ -8,13 +8,17 @@ import makeNode from '../node/make';
* @return element
*/
export function protectElement(element) {
var elementObject = pools.elementObject;
var attributeObject = pools.attributeObject;
if (Array.isArray(element)) {
return element.forEach(childNode => protectElement(childNode));
}

const elementObject = pools.elementObject;
const attributeObject = pools.attributeObject;

elementObject.protect(element);

element.attributes.forEach(attributeObject.protect, attributeObject);
element.childNodes.forEach(protectElement);
element.childNodes.forEach(childNode => protectElement(childNode));

return element;
}
Expand All @@ -26,8 +30,12 @@ export function protectElement(element) {
* @return
*/
export function unprotectElement(element, makeNode) {
var elementObject = pools.elementObject;
var attributeObject = pools.attributeObject;
if (Array.isArray(element)) {
return element.forEach(childNode => unprotectElement(childNode, makeNode));
}

const elementObject = pools.elementObject;
const attributeObject = pools.attributeObject;

elementObject.unprotect(element);
elementObject.cache.uuid.delete(element.uuid);
Expand All @@ -46,8 +54,8 @@ export function unprotectElement(element, makeNode) {
* Recycles all unprotected allocations.
*/
export function cleanMemory(makeNode) {
var elementObject = pools.elementObject;
var attributeObject = pools.attributeObject;
const elementObject = pools.elementObject;
const attributeObject = pools.attributeObject;

// Clean out unused elements.
if (makeNode && makeNode.nodes) {
Expand All @@ -60,14 +68,18 @@ export function cleanMemory(makeNode) {

// Empty all element allocations.
elementObject.cache.allocated.forEach(v => {
elementObject.cache.free.push(v);
if (elementObject.cache.free.length < count) {
elementObject.cache.free.push(v);
}
});

elementObject.cache.allocated.clear();

// Empty all attribute allocations.
attributeObject.cache.allocated.forEach(v => {
attributeObject.cache.free.push(v);
if (attributeObject.cache.free.length < count) {
attributeObject.cache.free.push(v);
}
});

attributeObject.cache.allocated.clear();
Expand Down
10 changes: 9 additions & 1 deletion lib/util/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const kAttributePattern =
/\b(id|class)\s*(=\s*("([^"]+)"|'([^']+)'|(\S+)))?/ig;

const reAttrPattern =
/\b([a-z][a-z0-9\-]*)\s*(=\s*("([^"]+)"|'([^']+)'|(\S+)))?/ig;
/\b([_a-z][_a-z0-9\-]*)\s*(=\s*("([^"]+)"|'([^']+)'|(\S+)))?/ig;

const kSelfClosingElements = {
meta: true,
Expand Down Expand Up @@ -281,6 +281,14 @@ export function parse(data, supplemental) {
}
}

// Find any last remaining text after the parsing completes over tags.
var remainingText = data.slice(lastTextPos).trim();

// If the text exists and isn't just whitespace, push into a new TextNode.
if (remainingText) {
currentParent.childNodes.push(TextNode(remainingText));
}

// This is an entire document, so only allow the HTML children to be
// body or head.
if (root.childNodes.length && root.childNodes[0].nodeName === 'html') {
Expand Down
Loading

0 comments on commit 23eafcb

Please sign in to comment.