bundler variable disambiguation

This commit is contained in:
Leo Horie 2016-05-20 22:38:10 -04:00
parent 2ca8fa6e66
commit e5391a1957
10 changed files with 75 additions and 542 deletions

View file

@ -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!

View file

@ -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})

View file

@ -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})

View file

@ -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

View file

@ -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() {

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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 = []

View file

@ -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)