Merge remote-tracking branch 'origin/next' into es6-promise
Conflicts: mithril.js
This commit is contained in:
commit
ac4863ff7c
9 changed files with 172 additions and 34 deletions
|
|
@ -8,6 +8,7 @@
|
|||
- there is more documentation for things that weren't that clear
|
||||
- json-p support added
|
||||
- `m()` now supports splat for children (e.g. `m("div", m("a"), m("b"), m("i"))` for nicer Coffeescript syntax
|
||||
- by popular demand, `m.module` now returns a controller instance
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,71 @@ The reason Mithril waits for all asynchronous services to complete before redraw
|
|||
|
||||
It's possible to opt out of the redrawing schedule by using the `background` option for `m.request`, or by simply not calling `m.startComputation` / `m.endComputation` when calling non-Mithril asynchronous functions.
|
||||
|
||||
```javascript
|
||||
//`background` option example
|
||||
var module = {}
|
||||
module.controller = function() {
|
||||
//setting `background` allows the module to redraw immediately, without waiting for the request to complete
|
||||
m.request({method: "GET", url: "/foo", background: true})
|
||||
}
|
||||
```
|
||||
|
||||
It's also possible to modify the strategy that Mithril uses for any given redraw, by using [`m.redraw.strategy`](mithril.redraw.md#changing-redraw-strategy). Note that changing the redraw strategy only affects the next scheduled redraw. After that, Mithril resets the `m.redraw.strategy` flag to either "all" or "diff" depending on whether the redraw was due to a route change or whether it was triggered by some other action.
|
||||
|
||||
```javascript
|
||||
//diff when routing, instead of redrawing from scratch
|
||||
//this preserves the `<input>` element and its 3rd party plugin after route changes, since the `<input>` doesn't change
|
||||
var module1 = {}
|
||||
module1.controller = function() {
|
||||
m.redraw.strategy("diff")
|
||||
}
|
||||
module1.view = function() {
|
||||
return [
|
||||
m("h1", "Hello Foo"),
|
||||
m("input", {config: plugin}) //assuming `plugin` initializes a 3rd party library
|
||||
]
|
||||
}
|
||||
|
||||
var module2 = {}
|
||||
module2.controller = function() {
|
||||
m.redraw.strategy("diff")
|
||||
}
|
||||
module2.view = function() {
|
||||
return [
|
||||
m("h1", "Hello Bar"),
|
||||
m("input", {config: plugin}) //assuming `plugin` initializes a 3rd party library
|
||||
]
|
||||
}
|
||||
|
||||
m.route(document.body, "/foo", {
|
||||
"/foo": module1,
|
||||
"/bar": module2,
|
||||
})
|
||||
```
|
||||
|
||||
```javascript
|
||||
//model
|
||||
var saved = false
|
||||
function save(e) {
|
||||
if (e.keyCode == 13) {
|
||||
//this causes a redraw, since event handlers active auto-redrawing by default
|
||||
saved = true
|
||||
}
|
||||
else {
|
||||
//we don't care about other keys, so don't redraw
|
||||
m.redraw.strategy("none")
|
||||
}
|
||||
}
|
||||
|
||||
//view
|
||||
var view = function() {
|
||||
return [
|
||||
m("button[type=button]", {onkeypress: save}, "Save"),
|
||||
saved ? "Saved" : ""
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Integrating multiple execution threads
|
||||
|
|
|
|||
|
|
@ -181,5 +181,6 @@ where:
|
|||
|
||||
Components are nothing more than decoupled classes that can be dynamically brought together as required. This permits the swapping of implementations at a routing level (for example, if implementing widgetized versions of existing components), and class dependency hierarchies can be structurally organized to provide uniform interfaces (for unit tests, for example).
|
||||
|
||||
- **returns Object controllerInstance**
|
||||
|
||||
|
||||
An instance of the controller constructor
|
||||
|
|
@ -69,10 +69,11 @@ m.request({method: "GET", url: "/users"})
|
|||
|
||||
### Third-party promise library support
|
||||
|
||||
If a promise is passed into `m.prop()`, its value will populate the prop after resolution.
|
||||
If a promise is passed into `m.prop()`, a Mithril promise is returned. Mithril promises are also getter-setter functions, which are populated with the resolved value if the promise is fulfilled successfully.
|
||||
|
||||
Until the promise is resolved, the value of the prop will resolve to `undefined`
|
||||
|
||||
Example using [Q](https://github.com/kriskowal/q)
|
||||
Here's an example using the [Q](https://github.com/kriskowal/q) promise library:
|
||||
|
||||
```javascript
|
||||
var deferred = Q.defer()
|
||||
|
|
@ -82,6 +83,9 @@ users() // undefined
|
|||
|
||||
deferred.resolve("Hello")
|
||||
users() // Hello
|
||||
users.then(function(value) {
|
||||
console.log(value) //Hello
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ If you are developing an asynchronous model-level service and finding that Mithr
|
|||
|
||||
If you need to change how Mithril performs redraws, you can change the value of the `m.redraw.strategy` getter-setter to either `"all"`, `"diff"` or `"none"`. By default, this value is set to `"all"` when running controller constructors, and it's set to `"diff"` for all subsequent redraws.
|
||||
|
||||
The strategy flag is meant to only be changed in a context where Mithril auto-redraws. This means `m.redraw.strategy` can be called from controller constructors and from template event handlers. Note that changing this flag only affects the next scheduled redraw.
|
||||
|
||||
After the redraw, Mithril resets the value of the flag to either "all" or "diff", depending on whether the redraw was due to a route change or not.
|
||||
|
||||
```javascript
|
||||
var module1 = {}
|
||||
module1.controller = function() {
|
||||
|
|
@ -39,17 +43,72 @@ module1.controller = function() {
|
|||
m.redraw.strategy("diff")
|
||||
}
|
||||
module1.view = function() {
|
||||
return m("h1", {config: module1.config}, "test")
|
||||
return m("h1", {config: module1.config}, "test") //assume all routes display the same thing
|
||||
}
|
||||
module1.config = function(el, isInit, ctx) {
|
||||
if (!isInit) ctx.data = "foo"
|
||||
if (!isInit) ctx.data = "foo" //we wish to initialize this only once, even if the route changes
|
||||
}
|
||||
```
|
||||
|
||||
Common reasons why one might need to change redraw strategy are:
|
||||
|
||||
- in order to avoid the full-page recreation when changing routes, for the sake of performance of global 3rd party components
|
||||
- in order to prevent redraw when dealing with `keypress` events where the event's keyCode is not of interest
|
||||
- in order to avoid the full-page recreation when changing routes, for the sake of performance of global 3rd party components
|
||||
|
||||
```javascript
|
||||
//diff when routing, instead of redrawing from scratch
|
||||
//this preserves the `<input>` element and its 3rd party plugin after route changes, since the `<input>` doesn't change
|
||||
var module1 = {}
|
||||
module1.controller = function() {
|
||||
m.redraw.strategy("diff")
|
||||
}
|
||||
module1.view = function() {
|
||||
return [
|
||||
m("h1", "Hello Foo"),
|
||||
m("input", {config: plugin}) //assuming `plugin` initializes a 3rd party library
|
||||
]
|
||||
}
|
||||
|
||||
var module2 = {}
|
||||
module2.controller = function() {
|
||||
m.redraw.strategy("diff")
|
||||
}
|
||||
module2.view = function() {
|
||||
return [
|
||||
m("h1", "Hello Bar"),
|
||||
m("input", {config: plugin}) //assuming `plugin` initializes a 3rd party library
|
||||
]
|
||||
}
|
||||
|
||||
m.route(document.body, "/foo", {
|
||||
"/foo": module1,
|
||||
"/bar": module2,
|
||||
})
|
||||
```
|
||||
|
||||
- in order to prevent redraw when dealing with `keypress` events where the event's keyCode is not of interest
|
||||
|
||||
```javascript
|
||||
//model
|
||||
var saved = false
|
||||
function save(e) {
|
||||
if (e.keyCode == 13) {
|
||||
//this causes a redraw, since event handlers active auto-redrawing by default
|
||||
saved = true
|
||||
}
|
||||
else {
|
||||
//we don't care about other keys, so don't redraw
|
||||
m.redraw.strategy("none")
|
||||
}
|
||||
}
|
||||
|
||||
//view
|
||||
var view = function() {
|
||||
return [
|
||||
m("button[type=button]", {onkeypress: save}, "Save"),
|
||||
saved ? "Saved" : ""
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Note that the redraw strategy is a global setting that affects the entire template trees of all modules 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)
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,12 @@ You can use it by adding a reference to your Typescript files. This will allow t
|
|||
|
||||
Mithril relies on some Ecmascript 5 features, namely: `Array::indexOf`, `Array::map` and `Object::keys`, as well as the `JSON` object.
|
||||
|
||||
You can use polyfill libraries to support these features in IE7.
|
||||
The easiest way to polyfill these features is to include this script:
|
||||
```markup
|
||||
<script src="https://polyfill.io/readable/gimme(array.prototype.indexof,object.keys,function.prototype.bind,array.prototype.foreach,JSON)"></script>
|
||||
```
|
||||
|
||||
You can also use other polyfills to support these features in IE7.
|
||||
|
||||
- [ES5 Shim](https://github.com/es-shims/es5-shim) or Mozilla.org's [Array::indexOf](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf), [Array::map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) and [Object::keys](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)
|
||||
|
||||
|
|
|
|||
32
mithril.js
32
mithril.js
|
|
@ -1,5 +1,5 @@
|
|||
Mithril = m = new function app(window, undefined) {
|
||||
var type = {}.toString
|
||||
var type = function(obj) {return {}.toString.call(obj)}
|
||||
var parser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g, attrParser = /\[(.+?)(?:=("|'|)(.*?)\2)?\]/
|
||||
var voidElements = /AREA|BASE|BR|COL|COMMAND|EMBED|HR|IMG|INPUT|KEYGEN|LINK|META|PARAM|SOURCE|TRACK|WBR/
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
*/
|
||||
function m() {
|
||||
var args = Array.prototype.slice.call(arguments, 0)
|
||||
var hasAttrs = args[1] != null && type.call(args[1]) == "[object Object]" && !("tag" in args[1]) && !("subtree" in args[1])
|
||||
var hasAttrs = args[1] != null && type(args[1]) == "[object Object]" && !("tag" in args[1]) && !("subtree" in args[1])
|
||||
var attrs = hasAttrs ? args[1] : {}
|
||||
var classAttrName = "class" in attrs ? "class" : "className"
|
||||
var cell = {tag: "div", attrs: {}}
|
||||
|
|
@ -78,7 +78,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
if (data == null) data = ""
|
||||
if (data.subtree === "retain") return cached
|
||||
|
||||
var cachedType = type.call(cached), dataType = type.call(data)
|
||||
var cachedType = type(cached), dataType = type(data)
|
||||
if (cached == null || cachedType != dataType) {
|
||||
if (cached != null) {
|
||||
if (parentCache && parentCache.nodes) {
|
||||
|
|
@ -160,7 +160,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
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
|
||||
var isArray = type.call(item) == "[object Array]"
|
||||
var isArray = type(item) == "[object Array]"
|
||||
subArrayCount += isArray ? item.length : 1
|
||||
cached[cacheCount++] = item
|
||||
}
|
||||
|
|
@ -324,7 +324,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
function unload(cached) {
|
||||
if (cached.configContext && typeof cached.configContext.onunload == "function") cached.configContext.onunload()
|
||||
if (cached.children) {
|
||||
if (type.call(cached.children) == "[object Array]") {
|
||||
if (type(cached.children) == "[object Array]") {
|
||||
for (var i = 0; i < cached.children.length; i++) unload(cached.children[i])
|
||||
}
|
||||
else if (cached.children.tag) unload(cached.children)
|
||||
|
|
@ -354,7 +354,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
var flattened = []
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var item = data[i]
|
||||
if (type.call(item) == "[object Array]") flattened.push.apply(flattened, flatten(item))
|
||||
if (type(item) == "[object Array]") flattened.push.apply(flattened, flatten(item))
|
||||
else flattened.push(item)
|
||||
}
|
||||
return flattened
|
||||
|
|
@ -435,7 +435,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
return gettersetter(store)
|
||||
}
|
||||
|
||||
var roots = [], modules = [], controllers = [], lastRedrawId = 0, computePostRedrawHook = null, prevented = false
|
||||
var roots = [], modules = [], controllers = [], lastRedrawId = 0, redrawAgain = false, computePostRedrawHook = null, prevented = false
|
||||
m.module = function(root, module) {
|
||||
var index = roots.indexOf(root)
|
||||
if (index < 0) index = roots.length
|
||||
|
|
@ -460,12 +460,19 @@ Mithril = m = new function app(window, undefined) {
|
|||
var cancel = window.cancelAnimationFrame || window.clearTimeout
|
||||
var defer = window.requestAnimationFrame || window.setTimeout
|
||||
if (lastRedrawId && force !== true) {
|
||||
cancel(lastRedrawId)
|
||||
lastRedrawId = defer(redraw, 0)
|
||||
redrawAgain = true
|
||||
}
|
||||
else {
|
||||
redraw()
|
||||
lastRedrawId = defer(function() {lastRedrawId = null}, 0)
|
||||
lastRedrawId = defer(delay, 16) //60 frames per second = 1 call per 16 ms
|
||||
}
|
||||
|
||||
function delay() {
|
||||
lastRedrawId = null
|
||||
if (redrawAgain) {
|
||||
redrawAgain = false
|
||||
m.redraw()
|
||||
}
|
||||
}
|
||||
}
|
||||
m.redraw.strategy = m.prop()
|
||||
|
|
@ -885,7 +892,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
var unwrap = (e.type == "load" ? xhrOptions.unwrapSuccess : xhrOptions.unwrapError) || identity
|
||||
var response = unwrap(deserialize(extract(e.target, xhrOptions)))
|
||||
if (e.type == "load") {
|
||||
if (type.call(response) == "[object Array]" && xhrOptions.type) {
|
||||
if (type(response) == "[object Array]" && xhrOptions.type) {
|
||||
for (var i = 0; i < response.length; i++) response[i] = new xhrOptions.type(response[i])
|
||||
}
|
||||
else if (xhrOptions.type) response = new xhrOptions.type(response)
|
||||
|
|
@ -893,8 +900,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
deferred[e.type == "load" ? "resolve" : "reject"](response)
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof SyntaxError) throw new SyntaxError("Could not parse HTTP response. See http://lhorie.github.io/mithril/mithril.request.html#using-variable-data-formats")
|
||||
else if (!rethrowUnchecked(e)) deferred.reject(e)
|
||||
if (!rethrowUnchecked(e)) deferred.reject(e)
|
||||
}
|
||||
if (xhrOptions.background !== true) m.endComputation()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -592,7 +592,6 @@ function testMithril(mock) {
|
|||
var firstBefore = root.childNodes[0]
|
||||
m.render(root, [m("a", {key: 2}), m("br"), m("a", {key: 1})])
|
||||
var firstAfter = root.childNodes[2]
|
||||
console.log(root.childNodes)
|
||||
return firstBefore == firstAfter && root.childNodes[0].key == 2 && root.childNodes.length == 3
|
||||
})
|
||||
test(function() {
|
||||
|
|
@ -1455,6 +1454,7 @@ function testMithril(mock) {
|
|||
}
|
||||
})
|
||||
root.childNodes[0].onclick({})
|
||||
mock.requestAnimationFrame.$resolve() //teardown
|
||||
return strategy == "diff" && root.childNodes[0].childNodes[0].nodeValue == "1"
|
||||
})
|
||||
test(function() {
|
||||
|
|
|
|||
|
|
@ -15,16 +15,10 @@ mock.window = new function() {
|
|||
insertBefore: function(node, reference) {
|
||||
node.parentNode = this
|
||||
var referenceIndex = this.childNodes.indexOf(reference)
|
||||
if (referenceIndex < 0) {
|
||||
var index = this.childNodes.indexOf(node)
|
||||
if (index > -1) this.childNodes.splice(index, 1)
|
||||
this.childNodes.push(node)
|
||||
}
|
||||
else {
|
||||
var index = this.childNodes.indexOf(node)
|
||||
if (index > -1) this.childNodes.splice(index, 1)
|
||||
this.childNodes.splice(referenceIndex, 0, node)
|
||||
}
|
||||
var index = this.childNodes.indexOf(node)
|
||||
if (index > -1) this.childNodes.splice(index, 1)
|
||||
if (referenceIndex < 0) this.childNodes.push(node)
|
||||
else this.childNodes.splice(referenceIndex, 0, node)
|
||||
},
|
||||
insertAdjacentHTML: function(position, html) {
|
||||
//todo: accept markup
|
||||
|
|
@ -99,8 +93,11 @@ mock.window = new function() {
|
|||
}
|
||||
window.requestAnimationFrame.$id = 1
|
||||
window.requestAnimationFrame.$resolve = function() {
|
||||
if (window.requestAnimationFrame.$callback) window.requestAnimationFrame.$callback()
|
||||
window.requestAnimationFrame.$callback = null
|
||||
if (window.requestAnimationFrame.$callback) {
|
||||
var callback = window.requestAnimationFrame.$callback
|
||||
window.requestAnimationFrame.$callback = null
|
||||
callback()
|
||||
}
|
||||
}
|
||||
window.XMLHttpRequest = new function() {
|
||||
var request = function() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue