Merge pull request #713 from impinball/names
Shorten some code, allow the JIT to optimize critical methods.
This commit is contained in:
commit
db56f9b635
3 changed files with 156 additions and 125 deletions
277
mithril.js
277
mithril.js
|
|
@ -1,9 +1,20 @@
|
|||
var m = (function app(window, undefined) {
|
||||
var OBJECT = "[object Object]", ARRAY = "[object Array]", STRING = "[object String]", FUNCTION = "function";
|
||||
function isFunction(object) {
|
||||
return typeof object === "function";
|
||||
}
|
||||
function isObject(object) {
|
||||
return type.call(object) === "[object Object]";
|
||||
}
|
||||
function isString(object) {
|
||||
return type.call(object) === "[object String]";
|
||||
}
|
||||
var isArray = Array.isArray || function (object) {
|
||||
return type.call(object) === "[object Array]";
|
||||
};
|
||||
var type = {}.toString;
|
||||
var parser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g, attrParser = /\[(.+?)(?:=("|'|)(.*?)\2)?\]/;
|
||||
var voidElements = /^(AREA|BASE|BR|COL|COMMAND|EMBED|HR|IMG|INPUT|KEYGEN|LINK|META|PARAM|SOURCE|TRACK|WBR)$/;
|
||||
var noop = function() {};
|
||||
var noop = function () {};
|
||||
|
||||
// caching commonly used variables
|
||||
var $document, $location, $requestAnimationFrame, $cancelAnimationFrame;
|
||||
|
|
@ -32,16 +43,18 @@ var m = (function app(window, undefined) {
|
|||
* @param {...mNode=[]} Zero or more Mithril child nodes. Can be an array, or splat (optional)
|
||||
*
|
||||
*/
|
||||
function m() {
|
||||
var args = [].slice.call(arguments);
|
||||
if (type.call(args[0]) === OBJECT) return parameterize(args[0], args.slice(1));
|
||||
var hasAttrs = args[1] != null && type.call(args[1]) === OBJECT && !("tag" in args[1] || "view" in args[1]) && !("subtree" in args[1]);
|
||||
var attrs = hasAttrs ? args[1] : {};
|
||||
function m(tag, pairs) {
|
||||
for (var args = [], i = 1; i < arguments.length; i++) {
|
||||
args[i - 1] = arguments[i];
|
||||
}
|
||||
if (isObject(tag)) return parameterize(tag, args);
|
||||
var hasAttrs = pairs != null && isObject(pairs) && !("tag" in pairs || "view" in pairs || "subtree" in pairs);
|
||||
var attrs = hasAttrs ? pairs : {};
|
||||
var classAttrName = "class" in attrs ? "class" : "className";
|
||||
var cell = {tag: "div", attrs: {}};
|
||||
var match, classes = [];
|
||||
if (type.call(args[0]) != STRING) throw new Error("selector in m(selector, attrs, children) should be a string");
|
||||
while (match = parser.exec(args[0])) {
|
||||
if (!isString(tag)) throw new Error("selector in m(selector, attrs, children) should be a string");
|
||||
while (match = parser.exec(tag)) {
|
||||
if (match[1] === "" && match[2]) cell.tag = match[2];
|
||||
else if (match[1] === "#") cell.attrs.id = match[2];
|
||||
else if (match[1] === ".") classes.push(match[2]);
|
||||
|
|
@ -51,8 +64,8 @@ var m = (function app(window, undefined) {
|
|||
}
|
||||
}
|
||||
|
||||
var children = hasAttrs ? args.slice(2) : args.slice(1);
|
||||
if (children.length === 1 && type.call(children[0]) === ARRAY) {
|
||||
var children = hasAttrs ? args.slice(1) : args;
|
||||
if (children.length === 1 && isArray(children[0])) {
|
||||
cell.children = children[0];
|
||||
}
|
||||
else {
|
||||
|
|
@ -68,10 +81,36 @@ var m = (function app(window, undefined) {
|
|||
else cell.attrs[attrName] = attrs[attrName];
|
||||
}
|
||||
}
|
||||
if (classes.length > 0) cell.attrs[classAttrName] = classes.join(" ");
|
||||
if (classes.length) cell.attrs[classAttrName] = classes.join(" ");
|
||||
|
||||
return cell;
|
||||
}
|
||||
function forEach(list, f) {
|
||||
for (var i = 0; i < list.length && !f(list[i], i++);) {}
|
||||
}
|
||||
function forKeys(list, f) {
|
||||
forEach(list, function (attrs, i) {
|
||||
return (attrs = attrs && attrs.attrs) && attrs.key != null && f(attrs, i);
|
||||
});
|
||||
}
|
||||
// This function was causing deopts in Chrome.
|
||||
function dataToString(data) {
|
||||
//data.toString() might throw or return null if data is the return value of Console.log in Firefox (behavior depends on version)
|
||||
try {
|
||||
if (data == null || data.toString() == null) data = "";
|
||||
} catch (e) {
|
||||
data = "";
|
||||
}
|
||||
return data;
|
||||
}
|
||||
// This function was causing deopts in Chrome.
|
||||
function injectTextNode(parentElement, first, index, data) {
|
||||
try {
|
||||
parentElement.insertBefore(first, parentElement.childNodes[index] || null);
|
||||
first.nodeValue = data;
|
||||
}
|
||||
catch (e) {} //IE erroneously throws error when appending an empty text node after a null
|
||||
}
|
||||
function build(parentElement, parentTag, parentCache, parentIndex, data, cached, shouldReattach, index, editable, namespace, configs) {
|
||||
//`build` is a recursive function that manages creation/diffing/removal of DOM elements based on comparison between `data` and `cached`
|
||||
//the diff algorithm can be summarized as this:
|
||||
|
|
@ -98,15 +137,13 @@ var m = (function app(window, undefined) {
|
|||
//there's logic that relies on the assumption that null and undefined data are equivalent to empty strings
|
||||
//- this prevents lifecycle surprises from procedural helpers that mix implicit and explicit return statements (e.g. function foo() {if (cond) return m("div")}
|
||||
//- it simplifies diffing code
|
||||
//data.toString() might throw or return null if data is the return value of Console.log in Firefox (behavior depends on version)
|
||||
try { if (data == null || data.toString() == null) data = ""; } catch (e) { data = ""; }
|
||||
data = dataToString(data);
|
||||
if (data.subtree === "retain") return cached;
|
||||
var cachedType = type.call(cached), dataType = type.call(data);
|
||||
if (cached == null || cachedType !== dataType) {
|
||||
if (cached == null || type.call(cached) !== type.call(data)) {
|
||||
if (cached != null) {
|
||||
if (parentCache && parentCache.nodes) {
|
||||
var offset = index - parentIndex;
|
||||
var end = offset + (dataType === ARRAY ? data : cached.nodes).length;
|
||||
var end = offset + (isArray(data) ? data : cached.nodes).length;
|
||||
clear(parentCache.nodes.slice(offset, end), parentCache.slice(offset, end));
|
||||
}
|
||||
else if (cached.nodes) clear(cached.nodes, cached);
|
||||
|
|
@ -116,13 +153,12 @@ var m = (function app(window, undefined) {
|
|||
cached.nodes = [];
|
||||
}
|
||||
|
||||
if (dataType === ARRAY) {
|
||||
if (isArray(data)) {
|
||||
//recursively flatten array
|
||||
for (var i = 0, len = data.length; i < len; i++) {
|
||||
if (type.call(data[i]) === ARRAY) {
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (isArray(data[i])) {
|
||||
data = data.concat.apply([], data);
|
||||
i--; //check current index again and flatten until there are no more nested arrays at that index
|
||||
len = data.length;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,55 +171,51 @@ var m = (function app(window, undefined) {
|
|||
//4) for each key, handle its corresponding action as marked in previous steps
|
||||
var DELETION = 1, INSERTION = 2 , MOVE = 3;
|
||||
var existing = {}, shouldMaintainIdentities = false;
|
||||
for (var i = 0; i < cached.length; i++) {
|
||||
if (cached[i] && cached[i].attrs && cached[i].attrs.key != null) {
|
||||
shouldMaintainIdentities = true;
|
||||
existing[cached[i].attrs.key] = {action: DELETION, index: i};
|
||||
}
|
||||
}
|
||||
forKeys(cached, function (attrs, i) {
|
||||
shouldMaintainIdentities = true;
|
||||
existing[cached[i].attrs.key] = {action: DELETION, index: i};
|
||||
});
|
||||
|
||||
var guid = 0;
|
||||
for (var i = 0, len = data.length; i < len; i++) {
|
||||
if (data[i] && data[i].attrs && data[i].attrs.key != null) {
|
||||
for (var j = 0, len = data.length; j < len; j++) {
|
||||
if (data[j] && data[j].attrs && data[j].attrs.key == null) data[j].attrs.key = "__mithril__" + guid++;
|
||||
forKeys(data, function () {
|
||||
forEach(data, function (attrs) {
|
||||
if ((attrs = attrs && attrs.attrs) && attrs.key == null) {
|
||||
attrs.key = "__mithril__" + guid++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return 1;
|
||||
});
|
||||
|
||||
if (shouldMaintainIdentities) {
|
||||
var keysDiffer = false;
|
||||
if (data.length != cached.length) keysDiffer = true;
|
||||
else for (var i = 0, cachedCell, dataCell; cachedCell = cached[i], dataCell = data[i]; i++) {
|
||||
if (cachedCell.attrs && dataCell.attrs && cachedCell.attrs.key != dataCell.attrs.key) {
|
||||
keysDiffer = true;
|
||||
break;
|
||||
}
|
||||
var keysDiffer = data.length != cached.length;
|
||||
if (!keysDiffer) {
|
||||
forKeys(data, function (attrs, i) {
|
||||
var cachedCell = cached[i];
|
||||
return keysDiffer = cachedCell && cachedCell.attrs && cachedCell.attrs.key != attrs.key;
|
||||
});
|
||||
}
|
||||
|
||||
if (keysDiffer) {
|
||||
for (var i = 0, len = data.length; i < len; i++) {
|
||||
if (data[i] && data[i].attrs) {
|
||||
if (data[i].attrs.key != null) {
|
||||
var key = data[i].attrs.key;
|
||||
if (!existing[key]) existing[key] = {action: INSERTION, index: i};
|
||||
else existing[key] = {
|
||||
action: MOVE,
|
||||
index: i,
|
||||
from: existing[key].index,
|
||||
element: cached.nodes[existing[key].index] || $document.createElement("div")
|
||||
};
|
||||
}
|
||||
forKeys(data, function (attrs, i) {
|
||||
var key = attrs.key;
|
||||
if (existing[key]) {
|
||||
existing[key] = {
|
||||
action: MOVE,
|
||||
index: i,
|
||||
from: existing[key].index,
|
||||
element: cached.nodes[existing[key].index] || $document.createElement("div")
|
||||
};
|
||||
} else {
|
||||
existing[key] = {action: INSERTION, index: i};
|
||||
}
|
||||
}
|
||||
});
|
||||
var actions = [];
|
||||
for (var prop in existing) actions.push(existing[prop]);
|
||||
var changes = actions.sort(sortChanges);
|
||||
var newCached = new Array(cached.length);
|
||||
newCached.nodes = cached.nodes.slice();
|
||||
|
||||
for (var i = 0, change; change = changes[i]; i++) {
|
||||
forEach(changes, function (change) {
|
||||
if (change.action === DELETION) {
|
||||
clear(cached[change.index].nodes, cached[change.index]);
|
||||
newCached.splice(change.index, 1);
|
||||
|
|
@ -203,43 +235,46 @@ var m = (function app(window, undefined) {
|
|||
newCached[change.index] = cached[change.from];
|
||||
newCached.nodes[change.index] = change.element;
|
||||
}
|
||||
}
|
||||
});
|
||||
cached = newCached;
|
||||
}
|
||||
}
|
||||
//end key algorithm
|
||||
|
||||
for (var i = 0, cacheCount = 0, len = data.length; i < len; i++) {
|
||||
var cacheCount = 0;
|
||||
forEach(data, function (entry) {
|
||||
//diff each item in the array
|
||||
var item = build(parentElement, parentTag, cached, index, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, editable, namespace, configs);
|
||||
if (item === undefined) continue;
|
||||
if (!item.nodes.intact) intact = false;
|
||||
if (item.$trusted) {
|
||||
//fix offset of next element if item was a trusted string w/ more than one html element
|
||||
//the first clause in the regexp matches elements
|
||||
//the second clause (after the pipe) matches text nodes
|
||||
subArrayCount += (item.match(/<[^\/]|\>\s*[^<]/g) || [0]).length;
|
||||
var item = build(parentElement, parentTag, cached, index, entry, cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, editable, namespace, configs);
|
||||
if (item !== undefined) {
|
||||
if (!item.nodes.intact) intact = false;
|
||||
if (item.$trusted) {
|
||||
//fix offset of next element if item was a trusted string w/ more than one html element
|
||||
//the first clause in the regexp matches elements
|
||||
//the second clause (after the pipe) matches text nodes
|
||||
subArrayCount += (item.match(/<[^\/]|\>\s*[^<]/g) || [0]).length;
|
||||
}
|
||||
else subArrayCount += isArray(item) ? item.length : 1;
|
||||
cached[cacheCount++] = item;
|
||||
}
|
||||
else subArrayCount += type.call(item) === ARRAY ? item.length : 1;
|
||||
cached[cacheCount++] = item;
|
||||
}
|
||||
});
|
||||
|
||||
if (!intact) {
|
||||
//diff the array itself
|
||||
|
||||
//update the list of DOM nodes by collecting the nodes from each item
|
||||
for (var i = 0, len = data.length; i < len; i++) {
|
||||
forEach(data, function (_, i) {
|
||||
if (cached[i] != null) nodes.push.apply(nodes, cached[i].nodes);
|
||||
}
|
||||
});
|
||||
//remove items from the end of the array if the new array is shorter than the old one
|
||||
//if errors ever happen here, the issue is most likely a bug in the construction of the `cached` data structure somewhere earlier in the program
|
||||
for (var i = 0, node; node = cached.nodes[i]; i++) {
|
||||
forEach(cached.nodes, function (node, i) {
|
||||
if (node.parentNode != null && nodes.indexOf(node) < 0) clear([node], [cached[i]]);
|
||||
}
|
||||
});
|
||||
if (data.length < cached.length) cached.length = data.length;
|
||||
cached.nodes = nodes;
|
||||
}
|
||||
}
|
||||
else if (data != null && dataType === OBJECT) {
|
||||
else if (data != null && isObject(data)) {
|
||||
var views = [], controllers = [];
|
||||
while (data.view) {
|
||||
var view = data.view.$original || data.view;
|
||||
|
|
@ -265,14 +300,14 @@ var m = (function app(window, undefined) {
|
|||
//if an element is different enough from the one in cache, recreate it
|
||||
if (data.tag != cached.tag || dataAttrKeys.sort().join() != Object.keys(cached.attrs).sort().join() || data.attrs.id != cached.attrs.id || data.attrs.key != cached.attrs.key || (m.redraw.strategy() == "all" && (!cached.configContext || cached.configContext.retain !== true)) || (m.redraw.strategy() == "diff" && cached.configContext && cached.configContext.retain === false)) {
|
||||
if (cached.nodes.length) clear(cached.nodes);
|
||||
if (cached.configContext && typeof cached.configContext.onunload === FUNCTION) cached.configContext.onunload();
|
||||
if (cached.configContext && isFunction(cached.configContext.onunload)) cached.configContext.onunload();
|
||||
if (cached.controllers) {
|
||||
for (var i = 0, controller; controller = cached.controllers[i]; i++) {
|
||||
if (typeof controller.onunload === FUNCTION) controller.onunload({preventDefault: noop});
|
||||
if (isFunction(controller.onunload)) controller.onunload({preventDefault: noop});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type.call(data.tag) != STRING) return;
|
||||
if (!isString(data.tag)) return;
|
||||
|
||||
var node, isNew = cached.nodes.length === 0;
|
||||
if (data.attrs.xmlns) namespace = data.attrs.xmlns;
|
||||
|
|
@ -321,7 +356,7 @@ var m = (function app(window, undefined) {
|
|||
if (shouldReattach === true && node != null) parentElement.insertBefore(node, parentElement.childNodes[index] || null);
|
||||
}
|
||||
//schedule configs to be called. They are called after `build` finishes running
|
||||
if (typeof data.attrs["config"] === FUNCTION) {
|
||||
if (isFunction(data.attrs["config"])) {
|
||||
var context = cached.configContext = cached.configContext || {};
|
||||
|
||||
// bind
|
||||
|
|
@ -333,7 +368,7 @@ var m = (function app(window, undefined) {
|
|||
configs.push(callback(data, [node, !isNew, context, cached]));
|
||||
}
|
||||
}
|
||||
else if (typeof data != FUNCTION) {
|
||||
else if (!isFunction(data)) {
|
||||
//handle text nodes
|
||||
var nodes;
|
||||
if (cached.nodes.length === 0) {
|
||||
|
|
@ -364,11 +399,7 @@ var m = (function app(window, undefined) {
|
|||
clear(cached.nodes, cached);
|
||||
nodes = [$document.createTextNode(data)];
|
||||
}
|
||||
try {
|
||||
parentElement.insertBefore(nodes[0], parentElement.childNodes[index] || null);
|
||||
nodes[0].nodeValue = data;
|
||||
}
|
||||
catch (e) {} //IE erroneously throws error when appending an empty text node after a null
|
||||
injectTextNode(parentElement, nodes[0], index, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -391,11 +422,11 @@ var m = (function app(window, undefined) {
|
|||
//`config` isn't a real attributes, so ignore it
|
||||
if (attrName === "config" || attrName == "key") continue;
|
||||
//hook event handlers to the auto-redrawing system
|
||||
else if (typeof dataAttr === FUNCTION && attrName.indexOf("on") === 0) {
|
||||
else if (isFunction(dataAttr) && attrName.indexOf("on") === 0) {
|
||||
node[attrName] = autoredraw(dataAttr, node);
|
||||
}
|
||||
//handle `style: {...}`
|
||||
else if (attrName === "style" && dataAttr != null && type.call(dataAttr) === OBJECT) {
|
||||
else if (attrName === "style" && dataAttr != null && isObject(dataAttr)) {
|
||||
for (var rule in dataAttr) {
|
||||
if (cachedAttr == null || cachedAttr[rule] !== dataAttr[rule]) node.style[rule] = dataAttr[rule];
|
||||
}
|
||||
|
|
@ -442,17 +473,17 @@ var m = (function app(window, undefined) {
|
|||
if (nodes.length != 0) nodes.length = 0;
|
||||
}
|
||||
function unload(cached) {
|
||||
if (cached.configContext && typeof cached.configContext.onunload === FUNCTION) {
|
||||
if (cached.configContext && isFunction(cached.configContext.onunload)) {
|
||||
cached.configContext.onunload();
|
||||
cached.configContext.onunload = null;
|
||||
}
|
||||
if (cached.controllers) {
|
||||
for (var i = 0, controller; controller = cached.controllers[i]; i++) {
|
||||
if (typeof controller.onunload === FUNCTION) controller.onunload({preventDefault: noop});
|
||||
if (isFunction(controller.onunload)) controller.onunload({preventDefault: noop});
|
||||
}
|
||||
}
|
||||
if (cached.children) {
|
||||
if (type.call(cached.children) === ARRAY) {
|
||||
if (isArray(cached.children)) {
|
||||
for (var i = 0, child; child = cached.children[i]; i++) unload(child);
|
||||
}
|
||||
else if (cached.children.tag) unload(cached.children);
|
||||
|
|
@ -544,7 +575,7 @@ var m = (function app(window, undefined) {
|
|||
|
||||
m.prop = function (store) {
|
||||
//note: using non-strict equality check here because we're checking if store is null OR undefined
|
||||
if (((store != null && type.call(store) === OBJECT) || typeof store === FUNCTION) && typeof store.then === FUNCTION) {
|
||||
if ((store != null && isObject(store) || isFunction(store)) && isFunction(store.then)) {
|
||||
return propify(store);
|
||||
}
|
||||
|
||||
|
|
@ -558,7 +589,7 @@ var m = (function app(window, undefined) {
|
|||
return (component.controller || noop).apply(this, args) || this;
|
||||
};
|
||||
var view = function(ctrl) {
|
||||
if (arguments.length > 1) args = args.concat([].slice.call(arguments, 1));
|
||||
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
|
||||
return component.view.apply(component, args ? [ctrl].concat(args) : [ctrl]);
|
||||
};
|
||||
view.$original = component.view;
|
||||
|
|
@ -567,7 +598,8 @@ var m = (function app(window, undefined) {
|
|||
return output;
|
||||
}
|
||||
m.component = function(component) {
|
||||
return parameterize(component, [].slice.call(arguments, 1));
|
||||
for (var args = [], i = 1; i < arguments.length; i++) args[i - 1] = arguments[i];
|
||||
return parameterize(component, args);
|
||||
};
|
||||
m.mount = m.module = function(root, component) {
|
||||
if (!root) throw new Error("Please ensure the DOM element exists before rendering a template into it.");
|
||||
|
|
@ -588,7 +620,7 @@ var m = (function app(window, undefined) {
|
|||
}
|
||||
else unloaders = [];
|
||||
|
||||
if (controllers[index] && typeof controllers[index].onunload === FUNCTION) {
|
||||
if (controllers[index] && isFunction(controllers[index].onunload)) {
|
||||
controllers[index].onunload(event);
|
||||
}
|
||||
|
||||
|
|
@ -597,7 +629,7 @@ var m = (function app(window, undefined) {
|
|||
m.startComputation();
|
||||
roots[index] = root;
|
||||
if (arguments.length > 2) component = subcomponent(component, [].slice.call(arguments, 2));
|
||||
var currentComponent = topComponent = component = component || {controller: function() {}};
|
||||
var currentComponent = topComponent = component = component || {controller: noop};
|
||||
var constructor = component.controller || noop;
|
||||
var controller = new constructor;
|
||||
//controllers may call m.mount recursively (via m.route redirects, for example)
|
||||
|
|
@ -618,7 +650,7 @@ var m = (function app(window, undefined) {
|
|||
try {
|
||||
//lastRedrawId is a positive number if a second redraw is requested before the next animation frame
|
||||
//lastRedrawID is null if it's the first redraw and not an event handler
|
||||
if (lastRedrawId && force !== true) {
|
||||
if (lastRedrawId && !force) {
|
||||
//when setTimeout: only reschedule redraw if time between now and previous redraw is bigger than a frame, otherwise keep currently scheduled timeout
|
||||
//when rAF: always reschedule redraw
|
||||
if ($requestAnimationFrame === window.requestAnimationFrame || new Date - lastRedrawCallTime > FRAME_BUDGET) {
|
||||
|
|
@ -682,18 +714,17 @@ var m = (function app(window, undefined) {
|
|||
//routing
|
||||
var modes = {pathname: "", hash: "#", search: "?"};
|
||||
var redirect = noop, routeParams, currentRoute, isDefaultRoute = false;
|
||||
m.route = function() {
|
||||
m.route = function(root, arg1, arg2, vdom) {
|
||||
//m.route()
|
||||
if (arguments.length === 0) return currentRoute;
|
||||
//m.route(el, defaultRoute, routes)
|
||||
else if (arguments.length === 3 && type.call(arguments[1]) === STRING) {
|
||||
var root = arguments[0], defaultRoute = arguments[1], router = arguments[2];
|
||||
else if (arguments.length === 3 && isString(arg1)) {
|
||||
redirect = function(source) {
|
||||
var path = currentRoute = normalizeRoute(source);
|
||||
if (!routeByValue(root, router, path)) {
|
||||
if (!routeByValue(root, arg2, path)) {
|
||||
if (isDefaultRoute) throw new Error("Ensure the default route matches one of the routes defined in m.route");
|
||||
isDefaultRoute = true;
|
||||
m.route(defaultRoute, true);
|
||||
m.route(arg1, true);
|
||||
isDefaultRoute = false;
|
||||
}
|
||||
};
|
||||
|
|
@ -709,32 +740,30 @@ var m = (function app(window, undefined) {
|
|||
window[listener]();
|
||||
}
|
||||
//config: m.route
|
||||
else if (arguments[0].addEventListener || arguments[0].attachEvent) {
|
||||
var element = arguments[0];
|
||||
var vdom = arguments[3];
|
||||
element.href = (m.route.mode !== 'pathname' ? $location.pathname : '') + modes[m.route.mode] + vdom.attrs.href;
|
||||
if (element.addEventListener) {
|
||||
element.removeEventListener("click", routeUnobtrusive);
|
||||
element.addEventListener("click", routeUnobtrusive);
|
||||
else if (root.addEventListener || root.attachEvent) {
|
||||
root.href = (m.route.mode !== 'pathname' ? $location.pathname : '') + modes[m.route.mode] + vdom.attrs.href;
|
||||
if (root.addEventListener) {
|
||||
root.removeEventListener("click", routeUnobtrusive);
|
||||
root.addEventListener("click", routeUnobtrusive);
|
||||
}
|
||||
else {
|
||||
element.detachEvent("onclick", routeUnobtrusive);
|
||||
element.attachEvent("onclick", routeUnobtrusive);
|
||||
root.detachEvent("onclick", routeUnobtrusive);
|
||||
root.attachEvent("onclick", routeUnobtrusive);
|
||||
}
|
||||
}
|
||||
//m.route(route, params, shouldReplaceHistoryEntry)
|
||||
else if (type.call(arguments[0]) === STRING) {
|
||||
else if (isString(root)) {
|
||||
var oldRoute = currentRoute;
|
||||
currentRoute = arguments[0];
|
||||
var args = arguments[1] || {};
|
||||
currentRoute = root;
|
||||
arg1 || {};
|
||||
var queryIndex = currentRoute.indexOf("?");
|
||||
var params = queryIndex > -1 ? parseQueryString(currentRoute.slice(queryIndex + 1)) : {};
|
||||
for (var i in args) params[i] = args[i];
|
||||
for (var i in arg1) params[i] = arg1[i];
|
||||
var querystring = buildQueryString(params);
|
||||
var currentPath = queryIndex > -1 ? currentRoute.slice(0, queryIndex) : currentRoute;
|
||||
if (querystring) currentRoute = currentPath + (currentPath.indexOf("?") === -1 ? "?" : "&") + querystring;
|
||||
|
||||
var shouldReplaceHistoryEntry = (arguments.length === 3 ? arguments[2] : arguments[1]) === true || oldRoute === arguments[0];
|
||||
var shouldReplaceHistoryEntry = (arguments.length === 3 ? arg2 : arg1) === true || oldRoute === root;
|
||||
|
||||
if (window.history.pushState) {
|
||||
computePreRedrawHook = setScroll;
|
||||
|
|
@ -816,8 +845,8 @@ var m = (function app(window, undefined) {
|
|||
var value = object[prop];
|
||||
var valueType = type.call(value);
|
||||
var pair = (value === null) ? encodeURIComponent(key) :
|
||||
valueType === OBJECT ? buildQueryString(value, key) :
|
||||
valueType === ARRAY ? value.reduce(function(memo, item) {
|
||||
isObject(value) ? buildQueryString(value, key) :
|
||||
isArray(value) ? value.reduce(function(memo, item) {
|
||||
if (!duplicates[key]) duplicates[key] = {};
|
||||
if (!duplicates[key][item]) {
|
||||
duplicates[key][item] = true;
|
||||
|
|
@ -839,7 +868,7 @@ var m = (function app(window, undefined) {
|
|||
var key = decodeURIComponent(pair[0]);
|
||||
var value = pair.length == 2 ? decodeURIComponent(pair[1]) : null;
|
||||
if (params[key] != null) {
|
||||
if (type.call(params[key]) !== ARRAY) params[key] = [params[key]];
|
||||
if (!isArray(params[key])) params[key] = [params[key]];
|
||||
params[key].push(value);
|
||||
}
|
||||
else params[key] = value;
|
||||
|
|
@ -921,7 +950,7 @@ var m = (function app(window, undefined) {
|
|||
}
|
||||
|
||||
function thennable(then, successCallback, failureCallback, notThennableCallback) {
|
||||
if (((promiseValue != null && type.call(promiseValue) === OBJECT) || typeof promiseValue === FUNCTION) && typeof then === FUNCTION) {
|
||||
if (((promiseValue != null && isObject(promiseValue)) || isFunction(promiseValue)) && isFunction(then)) {
|
||||
try {
|
||||
// count protects against abuse calls from spec checker
|
||||
var count = 0;
|
||||
|
|
@ -965,10 +994,10 @@ var m = (function app(window, undefined) {
|
|||
fire();
|
||||
}, function() {
|
||||
try {
|
||||
if (state === RESOLVING && typeof successCallback === FUNCTION) {
|
||||
if (state === RESOLVING && isFunction(successCallback)) {
|
||||
promiseValue = successCallback(promiseValue);
|
||||
}
|
||||
else if (state === REJECTING && typeof failureCallback === FUNCTION) {
|
||||
else if (state === REJECTING && isFunction(failureCallback)) {
|
||||
promiseValue = failureCallback(promiseValue);
|
||||
state = RESOLVING;
|
||||
}
|
||||
|
|
@ -1085,13 +1114,13 @@ var m = (function app(window, undefined) {
|
|||
if (options.deserialize === JSON.parse) {
|
||||
xhr.setRequestHeader("Accept", "application/json, text/*");
|
||||
}
|
||||
if (typeof options.config === FUNCTION) {
|
||||
if (isFunction(options.config)) {
|
||||
var maybeXhr = options.config(xhr, options);
|
||||
if (maybeXhr != null) xhr = maybeXhr;
|
||||
}
|
||||
|
||||
var data = options.method === "GET" || !options.data ? "" : options.data;
|
||||
if (data && (type.call(data) != STRING && data.constructor != window.FormData)) {
|
||||
if (data && (!isString(data) && data.constructor != window.FormData)) {
|
||||
throw "Request data should be either be a string or FormData. Check the `serialize` option in `m.request`";
|
||||
}
|
||||
xhr.send(data);
|
||||
|
|
@ -1137,8 +1166,10 @@ var m = (function app(window, undefined) {
|
|||
var unwrap = (e.type === "load" ? xhrOptions.unwrapSuccess : xhrOptions.unwrapError) || identity;
|
||||
var response = unwrap(deserialize(extract(e.target, xhrOptions)), e.target);
|
||||
if (e.type === "load") {
|
||||
if (type.call(response) === ARRAY && xhrOptions.type) {
|
||||
for (var i = 0; i < response.length; i++) response[i] = new xhrOptions.type(response[i]);
|
||||
if (isArray(response) && xhrOptions.type) {
|
||||
forEach(response, function (res, i) {
|
||||
response[i] = new xhrOptions.type(res);
|
||||
});
|
||||
}
|
||||
else if (xhrOptions.type) response = new xhrOptions.type(response);
|
||||
}
|
||||
|
|
|
|||
2
mithril.min.js
vendored
2
mithril.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue