fix contenteditable diffing

This commit is contained in:
Leo Horie 2014-05-01 22:32:10 -04:00
parent 10229546eb
commit 42a33b074c
7 changed files with 82 additions and 59 deletions

View file

@ -32,7 +32,7 @@ Mithril = m = new function app(window) {
} }
return cell return cell
} }
function build(parentElement, parentTag, data, cached, shouldReattach, index, namespace) { function build(parentElement, parentTag, data, cached, shouldReattach, index, editable, namespace) {
if (data === null || data === undefined) { if (data === null || data === undefined) {
if (cached) clear(cached.nodes) if (cached) clear(cached.nodes)
return return
@ -49,7 +49,7 @@ Mithril = m = new function app(window) {
if (dataType == "[object Array]") { if (dataType == "[object Array]") {
var nodes = [], intact = cached.length === data.length, subArrayCount = 0 var nodes = [], intact = cached.length === data.length, subArrayCount = 0
for (var i = 0, cacheCount = 0; i < data.length; i++) { for (var i = 0, cacheCount = 0; i < data.length; i++) {
var item = build(parentElement, null, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, namespace) var item = build(parentElement, null, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, editable, namespace)
if (item === undefined) continue if (item === undefined) continue
if (!item.nodes.intact) intact = false if (!item.nodes.intact) intact = false
subArrayCount += item instanceof Array ? item.length : 1 subArrayCount += item instanceof Array ? item.length : 1
@ -74,7 +74,7 @@ Mithril = m = new function app(window) {
cached = { cached = {
tag: data.tag, tag: data.tag,
attrs: setAttributes(node, data.tag, data.attrs, {}, namespace), attrs: setAttributes(node, data.tag, data.attrs, {}, namespace),
children: build(node, data.tag, data.children, cached.children, true, 0, namespace), children: build(node, data.tag, data.children, cached.children, true, 0, data.attrs.contenteditable ? node : editable, namespace),
nodes: [node] nodes: [node]
} }
parentElement.insertBefore(node, parentElement.childNodes[index] || null) parentElement.insertBefore(node, parentElement.childNodes[index] || null)
@ -82,7 +82,7 @@ Mithril = m = new function app(window) {
else { else {
node = cached.nodes[0] node = cached.nodes[0]
setAttributes(node, data.tag, data.attrs, cached.attrs, namespace) setAttributes(node, data.tag, data.attrs, cached.attrs, namespace)
cached.children = build(node, data.tag, data.children, cached.children, false, 0, namespace) cached.children = build(node, data.tag, data.children, cached.children, false, 0, data.attrs.contenteditable ? node : editable, namespace)
cached.nodes.intact = true cached.nodes.intact = true
if (shouldReattach === true) parentElement.insertBefore(node, parentElement.childNodes[index] || null) if (shouldReattach === true) parentElement.insertBefore(node, parentElement.childNodes[index] || null)
} }
@ -102,20 +102,25 @@ Mithril = m = new function app(window) {
cached.nodes = [node] cached.nodes = [node]
} }
else if (cached.valueOf() !== data.valueOf() || shouldReattach === true) { else if (cached.valueOf() !== data.valueOf() || shouldReattach === true) {
if (data.$trusted) { if (!editable || editable !== window.document.activeElement) {
var current = cached.nodes[0], nodes = [current] if (data.$trusted) {
if (current) { var current = cached.nodes[0], nodes = [current]
while (current = current.nextSibling) nodes.push(current) if (current) {
clear(nodes) while (current = current.nextSibling) nodes.push(current)
node = injectHTML(parentElement, index, data) clear(nodes)
node = injectHTML(parentElement, index, data)
}
else parentElement.innerHTML = data
}
else {
node = cached.nodes[0]
if (parentTag === "textarea") parentElement.value = data
else if (editable) editable.innerHTML = data
else {
parentElement.insertBefore(node, parentElement.childNodes[index] || null)
node.nodeValue = data
}
} }
else parentElement.innerHTML = data
}
else {
node = cached.nodes[0]
if (parentTag === "textarea") parentElement.value = data
else parentElement.insertBefore(node, parentElement.childNodes[index] || null)
node.nodeValue = data
} }
cached = new data.constructor(data) cached = new data.constructor(data)
cached.nodes = [node] cached.nodes = [node]
@ -137,6 +142,7 @@ Mithril = m = new function app(window) {
} }
else if (attrName === "style") { else if (attrName === "style") {
for (var rule in dataAttr) { for (var rule in dataAttr) {
console.log(node)
if (cachedAttr === undefined || cachedAttr[rule] !== dataAttr[rule]) node.style[rule] = dataAttr[rule] if (cachedAttr === undefined || cachedAttr[rule] !== dataAttr[rule]) node.style[rule] = dataAttr[rule]
} }
} }
@ -202,7 +208,7 @@ Mithril = m = new function app(window) {
var index = nodeCache.indexOf(root) var index = nodeCache.indexOf(root)
var id = index < 0 ? nodeCache.push(root) - 1 : index var id = index < 0 ? nodeCache.push(root) - 1 : index
var node = root == window.document || root == window.document.documentElement ? documentNode : root var node = root == window.document || root == window.document.documentElement ? documentNode : root
cellCache[id] = build(node, null, cell, cellCache[id], false, 0) cellCache[id] = build(node, null, cell, cellCache[id], false, 0, null, undefined)
} }
m.trust = function(value) { m.trust = function(value) {

View file

@ -32,7 +32,7 @@ Mithril = m = new function app(window) {
} }
return cell return cell
} }
function build(parentElement, parentTag, data, cached, shouldReattach, index, namespace) { function build(parentElement, parentTag, data, cached, shouldReattach, index, editable, namespace) {
if (data === null || data === undefined) { if (data === null || data === undefined) {
if (cached) clear(cached.nodes) if (cached) clear(cached.nodes)
return return
@ -49,7 +49,7 @@ Mithril = m = new function app(window) {
if (dataType == "[object Array]") { if (dataType == "[object Array]") {
var nodes = [], intact = cached.length === data.length, subArrayCount = 0 var nodes = [], intact = cached.length === data.length, subArrayCount = 0
for (var i = 0, cacheCount = 0; i < data.length; i++) { for (var i = 0, cacheCount = 0; i < data.length; i++) {
var item = build(parentElement, null, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, namespace) var item = build(parentElement, null, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, editable, namespace)
if (item === undefined) continue if (item === undefined) continue
if (!item.nodes.intact) intact = false if (!item.nodes.intact) intact = false
subArrayCount += item instanceof Array ? item.length : 1 subArrayCount += item instanceof Array ? item.length : 1
@ -74,7 +74,7 @@ Mithril = m = new function app(window) {
cached = { cached = {
tag: data.tag, tag: data.tag,
attrs: setAttributes(node, data.tag, data.attrs, {}, namespace), attrs: setAttributes(node, data.tag, data.attrs, {}, namespace),
children: build(node, data.tag, data.children, cached.children, true, 0, namespace), children: build(node, data.tag, data.children, cached.children, true, 0, data.attrs.contenteditable ? node : editable, namespace),
nodes: [node] nodes: [node]
} }
parentElement.insertBefore(node, parentElement.childNodes[index] || null) parentElement.insertBefore(node, parentElement.childNodes[index] || null)
@ -82,7 +82,7 @@ Mithril = m = new function app(window) {
else { else {
node = cached.nodes[0] node = cached.nodes[0]
setAttributes(node, data.tag, data.attrs, cached.attrs, namespace) setAttributes(node, data.tag, data.attrs, cached.attrs, namespace)
cached.children = build(node, data.tag, data.children, cached.children, false, 0, namespace) cached.children = build(node, data.tag, data.children, cached.children, false, 0, data.attrs.contenteditable ? node : editable, namespace)
cached.nodes.intact = true cached.nodes.intact = true
if (shouldReattach === true) parentElement.insertBefore(node, parentElement.childNodes[index] || null) if (shouldReattach === true) parentElement.insertBefore(node, parentElement.childNodes[index] || null)
} }
@ -102,20 +102,25 @@ Mithril = m = new function app(window) {
cached.nodes = [node] cached.nodes = [node]
} }
else if (cached.valueOf() !== data.valueOf() || shouldReattach === true) { else if (cached.valueOf() !== data.valueOf() || shouldReattach === true) {
if (data.$trusted) { if (!editable || editable !== window.document.activeElement) {
var current = cached.nodes[0], nodes = [current] if (data.$trusted) {
if (current) { var current = cached.nodes[0], nodes = [current]
while (current = current.nextSibling) nodes.push(current) if (current) {
clear(nodes) while (current = current.nextSibling) nodes.push(current)
node = injectHTML(parentElement, index, data) clear(nodes)
node = injectHTML(parentElement, index, data)
}
else parentElement.innerHTML = data
}
else {
node = cached.nodes[0]
if (parentTag === "textarea") parentElement.value = data
else if (editable) editable.innerHTML = data
else {
parentElement.insertBefore(node, parentElement.childNodes[index] || null)
node.nodeValue = data
}
} }
else parentElement.innerHTML = data
}
else {
node = cached.nodes[0]
if (parentTag === "textarea") parentElement.value = data
else parentElement.insertBefore(node, parentElement.childNodes[index] || null)
node.nodeValue = data
} }
cached = new data.constructor(data) cached = new data.constructor(data)
cached.nodes = [node] cached.nodes = [node]
@ -137,6 +142,7 @@ Mithril = m = new function app(window) {
} }
else if (attrName === "style") { else if (attrName === "style") {
for (var rule in dataAttr) { for (var rule in dataAttr) {
console.log(node)
if (cachedAttr === undefined || cachedAttr[rule] !== dataAttr[rule]) node.style[rule] = dataAttr[rule] if (cachedAttr === undefined || cachedAttr[rule] !== dataAttr[rule]) node.style[rule] = dataAttr[rule]
} }
} }
@ -202,7 +208,7 @@ Mithril = m = new function app(window) {
var index = nodeCache.indexOf(root) var index = nodeCache.indexOf(root)
var id = index < 0 ? nodeCache.push(root) - 1 : index var id = index < 0 ? nodeCache.push(root) - 1 : index
var node = root == window.document || root == window.document.documentElement ? documentNode : root var node = root == window.document || root == window.document.documentElement ? documentNode : root
cellCache[id] = build(node, null, cell, cellCache[id], false, 0) cellCache[id] = build(node, null, cell, cellCache[id], false, 0, null, undefined)
} }
m.trust = function(value) { m.trust = function(value) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -32,7 +32,7 @@ Mithril = m = new function app(window) {
} }
return cell return cell
} }
function build(parentElement, parentTag, data, cached, shouldReattach, index, namespace) { function build(parentElement, parentTag, data, cached, shouldReattach, index, editable, namespace) {
if (data === null || data === undefined) { if (data === null || data === undefined) {
if (cached) clear(cached.nodes) if (cached) clear(cached.nodes)
return return
@ -49,7 +49,7 @@ Mithril = m = new function app(window) {
if (dataType == "[object Array]") { if (dataType == "[object Array]") {
var nodes = [], intact = cached.length === data.length, subArrayCount = 0 var nodes = [], intact = cached.length === data.length, subArrayCount = 0
for (var i = 0, cacheCount = 0; i < data.length; i++) { for (var i = 0, cacheCount = 0; i < data.length; i++) {
var item = build(parentElement, null, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, namespace) var item = build(parentElement, null, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, editable, namespace)
if (item === undefined) continue if (item === undefined) continue
if (!item.nodes.intact) intact = false if (!item.nodes.intact) intact = false
subArrayCount += item instanceof Array ? item.length : 1 subArrayCount += item instanceof Array ? item.length : 1
@ -74,7 +74,7 @@ Mithril = m = new function app(window) {
cached = { cached = {
tag: data.tag, tag: data.tag,
attrs: setAttributes(node, data.tag, data.attrs, {}, namespace), attrs: setAttributes(node, data.tag, data.attrs, {}, namespace),
children: build(node, data.tag, data.children, cached.children, true, 0, namespace), children: build(node, data.tag, data.children, cached.children, true, 0, data.attrs.contenteditable ? node : editable, namespace),
nodes: [node] nodes: [node]
} }
parentElement.insertBefore(node, parentElement.childNodes[index] || null) parentElement.insertBefore(node, parentElement.childNodes[index] || null)
@ -82,7 +82,7 @@ Mithril = m = new function app(window) {
else { else {
node = cached.nodes[0] node = cached.nodes[0]
setAttributes(node, data.tag, data.attrs, cached.attrs, namespace) setAttributes(node, data.tag, data.attrs, cached.attrs, namespace)
cached.children = build(node, data.tag, data.children, cached.children, false, 0, namespace) cached.children = build(node, data.tag, data.children, cached.children, false, 0, data.attrs.contenteditable ? node : editable, namespace)
cached.nodes.intact = true cached.nodes.intact = true
if (shouldReattach === true) parentElement.insertBefore(node, parentElement.childNodes[index] || null) if (shouldReattach === true) parentElement.insertBefore(node, parentElement.childNodes[index] || null)
} }
@ -102,20 +102,25 @@ Mithril = m = new function app(window) {
cached.nodes = [node] cached.nodes = [node]
} }
else if (cached.valueOf() !== data.valueOf() || shouldReattach === true) { else if (cached.valueOf() !== data.valueOf() || shouldReattach === true) {
if (data.$trusted) { if (!editable || editable !== window.document.activeElement) {
var current = cached.nodes[0], nodes = [current] if (data.$trusted) {
if (current) { var current = cached.nodes[0], nodes = [current]
while (current = current.nextSibling) nodes.push(current) if (current) {
clear(nodes) while (current = current.nextSibling) nodes.push(current)
node = injectHTML(parentElement, index, data) clear(nodes)
node = injectHTML(parentElement, index, data)
}
else parentElement.innerHTML = data
}
else {
node = cached.nodes[0]
if (parentTag === "textarea") parentElement.value = data
else if (editable) editable.innerHTML = data
else {
parentElement.insertBefore(node, parentElement.childNodes[index] || null)
node.nodeValue = data
}
} }
else parentElement.innerHTML = data
}
else {
node = cached.nodes[0]
if (parentTag === "textarea") parentElement.value = data
else parentElement.insertBefore(node, parentElement.childNodes[index] || null)
node.nodeValue = data
} }
cached = new data.constructor(data) cached = new data.constructor(data)
cached.nodes = [node] cached.nodes = [node]
@ -137,6 +142,7 @@ Mithril = m = new function app(window) {
} }
else if (attrName === "style") { else if (attrName === "style") {
for (var rule in dataAttr) { for (var rule in dataAttr) {
console.log(node)
if (cachedAttr === undefined || cachedAttr[rule] !== dataAttr[rule]) node.style[rule] = dataAttr[rule] if (cachedAttr === undefined || cachedAttr[rule] !== dataAttr[rule]) node.style[rule] = dataAttr[rule]
} }
} }
@ -202,7 +208,7 @@ Mithril = m = new function app(window) {
var index = nodeCache.indexOf(root) var index = nodeCache.indexOf(root)
var id = index < 0 ? nodeCache.push(root) - 1 : index var id = index < 0 ? nodeCache.push(root) - 1 : index
var node = root == window.document || root == window.document.documentElement ? documentNode : root var node = root == window.document || root == window.document.documentElement ? documentNode : root
cellCache[id] = build(node, null, cell, cellCache[id], false, 0) cellCache[id] = build(node, null, cell, cellCache[id], false, 0, null, undefined)
} }
m.trust = function(value) { m.trust = function(value) {

View file

@ -17,20 +17,25 @@ app.view = function(ctrl) {
onkeyup: m.withAttr("value", ctrl.title), onkeyup: m.withAttr("value", ctrl.title),
value: ctrl.title() value: ctrl.title()
}), }),
m("br"), m("br"),
m("textarea", { m("textarea", {
onkeyup: m.withAttr("value", ctrl.title), onkeyup: m.withAttr("value", ctrl.title),
value: ctrl.title() value: ctrl.title()
}), }),
m("br"), m("br"),
m("textarea", { m("textarea", {
onkeyup: m.withAttr("value", ctrl.title) onkeyup: m.withAttr("value", ctrl.title)
}, ctrl.title()), }, ctrl.title()),
m("br"), m("br"),
m("div[contenteditable]", { m("div[contenteditable]", {
style: {border: "1px solid #888"}, style: {border: "1px solid #888"},
onkeyup: m.withAttr("innerHTML", ctrl.title) onkeyup: m.withAttr("innerHTML", ctrl.title)
}, ctrl.title()), }, ctrl.title()),
m("br"),
m("div[contenteditable]", {
style: {border: "1px solid #888"},
onkeyup: m.withAttr("innerHTML", ctrl.title)
}, m.trust(ctrl.title())),
]); ]);
} }