bundler variable disambiguation
This commit is contained in:
parent
2ca8fa6e66
commit
e5391a1957
10 changed files with 75 additions and 542 deletions
|
|
@ -4,6 +4,14 @@ Note: This branch is a sneak peek for the upcoming version 1.0. It's a rewrite f
|
||||||
|
|
||||||
This rewrite aims to fix longstanding API design issues, significantly improve performance, and clean up the codebase.
|
This rewrite aims to fix longstanding API design issues, significantly improve performance, and clean up the codebase.
|
||||||
|
|
||||||
|
## Early Preview
|
||||||
|
|
||||||
|
You can install this via NPN using this command:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install lhorie/mithril.js#rewrite
|
||||||
|
```
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Code still is in flux. Most notably, there's no promise polyfill yet and there are several use cases that still need to be polished. DO NOT USE IN PRODUCTION YET!
|
Code still is in flux. Most notably, there's no promise polyfill yet and there are several use cases that still need to be polished. DO NOT USE IN PRODUCTION YET!
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
var createRenderer = require("../render/render")
|
var coreRenderer = require("../render/render")
|
||||||
var throttle = require("../api/throttle")
|
var throttle = require("../api/throttle")
|
||||||
|
|
||||||
module.exports = function($window, redraw) {
|
module.exports = function($window, redraw) {
|
||||||
var renderer = createRenderer($window)
|
var renderer = coreRenderer($window)
|
||||||
return function(root, component) {
|
return function(root, component) {
|
||||||
var run = throttle(function() {
|
var run = throttle(function() {
|
||||||
renderer.render(root, {tag: component})
|
renderer.render(root, {tag: component})
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
var createRenderer = require("../render/render")
|
var coreRenderer = require("../render/render")
|
||||||
var createRouter = require("../router/router")
|
var coreRouter = require("../router/router")
|
||||||
var throttle = require("../api/throttle")
|
var throttle = require("../api/throttle")
|
||||||
|
|
||||||
module.exports = function($window, redraw) {
|
module.exports = function($window, redraw) {
|
||||||
var renderer = createRenderer($window)
|
var renderer = coreRenderer($window)
|
||||||
var router = createRouter($window)
|
var router = coreRouter($window)
|
||||||
var route = function(root, defaultRoute, routes) {
|
var route = function(root, defaultRoute, routes) {
|
||||||
var replay = router.defineRoutes(routes, function(component, args) {
|
var replay = router.defineRoutes(routes, function(component, args) {
|
||||||
renderer.render(root, {tag: component, attrs: args})
|
renderer.render(root, {tag: component, attrs: args})
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ var o = require("../../ospec/ospec")
|
||||||
var domMock = require("../../test-utils/domMock")
|
var domMock = require("../../test-utils/domMock")
|
||||||
|
|
||||||
var m = require("../../render/hyperscript")
|
var m = require("../../render/hyperscript")
|
||||||
var createMounter = require("../mount")
|
var apiMounter = require("../../api/mount")
|
||||||
|
|
||||||
o.spec("m.mount", function() {
|
o.spec("m.mount", function() {
|
||||||
var FRAME_BUDGET = 1000 / 60
|
var FRAME_BUDGET = 1000 / 60
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ var pushStateMock = require("../../test-utils/pushStateMock")
|
||||||
var domMock = require("../../test-utils/domMock")
|
var domMock = require("../../test-utils/domMock")
|
||||||
|
|
||||||
var m = require("../../render/hyperscript")
|
var m = require("../../render/hyperscript")
|
||||||
var router = require("../../api/router")
|
var apiRouter = require("../../api/router")
|
||||||
|
|
||||||
o.spec("m.route", function() {
|
o.spec("m.route", function() {
|
||||||
var FRAME_BUDGET = 1000 / 60
|
var FRAME_BUDGET = 1000 / 60
|
||||||
|
|
@ -23,7 +23,7 @@ o.spec("m.route", function() {
|
||||||
root = $window.document.body
|
root = $window.document.body
|
||||||
|
|
||||||
redraw = {}
|
redraw = {}
|
||||||
route = router($window, redraw)
|
route = apiRouter($window, redraw)
|
||||||
})
|
})
|
||||||
|
|
||||||
o("updates redraw object", function() {
|
o("updates redraw object", function() {
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,23 @@ var fs = require("fs")
|
||||||
var path = require("path")
|
var path = require("path")
|
||||||
|
|
||||||
var modules = {}
|
var modules = {}
|
||||||
|
var usedVariables = {}
|
||||||
|
|
||||||
function resolve(dir, data) {
|
function resolve(dir, data) {
|
||||||
var replacements = []
|
var replacements = []
|
||||||
data = data.replace(/((?:var|let|const|)\s*)([\w_$]+)(\s*=\s*)require\(([^\)]+)\)/g, function(match, def, variable, eq, dep) {
|
data = data.replace(/((?:var|let|const|)[\t ]*)([\w_$\.]+)(\s*=\s*)require\(([^\)]+)\)/g, function(match, def, variable, eq, dep) {
|
||||||
|
usedVariables[variable] = usedVariables[variable] ? usedVariables[variable]++ : 1
|
||||||
|
|
||||||
var filename = new Function("return " + dep).call()
|
var filename = new Function("return " + dep).call()
|
||||||
var pathname = path.dirname(path.resolve(dir, filename))
|
var normalized = path.resolve(dir, filename)
|
||||||
var normalized = path.normalize(dir + "/" + filename)
|
var pathname = path.dirname(normalized)
|
||||||
if (modules[normalized] === undefined) {
|
if (modules[normalized] === undefined) {
|
||||||
modules[normalized] = variable
|
modules[normalized] = variable
|
||||||
return resolve(pathname,
|
var exported = fixCollisions(fs.readFileSync(dir + "/" + filename + ".js", "utf8"))
|
||||||
fs.readFileSync(dir + "/" + filename + ".js", "utf8")
|
.replace(/"use strict"\s*/gm, "") // remove extraneous "use strict"
|
||||||
.replace(/"use strict"\s*/gm, "")
|
.replace(/module\.exports\s*=\s*/gm, def + variable + eq)
|
||||||
.replace(/module\.exports\s*=\s*/gm, def + variable + eq)
|
//.replace(/module\.exports(\.[\w_$]|\["[^\"]"\])/, def + variable + eq + "{}\n" + variable + "$1")
|
||||||
//.replace(/module\.exports(\.[\w_$]|\["[^\"]"\])/, def + variable + eq + "{}\n" + variable + "$1")
|
return resolve(pathname, exported)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (modules[normalized] !== variable) {
|
if (modules[normalized] !== variable) {
|
||||||
|
|
@ -33,6 +35,25 @@ function resolve(dir, data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
|
.replace(/(?:var|let|const)[\t ]([\w_$\.]+)(\s*=\s*)\1([\r\n;]+)/g, "$3") // remove assignments to itself
|
||||||
|
.replace(/(\r\n){2,}/g, "$1$1") // remove multiple consecutive line breaks
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync("mithril.js", resolve(".", fs.readFileSync("index.js", "utf8")), "utf8")
|
function fixCollisions(code) {
|
||||||
|
for (var variable in usedVariables) {
|
||||||
|
var collision = new RegExp("\\b" + variable + "\\b(?![\"'`])", "g")
|
||||||
|
var exported = new RegExp("module\\.exports\\s*=\\s*" + variable)
|
||||||
|
if (collision.test(code) && !exported.test(code)) {
|
||||||
|
var fixed = variable + usedVariables[variable]++
|
||||||
|
code = code.replace(collision, fixed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
function bundle(input, output) {
|
||||||
|
var code = resolve(".", fs.readFileSync(input, "utf8"))
|
||||||
|
if (new Function(code)) fs.writeFileSync(output, code, "utf8")
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle("index.js", "mithril.js")
|
||||||
|
|
|
||||||
16
index.js
16
index.js
|
|
@ -2,19 +2,19 @@
|
||||||
|
|
||||||
var m = require("./render/hyperscript")
|
var m = require("./render/hyperscript")
|
||||||
var trust = require("./render/trust")
|
var trust = require("./render/trust")
|
||||||
var createRenderer = require("./render/render")
|
var coreRenderer = require("./render/render")
|
||||||
var createMounter = require("./api/mount")
|
var apiMounter = require("./api/mount")
|
||||||
var createRouterInstance = require("./api/router")
|
var apiRouter = require("./api/router")
|
||||||
var createRequester = require("./request/request")
|
var coreRequester = require("./request/request")
|
||||||
var redraw = {run: function() {}}
|
var redraw = {run: function() {}}
|
||||||
|
|
||||||
m.redraw = function() {
|
m.redraw = function() {
|
||||||
redraw.run()
|
redraw.run()
|
||||||
}
|
}
|
||||||
m.trust = trust
|
m.trust = trust
|
||||||
m.render = createRenderer(window).render
|
m.render = coreRenderer(window).render
|
||||||
m.mount = createMounter(window, redraw)
|
m.mount = apiMounter(window, redraw)
|
||||||
m.route = createRouterInstance(window, redraw)
|
m.route = apiRouter(window, redraw)
|
||||||
m.request = createRequester(window, Promise).ajax
|
m.request = coreRequester(window, Promise).ajax
|
||||||
|
|
||||||
module.exports = m
|
module.exports = m
|
||||||
|
|
|
||||||
530
mithril.js
530
mithril.js
|
|
@ -15,8 +15,6 @@ Node.normalizeChildren = function normalizeChildren(children) {
|
||||||
return children
|
return children
|
||||||
}
|
}
|
||||||
|
|
||||||
var Node = Node
|
|
||||||
|
|
||||||
var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g, attrParser = /\[(.+?)(?:\s*=\s*("|'|)(.*?)\2)?\]/
|
var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g, attrParser = /\[(.+?)(?:\s*=\s*("|'|)(.*?)\2)?\]/
|
||||||
var selectorCache = {}
|
var selectorCache = {}
|
||||||
function hyperscript(selector) {
|
function hyperscript(selector) {
|
||||||
|
|
@ -91,500 +89,11 @@ function changeNS(ns, vnode) {
|
||||||
|
|
||||||
var m = hyperscript
|
var m = hyperscript
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var trust = function(html) {
|
var trust = function(html) {
|
||||||
return Node("<", undefined, undefined, html, undefined, undefined)
|
return Node("<", undefined, undefined, html, undefined, undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var coreRenderer = function($window) {
|
||||||
var createRenderer = function($window) {
|
|
||||||
var $doc = $window.document
|
|
||||||
|
|
||||||
var onevent
|
|
||||||
function setEventCallback(callback) {return onevent = callback}
|
|
||||||
|
|
||||||
//create
|
|
||||||
function createNodes(parent, vnodes, start, end, hooks, nextSibling) {
|
|
||||||
for (var i = start; i < end; i++) {
|
|
||||||
var vnode = vnodes[i]
|
|
||||||
if (vnode != null) {
|
|
||||||
insertNode(parent, createNode(vnode, hooks), nextSibling)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function createNode(vnode, hooks) {
|
|
||||||
var tag = vnode.tag
|
|
||||||
if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks)
|
|
||||||
if (typeof tag === "string") {
|
|
||||||
switch (tag) {
|
|
||||||
case "#": return createText(vnode)
|
|
||||||
case "<": return createHTML(vnode)
|
|
||||||
case "[": return createFragment(vnode, hooks)
|
|
||||||
default: return createElement(vnode, hooks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else return createComponent(vnode, hooks)
|
|
||||||
}
|
|
||||||
function createText(vnode) {
|
|
||||||
return vnode.dom = $doc.createTextNode(vnode.children)
|
|
||||||
}
|
|
||||||
function createHTML(vnode) {
|
|
||||||
var match = vnode.children.match(/^\s*?<(\w+)/im) || []
|
|
||||||
var parent = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}[match[1]] || "div"
|
|
||||||
var temp = $doc.createElement(parent)
|
|
||||||
|
|
||||||
temp.innerHTML = vnode.children
|
|
||||||
vnode.dom = temp.firstChild
|
|
||||||
vnode.domSize = temp.childNodes.length
|
|
||||||
var fragment = $doc.createDocumentFragment()
|
|
||||||
var child
|
|
||||||
while (child = temp.firstChild) {
|
|
||||||
fragment.appendChild(child)
|
|
||||||
}
|
|
||||||
return fragment
|
|
||||||
}
|
|
||||||
function createFragment(vnode, hooks) {
|
|
||||||
var fragment = $doc.createDocumentFragment()
|
|
||||||
if (vnode.children != null) {
|
|
||||||
var children = vnode.children
|
|
||||||
createNodes(fragment, children, 0, children.length, hooks, null)
|
|
||||||
}
|
|
||||||
vnode.dom = fragment.firstChild
|
|
||||||
vnode.domSize = fragment.childNodes.length
|
|
||||||
return fragment
|
|
||||||
}
|
|
||||||
function createElement(vnode, hooks) {
|
|
||||||
var tag = vnode.tag
|
|
||||||
var ns = vnode.ns
|
|
||||||
|
|
||||||
var attrs = vnode.attrs
|
|
||||||
var is = attrs && attrs.is
|
|
||||||
|
|
||||||
var element = ns ?
|
|
||||||
is ? $doc.createElementNS(ns, tag, is) : $doc.createElementNS(ns, tag) :
|
|
||||||
is ? $doc.createElement(tag, is) : $doc.createElement(tag)
|
|
||||||
vnode.dom = element
|
|
||||||
|
|
||||||
if (attrs != null) {
|
|
||||||
setAttrs(vnode, attrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vnode.text != null) {
|
|
||||||
if (vnode.text !== "") element.textContent = vnode.text
|
|
||||||
else vnode.children = [Node("#", undefined, undefined, vnode.text, undefined, undefined)]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vnode.children != null) {
|
|
||||||
var children = vnode.children
|
|
||||||
createNodes(element, children, 0, children.length, hooks, null)
|
|
||||||
setLateAttrs(vnode)
|
|
||||||
}
|
|
||||||
return element
|
|
||||||
}
|
|
||||||
function createComponent(vnode, hooks) {
|
|
||||||
vnode.state = copy(vnode.tag)
|
|
||||||
|
|
||||||
initLifecycle(vnode.tag, vnode, hooks)
|
|
||||||
vnode.instance = Node.normalize(vnode.tag.view.call(vnode.state, vnode))
|
|
||||||
var element = createNode(vnode.instance, hooks)
|
|
||||||
vnode.dom = vnode.instance.dom
|
|
||||||
vnode.domSize = vnode.instance.domSize
|
|
||||||
return element
|
|
||||||
}
|
|
||||||
|
|
||||||
//update
|
|
||||||
function updateNodes(parent, old, vnodes, hooks, nextSibling) {
|
|
||||||
if (old == null && vnodes == null) return
|
|
||||||
else if (old == null) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling)
|
|
||||||
else if (vnodes == null) removeNodes(parent, old, 0, old.length, vnodes)
|
|
||||||
else {
|
|
||||||
var recycling = isRecyclable(old, vnodes)
|
|
||||||
if (recycling) old = old.concat(old.pool)
|
|
||||||
|
|
||||||
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
|
|
||||||
while (oldEnd >= oldStart && end >= start) {
|
|
||||||
var o = old[oldStart], v = vnodes[start]
|
|
||||||
if (o === v) oldStart++, start++
|
|
||||||
else if (o != null && v != null && o.key === v.key) {
|
|
||||||
oldStart++, start++
|
|
||||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling)
|
|
||||||
if (recycling) insertNode(parent, toFragment(o), nextSibling)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var o = old[oldEnd]
|
|
||||||
if (o === v) oldEnd--, start++
|
|
||||||
else if (o != null && v != null && o.key === v.key) {
|
|
||||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling)
|
|
||||||
insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
|
|
||||||
oldEnd--, start++
|
|
||||||
}
|
|
||||||
else break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (oldEnd >= oldStart && end >= start) {
|
|
||||||
var o = old[oldEnd], v = vnodes[end]
|
|
||||||
if (o === v) oldEnd--, end--
|
|
||||||
else if (o != null && v != null && o.key === v.key) {
|
|
||||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling)
|
|
||||||
if (recycling) insertNode(parent, toFragment(o), nextSibling)
|
|
||||||
nextSibling = o.dom
|
|
||||||
oldEnd--, end--
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!map) map = getKeyMap(old, oldEnd)
|
|
||||||
if (v != null) {
|
|
||||||
var oldIndex = map[v.key]
|
|
||||||
if (oldIndex != null) {
|
|
||||||
var movable = old[oldIndex]
|
|
||||||
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling)
|
|
||||||
insertNode(parent, toFragment(movable), nextSibling)
|
|
||||||
old[oldIndex].skip = true
|
|
||||||
nextSibling = movable.dom
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var dom = createNode(v, hooks)
|
|
||||||
insertNode(parent, dom, nextSibling)
|
|
||||||
nextSibling = dom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end--
|
|
||||||
}
|
|
||||||
if (end < start) break
|
|
||||||
}
|
|
||||||
createNodes(parent, vnodes, start, end + 1, hooks, nextSibling)
|
|
||||||
removeNodes(parent, old, oldStart, oldEnd + 1, vnodes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function updateNode(parent, old, vnode, hooks, nextSibling, recycling) {
|
|
||||||
var oldTag = old.tag, tag = vnode.tag
|
|
||||||
if (oldTag === tag) {
|
|
||||||
vnode.state = old.state
|
|
||||||
vnode.events = old.events
|
|
||||||
if (shouldUpdate(vnode, old)) return
|
|
||||||
if (vnode.attrs != null) {
|
|
||||||
updateLifecycle(vnode.attrs, vnode, hooks, recycling)
|
|
||||||
}
|
|
||||||
if (typeof oldTag === "string") {
|
|
||||||
switch (oldTag) {
|
|
||||||
case "#": updateText(old, vnode); break
|
|
||||||
case "<": updateHTML(parent, old, vnode, nextSibling); break
|
|
||||||
case "[": updateFragment(parent, old, vnode, hooks, nextSibling); break
|
|
||||||
default: updateElement(old, vnode, hooks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else updateComponent(parent, old, vnode, hooks, nextSibling, recycling)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
removeNode(parent, old, null, false)
|
|
||||||
insertNode(parent, createNode(vnode, hooks), nextSibling)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function updateText(old, vnode) {
|
|
||||||
if (old.children.toString() !== vnode.children.toString()) {
|
|
||||||
old.dom.nodeValue = vnode.children
|
|
||||||
}
|
|
||||||
vnode.dom = old.dom
|
|
||||||
}
|
|
||||||
function updateHTML(parent, old, vnode, nextSibling) {
|
|
||||||
if (old.children !== vnode.children) {
|
|
||||||
toFragment(old)
|
|
||||||
insertNode(parent, createHTML(vnode), nextSibling)
|
|
||||||
}
|
|
||||||
else vnode.dom = old.dom
|
|
||||||
}
|
|
||||||
function updateFragment(parent, old, vnode, hooks, nextSibling) {
|
|
||||||
updateNodes(parent, old.children, vnode.children, hooks, nextSibling)
|
|
||||||
var domSize = 0, children = vnode.children
|
|
||||||
vnode.dom = null
|
|
||||||
if (children != null) {
|
|
||||||
for (var i = 0; i < children.length; i++) {
|
|
||||||
var child = children[i]
|
|
||||||
if (child != null) {
|
|
||||||
if (vnode.dom == null) vnode.dom = child.dom
|
|
||||||
domSize += child.domSize || 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (domSize !== 1) vnode.domSize = domSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function updateElement(old, vnode, hooks) {
|
|
||||||
var element = vnode.dom = old.dom
|
|
||||||
if (vnode.tag === "textarea") {
|
|
||||||
if (vnode.attrs == null) vnode.attrs = {}
|
|
||||||
if (vnode.text != null) vnode.attrs.value = vnode.text //FIXME handle multiple children
|
|
||||||
}
|
|
||||||
updateAttrs(vnode, old.attrs, vnode.attrs)
|
|
||||||
if (old.text != null && vnode.text != null && vnode.text !== "") {
|
|
||||||
if (old.text.toString() !== vnode.text.toString()) old.dom.firstChild.nodeValue = vnode.text
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (old.text != null) old.children = [Node("#", undefined, undefined, old.text, undefined, old.dom.firstChild)]
|
|
||||||
if (vnode.text != null) vnode.children = [Node("#", undefined, undefined, vnode.text, undefined, undefined)]
|
|
||||||
updateNodes(element, old.children, vnode.children, hooks, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function updateComponent(parent, old, vnode, hooks, nextSibling, recycling) {
|
|
||||||
vnode.instance = Node.normalize(vnode.tag.view.call(vnode.state, vnode))
|
|
||||||
updateLifecycle(vnode.tag, vnode, hooks, recycling)
|
|
||||||
updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling)
|
|
||||||
vnode.dom = vnode.instance.dom
|
|
||||||
vnode.domSize = vnode.instance.domSize
|
|
||||||
}
|
|
||||||
function isRecyclable(old, vnodes) {
|
|
||||||
if (old.pool != null && Math.abs(old.pool.length - vnodes.length) <= Math.abs(old.length - vnodes.length)) {
|
|
||||||
var oldChildrenLength = old[0] && old[0].children && old[0].children.length || 0
|
|
||||||
var poolChildrenLength = old.pool[0] && old.pool[0].children && old.pool[0].children.length || 0
|
|
||||||
var vnodesChildrenLength = vnodes[0] && vnodes[0].children && vnodes[0].children.length || 0
|
|
||||||
if (Math.abs(poolChildrenLength - vnodesChildrenLength) <= Math.abs(oldChildrenLength - vnodesChildrenLength)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
function getKeyMap(vnodes, end) {
|
|
||||||
var map = {}, i = 0
|
|
||||||
for (var i = 0; i < end; i++) {
|
|
||||||
var vnode = vnodes[i]
|
|
||||||
if (vnode != null) {
|
|
||||||
var key = vnode.key
|
|
||||||
if (key != null) map[key] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
function toFragment(vnode) {
|
|
||||||
var count = vnode.domSize
|
|
||||||
if (count != null) {
|
|
||||||
var fragment = $doc.createDocumentFragment()
|
|
||||||
if (count > 0) {
|
|
||||||
var dom = vnode.dom
|
|
||||||
while (--count) fragment.appendChild(dom.nextSibling)
|
|
||||||
fragment.insertBefore(dom, fragment.firstChild)
|
|
||||||
}
|
|
||||||
return fragment
|
|
||||||
}
|
|
||||||
else return vnode.dom
|
|
||||||
}
|
|
||||||
function getNextSibling(vnodes, i, nextSibling) {
|
|
||||||
for (; i < vnodes.length; i++) {
|
|
||||||
if (vnodes[i] != null) return vnodes[i].dom
|
|
||||||
}
|
|
||||||
return nextSibling
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertNode(parent, dom, nextSibling) {
|
|
||||||
if (nextSibling && nextSibling.parentNode) parent.insertBefore(dom, nextSibling)
|
|
||||||
else parent.appendChild(dom)
|
|
||||||
}
|
|
||||||
|
|
||||||
//remove
|
|
||||||
function removeNodes(parent, vnodes, start, end, context) {
|
|
||||||
for (var i = start; i < end; i++) {
|
|
||||||
var vnode = vnodes[i]
|
|
||||||
if (vnode != null) {
|
|
||||||
if (vnode.skip) vnode.skip = undefined
|
|
||||||
else removeNode(parent, vnode, context, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function removeNode(parent, vnode, context, deferred) {
|
|
||||||
if (deferred === false) {
|
|
||||||
var expected = 0, called = 0
|
|
||||||
var callback = function() {
|
|
||||||
if (++called === expected) removeNode(parent, vnode, context, true)
|
|
||||||
}
|
|
||||||
if (vnode.attrs && vnode.attrs.onbeforeremove) {
|
|
||||||
expected++
|
|
||||||
vnode.attrs.onbeforeremove.call(vnode, vnode, callback)
|
|
||||||
}
|
|
||||||
if (typeof vnode.tag !== "string" && vnode.tag.onbeforeremove) {
|
|
||||||
expected++
|
|
||||||
vnode.tag.onbeforeremove.call(vnode, vnode, callback)
|
|
||||||
}
|
|
||||||
if (expected > 0) return
|
|
||||||
}
|
|
||||||
|
|
||||||
onremove(vnode)
|
|
||||||
if (vnode.dom) {
|
|
||||||
var count = vnode.domSize || 1
|
|
||||||
if (count > 1) {
|
|
||||||
var dom = vnode.dom
|
|
||||||
while (--count) {
|
|
||||||
parent.removeChild(dom.nextSibling)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (vnode.dom.parentNode != null) parent.removeChild(vnode.dom)
|
|
||||||
if (context != null && vnode.domSize == null && !hasIntegrationMethods(vnode)) { //TODO test custom elements
|
|
||||||
if (!context.pool) context.pool = [vnode]
|
|
||||||
else context.pool.push(vnode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onremove(vnode) {
|
|
||||||
if (vnode.attrs && vnode.attrs.onremove) vnode.attrs.onremove.call(vnode.state, vnode)
|
|
||||||
if (typeof vnode.tag !== "string" && vnode.tag.onremove) vnode.tag.onremove.call(vnode.state, vnode)
|
|
||||||
|
|
||||||
var children = vnode.children
|
|
||||||
if (children instanceof Array) {
|
|
||||||
for (var i = 0; i < children.length; i++) {
|
|
||||||
var child = children[i]
|
|
||||||
if (child != null) onremove(child)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//attrs
|
|
||||||
function setAttrs(vnode, attrs) {
|
|
||||||
for (var key in attrs) {
|
|
||||||
setAttr(vnode, key, null, attrs[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function setAttr(vnode, key, old, value) {
|
|
||||||
var element = vnode.dom
|
|
||||||
if (key === "key" || (old === value && !isFormAttribute(vnode, key)) || typeof value === "undefined" || isLifecycleMethod(key)) return
|
|
||||||
var nsLastIndex = key.indexOf(":")
|
|
||||||
if (nsLastIndex > -1 && key.substr(0, nsLastIndex) === "xlink") {
|
|
||||||
element.setAttributeNS("http://www.w3.org/1999/xlink", key.slice(nsLastIndex + 1), value)
|
|
||||||
}
|
|
||||||
else if (key[0] === "o" && key[1] === "n" && typeof value === "function") updateEvent(vnode, key, value)
|
|
||||||
else if (key === "style") updateStyle(element, old, value)
|
|
||||||
else if (key in element && !isAttribute(key) && vnode.ns === undefined) {
|
|
||||||
//setting input[value] to same value by typing on focused element moves cursor to end in Chrome
|
|
||||||
if (vnode.tag === "input" && key === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return
|
|
||||||
element[key] = value
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (typeof value === "boolean") {
|
|
||||||
if (value) element.setAttribute(key, "")
|
|
||||||
else element.removeAttribute(key)
|
|
||||||
}
|
|
||||||
else element.setAttribute(key === "className" ? "class" : key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function setLateAttrs(vnode) {
|
|
||||||
var attrs = vnode.attrs
|
|
||||||
if (vnode.tag === "select" && attrs != null) {
|
|
||||||
if ("value" in attrs) setAttr(vnode, "value", null, attrs.value)
|
|
||||||
if ("selectedIndex" in attrs) setAttr(vnode, "selectedIndex", null, attrs.selectedIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function updateAttrs(vnode, old, attrs) {
|
|
||||||
if (attrs != null) {
|
|
||||||
for (var key in attrs) {
|
|
||||||
setAttr(vnode, key, old && old[key], attrs[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (old != null) {
|
|
||||||
for (var key in old) {
|
|
||||||
if (attrs == null || !(key in attrs)) {
|
|
||||||
if (key !== "key") vnode.dom.removeAttribute(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function isFormAttribute(vnode, attr) {
|
|
||||||
return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && vnode.dom === $doc.activeElement
|
|
||||||
}
|
|
||||||
function isLifecycleMethod(attr) {
|
|
||||||
return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "shouldUpdate"
|
|
||||||
}
|
|
||||||
function isAttribute(attr) {
|
|
||||||
return attr === "href" || attr === "list" || attr === "form"// || attr === "type" || attr === "width" || attr === "height"
|
|
||||||
}
|
|
||||||
function hasIntegrationMethods(vnode) {
|
|
||||||
return vnode.attrs != null && (vnode.attrs.oncreate || vnode.attrs.onupdate || vnode.attrs.onbeforeremove || vnode.attrs.onremove)
|
|
||||||
}
|
|
||||||
|
|
||||||
//style
|
|
||||||
function updateStyle(element, old, style) {
|
|
||||||
if (style == null) element.style = ""
|
|
||||||
else if (typeof style === "string") element.style = style
|
|
||||||
else {
|
|
||||||
if (typeof old === "string") element.style = ""
|
|
||||||
for (var key in style) {
|
|
||||||
element.style[key] = style[key]
|
|
||||||
}
|
|
||||||
if (old != null && typeof old !== "string") {
|
|
||||||
for (var key in old) {
|
|
||||||
if (!(key in style)) element.style[key] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//event
|
|
||||||
function updateEvent(vnode, key, value) {
|
|
||||||
var element = vnode.dom
|
|
||||||
var callback = function(e) {
|
|
||||||
var result = value.call(element, e)
|
|
||||||
if (typeof onevent === "function") onevent.call(element, e)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
if (key in element) element[key] = callback
|
|
||||||
else {
|
|
||||||
var eventName = key.slice(2)
|
|
||||||
if (vnode.events === undefined) vnode.events = {}
|
|
||||||
if (vnode.events[key] != null) element.removeEventListener(eventName, vnode.events[key], false)
|
|
||||||
vnode.events[key] = callback
|
|
||||||
element.addEventListener(eventName, vnode.events[key], false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//lifecycle
|
|
||||||
function initLifecycle(source, vnode, hooks) {
|
|
||||||
if (source.oninit != null) source.oninit.call(vnode.state, vnode)
|
|
||||||
if (source.oncreate != null) hooks.push(source.oncreate.bind(vnode.state, vnode))
|
|
||||||
}
|
|
||||||
function updateLifecycle(source, vnode, hooks, recycling) {
|
|
||||||
if (recycling) initLifecycle(source, vnode, hooks)
|
|
||||||
else if (source.onupdate != null) hooks.push(source.onupdate.bind(vnode.state, vnode))
|
|
||||||
}
|
|
||||||
function shouldUpdate(vnode, old) {
|
|
||||||
var forceVnodeUpdate, forceComponentUpdate
|
|
||||||
if (vnode.attrs != null && typeof vnode.attrs.shouldUpdate === "function") forceVnodeUpdate = vnode.attrs.shouldUpdate.call(vnode.state, vnode, old)
|
|
||||||
if (typeof vnode.tag !== "string" && typeof vnode.tag.shouldUpdate === "function") forceComponentUpdate = vnode.tag.shouldUpdate.call(vnode.state, vnode, old)
|
|
||||||
if (!(forceVnodeUpdate === undefined && forceComponentUpdate === undefined) && !forceVnodeUpdate && !forceComponentUpdate) {
|
|
||||||
vnode.dom = old.dom
|
|
||||||
vnode.domSize = old.domSize
|
|
||||||
vnode.instance = old.instance
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
function copy(data) {
|
|
||||||
if (data instanceof Array) {
|
|
||||||
var output = []
|
|
||||||
for (var i = 0; i < data.length; i++) output[i] = copy(data[i])
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
else if (typeof data === "object") {
|
|
||||||
var output = {}
|
|
||||||
for (var i in data) output[i] = copy(data[i])
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
function render(dom, vnodes) {
|
|
||||||
var hooks = []
|
|
||||||
var active = $doc.activeElement
|
|
||||||
if (dom.vnodes == null) dom.vnodes = []
|
|
||||||
|
|
||||||
if (!(vnodes instanceof Array)) vnodes = [vnodes]
|
|
||||||
updateNodes(dom, dom.vnodes, Node.normalizeChildren(vnodes), hooks, null)
|
|
||||||
for (var i = 0; i < hooks.length; i++) hooks[i]()
|
|
||||||
dom.vnodes = vnodes
|
|
||||||
if ($doc.activeElement !== active) active.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
return {render: render, setEventCallback: setEventCallback}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var createRenderer = function($window) {
|
|
||||||
var $doc = $window.document
|
var $doc = $window.document
|
||||||
|
|
||||||
var onevent
|
var onevent
|
||||||
|
|
@ -1070,12 +579,11 @@ var createRenderer = function($window) {
|
||||||
|
|
||||||
var throttle = function(callback) {
|
var throttle = function(callback) {
|
||||||
//60fps translates to 16.6ms, round it down since setTimeout requires int
|
//60fps translates to 16.6ms, round it down since setTimeout requires int
|
||||||
var time = 16
|
var time = 16
|
||||||
var last = 0, pending = null
|
var last = 0, pending = null
|
||||||
var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
|
var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
|
||||||
return function(synchronous) {
|
return function(synchronous) {
|
||||||
var now = new Date().getTime()
|
var now = new Date().getTime()
|
||||||
var diff = now - last
|
|
||||||
if (synchronous === true || last === 0 || now - last >= time) {
|
if (synchronous === true || last === 0 || now - last >= time) {
|
||||||
last = now
|
last = now
|
||||||
callback()
|
callback()
|
||||||
|
|
@ -1090,9 +598,8 @@ var throttle = function(callback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var apiMounter = function($window, redraw) {
|
||||||
var createMounter = function($window, redraw) {
|
var renderer = coreRenderer($window)
|
||||||
var renderer = createRenderer($window)
|
|
||||||
return function(root, component) {
|
return function(root, component) {
|
||||||
var run = throttle(function() {
|
var run = throttle(function() {
|
||||||
renderer.render(root, {tag: component})
|
renderer.render(root, {tag: component})
|
||||||
|
|
@ -1105,8 +612,7 @@ var createMounter = function($window, redraw) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var buildQueryString = function(object) {
|
||||||
var buildQueryString = function buildQueryString(object) {
|
|
||||||
if (Object.prototype.toString.call(object) !== "[object Object]") return ""
|
if (Object.prototype.toString.call(object) !== "[object Object]") return ""
|
||||||
|
|
||||||
var args = []
|
var args = []
|
||||||
|
|
@ -1129,7 +635,7 @@ var buildQueryString = function buildQueryString(object) {
|
||||||
else args.push(encodeURIComponent(key) + (value != null && value !== "" ? "=" + encodeURIComponent(value) : ""))
|
else args.push(encodeURIComponent(key) + (value != null && value !== "" ? "=" + encodeURIComponent(value) : ""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var parseQueryString = function parseQueryString(string) {
|
var parseQueryString = function(string) {
|
||||||
if (string === "" || string == null) return {}
|
if (string === "" || string == null) return {}
|
||||||
if (string.charAt(0) === "?") string = string.slice(1)
|
if (string.charAt(0) === "?") string = string.slice(1)
|
||||||
|
|
||||||
|
|
@ -1170,7 +676,7 @@ var parseQueryString = function parseQueryString(string) {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
var createRouter = function($window) {
|
var coreRouter = function($window) {
|
||||||
var supportsPushState = typeof $window.history.pushState === "function" && $window.location.protocol !== "file:"
|
var supportsPushState = typeof $window.history.pushState === "function" && $window.location.protocol !== "file:"
|
||||||
|
|
||||||
var prefix = "#!"
|
var prefix = "#!"
|
||||||
|
|
@ -1274,10 +780,9 @@ var createRouter = function($window) {
|
||||||
return {setPrefix: setPrefix, getPath: getPath, setPath: setPath, defineRoutes: defineRoutes, link: link}
|
return {setPrefix: setPrefix, getPath: getPath, setPath: setPath, defineRoutes: defineRoutes, link: link}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var apiRouter = function($window, redraw) {
|
||||||
var createRouterInstance = function($window, redraw) {
|
var renderer = coreRenderer($window)
|
||||||
var renderer = createRenderer($window)
|
var router = coreRouter($window)
|
||||||
var router = createRouter($window)
|
|
||||||
var route = function(root, defaultRoute, routes) {
|
var route = function(root, defaultRoute, routes) {
|
||||||
var replay = router.defineRoutes(routes, function(component, args) {
|
var replay = router.defineRoutes(routes, function(component, args) {
|
||||||
renderer.render(root, {tag: component, attrs: args})
|
renderer.render(root, {tag: component, attrs: args})
|
||||||
|
|
@ -1295,9 +800,7 @@ var createRouterInstance = function($window, redraw) {
|
||||||
return route
|
return route
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var coreRequester = function($window, Promise) {
|
||||||
|
|
||||||
var createRequester = function($window, Promise) {
|
|
||||||
var callbackCount = 0
|
var callbackCount = 0
|
||||||
|
|
||||||
function ajax(args) {
|
function ajax(args) {
|
||||||
|
|
@ -1362,7 +865,7 @@ var createRequester = function($window, Promise) {
|
||||||
resolve(data)
|
resolve(data)
|
||||||
$window[callbackKey] = undefined
|
$window[callbackKey] = undefined
|
||||||
}
|
}
|
||||||
script.onerror = function(e) {
|
script.onerror = function() {
|
||||||
script.parentNode.removeChild(script)
|
script.parentNode.removeChild(script)
|
||||||
reject(new Error("JSONP request failed"))
|
reject(new Error("JSONP request failed"))
|
||||||
$window[callbackKey] = undefined
|
$window[callbackKey] = undefined
|
||||||
|
|
@ -1407,15 +910,16 @@ var createRequester = function($window, Promise) {
|
||||||
|
|
||||||
return {ajax: ajax, jsonp: jsonp}
|
return {ajax: ajax, jsonp: jsonp}
|
||||||
}
|
}
|
||||||
|
|
||||||
var redraw = {run: function() {}}
|
var redraw = {run: function() {}}
|
||||||
|
|
||||||
m.redraw = function() {
|
m.redraw = function() {
|
||||||
redraw.run()
|
redraw.run()
|
||||||
}
|
}
|
||||||
m.trust = trust
|
m.trust = trust
|
||||||
m.render = createRenderer(window).render
|
m.render = coreRenderer(window).render
|
||||||
m.mount = createMounter(window, redraw)
|
m.mount = apiMounter(window, redraw)
|
||||||
m.route = createRouterInstance(window, redraw)
|
m.route = apiRouter(window, redraw)
|
||||||
m.request = createRequester(window, Promise).ajax
|
m.request = coreRequester(window, Promise).ajax
|
||||||
|
|
||||||
module.exports = m
|
module.exports = m
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
module.exports = function buildQueryString(object) {
|
module.exports = function(object) {
|
||||||
if (Object.prototype.toString.call(object) !== "[object Object]") return ""
|
if (Object.prototype.toString.call(object) !== "[object Object]") return ""
|
||||||
|
|
||||||
var args = []
|
var args = []
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
module.exports = function parseQueryString(string) {
|
module.exports = function(string) {
|
||||||
if (string === "" || string == null) return {}
|
if (string === "" || string == null) return {}
|
||||||
if (string.charAt(0) === "?") string = string.slice(1)
|
if (string.charAt(0) === "?") string = string.slice(1)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue