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
368 changes: 223 additions & 145 deletions README.md

Large diffs are not rendered by default.

318 changes: 188 additions & 130 deletions dist/index-with-deps.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index-with-deps.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index-with-deps.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index-with-deps.min.js.map

Large diffs are not rendered by default.

2,256 changes: 1,375 additions & 881 deletions dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions dist/index.min.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/index.min.js.map

This file was deleted.

1 change: 0 additions & 1 deletion gulpfile.js

This file was deleted.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"custom-event-polyfill": "^0.2.1",
"cz-conventional-changelog": "^1.2.0",
"semantic-release": "^6.3.2",
"skatejs-build": "^12.1.0"
"skatejs-build": "^12.1.0",
"worker-loader": "^0.7.1"
},
"scripts": {
"build": "sk-build",
Expand All @@ -33,7 +34,8 @@
},
"homepage": "https://github.com/skatejs/dom-diff#readme",
"dependencies": {
"object-assign": "^4.0.1"
"object-assign": "^4.0.1",
"skatejs-web-components": "^5.0.0"
},
"config": {
"commitizen": {
Expand Down
1 change: 0 additions & 1 deletion rollup.config.js

This file was deleted.

61 changes: 19 additions & 42 deletions src/compare/attributes.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,34 @@
import * as types from '../types';
import { getAccessor } from '../util/accessor';
import { REMOVE_ATTRIBUTE, SET_ATTRIBUTE } from '../types';

export default function (src, dst) {
let srcAttrs = src.attributes;
let dstAttrs = dst.attributes;
let srcAttrsLen = (srcAttrs || 0) && srcAttrs.length;
let dstAttrsLen = (dstAttrs || 0) && dstAttrs.length;
let instructions = [];
const empty = v => v == null;

// Bail early if possible.
if (!srcAttrsLen && !dstAttrsLen) {
return instructions;
}

// Merge attributes that exist in source with destination's.
for (let a = 0; a < srcAttrsLen; a++) {
const srcAttr = srcAttrs[a];
const srcAttrName = srcAttr.name;
const srcAttrValue = getAccessor(src, srcAttrName);
const dstAttr = dstAttrs[srcAttrName];
const dstAttrValue = getAccessor(dst, srcAttrName);
export default function (src, tar) {
const { attributes: srcValues } = src;
const { attributes: tarValues } = tar;
const instructions = [];

if (!dstAttr) {
instructions.push({
data: { name: srcAttrName },
destination: dst,
source: src,
type: types.REMOVE_ATTRIBUTE
});
} else if (srcAttrValue !== dstAttrValue) {
for (let name in srcValues) {
if (empty(tarValues[name])) {
instructions.push({
data: { name: srcAttrName, value: dstAttrValue },
destination: dst,
data: { name },
target: tar,
source: src,
type: types.SET_ATTRIBUTE
type: REMOVE_ATTRIBUTE
});
}
}

// We only need to worry about setting attributes that don't already exist
// in the source.
for (let a = 0; a < dstAttrsLen; a++) {
const dstAttr = dstAttrs[a];
const dstAttrName = dstAttr.name;
const dstAttrValue = getAccessor(dst, dstAttrName);
const srcAttr = srcAttrs[dstAttrName];
for (let name in tarValues) {
const srcValue = srcValues[name];
const tarValue = tarValues[name];

if (!srcAttr) {
// Only add attributes that have changed.
if (srcValue !== tarValue && !empty(tarValues[name])) {
instructions.push({
data: { name: dstAttrName, value: dstAttrValue },
destination: dst,
data: { name },
target: tar,
source: src,
type: types.SET_ATTRIBUTE
type: SET_ATTRIBUTE
});
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/compare/comment.js

This file was deleted.

9 changes: 6 additions & 3 deletions src/compare/element.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import compareAttributes from './attributes';
import compareEvents from './events';
import compareProperties from './properties';

export default function (src, dst) {
if (src.tagName === dst.tagName) {
return compareAttributes(src, dst).concat(compareEvents(src, dst));
export default function (src, tar) {
if (src.localName === tar.localName) {
return compareAttributes(src, tar)
.concat(compareEvents(src, tar))
.concat(compareProperties(src, tar));
}
}
32 changes: 17 additions & 15 deletions src/compare/events.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import * as types from '../types';
import eventMap from '../util/event-map';

export default function (src, dst) {
const dstEvents = dst.events;
const srcEvents = eventMap(src);
export default function (src, tar) {
const tarEvents = tar.events;
const srcEvents = src.events;
const instructions = [];

// Remove any source events that aren't in the destination before seeing if
// we need to add any from the destination.
// Remove any source events that aren't in the target before seeing if
// we need to add any from the target.
if (srcEvents) {
for (let name in srcEvents) {
if (dstEvents && dstEvents[name] !== srcEvents[name]) {
const srcEvent = srcEvents[name];
const tarEvent = tarEvents[name];
if (!tarEvent || srcEvent !== tarEvent) {
instructions.push({
data: { name, value: undefined },
destination: dst,
data: { name },
target: tar,
source: src,
type: types.SET_EVENT
});
Expand All @@ -24,13 +25,14 @@ export default function (src, dst) {
// After instructing to remove any old events, we then can instruct to add
// new events. This prevents the new events from being removed from earlier
// instructions.
if (dstEvents) {
for (let name in dstEvents) {
const value = dstEvents[name];
if (srcEvents[name] !== value) {
if (tarEvents) {
for (let name in tarEvents) {
const srcEvent = srcEvents[name];
const tarEvent = tarEvents[name];
if (srcEvent !== tarEvent) {
instructions.push({
data: { name, value },
destination: dst,
data: { name, value: tarEvent },
target: tar,
source: src,
type: types.SET_EVENT
});
Expand Down
28 changes: 10 additions & 18 deletions src/compare/node.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
import compareElement from './element';
import compareText from './text';
import compareComment from './comment';

const NODE_COMMENT = 8;
const NODE_ELEMENT = 1;
const NODE_TEXT = 3;

export default function (src, dst) {
let dstType, srcType;
export default function (src, tar) {
const tarType = tar.nodeType;
const srcType = src.nodeType;

if (!dst || !src) {
return;
if (tarType !== srcType) {
return [];
} else if (tarType === NODE_ELEMENT) {
return compareElement(src, tar);
} else if (tarType === NODE_TEXT) {
return compareText(src, tar);
}

dstType = dst.nodeType;
srcType = src.nodeType;

if (dstType !== srcType) {
return;
} else if (dstType === NODE_ELEMENT) {
return compareElement(src, dst);
} else if (dstType === NODE_TEXT) {
return compareText(src, dst);
} else if (dstType === NODE_COMMENT) {
return compareComment(src, dst);
}
return [];
}
35 changes: 35 additions & 0 deletions src/compare/properties.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { SET_PROPERTY } from '../types';

export default function (src, tar) {
const { properties: srcValues } = src;
const { properties: tarValues } = tar;
const instructions = [];

for (let name in srcValues) {
const srcValue = srcValues[name];
const tarValue = tarValues[name];
if (srcValue !== tarValue) {
instructions.push({
data: { name },
target: tar,
source: src,
type: SET_PROPERTY
});
}
}

for (let name in tarValues) {
const srcValue = srcValues[name];
const tarValue = tarValues[name];
if (srcValue !== tarValue) {
instructions.push({
data: { name },
target: tar,
source: src,
type: SET_PROPERTY
});
}
}

return instructions;
}
6 changes: 3 additions & 3 deletions src/compare/text.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as types from '../types';

export default function (src, dst) {
if (src.textContent === dst.textContent) {
export default function (src, tar) {
if (src.textContent === tar.textContent) {
return [];
}

return [{
destination: dst,
target: tar,
source: src,
type: types.TEXT_CONTENT
}];
Expand Down
83 changes: 11 additions & 72 deletions src/diff.js
Original file line number Diff line number Diff line change
@@ -1,77 +1,16 @@
import * as types from './types';
import compareNode from './compare/node';
import realNode from './util/real-node';
import realNodeMap from './util/real-node-map';
import diffMain from './diff/main';
import DiffWorker from 'worker-loader?name=./dist/[hash].[ext]!./diff/worker';
import root from './util/root';

const { Node } = window;
const { Node } = root;

function diffNode (source, destination) {
let nodeInstructions = compareNode(source, destination);

// If there are instructions (even an empty array) it means the node can be
// diffed and doesn't have to be replaced. If the instructions are falsy
// it means that the nodes are not similar (cannot be changed) and must be
// replaced instead.
if (nodeInstructions) {
return nodeInstructions.concat(diff({ source, destination }));
}

return [{
destination,
source,
type: types.REPLACE_CHILD
}];
function diffWorker (src, tar, { done }) {
const worker = new DiffWorker();
worker.addEventListener('message', e => done(e.data));
worker.postMessage([ src, tar ]);
}

export default function diff (opts = {}) {
const src = opts.source;
const dst = opts.destination;

if (!src || !dst) {
return [];
}

let instructions = opts.root ? diffNode(src, dst) : [];

const srcChs = src.childNodes;
const dstChs = dst.childNodes;
const srcChsLen = srcChs ? srcChs.length : 0;
const dstChsLen = dstChs ? dstChs.length : 0;

for (let a = 0; a < dstChsLen; a++) {
const curSrc = srcChs[a];
const curDst = dstChs[a];

// If there is no matching destination node it means we need to remove the
// current source node from the source.
if (!curSrc) {
instructions.push({
destination: dstChs[a],
source: src,
type: types.APPEND_CHILD
});
continue;
} else {
// Ensure the real node is carried over even if the destination isn't used.
// This is used in the render() function to keep track of the real node
// that corresponds to a virtual node if a virtual tree is being used.
if (!(curDst instanceof Node)) {
realNodeMap.set(curDst, realNode(curSrc));
}
}

instructions = instructions.concat(diffNode(curSrc, curDst));
}

if (dstChsLen < srcChsLen) {
for (let a = dstChsLen; a < srcChsLen; a++) {
instructions.push({
destination: srcChs[a],
source: src,
type: types.REMOVE_CHILD
});
}
}

return instructions;
export default function diff (src, tar, { done } = {}) {
const canDiffInWorker = done && !(src instanceof Node && tar instanceof Node);
return canDiffInWorker ? diffWorker(src, tar, { done }) : diffMain(src, tar, { done });
}
Loading