Merge branch 'next'
This commit is contained in:
commit
17f2ab2645
15 changed files with 378 additions and 496 deletions
82
.github/ISSUE_TEMPLATE/------question.md
vendored
82
.github/ISSUE_TEMPLATE/------question.md
vendored
|
|
@ -1,82 +0,0 @@
|
||||||
---
|
|
||||||
name: "\U0001F64B♀️ Question"
|
|
||||||
about: Ask a question about Mithril
|
|
||||||
title: ''
|
|
||||||
labels: question
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- Provide a general summary of your question in the "Title" above -->
|
|
||||||
<!--
|
|
||||||
Provide the exact version of Mithril you're experiencing these issues with. This
|
|
||||||
matters, even if it's really old like version 0.1.0.
|
|
||||||
-->
|
|
||||||
**Mithril version:**
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide the name and version of both the browser and operating system you're
|
|
||||||
experiencing these issues with. If it's multiple, feel free to list multiple.
|
|
||||||
This matters, even if it's super ancient like IE 6 on Windows XP.
|
|
||||||
-->
|
|
||||||
**Browser and OS:**
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Optional: Provide a link to your project, if it happens to be open source or if
|
|
||||||
you created a repo somewhere that we can look into further. If it's multiple
|
|
||||||
projects, feel free to list them all.
|
|
||||||
-->
|
|
||||||
**Project:**
|
|
||||||
|
|
||||||
## Code
|
|
||||||
<!--
|
|
||||||
What did you try? Please be specific here. If you'd prefer, replace this code
|
|
||||||
block with a link to a code playground like any of these:
|
|
||||||
|
|
||||||
- Flems <https://flems.io/mithril> (stores everything in URL hash)
|
|
||||||
- JSFiddle <https://jsfiddle.net>
|
|
||||||
- CodePen <https://codepen.io>
|
|
||||||
- JSBin <https://jsbin.com>
|
|
||||||
- Plunker <https://plnkr.co>
|
|
||||||
- Glitch <https://glitch.com> (supports backend)
|
|
||||||
- CodeSandbox <https://codesandbox.io> (supports backend)
|
|
||||||
|
|
||||||
Or if it's a remote development project on your own server, feel free to provide
|
|
||||||
that if it's serving unminified code we can look at.
|
|
||||||
-->
|
|
||||||
```javascript
|
|
||||||
// Code
|
|
||||||
```
|
|
||||||
|
|
||||||
## Expected Behavior
|
|
||||||
<!--
|
|
||||||
What did you expect to happen?
|
|
||||||
|
|
||||||
- An alert to pop up?
|
|
||||||
- A specific thing to be logged?
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Current Behavior
|
|
||||||
<!--
|
|
||||||
What actually happened?
|
|
||||||
|
|
||||||
- The alert never showed?
|
|
||||||
- The wrong thing was logged?
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Steps to Reproduce
|
|
||||||
<!--
|
|
||||||
What steps need to be taken to reproduce this behavior? Please include things
|
|
||||||
like specific data that need typed in, specific buttons that need clicked, and
|
|
||||||
so on.
|
|
||||||
-->
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
4.
|
|
||||||
|
|
||||||
## Context
|
|
||||||
<!--
|
|
||||||
How is this issue affecting you? What are you trying to do? Providing us context
|
|
||||||
helps us reach a conclusion that best fits your particular needs.
|
|
||||||
-->
|
|
||||||
102
.github/ISSUE_TEMPLATE/---bug.md
vendored
102
.github/ISSUE_TEMPLATE/---bug.md
vendored
|
|
@ -1,102 +0,0 @@
|
||||||
---
|
|
||||||
name: "\U0001F41B Bug"
|
|
||||||
about: Report a bug in Mithril
|
|
||||||
title: ''
|
|
||||||
labels: bug
|
|
||||||
assignees: isiahmeadows
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- Provide a general summary of your issue in the "Title" above -->
|
|
||||||
<!--
|
|
||||||
Provide the exact version of Mithril you're experiencing these issues with. This
|
|
||||||
matters, even if it's really old like version 0.1.0. Do note that bugs in older
|
|
||||||
versions are commonly fixed in newer versions, so you should try to test it
|
|
||||||
against the latest version if you can.
|
|
||||||
-->
|
|
||||||
**Mithril version:**
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide the name and version of both the browser and operating system you're
|
|
||||||
experiencing these issues with. If it's multiple, feel free to list multiple.
|
|
||||||
This matters, even if it's super ancient like IE 6 on Windows XP.
|
|
||||||
-->
|
|
||||||
**Browser and OS:**
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Optional: Provide a link to your project, if it happens to be open source or if
|
|
||||||
you created a repo somewhere that we can look into further. If it's multiple
|
|
||||||
projects, feel free to list them all.
|
|
||||||
-->
|
|
||||||
**Project:**
|
|
||||||
|
|
||||||
## Code
|
|
||||||
<!--
|
|
||||||
What did you try? What code is causing the unexpected behavior? Make sure to
|
|
||||||
try to reduce your code as best as you can while still reproducing the bug, so
|
|
||||||
we can more accurately determine the cause. Ideally, it should just be a bunch
|
|
||||||
of Mithril calls with virtually no logic at all, but it's sufficient to just
|
|
||||||
remove unrelated network calls, attributes, and the like.
|
|
||||||
|
|
||||||
In addition, make sure the bug still persists with the latest version of
|
|
||||||
Mithril. If it's an older version, the bug may have already been fixed.
|
|
||||||
|
|
||||||
If you'd prefer, replace this code block with a link to a code playground like
|
|
||||||
any of these:
|
|
||||||
|
|
||||||
- Flems <https://flems.io/mithril> (stores everything in URL hash)
|
|
||||||
- JSFiddle <https://jsfiddle.net>
|
|
||||||
- CodePen <https://codepen.io>
|
|
||||||
- JSBin <https://jsbin.com>
|
|
||||||
- Plunker <https://plnkr.co>
|
|
||||||
- Glitch <https://glitch.com> (supports backend)
|
|
||||||
- CodeSandbox <https://codesandbox.io> (supports backend)
|
|
||||||
|
|
||||||
Or if it's a remote development project on your own server, feel free to provide
|
|
||||||
that if it's serving unminified code we can look at.
|
|
||||||
|
|
||||||
If it's a closed-source repo, it's okay to censor names and pull out irrelevant
|
|
||||||
logic - we'd rather not sign NDAs just to see the code you're having trouble
|
|
||||||
with. We do still need code of some kind that triggers the bug you're running
|
|
||||||
into.
|
|
||||||
-->
|
|
||||||
```javascript
|
|
||||||
// Code
|
|
||||||
```
|
|
||||||
|
|
||||||
## Steps to Reproduce
|
|
||||||
<!--
|
|
||||||
What steps need to be taken to reproduce this behavior? Please include things
|
|
||||||
like specific data that need typed in, specific buttons that need clicked, and
|
|
||||||
so on.
|
|
||||||
-->
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
4.
|
|
||||||
|
|
||||||
## Expected Behavior
|
|
||||||
<!--
|
|
||||||
What did you expect to happen?
|
|
||||||
|
|
||||||
- An alert to pop up?
|
|
||||||
- A specific thing to be logged?
|
|
||||||
|
|
||||||
Please be very specific here.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Current Behavior
|
|
||||||
<!--
|
|
||||||
What actually happened?
|
|
||||||
|
|
||||||
- The alert never showed?
|
|
||||||
- The wrong thing was logged?
|
|
||||||
|
|
||||||
Please be very specific here.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Context
|
|
||||||
<!--
|
|
||||||
Optional: How is this issue affecting you? What are you trying to do? Providing
|
|
||||||
us context helps us reach a solution that best fits your particular needs.
|
|
||||||
-->
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
---
|
|
||||||
name: "\U0001F680 Feature or Enhancement"
|
|
||||||
about: Suggest an idea or feature for Mithril
|
|
||||||
title: ''
|
|
||||||
labels: enhancement
|
|
||||||
assignees: isiahmeadows
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- Provide a general summary of your suggestion in the "Title" above -->
|
|
||||||
<!--
|
|
||||||
Optional: Provide the exact version of Mithril you're experiencing issues with.
|
|
||||||
This could matter, even if it's really old like version 0.1.0. Do note that bugs
|
|
||||||
in older versions are commonly fixed in newer versions and that newer versions
|
|
||||||
do end up with a lot more features than older versions, so it's unlikely we'll
|
|
||||||
add new features to older versions like 0.1.x.
|
|
||||||
-->
|
|
||||||
**Mithril version:**
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Optional: Provide the name and version of both the browser and operating system
|
|
||||||
you're running Mithril on. If it's multiple, feel free to list multiple. This
|
|
||||||
could matter, even if it's super ancient like IE 6 on Windows XP.
|
|
||||||
-->
|
|
||||||
**Browser and OS:**
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Optional: Provide a link to your project, if it happens to be open source or if
|
|
||||||
you created a repo somewhere that we can look into further. If it's multiple
|
|
||||||
projects, feel free to list them all.
|
|
||||||
-->
|
|
||||||
**Project:**
|
|
||||||
|
|
||||||
<!-- Required -->
|
|
||||||
**Is this something you're interested in implementing yourself?**
|
|
||||||
|
|
||||||
### Description
|
|
||||||
<!--
|
|
||||||
What exactly are you suggesting? Is it a particular missing feature? An odd
|
|
||||||
design choice you think could be improved? This doesn't need to be a concrete,
|
|
||||||
fully-fledged proposal, but it does need to be clear - it's hard to act on
|
|
||||||
suggestions that are too vague or generic.
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Why
|
|
||||||
<!--
|
|
||||||
Why is this important to you? How would you use it? We need to know what
|
|
||||||
problems it would solve in the real world and what benefits it would bring, for
|
|
||||||
both you and other potential users, so we know how we should prioritize it and
|
|
||||||
so we can see if a better solution might exist.
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Possible Implementation
|
|
||||||
<!--
|
|
||||||
Optional: How might this be implemented? This is optional, but it helps us put
|
|
||||||
the size and cost of the feature into perspective. Simpler features to implement
|
|
||||||
can often be justified by just being helpful, but big, complex features could
|
|
||||||
require a massive benefit to pay for their size, scale, and complexity.
|
|
||||||
|
|
||||||
(This is why the discussion on a context API similar to React's got so
|
|
||||||
contentious - it's right on that line where it could go either way on the
|
|
||||||
cost/benefit ratio for us.)
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Open Questions
|
|
||||||
<!--
|
|
||||||
Optional: What things still need discussed? If there are certain details you
|
|
||||||
aren't sure about, this could help inform discussion. Open questions like these
|
|
||||||
are precisely what shaped our sync vs async redraw API to be what they are for
|
|
||||||
v2.
|
|
||||||
-->
|
|
||||||
102
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
102
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
|
|
@ -1,102 +0,0 @@
|
||||||
---
|
|
||||||
name: "\U0001F41BBug report"
|
|
||||||
about: Report a bug in Mithril
|
|
||||||
title: ''
|
|
||||||
labels: bug
|
|
||||||
assignees: isiahmeadows
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- Provide a general summary of your issue in the "Title" above -->
|
|
||||||
<!--
|
|
||||||
Provide the exact version of Mithril you're experiencing these issues with. This
|
|
||||||
matters, even if it's really old like version 0.1.0. Do note that bugs in older
|
|
||||||
versions are commonly fixed in newer versions, so you should try to test it
|
|
||||||
against the latest version if you can.
|
|
||||||
-->
|
|
||||||
**Mithril version:**
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide the name and version of both the browser and operating system you're
|
|
||||||
experiencing these issues with. If it's multiple, feel free to list multiple.
|
|
||||||
This matters, even if it's super ancient like IE 6 on Windows XP.
|
|
||||||
-->
|
|
||||||
**Browser and OS:**
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Optional: Provide a link to your project, if it happens to be open source or if
|
|
||||||
you created a repo somewhere that we can look into further. If it's multiple
|
|
||||||
projects, feel free to list them all.
|
|
||||||
-->
|
|
||||||
**Project:**
|
|
||||||
|
|
||||||
## Code
|
|
||||||
<!--
|
|
||||||
What did you try? What code is causing the unexpected behavior? Make sure to
|
|
||||||
try to reduce your code as best as you can while still reproducing the bug, so
|
|
||||||
we can more accurately determine the cause. Ideally, it should just be a bunch
|
|
||||||
of Mithril calls with virtually no logic at all, but it's sufficient to just
|
|
||||||
remove unrelated network calls, attributes, and the like.
|
|
||||||
|
|
||||||
In addition, make sure the bug still persists with the latest version of
|
|
||||||
Mithril. If it's an older version, the bug may have already been fixed.
|
|
||||||
|
|
||||||
If you'd prefer, replace this code block with a link to a code playground like
|
|
||||||
any of these:
|
|
||||||
|
|
||||||
- Flems <https://flems.io/mithril> (stores everything in URL hash)
|
|
||||||
- JSFiddle <https://jsfiddle.net>
|
|
||||||
- CodePen <https://codepen.io>
|
|
||||||
- JSBin <https://jsbin.com>
|
|
||||||
- Plunker <https://plnkr.co>
|
|
||||||
- Glitch <https://glitch.com> (supports backend)
|
|
||||||
- CodeSandbox <https://codesandbox.io> (supports backend)
|
|
||||||
|
|
||||||
Or if it's a remote development project on your own server, feel free to provide
|
|
||||||
that if it's serving unminified code we can look at.
|
|
||||||
|
|
||||||
If it's a closed-source repo, it's okay to censor names and pull out irrelevant
|
|
||||||
logic - we'd rather not sign NDAs just to see the code you're having trouble
|
|
||||||
with. We do still need code of some kind that triggers the bug you're running
|
|
||||||
into.
|
|
||||||
-->
|
|
||||||
```javascript
|
|
||||||
// Code
|
|
||||||
```
|
|
||||||
|
|
||||||
## Steps to Reproduce
|
|
||||||
<!--
|
|
||||||
What steps need to be taken to reproduce this behavior? Please include things
|
|
||||||
like specific data that need typed in, specific buttons that need clicked, and
|
|
||||||
so on.
|
|
||||||
-->
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
4.
|
|
||||||
|
|
||||||
## Expected Behavior
|
|
||||||
<!--
|
|
||||||
What did you expect to happen?
|
|
||||||
|
|
||||||
- An alert to pop up?
|
|
||||||
- A specific thing to be logged?
|
|
||||||
|
|
||||||
Please be very specific here.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Current Behavior
|
|
||||||
<!--
|
|
||||||
What actually happened?
|
|
||||||
|
|
||||||
- The alert never showed?
|
|
||||||
- The wrong thing was logged?
|
|
||||||
|
|
||||||
Please be very specific here.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Context
|
|
||||||
<!--
|
|
||||||
Optional: How is this issue affecting you? What are you trying to do? Providing
|
|
||||||
us context helps us reach a solution that best fits your particular needs.
|
|
||||||
-->
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
name: "\U0001F41BBug"
|
name: "🐛 Bug"
|
||||||
about: Report a bug in Mithril
|
about: Report a bug in Mithril
|
||||||
title: ''
|
title: ''
|
||||||
labels: bug
|
labels: bug
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
name: "\U0001F680Feature or Enhancement"
|
name: "🚀 Feature or Enhancement"
|
||||||
about: Suggest an idea or feature for Mithril
|
about: Suggest an idea or feature for Mithril
|
||||||
title: ''
|
title: ''
|
||||||
labels: enhancement
|
labels: enhancement
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
name: "\U0001F64B♀️Question"
|
name: "🙋♀️ Question"
|
||||||
about: Ask a question about Mithril
|
about: Ask a question about Mithril
|
||||||
title: ''
|
title: ''
|
||||||
labels: question
|
labels: question
|
||||||
|
|
@ -189,14 +189,16 @@ module.exports = function($window, mountRedraw) {
|
||||||
// Remove these so they don't get overwritten
|
// Remove these so they don't get overwritten
|
||||||
var attrs = {}, onclick, href
|
var attrs = {}, onclick, href
|
||||||
assign(attrs, vnode.attrs)
|
assign(attrs, vnode.attrs)
|
||||||
attrs.component = null
|
// The first two are internal, but the rest are magic attributes
|
||||||
attrs.options = null
|
// that need censored to not screw up rendering.
|
||||||
attrs.key = null
|
attrs.selector = attrs.options = attrs.key = attrs.oninit =
|
||||||
|
attrs.oncreate = attrs.onbeforeupdate = attrs.onupdate =
|
||||||
|
attrs.onbeforeremove = attrs.onremove = null
|
||||||
|
|
||||||
// Do this now so we can get the most current `href` and `disabled`.
|
// Do this now so we can get the most current `href` and `disabled`.
|
||||||
// Those attributes may also be specified in the selector, and we
|
// Those attributes may also be specified in the selector, and we
|
||||||
// should honor that.
|
// should honor that.
|
||||||
var child = m(vnode.attrs.component || "a", attrs, vnode.children)
|
var child = m(vnode.attrs.selector || "a", attrs, vnode.children)
|
||||||
|
|
||||||
// Let's provide a *right* way to disable a route link, rather than
|
// Let's provide a *right* way to disable a route link, rather than
|
||||||
// letting people screw up accessibility on accident.
|
// letting people screw up accessibility on accident.
|
||||||
|
|
@ -235,17 +237,18 @@ module.exports = function($window, mountRedraw) {
|
||||||
// link target, etc. Nope, this isn't just for blind people.
|
// link target, etc. Nope, this isn't just for blind people.
|
||||||
if (
|
if (
|
||||||
// Skip if `onclick` prevented default
|
// Skip if `onclick` prevented default
|
||||||
result === false || !e.defaultPrevented &&
|
result !== false && !e.defaultPrevented &&
|
||||||
// Ignore everything but left clicks
|
// Ignore everything but left clicks
|
||||||
(e.button === 0 || e.which === 0 || e.which === 1) &&
|
(e.button === 0 || e.which === 0 || e.which === 1) &&
|
||||||
// Let the browser handle `target=_blank`, etc.
|
// Let the browser handle `target=_blank`, etc.
|
||||||
(!e.currentTarget.target || e.currentTarget.target === "_self") &&
|
(!e.currentTarget.target || e.currentTarget.target === "_self") &&
|
||||||
// No modifier keys
|
// No modifier keys
|
||||||
!e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey
|
!e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey
|
||||||
) return
|
) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.redraw = false
|
e.redraw = false
|
||||||
route.set(href, null, options)
|
route.set(href, null, options)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return child
|
return child
|
||||||
|
|
|
||||||
|
|
@ -465,6 +465,7 @@ o.spec("route", function() {
|
||||||
o(oninit.callCount).equals(1)
|
o(oninit.callCount).equals(1)
|
||||||
|
|
||||||
root.firstChild.dispatchEvent(e)
|
root.firstChild.dispatchEvent(e)
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
// Wrapped to ensure no redraw fired
|
// Wrapped to ensure no redraw fired
|
||||||
return waitCycles(1).then(function() {
|
return waitCycles(1).then(function() {
|
||||||
|
|
@ -476,6 +477,7 @@ o.spec("route", function() {
|
||||||
var e = $window.document.createEvent("MouseEvents")
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
|
||||||
e.initEvent("click", true, true)
|
e.initEvent("click", true, true)
|
||||||
|
e.button = 0
|
||||||
|
|
||||||
$window.location.href = prefix + "/"
|
$window.location.href = prefix + "/"
|
||||||
route(root, "/", {
|
route(root, "/", {
|
||||||
|
|
@ -496,7 +498,7 @@ o.spec("route", function() {
|
||||||
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
|
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
|
||||||
|
|
||||||
root.firstChild.dispatchEvent(e)
|
root.firstChild.dispatchEvent(e)
|
||||||
|
throttleMock.fire()
|
||||||
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "") + "test")
|
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : "") + "test")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -505,6 +507,7 @@ o.spec("route", function() {
|
||||||
var e = $window.document.createEvent("MouseEvents")
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
|
||||||
e.initEvent("click", true, true)
|
e.initEvent("click", true, true)
|
||||||
|
e.button = 0
|
||||||
$window.location.href = prefix + "/"
|
$window.location.href = prefix + "/"
|
||||||
|
|
||||||
route(root, "/", {
|
route(root, "/", {
|
||||||
|
|
@ -550,6 +553,64 @@ o.spec("route", function() {
|
||||||
o(root.firstChild.firstChild.nodeValue).equals("text")
|
o(root.firstChild.firstChild.nodeValue).equals("text")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
o("route.Link keeps magic attributes from being double-called", function() {
|
||||||
|
$window = browserMock(env)
|
||||||
|
var render = coreRenderer($window)
|
||||||
|
route = apiRouter(null, null)
|
||||||
|
route.prefix = prefix
|
||||||
|
root = $window.document.body
|
||||||
|
|
||||||
|
var oninit = o.spy()
|
||||||
|
var oncreate = o.spy()
|
||||||
|
var onbeforeupdate = o.spy()
|
||||||
|
var onupdate = o.spy()
|
||||||
|
var onbeforeremove = o.spy()
|
||||||
|
var onremove = o.spy()
|
||||||
|
|
||||||
|
render(root, m(route.Link, {
|
||||||
|
href: "/test",
|
||||||
|
oninit: oninit,
|
||||||
|
oncreate: oncreate,
|
||||||
|
onbeforeupdate: onbeforeupdate,
|
||||||
|
onupdate: onupdate,
|
||||||
|
onbeforeremove: onbeforeremove,
|
||||||
|
onremove: onremove,
|
||||||
|
}, "text"))
|
||||||
|
|
||||||
|
o(oninit.callCount).equals(1)
|
||||||
|
o(oncreate.callCount).equals(1)
|
||||||
|
o(onbeforeupdate.callCount).equals(0)
|
||||||
|
o(onupdate.callCount).equals(0)
|
||||||
|
o(onbeforeremove.callCount).equals(0)
|
||||||
|
o(onremove.callCount).equals(0)
|
||||||
|
|
||||||
|
render(root, m(route.Link, {
|
||||||
|
href: "/test",
|
||||||
|
oninit: oninit,
|
||||||
|
oncreate: oncreate,
|
||||||
|
onbeforeupdate: onbeforeupdate,
|
||||||
|
onupdate: onupdate,
|
||||||
|
onbeforeremove: onbeforeremove,
|
||||||
|
onremove: onremove,
|
||||||
|
}, "text"))
|
||||||
|
|
||||||
|
o(oninit.callCount).equals(1)
|
||||||
|
o(oncreate.callCount).equals(1)
|
||||||
|
o(onbeforeupdate.callCount).equals(1)
|
||||||
|
o(onupdate.callCount).equals(1)
|
||||||
|
o(onbeforeremove.callCount).equals(0)
|
||||||
|
o(onremove.callCount).equals(0)
|
||||||
|
|
||||||
|
render(root, [])
|
||||||
|
|
||||||
|
o(oninit.callCount).equals(1)
|
||||||
|
o(oncreate.callCount).equals(1)
|
||||||
|
o(onbeforeupdate.callCount).equals(1)
|
||||||
|
o(onupdate.callCount).equals(1)
|
||||||
|
o(onbeforeremove.callCount).equals(1)
|
||||||
|
o(onremove.callCount).equals(1)
|
||||||
|
})
|
||||||
|
|
||||||
o("route.Link can render other tag without routes or dom access", function() {
|
o("route.Link can render other tag without routes or dom access", function() {
|
||||||
$window = browserMock(env)
|
$window = browserMock(env)
|
||||||
var render = coreRenderer($window)
|
var render = coreRenderer($window)
|
||||||
|
|
@ -557,7 +618,7 @@ o.spec("route", function() {
|
||||||
route.prefix = prefix
|
route.prefix = prefix
|
||||||
root = $window.document.body
|
root = $window.document.body
|
||||||
|
|
||||||
render(root, m(route.Link, {component: "button", href: "/test", foo: "bar"}, "text"))
|
render(root, m(route.Link, {selector: "button", href: "/test", foo: "bar"}, "text"))
|
||||||
|
|
||||||
o(root.childNodes.length).equals(1)
|
o(root.childNodes.length).equals(1)
|
||||||
o(root.firstChild.nodeName).equals("BUTTON")
|
o(root.firstChild.nodeName).equals("BUTTON")
|
||||||
|
|
@ -577,7 +638,7 @@ o.spec("route", function() {
|
||||||
route.prefix = prefix
|
route.prefix = prefix
|
||||||
root = $window.document.body
|
root = $window.document.body
|
||||||
|
|
||||||
render(root, m(route.Link, {component: "button[href=/test]", foo: "bar"}, "text"))
|
render(root, m(route.Link, {selector: "button[href=/test]", foo: "bar"}, "text"))
|
||||||
|
|
||||||
o(root.childNodes.length).equals(1)
|
o(root.childNodes.length).equals(1)
|
||||||
o(root.firstChild.nodeName).equals("BUTTON")
|
o(root.firstChild.nodeName).equals("BUTTON")
|
||||||
|
|
@ -670,6 +731,139 @@ o.spec("route", function() {
|
||||||
o(root.firstChild.firstChild.nodeValue).equals("text")
|
o(root.firstChild.firstChild.nodeValue).equals("text")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
o("route.Link doesn't redraw on wrong button", function() {
|
||||||
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
e.button = 10
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/"
|
||||||
|
route(root, "/", {
|
||||||
|
"/" : {
|
||||||
|
view: lock(function() {
|
||||||
|
return m(route.Link, {href: "/test"})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
"/test" : {
|
||||||
|
view : lock(function() {
|
||||||
|
return m("div")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var slash = prefix[0] === "/" ? "" : "/"
|
||||||
|
|
||||||
|
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
|
||||||
|
|
||||||
|
root.firstChild.dispatchEvent(e)
|
||||||
|
throttleMock.fire()
|
||||||
|
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
|
||||||
|
})
|
||||||
|
|
||||||
|
o("route.Link doesn't redraw on preventDefault", function() {
|
||||||
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
e.button = 0
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/"
|
||||||
|
route(root, "/", {
|
||||||
|
"/" : {
|
||||||
|
view: lock(function() {
|
||||||
|
return m(route.Link, {
|
||||||
|
href: "/test",
|
||||||
|
onclick: function(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
"/test" : {
|
||||||
|
view : lock(function() {
|
||||||
|
return m("div")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var slash = prefix[0] === "/" ? "" : "/"
|
||||||
|
|
||||||
|
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
|
||||||
|
|
||||||
|
root.firstChild.dispatchEvent(e)
|
||||||
|
throttleMock.fire()
|
||||||
|
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
|
||||||
|
})
|
||||||
|
|
||||||
|
o("route.Link doesn't redraw on preventDefault in handleEvent", function() {
|
||||||
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
e.button = 0
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/"
|
||||||
|
route(root, "/", {
|
||||||
|
"/" : {
|
||||||
|
view: lock(function() {
|
||||||
|
return m(route.Link, {
|
||||||
|
href: "/test",
|
||||||
|
onclick: {
|
||||||
|
handleEvent: function(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
"/test" : {
|
||||||
|
view : lock(function() {
|
||||||
|
return m("div")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var slash = prefix[0] === "/" ? "" : "/"
|
||||||
|
|
||||||
|
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
|
||||||
|
|
||||||
|
root.firstChild.dispatchEvent(e)
|
||||||
|
throttleMock.fire()
|
||||||
|
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
|
||||||
|
})
|
||||||
|
|
||||||
|
o("route.Link doesn't redraw on return false", function() {
|
||||||
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
e.button = 0
|
||||||
|
|
||||||
|
$window.location.href = prefix + "/"
|
||||||
|
route(root, "/", {
|
||||||
|
"/" : {
|
||||||
|
view: lock(function() {
|
||||||
|
return m(route.Link, {
|
||||||
|
href: "/test",
|
||||||
|
onclick: function() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
"/test" : {
|
||||||
|
view : lock(function() {
|
||||||
|
return m("div")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var slash = prefix[0] === "/" ? "" : "/"
|
||||||
|
|
||||||
|
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
|
||||||
|
|
||||||
|
root.firstChild.dispatchEvent(e)
|
||||||
|
throttleMock.fire()
|
||||||
|
o($window.location.href).equals(env.protocol + "//" + (env.hostname === "/" ? "" : env.hostname) + slash + (prefix ? prefix + "/" : ""))
|
||||||
|
})
|
||||||
|
|
||||||
o("accepts RouteResolver with onmatch that returns Component", function() {
|
o("accepts RouteResolver with onmatch that returns Component", function() {
|
||||||
var matchCount = 0
|
var matchCount = 0
|
||||||
var renderCount = 0
|
var renderCount = 0
|
||||||
|
|
|
||||||
23
docs/keys.md
23
docs/keys.md
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
- [What are keys](#what-are-keys)
|
- [What are keys](#what-are-keys)
|
||||||
- [How to use](#how-to-use)
|
- [How to use](#how-to-use)
|
||||||
|
- [Single-child keyed fragments](#single-child-keyed-fragments)
|
||||||
- [Debugging key related issues](#debugging-key-related-issues)
|
- [Debugging key related issues](#debugging-key-related-issues)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -75,7 +76,9 @@ function correctUserList(users) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Also, you might want to reinitialize a component. You can use the common pattern of a single-child keyed fragment where you change the key to destroy and reinitialize the element.
|
#### Single-child keyed fragments
|
||||||
|
|
||||||
|
Sometimes, you might want to reinitialize a component on command. You can use the common pattern of a single-child keyed fragment where you change the key to destroy and reinitialize the element.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function ResettableToggle() {
|
function ResettableToggle() {
|
||||||
|
|
@ -96,6 +99,20 @@ function ResettableToggle() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also bind it to a known identity, for things like item views where you need to fetch a remote resource based on an ID. It's usually simpler than implementing all the logic to diff the ID and re-fetch a resource if it changes.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function Page() {
|
||||||
|
return {
|
||||||
|
view: function() {
|
||||||
|
return m(Layout, [
|
||||||
|
[m(ItemView, {key: m.route.param("id")})],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Debugging key related issues
|
### Debugging key related issues
|
||||||
|
|
@ -120,7 +137,7 @@ users.map(function(u) {
|
||||||
If you refactor the code and make a user component, the key must be moved out of the component and put on the component itself, since it is now the immediate child of the array.
|
If you refactor the code and make a user component, the key must be moved out of the component and put on the component itself, since it is now the immediate child of the array.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// AVOID
|
// AVOID - doesn't work
|
||||||
var User = {
|
var User = {
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
return m("div", { key: vnode.attrs.user.id }, [
|
return m("div", { key: vnode.attrs.user.id }, [
|
||||||
|
|
@ -137,7 +154,7 @@ users.map(function(u) {
|
||||||
|
|
||||||
#### Avoid wrapping keyed elements in arrays
|
#### Avoid wrapping keyed elements in arrays
|
||||||
|
|
||||||
Arrays are [vnodes](vnodes.md), and therefore keyable. You should not wrap arrays around keyed elements
|
Arrays are [vnodes](vnodes.md), and therefore keyable. You should not wrap arrays around keyed elements outside [single-child keyed fragments](#single-child-keyed-fragments).
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// AVOID
|
// AVOID
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ You can pass other attributes, too, and you can also specify the tag name used.
|
||||||
m(m.route.Link, {
|
m(m.route.Link, {
|
||||||
// Any hyperscript selector is valid here - it's literally passed as the
|
// Any hyperscript selector is valid here - it's literally passed as the
|
||||||
// first parameter to `m`.
|
// first parameter to `m`.
|
||||||
component: "span",
|
selector: "span",
|
||||||
options: {replace: true},
|
options: {replace: true},
|
||||||
href: "/test",
|
href: "/test",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
|
@ -154,7 +154,7 @@ m(m.route.Link, {
|
||||||
}, "link name")
|
}, "link name")
|
||||||
```
|
```
|
||||||
|
|
||||||
Magic attributes used by this component (except `href` and `disabled`) *are* removed while proxying, so you won't have an odd `component="span"` or `options="[object Object]"` attribute show up in your link's DOM node. The above component renders to this hyperscript, assuming the prefix is the default `#!`:
|
Magic attributes used by this selector (except `href` and `disabled`) *are* removed while proxying, so you won't have an odd `selector="span"` or `options="[object Object]"` attribute show up in your link's DOM node. The above vnode renders to this hyperscript, assuming the prefix is the default `#!`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
m("span", {
|
m("span", {
|
||||||
|
|
@ -202,15 +202,15 @@ Do note that this doesn't also disable pointer events for you - you have to do t
|
||||||
|
|
||||||
`vnode = m(m.route.Link, attributes, children)`
|
`vnode = m(m.route.Link, attributes, children)`
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
Argument | Type | Required | Description
|
||||||
---------------------- | ------------------------------------ | -------- | ---
|
--------------------- | ------------------------------------ | -------- | ---
|
||||||
`attributes.href` | `Object` | Yes | The target route to navigate to.
|
`attributes.href` | `Object` | Yes | The target route to navigate to.
|
||||||
`attributes.component` | `String|Object|Function` | No | This sets the tag name to use. Must be a valid selector for [`m`](hyperscript.md) if given, defaults to `"a"`.
|
`attributes.selector` | `String|Object|Function` | No | This sets the tag name to use. Must be a valid selector for [`m`](hyperscript.md) if given, defaults to `"a"`.
|
||||||
`attributes.options` | `Object` | No | This sets the options passed to [`m.route.set`](#mrouteset).
|
`attributes.options` | `Object` | No | This sets the options passed to [`m.route.set`](#mrouteset).
|
||||||
`attributes.disabled` | `Object` | No | This sets the options passed to [`m.route.set`](#mrouteset).
|
`attributes.disabled` | `Object` | No | This sets the options passed to [`m.route.set`](#mrouteset).
|
||||||
`attributes` | `Object` | No | Other attributes to apply to the returned vnode may be passed.
|
`attributes` | `Object` | No | Other attributes to apply to the returned vnode may be passed.
|
||||||
`children` | `Array<Vnode>|String|Number|Boolean` | No | Child [vnodes](vnodes.md) for this link.
|
`children` | `Array<Vnode>|String|Number|Boolean` | No | Child [vnodes](vnodes.md) for this link.
|
||||||
**returns** | `Vnode` | | A [vnode](vnodes.md).
|
**returns** | `Vnode` | | A [vnode](vnodes.md).
|
||||||
|
|
||||||
##### m.route.param
|
##### m.route.param
|
||||||
|
|
||||||
|
|
@ -243,6 +243,15 @@ As a rule of thumb, RouteResolvers should be in the same file as the `m.route` c
|
||||||
|
|
||||||
`routeResolver = {onmatch, render}`
|
`routeResolver = {onmatch, render}`
|
||||||
|
|
||||||
|
When using components, you could think of them as special sugar for this route resolver, assuming your component is `Home`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var routeResolver = {
|
||||||
|
onmatch: function() { return Home },
|
||||||
|
render: function(vnode) { return [vnode] },
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
##### routeResolver.onmatch
|
##### routeResolver.onmatch
|
||||||
|
|
||||||
The `onmatch` hook is called when the router needs to find a component to render. It is called once per router path changes, but not on subsequent redraws while on the same path. It can be used to run logic before a component initializes (for example authentication logic, data preloading, redirection analytics tracking, etc)
|
The `onmatch` hook is called when the router needs to find a component to render. It is called once per router path changes, but not on subsequent redraws while on the same path. It can be used to run logic before a component initializes (for example authentication logic, data preloading, redirection analytics tracking, etc)
|
||||||
|
|
@ -266,7 +275,7 @@ If `onmatch` returns a promise that gets rejected, the router redirects back to
|
||||||
|
|
||||||
##### routeResolver.render
|
##### routeResolver.render
|
||||||
|
|
||||||
The `render` method is called on every redraw for a matching route. It is similar to the `view` method in components and it exists to simplify [component composition](#wrapping-a-layout-component).
|
The `render` method is called on every redraw for a matching route. It is similar to the `view` method in components and it exists to simplify [component composition](#wrapping-a-layout-component). It also lets you escape from Mithril's normal behavior of replacing the entire subtree.
|
||||||
|
|
||||||
`vnode = routeResolve.render(vnode)`
|
`vnode = routeResolve.render(vnode)`
|
||||||
|
|
||||||
|
|
@ -276,6 +285,8 @@ Argument | Type | Description
|
||||||
`vnode.attrs` | `Object` | A map of URL parameter values
|
`vnode.attrs` | `Object` | A map of URL parameter values
|
||||||
**returns** | `Array<Vnode>|Vnode` | The [vnodes](vnodes.md) to be rendered
|
**returns** | `Array<Vnode>|Vnode` | The [vnodes](vnodes.md) to be rendered
|
||||||
|
|
||||||
|
The `vnode` parameter is just `m(Component, m.route.param())` where `Component` is the resolved component for the route (after `routeResolver.onmatch`) and `m.route.param()` is as documented [here](#mrouteparam). If you omit this method, the default return value is `[vnode]`, wrapped in a fragment so you can use [key parameters](#key-parameter). Combined with a `:key` parameter, it becomes a [single-element keyed fragment](keys.md#single-child-keyed-fragments), since it ends up rendering to something like `[m(Component, {key: m.route.param("key"), ...})]`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### How it works
|
#### How it works
|
||||||
|
|
@ -352,7 +363,7 @@ m.route(document.body, "/", {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we specify two routes: `/` and `/page1`, which render their respective components when the user navigates to each URL. By default, the SPA router prefix is `#!`
|
Here we specify two routes: `/` and `/page1`, which render their respective components when the user navigates to each URL.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -364,6 +375,8 @@ You can also navigate programmatically, via `m.route.set(route)`. For example, `
|
||||||
|
|
||||||
When navigating between routes, the router prefix is handled for you. In other words, leave out the hashbang `#!` (or whatever prefix you set `m.route.prefix` to) when linking Mithril routes, including in both `m.route.set` and in `m.route.Link`.
|
When navigating between routes, the router prefix is handled for you. In other words, leave out the hashbang `#!` (or whatever prefix you set `m.route.prefix` to) when linking Mithril routes, including in both `m.route.set` and in `m.route.Link`.
|
||||||
|
|
||||||
|
Do note that when navigating between components, the entire subtree is replaced. Use [a route resolver with a `render` method](#routeresolverrender) if you want to just patch the subtree.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Routing parameters
|
### Routing parameters
|
||||||
|
|
|
||||||
|
|
@ -57,22 +57,22 @@ Functions with multiple arguments are denoted with parenthesis: `(String, Array)
|
||||||
|
|
||||||
### Component signatures
|
### Component signatures
|
||||||
|
|
||||||
Components are denoted via calls to `m`, but with the selector argument set to a constant named in the relevant prose:
|
Components are denoted via calls to `m`, but with the initial selector argument set to a constant named in the relevant prose:
|
||||||
|
|
||||||
`vnode = m(m.route.Link, attributes, children)`
|
`vnode = m(m.route.Link, attributes, children)`
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
Argument | Type | Required | Description
|
||||||
---------------------- | ------------------------------------ | -------- | ---
|
--------------------- | ------------------------------------ | -------- | ---
|
||||||
`attributes.href` | `Object` | Yes | The target route to navigate to.
|
`attributes.href` | `Object` | Yes | The target route to navigate to.
|
||||||
`attributes.component` | `String|Object|Function` | No | This sets the tag name to use. Must be a valid selector for [`m`](hyperscript.md) if given, defaults to `"a"`.
|
`attributes.selector` | `String|Object|Function` | No | This sets the tag name to use. Must be a valid selector for [`m`](hyperscript.md) if given, defaults to `"a"`.
|
||||||
`attributes.options` | `Object` | No | This sets the options passed to [`m.route.set`](#mrouteset).
|
`attributes.options` | `Object` | No | This sets the options passed to [`m.route.set`](#mrouteset).
|
||||||
`attributes` | `Object` | No | Other attributes to apply to the returned vnode may be passed.
|
`attributes` | `Object` | No | Other attributes to apply to the returned vnode may be passed.
|
||||||
`children` | `Array<Vnode>|String|Number|Boolean` | No | Child [vnodes](vnodes.md) for this link.
|
`children` | `Array<Vnode>|String|Number|Boolean` | No | Child [vnodes](vnodes.md) for this link.
|
||||||
**returns** | `Vnode` | | A [vnode](vnodes.md).
|
**returns** | `Vnode` | | A [vnode](vnodes.md).
|
||||||
|
|
||||||
Children here, if specified, are assumed to be able to be written as [splat arguments](#splats), unless otherwise specified in prose.
|
Children here, if specified, are assumed to be able to be written as [splat arguments](#splats), unless otherwise specified in prose.
|
||||||
|
|
||||||
An element with no sensible children and/or attributes may elect to elide the relevant parameter entirely, so it might look closer to this:
|
An element with no sensible children and/or attributes may choose to elide the relevant parameter entirely:
|
||||||
|
|
||||||
`vnode = m(Component, attributes)`
|
`vnode = m(Component, attributes)`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,6 @@ Property | Type | Description
|
||||||
`state` | `Object?` | An object that is persisted between redraws. It is provided by the core engine when needed. In POJO component vnodes, the `state` inherits prototypically from the component object/class. In class component vnodes it is an instance of the class. In closure components it is the object returned by the closure.
|
`state` | `Object?` | An object that is persisted between redraws. It is provided by the core engine when needed. In POJO component vnodes, the `state` inherits prototypically from the component object/class. In class component vnodes it is an instance of the class. In closure components it is the object returned by the closure.
|
||||||
`events` | `Object?` | An object that is persisted between redraws and that stores event handlers so that they can be removed using the DOM API. The `events` property is `undefined` if there are no event handlers defined. This property is only used internally by Mithril, do not use or modify it.
|
`events` | `Object?` | An object that is persisted between redraws and that stores event handlers so that they can be removed using the DOM API. The `events` property is `undefined` if there are no event handlers defined. This property is only used internally by Mithril, do not use or modify it.
|
||||||
`instance` | `Object?` | For components, a storage location for the value returned by the `view`. This property is only used internally by Mithril, do not use or modify it.
|
`instance` | `Object?` | For components, a storage location for the value returned by the `view`. This property is only used internally by Mithril, do not use or modify it.
|
||||||
`skip` | `Boolean` | This property is only used internally by Mithril when diffing keyed lists, do not use or modify it.
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -16,107 +16,124 @@ o.spec("event", function() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function eventSpy(fn) {
|
||||||
|
function spy(e) {
|
||||||
|
spy.calls.push({
|
||||||
|
this: this, type: e.type,
|
||||||
|
target: e.target, currentTarget: e.currentTarget,
|
||||||
|
})
|
||||||
|
if (fn) return fn.apply(this, arguments)
|
||||||
|
}
|
||||||
|
spy.calls = []
|
||||||
|
return spy
|
||||||
|
}
|
||||||
|
|
||||||
o("handles onclick", function() {
|
o("handles onclick", function() {
|
||||||
var spy = o.spy()
|
var spyDiv = eventSpy()
|
||||||
var div = {tag: "div", attrs: {onclick: spy}}
|
var spyParent = eventSpy()
|
||||||
var e = $window.document.createEvent("MouseEvents")
|
var div = {tag: "div", attrs: {onclick: spyDiv}}
|
||||||
e.initEvent("click", true, true)
|
var parent = {tag: "div", attrs: {onclick: spyParent}, children: [div]}
|
||||||
|
|
||||||
render(root, [div])
|
|
||||||
div.dom.dispatchEvent(e)
|
|
||||||
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
o(spy.this).equals(div.dom)
|
|
||||||
o(spy.args[0].type).equals("click")
|
|
||||||
o(spy.args[0].target).equals(div.dom)
|
|
||||||
o(redraw.callCount).equals(1)
|
|
||||||
o(redraw.this).equals(undefined)
|
|
||||||
o(redraw.args.length).equals(0)
|
|
||||||
o(e.$defaultPrevented).equals(false)
|
|
||||||
o(e.$propagationStopped).equals(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
o("handles onclick returning false", function() {
|
|
||||||
var spy = o.spy(function () { return false })
|
|
||||||
var div = {tag: "div", attrs: {onclick: spy}}
|
|
||||||
var e = $window.document.createEvent("MouseEvents")
|
|
||||||
e.initEvent("click", true, true)
|
|
||||||
|
|
||||||
render(root, [div])
|
|
||||||
div.dom.dispatchEvent(e)
|
|
||||||
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
o(spy.this).equals(div.dom)
|
|
||||||
o(spy.args[0].type).equals("click")
|
|
||||||
o(spy.args[0].target).equals(div.dom)
|
|
||||||
o(redraw.callCount).equals(1)
|
|
||||||
o(redraw.this).equals(undefined)
|
|
||||||
o(redraw.args.length).equals(0)
|
|
||||||
o(e.$defaultPrevented).equals(true)
|
|
||||||
o(e.$propagationStopped).equals(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
o("handles click EventListener object", function() {
|
|
||||||
var spy = o.spy()
|
|
||||||
var listener = {handleEvent: spy}
|
|
||||||
var div = {tag: "div", attrs: {onclick: listener}}
|
|
||||||
var e = $window.document.createEvent("MouseEvents")
|
|
||||||
e.initEvent("click", true, true)
|
|
||||||
|
|
||||||
render(root, [div])
|
|
||||||
div.dom.dispatchEvent(e)
|
|
||||||
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
o(spy.this).equals(listener)
|
|
||||||
o(spy.args[0].type).equals("click")
|
|
||||||
o(spy.args[0].target).equals(div.dom)
|
|
||||||
o(redraw.callCount).equals(1)
|
|
||||||
o(redraw.this).equals(undefined)
|
|
||||||
o(redraw.args.length).equals(0)
|
|
||||||
o(e.$defaultPrevented).equals(false)
|
|
||||||
o(e.$propagationStopped).equals(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
o("handles click EventListener object returning false", function() {
|
|
||||||
var spy = o.spy(function () { return false })
|
|
||||||
var listener = {handleEvent: spy}
|
|
||||||
var div = {tag: "div", attrs: {onclick: listener}}
|
|
||||||
var e = $window.document.createEvent("MouseEvents")
|
|
||||||
e.initEvent("click", true, true)
|
|
||||||
|
|
||||||
render(root, [div])
|
|
||||||
div.dom.dispatchEvent(e)
|
|
||||||
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
o(spy.this).equals(listener)
|
|
||||||
o(spy.args[0].type).equals("click")
|
|
||||||
o(spy.args[0].target).equals(div.dom)
|
|
||||||
o(redraw.callCount).equals(1)
|
|
||||||
o(redraw.this).equals(undefined)
|
|
||||||
o(redraw.args.length).equals(0)
|
|
||||||
o(e.$defaultPrevented).equals(false)
|
|
||||||
o(e.$propagationStopped).equals(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
o("handles propagated onclick", function() {
|
|
||||||
var spy = o.spy()
|
|
||||||
var child = {tag: "div"}
|
|
||||||
var parent = {tag: "div", attrs: {onclick: spy}, children: [child]}
|
|
||||||
var e = $window.document.createEvent("MouseEvents")
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
e.initEvent("click", true, true)
|
e.initEvent("click", true, true)
|
||||||
|
|
||||||
render(root, [parent])
|
render(root, [parent])
|
||||||
child.dom.dispatchEvent(e)
|
div.dom.dispatchEvent(e)
|
||||||
|
|
||||||
o(spy.callCount).equals(1)
|
o(spyDiv.calls.length).equals(1)
|
||||||
o(spy.this).equals(parent.dom)
|
o(spyDiv.calls[0].this).equals(div.dom)
|
||||||
o(spy.args[0].type).equals("click")
|
o(spyDiv.calls[0].type).equals("click")
|
||||||
o(spy.args[0].target).equals(child.dom)
|
o(spyDiv.calls[0].target).equals(div.dom)
|
||||||
|
o(spyDiv.calls[0].currentTarget).equals(div.dom)
|
||||||
|
o(spyParent.calls.length).equals(1)
|
||||||
|
o(spyParent.calls[0].this).equals(parent.dom)
|
||||||
|
o(spyParent.calls[0].type).equals("click")
|
||||||
|
o(spyParent.calls[0].target).equals(div.dom)
|
||||||
|
o(spyParent.calls[0].currentTarget).equals(parent.dom)
|
||||||
|
o(redraw.callCount).equals(2)
|
||||||
|
o(redraw.this).equals(undefined)
|
||||||
|
o(redraw.args.length).equals(0)
|
||||||
|
o(e.defaultPrevented).equals(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
o("handles onclick returning false", function() {
|
||||||
|
var spyDiv = eventSpy(function() { return false })
|
||||||
|
var spyParent = eventSpy()
|
||||||
|
var div = {tag: "div", attrs: {onclick: spyDiv}}
|
||||||
|
var parent = {tag: "div", attrs: {onclick: spyParent}, children: [div]}
|
||||||
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
|
||||||
|
render(root, [parent])
|
||||||
|
div.dom.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spyDiv.calls.length).equals(1)
|
||||||
|
o(spyDiv.calls[0].this).equals(div.dom)
|
||||||
|
o(spyDiv.calls[0].type).equals("click")
|
||||||
|
o(spyDiv.calls[0].target).equals(div.dom)
|
||||||
|
o(spyDiv.calls[0].currentTarget).equals(div.dom)
|
||||||
|
o(spyParent.calls.length).equals(0)
|
||||||
o(redraw.callCount).equals(1)
|
o(redraw.callCount).equals(1)
|
||||||
o(redraw.this).equals(undefined)
|
o(redraw.this).equals(undefined)
|
||||||
o(redraw.args.length).equals(0)
|
o(redraw.args.length).equals(0)
|
||||||
o(e.$defaultPrevented).equals(false)
|
o(e.defaultPrevented).equals(true)
|
||||||
o(e.$propagationStopped).equals(false)
|
})
|
||||||
|
|
||||||
|
o("handles click EventListener object", function() {
|
||||||
|
var spyDiv = eventSpy()
|
||||||
|
var spyParent = eventSpy()
|
||||||
|
var listenerDiv = {handleEvent: spyDiv}
|
||||||
|
var listenerParent = {handleEvent: spyParent}
|
||||||
|
var div = {tag: "div", attrs: {onclick: listenerDiv}}
|
||||||
|
var parent = {tag: "div", attrs: {onclick: listenerParent}, children: [div]}
|
||||||
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
|
||||||
|
render(root, [parent])
|
||||||
|
div.dom.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spyDiv.calls.length).equals(1)
|
||||||
|
o(spyDiv.calls[0].this).equals(listenerDiv)
|
||||||
|
o(spyDiv.calls[0].type).equals("click")
|
||||||
|
o(spyDiv.calls[0].target).equals(div.dom)
|
||||||
|
o(spyDiv.calls[0].currentTarget).equals(div.dom)
|
||||||
|
o(spyParent.calls.length).equals(1)
|
||||||
|
o(spyParent.calls[0].this).equals(listenerParent)
|
||||||
|
o(spyParent.calls[0].type).equals("click")
|
||||||
|
o(spyParent.calls[0].target).equals(div.dom)
|
||||||
|
o(spyParent.calls[0].currentTarget).equals(parent.dom)
|
||||||
|
o(redraw.callCount).equals(2)
|
||||||
|
o(redraw.this).equals(undefined)
|
||||||
|
o(redraw.args.length).equals(0)
|
||||||
|
o(e.defaultPrevented).equals(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
o("handles click EventListener object returning false", function() {
|
||||||
|
var spyDiv = eventSpy(function() { return false })
|
||||||
|
var spyParent = eventSpy()
|
||||||
|
var listenerDiv = {handleEvent: spyDiv}
|
||||||
|
var listenerParent = {handleEvent: spyParent}
|
||||||
|
var div = {tag: "div", attrs: {onclick: listenerDiv}}
|
||||||
|
var parent = {tag: "div", attrs: {onclick: listenerParent}, children: [div]}
|
||||||
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
|
||||||
|
render(root, [parent])
|
||||||
|
div.dom.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spyDiv.calls.length).equals(1)
|
||||||
|
o(spyDiv.calls[0].this).equals(listenerDiv)
|
||||||
|
o(spyDiv.calls[0].type).equals("click")
|
||||||
|
o(spyDiv.calls[0].target).equals(div.dom)
|
||||||
|
o(spyDiv.calls[0].currentTarget).equals(div.dom)
|
||||||
|
o(spyParent.calls.length).equals(1)
|
||||||
|
o(spyParent.calls[0].this).equals(listenerParent)
|
||||||
|
o(spyParent.calls[0].type).equals("click")
|
||||||
|
o(spyParent.calls[0].target).equals(div.dom)
|
||||||
|
o(spyParent.calls[0].currentTarget).equals(parent.dom)
|
||||||
|
o(redraw.callCount).equals(2)
|
||||||
|
o(redraw.this).equals(undefined)
|
||||||
|
o(redraw.args.length).equals(0)
|
||||||
|
o(e.defaultPrevented).equals(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
o("removes event", function() {
|
o("removes event", function() {
|
||||||
|
|
|
||||||
|
|
@ -401,7 +401,7 @@ module.exports = function(options) {
|
||||||
e.preventDefault = function() {
|
e.preventDefault = function() {
|
||||||
prevented = true
|
prevented = true
|
||||||
}
|
}
|
||||||
Object.defineProperty(e, "$defaultPrevented", {
|
Object.defineProperty(e, "defaultPrevented", {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: function () { return prevented }
|
get: function () { return prevented }
|
||||||
})
|
})
|
||||||
|
|
@ -409,10 +409,6 @@ module.exports = function(options) {
|
||||||
e.stopPropagation = function() {
|
e.stopPropagation = function() {
|
||||||
stopped = true
|
stopped = true
|
||||||
}
|
}
|
||||||
Object.defineProperty(e, "$propagationStopped", {
|
|
||||||
configurable: true,
|
|
||||||
get: function () { return prevented }
|
|
||||||
})
|
|
||||||
e.eventPhase = 1
|
e.eventPhase = 1
|
||||||
try {
|
try {
|
||||||
for (var i = parents.length - 1; 0 <= i; i--) {
|
for (var i = parents.length - 1; 0 <= i; i--) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue