Officially drop IE9-10 support, pull out our hacks (#2296)
- I also fixed a bunch of related comments - I had to polyfill `requestAnimationFrame` for Node - Drive-by: run `eslint . --fix` - Drive-by: update transpiling info in CONTRIBUTING.md - Drive-by: we aren't the only ones going semicolon-free
This commit is contained in:
parent
a8473e63c9
commit
4a641092dc
17 changed files with 54 additions and 45 deletions
|
|
@ -22,7 +22,7 @@ A modern client-side Javascript framework for building Single Page Applications.
|
||||||
|
|
||||||
Mithril is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍.
|
Mithril is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍.
|
||||||
|
|
||||||
Browsers all the way back to IE9 are supported, no polyfills required 👌.
|
Mithril supports IE11, Firefox ESR, and the last two versions of Firefox, Edge, Safari, and Chrome. No polyfills required. 👌
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,13 @@
|
||||||
var coreRenderer = require("../render/render")
|
var coreRenderer = require("../render/render")
|
||||||
|
|
||||||
function throttle(callback) {
|
function throttle(callback) {
|
||||||
//60fps translates to 16.6ms, round it down since setTimeout requires int
|
var pending = null
|
||||||
var delay = 16
|
|
||||||
var last = 0, pending = null
|
|
||||||
var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
|
|
||||||
return function() {
|
return function() {
|
||||||
var elapsed = Date.now() - last
|
|
||||||
if (pending === null) {
|
if (pending === null) {
|
||||||
pending = timeout(function() {
|
pending = requestAnimationFrame(function() {
|
||||||
pending = null
|
pending = null
|
||||||
callback()
|
callback()
|
||||||
last = Date.now()
|
})
|
||||||
}, delay - elapsed)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,19 @@ var domMock = require("../../test-utils/domMock")
|
||||||
var throttleMocker = require("../../test-utils/throttleMock")
|
var throttleMocker = require("../../test-utils/throttleMock")
|
||||||
var apiRedraw = require("../../api/redraw")
|
var apiRedraw = require("../../api/redraw")
|
||||||
|
|
||||||
|
// Because Node doesn't have this.
|
||||||
|
if (typeof requestAnimationFrame !== "function") {
|
||||||
|
global.requestAnimationFrame = (function (delay, last) {
|
||||||
|
return function(callback) {
|
||||||
|
var elapsed = Date.now() - last
|
||||||
|
return setTimeout(function() {
|
||||||
|
callback()
|
||||||
|
last = Date.now()
|
||||||
|
}, delay - elapsed)
|
||||||
|
}
|
||||||
|
})(16, 0)
|
||||||
|
}
|
||||||
|
|
||||||
o.spec("redrawService", function() {
|
o.spec("redrawService", function() {
|
||||||
var root, redrawService, $document
|
var root, redrawService, $document
|
||||||
o.beforeEach(function() {
|
o.beforeEach(function() {
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
#### News
|
#### News
|
||||||
|
|
||||||
|
- Mithril now only officially supports IE11, Firefox ESR, and the last two versions of Chrome/FF/Edge/Safari. ([#2296](https://github.com/MithrilJS/mithril.js/pull/2296))
|
||||||
- API: Introduction of `m.redraw.sync()` ([#1592](https://github.com/MithrilJS/mithril.js/pull/1592))
|
- API: Introduction of `m.redraw.sync()` ([#1592](https://github.com/MithrilJS/mithril.js/pull/1592))
|
||||||
- API: Event handlers may also be objects with `handleEvent` methods ([#1949](https://github.com/MithrilJS/mithril.js/pull/1949), [#2222](https://github.com/MithrilJS/mithril.js/pull/2222)).
|
- API: Event handlers may also be objects with `handleEvent` methods ([#1949](https://github.com/MithrilJS/mithril.js/pull/1949), [#2222](https://github.com/MithrilJS/mithril.js/pull/2222)).
|
||||||
- API: `m.route.link` accepts an optional `options` object ([#1930](https://github.com/MithrilJS/mithril.js/pull/1930))
|
- API: `m.route.link` accepts an optional `options` object ([#1930](https://github.com/MithrilJS/mithril.js/pull/1930))
|
||||||
|
|
|
||||||
|
|
@ -79,17 +79,15 @@ This simplifies the workflow for bug fixes, which means they can be fixed faster
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Why doesn't the Mithril codebase use ES6 via Babel? Would a PR to upgrade be welcome?
|
## Why doesn't the Mithril codebase use ES6 via Babel or Bublé? Would a PR to upgrade be welcome?
|
||||||
|
|
||||||
Being able to run Mithril raw source code in IE is a requirement for all browser-related modules in this repo.
|
Being able to run Mithril's raw source code in all supported browsers is a requirement for all browser-related modules in this repo. In addition, transpiled code is generally much bulkier.
|
||||||
|
|
||||||
In addition, ES6 features are usually less performant than equivalent ES5 code, and transpiled code is bulkier.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Why doesn't the Mithril codebase use trailing semi-colons? Would a PR to add them be welcome?
|
## Why doesn't the Mithril codebase use trailing semi-colons? Would a PR to add them be welcome?
|
||||||
|
|
||||||
I don't use them. Adding them means the semi-colon usage in the codebase will eventually become inconsistent.
|
I don't use them. Adding them means the semi-colon usage in the codebase will eventually become inconsistent. Besides, [we aren't the only one who've decided to drop the semicolon](https://standardjs.com/#who-uses-javascript-standard-style). (We don't use Standard, though.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ Mithril is used by companies like Vimeo and Nike, and open source platforms like
|
||||||
|
|
||||||
If you are an experienced developer and want to know how Mithril compares to other frameworks, see the [framework comparison](framework-comparison.md) page.
|
If you are an experienced developer and want to know how Mithril compares to other frameworks, see the [framework comparison](framework-comparison.md) page.
|
||||||
|
|
||||||
Mithril supports browsers all the way back to IE9, no polyfills required.
|
Mithril supports IE11, Firefox ESR, and the last two versions of Firefox, Edge, Safari, and Chrome. No polyfills required.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,4 +183,4 @@ traverseDirectory("./docs", function(pathname) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(process.exit)
|
.then(process.exit)
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ When callbacks outside of Mithril run, you need to notify Mithril's rendering en
|
||||||
|
|
||||||
To trigger a redraw, call `m.redraw()`. Note that `m.redraw` only works if you used `m.mount` or `m.route`. If you rendered via `m.render`, you should use `m.render` to redraw.
|
To trigger a redraw, call `m.redraw()`. Note that `m.redraw` only works if you used `m.mount` or `m.route`. If you rendered via `m.render`, you should use `m.render` to redraw.
|
||||||
|
|
||||||
`m.redraw()` always triggers an asynchronous redraws, whereas `m.redraw.sync()` triggers a synchronous one. `m.redraw()` is tied to `window.requestAnimationFrame()` (we provide a fallback for IE9). It will thus typically fire at most 60 times per second. It may fire faster if your monitor has a higher refresh rate.
|
`m.redraw()` always triggers an asynchronous redraws, whereas `m.redraw.sync()` triggers a synchronous one. `m.redraw()` is tied to `window.requestAnimationFrame()`. It will thus typically fire at most 60 times per second. It may fire faster if your monitor has a higher refresh rate.
|
||||||
|
|
||||||
`m.redraw.sync()` is mostly intended to make videos play work in iOS. That only works in response to user-triggered events. It comes with several caveat:
|
`m.redraw.sync()` is mostly intended to make videos play work in iOS. That only works in response to user-triggered events. It comes with several caveat:
|
||||||
|
|
||||||
|
|
@ -52,4 +52,4 @@ To trigger a redraw, call `m.redraw()`. Note that `m.redraw` only works if you u
|
||||||
- `m.redraw.sync()` called from an event handler can cause the DOM to be modified while an event is bubbling. Depending on the structure of the old and new DOM trees, the event can finish the bubbling phase in the new tree and trigger unwanted handlers.
|
- `m.redraw.sync()` called from an event handler can cause the DOM to be modified while an event is bubbling. Depending on the structure of the old and new DOM trees, the event can finish the bubbling phase in the new tree and trigger unwanted handlers.
|
||||||
- It is not throttled. One call to `m.redraw.sync()` causes immediately one `m.render()` call per root registered with [`m.mount()`](mount.md) or [`m.route()`](route.md).
|
- It is not throttled. One call to `m.redraw.sync()` causes immediately one `m.render()` call per root registered with [`m.mount()`](mount.md) or [`m.route()`](route.md).
|
||||||
|
|
||||||
`m.redraw()` doesn't have any of those issues, you can call it from wherever you like.
|
`m.redraw()` doesn't have any of those issues, you can call it from wherever you like.
|
||||||
|
|
|
||||||
|
|
@ -218,11 +218,11 @@ The routing strategy dictates how a library might actually implement routing. Th
|
||||||
- Using the querystring. A URL using this strategy typically looks like `http://localhost/?/page1`
|
- Using the querystring. A URL using this strategy typically looks like `http://localhost/?/page1`
|
||||||
- Using the pathname. A URL using this strategy typically looks like `http://localhost/page1`
|
- Using the pathname. A URL using this strategy typically looks like `http://localhost/page1`
|
||||||
|
|
||||||
Using the hash strategy is guaranteed to work in browsers that don't support `history.pushState` (namely, Internet Explorer 9), because it can fall back to using `onhashchange`. Use this strategy if you want to support IE9.
|
Using the hash strategy is guaranteed to work in browsers that don't support `history.pushState`, because it can fall back to using `onhashchange`. Use this strategy if you want to keep the hashes purely local.
|
||||||
|
|
||||||
The querystring strategy also technically works in IE9, but it falls back to reloading the page. Use this strategy if you want to support anchored links and you are not able to make the server-side necessary to support the pathname strategy.
|
The querystring strategy allows server-side detection, but it doesn't appear as a normal path. Use this strategy if you want to support and potentially detect anchored links server-side and you are not able to make the changes necessary to support the pathname strategy (like if you're using Apache and can't modify your .htaccess).
|
||||||
|
|
||||||
The pathname strategy produces the cleanest looking URLs, but does not work in IE9 *and* requires setting up the server to serve the single page application code from every URL that the application can route to. Use this strategy if you want cleaner-looking URLs and do not need to support IE9.
|
The pathname strategy produces the cleanest looking URLs, but requires setting up the server to serve the single page application code from every URL that the application can route to. Use this strategy if you want cleaner-looking URLs.
|
||||||
|
|
||||||
Single page applications that use the hash strategy often use the convention of having an exclamation mark after the hash to indicate that they're using the hash as a routing mechanism and not for the purposes of linking to anchors. The `#!` string is known as a *hashbang*.
|
Single page applications that use the hash strategy often use the convention of having an exclamation mark after the hash to indicate that they're using the hash as a routing mechanism and not for the purposes of linking to anchors. The `#!` string is known as a *hashbang*.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ window.module = {
|
||||||
set exports(value) {require.$$modules[require.$$current()] = value},
|
set exports(value) {require.$$modules[require.$$current()] = value},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.global = window
|
||||||
|
|
||||||
function require(name) {
|
function require(name) {
|
||||||
var relative = require.$$current()
|
var relative = require.$$current()
|
||||||
var slashIndex = relative.lastIndexOf("/")
|
var slashIndex = relative.lastIndexOf("/")
|
||||||
|
|
|
||||||
|
|
@ -676,7 +676,7 @@ o.spec("the done parser", function() {
|
||||||
var threw = false
|
var threw = false
|
||||||
oo("test", function(/*hey
|
oo("test", function(/*hey
|
||||||
*/ /**/ //ho
|
*/ /**/ //ho
|
||||||
done /*hey
|
done /*hey
|
||||||
*/ /**/ //huuu
|
*/ /**/ //huuu
|
||||||
, timeout
|
, timeout
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ module.exports = function($window) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IE9 - IE11 (at least) throw an UnspecifiedError when accessing document.activeElement when
|
// IE11 (at least) throws an UnspecifiedError when accessing document.activeElement when
|
||||||
// inside an iframe. Catch and swallow this error, and heavy-handidly return null.
|
// inside an iframe. Catch and swallow this error, and heavy-handidly return null.
|
||||||
function activeElement() {
|
function activeElement() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -892,7 +892,7 @@ module.exports = function($window) {
|
||||||
vnodes = Vnode.normalizeChildren(Array.isArray(vnodes) ? vnodes : [vnodes])
|
vnodes = Vnode.normalizeChildren(Array.isArray(vnodes) ? vnodes : [vnodes])
|
||||||
updateNodes(dom, dom.vnodes, vnodes, hooks, null, namespace === "http://www.w3.org/1999/xhtml" ? undefined : namespace)
|
updateNodes(dom, dom.vnodes, vnodes, hooks, null, namespace === "http://www.w3.org/1999/xhtml" ? undefined : namespace)
|
||||||
dom.vnodes = vnodes
|
dom.vnodes = vnodes
|
||||||
// document.activeElement can return null in IE https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement
|
// `document.activeElement` can return null: https://html.spec.whatwg.org/multipage/interaction.html#dom-document-activeelement
|
||||||
if (active != null && activeElement() !== active && typeof active.focus === "function") active.focus()
|
if (active != null && activeElement() !== active && typeof active.focus === "function") active.focus()
|
||||||
for (var i = 0; i < hooks.length; i++) hooks[i]()
|
for (var i = 0; i < hooks.length; i++) hooks[i]()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -415,9 +415,9 @@ o.spec("xhr", function() {
|
||||||
xhr({method: "GET", url: "/item", config: handleAbort}).catch(function() {
|
xhr({method: "GET", url: "/item", config: handleAbort}).catch(function() {
|
||||||
failed = true
|
failed = true
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
resolved = true
|
resolved = true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
o("doesn't fail on file:// status 0", function(done) {
|
o("doesn't fail on file:// status 0", function(done) {
|
||||||
mock.$defineRoutes({
|
mock.$defineRoutes({
|
||||||
|
|
|
||||||
|
|
@ -473,9 +473,9 @@ module.exports = function(options) {
|
||||||
if (!this.hasAttribute("type")) return "text"
|
if (!this.hasAttribute("type")) return "text"
|
||||||
var type = this.getAttribute("type")
|
var type = this.getAttribute("type")
|
||||||
return (/^(?:radio|button|checkbox|color|date|datetime|datetime-local|email|file|hidden|month|number|password|range|research|search|submit|tel|text|url|week|image)$/)
|
return (/^(?:radio|button|checkbox|color|date|datetime|datetime-local|email|file|hidden|month|number|password|range|research|search|submit|tel|text|url|week|image)$/)
|
||||||
.test(type)
|
.test(type)
|
||||||
? type
|
? type
|
||||||
: "text"
|
: "text"
|
||||||
},
|
},
|
||||||
set: typeSetter,
|
set: typeSetter,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ o.spec("test-utils/components", function() {
|
||||||
if (component.kind !== "constructible") {
|
if (component.kind !== "constructible") {
|
||||||
o(cmp2).deepEquals(methods)
|
o(cmp2).deepEquals(methods)
|
||||||
} else {
|
} else {
|
||||||
// deepEquals doesn't search the prototype, do it manually
|
// deepEquals doesn't search the prototype, do it manually
|
||||||
o(cmp2 != null).equals(true)
|
o(cmp2 != null).equals(true)
|
||||||
o(cmp2.view).equals(methods.view)
|
o(cmp2.view).equals(methods.view)
|
||||||
o(cmp2.oninit).equals(methods.oninit)
|
o(cmp2.oninit).equals(methods.oninit)
|
||||||
|
|
|
||||||
|
|
@ -1430,22 +1430,22 @@ o.spec("domMock", function() {
|
||||||
o(input.type).equals("text")
|
o(input.type).equals("text")
|
||||||
})
|
})
|
||||||
"radio|button|checkbox|color|date|datetime|datetime-local|email|file|hidden|month|number|password|range|research|search|submit|tel|text|url|week|image"
|
"radio|button|checkbox|color|date|datetime|datetime-local|email|file|hidden|month|number|password|range|research|search|submit|tel|text|url|week|image"
|
||||||
.split("|").forEach(function(type) {
|
.split("|").forEach(function(type) {
|
||||||
o("can be set to " + type, function(){
|
o("can be set to " + type, function(){
|
||||||
var input = $document.createElement("input")
|
var input = $document.createElement("input")
|
||||||
input.type = type
|
input.type = type
|
||||||
|
|
||||||
o(input.getAttribute("type")).equals(type)
|
o(input.getAttribute("type")).equals(type)
|
||||||
o(input.type).equals(type)
|
o(input.type).equals(type)
|
||||||
})
|
})
|
||||||
o("bad values set the attribute, but the getter corrects to 'text', " + type, function(){
|
o("bad values set the attribute, but the getter corrects to 'text', " + type, function(){
|
||||||
var input = $document.createElement("input")
|
var input = $document.createElement("input")
|
||||||
input.type = "badbad" + type
|
input.type = "badbad" + type
|
||||||
|
|
||||||
o(input.getAttribute("type")).equals("badbad" + type)
|
o(input.getAttribute("type")).equals("badbad" + type)
|
||||||
o(input.type).equals("text")
|
o(input.type).equals("text")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
})
|
})
|
||||||
o.spec("textarea[value]", function() {
|
o.spec("textarea[value]", function() {
|
||||||
o("reads from child if no value was ever set", function() {
|
o("reads from child if no value was ever set", function() {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ o.spec("api", function() {
|
||||||
o("works", function() {
|
o("works", function() {
|
||||||
o(typeof m.version).equals("string")
|
o(typeof m.version).equals("string")
|
||||||
o(m.version.indexOf(".") > -1).equals(true)
|
o(m.version.indexOf(".") > -1).equals(true)
|
||||||
o(/\d/.test(m.version)).equals(true)
|
o((/\d/).test(m.version)).equals(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
o.spec("m.trust", function() {
|
o.spec("m.trust", function() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue