change m.request return value from stream to promise
remove m.prop add m.Promise update tests and examples
This commit is contained in:
parent
8f1a69cfcb
commit
bc8cf4ed76
19 changed files with 650 additions and 852 deletions
|
|
@ -34,6 +34,6 @@ There are over 4000 assertions in the test suite, and tests cover even difficult
|
|||
|
||||
## Modularity
|
||||
|
||||
Despite the huge improvements in performance and modularity, the new codebase is smaller than v0.2.x, currently clocking at 7.2kb min+gzip
|
||||
Despite the huge improvements in performance and modularity, the new codebase is smaller than v0.2.x, currently clocking at 7.5kb min+gzip
|
||||
|
||||
In addition, Mithril is now completely modular: you can import only the modules that you need and easily integrate 3rd party modules if you wish to use a different library for routing, ajax, and even rendering
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ module.exports = function(input, output, options) {
|
|||
var data = {
|
||||
output_format: "json",
|
||||
output_info: ["compiled_code", "warnings", "errors", "statistics"],
|
||||
compilation_level: "SIMPLE_OPTIMIZATIONS",//"ADVANCED_OPTIMIZATIONS",
|
||||
compilation_level: options.advanced ? "ADVANCED_OPTIMIZATIONS" : "SIMPLE_OPTIMIZATIONS",
|
||||
warning_level: "default",
|
||||
output_file_name: "default.js",
|
||||
js_code: code,
|
||||
|
|
|
|||
|
|
@ -8,17 +8,16 @@
|
|||
|
||||
### API
|
||||
|
||||
`stream = m.jsonp(options)`
|
||||
`promise = m.jsonp(options)`
|
||||
|
||||
Argument | Type | Required | Description
|
||||
---------------------- | --------------------------------- | -------- | ---
|
||||
`options.url` | `String` | Yes | The URL to send the request to. The URL may be either absolute or relative, and it may contain [interpolations](#dynamic-urls).
|
||||
`options.data` | `any` | No | The data to be interpolated into the URL and serialized into the querystring (for GET requests) or body (for other types of requests).
|
||||
`options.type` | `any = Function(any)` | No | A constructor to be applied to each object in the response. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function).
|
||||
`options.initialValue` | `any` | No | A value to populate the returned stream before the request completes
|
||||
`options.callbackName` | `String` | No | The name of the function that will be called as the callback. Defaults to a randomized string (e.g. `_mithril_6888197422121285_0({a: 1})`
|
||||
`options.callbackKey` | `String` | No | The name of the querystring parameter name that specifies the callback name. Defaults to `callback` (e.g. `/someapi?callback=_mithril_6888197422121285_0`)
|
||||
**returns** | `Stream` | | A stream that resolves to the response data, after it has been piped through `type` method
|
||||
**returns** | `Promise` | | A promise that resolves to the response data, after it has been piped through `type` method
|
||||
|
||||
[How to read signatures](signatures.md)
|
||||
|
||||
|
|
|
|||
243
docs/request.md
243
docs/request.md
|
|
@ -12,13 +12,12 @@
|
|||
- [Non-JSON responses](#non-json-responses)
|
||||
- [Why JSON instead of HTML](#why-json-instead-of-html)
|
||||
- [Why XMLHttpRequest instead of fetch](#why-xmlhttprequest-instead-of-fetch)
|
||||
- [Why return streams](#why-return-streams)
|
||||
|
||||
---
|
||||
|
||||
### API
|
||||
|
||||
`stream = m.request([url,] options)`
|
||||
`promise = m.request([url,] options)`
|
||||
|
||||
Argument | Type | Required | Description
|
||||
---------------------- | --------------------------------- | -------- | ---
|
||||
|
|
@ -34,9 +33,8 @@ Argument | Type | Required | Descript
|
|||
`options.serialize` | `string = Function(any)` | No | A serialization method to be applied to `data`. Defaults to `JSON.stringify`, or if `options.data` is an instance of [`FormData`](https://developer.mozilla.org/en/docs/Web/API/FormData), defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function) (i.e. `function(value) {return value}`).
|
||||
`options.deserialize` | `any = Function(string)` | No | A deserialization method to be applied to the response. Defaults to a small wrapper around `JSON.parse` that returns `null` for empty responses.
|
||||
`options.extract` | `string = Function(xhr, options)` | No | A hook to specify how the XMLHttpRequest response should be read. Useful for reading response headers and cookies. Defaults to a function that returns `xhr.responseText`. If defined, the `xhr` parameter is the XMLHttpRequest instance used for the request, and `options` is the object that was passed to the `m.request` call. If a custom `extract` callback is set, `options.deserialize` is ignored.
|
||||
`options.initialValue` | `any` | No | A value to populate the returned stream before the request completes
|
||||
`options.useBody` | `Boolean` | No | Force the use of the HTTP body section for `data` in `GET` requests when set to `true`, or the use of querystring for other HTTP methods when set to `false`. Defaults to `false` for `GET` requests and `true` for other methods.
|
||||
**returns** | `Stream` | | A stream that resolves to the response data, after it has been piped through the `extract`, `deserialize` and `type` methods
|
||||
**returns** | `Promise` | | A promise that resolves to the response data, after it has been piped through the `extract`, `deserialize` and `type` methods
|
||||
|
||||
[How to read signatures](signatures.md)
|
||||
|
||||
|
|
@ -51,12 +49,12 @@ m.request({
|
|||
method: "GET",
|
||||
url: "/api/v1/users",
|
||||
})
|
||||
.run(function(users) {
|
||||
.then(function(users) {
|
||||
console.log(users)
|
||||
})
|
||||
```
|
||||
|
||||
Calls to `m.request` return a [stream](prop.md).
|
||||
Calls to `m.request` return a [promise](promise.md).
|
||||
|
||||
By default, `m.request` assumes the response is in JSON format and parses it into a Javascript object (or array).
|
||||
|
||||
|
|
@ -64,32 +62,41 @@ By default, `m.request` assumes the response is in JSON format and parses it int
|
|||
|
||||
### Typical usage
|
||||
|
||||
Here's an illustrative example of a self-contained component that uses `m.request` to retrieve some data from a server.
|
||||
Here's an illustrative example of a component that uses `m.request` to retrieve some data from a server.
|
||||
|
||||
```javascript
|
||||
var SimpleExample = {
|
||||
oninit: function(vnode) {
|
||||
vnode.state.items = m.request({
|
||||
var Data = {
|
||||
todos: {
|
||||
list: [],
|
||||
fetch: function() {
|
||||
m.request({
|
||||
method: "GET",
|
||||
url: "/api/items",
|
||||
initialValue: []
|
||||
url: "/api/todos",
|
||||
})
|
||||
},
|
||||
.then(function(items) {
|
||||
Data.todos.items = items
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var Todos = {
|
||||
oninit: Data.todos.fetch,
|
||||
view: function(vnode) {
|
||||
return vnode.state.items().map(function(item) {
|
||||
return m("div", item.name)
|
||||
return Data.todos.list.map(function(item) {
|
||||
return m("div", item.title)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
m.route(document.body, "/", {
|
||||
"/": SimpleExample
|
||||
"/": Todos
|
||||
})
|
||||
```
|
||||
|
||||
Let's assume making a request to the server URL `/api/items` returns an array of objects in JSON format.
|
||||
|
||||
When `m.route` is called at the bottom, `SimpleExample` is initialized. `oninit` is called, which calls `m.request` and assigns its return value (a stream) to `vnode.state.items`. This stream contains the `initialValue` (i.e. an empty array), and this value can be retrieved by calling the stream as a function (i.e. `value = vnode.state.items()`). After the oninit method returns, the component is then rendered. Since `vnode.state.items()` returns an empty array, the component's `view` method also returns an empty array, so no DOM elements are created. When the request to the server completes, `m.request` parses the response data into a Javascript array of objects and sets the value of the stream to that array. Then, the component is rendered again. This time, `vnode.state.items()` returns a non-empty array, so the component's `view` method returns an array of vnodes, which in turn are rendered into `div` DOM elements.
|
||||
When `m.route` is called at the bottom, the `Todos` component is initialized. `oninit` is called, which calls `m.request`. This retrieves an array of objects from the server asynchronously. "Asynchronously" means that Javascript continues running other code while it waits for the response from server. In this case, it means `fetch` returns, and the component is rendered using the original empty array as `Data.todos.list`. Once the request to the server completes, the array of objects `items` is assigned to `Data.todos.items` and the component is rendered again, yielding a list of `<div>`s containing the titles of each todo.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -98,44 +105,44 @@ When `m.route` is called at the bottom, `SimpleExample` is initialized. `oninit`
|
|||
Here's an expanded version of the example above that implements a loading indicator and an error message:
|
||||
|
||||
```javascript
|
||||
var RobustExample = {
|
||||
oninit: function(vnode) {
|
||||
var req = m.request({
|
||||
var Data = {
|
||||
todos: {
|
||||
list: null,
|
||||
error: "",
|
||||
fetch: function() {
|
||||
m.request({
|
||||
method: "GET",
|
||||
url: "/api/items",
|
||||
url: "/api/todos",
|
||||
})
|
||||
vnode.state.items = req.catch(function() {
|
||||
return []
|
||||
.then(function(items) {
|
||||
Data.todos.items = items
|
||||
})
|
||||
vnode.state.error = req.error.run(this.errorView)
|
||||
},
|
||||
.catch(function(e) {
|
||||
Data.todos.error = e.message
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var Todos = {
|
||||
oninit: Data.todos.fetch,
|
||||
view: function(vnode) {
|
||||
return [
|
||||
vnode.state.items() ? vnode.state.items().map(function(item) {
|
||||
return m("div", item.name)
|
||||
}) : m(".loading-icon"),
|
||||
vnode.state.error(),
|
||||
]
|
||||
},
|
||||
errorView: function(e) {
|
||||
return e ? m(".error", "An error occurred") : null
|
||||
return Data.todos.error ? [
|
||||
m(".error", Data.todos.error)
|
||||
] : Data.todos.list ? [
|
||||
Data.todos.list.map(function(item) {
|
||||
return m("div", item.title)
|
||||
})
|
||||
] : m(".loading-icon")
|
||||
}
|
||||
}
|
||||
|
||||
m.route(document.body, "/", {
|
||||
"/": RobustExample
|
||||
"/": Todos
|
||||
})
|
||||
```
|
||||
|
||||
When this component is initialized, `m.request` is called and its return value is assigned to `req`. Unlike the previous example, here there's no `initialValue`, so the `req` stream is in a pending state, and therefore has a value of `undefined`. `req.error` is the error stream for the request. Since `req` is pending, the `req.error` stream also remain in a pending state, and likewise, `vnode.state.error` stays pending and does not call `this.errorView`.
|
||||
|
||||
Then the component renders. Both `vnode.state.items()` and `vnode.state.error()` return `undefined`, so the component returns `[m(".loading-icon"), undefined]`, which in turn creates a loading icon element in the DOM.
|
||||
|
||||
When the request to the server completes, `req` is populated with the response data, which is propagated to the `vnode.state.items` dependent stream. (Note that the function in `catch` is not called if there's no error). After the request completes, the component is re-rendered. `req.error` is set to an active state (but it still has a value of `undefined`), and `vnode.state.error()` is then set to `null`. The `view` function returns a list of vnodes containing item names, and therefore the loading icon is replaced by a list of `div` elements are created in the DOM.
|
||||
|
||||
If the request to the server fails, `catch` is called and `vnode.state.items()` is set to an empty array. Also, `req.error` is populated with the error, and `vnode.state.error` is populated with the vnode tree returned by `errorView`. Therefore, `view` returns `[[], m(".error", "An error occurred")]`, which replaces the loading icon with the error message in the DOM.
|
||||
|
||||
To clear the error message, simply set the value of the `vnode.state.error` stream to `undefined`.
|
||||
There are a few difference between this example and the one before. Here, `Data.todos.list` is `null` at the beginning. Also, there's an extra field `error` for holding an error message, and the view of the `Todos` component was modified to displays an error message if one exists, or display a loading icon if `Data.todos.list` is not an array.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -148,7 +155,7 @@ m.request({
|
|||
method: "GET",
|
||||
url: "/api/v1/users/:id",
|
||||
data: {id: 123},
|
||||
}).run(function(user) {
|
||||
}).then(function(user) {
|
||||
console.log(user.id) // logs 123
|
||||
})
|
||||
```
|
||||
|
|
@ -329,7 +336,7 @@ m.request({
|
|||
url: "/api/v1/users",
|
||||
type: User
|
||||
})
|
||||
.run(function(users) {
|
||||
.then(function(users) {
|
||||
console.log(users[0].name) // logs a name
|
||||
})
|
||||
```
|
||||
|
|
@ -348,7 +355,7 @@ m.request({
|
|||
url: "/files/icon.svg",
|
||||
deserialize: function(value) {return value}
|
||||
})
|
||||
.run(function(svg) {
|
||||
.then(function(svg) {
|
||||
m.render(document.body, m.trust(svg))
|
||||
})
|
||||
```
|
||||
|
|
@ -363,7 +370,7 @@ m.request({
|
|||
url: "/files/data.csv",
|
||||
deserialize: parseCSV
|
||||
})
|
||||
.run(function(data) {
|
||||
.then(function(data) {
|
||||
console.log(data)
|
||||
})
|
||||
|
||||
|
|
@ -390,12 +397,12 @@ m.request({
|
|||
url: "/api/v1/users",
|
||||
extract: function(xhr) {return {status: xhr.status, body: xhr.responseText}}
|
||||
})
|
||||
.run(function(response) {
|
||||
.then(function(response) {
|
||||
console.log(response.status, response.body)
|
||||
})
|
||||
```
|
||||
|
||||
The parameter to `options.extract` is the XMLHttpRequest object once its operation is completed, but before it has been passed to the resulting [stream](prop.md), so the stream may still end up in an errored state if processing throws an exception.
|
||||
The parameter to `options.extract` is the XMLHttpRequest object once its operation is completed, but before it has been passed to the returned promise chain, so the promise may still end up in an rejected state if processing throws an exception.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -434,139 +441,3 @@ The `fetch()` API does have a few technical advantages over `XMLHttpRequest` in
|
|||
- it integrates to the [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API), which provides an extra layer of control over how and when network requests happen. This API also allows access to push notifications and background synchronization features.
|
||||
|
||||
In typical scenarios, streaming won't provide noticeable performance benefits because it's generally not advisable to download megabytes of data to begin with. Also, the memory gains from repeatedly reusing small buffers may be offset or nullified if they result in excessive browser repaints. For those reasons, choosing `fetch()` streaming instead of `m.request` is only recommended for extremely resource intensive applications.
|
||||
|
||||
---
|
||||
|
||||
### Why return streams
|
||||
|
||||
Normally, XMLHttpRequest makes HTTP requests to a server *asynchronously*. This means that it cannot return the response data via a `return` statement, since the `return` statement runs *synchronously* long before the response data is actually available. Therefore, any library that makes requests must expose the response data using some other mechanism.
|
||||
|
||||
Some older libraries do so via callback functions, and newer ones (including the `fetch` API) return [promises](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise). `m.request` returns *reactive streams*.
|
||||
|
||||
Callback functions are the most basic mechanism for asynchronous flow control. They are not *composable* because they require the callback function to be passed at call time, and error handling mechanisms must similarly be declared upfront.
|
||||
|
||||
However, it's desirable to allow the callback function to be defined (and broken into subroutines) in different places than the call site, in order to achieve better separation of concerns. In addition, it's also desirable to wrap an abstraction around errors so that they can be thrown freely and handled safely from a single place, rather than requiring `try/catch` blocks in every callback function, or duplicating error handling code. The problems that arise from callbacks' lack of composability are [infamous enough](http://callbackhell.com/) to earn nicknames such as "callback hell" and "pyramids of doom".
|
||||
|
||||
The Promise API is designed to address the shortcomings of callbacks. They are *composable*, which allows code to be refactored much more elegantly than using callbacks.
|
||||
|
||||
In the example below, the code is written in a naive style. It's highly procedural and does many different things: First it requests a project with id `123`, then requests a user whose id is the value of `project.ownerID`, then proceeds to do something useful with the user. If there's an error, it is logged to console.
|
||||
|
||||
```javascript
|
||||
// AVOID
|
||||
function doStuff() { /*...*/ }
|
||||
|
||||
function json(response) {
|
||||
return response.json()
|
||||
}
|
||||
function doStuffWithProjectOwner(projectID) {
|
||||
fetch("/api/v1/projects/" + projectID, {method: "GET"}).then(json).then(function(project) {
|
||||
fetch("/api/v1/users/" + project.ownerID, {method: "GET"}).then(json).then(function(user) {
|
||||
doStuff(user)
|
||||
})
|
||||
.catch(function(e) {
|
||||
console.log(e)
|
||||
})
|
||||
})
|
||||
.catch(function(e) {
|
||||
console.log(e)
|
||||
})
|
||||
}
|
||||
doStuffWithProjectOwner(123)
|
||||
```
|
||||
|
||||
Here's a refactored version that defines composable, easy-to-reuse units, and that takes advantage of the error propagation feature of Promises to avoid repetitive error handling code:
|
||||
|
||||
```javascript
|
||||
// PREFER
|
||||
function findProject(id) {
|
||||
return fetch("/api/v1/projects/" + id, {method: "GET"}).then(json)
|
||||
}
|
||||
function findUser(id) {
|
||||
return fetch("/api/v1/users/" + id, {method: "GET"}).then(json)
|
||||
}
|
||||
function getProjectOwnerID(project) {
|
||||
return project.ownerID
|
||||
}
|
||||
|
||||
function doStuffWithProjectOwner(projectID) {
|
||||
return findProject(projectID)
|
||||
.then(getProjectOwnerID)
|
||||
.then(findUser)
|
||||
.then(doStuff)
|
||||
.catch(function(e) {
|
||||
console.log(e)
|
||||
})
|
||||
}
|
||||
|
||||
doStuffWithProjectOwner(123)
|
||||
```
|
||||
|
||||
The code above separates each request into a `findProject` and `findUser` functions which can be used in more use cases than only finding the user object that owns a project.
|
||||
|
||||
You can think of `.then` callbacks as pipes: the `getProjectOwnerID` receives the response of the `findProject` request as an input, and return an id, which is then passed as the input to `findUser`.
|
||||
|
||||
The feature of Promises that let us simplify error handling is that promises *absorb* other promises: in the `.then(findUser)` line, `findUser` returns a promise. Instead of a promise being passed as an input to the next callback, the promise chain waits for the `findUser` promise to complete, and only then continues down the chain of callbacks with the resolved value. If `findUser` throws an error, the `.catch` callback handles it, in addition to handling erros from `findProject` (and from `getProjectOwnerID`, for that matter).
|
||||
|
||||
Promises provide the machinery that facilitates writing small straightforward functions and composing them in flexible ways.
|
||||
|
||||
Mithril streams have many similarities to promises. The example above could be written like this:
|
||||
|
||||
```javascript
|
||||
// PREFER
|
||||
function findProject(id) {
|
||||
return m.request({method: "GET", url: "/api/v1/projects/:id", data: {id: id}})
|
||||
}
|
||||
function findUser(id) {
|
||||
return m.request({method: "GET", url: "/api/v1/users/:id", data: {id: id}})
|
||||
}
|
||||
function getProjectOwnerID(project) {
|
||||
return project.ownerID
|
||||
}
|
||||
|
||||
function doStuffWithProjectOwner(projectID) {
|
||||
return findProject(projectID)
|
||||
.run(getProjectOwnerID)
|
||||
.run(findUser)
|
||||
.run(doStuff)
|
||||
.catch(function(e) {
|
||||
console.log(e)
|
||||
})
|
||||
}
|
||||
|
||||
doStuffWithProjectOwner(123)
|
||||
```
|
||||
|
||||
Aside from the API signature difference between `fetch` and `m.request`, the only change required to achieve the same functionality was to replace all instances of `.then` with `.run`.
|
||||
|
||||
However, stream have some additional interesting properties. Let's suppose project objects have a `team` property that contains a list of user objects, and we wanted to display a list of designers and a list of developers in a project:
|
||||
|
||||
```javascript
|
||||
function getProjectTeam(project) {
|
||||
return project.team
|
||||
}
|
||||
function getTeamUsersByType(team, type) {
|
||||
return team.filter(function(user) {
|
||||
return user.type === type
|
||||
})
|
||||
}
|
||||
var project = findProject(123)
|
||||
var team = project.run(getProjectTeam)
|
||||
var designers = team.run(function(team) {
|
||||
return getTeamUsersByType(team, "designer")
|
||||
})
|
||||
var developers = team.run(function(team) {
|
||||
return getTeamUsersByType(team, "developer")
|
||||
})
|
||||
```
|
||||
|
||||
Now let's suppose that the team changed for the project and we need to fetch the `project` object from the server again. We would logically also want to update `designers` and `developers` so that the UI displays the correct users in their respective lists.
|
||||
|
||||
Fortunately, `project` is a stream, and `team`, `designers` and `developers` are streams derived from `project`. So to update the state of all these streams, we only need to do this:
|
||||
|
||||
```javascript
|
||||
findProject(123).run(project)
|
||||
```
|
||||
|
||||
Doing so updates all the streams, and therefore there's no need to place the filtering code in the view, where the filtering code would recompute the same thing on every render.
|
||||
|
||||
Returning streams from `m.request` streamlines use cases where efficient reactivity is desired, without losing the composable semantics of promises.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# Migrating from `v0.2.x` to `v1.x`
|
||||
|
||||
`v1.x` is largely API-compatible with `v0.2.x`, but there are a few breaking changes.
|
||||
`v1.x` is largely API-compatible with `v0.2.x`, but there are some breaking changes.
|
||||
|
||||
- [`m.prop` removed](#mprop-removed)
|
||||
- [`m.component` removed](#mcomponent-removed)
|
||||
- [`config` function](#config-function)
|
||||
- [Cancelling redraw from event handlers](#cancelling-redraw-from-event-handlers)
|
||||
|
|
@ -17,13 +18,39 @@
|
|||
- [`m.request`](#mrequest)
|
||||
- [`xlink` namespace required](#xlink-namespace-required)
|
||||
|
||||
---
|
||||
|
||||
## `m.prop` removed
|
||||
|
||||
In `v1.x`, `m.prop` is now a more powerful stream micro-library, but it's no longer part of core.
|
||||
|
||||
### `v0.2.x`
|
||||
|
||||
```javascript
|
||||
var m = require("mithril")
|
||||
|
||||
var num = m.prop(1)
|
||||
```
|
||||
|
||||
### `v1.x`
|
||||
|
||||
```javascript
|
||||
var m = require("mithril")
|
||||
var prop = require("mithril/stream")
|
||||
|
||||
var num = prop(1)
|
||||
var doubled = num.map(function(n) {return n * 2})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `m.component` removed
|
||||
|
||||
In `v0.2.x` components could be created using either `m(component)` or `m.component(component)`. `v1.x` only supports `m(component)`.
|
||||
|
||||
### `v0.2.x`
|
||||
|
||||
```js
|
||||
```javascript
|
||||
// These are equivalent
|
||||
m.component(component);
|
||||
m(component);
|
||||
|
|
@ -31,10 +58,12 @@ m(component);
|
|||
|
||||
### `v1.x`
|
||||
|
||||
```js
|
||||
```javascript
|
||||
m(component);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `config` function
|
||||
|
||||
In `v0.2.x` mithril provided a single lifecycle method, `config`. `v1.x` provides much more fine-grained control over the lifecycle of a vnode.
|
||||
|
|
@ -73,6 +102,8 @@ m("div", {
|
|||
|
||||
If available the DOM-Element of the vnode can be accessed at `vnode.dom`.
|
||||
|
||||
---
|
||||
|
||||
## Cancelling redraw from event handlers
|
||||
|
||||
`m.mount()` and `m.route()` still automatically redraw after a DOM event handler runs. Cancelling these redraws from within your event handlers is now done by setting the `redraw` property on the passed-in event object to `false`.
|
||||
|
|
@ -97,6 +128,8 @@ m("div", {
|
|||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Component `controller` function
|
||||
|
||||
In `v1.x` there is no more `controller` property in components, use `oninit` instead.
|
||||
|
|
@ -147,6 +180,8 @@ m.mount(document.body, {
|
|||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Component arguments
|
||||
|
||||
Arguments to a component in `v1.x` must be an object, simple values like `String`/`Number`/`Boolean` will be treated as text children. Arguments are accessed within the component by reading them from the `vnode.attrs` object.
|
||||
|
|
@ -183,6 +218,8 @@ var component = {
|
|||
m("div", m(component, { fooga : 1 }));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `view()` parameters
|
||||
|
||||
In `v0.2.x` view functions are passed a reference to the `controller` instance and (optionally) any options passed to the component. In `v1.x` they are passed **only** the `vnode`, exactly like the `controller` function.
|
||||
|
|
@ -214,6 +251,8 @@ m.mount(document.body, {
|
|||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Passing components to `m()`
|
||||
|
||||
In `v0.2.x` you could pass components as the second argument of `m()` w/o any wrapping required. To help with consistency in `v1.x` they must always be wrapped with a `m()` invocation.
|
||||
|
|
@ -230,6 +269,8 @@ m("div", component);
|
|||
m("div", m(component));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Passing vnodes to `m.mount()` and `m.route()`
|
||||
|
||||
In `v0.2.x`, `m.mount(element, component)` tolerated [vnodes](vnodes.md) as second arguments instead of [components](components.md) (even though it wasn't documented). Likewise, `m.route(element, defaultRoute, routes)` accepted vnodes as values in the `routes` object.
|
||||
|
|
@ -258,24 +299,28 @@ m.route(element, '/', {
|
|||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `m.route.mode`
|
||||
|
||||
In `v0.2.x` the routing mode could be set by assigning a string of `"pathname"`, `"hash"`, or `"search"` to `m.route.mode`. In `v.1.x` it is replaced by `m.route.prefix(prefix)` where `prefix` can be `#`, `?`, or an empty string (for "pathname" mode). The new API also supports hashbang (`#!`), which is the default, and it supports non-root pathnames and arbitrary mode variations such as querybang (`?!`)
|
||||
|
||||
### `v0.2.x`
|
||||
|
||||
```js
|
||||
```javascript
|
||||
m.route.mode = "pathname";
|
||||
m.route.mode = "search";
|
||||
```
|
||||
|
||||
### `v1.x`
|
||||
|
||||
```js
|
||||
```javascript
|
||||
m.route.prefix("");
|
||||
m.route.prefix("?");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `m.route()` and anchor tags
|
||||
|
||||
Handling clicks on anchor tags via the mithril router is similar to `v0.2.x` but uses a new lifecycle method and API.
|
||||
|
|
@ -300,6 +345,8 @@ m("a", {
|
|||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Reading/writing the current route
|
||||
|
||||
In `v0.2.x` all interaction w/ the current route happened via `m.route()`. In `v1.x` this has been broken out into two functions.
|
||||
|
|
@ -324,6 +371,8 @@ m.route.get();
|
|||
m.route.set("/other/route");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accessing route params
|
||||
|
||||
In `v0.2.x` reading route params was all handled through the `m.route.param()` method. In `v1.x` any route params are passed as the `attrs` object on the vnode passed as the first argument to lifecycle methods/`view`.
|
||||
|
|
@ -355,69 +404,80 @@ m.route(document.body, "/booga", {
|
|||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## m.request
|
||||
|
||||
[m.request](request.md) now returns an [m.prop stream](prop.md) instead of a promise. The main difference is you'll have to use `.run` to get similar functionality as a promise's `.then`:
|
||||
Promises returned by [m.request](request.md) are no longer `m.prop` getter-setters. In addition, `initialValue` is no longer a supported option.
|
||||
|
||||
### `v0.2.x`
|
||||
|
||||
```js
|
||||
m.request({ method: 'GET', url: 'https://api.github.com/' })
|
||||
.then(function (responseBody) {
|
||||
return m.request({ method: 'GET', url: responseBody.emojis_url });
|
||||
```javascript
|
||||
var data = m.request({
|
||||
method: "GET",
|
||||
url: "https://api.github.com/",
|
||||
initialValue: [],
|
||||
})
|
||||
.then(function (emojis) {
|
||||
console.log("+1 url:", emojis['+1']);
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
console.log(data())
|
||||
}, 1000)
|
||||
```
|
||||
|
||||
### `v1.x`
|
||||
|
||||
```js
|
||||
m.request({ method: 'GET', url: 'https://api.github.com/' })
|
||||
.run(function (responseBody) {
|
||||
return m.request({ method: 'GET', url: responseBody.emojis_url });
|
||||
```javascript
|
||||
var data = []
|
||||
m.request({
|
||||
method: "GET",
|
||||
url: "https://api.github.com/",
|
||||
})
|
||||
.run(function (emojis) {
|
||||
console.log("+1 url:", emojis['+1']);
|
||||
});
|
||||
.then(function (responseBody) {
|
||||
data = responseBody
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
console.log(data) // note: not a getter-setter
|
||||
}, 1000)
|
||||
```
|
||||
|
||||
The equivalent of `m.sync` is now `m.prop.merge`:
|
||||
The equivalent of `m.sync` is now `m.Promise.all`
|
||||
|
||||
### `v0.2.x`
|
||||
|
||||
```js
|
||||
```javascript
|
||||
m.sync([
|
||||
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
|
||||
m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }),
|
||||
])
|
||||
.then(function (users) {
|
||||
console.log("Contributors:", users[0].name, "and", users[1].name);
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### `v1.x`
|
||||
|
||||
```js
|
||||
m.prop.merge([
|
||||
```javascript
|
||||
m.Promise.all([
|
||||
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
|
||||
m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }),
|
||||
])
|
||||
.run(function (users) {
|
||||
.then(function (users) {
|
||||
console.log("Contributors:", users[0].name, "and", users[1].name);
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
Additionally, if the `extract` option is passed to `m.request` the return value of the provided function will be passed to the [m.prop stream](prop.md) directly, and any `deserialize` callback is ignored.
|
||||
Additionally, if the `extract` option is passed to `m.request` the return value of the provided function will be used directly to resolve its promise, and the `deserialize` callback is ignored.
|
||||
|
||||
---
|
||||
|
||||
## `xlink` namespace required
|
||||
|
||||
In `v0.2.x` when generating SVGs using `m()` the `xlink` namespace would automatically be added on certain attributes. For consistency in `v1.x` if you want to namespace an attribute you must do it yourself.
|
||||
In `v0.2.x`, the `xlink` namespace was the only supported attribute namespace, and it was supported via special casing behavior. Now namespace parsing is fully supported, and namespaced attributes should explicitly declare their namespace.
|
||||
|
||||
### `v0.2.x`
|
||||
|
||||
```js
|
||||
```javascript
|
||||
m("svg",
|
||||
// the `href` attribute is namespaced automatically
|
||||
m("image[href='image.gif']")
|
||||
|
|
@ -426,7 +486,7 @@ m("svg",
|
|||
|
||||
### `v1.x`
|
||||
|
||||
```js
|
||||
```javascript
|
||||
m("svg",
|
||||
// User-specified namespace on the `href` attribute
|
||||
m("image[xlink:href='image.gif']")
|
||||
|
|
|
|||
|
|
@ -1,30 +1,29 @@
|
|||
T.time("Setup");
|
||||
|
||||
var m = require("../../mithril")
|
||||
var request = m.request
|
||||
|
||||
//API calls
|
||||
var api = {
|
||||
home: function() {
|
||||
T.timeEnd("Setup")
|
||||
return request({method: "GET", url: T.apiUrl + "/threads/"})
|
||||
return m.request({method: "GET", url: T.apiUrl + "/threads/"})
|
||||
},
|
||||
thread: function(id) {
|
||||
T.timeEnd("Setup")
|
||||
return request({method: "GET", url: T.apiUrl + "/comments/" + id}).run(T.transformResponse)
|
||||
return m.request({method: "GET", url: T.apiUrl + "/comments/" + id}).then(T.transformResponse)
|
||||
},
|
||||
newThread: function(text) {
|
||||
return request({method: "POST", url: T.apiUrl + "/threads/create",data: {text: text}})
|
||||
return m.request({method: "POST", url: T.apiUrl + "/threads/create",data: {text: text}})
|
||||
},
|
||||
newComment: function(text, id) {
|
||||
return request({method: "POST", url: T.apiUrl + "/comments/create", data: {text: text, parent: id}});
|
||||
return m.request({method: "POST", url: T.apiUrl + "/comments/create", data: {text: text, parent: id}});
|
||||
}
|
||||
};
|
||||
|
||||
var threads = [], current = null, loaded = false, error = false, notFound = false
|
||||
function loadThreads() {
|
||||
loaded = false
|
||||
api.home().run(function(response) {
|
||||
api.home().then(function(response) {
|
||||
document.title = "ThreaditJS: Mithril | Home"
|
||||
threads = response.data
|
||||
loaded = true
|
||||
|
|
@ -36,7 +35,7 @@ function loadThreads() {
|
|||
function loadThread(id) {
|
||||
loaded = false
|
||||
notFound = false
|
||||
api.thread(id).run(function(response) {
|
||||
api.thread(id).then(function(response) {
|
||||
document.title = "ThreaditJS: Mithril | " + T.trimTitle(response.root.text);
|
||||
loaded = true
|
||||
current = response
|
||||
|
|
@ -52,7 +51,7 @@ function unloadThread() {
|
|||
|
||||
function createThread() {
|
||||
var threadText = document.getElementById("threadText")
|
||||
api.newThread(threadText.value).run(function(response) {
|
||||
api.newThread(threadText.value).then(function(response) {
|
||||
threadText.value = "";
|
||||
threads.push(response.data);
|
||||
})
|
||||
|
|
@ -66,7 +65,7 @@ function showReplying(vnode) {
|
|||
}
|
||||
|
||||
function submitComment(vnode) {
|
||||
api.newComment(vnode.state.newComment, vnode.attrs.node.id).run(function(response) {
|
||||
api.newComment(vnode.state.newComment, vnode.attrs.node.id).then(function(response) {
|
||||
vnode.state.newComment = ""
|
||||
vnode.state.replying = false
|
||||
vnode.attrs.node.children.push(response.data)
|
||||
|
|
|
|||
2
index.js
2
index.js
|
|
@ -6,10 +6,10 @@ var redrawService = require("./redraw")
|
|||
|
||||
requestService.setCompletionCallback(redrawService.publish)
|
||||
|
||||
m.Promise = require("./promise/promise")
|
||||
m.mount = require("./mount")
|
||||
m.route = require("./route")
|
||||
m.withAttr = require("./util/withAttr")
|
||||
m.prop = require("./stream")
|
||||
m.render = require("./render").render
|
||||
m.redraw = redrawService.publish
|
||||
m.request = requestService.request
|
||||
|
|
|
|||
479
mithril.js
479
mithril.js
|
|
@ -80,198 +80,100 @@ hyperscript.fragment = function(attrs1, children) {
|
|||
return Vnode("[", attrs1.key, attrs1, Vnode.normalizeChildren(children), undefined, undefined)
|
||||
}
|
||||
var m = hyperscript
|
||||
var _7 = function(log) {
|
||||
var guid = 0, noop = function() {}, HALT = {}
|
||||
function createStream() {
|
||||
function stream() {
|
||||
if (arguments.length > 0 && arguments[0] !== HALT) updateStream(stream, arguments[0], undefined)
|
||||
return stream._state.value
|
||||
}
|
||||
initStream(stream)
|
||||
if (arguments.length > 0 && arguments[0] !== HALT) updateStream(stream, arguments[0], undefined)
|
||||
return stream
|
||||
}
|
||||
function initStream(stream) {
|
||||
stream.constructor = createStream
|
||||
stream._state = {id: guid++, value: undefined, error: undefined, state: 0, derive: undefined, recover: undefined, deps: {}, parents: [], errorStream: undefined, endStream: undefined}
|
||||
stream["fantasy-land/map"] = map, stream["fantasy-land/ap"] = ap, stream["fantasy-land/of"] = createStream
|
||||
stream.valueOf = valueOf, stream.toJSON = toJSON, stream.toString = valueOf
|
||||
stream.run = run, stream.catch = doCatch
|
||||
Object.defineProperties(stream, {
|
||||
error: {get: function() {
|
||||
if (!stream._state.errorStream) {
|
||||
var errorStream = function() {
|
||||
if (arguments.length > 0 && arguments[0] !== HALT) updateStream(stream, undefined, arguments[0])
|
||||
return stream._state.error
|
||||
}
|
||||
initStream(errorStream)
|
||||
initDependency(errorStream, [stream], noop, noop)
|
||||
stream._state.errorStream = errorStream
|
||||
}
|
||||
return stream._state.errorStream
|
||||
}},
|
||||
end: {get: function() {
|
||||
if (!stream._state.endStream) {
|
||||
var endStream = createStream()
|
||||
endStream["fantasy-land/map"](function(value) {
|
||||
if (value === true) unregisterStream(stream), unregisterStream(endStream)
|
||||
return value
|
||||
})
|
||||
stream._state.endStream = endStream
|
||||
}
|
||||
return stream._state.endStream
|
||||
}}
|
||||
})
|
||||
}
|
||||
function updateStream(stream, value, error) {
|
||||
updateState(stream, value, error)
|
||||
for (var id in stream._state.deps) updateDependency(stream._state.deps[id], false)
|
||||
finalize(stream)
|
||||
}
|
||||
function updateState(stream, value, error) {
|
||||
error = unwrapError(value, error)
|
||||
if (error !== undefined && typeof stream._state.recover === "function") {
|
||||
if (!resolve(stream, updateValues, true)) return
|
||||
}
|
||||
else updateValues(stream, value, error)
|
||||
stream._state.changed = true
|
||||
if (stream._state.state !== 2) stream._state.state = 1
|
||||
}
|
||||
function updateValues(stream, value, error) {
|
||||
stream._state.value = value
|
||||
stream._state.error = error
|
||||
}
|
||||
function updateDependency(stream, mustSync) {
|
||||
var state = stream._state, parents = state.parents
|
||||
if (parents.length > 0 && parents.filter(active).length === parents.length && (mustSync || parents.filter(changed).length > 0)) {
|
||||
var failed = parents.filter(errored)
|
||||
if (failed.length > 0) updateState(stream, undefined, failed[0]._state.error)
|
||||
else resolve(stream, updateState, false)
|
||||
}
|
||||
}
|
||||
function resolve(stream, update, shouldRecover) {
|
||||
/** @constructor */
|
||||
var PromisePolyfill0 = function(executor) {
|
||||
if (!(this instanceof PromisePolyfill0)) throw new Error("Promise must be called with `new`")
|
||||
if (typeof executor !== "function") throw new TypeError("executor must be a function")
|
||||
var self = this, resolvers = [], rejectors = [], resolveCurrent = handler(resolvers, true), rejectCurrent = handler(rejectors, false)
|
||||
var instance = self._instance = {resolvers: resolvers, rejectors: rejectors}
|
||||
var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout
|
||||
function handler(list, shouldAbsorb) {
|
||||
return function execute(value) {
|
||||
var then
|
||||
try {
|
||||
var value = shouldRecover ? stream._state.recover() : stream._state.derive()
|
||||
if (value === HALT) return false
|
||||
update(stream, value, undefined)
|
||||
if (shouldAbsorb && value != null && (typeof value === "object" || typeof value === "function") && typeof (then = value.then) === "function") {
|
||||
if (value === self) throw new TypeError("Promise can't be resolved w/ itself")
|
||||
executeOnce(then.bind(value))
|
||||
}
|
||||
else {
|
||||
callAsync(function() {
|
||||
if (!shouldAbsorb && list.length === 0) console.error("Possible unhandled promise rejection:", value)
|
||||
for (var i = 0; i < list.length; i++) list[i](value)
|
||||
resolvers.length = 0, rejectors.length = 0
|
||||
instance.state = shouldAbsorb
|
||||
instance.retry = function() {execute(value)}
|
||||
})
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
update(stream, undefined, e.__error != null ? e.__error : e)
|
||||
if (e.__error == null) reportUncaughtError(stream, e)
|
||||
}
|
||||
return true
|
||||
}
|
||||
function unwrapError(value, error) {
|
||||
if (value != null && value.constructor === createStream) {
|
||||
if (value._state.error !== undefined) error = value._state.error
|
||||
else error = unwrapError(value._state.value, value._state.error)
|
||||
}
|
||||
return error
|
||||
}
|
||||
function finalize(stream) {
|
||||
stream._state.changed = false
|
||||
for (var id in stream._state.deps) stream._state.deps[id]._state.changed = false
|
||||
}
|
||||
function reportUncaughtError(stream, e) {
|
||||
if (Object.keys(stream._state.deps).length === 0) {
|
||||
setTimeout(function() {
|
||||
if (Object.keys(stream._state.deps).length === 0) log(e)
|
||||
}, 0)
|
||||
rejectCurrent(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
function executeOnce(then) {
|
||||
var runs = 0
|
||||
function run(fn) {
|
||||
var self = createStream(), stream = this
|
||||
return initDependency(self, [stream], function() {
|
||||
return absorb(self, fn(stream()))
|
||||
}, undefined)
|
||||
return function(value) {
|
||||
if (runs++ > 0) return
|
||||
fn(value)
|
||||
}
|
||||
function doCatch(fn) {
|
||||
var self = createStream(), stream = this
|
||||
var derive = function() {return stream._state.value}
|
||||
var recover = function() {return absorb(self, fn(stream._state.error))}
|
||||
return initDependency(self, [stream], derive, recover)
|
||||
}
|
||||
function combine(fn, streams) {
|
||||
if (streams.length > streams.filter(valid).length) throw new Error("Ensure that each item passed to m.prop.combine/m.prop.merge is a stream")
|
||||
return initDependency(createStream(), streams, function() {
|
||||
var failed = streams.filter(errored)
|
||||
if (failed.length > 0) throw {__error: failed[0]._state.error}
|
||||
return fn.apply(this, streams.concat([streams.filter(changed)]))
|
||||
}, undefined)
|
||||
var onerror = run(rejectCurrent)
|
||||
try {then(run(resolveCurrent), onerror)} catch (e) {onerror(e)}
|
||||
}
|
||||
function absorb(stream, value) {
|
||||
if (value != null && value.constructor === createStream) {
|
||||
var absorbable = value
|
||||
var update = function() {
|
||||
updateState(stream, absorbable._state.value, absorbable._state.error)
|
||||
for (var id in stream._state.deps) updateDependency(stream._state.deps[id], false)
|
||||
executeOnce(executor)
|
||||
}
|
||||
absorbable["fantasy-land/map"](update).catch(function(e) {
|
||||
update()
|
||||
throw {__error: e}
|
||||
PromisePolyfill0.prototype.then = function(onFulfilled, onRejection) {
|
||||
var self = this, instance = self._instance
|
||||
function handle(callback, list, next, state) {
|
||||
list.push(function(value) {
|
||||
if (typeof callback !== "function") next(value)
|
||||
else try {resolveNext(callback(value))} catch (e) {if (rejectNext) rejectNext(e)}
|
||||
})
|
||||
|
||||
if (absorbable._state.state === 0) return HALT
|
||||
if (absorbable._state.error) throw {__error: absorbable._state.error}
|
||||
value = absorbable._state.value
|
||||
if (typeof instance.retry === "function" && state === instance.state) instance.retry()
|
||||
}
|
||||
return value
|
||||
var resolveNext, rejectNext
|
||||
var promise = new PromisePolyfill0(function(resolve, reject) {resolveNext = resolve, rejectNext = reject})
|
||||
handle(onFulfilled, instance.resolvers, resolveNext, true), handle(onRejection, instance.rejectors, rejectNext, false)
|
||||
return promise
|
||||
}
|
||||
function initDependency(dep, streams, derive, recover) {
|
||||
var state = dep._state
|
||||
state.derive = derive
|
||||
state.recover = recover
|
||||
state.parents = streams.filter(notEnded)
|
||||
registerDependency(dep, state.parents)
|
||||
updateDependency(dep, true)
|
||||
return dep
|
||||
PromisePolyfill0.prototype.catch = function(onRejection) {
|
||||
return this.then(null, onRejection)
|
||||
}
|
||||
function registerDependency(stream, parents) {
|
||||
for (var i = 0; i < parents.length; i++) {
|
||||
parents[i]._state.deps[stream._state.id] = stream
|
||||
registerDependency(stream, parents[i]._state.parents)
|
||||
PromisePolyfill0.resolve = function(value) {
|
||||
if (value instanceof PromisePolyfill0) return value
|
||||
return new PromisePolyfill0(function(resolve) {resolve(value)})
|
||||
}
|
||||
PromisePolyfill0.reject = function(value) {
|
||||
return new PromisePolyfill0(function(resolve, reject) {reject(value)})
|
||||
}
|
||||
function unregisterStream(stream) {
|
||||
for (var i = 0; i < stream._state.parents.length; i++) {
|
||||
var parent = stream._state.parents[i]
|
||||
delete parent._state.deps[stream._state.id]
|
||||
PromisePolyfill0.all = function(list) {
|
||||
return new PromisePolyfill0(function(resolve, reject) {
|
||||
var total = list.length, count = 0, values = []
|
||||
if (list.length === 0) resolve([])
|
||||
else for (var i = 0; i < list.length; i++) {
|
||||
(function(i) {
|
||||
function consume(value) {
|
||||
count++
|
||||
values[i] = value
|
||||
if (count === total) resolve(values)
|
||||
}
|
||||
for (var id in stream._state.deps) {
|
||||
var dependent = stream._state.deps[id]
|
||||
var index = dependent._state.parents.indexOf(stream)
|
||||
if (index > -1) dependent._state.parents.splice(index, 1)
|
||||
if (list[i] != null && (typeof list[i] === "object" || typeof list[i] === "function") && typeof list[i].then === "function") {
|
||||
list[i].then(consume, reject)
|
||||
}
|
||||
stream._state.state = 2 //ended
|
||||
stream._state.deps = {}
|
||||
else consume(list[i])
|
||||
})(i)
|
||||
}
|
||||
function map(fn) {return combine(function(stream) {return fn(stream())}, [this])}
|
||||
function ap(stream) {return combine(function(s1, s2) {return s1()(s2())}, [stream, this])}
|
||||
function valueOf() {return this._state.value}
|
||||
function toJSON() {return this._state.value != null && typeof this._state.value.toJSON === "function" ? this._state.value.toJSON() : this._state.value}
|
||||
function valid(stream) {return stream._state }
|
||||
function active(stream) {return stream._state.state === 1}
|
||||
function changed(stream) {return stream._state.changed}
|
||||
function notEnded(stream) {return stream._state.state !== 2}
|
||||
function errored(stream) {return stream._state.error}
|
||||
function reject(e) {
|
||||
var stream = createStream()
|
||||
stream.error(e)
|
||||
return stream
|
||||
})
|
||||
}
|
||||
function merge(streams) {
|
||||
return combine(function () {
|
||||
return streams.map(function(s) {return s()})
|
||||
}, streams)
|
||||
PromisePolyfill0.race = function(list) {
|
||||
return new PromisePolyfill0(function(resolve, reject) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
list[i].then(resolve, reject)
|
||||
}
|
||||
createStream["fantasy-land/of"] = createStream
|
||||
createStream.merge = merge
|
||||
createStream.combine = combine
|
||||
createStream.reject = reject
|
||||
createStream.HALT = HALT
|
||||
return createStream
|
||||
})
|
||||
}
|
||||
var Stream = _7(console.log.bind(console))
|
||||
var PromisePolyfill = typeof Promise !== "undefined" ? Promise : PromisePolyfill0
|
||||
var buildQueryString = function(object) {
|
||||
if (Object.prototype.toString.call(object) !== "[object Object]") return ""
|
||||
var args = []
|
||||
|
|
@ -279,25 +181,42 @@ var buildQueryString = function(object) {
|
|||
destructure(key0, object[key0])
|
||||
}
|
||||
return args.join("&")
|
||||
function destructure(key0, value1) {
|
||||
if (value1 instanceof Array) {
|
||||
for (var i = 0; i < value1.length; i++) {
|
||||
destructure(key0 + "[" + i + "]", value1[i])
|
||||
function destructure(key0, value) {
|
||||
if (value instanceof Array) {
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
destructure(key0 + "[" + i + "]", value[i])
|
||||
}
|
||||
}
|
||||
else if (Object.prototype.toString.call(value1) === "[object Object]") {
|
||||
for (var i in value1) {
|
||||
destructure(key0 + "[" + i + "]", value1[i])
|
||||
else if (Object.prototype.toString.call(value) === "[object Object]") {
|
||||
for (var i in value) {
|
||||
destructure(key0 + "[" + i + "]", value[i])
|
||||
}
|
||||
}
|
||||
else args.push(encodeURIComponent(key0) + (value1 != null && value1 !== "" ? "=" + encodeURIComponent(value1) : ""))
|
||||
else args.push(encodeURIComponent(key0) + (value != null && value !== "" ? "=" + encodeURIComponent(value) : ""))
|
||||
}
|
||||
}
|
||||
var _9 = function($window, Stream0) {
|
||||
var _8 = function($window, Promise) {
|
||||
var callbackCount = 0
|
||||
var count = 0
|
||||
var oncompletion
|
||||
function setCompletionCallback(callback) {oncompletion = callback}
|
||||
function complete() {if (--count === 0 && typeof oncompletion === "function") oncompletion()}
|
||||
function finalize(promise0) {
|
||||
var then0 = promise0.then
|
||||
promise0.then = function() {
|
||||
count++
|
||||
var next = then0.apply(promise0, arguments)
|
||||
next.then(complete, function(e) {
|
||||
complete()
|
||||
throw e
|
||||
})
|
||||
return finalize(next)
|
||||
}
|
||||
return promise0
|
||||
}
|
||||
|
||||
function request(args, extra) {
|
||||
return finalize(new Promise(function(resolve, reject) {
|
||||
if (typeof args === "string") {
|
||||
var url = args
|
||||
if (typeof extra === "object") args = extra
|
||||
|
|
@ -305,11 +224,9 @@ var _9 = function($window, Stream0) {
|
|||
if (typeof args.url === "undefined") args.url = url
|
||||
}
|
||||
if (typeof args.method === "undefined") args.method = "GET"
|
||||
var stream0 = Stream0()
|
||||
if (args.initialValue !== undefined) stream0(args.initialValue)
|
||||
args.method = args.method.toUpperCase()
|
||||
var useBody = typeof args.useBody === "boolean" ? args.useBody : args.method !== "GET" && args.method !== "TRACE"
|
||||
if (typeof args.serialize !== "function") args.serialize = typeof FormData !== "undefined" && args.data instanceof FormData ? function(value0) {return value0} : JSON.stringify
|
||||
if (typeof args.serialize !== "function") args.serialize = typeof FormData !== "undefined" && args.data instanceof FormData ? function(value) {return value} : JSON.stringify
|
||||
if (typeof args.deserialize !== "function") args.deserialize = deserialize
|
||||
if (typeof args.extract !== "function") args.extract = extract
|
||||
args.url = interpolate(args.url, args.data)
|
||||
|
|
@ -329,39 +246,35 @@ var _9 = function($window, Stream0) {
|
|||
try {
|
||||
var response = (args.extract !== extract) ? args.extract(xhr, args) : args.deserialize(args.extract(xhr, args))
|
||||
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
|
||||
stream0(cast(args.type, response))
|
||||
resolve(cast(args.type, response))
|
||||
}
|
||||
else {
|
||||
var error = new Error(xhr.responseText)
|
||||
for (var key in response) error[key] = response[key]
|
||||
stream0.error(error)
|
||||
reject(error)
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
stream0.error(e)
|
||||
reject(e)
|
||||
}
|
||||
if (typeof oncompletion === "function") oncompletion()
|
||||
}
|
||||
}
|
||||
if (useBody && (args.data != null)) xhr.send(args.data)
|
||||
else xhr.send()
|
||||
return stream0
|
||||
}))
|
||||
}
|
||||
function jsonp(args) {
|
||||
var stream0 = Stream0()
|
||||
if (args.initialValue !== undefined) stream0(args.initialValue)
|
||||
return finalize(new Promise(function(resolve, reject) {
|
||||
var callbackName = args.callbackName || "_mithril_" + Math.round(Math.random() * 1e16) + "_" + callbackCount++
|
||||
var script = $window.document.createElement("script")
|
||||
$window[callbackName] = function(data) {
|
||||
script.parentNode.removeChild(script)
|
||||
stream0(cast(args.type, data))
|
||||
if (typeof oncompletion === "function") oncompletion()
|
||||
resolve(cast(args.type, data))
|
||||
delete $window[callbackName]
|
||||
}
|
||||
script.onerror = function() {
|
||||
script.parentNode.removeChild(script)
|
||||
stream0.error(new Error("JSONP request failed"))
|
||||
if (typeof oncompletion === "function") oncompletion()
|
||||
reject(new Error("JSONP request failed"))
|
||||
delete $window[callbackName]
|
||||
}
|
||||
if (args.data == null) args.data = {}
|
||||
|
|
@ -369,7 +282,7 @@ var _9 = function($window, Stream0) {
|
|||
args.data[args.callbackKey || "callback"] = callbackName
|
||||
script.src = assemble(args.url, args.data)
|
||||
$window.document.documentElement.appendChild(script)
|
||||
return stream0
|
||||
}))
|
||||
}
|
||||
function interpolate(url, data) {
|
||||
if (data == null) return url
|
||||
|
|
@ -409,12 +322,12 @@ var _9 = function($window, Stream0) {
|
|||
}
|
||||
return {request: request, jsonp: jsonp, setCompletionCallback: setCompletionCallback}
|
||||
}
|
||||
var requestService = _9(window, Stream)
|
||||
var _12 = function() {
|
||||
var requestService = _8(window, PromisePolyfill)
|
||||
var _11 = function() {
|
||||
var callbacks = []
|
||||
function unsubscribe(callback) {
|
||||
var index0 = callbacks.indexOf(callback)
|
||||
if (index0 > -1) callbacks.splice(index0, 1)
|
||||
var index = callbacks.indexOf(callback)
|
||||
if (index > -1) callbacks.splice(index, 1)
|
||||
}
|
||||
function publish() {
|
||||
for (var i = 0; i < callbacks.length; i++) {
|
||||
|
|
@ -423,19 +336,20 @@ var _12 = function() {
|
|||
}
|
||||
return {subscribe: callbacks.push.bind(callbacks), unsubscribe: unsubscribe, publish: publish}
|
||||
}
|
||||
var redrawService = _12()
|
||||
var redrawService = _11()
|
||||
requestService.setCompletionCallback(redrawService.publish)
|
||||
m.Promise = PromisePolyfill
|
||||
var _14 = function($window) {
|
||||
var $doc = $window.document
|
||||
var $emptyFragment = $doc.createDocumentFragment()
|
||||
var onevent
|
||||
function setEventCallback(callback) {return onevent = callback}
|
||||
//create
|
||||
function createNodes(parent0, vnodes, start, end, hooks, nextSibling, ns) {
|
||||
function createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) {
|
||||
for (var i = start; i < end; i++) {
|
||||
var vnode = vnodes[i]
|
||||
if (vnode != null) {
|
||||
insertNode(parent0, createNode(vnode, hooks, ns), nextSibling)
|
||||
insertNode(parent, createNode(vnode, hooks, ns), nextSibling)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -457,8 +371,8 @@ var _14 = function($window) {
|
|||
}
|
||||
function createHTML(vnode) {
|
||||
var match1 = vnode.children.match(/^\s*?<(\w+)/im) || []
|
||||
var parent0 = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}[match1[1]] || "div"
|
||||
var temp = $doc.createElement(parent0)
|
||||
var parent = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}[match1[1]] || "div"
|
||||
var temp = $doc.createElement(parent)
|
||||
temp.innerHTML = vnode.children
|
||||
vnode.dom = temp.firstChild
|
||||
vnode.domSize = temp.childNodes.length
|
||||
|
|
@ -511,7 +425,7 @@ var _14 = function($window) {
|
|||
return element
|
||||
}
|
||||
function createComponent(vnode, hooks, ns) {
|
||||
// For object literals since `Vnode()` always sets the `state0` field.
|
||||
// For object literals since `Vnode()` always sets the `state` field.
|
||||
if (!vnode.state) vnode.state = {}
|
||||
assign(vnode.state, vnode.tag)
|
||||
var view = vnode.tag.view
|
||||
|
|
@ -532,10 +446,10 @@ var _14 = function($window) {
|
|||
return $emptyFragment
|
||||
}
|
||||
}
|
||||
//update0
|
||||
function updateNodes(parent0, old, vnodes, hooks, nextSibling, ns) {
|
||||
//update
|
||||
function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) {
|
||||
if (old === vnodes || old == null && vnodes == null) return
|
||||
else if (old == null) createNodes(parent0, vnodes, 0, vnodes.length, hooks, nextSibling, undefined)
|
||||
else if (old == null) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, undefined)
|
||||
else if (vnodes == null) removeNodes(old, 0, old.length, vnodes)
|
||||
else {
|
||||
var recycling = isRecyclable(old, vnodes)
|
||||
|
|
@ -543,28 +457,28 @@ var _14 = function($window) {
|
|||
if (old.length === vnodes.length && vnodes[0] != null && vnodes[0].key == null) {
|
||||
for (var i = 0; i < old.length; i++) {
|
||||
if (old[i] === vnodes[i] || old[i] == null && vnodes[i] == null) continue
|
||||
else if (old[i] == null) insertNode(parent0, createNode(vnodes[i], hooks, ns), getNextSibling(old, i + 1, nextSibling))
|
||||
else if (old[i] == null) insertNode(parent, createNode(vnodes[i], hooks, ns), getNextSibling(old, i + 1, nextSibling))
|
||||
else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes)
|
||||
else updateNode(parent0, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), recycling, ns)
|
||||
if (recycling && old[i].tag === vnodes[i].tag) insertNode(parent0, toFragment(old[i]), getNextSibling(old, i + 1, nextSibling))
|
||||
else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), recycling, ns)
|
||||
if (recycling && old[i].tag === vnodes[i].tag) insertNode(parent, toFragment(old[i]), getNextSibling(old, i + 1, nextSibling))
|
||||
}
|
||||
}
|
||||
else {
|
||||
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map0
|
||||
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 && !recycling) oldStart++, start++
|
||||
else if (o != null && v != null && o.key === v.key) {
|
||||
oldStart++, start++
|
||||
updateNode(parent0, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
|
||||
if (recycling && o.tag === v.tag) insertNode(parent0, toFragment(o), nextSibling)
|
||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
|
||||
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
||||
}
|
||||
else {
|
||||
var o = old[oldEnd]
|
||||
if (o === v && !recycling) oldEnd--, start++
|
||||
else if (o != null && v != null && o.key === v.key) {
|
||||
updateNode(parent0, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||
if (recycling || start < end) insertNode(parent0, toFragment(o), getNextSibling(old, oldStart, nextSibling))
|
||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||
if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
|
||||
oldEnd--, start++
|
||||
}
|
||||
else break
|
||||
|
|
@ -574,25 +488,25 @@ var _14 = function($window) {
|
|||
var o = old[oldEnd], v = vnodes[end]
|
||||
if (o === v && !recycling) oldEnd--, end--
|
||||
else if (o != null && v != null && o.key === v.key) {
|
||||
updateNode(parent0, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||
if (recycling && o.tag === v.tag) insertNode(parent0, toFragment(o), nextSibling)
|
||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
||||
if (o.dom != null) nextSibling = o.dom
|
||||
oldEnd--, end--
|
||||
}
|
||||
else {
|
||||
if (!map0) map0 = getKeyMap(old, oldEnd)
|
||||
if (!map) map = getKeyMap(old, oldEnd)
|
||||
if (v != null) {
|
||||
var oldIndex = map0[v.key]
|
||||
var oldIndex = map[v.key]
|
||||
if (oldIndex != null) {
|
||||
var movable = old[oldIndex]
|
||||
updateNode(parent0, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||
insertNode(parent0, toFragment(movable), nextSibling)
|
||||
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||
insertNode(parent, toFragment(movable), nextSibling)
|
||||
old[oldIndex].skip = true
|
||||
if (movable.dom != null) nextSibling = movable.dom
|
||||
}
|
||||
else {
|
||||
var dom = createNode(v, hooks, undefined)
|
||||
insertNode(parent0, dom, nextSibling)
|
||||
insertNode(parent, dom, nextSibling)
|
||||
nextSibling = dom
|
||||
}
|
||||
}
|
||||
|
|
@ -600,12 +514,12 @@ var _14 = function($window) {
|
|||
}
|
||||
if (end < start) break
|
||||
}
|
||||
createNodes(parent0, vnodes, start, end + 1, hooks, nextSibling, ns)
|
||||
createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)
|
||||
removeNodes(old, oldStart, oldEnd + 1, vnodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
function updateNode(parent0, old, vnode, hooks, nextSibling, recycling, ns) {
|
||||
function updateNode(parent, old, vnode, hooks, nextSibling, recycling, ns) {
|
||||
var oldTag = old.tag, tag = vnode.tag
|
||||
if (oldTag === tag) {
|
||||
vnode.state = old.state
|
||||
|
|
@ -617,16 +531,16 @@ var _14 = function($window) {
|
|||
if (typeof oldTag === "string") {
|
||||
switch (oldTag) {
|
||||
case "#": updateText(old, vnode); break
|
||||
case "<": updateHTML(parent0, old, vnode, nextSibling); break
|
||||
case "[": updateFragment(parent0, old, vnode, hooks, nextSibling, ns); break
|
||||
case "<": updateHTML(parent, old, vnode, nextSibling); break
|
||||
case "[": updateFragment(parent, old, vnode, hooks, nextSibling, ns); break
|
||||
default: updateElement(old, vnode, hooks, ns)
|
||||
}
|
||||
}
|
||||
else updateComponent(parent0, old, vnode, hooks, nextSibling, recycling, ns)
|
||||
else updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns)
|
||||
}
|
||||
else {
|
||||
removeNode(old, null)
|
||||
insertNode(parent0, createNode(vnode, hooks, undefined), nextSibling)
|
||||
insertNode(parent, createNode(vnode, hooks, undefined), nextSibling)
|
||||
}
|
||||
}
|
||||
function updateText(old, vnode) {
|
||||
|
|
@ -635,15 +549,15 @@ var _14 = function($window) {
|
|||
}
|
||||
vnode.dom = old.dom
|
||||
}
|
||||
function updateHTML(parent0, old, vnode, nextSibling) {
|
||||
function updateHTML(parent, old, vnode, nextSibling) {
|
||||
if (old.children !== vnode.children) {
|
||||
toFragment(old)
|
||||
insertNode(parent0, createHTML(vnode), nextSibling)
|
||||
insertNode(parent, createHTML(vnode), nextSibling)
|
||||
}
|
||||
else vnode.dom = old.dom, vnode.domSize = old.domSize
|
||||
}
|
||||
function updateFragment(parent0, old, vnode, hooks, nextSibling, ns) {
|
||||
updateNodes(parent0, old.children, vnode.children, hooks, nextSibling, ns)
|
||||
function updateFragment(parent, old, vnode, hooks, nextSibling, ns) {
|
||||
updateNodes(parent, old.children, vnode.children, hooks, nextSibling, ns)
|
||||
var domSize = 0, children = vnode.children
|
||||
vnode.dom = null
|
||||
if (children != null) {
|
||||
|
|
@ -666,7 +580,7 @@ var _14 = function($window) {
|
|||
if (vnode.tag === "textarea") {
|
||||
if (vnode.attrs == null) vnode.attrs = {}
|
||||
if (vnode.text != null) {
|
||||
vnode.attrs.value = vnode.text //FIXME handle multiple children
|
||||
vnode.attrs.value = vnode.text //FIXME handle0 multiple children
|
||||
vnode.text = undefined
|
||||
}
|
||||
}
|
||||
|
|
@ -683,12 +597,12 @@ var _14 = function($window) {
|
|||
updateNodes(element, old.children, vnode.children, hooks, null, ns)
|
||||
}
|
||||
}
|
||||
function updateComponent(parent0, old, vnode, hooks, nextSibling, recycling, ns) {
|
||||
function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) {
|
||||
vnode.instance = Vnode.normalize(vnode.tag.view.call(vnode.state, vnode))
|
||||
updateLifecycle(vnode.tag, vnode, hooks, recycling)
|
||||
if (vnode.instance != null) {
|
||||
if (old.instance == null) insertNode(parent0, createNode(vnode.instance, hooks, ns), nextSibling)
|
||||
else updateNode(parent0, old.instance, vnode.instance, hooks, nextSibling, recycling, ns)
|
||||
if (old.instance == null) insertNode(parent, createNode(vnode.instance, hooks, ns), nextSibling)
|
||||
else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns)
|
||||
vnode.dom = vnode.instance.dom
|
||||
vnode.domSize = vnode.instance.domSize
|
||||
}
|
||||
|
|
@ -714,23 +628,23 @@ var _14 = function($window) {
|
|||
return false
|
||||
}
|
||||
function getKeyMap(vnodes, end) {
|
||||
var map0 = {}, i = 0
|
||||
var map = {}, i = 0
|
||||
for (var i = 0; i < end; i++) {
|
||||
var vnode = vnodes[i]
|
||||
if (vnode != null) {
|
||||
var key1 = vnode.key
|
||||
if (key1 != null) map0[key1] = i
|
||||
if (key1 != null) map[key1] = i
|
||||
}
|
||||
}
|
||||
return map0
|
||||
return map
|
||||
}
|
||||
function toFragment(vnode) {
|
||||
var count = vnode.domSize
|
||||
if (count != null || vnode.dom == null) {
|
||||
var count0 = vnode.domSize
|
||||
if (count0 != null || vnode.dom == null) {
|
||||
var fragment = $doc.createDocumentFragment()
|
||||
if (count > 0) {
|
||||
if (count0 > 0) {
|
||||
var dom = vnode.dom
|
||||
while (--count) fragment.appendChild(dom.nextSibling)
|
||||
while (--count0) fragment.appendChild(dom.nextSibling)
|
||||
fragment.insertBefore(dom, fragment.firstChild)
|
||||
}
|
||||
return fragment
|
||||
|
|
@ -743,9 +657,9 @@ var _14 = function($window) {
|
|||
}
|
||||
return nextSibling
|
||||
}
|
||||
function insertNode(parent0, dom, nextSibling) {
|
||||
if (nextSibling && nextSibling.parentNode) parent0.insertBefore(dom, nextSibling)
|
||||
else parent0.appendChild(dom)
|
||||
function insertNode(parent, dom, nextSibling) {
|
||||
if (nextSibling && nextSibling.parentNode) parent.insertBefore(dom, nextSibling)
|
||||
else parent.appendChild(dom)
|
||||
}
|
||||
function setContentEditable(vnode) {
|
||||
var children = vnode.children
|
||||
|
|
@ -789,10 +703,10 @@ var _14 = function($window) {
|
|||
if (++called === expected) {
|
||||
onremove(vnode)
|
||||
if (vnode.dom) {
|
||||
var count = vnode.domSize || 1
|
||||
if (count > 1) {
|
||||
var count0 = vnode.domSize || 1
|
||||
if (count0 > 1) {
|
||||
var dom = vnode.dom
|
||||
while (--count) {
|
||||
while (--count0) {
|
||||
removeNodeFromDOM(dom.nextSibling)
|
||||
}
|
||||
}
|
||||
|
|
@ -806,8 +720,8 @@ var _14 = function($window) {
|
|||
}
|
||||
}
|
||||
function removeNodeFromDOM(node) {
|
||||
var parent0 = node.parentNode
|
||||
if (parent0 != null) parent0.removeChild(node)
|
||||
var parent = node.parentNode
|
||||
if (parent != null) parent.removeChild(node)
|
||||
}
|
||||
function onremove(vnode) {
|
||||
if (vnode.attrs && vnode.attrs.onremove) vnode.attrs.onremove.call(vnode.state, vnode)
|
||||
|
|
@ -829,26 +743,26 @@ var _14 = function($window) {
|
|||
setAttr(vnode, key1, null, attrs2[key1], ns)
|
||||
}
|
||||
}
|
||||
function setAttr(vnode, key1, old, value2, ns) {
|
||||
function setAttr(vnode, key1, old, value, ns) {
|
||||
var element = vnode.dom
|
||||
if (key1 === "key" || (old === value2 && !isFormAttribute(vnode, key1)) && typeof value2 !== "object" || typeof value2 === "undefined" || isLifecycleMethod(key1)) return
|
||||
if (key1 === "key" || (old === value && !isFormAttribute(vnode, key1)) && typeof value !== "object" || typeof value === "undefined" || isLifecycleMethod(key1)) return
|
||||
var nsLastIndex = key1.indexOf(":")
|
||||
if (nsLastIndex > -1 && key1.substr(0, nsLastIndex) === "xlink") {
|
||||
element.setAttributeNS("http://www.w3.org/1999/xlink", key1.slice(nsLastIndex + 1), value2)
|
||||
element.setAttributeNS("http://www.w3.org/1999/xlink", key1.slice(nsLastIndex + 1), value)
|
||||
}
|
||||
else if (key1[0] === "o" && key1[1] === "n" && typeof value2 === "function") updateEvent(vnode, key1, value2)
|
||||
else if (key1 === "style") updateStyle(element, old, value2)
|
||||
else if (key1[0] === "o" && key1[1] === "n" && typeof value === "function") updateEvent(vnode, key1, value)
|
||||
else if (key1 === "style") updateStyle(element, old, value)
|
||||
else if (key1 in element && !isAttribute(key1) && ns === undefined) {
|
||||
//setting input[value2] to same value2 by typing on focused element moves cursor to end in Chrome
|
||||
if (vnode.tag === "input" && key1 === "value" && vnode.dom.value === value2 && vnode.dom === $doc.activeElement) return
|
||||
element[key1] = value2
|
||||
//setting input[value] to same value by typing on focused element moves cursor to end in Chrome
|
||||
if (vnode.tag === "input" && key1 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return
|
||||
element[key1] = value
|
||||
}
|
||||
else {
|
||||
if (typeof value2 === "boolean") {
|
||||
if (value2) element.setAttribute(key1, "")
|
||||
if (typeof value === "boolean") {
|
||||
if (value) element.setAttribute(key1, "")
|
||||
else element.removeAttribute(key1)
|
||||
}
|
||||
else element.setAttribute(key1 === "className" ? "class" : key1, value2)
|
||||
else element.setAttribute(key1 === "className" ? "class" : key1, value)
|
||||
}
|
||||
}
|
||||
function setLateAttrs(vnode) {
|
||||
|
|
@ -904,19 +818,19 @@ var _14 = function($window) {
|
|||
}
|
||||
}
|
||||
//event
|
||||
function updateEvent(vnode, key1, value2) {
|
||||
function updateEvent(vnode, key1, value) {
|
||||
var element = vnode.dom
|
||||
var callback = function(e) {
|
||||
var result = value2.call(element, e)
|
||||
var result = value.call(element, e)
|
||||
if (typeof onevent === "function") onevent.call(element, e)
|
||||
return result
|
||||
}
|
||||
if (key1 in element) element[key1] = typeof value2 === "function" ? callback : null
|
||||
if (key1 in element) element[key1] = typeof value === "function" ? callback : null
|
||||
else {
|
||||
var eventName = key1.slice(2)
|
||||
if (vnode.events === undefined) vnode.events = {}
|
||||
if (vnode.events[key1] != null) element.removeEventListener(eventName, vnode.events[key1], false)
|
||||
if (typeof value2 === "function") {
|
||||
if (typeof value === "function") {
|
||||
vnode.events[key1] = callback
|
||||
element.addEventListener(eventName, vnode.events[key1], false)
|
||||
}
|
||||
|
|
@ -949,14 +863,14 @@ var _14 = function($window) {
|
|||
function render(dom, vnodes) {
|
||||
if (!dom) throw new Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.")
|
||||
var hooks = []
|
||||
var active0 = $doc.activeElement
|
||||
var active = $doc.activeElement
|
||||
// First time rendering into a node clears it out
|
||||
if (dom.vnodes == null) dom.textContent = ""
|
||||
if (!(vnodes instanceof Array)) vnodes = [vnodes]
|
||||
updateNodes(dom, dom.vnodes, Vnode.normalizeChildren(vnodes), hooks, null, undefined)
|
||||
dom.vnodes = vnodes
|
||||
for (var i = 0; i < hooks.length; i++) hooks[i]()
|
||||
if ($doc.activeElement !== active0) active0.focus()
|
||||
if ($doc.activeElement !== active) active.focus()
|
||||
}
|
||||
return {render: render, setEventCallback: setEventCallback}
|
||||
}
|
||||
|
|
@ -1019,9 +933,9 @@ var parseQueryString = function(string) {
|
|||
for (var i = 0; i < entries.length; i++) {
|
||||
var entry = entries[i].split("=")
|
||||
var key3 = decodeURIComponent(entry[0])
|
||||
var value4 = entry.length === 2 ? decodeURIComponent(entry[1]) : ""
|
||||
if (value4 === "true") value4 = true
|
||||
else if (value4 === "false") value4 = false
|
||||
var value = entry.length === 2 ? decodeURIComponent(entry[1]) : ""
|
||||
if (value === "true") value = true
|
||||
else if (value === "false") value = false
|
||||
var levels = key3.split(/\]\[?|\[/)
|
||||
var cursor = data0
|
||||
if (key3.indexOf("[") > -1) levels.pop()
|
||||
|
|
@ -1035,7 +949,7 @@ var parseQueryString = function(string) {
|
|||
level = counters[key3]++
|
||||
}
|
||||
if (cursor[level] == null) {
|
||||
cursor[level] = isValue ? value4 : isNumber ? [] : {}
|
||||
cursor[level] = isValue ? value : isNumber ? [] : {}
|
||||
}
|
||||
cursor = cursor[level]
|
||||
}
|
||||
|
|
@ -1044,9 +958,9 @@ var parseQueryString = function(string) {
|
|||
}
|
||||
var coreRouter = function($window) {
|
||||
var supportsPushState = typeof $window.history.pushState === "function"
|
||||
var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout
|
||||
var callAsync0 = typeof setImmediate === "function" ? setImmediate : setTimeout
|
||||
var prefix1 = "#!"
|
||||
function setPrefix(value3) {prefix1 = value3}
|
||||
function setPrefix(value) {prefix1 = value}
|
||||
function normalize(fragment0) {
|
||||
var data = $window.location[fragment0].replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent)
|
||||
if (fragment0 === "pathname" && data[0] !== "/") data = "/" + data
|
||||
|
|
@ -1056,7 +970,7 @@ var coreRouter = function($window) {
|
|||
function debounceAsync(f) {
|
||||
return function() {
|
||||
if (asyncId != null) return
|
||||
asyncId = callAsync(function() {
|
||||
asyncId = callAsync0(function() {
|
||||
asyncId = null
|
||||
f()
|
||||
})
|
||||
|
|
@ -1106,7 +1020,7 @@ var coreRouter = function($window) {
|
|||
}
|
||||
else $window.location.href = prefix1 + path
|
||||
}
|
||||
function defineRoutes(routes, resolve1, reject0) {
|
||||
function defineRoutes(routes, resolve0, reject) {
|
||||
if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute)
|
||||
else if (prefix1.charAt(0) === "#") $window.onhashchange = resolveRoute
|
||||
resolveRoute()
|
||||
|
|
@ -1125,12 +1039,12 @@ var coreRouter = function($window) {
|
|||
for (var i = 0; i < keys.length; i++) {
|
||||
params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i])
|
||||
}
|
||||
resolve1(routes[route0], params, path, route0)
|
||||
resolve0(routes[route0], params, path, route0)
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
reject0(path, params)
|
||||
reject(path, params)
|
||||
}
|
||||
return resolveRoute
|
||||
}
|
||||
|
|
@ -1164,8 +1078,8 @@ var _24 = function($window, mount0) {
|
|||
router.defineRoutes(routes, function(payload, args0, path) {
|
||||
var isResolver = typeof payload.view !== "function"
|
||||
var render1 = defaultRender
|
||||
var resolve0 = currentResolve = function (component) {
|
||||
if (resolve0 !== currentResolve) return
|
||||
var resolve = currentResolve = function (component) {
|
||||
if (resolve !== currentResolve) return
|
||||
currentResolve = null
|
||||
currentComponent = component != null ? component : isResolver ? "div" : payload
|
||||
currentRender = render1
|
||||
|
|
@ -1174,14 +1088,14 @@ var _24 = function($window, mount0) {
|
|||
root.redraw(true)
|
||||
}
|
||||
var onmatch = function() {
|
||||
resolve0()
|
||||
resolve()
|
||||
}
|
||||
if (isResolver) {
|
||||
if (typeof payload.render === "function") render1 = payload.render.bind(payload)
|
||||
if (typeof payload.onmatch === "function") onmatch = payload.onmatch
|
||||
}
|
||||
|
||||
onmatch.call(payload, resolve0, args0, path)
|
||||
onmatch.call(payload, resolve, args0, path)
|
||||
}, function() {
|
||||
router.setPath(defaultRoute, null, {replace: true})
|
||||
})
|
||||
|
|
@ -1198,7 +1112,6 @@ m.withAttr = function(attrName, callback2, context) {
|
|||
return callback2.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))
|
||||
}
|
||||
}
|
||||
m.prop = Stream
|
||||
m.render = renderService.render
|
||||
m.redraw = redrawService.publish
|
||||
m.request = requestService.request
|
||||
|
|
|
|||
84
mithril.min.js
vendored
84
mithril.min.js
vendored
|
|
@ -1,44 +1,40 @@
|
|||
new function(){function u(c,f,n,g,k,h){return{tag:c,key:f,attrs:n,children:g,text:k,dom:h,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function v(c){if(null==c||"string"!==typeof c&&null==c.view)throw Error("The selector must be either a string or a component.");if("string"===typeof c&&void 0===K[c]){for(var f,n,g=[],k={};f=R.exec(c);){var h=f[1],x=f[2];""===h&&""!==x?n=x:"#"===h?k.id=x:"."===h?g.push(x):"["===f[3][0]&&((h=f[6])&&(h=h.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")),
|
||||
k[f[4]]=h||!0)}0<g.length&&(k.className=g.join(" "));K[c]=function(c,e){var f=!1,l,g,h=c.className||c["class"],r;for(r in k)c[r]=k[r];void 0!==h&&(void 0!==c["class"]&&(c["class"]=void 0,c.className=h),void 0!==k.className&&(c.className=k.className+" "+h));for(r in c)if("key"!==r){f=!0;break}e instanceof Array&&1==e.length&&null!=e[0]&&"#"===e[0].tag?g=e[0].children:l=e;return u(n||"div",c.key,f?c:void 0,l,g,void 0)}}var z;null!=arguments[1]&&("object"!==typeof arguments[1]||void 0!==arguments[1].tag||
|
||||
arguments[1]instanceof Array)?g=1:(z=arguments[1],g=2);if(arguments.length===g+1)f=arguments[g]instanceof Array?arguments[g]:[arguments[g]];else for(f=[];g<arguments.length;g++)f.push(arguments[g]);return"string"===typeof c?K[c](z||{},u.normalizeChildren(f)):u(c,z&&z.key,z||{},u.normalizeChildren(f),void 0,void 0)}u.normalize=function(c){return c instanceof Array?u("[",void 0,void 0,u.normalizeChildren(c),void 0,void 0):null!=c&&"object"!==typeof c?u("#",void 0,void 0,c,void 0,void 0):c};u.normalizeChildren=
|
||||
function(c){for(var f=0;f<c.length;f++)c[f]=u.normalize(c[f]);return c};var R=/(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g,K={};v.trust=function(c){null==c&&(c="");return u("<",void 0,void 0,c,void 0,void 0)};v.fragment=function(c,f){return u("[",c.key,c,u.normalizeChildren(f),void 0,void 0)};var P=function(c){function f(){function a(){0<arguments.length&&arguments[0]!==q&&g(a,arguments[0],void 0);return a._state.value}n(a);0<arguments.length&&arguments[0]!==q&&g(a,
|
||||
arguments[0],void 0);return a}function n(a){a.constructor=f;a._state={id:B++,value:void 0,error:void 0,state:0,derive:void 0,recover:void 0,deps:{},parents:[],errorStream:void 0,endStream:void 0};a["fantasy-land/map"]=u;a["fantasy-land/ap"]=v;a["fantasy-land/of"]=f;a.valueOf=H;a.toJSON=C;a.toString=H;a.run=w;a["catch"]=l;Object.defineProperties(a,{error:{get:function(){if(!a._state.errorStream){var b=function(){0<arguments.length&&arguments[0]!==q&&g(a,void 0,arguments[0]);return a._state.error};
|
||||
n(b);r(b,[a],y,y);a._state.errorStream=b}return a._state.errorStream}},end:{get:function(){if(!a._state.endStream){var b=f();b["fantasy-land/map"](function(d){!0===d&&(D(a),D(b));return d});a._state.endStream=b}return a._state.endStream}}})}function g(a,b,d){k(a,b,d);for(var m in a._state.deps)x(a._state.deps[m],!1);a._state.changed=!1;for(var e in a._state.deps)a._state.deps[e]._state.changed=!1}function k(a,b,d){d=t(b,d);if(void 0!==d&&"function"===typeof a._state.recover){if(!z(a,h,!0))return}else h(a,
|
||||
b,d);a._state.changed=!0;2!==a._state.state&&(a._state.state=1)}function h(a,b,d){a._state.value=b;a._state.error=d}function x(b,e){var m=b._state.parents;0<m.length&&m.filter(F).length===m.length&&(e||0<m.filter(a).length)&&(m=m.filter(d),0<m.length?k(b,void 0,m[0]._state.error):z(b,k,!1))}function z(a,b,d){try{var m=d?a._state.recover():a._state.derive();if(m===q)return!1;b(a,m,void 0)}catch(E){b(a,void 0,null!=E.__error?E.__error:E),null==E.__error&&e(a,E)}return!0}function t(a,b){null!=a&&a.constructor===
|
||||
f&&(b=void 0!==a._state.error?a._state.error:t(a._state.value,a._state.error));return b}function e(a,b){0===Object.keys(a._state.deps).length&&setTimeout(function(){0===Object.keys(a._state.deps).length&&c(b)},0)}function w(a){var b=f(),d=this;return r(b,[d],function(){return p(b,a(d()))},void 0)}function l(a){var b=f(),d=this;return r(b,[d],function(){return d._state.value},function(){return p(b,a(d._state.error))})}function A(b,e){if(e.length>e.filter(I).length)throw Error("Ensure that each item passed to m.prop.combine/m.prop.merge is a stream");
|
||||
return r(f(),e,function(){var m=e.filter(d);if(0<m.length)throw{__error:m[0]._state.error};return b.apply(this,e.concat([e.filter(a)]))},void 0)}function p(a,b){if(null!=b&&b.constructor===f){var d=b,e=function(){k(a,d._state.value,d._state.error);for(var b in a._state.deps)x(a._state.deps[b],!1)};d["fantasy-land/map"](e)["catch"](function(a){e();throw{__error:a};});if(0===d._state.state)return q;if(d._state.error)throw{__error:d._state.error};b=d._state.value}return b}function r(a,d,e,c){var m=a._state;
|
||||
m.derive=e;m.recover=c;m.parents=d.filter(b);G(a,m.parents);x(a,!0);return a}function G(a,b){for(var d=0;d<b.length;d++)b[d]._state.deps[a._state.id]=a,G(a,b[d]._state.parents)}function D(a){for(var b=0;b<a._state.parents.length;b++)delete a._state.parents[b]._state.deps[a._state.id];for(var d in a._state.deps){var b=a._state.deps[d],e=b._state.parents.indexOf(a);-1<e&&b._state.parents.splice(e,1)}a._state.state=2;a._state.deps={}}function u(a){return A(function(b){return a(b())},[this])}function v(a){return A(function(a,
|
||||
b){return a()(b())},[a,this])}function H(){return this._state.value}function C(){return null!=this._state.value&&"function"===typeof this._state.value.toJSON?this._state.value.toJSON():this._state.value}function I(a){return a._state}function F(a){return 1===a._state.state}function a(a){return a._state.changed}function b(a){return 2!==a._state.state}function d(a){return a._state.error}var B=0,y=function(){},q={};f["fantasy-land/of"]=f;f.merge=function(a){return A(function(){return a.map(function(a){return a()})},
|
||||
a)};f.combine=A;f.reject=function(a){var b=f();b.error(a);return b};f.HALT=q;return f}(console.log.bind(console)),J=function(c){function f(c,h){if(h instanceof Array)for(var g=0;g<h.length;g++)f(c+"["+g+"]",h[g]);else if("[object Object]"===Object.prototype.toString.call(h))for(g in h)f(c+"["+g+"]",h[g]);else n.push(encodeURIComponent(c)+(null!=h&&""!==h?"="+encodeURIComponent(h):""))}if("[object Object]"!==Object.prototype.toString.call(c))return"";var n=[],g;for(g in c)f(g,c[g]);return n.join("&")},
|
||||
L=function(c,f){function n(e,c){if(null==c)return e;for(var f=e.match(/:[^\/]+/gi)||[],g=0;g<f.length;g++){var w=f[g].slice(1);null!=c[w]&&(e=e.replace(f[g],c[w]),delete c[w])}return e}function g(e,c){var f=J(c);if(""!==f){var g=0>e.indexOf("?")?"?":"&";e+=g+f}return e}function k(e){try{return""!==e?JSON.parse(e):null}catch(w){throw Error(e);}}function h(e){return e.responseText}function x(e,c){if("function"===typeof e)if(c instanceof Array)for(var f=0;f<c.length;f++)c[f]=new e(c[f]);else return new e(c);
|
||||
return c}var z=0,t;return{request:function(e,w){if("string"===typeof e){var l=e;e="object"===typeof w?w:{};"undefined"===typeof e.url&&(e.url=l)}"undefined"===typeof e.method&&(e.method="GET");var A=f();void 0!==e.initialValue&&A(e.initialValue);e.method=e.method.toUpperCase();l="boolean"===typeof e.useBody?e.useBody:"GET"!==e.method&&"TRACE"!==e.method;"function"!==typeof e.serialize&&(e.serialize="undefined"!==typeof FormData&&e.data instanceof FormData?function(e){return e}:JSON.stringify);"function"!==
|
||||
typeof e.deserialize&&(e.deserialize=k);"function"!==typeof e.extract&&(e.extract=h);e.url=n(e.url,e.data);l?e.data=e.serialize(e.data):e.url=g(e.url,e.data);var p=new c.XMLHttpRequest;p.open(e.method,e.url,"boolean"===typeof e.async?e.async:!0,"string"===typeof e.user?e.user:void 0,"string"===typeof e.password?e.password:void 0);e.serialize===JSON.stringify&&l&&p.setRequestHeader("Content-Type","application/json; charset=utf-8");e.deserialize===k&&p.setRequestHeader("Accept","application/json, text/*");
|
||||
"function"===typeof e.config&&(p=e.config(p,e)||p);p.onreadystatechange=function(){if(4===p.readyState){try{var c=e.extract!==h?e.extract(p,e):e.deserialize(e.extract(p,e));if(200<=p.status&&300>p.status||304===p.status)A(x(e.type,c));else{var f=Error(p.responseText),g;for(g in c)f[g]=c[g];A.error(f)}}catch(S){A.error(S)}"function"===typeof t&&t()}};l&&null!=e.data?p.send(e.data):p.send();return A},jsonp:function(e){var h=f();void 0!==e.initialValue&&h(e.initialValue);var l=e.callbackName||"_mithril_"+
|
||||
Math.round(1E16*Math.random())+"_"+z++,k=c.document.createElement("script");c[l]=function(f){k.parentNode.removeChild(k);h(x(e.type,f));"function"===typeof t&&t();delete c[l]};k.onerror=function(){k.parentNode.removeChild(k);h.error(Error("JSONP request failed"));"function"===typeof t&&t();delete c[l]};null==e.data&&(e.data={});e.url=n(e.url,e.data);e.data[e.callbackKey||"callback"]=l;k.src=g(e.url,e.data);c.document.documentElement.appendChild(k);return h},setCompletionCallback:function(e){t=e}}}(window,
|
||||
P),M=function(){var c=[];return{subscribe:c.push.bind(c),unsubscribe:function(f){f=c.indexOf(f);-1<f&&c.splice(f,1)},publish:function(){for(var f=0;f<c.length;f++)c[f].apply(this,arguments)}}}();L.setCompletionCallback(M.publish);var Q=function(c){function f(a,b,d,e,c,f,g){for(;d<e;d++){var B=b[d];null!=B&&t(a,n(B,c,g),f)}}function n(a,b,d){var c=a.tag;null!=a.attrs&&v(a.attrs,a,b);if("string"===typeof c)switch(c){case "#":return a.dom=C.createTextNode(a.children);case "<":return g(a);case "[":var h=
|
||||
C.createDocumentFragment();null!=a.children&&(c=a.children,f(h,c,0,c.length,b,null,d));a.dom=h.firstChild;a.domSize=h.childNodes.length;return h;default:var q=a.tag;switch(a.tag){case "svg":d="http://www.w3.org/2000/svg";break;case "math":d="http://www.w3.org/1998/Math/MathML"}var m=(c=a.attrs)&&c.is,q=d?m?C.createElementNS(d,q,{is:m}):C.createElementNS(d,q):m?C.createElement(q,{is:m}):C.createElement(q);a.dom=q;if(null!=c)for(h in m=d,c)r(a,h,null,c[h],m);null!=a.attrs&&null!=a.attrs.contenteditable?
|
||||
e(a):(null!=a.text&&(""!==a.text?q.textContent=a.text:a.children=[u("#",void 0,void 0,a.text,void 0,void 0)]),null!=a.children&&(h=a.children,f(q,h,0,h.length,b,null,d),b=a.attrs,"select"===a.tag&&null!=b&&("value"in b&&r(a,"value",null,b.value,void 0),"selectedIndex"in b&&r(a,"selectedIndex",null,b.selectedIndex,void 0))));return q}else{a.state||(a.state={});H(a.state,a.tag);h=a.tag.view;if(null!=h.reentrantLock)a=I;else if(h.reentrantLock=!0,v(a.tag,a,b),a.instance=u.normalize(h.call(a.state,a)),
|
||||
h.reentrantLock=null,null!=a.instance){if(a.instance===a)throw Error("A view cannot return the vnode it received as arguments");b=n(a.instance,b,d);a.dom=a.instance.dom;a.domSize=null!=a.dom?a.instance.domSize:0;a=b}else a.domSize=0,a=I;return a}}function g(a){var b={caption:"table",thead:"table",tbody:"table",tfoot:"table",tr:"tbody",th:"tr",td:"tr",colgroup:"table",col:"colgroup"}[(a.children.match(/^\s*?<(\w+)/im)||[])[1]]||"div",b=C.createElement(b);b.innerHTML=a.children;a.dom=b.firstChild;a.domSize=
|
||||
b.childNodes.length;a=C.createDocumentFragment();for(var d;d=b.firstChild;)a.appendChild(d);return a}function k(a,b,d,c,e,g){if(b!==d&&(null!=b||null!=d))if(null==b)f(a,d,0,d.length,c,e,void 0);else if(null==d)w(b,0,b.length,d);else{var m;a:{if(null!=b.pool&&Math.abs(b.pool.length-d.length)<=Math.abs(b.length-d.length)&&(m=d[0]&&d[0].children&&d[0].children.length||0,Math.abs((b.pool[0]&&b.pool[0].children&&b.pool[0].children.length||0)-m)<=Math.abs((b[0]&&b[0].children&&b[0].children.length||0)-
|
||||
m))){m=!0;break a}m=!1}m&&(b=b.concat(b.pool));if(b.length===d.length&&null!=d[0]&&null==d[0].key)for(var q=0;q<b.length;q++)b[q]===d[q]||null==b[q]&&null==d[q]||(null==b[q]?t(a,n(d[q],c,g),z(b,q+1,e)):null==d[q]?w(b,q,q+1,d):h(a,b[q],d[q],c,z(b,q+1,e),m,g),m&&b[q].tag===d[q].tag&&t(a,x(b[q]),z(b,q+1,e)));else{for(var B=q=0,l=b.length-1,k=d.length-1,p;l>=q&&k>=B;){var r=b[q],y=d[B];if(r!==y||m)if(null!=r&&null!=y&&r.key===y.key)q++,B++,h(a,r,y,c,z(b,q,e),m,g),m&&r.tag===y.tag&&t(a,x(r),e);else if(r=
|
||||
b[l],r!==y||m)if(null!=r&&null!=y&&r.key===y.key)h(a,r,y,c,z(b,l+1,e),m,g),(m||B<k)&&t(a,x(r),z(b,q,e)),l--,B++;else break;else l--,B++;else q++,B++}for(;l>=q&&k>=B;){r=b[l];y=d[k];if(r!==y||m)if(null!=r&&null!=y&&r.key===y.key)h(a,r,y,c,z(b,l+1,e),m,g),m&&r.tag===y.tag&&t(a,x(r),e),null!=r.dom&&(e=r.dom),l--;else{if(!p){p=b;var r=l,u={},A;for(A=0;A<r;A++){var v=p[A];null!=v&&(v=v.key,null!=v&&(u[v]=A))}p=u}null!=y&&(r=p[y.key],null!=r?(u=b[r],h(a,u,y,c,z(b,l+1,e),m,g),t(a,x(u),e),b[r].skip=!0,null!=
|
||||
u.dom&&(e=u.dom)):(y=n(y,c,void 0),t(a,y,e),e=y))}else l--;k--;if(k<B)break}f(a,d,B,k+1,c,e,g);w(b,q,l+1,d)}}}function h(a,b,d,c,f,q,m){var l=b.tag;if(l===d.tag){d.state=b.state;d.events=b.events;var w;var B;null!=d.attrs&&"function"===typeof d.attrs.onbeforeupdate&&(w=d.attrs.onbeforeupdate.call(d.state,d,b));"string"!==typeof d.tag&&"function"===typeof d.tag.onbeforeupdate&&(B=d.tag.onbeforeupdate.call(d.state,d,b));void 0===w&&void 0===B||w||B?w=!1:(d.dom=b.dom,d.domSize=b.domSize,d.instance=b.instance,
|
||||
w=!0);if(!w)if(null!=d.attrs&&O(d.attrs,d,c,q),"string"===typeof l)switch(l){case "#":b.children.toString()!==d.children.toString()&&(b.dom.nodeValue=d.children);d.dom=b.dom;break;case "<":b.children!==d.children?(x(b),t(a,g(d),f)):(d.dom=b.dom,d.domSize=b.domSize);break;case "[":k(a,b.children,d.children,c,f,m);b=0;c=d.children;d.dom=null;if(null!=c){for(var p=0;p<c.length;p++)a=c[p],null!=a&&null!=a.dom&&(null==d.dom&&(d.dom=a.dom),b+=a.domSize||1);1!==b&&(d.domSize=b)}break;default:a=m;f=d.dom=
|
||||
b.dom;switch(d.tag){case "svg":a="http://www.w3.org/2000/svg";break;case "math":a="http://www.w3.org/1998/Math/MathML"}"textarea"===d.tag&&(null==d.attrs&&(d.attrs={}),null!=d.text&&(d.attrs.value=d.text,d.text=void 0));q=b.attrs;m=d.attrs;l=a;if(null!=m)for(p in m)r(d,p,q&&q[p],m[p],l);if(null!=q)for(p in q)null!=m&&p in m||("className"===p&&(p="class"),"o"!==p[0]||"n"!==p[1]||G(p)?"key"!==p&&d.dom.removeAttribute(p):D(d,p,void 0));null!=d.attrs&&null!=d.attrs.contenteditable?e(d):null!=b.text&&
|
||||
null!=d.text&&""!==d.text?b.text.toString()!==d.text.toString()&&(b.dom.firstChild.nodeValue=d.text):(null!=b.text&&(b.children=[u("#",void 0,void 0,b.text,void 0,b.dom.firstChild)]),null!=d.text&&(d.children=[u("#",void 0,void 0,d.text,void 0,void 0)]),k(f,b.children,d.children,c,null,a))}else d.instance=u.normalize(d.tag.view.call(d.state,d)),O(d.tag,d,c,q),null!=d.instance?(null==b.instance?t(a,n(d.instance,c,m),f):h(a,b.instance,d.instance,c,f,q,m),d.dom=d.instance.dom,d.domSize=d.instance.domSize):
|
||||
null!=b.instance?(A(b.instance,null),d.dom=void 0,d.domSize=0):(d.dom=b.dom,d.domSize=b.domSize)}else A(b,null),t(a,n(d,c,void 0),f)}function x(a){var b=a.domSize;if(null!=b||null==a.dom){var d=C.createDocumentFragment();if(0<b){for(a=a.dom;--b;)d.appendChild(a.nextSibling);d.insertBefore(a,d.firstChild)}return d}return a.dom}function z(a,b,d){for(;b<a.length;b++)if(null!=a[b]&&null!=a[b].dom)return a[b].dom;return d}function t(a,b,d){d&&d.parentNode?a.insertBefore(b,d):a.appendChild(b)}function e(a){var b=
|
||||
a.children;if(null!=b&&1===b.length&&"<"===b[0].tag)b=b[0].children,a.dom.innerHTML!==b&&(a.dom.innerHTML=b);else if(null!=b||null!=a.text)throw Error("Child node of a contenteditable must be trusted");}function w(a,b,d,c){for(;b<d;b++){var e=a[b];null!=e&&(e.skip?e.skip=!1:A(e,c))}}function l(a){var b=!1;return function(){b||(b=!0,a())}}function A(a,b){function d(){if(++e===c&&(p(a),a.dom)){var d=a.domSize||1;if(1<d)for(var f=a.dom;--d;){var g=f.nextSibling,h=g.parentNode;null!=h&&h.removeChild(g)}d=
|
||||
a.dom;f=d.parentNode;null!=f&&f.removeChild(d);if(d=null!=b&&null==a.domSize)d=a.attrs,d=!(null!=d&&(d.oncreate||d.onupdate||d.onbeforeremove||d.onremove));d&&"string"===typeof a.tag&&(b.pool?b.pool.push(a):b.pool=[a])}}var c=1,e=0;a.attrs&&a.attrs.onbeforeremove&&(c++,a.attrs.onbeforeremove.call(a.state,a,l(d)));"string"!==typeof a.tag&&a.tag.onbeforeremove&&(c++,a.tag.onbeforeremove.call(a.state,a,l(d)));d()}function p(a){a.attrs&&a.attrs.onremove&&a.attrs.onremove.call(a.state,a);"string"!==typeof a.tag&&
|
||||
a.tag.onremove&&a.tag.onremove.call(a.state,a);if(null!=a.instance)p(a.instance);else if(a=a.children,a instanceof Array)for(var b=0;b<a.length;b++){var d=a[b];null!=d&&p(d)}}function r(a,b,d,c,e){var f=a.dom;if("key"!==b&&(d!==c||"value"===b||"checked"===b||"selectedIndex"===b||"selected"===b&&a.dom===C.activeElement||"object"===typeof c)&&"undefined"!==typeof c&&!G(b)){var g=b.indexOf(":");if(-1<g&&"xlink"===b.substr(0,g))f.setAttributeNS("http://www.w3.org/1999/xlink",b.slice(g+1),c);else if("o"===
|
||||
b[0]&&"n"===b[1]&&"function"===typeof c)D(a,b,c);else if("style"===b)if(a=d,a===c&&(f.style.cssText="",a=null),null==c)f.style.cssText="";else if("string"===typeof c)f.style.cssText=c;else{"string"===typeof a&&(f.style.cssText="");for(var h in c)f.style[h]=c[h];if(null!=a&&"string"!==typeof a)for(h in a)h in c||(f.style[h]="")}else if(b in f&&"href"!==b&&"list"!==b&&"form"!==b&&"width"!==b&&"height"!==b&&void 0===e){if("input"!==a.tag||"value"!==b||a.dom.value!==c||a.dom!==C.activeElement)f[b]=c}else"boolean"===
|
||||
typeof c?c?f.setAttribute(b,""):f.removeAttribute(b):f.setAttribute("className"===b?"class":b,c)}}function G(a){return"oninit"===a||"oncreate"===a||"onupdate"===a||"onremove"===a||"onbeforeremove"===a||"onbeforeupdate"===a}function D(a,b,d){var c=a.dom,e=function(a){var b=d.call(c,a);"function"===typeof F&&F.call(c,a);return b};if(b in c)c[b]="function"===typeof d?e:null;else{var f=b.slice(2);void 0===a.events&&(a.events={});null!=a.events[b]&&c.removeEventListener(f,a.events[b],!1);"function"===
|
||||
typeof d&&(a.events[b]=e,c.addEventListener(f,a.events[b],!1))}}function v(a,b,d){"function"===typeof a.oninit&&a.oninit.call(b.state,b);"function"===typeof a.oncreate&&d.push(a.oncreate.bind(b.state,b))}function O(a,b,d,c){c?v(a,b,d):"function"===typeof a.onupdate&&d.push(a.onupdate.bind(b.state,b))}function H(a,b){Object.keys(b).forEach(function(d){a[d]=b[d]})}var C=c.document,I=C.createDocumentFragment(),F;return{render:function(a,b){if(!a)throw Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.");
|
||||
var d=[],c=C.activeElement;null==a.vnodes&&(a.textContent="");b instanceof Array||(b=[b]);k(a,a.vnodes,u.normalizeChildren(b),d,null,void 0);a.vnodes=b;for(var e=0;e<d.length;e++)d[e]();C.activeElement!==c&&c.focus()},setEventCallback:function(a){return F=a}}}(window),T=function(c){var f=0,n=null,g="function"===typeof requestAnimationFrame?requestAnimationFrame:setTimeout;return function(k){var h=Date.now();!0===k||0===f||16<=h-f?(f=h,c()):null===n&&(n=g(function(){n=null;c();f=Date.now()},16-(h-
|
||||
f)))}},U=function(c,f,n,g){g=T(g);null!=f&&f.setEventCallback(function(c){!1!==c.redraw&&n.publish()});null!=n&&(c.redraw&&n.unsubscribe(c.redraw),n.subscribe(g));return c.redraw=g};v.mount=function(c,f){return function(n,g){if(null===g)c.render(n,[]),f.unsubscribe(n.redraw),delete n.redraw;else{if(null==g.view)throw Error("m.mount(element, component) expects a component, not a vnode");U(n,c,f,function(){c.render(n,u(g,void 0,void 0,void 0,void 0,void 0))})()}}}(Q,M);var N=function(c){if(""===c||
|
||||
null==c)return{};"?"===c.charAt(0)&&(c=c.slice(1));c=c.split("&");for(var f={},n={},g=0;g<c.length;g++){var k=c[g].split("="),h=decodeURIComponent(k[0]),k=2===k.length?decodeURIComponent(k[1]):"";"true"===k?k=!0:"false"===k&&(k=!1);var x=h.split(/\]\[?|\[/),u=f;-1<h.indexOf("[")&&x.pop();for(var t=0;t<x.length;t++){var h=x[t],e=x[t+1],e=""==e||!isNaN(parseInt(e,10)),w=t===x.length-1;""===h&&(h=x.slice(0,t).join(),null==n[h]&&(n[h]=0),h=n[h]++);null==u[h]&&(u[h]=w?k:e?[]:{});u=u[h]}}return f},V=function(c){function f(e){var f=
|
||||
c.location[e].replace(/(?:%[a-f89][a-f0-9])+/gim,decodeURIComponent);"pathname"===e&&"/"!==f[0]&&(f="/"+f);return f}function n(c){return function(){null==e&&(e=v(function(){e=null;c()}))}}function g(c,e,f){var g=c.indexOf("?"),h=c.indexOf("#"),l=-1<g?g:-1<h?h:c.length;if(-1<g){var g=N(c.slice(g+1,-1<h?h:c.length)),k;for(k in g)e[k]=g[k]}if(-1<h)for(k in e=N(c.slice(h+1)),e)f[k]=e[k];return c.slice(0,l)}function k(){switch(t.charAt(0)){case "#":return f("hash").slice(t.length);case "?":return f("search").slice(t.length)+
|
||||
f("hash");default:return f("pathname").slice(t.length)+f("search")+f("hash")}}function h(e,f,h){var k={},r={};e=g(e,k,r);if(null!=f){for(var l in f)k[l]=f[l];e=e.replace(/:([^\/]+)/g,function(c,e){delete k[e];return f[e]})}(l=J(k))&&(e+="?"+l);(r=J(r))&&(e+="#"+r);u?(h&&h.replace?c.history.replaceState(null,null,t+e):c.history.pushState(null,null,t+e),c.onpopstate()):c.location.href=t+e}var u="function"===typeof c.history.pushState,v="function"===typeof setImmediate?setImmediate:setTimeout,t="#!",
|
||||
e;return{setPrefix:function(c){t=c},getPath:k,setPath:h,defineRoutes:function(e,f,h){function l(){var c=k(),l={},p=g(c,l,l),n;for(n in e){var t=new RegExp("^"+n.replace(/:[^\/]+?\.{3}/g,"(.*?)").replace(/:[^\/]+/g,"([^\\/]+)")+"/?$");if(t.test(p)){p.replace(t,function(){for(var g=n.match(/:[^\/]+/g)||[],h=[].slice.call(arguments,1,-2),k=0;k<g.length;k++)l[g[k].replace(/:|\./g,"")]=decodeURIComponent(h[k]);f(e[n],l,c,n)});return}}h(c,l)}u?c.onpopstate=n(l):"#"===t.charAt(0)&&(c.onhashchange=l);l();
|
||||
return l},link:function(c){c.dom.setAttribute("href",t+c.attrs.href);c.dom.onclick=function(c){c.ctrlKey||c.metaKey||c.shiftKey||2===c.which||(c.preventDefault(),c.redraw=!1,c=this.getAttribute("href"),0===c.indexOf(t)&&(c=c.slice(t.length)),h(c,void 0,void 0))}}}};v.route=function(c,f){function n(c){return c}var g=V(c),k,h,v,z,t,e={view:function(){return[v(u(h,null,z,void 0,void 0,void 0))]}},w=function(c,u,p){h="div";v=n;z=null;f(c,e);g.defineRoutes(p,function(e,f,g){var l="function"!==typeof e.view,
|
||||
p=n,r=k=function(n){r===k&&(k=null,h=null!=n?n:l?"div":e,v=p,z=f,t=g,c.redraw(!0))},u=function(){r()};l&&("function"===typeof e.render&&(p=e.render.bind(e)),"function"===typeof e.onmatch&&(u=e.onmatch));u.call(e,r,f,g)},function(){g.setPath(u,null,{replace:!0})})};w.link=g.link;w.prefix=g.setPrefix;w.set=g.setPath;w.get=function(){return t};return w}(window,v.mount);v.withAttr=function(c,f,n){return function(g){return f.call(n||this,c in g.currentTarget?g.currentTarget[c]:g.currentTarget.getAttribute(c))}};
|
||||
v.prop=P;v.render=Q.render;v.redraw=M.publish;v.request=L.request;v.jsonp=L.jsonp;v.parseQueryString=N;v.buildQueryString=J;v.version="1.0.0-rc.3";"undefined"!==typeof module?module.exports=v:window.m=v};
|
||||
new function(){function m(a,b,k,d,l,h){return{tag:a,key:b,attrs:k,children:d,text:l,dom:h,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function t(a){if(null==a||"string"!==typeof a&&null==a.view)throw Error("The selector must be either a string or a component.");if("string"===typeof a&&void 0===H[a]){for(var b,k,d=[],l={};b=N.exec(a);){var h=b[1],u=b[2];""===h&&""!==u?k=u:"#"===h?l.id=u:"."===h?d.push(u):"["===b[3][0]&&((h=b[6])&&(h=h.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")),
|
||||
l[b[4]]=h||!0)}0<d.length&&(l.className=d.join(" "));H[a]=function(a,b){var d=!1,h,f,w=a.className||a["class"],p;for(p in l)a[p]=l[p];void 0!==w&&(void 0!==a["class"]&&(a["class"]=void 0,a.className=w),void 0!==l.className&&(a.className=l.className+" "+w));for(p in a)if("key"!==p){d=!0;break}b instanceof Array&&1==b.length&&null!=b[0]&&"#"===b[0].tag?f=b[0].children:h=b;return m(k||"div",a.key,d?a:void 0,h,f,void 0)}}var q;null!=arguments[1]&&("object"!==typeof arguments[1]||void 0!==arguments[1].tag||
|
||||
arguments[1]instanceof Array)?d=1:(q=arguments[1],d=2);if(arguments.length===d+1)b=arguments[d]instanceof Array?arguments[d]:[arguments[d]];else for(b=[];d<arguments.length;d++)b.push(arguments[d]);return"string"===typeof a?H[a](q||{},m.normalizeChildren(b)):m(a,q&&q.key,q||{},m.normalizeChildren(b),void 0,void 0)}m.normalize=function(a){return a instanceof Array?m("[",void 0,void 0,m.normalizeChildren(a),void 0,void 0):null!=a&&"object"!==typeof a?m("#",void 0,void 0,a,void 0,void 0):a};m.normalizeChildren=
|
||||
function(a){for(var b=0;b<a.length;b++)a[b]=m.normalize(a[b]);return a};var N=/(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g,H={};t.trust=function(a){null==a&&(a="");return m("<",void 0,void 0,a,void 0,void 0)};t.fragment=function(a,b){return m("[",a.key,a,m.normalizeChildren(b),void 0,void 0)};var z=function(a){function b(a,b){return function w(p){var n;try{if(!b||null==p||"object"!==typeof p&&"function"!==typeof p||"function"!==typeof(n=p.then))m(function(){b||0!==
|
||||
a.length||console.error("Possible unhandled promise rejection:",p);for(var d=0;d<a.length;d++)a[d](p);l.length=0;h.length=0;r.state=b;r.retry=function(){w(p)}});else{if(p===d)throw new TypeError("Promise can't be resolved w/ itself");k(n.bind(p))}}catch(E){q(E)}}}function k(a){function b(a){return function(b){0<f++||a(b)}}var f=0,d=b(q);try{a(b(u),d)}catch(p){d(p)}}if(!(this instanceof z))throw Error("Promise must be called with `new`");if("function"!==typeof a)throw new TypeError("executor must be a function");
|
||||
var d=this,l=[],h=[],u=b(l,!0),q=b(h,!1),r=d._instance={resolvers:l,rejectors:h},m="function"===typeof setImmediate?setImmediate:setTimeout;k(a)};z.prototype.then=function(a,b){function k(a,b,k,n){b.push(function(b){if("function"!==typeof a)k(b);else try{l(a(b))}catch(f){h&&h(f)}});"function"===typeof d.retry&&n===d.state&&d.retry()}var d=this._instance,l,h,u=new z(function(a,b){l=a;h=b});k(a,d.resolvers,l,!0);k(b,d.rejectors,h,!1);return u};z.prototype["catch"]=function(a){return this.then(null,
|
||||
a)};z.resolve=function(a){return a instanceof z?a:new z(function(b){b(a)})};z.reject=function(a){return new z(function(b,k){k(a)})};z.all=function(a){return new z(function(b,k){var d=a.length,l=0,h=[];if(0===a.length)b([]);else for(var u=0;u<a.length;u++)(function(u){function r(a){l++;h[u]=a;l===d&&b(h)}null==a[u]||"object"!==typeof a[u]&&"function"!==typeof a[u]||"function"!==typeof a[u].then?r(a[u]):a[u].then(r,k)})(u)})};z.race=function(a){return new z(function(b,k){for(var d=0;d<a.length;d++)a[d].then(b,
|
||||
k)})};var C="undefined"!==typeof Promise?Promise:z,F=function(a){function b(a,d){if(d instanceof Array)for(var h=0;h<d.length;h++)b(a+"["+h+"]",d[h]);else if("[object Object]"===Object.prototype.toString.call(d))for(h in d)b(a+"["+h+"]",d[h]);else k.push(encodeURIComponent(a)+(null!=d&&""!==d?"="+encodeURIComponent(d):""))}if("[object Object]"!==Object.prototype.toString.call(a))return"";var k=[],d;for(d in a)b(d,a[d]);return k.join("&")},J=function(a,b){function k(){0===--n&&"function"===typeof v&&
|
||||
v()}function d(a){var f=a.then;a.then=function(){n++;var b=f.apply(a,arguments);b.then(k,function(a){k();throw a;});return d(b)};return a}function l(a,b){if(null==b)return a;for(var f=a.match(/:[^\/]+/gi)||[],d=0;d<f.length;d++){var h=f[d].slice(1);null!=b[h]&&(a=a.replace(f[d],b[h]),delete b[h])}return a}function h(a,b){var f=F(b);if(""!==f){var d=0>a.indexOf("?")?"?":"&";a+=d+f}return a}function u(a){try{return""!==a?JSON.parse(a):null}catch(w){throw Error(a);}}function q(a){return a.responseText}
|
||||
function r(a,b){if("function"===typeof a)if(b instanceof Array)for(var d=0;d<b.length;d++)b[d]=new a(b[d]);else return new a(b);return b}var m=0,n=0,v;return{request:function(f,k){return d(new b(function(b,d){if("string"===typeof f){var n=f;f="object"===typeof k?k:{};"undefined"===typeof f.url&&(f.url=n)}"undefined"===typeof f.method&&(f.method="GET");f.method=f.method.toUpperCase();n="boolean"===typeof f.useBody?f.useBody:"GET"!==f.method&&"TRACE"!==f.method;"function"!==typeof f.serialize&&(f.serialize=
|
||||
"undefined"!==typeof FormData&&f.data instanceof FormData?function(a){return a}:JSON.stringify);"function"!==typeof f.deserialize&&(f.deserialize=u);"function"!==typeof f.extract&&(f.extract=q);f.url=l(f.url,f.data);n?f.data=f.serialize(f.data):f.url=h(f.url,f.data);var p=new a.XMLHttpRequest;p.open(f.method,f.url,"boolean"===typeof f.async?f.async:!0,"string"===typeof f.user?f.user:void 0,"string"===typeof f.password?f.password:void 0);f.serialize===JSON.stringify&&n&&p.setRequestHeader("Content-Type",
|
||||
"application/json; charset=utf-8");f.deserialize===u&&p.setRequestHeader("Accept","application/json, text/*");"function"===typeof f.config&&(p=f.config(p,f)||p);p.onreadystatechange=function(){if(4===p.readyState)try{var a=f.extract!==q?f.extract(p,f):f.deserialize(f.extract(p,f));if(200<=p.status&&300>p.status||304===p.status)b(r(f.type,a));else{var h=Error(p.responseText),k;for(k in a)h[k]=a[k];d(h)}}catch(G){d(G)}};n&&null!=f.data?p.send(f.data):p.send()}))},jsonp:function(f){return d(new b(function(b,
|
||||
d){var k=f.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+m++,p=a.document.createElement("script");a[k]=function(d){p.parentNode.removeChild(p);b(r(f.type,d));delete a[k]};p.onerror=function(){p.parentNode.removeChild(p);d(Error("JSONP request failed"));delete a[k]};null==f.data&&(f.data={});f.url=l(f.url,f.data);f.data[f.callbackKey||"callback"]=k;p.src=h(f.url,f.data);a.document.documentElement.appendChild(p)}))},setCompletionCallback:function(a){v=a}}}(window,C),K=function(){var a=
|
||||
[];return{subscribe:a.push.bind(a),unsubscribe:function(b){b=a.indexOf(b);-1<b&&a.splice(b,1)},publish:function(){for(var b=0;b<a.length;b++)a[b].apply(this,arguments)}}}();J.setCompletionCallback(K.publish);t.Promise=C;var C=function(a){function b(c,g,a,b,d,f,h){for(;a<b;a++){var e=g[a];null!=e&&r(c,k(e,d,h),f)}}function k(c,g,a){var e=c.tag;null!=c.attrs&&I(c.attrs,c,g);if("string"===typeof e)switch(e){case "#":return c.dom=y.createTextNode(c.children);case "<":return d(c);case "[":var f=y.createDocumentFragment();
|
||||
null!=c.children&&(e=c.children,b(f,e,0,e.length,g,null,a));c.dom=f.firstChild;c.domSize=f.childNodes.length;return f;default:var h=c.tag;switch(c.tag){case "svg":a="http://www.w3.org/2000/svg";break;case "math":a="http://www.w3.org/1998/Math/MathML"}var n=(e=c.attrs)&&e.is,h=a?n?y.createElementNS(a,h,{is:n}):y.createElementNS(a,h):n?y.createElement(h,{is:n}):y.createElement(h);c.dom=h;if(null!=e)for(f in n=a,e)p(c,f,null,e[f],n);null!=c.attrs&&null!=c.attrs.contenteditable?t(c):(null!=c.text&&(""!==
|
||||
c.text?h.textContent=c.text:c.children=[m("#",void 0,void 0,c.text,void 0,void 0)]),null!=c.children&&(f=c.children,b(h,f,0,f.length,g,null,a),g=c.attrs,"select"===c.tag&&null!=g&&("value"in g&&p(c,"value",null,g.value,void 0),"selectedIndex"in g&&p(c,"selectedIndex",null,g.selectedIndex,void 0))));return h}else{c.state||(c.state={});O(c.state,c.tag);f=c.tag.view;if(null!=f.reentrantLock)c=G;else if(f.reentrantLock=!0,I(c.tag,c,g),c.instance=m.normalize(f.call(c.state,c)),f.reentrantLock=null,null!=
|
||||
c.instance){if(c.instance===c)throw Error("A view cannot return the vnode it received as arguments");g=k(c.instance,g,a);c.dom=c.instance.dom;c.domSize=null!=c.dom?c.instance.domSize:0;c=g}else c.domSize=0,c=G;return c}}function d(c){var g={caption:"table",thead:"table",tbody:"table",tfoot:"table",tr:"tbody",th:"tr",td:"tr",colgroup:"table",col:"colgroup"}[(c.children.match(/^\s*?<(\w+)/im)||[])[1]]||"div",g=y.createElement(g);g.innerHTML=c.children;c.dom=g.firstChild;c.domSize=g.childNodes.length;
|
||||
c=y.createDocumentFragment();for(var a;a=g.firstChild;)c.appendChild(a);return c}function l(c,a,e,f,d,p){if(a!==e&&(null!=a||null!=e))if(null==a)b(c,e,0,e.length,f,d,void 0);else if(null==e)n(a,0,a.length,e);else{var g;a:{if(null!=a.pool&&Math.abs(a.pool.length-e.length)<=Math.abs(a.length-e.length)&&(g=e[0]&&e[0].children&&e[0].children.length||0,Math.abs((a.pool[0]&&a.pool[0].children&&a.pool[0].children.length||0)-g)<=Math.abs((a[0]&&a[0].children&&a[0].children.length||0)-g))){g=!0;break a}g=
|
||||
!1}g&&(a=a.concat(a.pool));if(a.length===e.length&&null!=e[0]&&null==e[0].key)for(var l=0;l<a.length;l++)a[l]===e[l]||null==a[l]&&null==e[l]||(null==a[l]?r(c,k(e[l],f,p),q(a,l+1,d)):null==e[l]?n(a,l,l+1,e):h(c,a[l],e[l],f,q(a,l+1,d),g,p),g&&a[l].tag===e[l].tag&&r(c,u(a[l]),q(a,l+1,d)));else{for(var A=l=0,v=a.length-1,B=e.length-1,w;v>=l&&B>=A;){var x=a[l],m=e[A];if(x!==m||g)if(null!=x&&null!=m&&x.key===m.key)l++,A++,h(c,x,m,f,q(a,l,d),g,p),g&&x.tag===m.tag&&r(c,u(x),d);else if(x=a[v],x!==m||g)if(null!=
|
||||
x&&null!=m&&x.key===m.key)h(c,x,m,f,q(a,v+1,d),g,p),(g||A<B)&&r(c,u(x),q(a,l,d)),v--,A++;else break;else v--,A++;else l++,A++}for(;v>=l&&B>=A;){x=a[v];m=e[B];if(x!==m||g)if(null!=x&&null!=m&&x.key===m.key)h(c,x,m,f,q(a,v+1,d),g,p),g&&x.tag===m.tag&&r(c,u(x),d),null!=x.dom&&(d=x.dom),v--;else{if(!w){w=a;var x=v,D={},t;for(t=0;t<x;t++){var y=w[t];null!=y&&(y=y.key,null!=y&&(D[y]=t))}w=D}null!=m&&(x=w[m.key],null!=x?(D=a[x],h(c,D,m,f,q(a,v+1,d),g,p),r(c,u(D),d),a[x].skip=!0,null!=D.dom&&(d=D.dom)):(m=
|
||||
k(m,f,void 0),r(c,m,d),d=m))}else v--;B--;if(B<A)break}b(c,e,A,B+1,f,d,p);n(a,l,v+1,e)}}}function h(a,g,e,b,n,v,w){var c=g.tag;if(c===e.tag){e.state=g.state;e.events=g.events;var A;var B;null!=e.attrs&&"function"===typeof e.attrs.onbeforeupdate&&(A=e.attrs.onbeforeupdate.call(e.state,e,g));"string"!==typeof e.tag&&"function"===typeof e.tag.onbeforeupdate&&(B=e.tag.onbeforeupdate.call(e.state,e,g));void 0===A&&void 0===B||A||B?A=!1:(e.dom=g.dom,e.domSize=g.domSize,e.instance=g.instance,A=!0);if(!A)if(null!=
|
||||
e.attrs&&z(e.attrs,e,b,v),"string"===typeof c)switch(c){case "#":g.children.toString()!==e.children.toString()&&(g.dom.nodeValue=e.children);e.dom=g.dom;break;case "<":g.children!==e.children?(u(g),r(a,d(e),n)):(e.dom=g.dom,e.domSize=g.domSize);break;case "[":l(a,g.children,e.children,b,n,w);g=0;b=e.children;e.dom=null;if(null!=b){for(var q=0;q<b.length;q++)a=b[q],null!=a&&null!=a.dom&&(null==e.dom&&(e.dom=a.dom),g+=a.domSize||1);1!==g&&(e.domSize=g)}break;default:a=w;n=e.dom=g.dom;switch(e.tag){case "svg":a=
|
||||
"http://www.w3.org/2000/svg";break;case "math":a="http://www.w3.org/1998/Math/MathML"}"textarea"===e.tag&&(null==e.attrs&&(e.attrs={}),null!=e.text&&(e.attrs.value=e.text,e.text=void 0));v=g.attrs;w=e.attrs;c=a;if(null!=w)for(q in w)p(e,q,v&&v[q],w[q],c);if(null!=v)for(q in v)null!=w&&q in w||("className"===q&&(q="class"),"o"!==q[0]||"n"!==q[1]||M(q)?"key"!==q&&e.dom.removeAttribute(q):E(e,q,void 0));null!=e.attrs&&null!=e.attrs.contenteditable?t(e):null!=g.text&&null!=e.text&&""!==e.text?g.text.toString()!==
|
||||
e.text.toString()&&(g.dom.firstChild.nodeValue=e.text):(null!=g.text&&(g.children=[m("#",void 0,void 0,g.text,void 0,g.dom.firstChild)]),null!=e.text&&(e.children=[m("#",void 0,void 0,e.text,void 0,void 0)]),l(n,g.children,e.children,b,null,a))}else e.instance=m.normalize(e.tag.view.call(e.state,e)),z(e.tag,e,b,v),null!=e.instance?(null==g.instance?r(a,k(e.instance,b,w),n):h(a,g.instance,e.instance,b,n,v,w),e.dom=e.instance.dom,e.domSize=e.instance.domSize):null!=g.instance?(f(g.instance,null),e.dom=
|
||||
void 0,e.domSize=0):(e.dom=g.dom,e.domSize=g.domSize)}else f(g,null),r(a,k(e,b,void 0),n)}function u(a){var c=a.domSize;if(null!=c||null==a.dom){var e=y.createDocumentFragment();if(0<c){for(a=a.dom;--c;)e.appendChild(a.nextSibling);e.insertBefore(a,e.firstChild)}return e}return a.dom}function q(a,g,e){for(;g<a.length;g++)if(null!=a[g]&&null!=a[g].dom)return a[g].dom;return e}function r(a,g,e){e&&e.parentNode?a.insertBefore(g,e):a.appendChild(g)}function t(a){var c=a.children;if(null!=c&&1===c.length&&
|
||||
"<"===c[0].tag)c=c[0].children,a.dom.innerHTML!==c&&(a.dom.innerHTML=c);else if(null!=c||null!=a.text)throw Error("Child node of a contenteditable must be trusted");}function n(a,g,e,b){for(;g<e;g++){var c=a[g];null!=c&&(c.skip?c.skip=!1:f(c,b))}}function v(a){var c=!1;return function(){c||(c=!0,a())}}function f(a,g){function c(){if(++d===b&&(w(a),a.dom)){var c=a.domSize||1;if(1<c)for(var e=a.dom;--c;){var f=e.nextSibling,h=f.parentNode;null!=h&&h.removeChild(f)}c=a.dom;e=c.parentNode;null!=e&&e.removeChild(c);
|
||||
if(c=null!=g&&null==a.domSize)c=a.attrs,c=!(null!=c&&(c.oncreate||c.onupdate||c.onbeforeremove||c.onremove));c&&"string"===typeof a.tag&&(g.pool?g.pool.push(a):g.pool=[a])}}var b=1,d=0;a.attrs&&a.attrs.onbeforeremove&&(b++,a.attrs.onbeforeremove.call(a.state,a,v(c)));"string"!==typeof a.tag&&a.tag.onbeforeremove&&(b++,a.tag.onbeforeremove.call(a.state,a,v(c)));c()}function w(a){a.attrs&&a.attrs.onremove&&a.attrs.onremove.call(a.state,a);"string"!==typeof a.tag&&a.tag.onremove&&a.tag.onremove.call(a.state,
|
||||
a);if(null!=a.instance)w(a.instance);else if(a=a.children,a instanceof Array)for(var c=0;c<a.length;c++){var e=a[c];null!=e&&w(e)}}function p(a,g,e,b,d){var c=a.dom;if("key"!==g&&(e!==b||"value"===g||"checked"===g||"selectedIndex"===g||"selected"===g&&a.dom===y.activeElement||"object"===typeof b)&&"undefined"!==typeof b&&!M(g)){var f=g.indexOf(":");if(-1<f&&"xlink"===g.substr(0,f))c.setAttributeNS("http://www.w3.org/1999/xlink",g.slice(f+1),b);else if("o"===g[0]&&"n"===g[1]&&"function"===typeof b)E(a,
|
||||
g,b);else if("style"===g)if(a=e,a===b&&(c.style.cssText="",a=null),null==b)c.style.cssText="";else if("string"===typeof b)c.style.cssText=b;else{"string"===typeof a&&(c.style.cssText="");for(var h in b)c.style[h]=b[h];if(null!=a&&"string"!==typeof a)for(h in a)h in b||(c.style[h]="")}else if(g in c&&"href"!==g&&"list"!==g&&"form"!==g&&"width"!==g&&"height"!==g&&void 0===d){if("input"!==a.tag||"value"!==g||a.dom.value!==b||a.dom!==y.activeElement)c[g]=b}else"boolean"===typeof b?b?c.setAttribute(g,
|
||||
""):c.removeAttribute(g):c.setAttribute("className"===g?"class":g,b)}}function M(a){return"oninit"===a||"oncreate"===a||"onupdate"===a||"onremove"===a||"onbeforeremove"===a||"onbeforeupdate"===a}function E(a,b,e){var c=a.dom,g=function(a){var b=e.call(c,a);"function"===typeof C&&C.call(c,a);return b};if(b in c)c[b]="function"===typeof e?g:null;else{var d=b.slice(2);void 0===a.events&&(a.events={});null!=a.events[b]&&c.removeEventListener(d,a.events[b],!1);"function"===typeof e&&(a.events[b]=g,c.addEventListener(d,
|
||||
a.events[b],!1))}}function I(a,b,e){"function"===typeof a.oninit&&a.oninit.call(b.state,b);"function"===typeof a.oncreate&&e.push(a.oncreate.bind(b.state,b))}function z(a,b,e,d){d?I(a,b,e):"function"===typeof a.onupdate&&e.push(a.onupdate.bind(b.state,b))}function O(a,b){Object.keys(b).forEach(function(c){a[c]=b[c]})}var y=a.document,G=y.createDocumentFragment(),C;return{render:function(a,b){if(!a)throw Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.");var c=
|
||||
[],d=y.activeElement;null==a.vnodes&&(a.textContent="");b instanceof Array||(b=[b]);l(a,a.vnodes,m.normalizeChildren(b),c,null,void 0);a.vnodes=b;for(var f=0;f<c.length;f++)c[f]();y.activeElement!==d&&d.focus()},setEventCallback:function(a){return C=a}}}(window),P=function(a){var b=0,k=null,d="function"===typeof requestAnimationFrame?requestAnimationFrame:setTimeout;return function(l){var h=Date.now();!0===l||0===b||16<=h-b?(b=h,a()):null===k&&(k=d(function(){k=null;a();b=Date.now()},16-(h-b)))}},
|
||||
Q=function(a,b,k,d){d=P(d);null!=b&&b.setEventCallback(function(a){!1!==a.redraw&&k.publish()});null!=k&&(a.redraw&&k.unsubscribe(a.redraw),k.subscribe(d));return a.redraw=d};t.mount=function(a,b){return function(k,d){if(null===d)a.render(k,[]),b.unsubscribe(k.redraw),delete k.redraw;else{if(null==d.view)throw Error("m.mount(element, component) expects a component, not a vnode");Q(k,a,b,function(){a.render(k,m(d,void 0,void 0,void 0,void 0,void 0))})()}}}(C,K);var L=function(a){if(""===a||null==a)return{};
|
||||
"?"===a.charAt(0)&&(a=a.slice(1));a=a.split("&");for(var b={},k={},d=0;d<a.length;d++){var l=a[d].split("="),h=decodeURIComponent(l[0]),l=2===l.length?decodeURIComponent(l[1]):"";"true"===l?l=!0:"false"===l&&(l=!1);var m=h.split(/\]\[?|\[/),q=b;-1<h.indexOf("[")&&m.pop();for(var r=0;r<m.length;r++){var h=m[r],t=m[r+1],t=""==t||!isNaN(parseInt(t,10)),n=r===m.length-1;""===h&&(h=m.slice(0,r).join(),null==k[h]&&(k[h]=0),h=k[h]++);null==q[h]&&(q[h]=n?l:t?[]:{});q=q[h]}}return b},R=function(a){function b(b){var d=
|
||||
a.location[b].replace(/(?:%[a-f89][a-f0-9])+/gim,decodeURIComponent);"pathname"===b&&"/"!==d[0]&&(d="/"+d);return d}function k(a){return function(){null==t&&(t=q(function(){t=null;a()}))}}function d(a,b,d){var f=a.indexOf("?"),h=a.indexOf("#"),l=-1<f?f:-1<h?h:a.length;if(-1<f){var f=L(a.slice(f+1,-1<h?h:a.length)),k;for(k in f)b[k]=f[k]}if(-1<h)for(k in b=L(a.slice(h+1)),b)d[k]=b[k];return a.slice(0,l)}function l(){switch(r.charAt(0)){case "#":return b("hash").slice(r.length);case "?":return b("search").slice(r.length)+
|
||||
b("hash");default:return b("pathname").slice(r.length)+b("search")+b("hash")}}function h(b,h,f){var l={},k={};b=d(b,l,k);if(null!=h){for(var n in h)l[n]=h[n];b=b.replace(/:([^\/]+)/g,function(a,b){delete l[b];return h[b]})}(n=F(l))&&(b+="?"+n);(k=F(k))&&(b+="#"+k);m?(f&&f.replace?a.history.replaceState(null,null,r+b):a.history.pushState(null,null,r+b),a.onpopstate()):a.location.href=r+b}var m="function"===typeof a.history.pushState,q="function"===typeof setImmediate?setImmediate:setTimeout,r="#!",
|
||||
t;return{setPrefix:function(a){r=a},getPath:l,setPath:h,defineRoutes:function(b,h,f){function n(){var a=l(),k={},m=d(a,k,k),n;for(n in b){var q=new RegExp("^"+n.replace(/:[^\/]+?\.{3}/g,"(.*?)").replace(/:[^\/]+/g,"([^\\/]+)")+"/?$");if(q.test(m)){m.replace(q,function(){for(var d=n.match(/:[^\/]+/g)||[],f=[].slice.call(arguments,1,-2),l=0;l<d.length;l++)k[d[l].replace(/:|\./g,"")]=decodeURIComponent(f[l]);h(b[n],k,a,n)});return}}f(a,k)}m?a.onpopstate=k(n):"#"===r.charAt(0)&&(a.onhashchange=n);n();
|
||||
return n},link:function(a){a.dom.setAttribute("href",r+a.attrs.href);a.dom.onclick=function(a){a.ctrlKey||a.metaKey||a.shiftKey||2===a.which||(a.preventDefault(),a.redraw=!1,a=this.getAttribute("href"),0===a.indexOf(r)&&(a=a.slice(r.length)),h(a,void 0,void 0))}}}};t.route=function(a,b){function k(a){return a}var d=R(a),l,h,t,q,r,z={view:function(){return[t(m(h,null,q,void 0,void 0,void 0))]}},n=function(a,f,m){h="div";t=k;q=null;b(a,z);d.defineRoutes(m,function(b,d,f){var m="function"!==typeof b.view,
|
||||
n=k,p=l=function(k){p===l&&(l=null,h=null!=k?k:m?"div":b,t=n,q=d,r=f,a.redraw(!0))},u=function(){p()};m&&("function"===typeof b.render&&(n=b.render.bind(b)),"function"===typeof b.onmatch&&(u=b.onmatch));u.call(b,p,d,f)},function(){d.setPath(f,null,{replace:!0})})};n.link=d.link;n.prefix=d.setPrefix;n.set=d.setPath;n.get=function(){return r};return n}(window,t.mount);t.withAttr=function(a,b,k){return function(d){return b.call(k||this,a in d.currentTarget?d.currentTarget[a]:d.currentTarget.getAttribute(a))}};
|
||||
t.render=C.render;t.redraw=K.publish;t.request=J.request;t.jsonp=J.jsonp;t.parseQueryString=L;t.buildQueryString=F;t.version="1.0.0-rc.3";"undefined"!==typeof module?module.exports=t:window.m=t};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
"use strict"
|
||||
/** @constructor */
|
||||
var Promise = function(executor) {
|
||||
if (!(this instanceof Promise)) throw new Error("Promise must be called with `new`")
|
||||
var PromisePolyfill = function(executor) {
|
||||
if (!(this instanceof PromisePolyfill)) throw new Error("Promise must be called with `new`")
|
||||
if (typeof executor !== "function") throw new TypeError("executor must be a function")
|
||||
|
||||
var self = this, resolvers = [], rejectors = [], resolveCurrent = handler(resolvers, true), rejectCurrent = handler(rejectors, false)
|
||||
|
|
@ -44,7 +44,7 @@ var Promise = function(executor) {
|
|||
|
||||
executeOnce(executor)
|
||||
}
|
||||
Promise.prototype.then = function(onFulfilled, onRejection) {
|
||||
PromisePolyfill.prototype.then = function(onFulfilled, onRejection) {
|
||||
var self = this, instance = self._instance
|
||||
function handle(callback, list, next, state) {
|
||||
list.push(function(value) {
|
||||
|
|
@ -54,22 +54,22 @@ Promise.prototype.then = function(onFulfilled, onRejection) {
|
|||
if (typeof instance.retry === "function" && state === instance.state) instance.retry()
|
||||
}
|
||||
var resolveNext, rejectNext
|
||||
var promise = new Promise(function(resolve, reject) {resolveNext = resolve, rejectNext = reject})
|
||||
var promise = new PromisePolyfill(function(resolve, reject) {resolveNext = resolve, rejectNext = reject})
|
||||
handle(onFulfilled, instance.resolvers, resolveNext, true), handle(onRejection, instance.rejectors, rejectNext, false)
|
||||
return promise
|
||||
}
|
||||
Promise.prototype.catch = function(onRejection) {
|
||||
PromisePolyfill.prototype.catch = function(onRejection) {
|
||||
return this.then(null, onRejection)
|
||||
}
|
||||
Promise.resolve = function(value) {
|
||||
if (value instanceof Promise) return value
|
||||
return new Promise(function(resolve) {resolve(value)})
|
||||
PromisePolyfill.resolve = function(value) {
|
||||
if (value instanceof PromisePolyfill) return value
|
||||
return new PromisePolyfill(function(resolve) {resolve(value)})
|
||||
}
|
||||
Promise.reject = function(value) {
|
||||
return new Promise(function(resolve, reject) {reject(value)})
|
||||
PromisePolyfill.reject = function(value) {
|
||||
return new PromisePolyfill(function(resolve, reject) {reject(value)})
|
||||
}
|
||||
Promise.all = function(list) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
PromisePolyfill.all = function(list) {
|
||||
return new PromisePolyfill(function(resolve, reject) {
|
||||
var total = list.length, count = 0, values = []
|
||||
if (list.length === 0) resolve([])
|
||||
else for (var i = 0; i < list.length; i++) {
|
||||
|
|
@ -87,12 +87,12 @@ Promise.all = function(list) {
|
|||
}
|
||||
})
|
||||
}
|
||||
Promise.race = function(list) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
PromisePolyfill.race = function(list) {
|
||||
return new PromisePolyfill(function(resolve, reject) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
list[i].then(resolve, reject)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = Promise
|
||||
module.exports = typeof Promise !== "undefined" ? Promise : PromisePolyfill
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
var Stream = require("./stream")
|
||||
module.exports = require("./request/request")(window, Stream)
|
||||
var PromisePolyfill = require("./promise/promise")
|
||||
module.exports = require("./request/request")(window, PromisePolyfill)
|
||||
|
|
|
|||
|
|
@ -2,26 +2,37 @@
|
|||
|
||||
var buildQueryString = require("../querystring/build")
|
||||
|
||||
module.exports = function($window, Stream) {
|
||||
module.exports = function($window, Promise) {
|
||||
var callbackCount = 0
|
||||
|
||||
var count = 0
|
||||
var oncompletion
|
||||
function setCompletionCallback(callback) {oncompletion = callback}
|
||||
function complete() {if (--count === 0 && typeof oncompletion === "function") oncompletion()}
|
||||
|
||||
function request(args, extra) {
|
||||
if(typeof args === "string"){
|
||||
var url = args
|
||||
|
||||
if(typeof extra === "object") args = extra
|
||||
else args = {}
|
||||
|
||||
if(typeof args.url === "undefined") args.url = url
|
||||
function finalize(promise) {
|
||||
var then = promise.then
|
||||
promise.then = function() {
|
||||
count++
|
||||
var next = then.apply(promise, arguments)
|
||||
next.then(complete, function(e) {
|
||||
complete()
|
||||
throw e
|
||||
})
|
||||
return finalize(next)
|
||||
}
|
||||
return promise
|
||||
}
|
||||
|
||||
if(typeof args.method === "undefined") args.method = "GET"
|
||||
function request(args, extra) {
|
||||
return finalize(new Promise(function(resolve, reject) {
|
||||
if (typeof args === "string") {
|
||||
var url = args
|
||||
args = extra || {}
|
||||
if (args.url == null) args.url = url
|
||||
}
|
||||
|
||||
var stream = Stream()
|
||||
if (args.initialValue !== undefined) stream(args.initialValue)
|
||||
if (args.method == null) args.method = "GET"
|
||||
args.method = args.method.toUpperCase()
|
||||
|
||||
var useBody = typeof args.useBody === "boolean" ? args.useBody : args.method !== "GET" && args.method !== "TRACE"
|
||||
|
|
@ -51,43 +62,37 @@ module.exports = function($window, Stream) {
|
|||
try {
|
||||
var response = (args.extract !== extract) ? args.extract(xhr, args) : args.deserialize(args.extract(xhr, args))
|
||||
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
|
||||
stream(cast(args.type, response))
|
||||
resolve(cast(args.type, response))
|
||||
}
|
||||
else {
|
||||
var error = new Error(xhr.responseText)
|
||||
for (var key in response) error[key] = response[key]
|
||||
stream.error(error)
|
||||
reject(error)
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
stream.error(e)
|
||||
reject(e)
|
||||
}
|
||||
if (typeof oncompletion === "function") oncompletion()
|
||||
}
|
||||
}
|
||||
|
||||
if (useBody && (args.data != null)) xhr.send(args.data)
|
||||
else xhr.send()
|
||||
|
||||
return stream
|
||||
}))
|
||||
}
|
||||
|
||||
function jsonp(args) {
|
||||
var stream = Stream()
|
||||
if (args.initialValue !== undefined) stream(args.initialValue)
|
||||
|
||||
return finalize(new Promise(function(resolve, reject) {
|
||||
var callbackName = args.callbackName || "_mithril_" + Math.round(Math.random() * 1e16) + "_" + callbackCount++
|
||||
var script = $window.document.createElement("script")
|
||||
$window[callbackName] = function(data) {
|
||||
script.parentNode.removeChild(script)
|
||||
stream(cast(args.type, data))
|
||||
if (typeof oncompletion === "function") oncompletion()
|
||||
resolve(cast(args.type, data))
|
||||
delete $window[callbackName]
|
||||
}
|
||||
script.onerror = function() {
|
||||
script.parentNode.removeChild(script)
|
||||
stream.error(new Error("JSONP request failed"))
|
||||
if (typeof oncompletion === "function") oncompletion()
|
||||
reject(new Error("JSONP request failed"))
|
||||
delete $window[callbackName]
|
||||
}
|
||||
if (args.data == null) args.data = {}
|
||||
|
|
@ -95,7 +100,7 @@ module.exports = function($window, Stream) {
|
|||
args.data[args.callbackKey || "callback"] = callbackName
|
||||
script.src = assemble(args.url, args.data)
|
||||
$window.document.documentElement.appendChild(script)
|
||||
return stream
|
||||
}))
|
||||
}
|
||||
|
||||
function interpolate(url, data) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script src="../../test-utils/xhrMock.js"></script>
|
||||
|
||||
<script src="../../querystring/build.js"></script>
|
||||
<script src="../../util/stream.js"></script>
|
||||
<script src="../../promise/promise.js"></script>
|
||||
<script src="../../request/request.js"></script>
|
||||
<script src="test-request.js"></script>
|
||||
<script src="test-jsonp.js"></script>
|
||||
|
|
|
|||
|
|
@ -3,15 +3,14 @@
|
|||
var o = require("../../ospec/ospec")
|
||||
var xhrMock = require("../../test-utils/xhrMock")
|
||||
var Request = require("../../request/request")
|
||||
var StreamFactory = require("../../util/stream")
|
||||
var Promise = require("../../promise/promise")
|
||||
var parseQueryString = require("../../querystring/parse")
|
||||
|
||||
o.spec("jsonp", function() {
|
||||
var mock, jsonp, spy
|
||||
o.beforeEach(function() {
|
||||
mock = xhrMock()
|
||||
spy = o.spy()
|
||||
jsonp = new Request(mock, StreamFactory(spy)).jsonp
|
||||
jsonp = new Request(mock, Promise).jsonp
|
||||
})
|
||||
|
||||
o("works", function(done) {
|
||||
|
|
@ -21,9 +20,9 @@ o.spec("jsonp", function() {
|
|||
return {status: 200, responseText: queryData["callback"] + "(" + JSON.stringify({a: 1}) + ")"}
|
||||
}
|
||||
})
|
||||
jsonp({url: "/item"}).run(function(data) {
|
||||
jsonp({url: "/item"}).then(function(data) {
|
||||
o(data).deepEquals({a: 1})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("works w/ other querystring params", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -32,10 +31,10 @@ o.spec("jsonp", function() {
|
|||
return {status: 200, responseText: queryData["callback"] + "(" + JSON.stringify(queryData) + ")"}
|
||||
}
|
||||
})
|
||||
jsonp({url: "/item", data: {a: "b", c: "d"}}).run(function(data) {
|
||||
jsonp({url: "/item", data: {a: "b", c: "d"}}).then(function(data) {
|
||||
delete data["callback"]
|
||||
o(data).deepEquals({a: "b", c: "d"})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("works w/ custom callbackKey", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -44,9 +43,9 @@ o.spec("jsonp", function() {
|
|||
return {status: 200, responseText: queryData["cb"] + "(" + JSON.stringify({a: 2}) + ")"}
|
||||
}
|
||||
})
|
||||
jsonp({url: "/item", callbackKey: "cb"}).run(function(data) {
|
||||
jsonp({url: "/item", callbackKey: "cb"}).then(function(data) {
|
||||
o(data).deepEquals({a: 2})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("handles error", function(done) {
|
||||
jsonp({url: "/item", callbackKey: "cb"}).catch(function(e) {
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@
|
|||
var o = require("../../ospec/ospec")
|
||||
var xhrMock = require("../../test-utils/xhrMock")
|
||||
var Request = require("../../request/request")
|
||||
var StreamFactory = require("../../util/stream")
|
||||
var Promise = require("../../promise/promise")
|
||||
|
||||
o.spec("xhr", function() {
|
||||
var mock, xhr, spy
|
||||
var mock, xhr
|
||||
o.beforeEach(function() {
|
||||
mock = xhrMock()
|
||||
spy = o.spy()
|
||||
xhr = new Request(mock, StreamFactory(spy)).request
|
||||
xhr = new Request(mock, Promise).request
|
||||
})
|
||||
|
||||
o.spec("success", function() {
|
||||
|
|
@ -21,9 +20,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: 1})}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item"}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item"}).then(function(data) {
|
||||
o(data).deepEquals({a: 1})
|
||||
}).run(function() {
|
||||
}).then(function() {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
|
@ -34,9 +33,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: 1})}
|
||||
}
|
||||
})
|
||||
xhr({url: "/item"}).run(function(data) {
|
||||
xhr({url: "/item"}).then(function(data) {
|
||||
o(data).deepEquals({a: 1})
|
||||
}).run(function() {
|
||||
}).then(function() {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
|
@ -47,9 +46,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: 1})}
|
||||
}
|
||||
})
|
||||
xhr("/item").run(function(data) {
|
||||
xhr("/item").then(function(data) {
|
||||
o(data).deepEquals({a: 1})
|
||||
}).run(function() {
|
||||
}).then(function() {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
|
@ -59,9 +58,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: 1})}
|
||||
}
|
||||
})
|
||||
xhr({method: "POST", url: "/item"}).run(function(data) {
|
||||
xhr({method: "POST", url: "/item"}).then(function(data) {
|
||||
o(data).deepEquals({a: 1})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("first argument can act as URI with second argument providing options", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -69,9 +68,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: 1})}
|
||||
}
|
||||
})
|
||||
xhr("/item", {method: "POST"}).run(function(data) {
|
||||
xhr("/item", {method: "POST"}).then(function(data) {
|
||||
o(data).deepEquals({a: 1})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("works w/ parameterized data via GET", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -79,9 +78,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: request.query})}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item", data: {x: "y"}}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item", data: {x: "y"}}).then(function(data) {
|
||||
o(data).deepEquals({a: "?x=y"})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("works w/ parameterized data via POST", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -89,9 +88,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: JSON.parse(request.body)})}
|
||||
}
|
||||
})
|
||||
xhr({method: "POST", url: "/item", data: {x: "y"}}).run(function(data) {
|
||||
xhr({method: "POST", url: "/item", data: {x: "y"}}).then(function(data) {
|
||||
o(data).deepEquals({a: {x: "y"}})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("works w/ parameterized data containing colon via GET", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -99,9 +98,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: request.query})}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item", data: {x: ":y"}}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item", data: {x: ":y"}}).then(function(data) {
|
||||
o(data).deepEquals({a: "?x=%3Ay"})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("works w/ parameterized data containing colon via POST", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -109,9 +108,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: JSON.parse(request.body)})}
|
||||
}
|
||||
})
|
||||
xhr({method: "POST", url: "/item", data: {x: ":y"}}).run(function(data) {
|
||||
xhr({method: "POST", url: "/item", data: {x: ":y"}}).then(function(data) {
|
||||
o(data).deepEquals({a: {x: ":y"}})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("works w/ parameterized url via GET", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -119,9 +118,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: request.url, b: request.query})}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item/:x", data: {x: "y"}}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item/:x", data: {x: "y"}}).then(function(data) {
|
||||
o(data).deepEquals({a: "/item/y", b: {}})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("works w/ parameterized url via POST", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -129,9 +128,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: request.url, b: JSON.parse(request.body)})}
|
||||
}
|
||||
})
|
||||
xhr({method: "POST", url: "/item/:x", data: {x: "y"}}).run(function(data) {
|
||||
xhr({method: "POST", url: "/item/:x", data: {x: "y"}}).then(function(data) {
|
||||
o(data).deepEquals({a: "/item/y", b: {}})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("ignores unresolved parameter via GET", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -139,9 +138,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: request.url})}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item/:x"}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item/:x"}).then(function(data) {
|
||||
o(data).deepEquals({a: "/item/:x"})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("ignores unresolved parameter via POST", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -149,9 +148,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({a: request.url})}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item/:x"}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item/:x"}).then(function(data) {
|
||||
o(data).deepEquals({a: "/item/:x"})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("type parameter works for Array responses", function(done) {
|
||||
var Entity = function(args) {
|
||||
|
|
@ -163,9 +162,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify([{id: 1}, {id: 2}, {id: 3}])}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item", type: Entity}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item", type: Entity}).then(function(data) {
|
||||
o(data).deepEquals([{_id: 1}, {_id: 2}, {_id: 3}])
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("type parameter works for Object responses", function(done) {
|
||||
var Entity = function(args) {
|
||||
|
|
@ -177,9 +176,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({id: 1})}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item", type: Entity}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item", type: Entity}).then(function(data) {
|
||||
o(data).deepEquals({_id: 1})
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("serialize parameter works in GET", function(done) {
|
||||
var serialize = function(data) {
|
||||
|
|
@ -191,9 +190,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({body: request.query})}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item", serialize: serialize, data: {id: 1}}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item", serialize: serialize, data: {id: 1}}).then(function(data) {
|
||||
o(data.body).equals("?id=1")
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("serialize parameter works in POST", function(done) {
|
||||
var serialize = function(data) {
|
||||
|
|
@ -205,9 +204,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({body: request.body})}
|
||||
}
|
||||
})
|
||||
xhr({method: "POST", url: "/item", serialize: serialize, data: {id: 1}}).run(function(data) {
|
||||
xhr({method: "POST", url: "/item", serialize: serialize, data: {id: 1}}).then(function(data) {
|
||||
o(data.body).equals("id=1")
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("deserialize parameter works in GET", function(done) {
|
||||
var deserialize = function(data) {
|
||||
|
|
@ -219,9 +218,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({test: 123})}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item", deserialize: deserialize}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item", deserialize: deserialize}).then(function(data) {
|
||||
o(data).equals("{\"test\":123}")
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("deserialize parameter works in POST", function(done) {
|
||||
var deserialize = function(data) {
|
||||
|
|
@ -233,9 +232,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: JSON.stringify({test: 123})}
|
||||
}
|
||||
})
|
||||
xhr({method: "POST", url: "/item", deserialize: deserialize}).run(function(data) {
|
||||
xhr({method: "POST", url: "/item", deserialize: deserialize}).then(function(data) {
|
||||
o(data).equals("{\"test\":123}")
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("extract parameter works in GET", function(done) {
|
||||
var extract = function(data) {
|
||||
|
|
@ -247,9 +246,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: ""}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item", extract: extract}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item", extract: extract}).then(function(data) {
|
||||
o(data).equals("{\"test\":123}")
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("extract parameter works in POST", function(done) {
|
||||
var extract = function(data) {
|
||||
|
|
@ -261,9 +260,9 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: ""}
|
||||
}
|
||||
})
|
||||
xhr({method: "POST", url: "/item", extract: extract}).run(function(data) {
|
||||
xhr({method: "POST", url: "/item", extract: extract}).then(function(data) {
|
||||
o(data).equals("{\"test\":123}")
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("ignores deserialize if extract is defined", function(done) {
|
||||
var extract = function(data) {
|
||||
|
|
@ -276,11 +275,11 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: ""}
|
||||
}
|
||||
})
|
||||
xhr({method: "GET", url: "/item", extract: extract, deserialize: deserialize}).run(function(data) {
|
||||
xhr({method: "GET", url: "/item", extract: extract, deserialize: deserialize}).then(function(data) {
|
||||
o(data).equals(200)
|
||||
}).run(function() {
|
||||
}).then(function() {
|
||||
o(deserialize.callCount).equals(0)
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("config parameter works", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -288,7 +287,7 @@ o.spec("xhr", function() {
|
|||
return {status: 200, responseText: ""}
|
||||
}
|
||||
})
|
||||
xhr({method: "POST", url: "/item", config: config}).run(done)
|
||||
xhr({method: "POST", url: "/item", config: config}).then(done)
|
||||
|
||||
function config(xhr) {
|
||||
o(typeof xhr.setRequestHeader).equals("function")
|
||||
|
|
@ -296,16 +295,6 @@ o.spec("xhr", function() {
|
|||
o(typeof xhr.send).equals("function")
|
||||
}
|
||||
})
|
||||
o("initialValue parameter works", function() {
|
||||
mock.$defineRoutes({
|
||||
"GET /items": function() {
|
||||
return {status: 200, responseText: JSON.stringify([{a: 1}])}
|
||||
}
|
||||
})
|
||||
var items = xhr({method: "GET", url: "/items", initialValue: []})
|
||||
|
||||
o(items()).deepEquals([])
|
||||
})
|
||||
})
|
||||
o.spec("failure", function() {
|
||||
o("rejects on server error", function(done) {
|
||||
|
|
@ -317,7 +306,7 @@ o.spec("xhr", function() {
|
|||
xhr({method: "GET", url: "/item"}).catch(function(e) {
|
||||
o(e instanceof Error).equals(true)
|
||||
o(e.message).equals(JSON.stringify({error: "error"}))
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("extends Error with JSON response", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -329,7 +318,7 @@ o.spec("xhr", function() {
|
|||
o(e instanceof Error).equals(true)
|
||||
o(e.message).equals("error")
|
||||
o(e.stack).equals("error on line 1")
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
o("rejects on non-JSON server error", function(done) {
|
||||
mock.$defineRoutes({
|
||||
|
|
@ -339,7 +328,7 @@ o.spec("xhr", function() {
|
|||
})
|
||||
xhr({method: "GET", url: "/item"}).catch(function(e) {
|
||||
o(e.message).equals("error")
|
||||
}).run(done)
|
||||
}).then(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
"use strict"
|
||||
|
||||
module.exports = function(object, callback) {
|
||||
return object["fantasy-land/map"](callback)
|
||||
}
|
||||
|
|
@ -44,36 +44,6 @@ o.spec("api", function() {
|
|||
o(vnode.children[0].tag).equals("div")
|
||||
})
|
||||
})
|
||||
o.spec("m.prop", function() {
|
||||
o("works", function() {
|
||||
var stream = m.prop(5)
|
||||
var doubled = stream.run(function(value) {return value * 2})
|
||||
|
||||
o(doubled()).equals(10)
|
||||
})
|
||||
o("m.prop.combine works", function() {
|
||||
var added = m.prop.combine(function(a, b) {return a() + b()}, [
|
||||
m.prop(1),
|
||||
m.prop(2),
|
||||
])
|
||||
|
||||
o(added()).equals(3)
|
||||
})
|
||||
o("m.prop.merge works", function() {
|
||||
var added = m.prop.merge([
|
||||
m.prop(1),
|
||||
m.prop(2),
|
||||
])
|
||||
.run(function(values) {return values[0] + values[1]})
|
||||
|
||||
o(added()).equals(3)
|
||||
})
|
||||
o("m.prop.reject works", function() {
|
||||
var stream = m.prop.reject(new Error("error"))
|
||||
|
||||
o(stream.error().message).equals("error")
|
||||
})
|
||||
})
|
||||
o.spec("m.withAttr", function() {
|
||||
o("works", function() {
|
||||
var spy = o.spy()
|
||||
|
|
@ -191,9 +161,6 @@ o.spec("api", function() {
|
|||
o("works", function() {
|
||||
o(typeof m.request).equals("function") // TODO improve
|
||||
})
|
||||
o("return value is stream", function() {
|
||||
o(m.request({method: "GET", url: "[invalid]"}).constructor).equals(m.prop().constructor)
|
||||
})
|
||||
})
|
||||
o.spec("m.jsonp", function() {
|
||||
o("works", function() {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ module.exports = function(log) {
|
|||
function initStream(stream) {
|
||||
stream.constructor = createStream
|
||||
stream._state = {id: guid++, value: undefined, error: undefined, state: 0, derive: undefined, recover: undefined, deps: {}, parents: [], errorStream: undefined, endStream: undefined}
|
||||
stream["fantasy-land/map"] = map, stream["fantasy-land/ap"] = ap, stream["fantasy-land/of"] = createStream
|
||||
stream.map = stream["fantasy-land/map"] = map, stream["fantasy-land/ap"] = ap, stream["fantasy-land/of"] = createStream
|
||||
stream.valueOf = valueOf, stream.toJSON = toJSON, stream.toString = valueOf
|
||||
stream.run = run, stream.catch = doCatch
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ module.exports = function(log) {
|
|||
end: {get: function() {
|
||||
if (!stream._state.endStream) {
|
||||
var endStream = createStream()
|
||||
endStream["fantasy-land/map"](function(value) {
|
||||
endStream.map(function(value) {
|
||||
if (value === true) unregisterStream(stream), unregisterStream(endStream)
|
||||
return value
|
||||
})
|
||||
|
|
|
|||
|
|
@ -918,6 +918,11 @@ o.spec("stream", function() {
|
|||
|
||||
o(mapped()()).equals(undefined)
|
||||
})
|
||||
o("has alias", function() {
|
||||
var stream = Stream(undefined)
|
||||
|
||||
o(stream["fantasy-land/map"]).equals(stream.map)
|
||||
})
|
||||
})
|
||||
o.spec("ap", function() {
|
||||
o("works", function() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue