vendor/assets/javascripts/mithril.js in mithril-pipeline-0.2.5 vs vendor/assets/javascripts/mithril.js in mithril-pipeline-1.0.1

- old
+ new

@@ -1,2233 +1,1163 @@ -;(function (global, factory) { // eslint-disable-line - "use strict" - /* eslint-disable no-undef */ - var m = factory(global) - if (typeof module === "object" && module != null && module.exports) { - module.exports = m - } else if (typeof define === "function" && define.amd) { - define(function () { return m }) - } else { - global.m = m - } - /* eslint-enable no-undef */ -})(typeof window !== "undefined" ? window : this, function (global, undefined) { // eslint-disable-line - "use strict" - - m.version = function () { - return "v0.2.5" - } - - var hasOwn = {}.hasOwnProperty - var type = {}.toString - - function isFunction(object) { - return typeof object === "function" - } - - function isObject(object) { - return type.call(object) === "[object Object]" - } - - function isString(object) { - return type.call(object) === "[object String]" - } - - var isArray = Array.isArray || function (object) { - return type.call(object) === "[object Array]" - } - - function noop() {} - - var voidElements = { - AREA: 1, - BASE: 1, - BR: 1, - COL: 1, - COMMAND: 1, - EMBED: 1, - HR: 1, - IMG: 1, - INPUT: 1, - KEYGEN: 1, - LINK: 1, - META: 1, - PARAM: 1, - SOURCE: 1, - TRACK: 1, - WBR: 1 - } - - // caching commonly used variables - var $document, $location, $requestAnimationFrame, $cancelAnimationFrame - - // self invoking function needed because of the way mocks work - function initialize(mock) { - $document = mock.document - $location = mock.location - $cancelAnimationFrame = mock.cancelAnimationFrame || mock.clearTimeout - $requestAnimationFrame = mock.requestAnimationFrame || mock.setTimeout - } - - // testing API - m.deps = function (mock) { - initialize(global = mock || window) - return global - } - - m.deps(global) - - /** - * @typedef {String} Tag - * A string that looks like -> div.classname#id[param=one][param2=two] - * Which describes a DOM node - */ - - function parseTagAttrs(cell, tag) { - var classes = [] - var parser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g - var match - - while ((match = parser.exec(tag))) { - if (match[1] === "" && match[2]) { - cell.tag = match[2] - } else if (match[1] === "#") { - cell.attrs.id = match[2] - } else if (match[1] === ".") { - classes.push(match[2]) - } else if (match[3][0] === "[") { - var pair = /\[(.+?)(?:=("|'|)(.*?)\2)?\]/.exec(match[3]) - cell.attrs[pair[1]] = pair[3] || "" - } - } - - return classes - } - - function getVirtualChildren(args, hasAttrs) { - var children = hasAttrs ? args.slice(1) : args - - if (children.length === 1 && isArray(children[0])) { - return children[0] - } else { - return children - } - } - - function assignAttrs(target, attrs, classes) { - var classAttr = "class" in attrs ? "class" : "className" - - for (var attrName in attrs) { - if (hasOwn.call(attrs, attrName)) { - if (attrName === classAttr && - attrs[attrName] != null && - attrs[attrName] !== "") { - classes.push(attrs[attrName]) - // create key in correct iteration order - target[attrName] = "" - } else { - target[attrName] = attrs[attrName] - } - } - } - - if (classes.length) target[classAttr] = classes.join(" ") - } - - /** - * - * @param {Tag} The DOM node tag - * @param {Object=[]} optional key-value pairs to be mapped to DOM attrs - * @param {...mNode=[]} Zero or more Mithril child nodes. Can be an array, - * or splat (optional) - */ - function m(tag, pairs) { - var args = [] - - for (var i = 1, length = arguments.length; i < length; i++) { - args[i - 1] = arguments[i] - } - - if (isObject(tag)) return parameterize(tag, args) - - if (!isString(tag)) { - throw new Error("selector in m(selector, attrs, children) should " + - "be a string") - } - - var hasAttrs = pairs != null && isObject(pairs) && - !("tag" in pairs || "view" in pairs || "subtree" in pairs) - - var attrs = hasAttrs ? pairs : {} - var cell = { - tag: "div", - attrs: {}, - children: getVirtualChildren(args, hasAttrs) - } - - assignAttrs(cell.attrs, attrs, parseTagAttrs(cell, tag)) - return cell - } - - function forEach(list, f) { - for (var i = 0; i < list.length && !f(list[i], i++);) { - // function called in condition - } - } - - function forKeys(list, f) { - forEach(list, function (attrs, i) { - return (attrs = attrs && attrs.attrs) && - attrs.key != null && - f(attrs, i) - }) - } - // This function was causing deopts in Chrome. - function dataToString(data) { - // data.toString() might throw or return null if data is the return - // value of Console.log in some versions of Firefox (behavior depends on - // version) - try { - if (data != null && data.toString() != null) return data - } catch (e) { - // silently ignore errors - } - return "" - } - - // This function was causing deopts in Chrome. - function injectTextNode(parentElement, first, index, data) { - try { - insertNode(parentElement, first, index) - first.nodeValue = data - } catch (e) { - // IE erroneously throws error when appending an empty text node - // after a null - } - } - - function flatten(list) { - // recursively flatten array - for (var i = 0; i < list.length; i++) { - if (isArray(list[i])) { - list = list.concat.apply([], list) - // check current index again and flatten until there are no more - // nested arrays at that index - i-- - } - } - return list - } - - function insertNode(parentElement, node, index) { - parentElement.insertBefore(node, - parentElement.childNodes[index] || null) - } - - var DELETION = 1 - var INSERTION = 2 - var MOVE = 3 - - function handleKeysDiffer(data, existing, cached, parentElement) { - forKeys(data, function (key, i) { - existing[key = key.key] = existing[key] ? { - action: MOVE, - index: i, - from: existing[key].index, - element: cached.nodes[existing[key].index] || - $document.createElement("div") - } : {action: INSERTION, index: i} - }) - - var actions = [] - for (var prop in existing) { - if (hasOwn.call(existing, prop)) { - actions.push(existing[prop]) - } - } - - var changes = actions.sort(sortChanges) - var newCached = new Array(cached.length) - - newCached.nodes = cached.nodes.slice() - - forEach(changes, function (change) { - var index = change.index - if (change.action === DELETION) { - clear(cached[index].nodes, cached[index]) - newCached.splice(index, 1) - } - if (change.action === INSERTION) { - var dummy = $document.createElement("div") - dummy.key = data[index].attrs.key - insertNode(parentElement, dummy, index) - newCached.splice(index, 0, { - attrs: {key: data[index].attrs.key}, - nodes: [dummy] - }) - newCached.nodes[index] = dummy - } - - if (change.action === MOVE) { - var changeElement = change.element - var maybeChanged = parentElement.childNodes[index] - if (maybeChanged !== changeElement && changeElement !== null) { - parentElement.insertBefore(changeElement, - maybeChanged || null) - } - newCached[index] = cached[change.from] - newCached.nodes[index] = changeElement - } - }) - - return newCached - } - - function diffKeys(data, cached, existing, parentElement) { - var keysDiffer = data.length !== cached.length - - if (!keysDiffer) { - forKeys(data, function (attrs, i) { - var cachedCell = cached[i] - return keysDiffer = cachedCell && - cachedCell.attrs && - cachedCell.attrs.key !== attrs.key - }) - } - - if (keysDiffer) { - return handleKeysDiffer(data, existing, cached, parentElement) - } else { - return cached - } - } - - function diffArray(data, cached, nodes) { - // diff the array itself - - // update the list of DOM nodes by collecting the nodes from each item - forEach(data, function (_, i) { - if (cached[i] != null) nodes.push.apply(nodes, cached[i].nodes) - }) - // remove items from the end of the array if the new array is shorter - // than the old one. if errors ever happen here, the issue is most - // likely a bug in the construction of the `cached` data structure - // somewhere earlier in the program - forEach(cached.nodes, function (node, i) { - if (node.parentNode != null && nodes.indexOf(node) < 0) { - clear([node], [cached[i]]) - } - }) - - if (data.length < cached.length) cached.length = data.length - cached.nodes = nodes - } - - function buildArrayKeys(data) { - var guid = 0 - forKeys(data, function () { - forEach(data, function (attrs) { - if ((attrs = attrs && attrs.attrs) && attrs.key == null) { - attrs.key = "__mithril__" + guid++ - } - }) - return 1 - }) - } - - function isDifferentEnough(data, cached, dataAttrKeys) { - if (data.tag !== cached.tag) return true - - if (dataAttrKeys.sort().join() !== - Object.keys(cached.attrs).sort().join()) { - return true - } - - if (data.attrs.id !== cached.attrs.id) { - return true - } - - if (data.attrs.key !== cached.attrs.key) { - return true - } - - if (m.redraw.strategy() === "all") { - return !cached.configContext || cached.configContext.retain !== true - } - - if (m.redraw.strategy() === "diff") { - return cached.configContext && cached.configContext.retain === false - } - - return false - } - - function maybeRecreateObject(data, cached, dataAttrKeys) { - // if an element is different enough from the one in cache, recreate it - if (isDifferentEnough(data, cached, dataAttrKeys)) { - if (cached.nodes.length) clear(cached.nodes) - - if (cached.configContext && - isFunction(cached.configContext.onunload)) { - cached.configContext.onunload() - } - - if (cached.controllers) { - forEach(cached.controllers, function (controller) { - if (controller.onunload) { - controller.onunload({preventDefault: noop}) - } - }) - } - } - } - - function getObjectNamespace(data, namespace) { - if (data.attrs.xmlns) return data.attrs.xmlns - if (data.tag === "svg") return "http://www.w3.org/2000/svg" - if (data.tag === "math") return "http://www.w3.org/1998/Math/MathML" - return namespace - } - - var pendingRequests = 0 - m.startComputation = function () { pendingRequests++ } - m.endComputation = function () { - if (pendingRequests > 1) { - pendingRequests-- - } else { - pendingRequests = 0 - m.redraw() - } - } - - function unloadCachedControllers(cached, views, controllers) { - if (controllers.length) { - cached.views = views - cached.controllers = controllers - forEach(controllers, function (controller) { - if (controller.onunload && controller.onunload.$old) { - controller.onunload = controller.onunload.$old - } - - if (pendingRequests && controller.onunload) { - var onunload = controller.onunload - controller.onunload = noop - controller.onunload.$old = onunload - } - }) - } - } - - function scheduleConfigsToBeCalled(configs, data, node, isNew, cached) { - // schedule configs to be called. They are called after `build` finishes - // running - if (isFunction(data.attrs.config)) { - var context = cached.configContext = cached.configContext || {} - - // bind - configs.push(function () { - return data.attrs.config.call(data, node, !isNew, context, - cached) - }) - } - } - - function buildUpdatedNode( - cached, - data, - editable, - hasKeys, - namespace, - views, - configs, - controllers - ) { - var node = cached.nodes[0] - - if (hasKeys) { - setAttributes(node, data.tag, data.attrs, cached.attrs, namespace) - } - - cached.children = build( - node, - data.tag, - undefined, - undefined, - data.children, - cached.children, - false, - 0, - data.attrs.contenteditable ? node : editable, - namespace, - configs - ) - - cached.nodes.intact = true - - if (controllers.length) { - cached.views = views - cached.controllers = controllers - } - - return node - } - - function handleNonexistentNodes(data, parentElement, index) { - var nodes - if (data.$trusted) { - nodes = injectHTML(parentElement, index, data) - } else { - nodes = [$document.createTextNode(data)] - if (!(parentElement.nodeName in voidElements)) { - insertNode(parentElement, nodes[0], index) - } - } - - var cached - - if (typeof data === "string" || - typeof data === "number" || - typeof data === "boolean") { - cached = new data.constructor(data) - } else { - cached = data - } - - cached.nodes = nodes - return cached - } - - function reattachNodes( - data, - cached, - parentElement, - editable, - index, - parentTag - ) { - var nodes = cached.nodes - if (!editable || editable !== $document.activeElement) { - if (data.$trusted) { - clear(nodes, cached) - nodes = injectHTML(parentElement, index, data) - } else if (parentTag === "textarea") { - // <textarea> uses `value` instead of `nodeValue`. - parentElement.value = data - } else if (editable) { - // contenteditable nodes use `innerHTML` instead of `nodeValue`. - editable.innerHTML = data - } else { - // was a trusted string - if (nodes[0].nodeType === 1 || nodes.length > 1 || - (nodes[0].nodeValue.trim && - !nodes[0].nodeValue.trim())) { - clear(cached.nodes, cached) - nodes = [$document.createTextNode(data)] - } - - injectTextNode(parentElement, nodes[0], index, data) - } - } - cached = new data.constructor(data) - cached.nodes = nodes - return cached - } - - function handleTextNode( - cached, - data, - index, - parentElement, - shouldReattach, - editable, - parentTag - ) { - if (!cached.nodes.length) { - return handleNonexistentNodes(data, parentElement, index) - } else if (cached.valueOf() !== data.valueOf() || shouldReattach) { - return reattachNodes(data, cached, parentElement, editable, index, - parentTag) - } else { - return (cached.nodes.intact = true, cached) - } - } - - function getSubArrayCount(item) { - if (item.$trusted) { - // fix offset of next element if item was a trusted string w/ more - // than one html element - // the first clause in the regexp matches elements - // the second clause (after the pipe) matches text nodes - var match = item.match(/<[^\/]|\>\s*[^<]/g) - if (match != null) return match.length - } else if (isArray(item)) { - return item.length - } - return 1 - } - - function buildArray( - data, - cached, - parentElement, - index, - parentTag, - shouldReattach, - editable, - namespace, - configs - ) { - data = flatten(data) - var nodes = [] - var intact = cached.length === data.length - var subArrayCount = 0 - - // keys algorithm: sort elements without recreating them if keys are - // present - // - // 1) create a map of all existing keys, and mark all for deletion - // 2) add new keys to map and mark them for addition - // 3) if key exists in new list, change action from deletion to a move - // 4) for each key, handle its corresponding action as marked in - // previous steps - - var existing = {} - var shouldMaintainIdentities = false - - forKeys(cached, function (attrs, i) { - shouldMaintainIdentities = true - existing[cached[i].attrs.key] = {action: DELETION, index: i} - }) - - buildArrayKeys(data) - if (shouldMaintainIdentities) { - cached = diffKeys(data, cached, existing, parentElement) - } - // end key algorithm - - var cacheCount = 0 - // faster explicitly written - for (var i = 0, len = data.length; i < len; i++) { - // diff each item in the array - var item = build( - parentElement, - parentTag, - cached, - index, - data[i], - cached[cacheCount], - shouldReattach, - index + subArrayCount || subArrayCount, - editable, - namespace, - configs) - - if (item !== undefined) { - intact = intact && item.nodes.intact - subArrayCount += getSubArrayCount(item) - cached[cacheCount++] = item - } - } - - if (!intact) diffArray(data, cached, nodes) - return cached - } - - function makeCache(data, cached, index, parentIndex, parentCache) { - if (cached != null) { - if (type.call(cached) === type.call(data)) return cached - - if (parentCache && parentCache.nodes) { - var offset = index - parentIndex - var end = offset + (isArray(data) ? data : cached.nodes).length - clear( - parentCache.nodes.slice(offset, end), - parentCache.slice(offset, end)) - } else if (cached.nodes) { - clear(cached.nodes, cached) - } - } - - cached = new data.constructor() - // if constructor creates a virtual dom element, use a blank object as - // the base cached node instead of copying the virtual el (#277) - if (cached.tag) cached = {} - cached.nodes = [] - return cached - } - - function constructNode(data, namespace) { - if (data.attrs.is) { - if (namespace == null) { - return $document.createElement(data.tag, data.attrs.is) - } else { - return $document.createElementNS(namespace, data.tag, - data.attrs.is) - } - } else if (namespace == null) { - return $document.createElement(data.tag) - } else { - return $document.createElementNS(namespace, data.tag) - } - } - - function constructAttrs(data, node, namespace, hasKeys) { - if (hasKeys) { - return setAttributes(node, data.tag, data.attrs, {}, namespace) - } else { - return data.attrs - } - } - - function constructChildren( - data, - node, - cached, - editable, - namespace, - configs - ) { - if (data.children != null && data.children.length > 0) { - return build( - node, - data.tag, - undefined, - undefined, - data.children, - cached.children, - true, - 0, - data.attrs.contenteditable ? node : editable, - namespace, - configs) - } else { - return data.children - } - } - - function reconstructCached( - data, - attrs, - children, - node, - namespace, - views, - controllers - ) { - var cached = { - tag: data.tag, - attrs: attrs, - children: children, - nodes: [node] - } - - unloadCachedControllers(cached, views, controllers) - - if (cached.children && !cached.children.nodes) { - cached.children.nodes = [] - } - - // edge case: setting value on <select> doesn't work before children - // exist, so set it again after children have been created - if (data.tag === "select" && "value" in data.attrs) { - setAttributes(node, data.tag, {value: data.attrs.value}, {}, - namespace) - } - - return cached - } - - function getController(views, view, cachedControllers, controller) { - var controllerIndex - - if (m.redraw.strategy() === "diff" && views) { - controllerIndex = views.indexOf(view) - } else { - controllerIndex = -1 - } - - if (controllerIndex > -1) { - return cachedControllers[controllerIndex] - } else if (isFunction(controller)) { - return new controller() - } else { - return {} - } - } - - var unloaders = [] - - function updateLists(views, controllers, view, controller) { - if (controller.onunload != null && - unloaders.map(function (u) { return u.handler }) - .indexOf(controller.onunload) < 0) { - unloaders.push({ - controller: controller, - handler: controller.onunload - }) - } - - views.push(view) - controllers.push(controller) - } - - var forcing = false - function checkView( - data, - view, - cached, - cachedControllers, - controllers, - views - ) { - var controller = getController( - cached.views, - view, - cachedControllers, - data.controller) - - var key = data && data.attrs && data.attrs.key - - if (pendingRequests === 0 || - forcing || - cachedControllers && - cachedControllers.indexOf(controller) > -1) { - data = data.view(controller) - } else { - data = {tag: "placeholder"} - } - - if (data.subtree === "retain") return data - data.attrs = data.attrs || {} - data.attrs.key = key - updateLists(views, controllers, view, controller) - return data - } - - function markViews(data, cached, views, controllers) { - var cachedControllers = cached && cached.controllers - - while (data.view != null) { - data = checkView( - data, - data.view.$original || data.view, - cached, - cachedControllers, - controllers, - views) - } - - return data - } - - function buildObject( // eslint-disable-line max-statements - data, - cached, - editable, - parentElement, - index, - shouldReattach, - namespace, - configs - ) { - var views = [] - var controllers = [] - - data = markViews(data, cached, views, controllers) - - if (data.subtree === "retain") return cached - - if (!data.tag && controllers.length) { - throw new Error("Component template must return a virtual " + - "element, not an array, string, etc.") - } - - data.attrs = data.attrs || {} - cached.attrs = cached.attrs || {} - - var dataAttrKeys = Object.keys(data.attrs) - var hasKeys = dataAttrKeys.length > ("key" in data.attrs ? 1 : 0) - - maybeRecreateObject(data, cached, dataAttrKeys) - - if (!isString(data.tag)) return - - var isNew = cached.nodes.length === 0 - - namespace = getObjectNamespace(data, namespace) - - var node - if (isNew) { - node = constructNode(data, namespace) - // set attributes first, then create children - var attrs = constructAttrs(data, node, namespace, hasKeys) - - // add the node to its parent before attaching children to it - insertNode(parentElement, node, index) - - var children = constructChildren(data, node, cached, editable, - namespace, configs) - - cached = reconstructCached( - data, - attrs, - children, - node, - namespace, - views, - controllers) - } else { - node = buildUpdatedNode( - cached, - data, - editable, - hasKeys, - namespace, - views, - configs, - controllers) - } - - if (!isNew && shouldReattach === true && node != null) { - insertNode(parentElement, node, index) - } - - // The configs are called after `build` finishes running - scheduleConfigsToBeCalled(configs, data, node, isNew, cached) - - return cached - } - - function build( - parentElement, - parentTag, - parentCache, - parentIndex, - data, - cached, - shouldReattach, - index, - editable, - namespace, - configs - ) { - /* - * `build` is a recursive function that manages creation/diffing/removal - * of DOM elements based on comparison between `data` and `cached` the - * diff algorithm can be summarized as this: - * - * 1 - compare `data` and `cached` - * 2 - if they are different, copy `data` to `cached` and update the DOM - * based on what the difference is - * 3 - recursively apply this algorithm for every array and for the - * children of every virtual element - * - * The `cached` data structure is essentially the same as the previous - * redraw's `data` data structure, with a few additions: - * - `cached` always has a property called `nodes`, which is a list of - * DOM elements that correspond to the data represented by the - * respective virtual element - * - in order to support attaching `nodes` as a property of `cached`, - * `cached` is *always* a non-primitive object, i.e. if the data was - * a string, then cached is a String instance. If data was `null` or - * `undefined`, cached is `new String("")` - * - `cached also has a `configContext` property, which is the state - * storage object exposed by config(element, isInitialized, context) - * - when `cached` is an Object, it represents a virtual element; when - * it's an Array, it represents a list of elements; when it's a - * String, Number or Boolean, it represents a text node - * - * `parentElement` is a DOM element used for W3C DOM API calls - * `parentTag` is only used for handling a corner case for textarea - * values - * `parentCache` is used to remove nodes in some multi-node cases - * `parentIndex` and `index` are used to figure out the offset of nodes. - * They're artifacts from before arrays started being flattened and are - * likely refactorable - * `data` and `cached` are, respectively, the new and old nodes being - * diffed - * `shouldReattach` is a flag indicating whether a parent node was - * recreated (if so, and if this node is reused, then this node must - * reattach itself to the new parent) - * `editable` is a flag that indicates whether an ancestor is - * contenteditable - * `namespace` indicates the closest HTML namespace as it cascades down - * from an ancestor - * `configs` is a list of config functions to run after the topmost - * `build` call finishes running - * - * there's logic that relies on the assumption that null and undefined - * data are equivalent to empty strings - * - this prevents lifecycle surprises from procedural helpers that mix - * implicit and explicit return statements (e.g. - * function foo() {if (cond) return m("div")} - * - it simplifies diffing code - */ - data = dataToString(data) - if (data.subtree === "retain") return cached - cached = makeCache(data, cached, index, parentIndex, parentCache) - - if (isArray(data)) { - return buildArray( - data, - cached, - parentElement, - index, - parentTag, - shouldReattach, - editable, - namespace, - configs) - } else if (data != null && isObject(data)) { - return buildObject( - data, - cached, - editable, - parentElement, - index, - shouldReattach, - namespace, - configs) - } else if (!isFunction(data)) { - return handleTextNode( - cached, - data, - index, - parentElement, - shouldReattach, - editable, - parentTag) - } else { - return cached - } - } - - function sortChanges(a, b) { - return a.action - b.action || a.index - b.index - } - - function copyStyleAttrs(node, dataAttr, cachedAttr) { - for (var rule in dataAttr) { - if (hasOwn.call(dataAttr, rule)) { - if (cachedAttr == null || cachedAttr[rule] !== dataAttr[rule]) { - node.style[rule] = dataAttr[rule] - } - } - } - - for (rule in cachedAttr) { - if (hasOwn.call(cachedAttr, rule)) { - if (!hasOwn.call(dataAttr, rule)) node.style[rule] = "" - } - } - } - - var shouldUseSetAttribute = { - list: 1, - style: 1, - form: 1, - type: 1, - width: 1, - height: 1 - } - - function setSingleAttr( - node, - attrName, - dataAttr, - cachedAttr, - tag, - namespace - ) { - if (attrName === "config" || attrName === "key") { - // `config` isn't a real attribute, so ignore it - return true - } else if (isFunction(dataAttr) && attrName.slice(0, 2) === "on") { - // hook event handlers to the auto-redrawing system - node[attrName] = autoredraw(dataAttr, node) - } else if (attrName === "style" && dataAttr != null && - isObject(dataAttr)) { - // handle `style: {...}` - copyStyleAttrs(node, dataAttr, cachedAttr) - } else if (namespace != null) { - // handle SVG - if (attrName === "href") { - node.setAttributeNS("http://www.w3.org/1999/xlink", - "href", dataAttr) - } else { - node.setAttribute( - attrName === "className" ? "class" : attrName, - dataAttr) - } - } else if (attrName in node && !shouldUseSetAttribute[attrName]) { - // handle cases that are properties (but ignore cases where we - // should use setAttribute instead) - // - // - list and form are typically used as strings, but are DOM - // element references in js - // - // - when using CSS selectors (e.g. `m("[style='']")`), style is - // used as a string, but it's an object in js - // - // #348 don't set the value if not needed - otherwise, cursor - // placement breaks in Chrome - try { - if (tag !== "input" || node[attrName] !== dataAttr) { - node[attrName] = dataAttr - } - } catch (e) { - node.setAttribute(attrName, dataAttr) - } - } - else node.setAttribute(attrName, dataAttr) - } - - function trySetAttr( - node, - attrName, - dataAttr, - cachedAttr, - cachedAttrs, - tag, - namespace - ) { - if (!(attrName in cachedAttrs) || (cachedAttr !== dataAttr) || ($document.activeElement === node)) { - cachedAttrs[attrName] = dataAttr - try { - return setSingleAttr( - node, - attrName, - dataAttr, - cachedAttr, - tag, - namespace) - } catch (e) { - // swallow IE's invalid argument errors to mimic HTML's - // fallback-to-doing-nothing-on-invalid-attributes behavior - if (e.message.indexOf("Invalid argument") < 0) throw e - } - } else if (attrName === "value" && tag === "input" && - node.value !== dataAttr) { - // #348 dataAttr may not be a string, so use loose comparison - node.value = dataAttr - } - } - - function setAttributes(node, tag, dataAttrs, cachedAttrs, namespace) { - for (var attrName in dataAttrs) { - if (hasOwn.call(dataAttrs, attrName)) { - if (trySetAttr( - node, - attrName, - dataAttrs[attrName], - cachedAttrs[attrName], - cachedAttrs, - tag, - namespace)) { - continue - } - } - } - return cachedAttrs - } - - function clear(nodes, cached) { - for (var i = nodes.length - 1; i > -1; i--) { - if (nodes[i] && nodes[i].parentNode) { - try { - nodes[i].parentNode.removeChild(nodes[i]) - } catch (e) { - /* eslint-disable max-len */ - // ignore if this fails due to order of events (see - // http://stackoverflow.com/questions/21926083/failed-to-execute-removechild-on-node) - /* eslint-enable max-len */ - } - cached = [].concat(cached) - if (cached[i]) unload(cached[i]) - } - } - // release memory if nodes is an array. This check should fail if nodes - // is a NodeList (see loop above) - if (nodes.length) { - nodes.length = 0 - } - } - - function unload(cached) { - if (cached.configContext && isFunction(cached.configContext.onunload)) { - cached.configContext.onunload() - cached.configContext.onunload = null - } - if (cached.controllers) { - forEach(cached.controllers, function (controller) { - if (isFunction(controller.onunload)) { - controller.onunload({preventDefault: noop}) - } - }) - } - if (cached.children) { - if (isArray(cached.children)) forEach(cached.children, unload) - else if (cached.children.tag) unload(cached.children) - } - } - - function appendTextFragment(parentElement, data) { - try { - parentElement.appendChild( - $document.createRange().createContextualFragment(data)) - } catch (e) { - parentElement.insertAdjacentHTML("beforeend", data) - replaceScriptNodes(parentElement) - } - } - - // Replace script tags inside given DOM element with executable ones. - // Will also check children recursively and replace any found script - // tags in same manner. - function replaceScriptNodes(node) { - if (node.tagName === "SCRIPT") { - node.parentNode.replaceChild(buildExecutableNode(node), node) - } else { - var children = node.childNodes - if (children && children.length) { - for (var i = 0; i < children.length; i++) { - replaceScriptNodes(children[i]) - } - } - } - - return node - } - - // Replace script element with one whose contents are executable. - function buildExecutableNode(node){ - var scriptEl = document.createElement("script") - var attrs = node.attributes - - for (var i = 0; i < attrs.length; i++) { - scriptEl.setAttribute(attrs[i].name, attrs[i].value) - } - - scriptEl.text = node.innerHTML - return scriptEl - } - - function injectHTML(parentElement, index, data) { - var nextSibling = parentElement.childNodes[index] - if (nextSibling) { - var isElement = nextSibling.nodeType !== 1 - var placeholder = $document.createElement("span") - if (isElement) { - parentElement.insertBefore(placeholder, nextSibling || null) - placeholder.insertAdjacentHTML("beforebegin", data) - parentElement.removeChild(placeholder) - } else { - nextSibling.insertAdjacentHTML("beforebegin", data) - } - } else { - appendTextFragment(parentElement, data) - } - - var nodes = [] - - while (parentElement.childNodes[index] !== nextSibling) { - nodes.push(parentElement.childNodes[index]) - index++ - } - - return nodes - } - - function autoredraw(callback, object) { - return function (e) { - e = e || event - m.redraw.strategy("diff") - m.startComputation() - try { - return callback.call(object, e) - } finally { - endFirstComputation() - } - } - } - - var html - var documentNode = { - appendChild: function (node) { - if (html === undefined) html = $document.createElement("html") - if ($document.documentElement && - $document.documentElement !== node) { - $document.replaceChild(node, $document.documentElement) - } else { - $document.appendChild(node) - } - - this.childNodes = $document.childNodes - }, - - insertBefore: function (node) { - this.appendChild(node) - }, - - childNodes: [] - } - - var nodeCache = [] - var cellCache = {} - - m.render = function (root, cell, forceRecreation) { - if (!root) { - throw new Error("Ensure the DOM element being passed to " + - "m.route/m.mount/m.render is not undefined.") - } - var configs = [] - var id = getCellCacheKey(root) - var isDocumentRoot = root === $document - var node - - if (isDocumentRoot || root === $document.documentElement) { - node = documentNode - } else { - node = root - } - - if (isDocumentRoot && cell.tag !== "html") { - cell = {tag: "html", attrs: {}, children: cell} - } - - if (cellCache[id] === undefined) clear(node.childNodes) - if (forceRecreation === true) reset(root) - - cellCache[id] = build( - node, - null, - undefined, - undefined, - cell, - cellCache[id], - false, - 0, - null, - undefined, - configs) - - forEach(configs, function (config) { config() }) - } - - function getCellCacheKey(element) { - var index = nodeCache.indexOf(element) - return index < 0 ? nodeCache.push(element) - 1 : index - } - - m.trust = function (value) { - value = new String(value) // eslint-disable-line no-new-wrappers - value.$trusted = true - return value - } - - function gettersetter(store) { - function prop() { - if (arguments.length) store = arguments[0] - return store - } - - prop.toJSON = function () { - return store - } - - return prop - } - - m.prop = function (store) { - if ((store != null && (isObject(store) || isFunction(store)) || ((typeof Promise !== "undefined") && (store instanceof Promise))) && - isFunction(store.then)) { - return propify(store) - } - - return gettersetter(store) - } - - var roots = [] - var components = [] - var controllers = [] - var lastRedrawId = null - var lastRedrawCallTime = 0 - var computePreRedrawHook = null - var computePostRedrawHook = null - var topComponent - var FRAME_BUDGET = 16 // 60 frames per second = 1 call per 16 ms - - function parameterize(component, args) { - function controller() { - /* eslint-disable no-invalid-this */ - return (component.controller || noop).apply(this, args) || this - /* eslint-enable no-invalid-this */ - } - - if (component.controller) { - controller.prototype = component.controller.prototype - } - - function view(ctrl) { - var currentArgs = [ctrl].concat(args) - for (var i = 1; i < arguments.length; i++) { - currentArgs.push(arguments[i]) - } - - return component.view.apply(component, currentArgs) - } - - view.$original = component.view - var output = {controller: controller, view: view} - if (args[0] && args[0].key != null) output.attrs = {key: args[0].key} - return output - } - - m.component = function (component) { - var args = new Array(arguments.length - 1) - - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i] - } - - return parameterize(component, args) - } - - function checkPrevented(component, root, index, isPrevented) { - if (!isPrevented) { - m.redraw.strategy("all") - m.startComputation() - roots[index] = root - var currentComponent - - if (component) { - currentComponent = topComponent = component - } else { - currentComponent = topComponent = component = {controller: noop} - } - - var controller = new (component.controller || noop)() - - // controllers may call m.mount recursively (via m.route redirects, - // for example) - // this conditional ensures only the last recursive m.mount call is - // applied - if (currentComponent === topComponent) { - controllers[index] = controller - components[index] = component - } - endFirstComputation() - if (component === null) { - removeRootElement(root, index) - } - return controllers[index] - } else if (component == null) { - removeRootElement(root, index) - } - } - - m.mount = m.module = function (root, component) { - if (!root) { - throw new Error("Please ensure the DOM element exists before " + - "rendering a template into it.") - } - - var index = roots.indexOf(root) - if (index < 0) index = roots.length - - var isPrevented = false - var event = { - preventDefault: function () { - isPrevented = true - computePreRedrawHook = computePostRedrawHook = null - } - } - - forEach(unloaders, function (unloader) { - unloader.handler.call(unloader.controller, event) - unloader.controller.onunload = null - }) - - if (isPrevented) { - forEach(unloaders, function (unloader) { - unloader.controller.onunload = unloader.handler - }) - } else { - unloaders = [] - } - - if (controllers[index] && isFunction(controllers[index].onunload)) { - controllers[index].onunload(event) - } - - return checkPrevented(component, root, index, isPrevented) - } - - function removeRootElement(root, index) { - roots.splice(index, 1) - controllers.splice(index, 1) - components.splice(index, 1) - reset(root) - nodeCache.splice(getCellCacheKey(root), 1) - } - - var redrawing = false - m.redraw = function (force) { - if (redrawing) return - redrawing = true - if (force) forcing = true - - try { - // lastRedrawId is a positive number if a second redraw is requested - // before the next animation frame - // lastRedrawId is null if it's the first redraw and not an event - // handler - if (lastRedrawId && !force) { - // when setTimeout: only reschedule redraw if time between now - // and previous redraw is bigger than a frame, otherwise keep - // currently scheduled timeout - // when rAF: always reschedule redraw - if ($requestAnimationFrame === global.requestAnimationFrame || - new Date() - lastRedrawCallTime > FRAME_BUDGET) { - if (lastRedrawId > 0) $cancelAnimationFrame(lastRedrawId) - lastRedrawId = $requestAnimationFrame(redraw, FRAME_BUDGET) - } - } else { - redraw() - lastRedrawId = $requestAnimationFrame(function () { - lastRedrawId = null - }, FRAME_BUDGET) - } - } finally { - redrawing = forcing = false - } - } - - m.redraw.strategy = m.prop() - function redraw() { - if (computePreRedrawHook) { - computePreRedrawHook() - computePreRedrawHook = null - } - forEach(roots, function (root, i) { - var component = components[i] - if (controllers[i]) { - var args = [controllers[i]] - m.render(root, - component.view ? component.view(controllers[i], args) : "") - } - }) - // after rendering within a routed context, we need to scroll back to - // the top, and fetch the document title for history.pushState - if (computePostRedrawHook) { - computePostRedrawHook() - computePostRedrawHook = null - } - lastRedrawId = null - lastRedrawCallTime = new Date() - m.redraw.strategy("diff") - } - - function endFirstComputation() { - if (m.redraw.strategy() === "none") { - pendingRequests-- - m.redraw.strategy("diff") - } else { - m.endComputation() - } - } - - m.withAttr = function (prop, withAttrCallback, callbackThis) { - return function (e) { - e = e || window.event - /* eslint-disable no-invalid-this */ - var currentTarget = e.currentTarget || this - var _this = callbackThis || this - /* eslint-enable no-invalid-this */ - var target = prop in currentTarget ? - currentTarget[prop] : - currentTarget.getAttribute(prop) - withAttrCallback.call(_this, target) - } - } - - // routing - var modes = {pathname: "", hash: "#", search: "?"} - var redirect = noop - var isDefaultRoute = false - var routeParams, currentRoute - - m.route = function (root, arg1, arg2, vdom) { // eslint-disable-line - // m.route() - if (arguments.length === 0) return currentRoute - // m.route(el, defaultRoute, routes) - if (arguments.length === 3 && isString(arg1)) { - redirect = function (source) { - var path = currentRoute = normalizeRoute(source) - if (!routeByValue(root, arg2, path)) { - if (isDefaultRoute) { - throw new Error("Ensure the default route matches " + - "one of the routes defined in m.route") - } - - isDefaultRoute = true - m.route(arg1, true) - isDefaultRoute = false - } - } - - var listener = m.route.mode === "hash" ? - "onhashchange" : - "onpopstate" - - global[listener] = function () { - var path = $location[m.route.mode] - if (m.route.mode === "pathname") path += $location.search - if (currentRoute !== normalizeRoute(path)) redirect(path) - } - - computePreRedrawHook = setScroll - global[listener]() - - return - } - - // config: m.route - if (root.addEventListener || root.attachEvent) { - var base = m.route.mode !== "pathname" ? $location.pathname : "" - root.href = base + modes[m.route.mode] + vdom.attrs.href - if (root.addEventListener) { - root.removeEventListener("click", routeUnobtrusive) - root.addEventListener("click", routeUnobtrusive) - } else { - root.detachEvent("onclick", routeUnobtrusive) - root.attachEvent("onclick", routeUnobtrusive) - } - - return - } - // m.route(route, params, shouldReplaceHistoryEntry) - if (isString(root)) { - var oldRoute = currentRoute - currentRoute = root - - var args = arg1 || {} - var queryIndex = currentRoute.indexOf("?") - var params - - if (queryIndex > -1) { - params = parseQueryString(currentRoute.slice(queryIndex + 1)) - } else { - params = {} - } - - for (var i in args) { - if (hasOwn.call(args, i)) { - params[i] = args[i] - } - } - - var querystring = buildQueryString(params) - var currentPath - - if (queryIndex > -1) { - currentPath = currentRoute.slice(0, queryIndex) - } else { - currentPath = currentRoute - } - - if (querystring) { - currentRoute = currentPath + - (currentPath.indexOf("?") === -1 ? "?" : "&") + - querystring - } - - var replaceHistory = - (arguments.length === 3 ? arg2 : arg1) === true || - oldRoute === root - - if (global.history.pushState) { - var method = replaceHistory ? "replaceState" : "pushState" - computePreRedrawHook = setScroll - computePostRedrawHook = function () { - try { - global.history[method](null, $document.title, - modes[m.route.mode] + currentRoute) - } catch (err) { - // In the event of a pushState or replaceState failure, - // fallback to a standard redirect. This is specifically - // to address a Safari security error when attempting to - // call pushState more than 100 times. - $location[m.route.mode] = currentRoute - } - } - redirect(modes[m.route.mode] + currentRoute) - } else { - $location[m.route.mode] = currentRoute - redirect(modes[m.route.mode] + currentRoute) - } - } - } - - m.route.param = function (key) { - if (!routeParams) { - throw new Error("You must call m.route(element, defaultRoute, " + - "routes) before calling m.route.param()") - } - - if (!key) { - return routeParams - } - - return routeParams[key] - } - - m.route.mode = "search" - - function normalizeRoute(route) { - return route.slice(modes[m.route.mode].length) - } - - function routeByValue(root, router, path) { - routeParams = {} - - var queryStart = path.indexOf("?") - if (queryStart !== -1) { - routeParams = parseQueryString( - path.substr(queryStart + 1, path.length)) - path = path.substr(0, queryStart) - } - - // Get all routes and check if there's - // an exact match for the current path - var keys = Object.keys(router) - var index = keys.indexOf(path) - - if (index !== -1){ - m.mount(root, router[keys [index]]) - return true - } - - for (var route in router) { - if (hasOwn.call(router, route)) { - if (route === path) { - m.mount(root, router[route]) - return true - } - - var matcher = new RegExp("^" + route - .replace(/:[^\/]+?\.{3}/g, "(.*?)") - .replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$") - - if (matcher.test(path)) { - /* eslint-disable no-loop-func */ - path.replace(matcher, function () { - var keys = route.match(/:[^\/]+/g) || [] - var values = [].slice.call(arguments, 1, -2) - forEach(keys, function (key, i) { - routeParams[key.replace(/:|\./g, "")] = - decodeURIComponent(values[i]) - }) - m.mount(root, router[route]) - }) - /* eslint-enable no-loop-func */ - return true - } - } - } - } - - function routeUnobtrusive(e) { - e = e || event - if (e.ctrlKey || e.metaKey || e.shiftKey || e.which === 2) return - - if (e.preventDefault) { - e.preventDefault() - } else { - e.returnValue = false - } - - var currentTarget = e.currentTarget || e.srcElement - var args - - if (m.route.mode === "pathname" && currentTarget.search) { - args = parseQueryString(currentTarget.search.slice(1)) - } else { - args = {} - } - - while (currentTarget && !/a/i.test(currentTarget.nodeName)) { - currentTarget = currentTarget.parentNode - } - - // clear pendingRequests because we want an immediate route change - pendingRequests = 0 - m.route(currentTarget[m.route.mode] - .slice(modes[m.route.mode].length), args) - } - - function setScroll() { - if (m.route.mode !== "hash" && $location.hash) { - $location.hash = $location.hash - } else { - global.scrollTo(0, 0) - } - } - - function buildQueryString(object, prefix) { - var duplicates = {} - var str = [] - - for (var prop in object) { - if (hasOwn.call(object, prop)) { - var key = prefix ? prefix + "[" + prop + "]" : prop - var value = object[prop] - - if (value === null) { - str.push(encodeURIComponent(key)) - } else if (isObject(value)) { - str.push(buildQueryString(value, key)) - } else if (isArray(value)) { - var keys = [] - duplicates[key] = duplicates[key] || {} - /* eslint-disable no-loop-func */ - forEach(value, function (item) { - /* eslint-enable no-loop-func */ - if (!duplicates[key][item]) { - duplicates[key][item] = true - keys.push(encodeURIComponent(key) + "=" + - encodeURIComponent(item)) - } - }) - str.push(keys.join("&")) - } else if (value !== undefined) { - str.push(encodeURIComponent(key) + "=" + - encodeURIComponent(value)) - } - } - } - - return str.join("&") - } - - function parseQueryString(str) { - if (str === "" || str == null) return {} - if (str.charAt(0) === "?") str = str.slice(1) - - var pairs = str.split("&") - var params = {} - - forEach(pairs, function (string) { - var pair = string.split("=") - var key = decodeURIComponent(pair[0]) - var value = pair.length === 2 ? decodeURIComponent(pair[1]) : null - if (params[key] != null) { - if (!isArray(params[key])) params[key] = [params[key]] - params[key].push(value) - } - else params[key] = value - }) - - return params - } - - m.route.buildQueryString = buildQueryString - m.route.parseQueryString = parseQueryString - - function reset(root) { - var cacheKey = getCellCacheKey(root) - clear(root.childNodes, cellCache[cacheKey]) - cellCache[cacheKey] = undefined - } - - m.deferred = function () { - var deferred = new Deferred() - deferred.promise = propify(deferred.promise) - return deferred - } - - function propify(promise, initialValue) { - var prop = m.prop(initialValue) - promise.then(prop) - prop.then = function (resolve, reject) { - return propify(promise.then(resolve, reject), initialValue) - } - - prop.catch = prop.then.bind(null, null) - return prop - } - // Promiz.mithril.js | Zolmeister | MIT - // a modified version of Promiz.js, which does not conform to Promises/A+ - // for two reasons: - // - // 1) `then` callbacks are called synchronously (because setTimeout is too - // slow, and the setImmediate polyfill is too big - // - // 2) throwing subclasses of Error cause the error to be bubbled up instead - // of triggering rejection (because the spec does not account for the - // important use case of default browser error handling, i.e. message w/ - // line number) - - var RESOLVING = 1 - var REJECTING = 2 - var RESOLVED = 3 - var REJECTED = 4 - - function Deferred(onSuccess, onFailure) { - var self = this - var state = 0 - var promiseValue = 0 - var next = [] - - self.promise = {} - - self.resolve = function (value) { - if (!state) { - promiseValue = value - state = RESOLVING - - fire() - } - - return self - } - - self.reject = function (value) { - if (!state) { - promiseValue = value - state = REJECTING - - fire() - } - - return self - } - - self.promise.then = function (onSuccess, onFailure) { - var deferred = new Deferred(onSuccess, onFailure) - - if (state === RESOLVED) { - deferred.resolve(promiseValue) - } else if (state === REJECTED) { - deferred.reject(promiseValue) - } else { - next.push(deferred) - } - - return deferred.promise - } - - function finish(type) { - state = type || REJECTED - next.map(function (deferred) { - if (state === RESOLVED) { - deferred.resolve(promiseValue) - } else { - deferred.reject(promiseValue) - } - }) - } - - function thennable(then, success, failure, notThennable) { - if (((promiseValue != null && isObject(promiseValue)) || - isFunction(promiseValue)) && isFunction(then)) { - try { - // count protects against abuse calls from spec checker - var count = 0 - then.call(promiseValue, function (value) { - if (count++) return - promiseValue = value - success() - }, function (value) { - if (count++) return - promiseValue = value - failure() - }) - } catch (e) { - m.deferred.onerror(e) - promiseValue = e - failure() - } - } else { - notThennable() - } - } - - function fire() { - // check if it's a thenable - var then - try { - then = promiseValue && promiseValue.then - } catch (e) { - m.deferred.onerror(e) - promiseValue = e - state = REJECTING - return fire() - } - - if (state === REJECTING) { - m.deferred.onerror(promiseValue) - } - - thennable(then, function () { - state = RESOLVING - fire() - }, function () { - state = REJECTING - fire() - }, function () { - try { - if (state === RESOLVING && isFunction(onSuccess)) { - promiseValue = onSuccess(promiseValue) - } else if (state === REJECTING && isFunction(onFailure)) { - promiseValue = onFailure(promiseValue) - state = RESOLVING - } - } catch (e) { - m.deferred.onerror(e) - promiseValue = e - return finish() - } - - if (promiseValue === self) { - promiseValue = TypeError() - finish() - } else { - thennable(then, function () { - finish(RESOLVED) - }, finish, function () { - finish(state === RESOLVING && RESOLVED) - }) - } - }) - } - } - - m.deferred.onerror = function (e) { - if (type.call(e) === "[object Error]" && - !/ Error/.test(e.constructor.toString())) { - pendingRequests = 0 - throw e - } - } - - m.sync = function (args) { - var deferred = m.deferred() - var outstanding = args.length - var results = [] - var method = "resolve" - - function synchronizer(pos, resolved) { - return function (value) { - results[pos] = value - if (!resolved) method = "reject" - if (--outstanding === 0) { - deferred.promise(results) - deferred[method](results) - } - return value - } - } - - if (args.length > 0) { - forEach(args, function (arg, i) { - arg.then(synchronizer(i, true), synchronizer(i, false)) - }) - } else { - deferred.resolve([]) - } - - return deferred.promise - } - - function identity(value) { return value } - - function handleJsonp(options) { - var callbackKey = options.callbackName || "mithril_callback_" + - new Date().getTime() + "_" + - (Math.round(Math.random() * 1e16)).toString(36) - - var script = $document.createElement("script") - - global[callbackKey] = function (resp) { - script.parentNode.removeChild(script) - options.onload({ - type: "load", - target: { - responseText: resp - } - }) - global[callbackKey] = undefined - } - - script.onerror = function () { - script.parentNode.removeChild(script) - - options.onerror({ - type: "error", - target: { - status: 500, - responseText: JSON.stringify({ - error: "Error making jsonp request" - }) - } - }) - global[callbackKey] = undefined - - return false - } - - script.onload = function () { - return false - } - - script.src = options.url + - (options.url.indexOf("?") > 0 ? "&" : "?") + - (options.callbackKey ? options.callbackKey : "callback") + - "=" + callbackKey + - "&" + buildQueryString(options.data || {}) - - $document.body.appendChild(script) - } - - function createXhr(options) { - var xhr = new global.XMLHttpRequest() - xhr.open(options.method, options.url, true, options.user, - options.password) - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - if (xhr.status >= 200 && xhr.status < 300) { - options.onload({type: "load", target: xhr}) - } else { - options.onerror({type: "error", target: xhr}) - } - } - } - - if (options.serialize === JSON.stringify && - options.data && - options.method !== "GET") { - xhr.setRequestHeader("Content-Type", - "application/json; charset=utf-8") - } - - if (options.deserialize === JSON.parse) { - xhr.setRequestHeader("Accept", "application/json, text/*") - } - - if (isFunction(options.config)) { - var maybeXhr = options.config(xhr, options) - if (maybeXhr != null) xhr = maybeXhr - } - - var data = options.method === "GET" || !options.data ? "" : options.data - - if (data && !isString(data) && data.constructor !== global.FormData) { - throw new Error("Request data should be either be a string or " + - "FormData. Check the `serialize` option in `m.request`") - } - - xhr.send(data) - return xhr - } - - function ajax(options) { - if (options.dataType && options.dataType.toLowerCase() === "jsonp") { - return handleJsonp(options) - } else { - return createXhr(options) - } - } - - function bindData(options, data, serialize) { - if (options.method === "GET" && options.dataType !== "jsonp") { - var prefix = options.url.indexOf("?") < 0 ? "?" : "&" - var querystring = buildQueryString(data) - options.url += (querystring ? prefix + querystring : "") - } else { - options.data = serialize(data) - } - } - - function parameterizeUrl(url, data) { - if (data) { - url = url.replace(/:[a-z]\w+/gi, function (token){ - var key = token.slice(1) - var value = data[key] || token - delete data[key] - return value - }) - } - return url - } - - m.request = function (options) { - if (options.background !== true) m.startComputation() - var deferred = new Deferred() - var isJSONP = options.dataType && - options.dataType.toLowerCase() === "jsonp" - - var serialize, deserialize, extract - - if (isJSONP) { - serialize = options.serialize = - deserialize = options.deserialize = identity - - extract = function (jsonp) { return jsonp.responseText } - } else { - serialize = options.serialize = options.serialize || JSON.stringify - - deserialize = options.deserialize = - options.deserialize || JSON.parse - extract = options.extract || function (xhr) { - if (xhr.responseText.length || deserialize !== JSON.parse) { - return xhr.responseText - } else { - return null - } - } - } - - options.method = (options.method || "GET").toUpperCase() - options.url = parameterizeUrl(options.url, options.data) - bindData(options, options.data, serialize) - options.onload = options.onerror = function (ev) { - try { - ev = ev || event - var response = deserialize(extract(ev.target, options)) - if (ev.type === "load") { - if (options.unwrapSuccess) { - response = options.unwrapSuccess(response, ev.target) - } - - if (isArray(response) && options.type) { - forEach(response, function (res, i) { - response[i] = new options.type(res) - }) - } else if (options.type) { - response = new options.type(response) - } - - deferred.resolve(response) - } else { - if (options.unwrapError) { - response = options.unwrapError(response, ev.target) - } - - deferred.reject(response) - } - } catch (e) { - deferred.reject(e) - m.deferred.onerror(e) - } finally { - if (options.background !== true) m.endComputation() - } - } - - ajax(options) - deferred.promise = propify(deferred.promise, options.initialValue) - return deferred.promise - } - - return m -}); // eslint-disable-line +new function() { + + function Vnode(tag, key, attrs0, children, text, dom) { + return {tag: tag, key: key, attrs: attrs0, children: children, text: text, dom: dom, domSize: undefined, state: {}, events: undefined, instance: undefined, skip: false} + } + Vnode.normalize = function(node) { + if (Array.isArray(node)) return Vnode("[", undefined, undefined, Vnode.normalizeChildren(node), undefined, undefined) + if (node != null && typeof node !== "object") return Vnode("#", undefined, undefined, node === false ? "" : node, undefined, undefined) + return node + } + Vnode.normalizeChildren = function normalizeChildren(children) { + for (var i = 0; i < children.length; i++) { + children[i] = Vnode.normalize(children[i]) + } + return children + } + var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g + var selectorCache = {} + function hyperscript(selector) { + if (selector == null || typeof selector !== "string" && typeof selector.view !== "function") { + throw Error("The selector must be either a string or a component."); + } + if (typeof selector === "string" && selectorCache[selector] === undefined) { + var match, tag, classes = [], attributes = {} + while (match = selectorParser.exec(selector)) { + var type = match[1], value = match[2] + if (type === "" && value !== "") tag = value + else if (type === "#") attributes.id = value + else if (type === ".") classes.push(value) + else if (match[3][0] === "[") { + var attrValue = match[6] + if (attrValue) attrValue = attrValue.replace(/\\(["'])/g, "$1").replace(/\\\\/g, "\\") + if (match[4] === "class") classes.push(attrValue) + else attributes[match[4]] = attrValue || true + } + } + if (classes.length > 0) attributes.className = classes.join(" ") + selectorCache[selector] = function(attrs, children) { + var hasAttrs = false, childList, text + var className = attrs.className || attrs.class + for (var key in attributes) attrs[key] = attributes[key] + if (className !== undefined) { + if (attrs.class !== undefined) { + attrs.class = undefined + attrs.className = className + } + if (attributes.className !== undefined) attrs.className = attributes.className + " " + className + } + for (var key in attrs) { + if (key !== "key") { + hasAttrs = true + break + } + } + if (Array.isArray(children) && children.length == 1 && children[0] != null && children[0].tag === "#") text = children[0].children + else childList = children + return Vnode(tag || "div", attrs.key, hasAttrs ? attrs : undefined, childList, text, undefined) + } + } + var attrs, children, childrenIndex + if (arguments[1] == null || typeof arguments[1] === "object" && arguments[1].tag === undefined && !Array.isArray(arguments[1])) { + attrs = arguments[1] + childrenIndex = 2 + } + else childrenIndex = 1 + if (arguments.length === childrenIndex + 1) { + children = Array.isArray(arguments[childrenIndex]) ? arguments[childrenIndex] : [arguments[childrenIndex]] + } + else { + children = [] + for (var i = childrenIndex; i < arguments.length; i++) children.push(arguments[i]) + } + if (typeof selector === "string") return selectorCache[selector](attrs || {}, Vnode.normalizeChildren(children)) + return Vnode(selector, attrs && attrs.key, attrs || {}, Vnode.normalizeChildren(children), undefined, undefined) + } + hyperscript.trust = function(html) { + if (html == null) html = "" + return Vnode("<", undefined, undefined, html, undefined, undefined) + } + hyperscript.fragment = function(attrs1, children) { + return Vnode("[", attrs1.key, attrs1, Vnode.normalizeChildren(children), undefined, undefined) + } + var m = hyperscript + /** @constructor */ + 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) + 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 { + 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) { + rejectCurrent(e) + } + } + } + function executeOnce(then) { + var runs = 0 + function run(fn) { + return function(value) { + if (runs++ > 0) return + fn(value) + } + } + var onerror = run(rejectCurrent) + try {then(run(resolveCurrent), onerror)} catch (e) {onerror(e)} + } + executeOnce(executor) + } + PromisePolyfill.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 (typeof instance.retry === "function" && state === instance.state) instance.retry() + } + var resolveNext, rejectNext + 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 + } + PromisePolyfill.prototype.catch = function(onRejection) { + return this.then(null, onRejection) + } + PromisePolyfill.resolve = function(value) { + if (value instanceof PromisePolyfill) return value + return new PromisePolyfill(function(resolve) {resolve(value)}) + } + PromisePolyfill.reject = function(value) { + return new PromisePolyfill(function(resolve, reject) {reject(value)}) + } + 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++) { + (function(i) { + function consume(value) { + count++ + values[i] = value + if (count === total) resolve(values) + } + if (list[i] != null && (typeof list[i] === "object" || typeof list[i] === "function") && typeof list[i].then === "function") { + list[i].then(consume, reject) + } + else consume(list[i]) + })(i) + } + }) + } + PromisePolyfill.race = function(list) { + return new PromisePolyfill(function(resolve, reject) { + for (var i = 0; i < list.length; i++) { + list[i].then(resolve, reject) + } + }) + } + if (typeof window !== "undefined") { + if (typeof window.Promise === "undefined") window.Promise = PromisePolyfill + var PromisePolyfill = window.Promise + } else if (typeof global !== "undefined") { + if (typeof global.Promise === "undefined") global.Promise = PromisePolyfill + var PromisePolyfill = global.Promise + } else { + } + var buildQueryString = function(object) { + if (Object.prototype.toString.call(object) !== "[object Object]") return "" + var args = [] + for (var key0 in object) { + destructure(key0, object[key0]) + } + return args.join("&") + function destructure(key0, value) { + if (Array.isArray(value)) { + for (var i = 0; i < value.length; i++) { + destructure(key0 + "[" + i + "]", value[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) + (value != null && value !== "" ? "=" + encodeURIComponent(value) : "")) + } + } + var _8 = function($window, Promise) { + var callbackCount = 0 + var oncompletion + function setCompletionCallback(callback) {oncompletion = callback} + function finalizer() { + var count = 0 + function complete() {if (--count === 0 && typeof oncompletion === "function") oncompletion()} + return function finalize(promise0) { + var then0 = promise0.then + promise0.then = function() { + count++ + var next = then0.apply(promise0, arguments) + next.then(complete, function(e) { + complete() + if (count === 0) throw e + }) + return finalize(next) + } + return promise0 + } + } + function normalize(args, extra) { + if (typeof args === "string") { + var url = args + args = extra || {} + if (args.url == null) args.url = url + } + return args + } + function request(args, extra) { + var finalize = finalizer() + args = normalize(args, extra) + var promise0 = new Promise(function(resolve, reject) { + 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" + 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) + if (useBody) args.data = args.serialize(args.data) + else args.url = assemble(args.url, args.data) + var xhr = new $window.XMLHttpRequest() + xhr.open(args.method, args.url, typeof args.async === "boolean" ? args.async : true, typeof args.user === "string" ? args.user : undefined, typeof args.password === "string" ? args.password : undefined) + if (args.serialize === JSON.stringify && useBody) { + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8") + } + if (args.deserialize === deserialize) { + xhr.setRequestHeader("Accept", "application/json, text/*") + } + if (args.withCredentials) xhr.withCredentials = args.withCredentials + for (var key in args.headers) if ({}.hasOwnProperty.call(args.headers, key)) { + xhr.setRequestHeader(key, args.headers[key]) + } + if (typeof args.config === "function") xhr = args.config(xhr, args) || xhr + xhr.onreadystatechange = function() { + // Don't throw errors on xhr.abort(). XMLHttpRequests ends up in a state of + // xhr.status == 0 and xhr.readyState == 4 if aborted after open, but before completion. + if (xhr.status && xhr.readyState === 4) { + 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) { + resolve(cast(args.type, response)) + } + else { + var error = new Error(xhr.responseText) + for (var key in response) error[key] = response[key] + reject(error) + } + } + catch (e) { + reject(e) + } + } + } + if (useBody && (args.data != null)) xhr.send(args.data) + else xhr.send() + }) + return args.background === true ? promise0 : finalize(promise0) + } + function jsonp(args, extra) { + var finalize = finalizer() + args = normalize(args, extra) + var promise0 = 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) + resolve(cast(args.type, data)) + delete $window[callbackName] + } + script.onerror = function() { + script.parentNode.removeChild(script) + reject(new Error("JSONP request failed")) + delete $window[callbackName] + } + if (args.data == null) args.data = {} + args.url = interpolate(args.url, args.data) + args.data[args.callbackKey || "callback"] = callbackName + script.src = assemble(args.url, args.data) + $window.document.documentElement.appendChild(script) + }) + return args.background === true? promise0 : finalize(promise0) + } + function interpolate(url, data) { + if (data == null) return url + var tokens = url.match(/:[^\/]+/gi) || [] + for (var i = 0; i < tokens.length; i++) { + var key = tokens[i].slice(1) + if (data[key] != null) { + url = url.replace(tokens[i], data[key]) + } + } + return url + } + function assemble(url, data) { + var querystring = buildQueryString(data) + if (querystring !== "") { + var prefix = url.indexOf("?") < 0 ? "?" : "&" + url += prefix + querystring + } + return url + } + function deserialize(data) { + try {return data !== "" ? JSON.parse(data) : null} + catch (e) {throw new Error(data)} + } + function extract(xhr) {return xhr.responseText} + function cast(type0, data) { + if (typeof type0 === "function") { + if (Array.isArray(data)) { + for (var i = 0; i < data.length; i++) { + data[i] = new type0(data[i]) + } + } + else return new type0(data) + } + return data + } + return {request: request, jsonp: jsonp, setCompletionCallback: setCompletionCallback} + } + var requestService = _8(window, PromisePolyfill) + var coreRenderer = function($window) { + var $doc = $window.document + var $emptyFragment = $doc.createDocumentFragment() + var onevent + function setEventCallback(callback) {return onevent = callback} + //create + function createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) { + for (var i = start; i < end; i++) { + var vnode = vnodes[i] + if (vnode != null) { + createNode(parent, vnode, hooks, ns, nextSibling) + } + } + } + function createNode(parent, vnode, hooks, ns, nextSibling) { + var tag = vnode.tag + if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) + if (typeof tag === "string") { + switch (tag) { + case "#": return createText(parent, vnode, nextSibling) + case "<": return createHTML(parent, vnode, nextSibling) + case "[": return createFragment(parent, vnode, hooks, ns, nextSibling) + default: return createElement(parent, vnode, hooks, ns, nextSibling) + } + } + else return createComponent(parent, vnode, hooks, ns, nextSibling) + } + function createText(parent, vnode, nextSibling) { + vnode.dom = $doc.createTextNode(vnode.children) + insertNode(parent, vnode.dom, nextSibling) + return vnode.dom + } + function createHTML(parent, vnode, nextSibling) { + var match1 = vnode.children.match(/^\s*?<(\w+)/im) || [] + var parent1 = {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(parent1) + temp.innerHTML = vnode.children + vnode.dom = temp.firstChild + vnode.domSize = temp.childNodes.length + var fragment = $doc.createDocumentFragment() + var child + while (child = temp.firstChild) { + fragment.appendChild(child) + } + insertNode(parent, fragment, nextSibling) + return fragment + } + function createFragment(parent, vnode, hooks, ns, nextSibling) { + var fragment = $doc.createDocumentFragment() + if (vnode.children != null) { + var children = vnode.children + createNodes(fragment, children, 0, children.length, hooks, null, ns) + } + vnode.dom = fragment.firstChild + vnode.domSize = fragment.childNodes.length + insertNode(parent, fragment, nextSibling) + return fragment + } + function createElement(parent, vnode, hooks, ns, nextSibling) { + var tag = vnode.tag + switch (vnode.tag) { + case "svg": ns = "http://www.w3.org/2000/svg"; break + case "math": ns = "http://www.w3.org/1998/Math/MathML"; break + } + var attrs2 = vnode.attrs + var is = attrs2 && attrs2.is + var element = ns ? + is ? $doc.createElementNS(ns, tag, {is: is}) : $doc.createElementNS(ns, tag) : + is ? $doc.createElement(tag, {is: is}) : $doc.createElement(tag) + vnode.dom = element + if (attrs2 != null) { + setAttrs(vnode, attrs2, ns) + } + insertNode(parent, element, nextSibling) + if (vnode.attrs != null && vnode.attrs.contenteditable != null) { + setContentEditable(vnode) + } + else { + if (vnode.text != null) { + if (vnode.text !== "") element.textContent = vnode.text + else vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)] + } + if (vnode.children != null) { + var children = vnode.children + createNodes(element, children, 0, children.length, hooks, null, ns) + setLateAttrs(vnode) + } + } + return element + } + function createComponent(parent, vnode, hooks, ns, nextSibling) { + vnode.state = Object.create(vnode.tag) + var view = vnode.tag.view + if (view.reentrantLock != null) return $emptyFragment + view.reentrantLock = true + initLifecycle(vnode.tag, vnode, hooks) + vnode.instance = Vnode.normalize(view.call(vnode.state, vnode)) + view.reentrantLock = null + if (vnode.instance != null) { + if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as arguments") + var element = createNode(parent, vnode.instance, hooks, ns, nextSibling) + vnode.dom = vnode.instance.dom + vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0 + insertNode(parent, element, nextSibling) + return element + } + else { + vnode.domSize = 0 + return $emptyFragment + } + } + //update + function updateNodes(parent, old, vnodes, recycling, hooks, nextSibling, ns) { + if (old === vnodes || old == null && vnodes == null) return + else if (old == null) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, undefined) + else if (vnodes == null) removeNodes(old, 0, old.length, vnodes) + else { + if (old.length === vnodes.length) { + var isUnkeyed = false + for (var i = 0; i < vnodes.length; i++) { + if (vnodes[i] != null && old[i] != null) { + isUnkeyed = vnodes[i].key == null && old[i].key == null + break + } + } + if (isUnkeyed) { + for (var i = 0; i < old.length; i++) { + if (old[i] === vnodes[i]) continue + else if (old[i] == null && vnodes[i] != null) createNode(parent, vnodes[i], hooks, ns, getNextSibling(old, i + 1, nextSibling)) + else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes) + else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), false, ns) + } + return + } + } + recycling = recycling || isRecyclable(old, vnodes) + if (recycling) old = old.concat(old.pool) + + 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) oldStart++ + else if (v == null) start++ + else if (o.key === v.key) { + oldStart++, start++ + 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) oldEnd-- + else if (v == null) start++ + else if (o.key === v.key) { + 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 + } + } + while (oldEnd >= oldStart && end >= start) { + var o = old[oldEnd], v = vnodes[end] + if (o === v && !recycling) oldEnd--, end-- + else if (o == null) oldEnd-- + else if (v == null) end-- + else if (o.key === v.key) { + 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 (!map) map = getKeyMap(old, oldEnd) + if (v != null) { + var oldIndex = map[v.key] + if (oldIndex != null) { + var movable = old[oldIndex] + 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(parent, v, hooks, undefined, nextSibling) + nextSibling = dom + } + } + end-- + } + if (end < start) break + } + createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns) + removeNodes(old, oldStart, oldEnd + 1, vnodes) + } + } + function updateNode(parent, old, vnode, hooks, nextSibling, recycling, ns) { + var oldTag = old.tag, tag = vnode.tag + if (oldTag === tag) { + vnode.state = old.state + vnode.events = old.events + if (shouldUpdate(vnode, old)) return + if (vnode.attrs != null) { + updateLifecycle(vnode.attrs, vnode, hooks, recycling) + } + if (typeof oldTag === "string") { + switch (oldTag) { + case "#": updateText(old, vnode); break + case "<": updateHTML(parent, old, vnode, nextSibling); break + case "[": updateFragment(parent, old, vnode, recycling, hooks, nextSibling, ns); break + default: updateElement(old, vnode, recycling, hooks, ns) + } + } + else updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) + } + else { + removeNode(old, null) + createNode(parent, vnode, hooks, ns, nextSibling) + } + } + function updateText(old, vnode) { + if (old.children.toString() !== vnode.children.toString()) { + old.dom.nodeValue = vnode.children + } + vnode.dom = old.dom + } + function updateHTML(parent, old, vnode, nextSibling) { + if (old.children !== vnode.children) { + toFragment(old) + createHTML(parent, vnode, nextSibling) + } + else vnode.dom = old.dom, vnode.domSize = old.domSize + } + function updateFragment(parent, old, vnode, recycling, hooks, nextSibling, ns) { + updateNodes(parent, old.children, vnode.children, recycling, hooks, nextSibling, ns) + var domSize = 0, children = vnode.children + vnode.dom = null + if (children != null) { + for (var i = 0; i < children.length; i++) { + var child = children[i] + if (child != null && child.dom != null) { + if (vnode.dom == null) vnode.dom = child.dom + domSize += child.domSize || 1 + } + } + if (domSize !== 1) vnode.domSize = domSize + } + } + function updateElement(old, vnode, recycling, hooks, ns) { + var element = vnode.dom = old.dom + switch (vnode.tag) { + case "svg": ns = "http://www.w3.org/2000/svg"; break + case "math": ns = "http://www.w3.org/1998/Math/MathML"; break + } + if (vnode.tag === "textarea") { + if (vnode.attrs == null) vnode.attrs = {} + if (vnode.text != null) { + vnode.attrs.value = vnode.text //FIXME handle0 multiple children + vnode.text = undefined + } + } + updateAttrs(vnode, old.attrs, vnode.attrs, ns) + if (vnode.attrs != null && vnode.attrs.contenteditable != null) { + setContentEditable(vnode) + } + else if (old.text != null && vnode.text != null && vnode.text !== "") { + if (old.text.toString() !== vnode.text.toString()) old.dom.firstChild.nodeValue = vnode.text + } + else { + if (old.text != null) old.children = [Vnode("#", undefined, undefined, old.text, undefined, old.dom.firstChild)] + if (vnode.text != null) vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)] + updateNodes(element, old.children, vnode.children, recycling, hooks, null, 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) createNode(parent, 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 + } + else if (old.instance != null) { + removeNode(old.instance, null) + vnode.dom = undefined + vnode.domSize = 0 + } + else { + vnode.dom = old.dom + vnode.domSize = old.domSize + } + } + function isRecyclable(old, vnodes) { + if (old.pool != null && Math.abs(old.pool.length - vnodes.length) <= Math.abs(old.length - vnodes.length)) { + var oldChildrenLength = old[0] && old[0].children && old[0].children.length || 0 + var poolChildrenLength = old.pool[0] && old.pool[0].children && old.pool[0].children.length || 0 + var vnodesChildrenLength = vnodes[0] && vnodes[0].children && vnodes[0].children.length || 0 + if (Math.abs(poolChildrenLength - vnodesChildrenLength) <= Math.abs(oldChildrenLength - vnodesChildrenLength)) { + return true + } + } + return false + } + function getKeyMap(vnodes, end) { + var map = {}, i = 0 + for (var i = 0; i < end; i++) { + var vnode = vnodes[i] + if (vnode != null) { + var key2 = vnode.key + if (key2 != null) map[key2] = i + } + } + return map + } + function toFragment(vnode) { + var count0 = vnode.domSize + if (count0 != null || vnode.dom == null) { + var fragment = $doc.createDocumentFragment() + if (count0 > 0) { + var dom = vnode.dom + while (--count0) fragment.appendChild(dom.nextSibling) + fragment.insertBefore(dom, fragment.firstChild) + } + return fragment + } + else return vnode.dom + } + function getNextSibling(vnodes, i, nextSibling) { + for (; i < vnodes.length; i++) { + if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom + } + return nextSibling + } + function insertNode(parent, dom, nextSibling) { + if (nextSibling && nextSibling.parentNode) parent.insertBefore(dom, nextSibling) + else parent.appendChild(dom) + } + function setContentEditable(vnode) { + var children = vnode.children + if (children != null && children.length === 1 && children[0].tag === "<") { + var content = children[0].children + if (vnode.dom.innerHTML !== content) vnode.dom.innerHTML = content + } + else if (vnode.text != null || children != null && children.length !== 0) throw new Error("Child node of a contenteditable must be trusted") + } + //remove + function removeNodes(vnodes, start, end, context) { + for (var i = start; i < end; i++) { + var vnode = vnodes[i] + if (vnode != null) { + if (vnode.skip) vnode.skip = false + else removeNode(vnode, context) + } + } + } + function removeNode(vnode, context) { + var expected = 1, called = 0 + if (vnode.attrs && vnode.attrs.onbeforeremove) { + var result = vnode.attrs.onbeforeremove.call(vnode.state, vnode) + if (result != null && typeof result.then === "function") { + expected++ + result.then(continuation, continuation) + } + } + if (typeof vnode.tag !== "string" && vnode.tag.onbeforeremove) { + var result = vnode.tag.onbeforeremove.call(vnode.state, vnode) + if (result != null && typeof result.then === "function") { + expected++ + result.then(continuation, continuation) + } + } + continuation() + function continuation() { + if (++called === expected) { + onremove(vnode) + if (vnode.dom) { + var count0 = vnode.domSize || 1 + if (count0 > 1) { + var dom = vnode.dom + while (--count0) { + removeNodeFromDOM(dom.nextSibling) + } + } + removeNodeFromDOM(vnode.dom) + if (context != null && vnode.domSize == null && !hasIntegrationMethods(vnode.attrs) && typeof vnode.tag === "string") { //TODO test custom elements + if (!context.pool) context.pool = [vnode] + else context.pool.push(vnode) + } + } + } + } + } + function removeNodeFromDOM(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) + if (typeof vnode.tag !== "string" && vnode.tag.onremove) vnode.tag.onremove.call(vnode.state, vnode) + if (vnode.instance != null) onremove(vnode.instance) + else { + var children = vnode.children + if (Array.isArray(children)) { + for (var i = 0; i < children.length; i++) { + var child = children[i] + if (child != null) onremove(child) + } + } + } + } + //attrs2 + function setAttrs(vnode, attrs2, ns) { + for (var key2 in attrs2) { + setAttr(vnode, key2, null, attrs2[key2], ns) + } + } + function setAttr(vnode, key2, old, value, ns) { + var element = vnode.dom + if (key2 === "key" || key2 === "is" || (old === value && !isFormAttribute(vnode, key2)) && typeof value !== "object" || typeof value === "undefined" || isLifecycleMethod(key2)) return + var nsLastIndex = key2.indexOf(":") + if (nsLastIndex > -1 && key2.substr(0, nsLastIndex) === "xlink") { + element.setAttributeNS("http://www.w3.org/1999/xlink", key2.slice(nsLastIndex + 1), value) + } + else if (key2[0] === "o" && key2[1] === "n" && typeof value === "function") updateEvent(vnode, key2, value) + else if (key2 === "style") updateStyle(element, old, value) + else if (key2 in element && !isAttribute(key2) && ns === undefined && !isCustomElement(vnode)) { + //setting input[value] to same value by typing on focused element moves cursor to end in Chrome + if (vnode.tag === "input" && key2 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return + //setting select[value] to same value while having select open blinks select dropdown in Chrome + if (vnode.tag === "select" && key2 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return + //setting option[value] to same value while having select open blinks select dropdown in Chrome + if (vnode.tag === "option" && key2 === "value" && vnode.dom.value === value) return + element[key2] = value + } + else { + if (typeof value === "boolean") { + if (value) element.setAttribute(key2, "") + else element.removeAttribute(key2) + } + else element.setAttribute(key2 === "className" ? "class" : key2, value) + } + } + function setLateAttrs(vnode) { + var attrs2 = vnode.attrs + if (vnode.tag === "select" && attrs2 != null) { + if ("value" in attrs2) setAttr(vnode, "value", null, attrs2.value, undefined) + if ("selectedIndex" in attrs2) setAttr(vnode, "selectedIndex", null, attrs2.selectedIndex, undefined) + } + } + function updateAttrs(vnode, old, attrs2, ns) { + if (attrs2 != null) { + for (var key2 in attrs2) { + setAttr(vnode, key2, old && old[key2], attrs2[key2], ns) + } + } + if (old != null) { + for (var key2 in old) { + if (attrs2 == null || !(key2 in attrs2)) { + if (key2 === "className") key2 = "class" + if (key2[0] === "o" && key2[1] === "n" && !isLifecycleMethod(key2)) updateEvent(vnode, key2, undefined) + else if (key2 !== "key") vnode.dom.removeAttribute(key2) + } + } + } + } + function isFormAttribute(vnode, attr) { + return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && vnode.dom === $doc.activeElement + } + function isLifecycleMethod(attr) { + return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "onbeforeupdate" + } + function isAttribute(attr) { + return attr === "href" || attr === "list" || attr === "form" || attr === "width" || attr === "height"// || attr === "type" + } + function isCustomElement(vnode){ + return vnode.attrs.is || vnode.tag.indexOf("-") > -1 + } + function hasIntegrationMethods(source) { + return source != null && (source.oncreate || source.onupdate || source.onbeforeremove || source.onremove) + } + //style + function updateStyle(element, old, style) { + if (old === style) element.style.cssText = "", old = null + if (style == null) element.style.cssText = "" + else if (typeof style === "string") element.style.cssText = style + else { + if (typeof old === "string") element.style.cssText = "" + for (var key2 in style) { + element.style[key2] = style[key2] + } + if (old != null && typeof old !== "string") { + for (var key2 in old) { + if (!(key2 in style)) element.style[key2] = "" + } + } + } + } + //event + function updateEvent(vnode, key2, value) { + var element = vnode.dom + var callback = typeof onevent !== "function" ? value : function(e) { + var result = value.call(element, e) + onevent.call(element, e) + return result + } + if (key2 in element) element[key2] = typeof value === "function" ? callback : null + else { + var eventName = key2.slice(2) + if (vnode.events === undefined) vnode.events = {} + if (vnode.events[key2] === callback) return + if (vnode.events[key2] != null) element.removeEventListener(eventName, vnode.events[key2], false) + if (typeof value === "function") { + vnode.events[key2] = callback + element.addEventListener(eventName, vnode.events[key2], false) + } + } + } + //lifecycle + function initLifecycle(source, vnode, hooks) { + if (typeof source.oninit === "function") source.oninit.call(vnode.state, vnode) + if (typeof source.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode)) + } + function updateLifecycle(source, vnode, hooks, recycling) { + if (recycling) initLifecycle(source, vnode, hooks) + else if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode)) + } + function shouldUpdate(vnode, old) { + var forceVnodeUpdate, forceComponentUpdate + if (vnode.attrs != null && typeof vnode.attrs.onbeforeupdate === "function") forceVnodeUpdate = vnode.attrs.onbeforeupdate.call(vnode.state, vnode, old) + if (typeof vnode.tag !== "string" && typeof vnode.tag.onbeforeupdate === "function") forceComponentUpdate = vnode.tag.onbeforeupdate.call(vnode.state, vnode, old) + if (!(forceVnodeUpdate === undefined && forceComponentUpdate === undefined) && !forceVnodeUpdate && !forceComponentUpdate) { + vnode.dom = old.dom + vnode.domSize = old.domSize + vnode.instance = old.instance + return true + } + return false + } + 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 active = $doc.activeElement + // First time0 rendering into a node clears it out + if (dom.vnodes == null) dom.textContent = "" + if (!Array.isArray(vnodes)) vnodes = [vnodes] + updateNodes(dom, dom.vnodes, Vnode.normalizeChildren(vnodes), false, hooks, null, undefined) + dom.vnodes = vnodes + for (var i = 0; i < hooks.length; i++) hooks[i]() + if ($doc.activeElement !== active) active.focus() + } + return {render: render, setEventCallback: setEventCallback} + } + function throttle(callback) { + //60fps translates to 16.6ms, round it down since setTimeout requires int + var time = 16 + var last = 0, pending = null + var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout + return function() { + var now = Date.now() + if (last === 0 || now - last >= time) { + last = now + callback() + } + else if (pending === null) { + pending = timeout(function() { + pending = null + callback() + last = Date.now() + }, time - (now - last)) + } + } + } + var _11 = function($window) { + var renderService = coreRenderer($window) + renderService.setEventCallback(function(e) { + if (e.redraw !== false) redraw() + }) + var callbacks = [] + function subscribe(key1, callback) { + unsubscribe(key1) + callbacks.push(key1, throttle(callback)) + } + function unsubscribe(key1) { + var index = callbacks.indexOf(key1) + if (index > -1) callbacks.splice(index, 2) + } + function redraw() { + for (var i = 1; i < callbacks.length; i += 2) { + callbacks[i]() + } + } + return {subscribe: subscribe, unsubscribe: unsubscribe, redraw: redraw, render: renderService.render} + } + var redrawService = _11(window) + requestService.setCompletionCallback(redrawService.redraw) + var _16 = function(redrawService0) { + return function(root, component) { + if (component === null) { + redrawService0.render(root, []) + redrawService0.unsubscribe(root) + return + } + + if (component.view == null) throw new Error("m.mount(element, component) expects a component, not a vnode") + + var run0 = function() { + redrawService0.render(root, Vnode(component)) + } + redrawService0.subscribe(root, run0) + redrawService0.redraw() + } + } + m.mount = _16(redrawService) + var Promise = PromisePolyfill + var parseQueryString = function(string) { + if (string === "" || string == null) return {} + if (string.charAt(0) === "?") string = string.slice(1) + var entries = string.split("&"), data0 = {}, counters = {} + for (var i = 0; i < entries.length; i++) { + var entry = entries[i].split("=") + var key5 = decodeURIComponent(entry[0]) + var value = entry.length === 2 ? decodeURIComponent(entry[1]) : "" + if (value === "true") value = true + else if (value === "false") value = false + var levels = key5.split(/\]\[?|\[/) + var cursor = data0 + if (key5.indexOf("[") > -1) levels.pop() + for (var j = 0; j < levels.length; j++) { + var level = levels[j], nextLevel = levels[j + 1] + var isNumber = nextLevel == "" || !isNaN(parseInt(nextLevel, 10)) + var isValue = j === levels.length - 1 + if (level === "") { + var key5 = levels.slice(0, j).join() + if (counters[key5] == null) counters[key5] = 0 + level = counters[key5]++ + } + if (cursor[level] == null) { + cursor[level] = isValue ? value : isNumber ? [] : {} + } + cursor = cursor[level] + } + } + return data0 + } + var coreRouter = function($window) { + var supportsPushState = typeof $window.history.pushState === "function" + var callAsync0 = typeof setImmediate === "function" ? setImmediate : setTimeout + function normalize1(fragment0) { + var data = $window.location[fragment0].replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent) + if (fragment0 === "pathname" && data[0] !== "/") data = "/" + data + return data + } + var asyncId + function debounceAsync(callback0) { + return function() { + if (asyncId != null) return + asyncId = callAsync0(function() { + asyncId = null + callback0() + }) + } + } + function parsePath(path, queryData, hashData) { + var queryIndex = path.indexOf("?") + var hashIndex = path.indexOf("#") + var pathEnd = queryIndex > -1 ? queryIndex : hashIndex > -1 ? hashIndex : path.length + if (queryIndex > -1) { + var queryEnd = hashIndex > -1 ? hashIndex : path.length + var queryParams = parseQueryString(path.slice(queryIndex + 1, queryEnd)) + for (var key4 in queryParams) queryData[key4] = queryParams[key4] + } + if (hashIndex > -1) { + var hashParams = parseQueryString(path.slice(hashIndex + 1)) + for (var key4 in hashParams) hashData[key4] = hashParams[key4] + } + return path.slice(0, pathEnd) + } + var router = {prefix: "#!"} + router.getPath = function() { + var type2 = router.prefix.charAt(0) + switch (type2) { + case "#": return normalize1("hash").slice(router.prefix.length) + case "?": return normalize1("search").slice(router.prefix.length) + normalize1("hash") + default: return normalize1("pathname").slice(router.prefix.length) + normalize1("search") + normalize1("hash") + } + } + router.setPath = function(path, data, options) { + var queryData = {}, hashData = {} + path = parsePath(path, queryData, hashData) + if (data != null) { + for (var key4 in data) queryData[key4] = data[key4] + path = path.replace(/:([^\/]+)/g, function(match2, token) { + delete queryData[token] + return data[token] + }) + } + var query = buildQueryString(queryData) + if (query) path += "?" + query + var hash = buildQueryString(hashData) + if (hash) path += "#" + hash + if (supportsPushState) { + var state = options ? options.state : null + var title = options ? options.title : null + $window.onpopstate() + if (options && options.replace) $window.history.replaceState(state, title, router.prefix + path) + else $window.history.pushState(state, title, router.prefix + path) + } + else $window.location.href = router.prefix + path + } + router.defineRoutes = function(routes, resolve, reject) { + function resolveRoute() { + var path = router.getPath() + var params = {} + var pathname = parsePath(path, params, params) + var state = $window.history.state + if (state != null) { + for (var k in state) params[k] = state[k] + } + for (var route0 in routes) { + var matcher = new RegExp("^" + route0.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$") + if (matcher.test(pathname)) { + pathname.replace(matcher, function() { + var keys = route0.match(/:[^\/]+/g) || [] + var values = [].slice.call(arguments, 1, -2) + for (var i = 0; i < keys.length; i++) { + params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i]) + } + resolve(routes[route0], params, path, route0) + }) + return + } + } + reject(path, params) + } + if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute) + else if (router.prefix.charAt(0) === "#") $window.onhashchange = resolveRoute + resolveRoute() + } + return router + } + var _20 = function($window, redrawService0) { + var routeService = coreRouter($window) + var identity = function(v) {return v} + var render1, component, attrs3, currentPath, lastUpdate + var route = function(root, defaultRoute, routes) { + if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined") + var run1 = function() { + if (render1 != null) redrawService0.render(root, render1(Vnode(component, attrs3.key, attrs3))) + } + var bail = function(path) { + if (path !== defaultRoute) routeService.setPath(defaultRoute, null, {replace: true}) + else throw new Error("Could not resolve default route " + defaultRoute) + } + routeService.defineRoutes(routes, function(payload, params, path) { + var update = lastUpdate = function(routeResolver, comp) { + if (update !== lastUpdate) return + component = comp != null && typeof comp.view === "function" ? comp : "div", attrs3 = params, currentPath = path, lastUpdate = null + render1 = (routeResolver.render || identity).bind(routeResolver) + run1() + } + if (payload.view) update({}, payload) + else { + if (payload.onmatch) { + Promise.resolve(payload.onmatch(params, path)).then(function(resolved) { + update(payload, resolved) + }, bail) + } + else update(payload, "div") + } + }, bail) + redrawService0.subscribe(root, run1) + } + route.set = function(path, data, options) { + if (lastUpdate != null) options = {replace: true} + lastUpdate = null + routeService.setPath(path, data, options) + } + route.get = function() {return currentPath} + route.prefix = function(prefix0) {routeService.prefix = prefix0} + route.link = function(vnode1) { + vnode1.dom.setAttribute("href", routeService.prefix + vnode1.attrs.href) + vnode1.dom.onclick = function(e) { + if (e.ctrlKey || e.metaKey || e.shiftKey || e.which === 2) return + e.preventDefault() + e.redraw = false + var href = this.getAttribute("href") + if (href.indexOf(routeService.prefix) === 0) href = href.slice(routeService.prefix.length) + route.set(href, undefined, undefined) + } + } + route.param = function(key3) { + if(typeof attrs3 !== "undefined" && typeof key3 !== "undefined") return attrs3[key3] + return attrs3 + } + return route + } + m.route = _20(window, redrawService) + m.withAttr = function(attrName, callback1, context) { + return function(e) { + callback1.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName)) + } + } + var _28 = coreRenderer(window) + m.render = _28.render + m.redraw = redrawService.redraw + m.request = requestService.request + m.jsonp = requestService.jsonp + m.parseQueryString = parseQueryString + m.buildQueryString = buildQueryString + m.version = "1.0.1" + m.vnode = Vnode + if (typeof module !== "undefined") module["exports"] = m + else window.m = m +} \ No newline at end of file