From 6ec6ede38a0224d0cf2911813b340bbfb21989d2 Mon Sep 17 00:00:00 2001 From: Christopher Venning Date: Tue, 9 Jun 2015 19:10:51 -0400 Subject: [PATCH 1/9] DOCS: missing comma in example --- docs/mithril.component.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mithril.component.md b/docs/mithril.component.md index 0766de56..05d5cda6 100644 --- a/docs/mithril.component.md +++ b/docs/mithril.component.md @@ -219,7 +219,7 @@ Components can be placed anywhere a regular element can. If you have components var App = { controller: function() { return {data: [1, 2, 3]} - } + }, view: function(ctrl) { return m(".app", [ //pressing the button reverses the list From 79b127d094b7c28125830a9de15aaa82771c1471 Mon Sep 17 00:00:00 2001 From: Joe Turner Date: Thu, 11 Jun 2015 15:28:57 +0100 Subject: [PATCH 2/9] Clarify order of m.route.mode and m.route It's not completely obvious that `m.route.mode` needs setting before `m.route` is called (or at least it wasn't to me!), otherwise the initial routing from a URL typed in the location bar fails. This PR adds a note in the documentation to make this more clear. --- docs/routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/routing.md b/docs/routing.md index da660ea3..a6970a25 100644 --- a/docs/routing.md +++ b/docs/routing.md @@ -62,7 +62,7 @@ In this case, since there's only one route, the app redirects to the default rou The string `johndoe` is bound to the `:userID` parameter, which can be retrieved programmatically in the controller via `m.route.param("userID")`. -The `m.route.mode` property defines which URL portion is used to implement the routing mechanism. Its value can be set to either "search", "hash" or "pathname". The default value is "search". +The `m.route.mode` property defines which URL portion is used to implement the routing mechanism. It should be set before any calls to `m.route`. Its value can be set to either "search", "hash" or "pathname". The default value is "search". - `search` mode uses the querystring. This allows named anchors (i.e. `Back to top`, ``) to work on the page, but routing changes causes page refreshes in IE8, due to its lack of support for `history.pushState`. From b911d171503958e4da3f1e2c1562f8a2907b1abc Mon Sep 17 00:00:00 2001 From: Yoshiki Shibukawa Date: Fri, 12 Jun 2015 00:56:03 +0900 Subject: [PATCH 3/9] Update mithril.deferred.md --- docs/mithril.deferred.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/mithril.deferred.md b/docs/mithril.deferred.md index 69120a21..9266f0c5 100644 --- a/docs/mithril.deferred.md +++ b/docs/mithril.deferred.md @@ -223,7 +223,7 @@ where: The default value (if this parameter is falsy) is the identity function `function(value) {return value}` - If this function returns undefined, then it passes the `value` argument to the next step in the thennable queue, if any + If this function returns undefined, then it passes the `value` argument to the next step in the thenable queue, if any - **any errorCallback(any value)** (optional) @@ -231,7 +231,7 @@ where: The default value (if this parameter is falsy) is the identity function `function(value) {return value}` - If this function returns undefined, then it passes the `value` argument to the next step in the thennable queue, if any + If this function returns undefined, then it passes the `value` argument to the next step in the thenable queue, if any - **returns Promise promise** From 47e35e32f3e4271be4663e4b65354559050e0a5e Mon Sep 17 00:00:00 2001 From: Yoshiki Shibukawa Date: Fri, 12 Jun 2015 00:57:41 +0900 Subject: [PATCH 4/9] Update web-services.md --- docs/web-services.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/web-services.md b/docs/web-services.md index ba1bfb54..469bfb4b 100644 --- a/docs/web-services.md +++ b/docs/web-services.md @@ -27,7 +27,7 @@ var users = m.request({method: "GET", url: "/user"}); Note that this getter-setter holds an *undefined* value until the AJAX request completes. Attempting to unwrap its value early will likely result in errors. -The returned getter-setter also implements the [promise](mithril.deferred.md) interface (also known as a *thennable*): this is the mechanism you should always use to queue operations to be performed on the data from the web service. +The returned getter-setter also implements the [promise](mithril.deferred.md) interface (also known as a *thenable*): this is the mechanism you should always use to queue operations to be performed on the data from the web service. The simplest use case of this feature is to implement functional value assignment via `m.prop` (i.e. the same thing as above). You can bind a pre-existing getter-setter by passing it in as a parameter to a `.then` method: @@ -49,9 +49,9 @@ var doSomething = function() { /*...*/ } m.request({method: "GET", url: "/user"}).then(users).then(doSomething) ``` -While both basic assignment syntax and thennable syntax can be used to the same effect, typically it's recommended that you use the assignment syntax in the first example whenever possible, as it's easier to read. +While both basic assignment syntax and thenable syntax can be used to the same effect, typically it's recommended that you use the assignment syntax in the first example whenever possible, as it's easier to read. -The thennable mechanism is intended to be used in three ways: +The thenable mechanism is intended to be used in three ways: - In the model layer: to process web service data in transformative ways (e.g. filtering a list based on a parameter that the web service doesn't support) - In the controller layer: to bind redirection code upon a condition @@ -98,7 +98,7 @@ var controller = function() { #### Binding errors -Mithril thennables take two functions as optional parameters: the first parameter is called if the web service request completes successfully. The second parameter is called if it completes with an error. +Mithril thenables take two functions as optional parameters: the first parameter is called if the web service request completes successfully. The second parameter is called if it completes with an error. Error binding is meant to be done in the controller layer. Doing it in the model level is also possible, but generally leads to more code in order to connect all the dots. From 34fe969b61cc791c1fcf4284471429fa22ed6674 Mon Sep 17 00:00:00 2001 From: Christopher Venning Date: Thu, 11 Jun 2015 16:12:10 -0400 Subject: [PATCH 5/9] Change JSFiddle example to use Mithril 0.2.0 --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 0b2814ce..fdad414d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -498,7 +498,7 @@ m.mount(document, {controller: todo.controller, view: todo.view}); ``` -This example is also available as a [jsFiddle](http://jsfiddle.net/milesmatthias/fbgypzbr/1/). +This example is also available as a [jsFiddle](http://jsfiddle.net/fbgypzbr/16/). There is also [Extended example](http://jsfiddle.net/glebcha/q7tvLxsa/) available on jsfiddle. --- From 58d5b306fd0981298e24e83964a7e66a2834fbf9 Mon Sep 17 00:00:00 2001 From: "Jona H." Date: Sat, 13 Jun 2015 09:47:20 +0200 Subject: [PATCH 6/9] fixed bower description Technically, Bower isn't really a package manager for Node.js, but rather, it's made in Node and built for usage in the frontend, hence why many popular frontend libraries, such as JQuery or Bootstrap have their distributions on there. Bower also has its own package repositories. --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 170a6a02..206caa37 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -60,7 +60,7 @@ Then, to use Mithril, point a script tag to the downloaded file: ### Bower -[Bower](http://bower.io) is a package manager for [NodeJS](http://nodejs.org/). If you're using NodeJS already or planning on using [Grunt](http://gruntjs.com/) to create a build system, you can use Bower to conveniently keep up-to-date with Mithril versions. +[Bower](http://bower.io) is a frontend package manager built in [NodeJS](http://nodejs.org/). If you're using NodeJS already or planning on using [Grunt](http://gruntjs.com/) to create a build system, you can use Bower to conveniently keep up-to-date with Mithril versions. Assuming you have NodeJS installed, you can install Bower by typing this in the command line: From 00db0b1ec06e97453bac9106b243be146fc71ac8 Mon Sep 17 00:00:00 2001 From: Christopher Venning Date: Tue, 16 Jun 2015 16:15:42 -0400 Subject: [PATCH 7/9] Fixed typos in doc links --- docs/mithril.component.md | 2 +- docs/mithril.redraw.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/mithril.component.md b/docs/mithril.component.md index 05d5cda6..4e26d0b0 100644 --- a/docs/mithril.component.md +++ b/docs/mithril.component.md @@ -513,7 +513,7 @@ There are a few other technical caveats when nesting components: 1. Nested component views must return either a virtual element or another component. Returning an array, a string, a number, boolean, falsy value, etc will result in an error. -2. Nested components cannot change `m.redraw.strategy` from the controller constructor (but they can from event handlers). It's recommended that you use the [`ctx.retain`](mithril.md#persising-dom-elements-across-route-changes) flag instead of changing the redraw strategy in controller constructors. +2. Nested components cannot change `m.redraw.strategy` from the controller constructor (but they can from event handlers). It's recommended that you use the [`ctx.retain`](mithril.md#persisting-dom-elements-across-route-changes) flag instead of changing the redraw strategy in controller constructors. 3. The root DOM element in a component's view must not be changed during the lifecycle of the component, otherwise undefined behavior will occur. In other words, don't do this: diff --git a/docs/mithril.redraw.md b/docs/mithril.redraw.md index 88ef3f45..ee0f0e51 100644 --- a/docs/mithril.redraw.md +++ b/docs/mithril.redraw.md @@ -126,7 +126,7 @@ Common reasons why one might need to change redraw strategy are: Note that the redraw strategy is a global setting that affects the entire template trees of all components on the page. In order to prevent redraws in *some parts* of an application, but not others, see [subtree directives](mithril.render.md#subtree-directives) -You can also configure individual elements to always be diffed, instead of recreated from scratch (even across route changes), by using the [`ctx.retain` flag](mithril.md#persising-dom-elements-across-route-changes). If you need to persist DOM state across route changes, it's recommended that you use the `ctx.retain` flag instead of `m.redraw.strategy("diff")`. +You can also configure individual elements to always be diffed, instead of recreated from scratch (even across route changes), by using the [`ctx.retain` flag](mithril.md#persisting-dom-elements-across-route-changes). If you need to persist DOM state across route changes, it's recommended that you use the `ctx.retain` flag instead of `m.redraw.strategy("diff")`. --- From 05e6088f130b2d06eb7477c87809438e24151d98 Mon Sep 17 00:00:00 2001 From: Christopher Venning Date: Thu, 18 Jun 2015 17:19:15 -0400 Subject: [PATCH 8/9] Consistency: use variable instead of literal --- mithril.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mithril.js b/mithril.js index cd79c442..6b061ad8 100644 --- a/mithril.js +++ b/mithril.js @@ -964,7 +964,7 @@ var m = (function app(window, undefined) { if (state === RESOLVING && typeof successCallback === FUNCTION) { promiseValue = successCallback(promiseValue) } - else if (state === REJECTING && typeof failureCallback === "function") { + else if (state === REJECTING && typeof failureCallback === FUNCTION) { promiseValue = failureCallback(promiseValue); state = RESOLVING } From fe7683fce8f819d6c2e6e8616fbd9987b19d4fd9 Mon Sep 17 00:00:00 2001 From: Adam Vlasak Date: Sat, 20 Jun 2015 19:19:54 +0200 Subject: [PATCH 9/9] fixed missing semicolons --- mithril.js | 556 ++++++++++++++++++++++++++--------------------------- 1 file changed, 278 insertions(+), 278 deletions(-) diff --git a/mithril.js b/mithril.js index 6b061ad8..ebdce7e1 100644 --- a/mithril.js +++ b/mithril.js @@ -3,7 +3,7 @@ var m = (function app(window, undefined) { 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; @@ -40,37 +40,37 @@ var m = (function app(window, undefined) { 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") + 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 (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]); else if (match[3][0] === "[") { var pair = attrParser.exec(match[3]); - cell.attrs[pair[1]] = pair[3] || (pair[2] ? "" :true) + cell.attrs[pair[1]] = pair[3] || (pair[2] ? "" :true); } } var children = hasAttrs ? args.slice(2) : args.slice(1); if (children.length === 1 && type.call(children[0]) === ARRAY) { - cell.children = children[0] + cell.children = children[0]; } else { - cell.children = children + cell.children = children; } - + for (var attrName in attrs) { if (attrs.hasOwnProperty(attrName)) { if (attrName === classAttrName && attrs[attrName] != null && attrs[attrName] !== "") { - classes.push(attrs[attrName]) - cell.attrs[attrName] = "" //create key in correct iteration order + classes.push(attrs[attrName]); + cell.attrs[attrName] = ""; //create key in correct iteration order } - else cell.attrs[attrName] = attrs[attrName] + else cell.attrs[attrName] = attrs[attrName]; } } if (classes.length > 0) cell.attrs[classAttrName] = classes.join(" "); - - return cell + + return cell; } 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` @@ -99,7 +99,7 @@ var m = (function app(window, undefined) { //- 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 = ""} + try { if (data == null || data.toString() == null) data = ""; } catch (e) { data = ""; } if (data.subtree === "retain") return cached; var cachedType = type.call(cached), dataType = type.call(data); if (cached == null || cachedType !== dataType) { @@ -107,13 +107,13 @@ var m = (function app(window, undefined) { if (parentCache && parentCache.nodes) { var offset = index - parentIndex; var end = offset + (dataType === ARRAY ? data : cached.nodes).length; - clear(parentCache.nodes.slice(offset, end), parentCache.slice(offset, end)) + clear(parentCache.nodes.slice(offset, end), parentCache.slice(offset, end)); } - else if (cached.nodes) clear(cached.nodes, cached) + else if (cached.nodes) clear(cached.nodes, cached); } cached = new data.constructor; if (cached.tag) cached = {}; //if constructor creates a virtual dom element, use a blank object as the base cached node instead of copying the virtual el (#277) - cached.nodes = [] + cached.nodes = []; } if (dataType === ARRAY) { @@ -121,11 +121,11 @@ var m = (function app(window, undefined) { for (var i = 0, len = data.length; i < len; i++) { if (type.call(data[i]) === ARRAY) { 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 + i--; //check current index again and flatten until there are no more nested arrays at that index + len = data.length; } } - + var nodes = [], intact = cached.length === data.length, subArrayCount = 0; //keys algorithm: sort elements without recreating them if keys are present @@ -138,30 +138,30 @@ var m = (function app(window, undefined) { 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} + existing[cached[i].attrs.key] = {action: DELETION, index: i}; } } - - var guid = 0 + + 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++ + if (data[j] && data[j].attrs && data[j].attrs.key == null) data[j].attrs.key = "__mithril__" + guid++; } - break + break; } } - + if (shouldMaintainIdentities) { - var keysDiffer = false - if (data.length != cached.length) keysDiffer = true + 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 + keysDiffer = true; + break; } } - + if (keysDiffer) { for (var i = 0, len = data.length; i < len; i++) { if (data[i] && data[i].attrs) { @@ -173,35 +173,35 @@ var m = (function app(window, undefined) { index: i, from: existing[key].index, element: cached.nodes[existing[key].index] || $document.createElement("div") - } + }; } } } - var actions = [] - for (var prop in existing) actions.push(existing[prop]) + 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() + var newCached = new Array(cached.length); + newCached.nodes = cached.nodes.slice(); for (var i = 0, change; change = changes[i]; i++) { if (change.action === DELETION) { clear(cached[change.index].nodes, cached[change.index]); - newCached.splice(change.index, 1) + newCached.splice(change.index, 1); } if (change.action === INSERTION) { var dummy = $document.createElement("div"); dummy.key = data[change.index].attrs.key; parentElement.insertBefore(dummy, parentElement.childNodes[change.index] || null); - newCached.splice(change.index, 0, {attrs: {key: data[change.index].attrs.key}, nodes: [dummy]}) - newCached.nodes[change.index] = dummy + newCached.splice(change.index, 0, {attrs: {key: data[change.index].attrs.key}, nodes: [dummy]}); + newCached.nodes[change.index] = dummy; } if (change.action === MOVE) { if (parentElement.childNodes[change.index] !== change.element && change.element !== null) { - parentElement.insertBefore(change.element, parentElement.childNodes[change.index] || null) + parentElement.insertBefore(change.element, parentElement.childNodes[change.index] || null); } - newCached[change.index] = cached[change.from] - newCached.nodes[change.index] = change.element + newCached[change.index] = cached[change.from]; + newCached.nodes[change.index] = change.element; } } cached = newCached; @@ -218,57 +218,57 @@ var m = (function app(window, undefined) { //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 + subArrayCount += (item.match(/<[^\/]|\>\s*[^<]/g) || [0]).length; } else subArrayCount += type.call(item) === ARRAY ? item.length : 1; - cached[cacheCount++] = item + 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++) { - if (cached[i] != null) nodes.push.apply(nodes, cached[i].nodes) + 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++) { - if (node.parentNode != null && nodes.indexOf(node) < 0) clear([node], [cached[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 + cached.nodes = nodes; } } else if (data != null && dataType === OBJECT) { - var views = [], controllers = [] + var views = [], controllers = []; while (data.view) { - var view = data.view.$original || data.view - var controllerIndex = m.redraw.strategy() == "diff" && cached.views ? cached.views.indexOf(view) : -1 - var controller = controllerIndex > -1 ? cached.controllers[controllerIndex] : new (data.controller || noop) - var key = data && data.attrs && data.attrs.key - data = (pendingRequests == 0 || forcing) || (cached && cached.controllers && cached.controllers.indexOf(controller) > -1) ? data.view(controller) : {tag: "placeholder"} + var view = data.view.$original || data.view; + var controllerIndex = m.redraw.strategy() == "diff" && cached.views ? cached.views.indexOf(view) : -1; + var controller = controllerIndex > -1 ? cached.controllers[controllerIndex] : new (data.controller || noop); + var key = data && data.attrs && data.attrs.key; + data = (pendingRequests == 0 || forcing) || (cached && cached.controllers && cached.controllers.indexOf(controller) > -1) ? data.view(controller) : {tag: "placeholder"}; if (data.subtree === "retain") return cached; if (key) { - if (!data.attrs) data.attrs = {} - data.attrs.key = key + if (!data.attrs) data.attrs = {}; + data.attrs.key = key; } - if (controller.onunload) unloaders.push({controller: controller, handler: controller.onunload}) - views.push(view) - controllers.push(controller) + if (controller.onunload) unloaders.push({controller: controller, handler: controller.onunload}); + views.push(view); + controllers.push(controller); } - if (!data.tag && controllers.length) throw new Error("Component template must return a virtual element, not an array, string, etc.") + if (!data.tag && controllers.length) throw new Error("Component template must return a virtual element, not an array, string, etc."); if (!data.attrs) data.attrs = {}; if (!cached.attrs) cached.attrs = {}; - var dataAttrKeys = Object.keys(data.attrs) - var hasKeys = dataAttrKeys.length > ("key" in data.attrs ? 1 : 0) + var dataAttrKeys = Object.keys(data.attrs); + var hasKeys = dataAttrKeys.length > ("key" in data.attrs ? 1 : 0); //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 && typeof cached.configContext.onunload === FUNCTION) 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 (typeof controller.onunload === FUNCTION) controller.onunload({preventDefault: noop}); } } } @@ -278,7 +278,7 @@ var m = (function app(window, undefined) { if (data.attrs.xmlns) namespace = data.attrs.xmlns; else if (data.tag === "svg") namespace = "http://www.w3.org/2000/svg"; else if (data.tag === "math") namespace = "http://www.w3.org/1998/Math/MathML"; - + if (isNew) { if (data.attrs.is) node = namespace === undefined ? $document.createElement(data.tag, data.attrs.is) : $document.createElementNS(namespace, data.tag, data.attrs.is); else node = namespace === undefined ? $document.createElement(data.tag) : $document.createElementNS(namespace, data.tag); @@ -292,22 +292,22 @@ var m = (function app(window, undefined) { nodes: [node] }; if (controllers.length) { - cached.views = views - cached.controllers = controllers + cached.views = views; + cached.controllers = controllers; for (var i = 0, controller; controller = controllers[i]; i++) { - if (controller.onunload && controller.onunload.$old) controller.onunload = controller.onunload.$old + if (controller.onunload && controller.onunload.$old) controller.onunload = controller.onunload.$old; if (pendingRequests && controller.onunload) { - var onunload = controller.onunload - controller.onunload = noop - controller.onunload.$old = onunload + var onunload = controller.onunload; + controller.onunload = noop; + controller.onunload.$old = onunload; } } } - + if (cached.children && !cached.children.nodes) cached.children.nodes = []; //edge case: setting value on