diff --git a/api/router.js b/api/router.js index 96cf56f89..fdf4ed8a3 100644 --- a/api/router.js +++ b/api/router.js @@ -6,7 +6,6 @@ var m = require("../render/hyperscript") var buildPathname = require("../pathname/build") var parsePathname = require("../pathname/parse") var compileTemplate = require("../pathname/compileTemplate") -var assign = require("../util/assign") var censor = require("../util/censor") var sentinel = {} @@ -78,7 +77,7 @@ module.exports = function($window, mountRedraw) { .slice(route.prefix.length) var data = parsePathname(path) - assign(data.params, $window.history.state) + Object.assign(data.params, $window.history.state) function reject(e) { console.error(e) diff --git a/pathname/build.js b/pathname/build.js index 848e3841d..3cd033c3e 100644 --- a/pathname/build.js +++ b/pathname/build.js @@ -1,7 +1,6 @@ "use strict" var buildQueryString = require("../querystring/build") -var assign = require("../util/assign") // Returns `path` from `template` + `params` module.exports = function(template, params) { @@ -16,7 +15,7 @@ module.exports = function(template, params) { var path = template.slice(0, pathEnd) var query = {} - assign(query, params) + Object.assign(query, params) var resolved = path.replace(/:([^\/\.-]+)(\.{3})?/g, function(m, key, variadic) { delete query[key] diff --git a/render/hyperscript.js b/render/hyperscript.js index 028c8bdbe..8b34233e9 100644 --- a/render/hyperscript.js +++ b/render/hyperscript.js @@ -5,7 +5,7 @@ var hyperscriptVnode = require("./hyperscriptVnode") var hasOwn = require("../util/hasOwn") var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g -var selectorCache = {} +var selectorCache = Object.create(null) function isEmpty(object) { for (var key in object) if (hasOwn.call(object, key)) return false @@ -27,6 +27,7 @@ function compileSelector(selector) { } } if (classes.length > 0) attrs.className = classes.join(" ") + if (isEmpty(attrs)) attrs = null return selectorCache[selector] = {tag: tag, attrs: attrs} } @@ -37,32 +38,30 @@ function execSelector(state, vnode) { vnode.tag = state.tag - if (!isEmpty(state.attrs)) { - var newAttrs = {} - - for (var key in attrs) { - if (hasOwn.call(attrs, key)) newAttrs[key] = attrs[key] - } - - attrs = newAttrs - } - - for (var key in state.attrs) { - if (hasOwn.call(state.attrs, key) && key !== "className" && !hasOwn.call(attrs, key)){ - attrs[key] = state.attrs[key] - } + if (state.attrs != null) { + attrs = Object.assign({}, state.attrs, attrs) + + if (className != null || state.attrs.className != null) attrs.className = + className != null + ? state.attrs.className != null + ? String(state.attrs.className) + " " + String(className) + : className + : state.attrs.className != null + ? state.attrs.className + : null + } else { + if (className != null) attrs.className = className } - if (className != null || state.attrs.className != null) attrs.className = - className != null - ? state.attrs.className != null - ? String(state.attrs.className) + " " + String(className) - : className - : state.attrs.className != null - ? state.attrs.className - : null if (hasClass) attrs.class = null + // workaround for #2622 (reorder keys in attrs to set "type" first) + // The DOM does things to inputs based on the "type", so it needs set first. + // See: https://github.com/MithrilJS/mithril.js/issues/2622 + if (state.tag === "input" && hasOwn.call(attrs, "type")) { + attrs = Object.assign({type: attrs.type}, attrs) + } + vnode.attrs = attrs return vnode diff --git a/render/render.js b/render/render.js index a41244324..a31d19346 100644 --- a/render/render.js +++ b/render/render.js @@ -671,18 +671,12 @@ module.exports = function() { //attrs function setAttrs(vnode, attrs, ns) { - // If you assign an input type that is not supported by IE 11 with an assignment expression, an error will occur. - // - // Also, the DOM does things to inputs based on the value, so it needs set first. - // See: https://github.com/MithrilJS/mithril.js/issues/2622 - if (vnode.tag === "input" && attrs.type != null) vnode.dom.setAttribute("type", attrs.type) - var isFileInput = attrs != null && vnode.tag === "input" && attrs.type === "file" for (var key in attrs) { - setAttr(vnode, key, null, attrs[key], ns, isFileInput) + setAttr(vnode, key, null, attrs[key], ns) } } - function setAttr(vnode, key, old, value, ns, isFileInput) { - if (key === "key" || key === "is" || value == null || isLifecycleMethod(key) || (old === value && !isFormAttribute(vnode, key)) && typeof value !== "object" || key === "type" && vnode.tag === "input") return + function setAttr(vnode, key, old, value, ns) { + if (key === "key" || key === "is" || value == null || isLifecycleMethod(key) || (old === value && !isFormAttribute(vnode, key)) && typeof value !== "object") return if (key[0] === "o" && key[1] === "n") return updateEvent(vnode, key, value) if (key.slice(0, 6) === "xlink:") vnode.dom.setAttributeNS("http://www.w3.org/1999/xlink", key.slice(6), value) else if (key === "style") updateStyle(vnode.dom, old, value) @@ -690,6 +684,7 @@ module.exports = function() { if (key === "value") { // Only do the coercion if we're actually going to check the value. /* eslint-disable no-implicit-coercion */ + var isFileInput = vnode.tag === "input" && vnode.attrs.type === "file" //setting input[value] to same value by typing on focused element moves cursor to end in Chrome //setting input[type=file][value] to same value causes an error to be generated if it's non-empty if ((vnode.tag === "input" || vnode.tag === "textarea") && vnode.dom.value === "" + value && (isFileInput || vnode.dom === activeElement(vnode.dom))) return @@ -702,7 +697,9 @@ module.exports = function() { if (isFileInput && "" + value !== "") { console.error("`value` is read-only on file inputs!"); return } /* eslint-enable no-implicit-coercion */ } - vnode.dom[key] = value + // If you assign an input type that is not supported by IE 11 with an assignment expression, an error will occur. + if (vnode.tag === "input" && key === "type") vnode.dom.setAttribute(key, value) + else vnode.dom[key] = value } else { if (typeof value === "boolean") { if (value) vnode.dom.setAttribute(key, "") @@ -750,14 +747,8 @@ module.exports = function() { console.warn("Don't reuse attrs object, use new object for every redraw, this will throw in next major") } if (attrs != null) { - // If you assign an input type that is not supported by IE 11 with an assignment expression, an error will occur. - // - // Also, the DOM does things to inputs based on the value, so it needs set first. - // See: https://github.com/MithrilJS/mithril.js/issues/2622 - if (vnode.tag === "input" && attrs.type != null) vnode.dom.setAttribute("type", attrs.type) - var isFileInput = vnode.tag === "input" && attrs.type === "file" for (var key in attrs) { - setAttr(vnode, key, old && old[key], attrs[key], ns, isFileInput) + setAttr(vnode, key, old && old[key], attrs[key], ns) } } var val diff --git a/util/assign.js b/util/assign.js deleted file mode 100644 index 1695f4b3a..000000000 --- a/util/assign.js +++ /dev/null @@ -1,10 +0,0 @@ -// This exists so I'm only saving it once. -"use strict" - -var hasOwn = require("./hasOwn") - -module.exports = Object.assign || function(target, source) { - for (var key in source) { - if (hasOwn.call(source, key)) target[key] = source[key] - } -}