Skip to content

Instantly share code, notes, and snippets.

@yoshuawuyts
Last active February 15, 2023 09:49
Show Gist options
  • Save yoshuawuyts/812293f5fdc0d536e0629954efa100fc to your computer and use it in GitHub Desktop.
Save yoshuawuyts/812293f5fdc0d536e0629954efa100fc to your computer and use it in GitHub Desktop.
// var diffSwap = require('myers-diff-array-swap')
var remove = require('remove-array-items')
var diff = require('myers-diff-array')
var assert = require('assert')
var morphNode = require('./lib/morph')
function Morph () {
this.results = []
this.swaps = []
this.from = []
this.to = []
this.index = -1
}
Morph.prototype.morph = function (from, to) {
assert.equal(typeof from, 'object', 'nanomorph: from should be an object')
assert.equal(typeof to, 'object', 'nanomorph: to should be an object')
this._morph(from, to)
}
// Look at a single node, and figure out what to do.
Morph.prototype._morph = function (from, to) {
if (!from) return to // {from} does not exist, return {to}.
else if (!to) return null // {to} does not exist, return null.
else if (to.isSameNode && to.isSameNode(from)) return from // if .isSameNode() checks out, return {from}.
else if (to.tagName !== from.tagName) return to // {from, to} are different DOM types, return {to}.
else return morphNode(from, to) && this._children(from, to) && from // {from, to} are same DOM types, copy over attributes.
}
Morph.prototype._children = function (from, to) {
if (!from.firstChild && to.firstChild) {
while (to.firstChild) to.removeChild(to.lastChild) // {from} has no childNodes, delete all {to} childNodes.
} else if (!to.firstChild && from.firstChild) {
while (from.firstChild) to.appendChild(from.firstChild) // {to} has no childNodes, copy over all {from} childNodes.
} else {
this._morphChildNodes(to, from) // {from, to} have childNodes, apply Myers diff reorder.
}
}
Morph.prototype._morphChildNodes = function (from, to) {
this.results.length = 0
this.swaps.length = 0
this.from.length = 0
this.to.length = 0
this.index = -1
var fromNode, toNode,
fromPosition, toPosition
var fromTmpId = 0
var toTmpId = 0
// Walk over the elements in the array, and write their id to the array
// if a new child element is encountered, increment a tmp id.
var max = Math.max(this.from.length, this.to.length)
for (var i = 0; i < max; i++) {
fromNode = from.childNodes[i]
if (fromNode) this.from.push(fromNode.id || fromTmpId++)
toNode = to.childNodes[i]
if (toNode) this.to.push(toNode.id || toTmpId++)
}
// Calculate the shortest diff, and mark all points where we can swap instead.
diff(this.from, this.to, this.result)
// diffSwap(this.results, this.swaps)
if (this.swaps.length > 0) this.index = this.swaps[0]
// Apply the diffs generated from the previous step.
for (var j = 0, len = this.results.length; j < len; j += 2) {
fromPosition = this.results[j]
toPosition = this.results[j + 1]
if (fromPosition === -1) {
remove(this.from, toPosition, 1)
} else if (fromPosition === toPosition) {
this._morph(this.from[fromPosition], this.to[toPosition])
} else {
this.to[toPosition].insertBefore(this.from[fromPosition])
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment