/* Ractive.js v0.7.3 Sat Apr 25 2015 13:52:38 GMT-0400 (EDT) - commit da40f81c660ba2f09c45a09a9c20fdd34ee36d80 http://ractivejs.org http://twitter.com/RactiveJS Released under the MIT License. */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.Ractive = factory() }(this, function () { 'use strict'; var TEMPLATE_VERSION = 3; var defaultOptions = { // render placement: el: void 0, append: false, // template: template: { v: TEMPLATE_VERSION, t: [] }, // parse: // TODO static delimiters? preserveWhitespace: false, sanitize: false, stripComments: true, delimiters: ["{{", "}}"], tripleDelimiters: ["{{{", "}}}"], interpolate: false, // data & binding: data: {}, computed: {}, magic: false, modifyArrays: true, adapt: [], isolated: false, twoway: true, lazy: false, // transitions: noIntro: false, transitionsEnabled: true, complete: void 0, // css: css: null, noCssTransform: false }; var config_defaults = defaultOptions; // These are a subset of the easing equations found at // https://raw.github.com/danro/easing-js - license info // follows: // -------------------------------------------------- // easing.js v0.5.4 // Generic set of easing functions with AMD support // https://github.com/danro/easing-js // This code may be freely distributed under the MIT license // http://danro.mit-license.org/ // -------------------------------------------------- // All functions adapted from Thomas Fuchs & Jeremy Kahn // Easing Equations (c) 2003 Robert Penner, BSD license // https://raw.github.com/danro/easing-js/master/LICENSE // -------------------------------------------------- // In that library, the functions named easeIn, easeOut, and // easeInOut below are named easeInCubic, easeOutCubic, and // (you guessed it) easeInOutCubic. // // You can add additional easing functions to this list, and they // will be globally available. var static_easing = { linear: function (pos) { return pos; }, easeIn: function (pos) { return Math.pow(pos, 3); }, easeOut: function (pos) { return Math.pow(pos - 1, 3) + 1; }, easeInOut: function (pos) { if ((pos /= 0.5) < 1) { return 0.5 * Math.pow(pos, 3); } return 0.5 * (Math.pow(pos - 2, 3) + 2); } }; /*global console, navigator */ var isClient, isJsdom, hasConsole, environment__magic, namespaces, svg, vendors; isClient = typeof document === "object"; isJsdom = typeof navigator !== "undefined" && /jsDom/.test(navigator.appName); hasConsole = typeof console !== "undefined" && typeof console.warn === "function" && typeof console.warn.apply === "function"; try { Object.defineProperty({}, "test", { value: 0 }); environment__magic = true; } catch (e) { environment__magic = false; } namespaces = { html: "http://www.w3.org/1999/xhtml", mathml: "http://www.w3.org/1998/Math/MathML", svg: "http://www.w3.org/2000/svg", xlink: "http://www.w3.org/1999/xlink", xml: "http://www.w3.org/XML/1998/namespace", xmlns: "http://www.w3.org/2000/xmlns/" }; if (typeof document === "undefined") { svg = false; } else { svg = document && document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"); } vendors = ["o", "ms", "moz", "webkit"]; var createElement, matches, dom__div, methodNames, unprefixed, prefixed, dom__i, j, makeFunction; // Test for SVG support if (!svg) { createElement = function (type, ns) { if (ns && ns !== namespaces.html) { throw "This browser does not support namespaces other than http://www.w3.org/1999/xhtml. The most likely cause of this error is that you're trying to render SVG in an older browser. See http://docs.ractivejs.org/latest/svg-and-older-browsers for more information"; } return document.createElement(type); }; } else { createElement = function (type, ns) { if (!ns || ns === namespaces.html) { return document.createElement(type); } return document.createElementNS(ns, type); }; } function getElement(input) { var output; if (!input || typeof input === "boolean") { return; } if (typeof window === "undefined" || !document || !input) { return null; } // We already have a DOM node - no work to do. (Duck typing alert!) if (input.nodeType) { return input; } // Get node from string if (typeof input === "string") { // try ID first output = document.getElementById(input); // then as selector, if possible if (!output && document.querySelector) { output = document.querySelector(input); } // did it work? if (output && output.nodeType) { return output; } } // If we've been given a collection (jQuery, Zepto etc), extract the first item if (input[0] && input[0].nodeType) { return input[0]; } return null; } if (!isClient) { matches = null; } else { dom__div = createElement("div"); methodNames = ["matches", "matchesSelector"]; makeFunction = function (methodName) { return function (node, selector) { return node[methodName](selector); }; }; dom__i = methodNames.length; while (dom__i-- && !matches) { unprefixed = methodNames[dom__i]; if (dom__div[unprefixed]) { matches = makeFunction(unprefixed); } else { j = vendors.length; while (j--) { prefixed = vendors[dom__i] + unprefixed.substr(0, 1).toUpperCase() + unprefixed.substring(1); if (dom__div[prefixed]) { matches = makeFunction(prefixed); break; } } } } // IE8... if (!matches) { matches = function (node, selector) { var nodes, parentNode, i; parentNode = node.parentNode; if (!parentNode) { // empty dummy
dom__div.innerHTML = ""; parentNode = dom__div; node = node.cloneNode(); dom__div.appendChild(node); } nodes = parentNode.querySelectorAll(selector); i = nodes.length; while (i--) { if (nodes[i] === node) { return true; } } return false; }; } } function detachNode(node) { if (node && typeof node.parentNode !== "unknown" && node.parentNode) { node.parentNode.removeChild(node); } return node; } function safeToStringValue(value) { return value == null || !value.toString ? "" : value; } var legacy = null; var create, defineProperty, defineProperties; try { Object.defineProperty({}, "test", { value: 0 }); if (isClient) { Object.defineProperty(document.createElement("div"), "test", { value: 0 }); } defineProperty = Object.defineProperty; } catch (err) { // Object.defineProperty doesn't exist, or we're in IE8 where you can // only use it with DOM objects (what were you smoking, MSFT?) defineProperty = function (obj, prop, desc) { obj[prop] = desc.value; }; } try { try { Object.defineProperties({}, { test: { value: 0 } }); } catch (err) { // TODO how do we account for this? noMagic = true; throw err; } if (isClient) { Object.defineProperties(createElement("div"), { test: { value: 0 } }); } defineProperties = Object.defineProperties; } catch (err) { defineProperties = function (obj, props) { var prop; for (prop in props) { if (props.hasOwnProperty(prop)) { defineProperty(obj, prop, props[prop]); } } }; } try { Object.create(null); create = Object.create; } catch (err) { // sigh create = (function () { var F = function () {}; return function (proto, props) { var obj; if (proto === null) { return {}; } F.prototype = proto; obj = new F(); if (props) { Object.defineProperties(obj, props); } return obj; }; })(); } function utils_object__extend(target) { for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { sources[_key - 1] = arguments[_key]; } var prop, source; while (source = sources.shift()) { for (prop in source) { if (hasOwn.call(source, prop)) { target[prop] = source[prop]; } } } return target; } function fillGaps(target) { for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { sources[_key - 1] = arguments[_key]; } sources.forEach(function (s) { for (var key in s) { if (s.hasOwnProperty(key) && !(key in target)) { target[key] = s[key]; } } }); return target; } var hasOwn = Object.prototype.hasOwnProperty; // thanks, http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/ var is__toString = Object.prototype.toString, arrayLikePattern = /^\[object (?:Array|FileList)\]$/; function isArray(thing) { return is__toString.call(thing) === "[object Array]"; } function isArrayLike(obj) { return arrayLikePattern.test(is__toString.call(obj)); } function isEqual(a, b) { if (a === null && b === null) { return true; } if (typeof a === "object" || typeof b === "object") { return false; } return a === b; } function is__isNumeric(thing) { return !isNaN(parseFloat(thing)) && isFinite(thing); } function isObject(thing) { return thing && is__toString.call(thing) === "[object Object]"; } var noop = function () {}; /* global console */ var alreadyWarned = {}, log, printWarning, welcome; if (hasConsole) { (function () { var welcomeIntro = ["%cRactive.js %c0.7.3 %cin debug mode, %cmore...", "color: rgb(114, 157, 52); font-weight: normal;", "color: rgb(85, 85, 85); font-weight: normal;", "color: rgb(85, 85, 85); font-weight: normal;", "color: rgb(82, 140, 224); font-weight: normal; text-decoration: underline;"]; var welcomeMessage = "You're running Ractive 0.7.3 in debug mode - messages will be printed to the console to help you fix problems and optimise your application.\n\nTo disable debug mode, add this line at the start of your app:\n Ractive.DEBUG = false;\n\nTo disable debug mode when your app is minified, add this snippet:\n Ractive.DEBUG = /unminified/.test(function(){/*unminified*/});\n\nGet help and support:\n http://docs.ractivejs.org\n http://stackoverflow.com/questions/tagged/ractivejs\n http://groups.google.com/forum/#!forum/ractive-js\n http://twitter.com/ractivejs\n\nFound a bug? Raise an issue:\n https://github.com/ractivejs/ractive/issues\n\n"; welcome = function () { var hasGroup = !!console.groupCollapsed; console[hasGroup ? "groupCollapsed" : "log"].apply(console, welcomeIntro); console.log(welcomeMessage); if (hasGroup) { console.groupEnd(welcomeIntro); } welcome = noop; }; printWarning = function (message, args) { welcome(); // extract information about the instance this message pertains to, if applicable if (typeof args[args.length - 1] === "object") { var options = args.pop(); var ractive = options ? options.ractive : null; if (ractive) { // if this is an instance of a component that we know the name of, add // it to the message var _name = undefined; if (ractive.component && (_name = ractive.component.name)) { message = "<" + _name + "> " + message; } var node = undefined; if (node = options.node || ractive.fragment && ractive.fragment.rendered && ractive.find("*")) { args.push(node); } } } console.warn.apply(console, ["%cRactive.js: %c" + message, "color: rgb(114, 157, 52);", "color: rgb(85, 85, 85);"].concat(args)); }; log = function () { console.log.apply(console, arguments); }; })(); } else { printWarning = log = welcome = noop; } function format(message, args) { return message.replace(/%s/g, function () { return args.shift(); }); } function fatal(message) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } message = format(message, args); throw new Error(message); } function logIfDebug() { if (_Ractive.DEBUG) { log.apply(null, arguments); } } function warn(message) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } message = format(message, args); printWarning(message, args); } function warnOnce(message) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } message = format(message, args); if (alreadyWarned[message]) { return; } alreadyWarned[message] = true; printWarning(message, args); } function warnIfDebug() { if (_Ractive.DEBUG) { warn.apply(null, arguments); } } function warnOnceIfDebug() { if (_Ractive.DEBUG) { warnOnce.apply(null, arguments); } } // Error messages that are used (or could be) in multiple places var badArguments = "Bad arguments"; var noRegistryFunctionReturn = "A function was specified for \"%s\" %s, but no %s was returned"; var missingPlugin = function (name, type) { return "Missing \"" + name + "\" " + type + " plugin. You may need to download a plugin via http://docs.ractivejs.org/latest/plugins#" + type + "s"; }; function findInViewHierarchy(registryName, ractive, name) { var instance = findInstance(registryName, ractive, name); return instance ? instance[registryName][name] : null; } function findInstance(registryName, ractive, name) { while (ractive) { if (name in ractive[registryName]) { return ractive; } if (ractive.isolated) { return null; } ractive = ractive.parent; } } var interpolate = function (from, to, ractive, type) { if (from === to) { return snap(to); } if (type) { var interpol = findInViewHierarchy("interpolators", ractive, type); if (interpol) { return interpol(from, to) || snap(to); } fatal(missingPlugin(type, "interpolator")); } return static_interpolators.number(from, to) || static_interpolators.array(from, to) || static_interpolators.object(from, to) || snap(to); }; var shared_interpolate = interpolate; function snap(to) { return function () { return to; }; } var interpolators = { number: function (from, to) { var delta; if (!is__isNumeric(from) || !is__isNumeric(to)) { return null; } from = +from; to = +to; delta = to - from; if (!delta) { return function () { return from; }; } return function (t) { return from + t * delta; }; }, array: function (from, to) { var intermediate, interpolators, len, i; if (!isArray(from) || !isArray(to)) { return null; } intermediate = []; interpolators = []; i = len = Math.min(from.length, to.length); while (i--) { interpolators[i] = shared_interpolate(from[i], to[i]); } // surplus values - don't interpolate, but don't exclude them either for (i = len; i < from.length; i += 1) { intermediate[i] = from[i]; } for (i = len; i < to.length; i += 1) { intermediate[i] = to[i]; } return function (t) { var i = len; while (i--) { intermediate[i] = interpolators[i](t); } return intermediate; }; }, object: function (from, to) { var properties, len, interpolators, intermediate, prop; if (!isObject(from) || !isObject(to)) { return null; } properties = []; intermediate = {}; interpolators = {}; for (prop in from) { if (hasOwn.call(from, prop)) { if (hasOwn.call(to, prop)) { properties.push(prop); interpolators[prop] = shared_interpolate(from[prop], to[prop]); } else { intermediate[prop] = from[prop]; } } } for (prop in to) { if (hasOwn.call(to, prop) && !hasOwn.call(from, prop)) { intermediate[prop] = to[prop]; } } len = properties.length; return function (t) { var i = len, prop; while (i--) { prop = properties[i]; intermediate[prop] = interpolators[prop](t); } return intermediate; }; } }; var static_interpolators = interpolators; // This function takes a keypath such as 'foo.bar.baz', and returns // all the variants of that keypath that include a wildcard in place // of a key, such as 'foo.bar.*', 'foo.*.baz', 'foo.*.*' and so on. // These are then checked against the dependants map (ractive.viewmodel.depsMap) // to see if any pattern observers are downstream of one or more of // these wildcard keypaths (e.g. 'foo.bar.*.status') var utils_getPotentialWildcardMatches = getPotentialWildcardMatches; var starMaps = {}; function getPotentialWildcardMatches(keypath) { var keys, starMap, mapper, i, result, wildcardKeypath; keys = keypath.split("."); if (!(starMap = starMaps[keys.length])) { starMap = getStarMap(keys.length); } result = []; mapper = function (star, i) { return star ? "*" : keys[i]; }; i = starMap.length; while (i--) { wildcardKeypath = starMap[i].map(mapper).join("."); if (!result.hasOwnProperty(wildcardKeypath)) { result.push(wildcardKeypath); result[wildcardKeypath] = true; } } return result; } // This function returns all the possible true/false combinations for // a given number - e.g. for two, the possible combinations are // [ true, true ], [ true, false ], [ false, true ], [ false, false ]. // It does so by getting all the binary values between 0 and e.g. 11 function getStarMap(num) { var ones = "", max, binary, starMap, mapper, i, j, l, map; if (!starMaps[num]) { starMap = []; while (ones.length < num) { ones += 1; } max = parseInt(ones, 2); mapper = function (digit) { return digit === "1"; }; for (i = 0; i <= max; i += 1) { binary = i.toString(2); while (binary.length < num) { binary = "0" + binary; } map = []; l = binary.length; for (j = 0; j < l; j++) { map.push(mapper(binary[j])); } starMap[i] = map; } starMaps[num] = starMap; } return starMaps[num]; } var refPattern = /\[\s*(\*|[0-9]|[1-9][0-9]+)\s*\]/g; var patternPattern = /\*/; var keypathCache = {}; var Keypath = function (str) { var keys = str.split("."); this.str = str; if (str[0] === "@") { this.isSpecial = true; this.value = decodeKeypath(str); } this.firstKey = keys[0]; this.lastKey = keys.pop(); this.isPattern = patternPattern.test(str); this.parent = str === "" ? null : getKeypath(keys.join(".")); this.isRoot = !str; }; Keypath.prototype = { equalsOrStartsWith: function (keypath) { return keypath === this || this.startsWith(keypath); }, join: function (str) { return getKeypath(this.isRoot ? String(str) : this.str + "." + str); }, replace: function (oldKeypath, newKeypath) { if (this === oldKeypath) { return newKeypath; } if (this.startsWith(oldKeypath)) { return newKeypath === null ? newKeypath : getKeypath(this.str.replace(oldKeypath.str + ".", newKeypath.str + ".")); } }, startsWith: function (keypath) { if (!keypath) { // TODO under what circumstances does this happen? return false; } return keypath && this.str.substr(0, keypath.str.length + 1) === keypath.str + "."; }, toString: function () { throw new Error("Bad coercion"); }, valueOf: function () { throw new Error("Bad coercion"); }, wildcardMatches: function () { return this._wildcardMatches || (this._wildcardMatches = utils_getPotentialWildcardMatches(this.str)); } }; function assignNewKeypath(target, property, oldKeypath, newKeypath) { var existingKeypath = target[property]; if (existingKeypath && (existingKeypath.equalsOrStartsWith(newKeypath) || !existingKeypath.equalsOrStartsWith(oldKeypath))) { return; } target[property] = existingKeypath ? existingKeypath.replace(oldKeypath, newKeypath) : newKeypath; return true; } function decodeKeypath(keypath) { var value = keypath.slice(2); if (keypath[1] === "i") { return is__isNumeric(value) ? +value : value; } else { return value; } } function getKeypath(str) { if (str == null) { return str; } // TODO it *may* be worth having two versions of this function - one where // keypathCache inherits from null, and one for IE8. Depends on how // much of an overhead hasOwnProperty is - probably negligible if (!keypathCache.hasOwnProperty(str)) { keypathCache[str] = new Keypath(str); } return keypathCache[str]; } function getMatchingKeypaths(ractive, keypath) { var keys, key, matchingKeypaths; keys = keypath.str.split("."); matchingKeypaths = [rootKeypath]; while (key = keys.shift()) { if (key === "*") { // expand to find all valid child keypaths matchingKeypaths = matchingKeypaths.reduce(expand, []); } else { if (matchingKeypaths[0] === rootKeypath) { // first key matchingKeypaths[0] = getKeypath(key); } else { matchingKeypaths = matchingKeypaths.map(concatenate(key)); } } } return matchingKeypaths; function expand(matchingKeypaths, keypath) { var wrapper, value, keys; if (keypath.isRoot) { keys = [].concat(Object.keys(ractive.viewmodel.data), Object.keys(ractive.viewmodel.mappings), Object.keys(ractive.viewmodel.computations)); } else { wrapper = ractive.viewmodel.wrapped[keypath.str]; value = wrapper ? wrapper.get() : ractive.viewmodel.get(keypath); keys = value ? Object.keys(value) : null; } if (keys) { keys.forEach(function (key) { if (key !== "_ractive" || !isArray(value)) { matchingKeypaths.push(keypath.join(key)); } }); } return matchingKeypaths; } } function concatenate(key) { return function (keypath) { return keypath.join(key); }; } function normalise(ref) { return ref ? ref.replace(refPattern, ".$1") : ""; } var rootKeypath = getKeypath(""); var shared_add = add; var shared_add__errorMessage = "Cannot add to a non-numeric value"; function add(root, keypath, d) { if (typeof keypath !== "string" || !is__isNumeric(d)) { throw new Error("Bad arguments"); } var value = undefined, changes = undefined; if (/\*/.test(keypath)) { changes = {}; getMatchingKeypaths(root, getKeypath(normalise(keypath))).forEach(function (keypath) { var value = root.viewmodel.get(keypath); if (!is__isNumeric(value)) { throw new Error(shared_add__errorMessage); } changes[keypath.str] = value + d; }); return root.set(changes); } value = root.get(keypath); if (!is__isNumeric(value)) { throw new Error(shared_add__errorMessage); } return root.set(keypath, +value + d); } var prototype_add = Ractive$add; function Ractive$add(keypath, d) { return shared_add(this, keypath, d === undefined ? 1 : +d); } var requestAnimationFrame; // If window doesn't exist, we don't need requestAnimationFrame if (typeof window === "undefined") { requestAnimationFrame = null; } else { // https://gist.github.com/paulirish/1579671 (function (vendors, lastTime, window) { var x, setTimeout; if (window.requestAnimationFrame) { return; } for (x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"]; } if (!window.requestAnimationFrame) { setTimeout = window.setTimeout; window.requestAnimationFrame = function (callback) { var currTime, timeToCall, id; currTime = Date.now(); timeToCall = Math.max(0, 16 - (currTime - lastTime)); id = setTimeout(function () { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; } })(vendors, 0, window); requestAnimationFrame = window.requestAnimationFrame; } var rAF = requestAnimationFrame; var getTime; if (typeof window !== "undefined" && window.performance && typeof window.performance.now === "function") { getTime = function () { return window.performance.now(); }; } else { getTime = function () { return Date.now(); }; } var utils_getTime = getTime; var deprecations = { construct: { deprecated: "beforeInit", replacement: "onconstruct" }, render: { deprecated: "init", message: "The \"init\" method has been deprecated " + "and will likely be removed in a future release. " + "You can either use the \"oninit\" method which will fire " + "only once prior to, and regardless of, any eventual ractive " + "instance being rendered, or if you need to access the " + "rendered DOM, use \"onrender\" instead. " + "See http://docs.ractivejs.org/latest/migrating for more information." }, complete: { deprecated: "complete", replacement: "oncomplete" } }; function Hook(event) { this.event = event; this.method = "on" + event; this.deprecate = deprecations[event]; } Hook.prototype.fire = function (ractive, arg) { function call(method) { if (ractive[method]) { arg ? ractive[method](arg) : ractive[method](); return true; } } call(this.method); if (!ractive[this.method] && this.deprecate && call(this.deprecate.deprecated)) { if (this.deprecate.message) { warnIfDebug(this.deprecate.message); } else { warnIfDebug("The method \"%s\" has been deprecated in favor of \"%s\" and will likely be removed in a future release. See http://docs.ractivejs.org/latest/migrating for more information.", this.deprecate.deprecated, this.deprecate.replacement); } } arg ? ractive.fire(this.event, arg) : ractive.fire(this.event); }; var hooks_Hook = Hook; function addToArray(array, value) { var index = array.indexOf(value); if (index === -1) { array.push(value); } } function arrayContains(array, value) { for (var i = 0, c = array.length; i < c; i++) { if (array[i] == value) { return true; } } return false; } function arrayContentsMatch(a, b) { var i; if (!isArray(a) || !isArray(b)) { return false; } if (a.length !== b.length) { return false; } i = a.length; while (i--) { if (a[i] !== b[i]) { return false; } } return true; } function ensureArray(x) { if (typeof x === "string") { return [x]; } if (x === undefined) { return []; } return x; } function lastItem(array) { return array[array.length - 1]; } function removeFromArray(array, member) { var index = array.indexOf(member); if (index !== -1) { array.splice(index, 1); } } function toArray(arrayLike) { var array = [], i = arrayLike.length; while (i--) { array[i] = arrayLike[i]; } return array; } var _Promise, PENDING = {}, FULFILLED = {}, REJECTED = {}; if (typeof Promise === "function") { // use native Promise _Promise = Promise; } else { _Promise = function (callback) { var fulfilledHandlers = [], rejectedHandlers = [], state = PENDING, result, dispatchHandlers, makeResolver, fulfil, reject, promise; makeResolver = function (newState) { return function (value) { if (state !== PENDING) { return; } result = value; state = newState; dispatchHandlers = makeDispatcher(state === FULFILLED ? fulfilledHandlers : rejectedHandlers, result); // dispatch onFulfilled and onRejected handlers asynchronously wait(dispatchHandlers); }; }; fulfil = makeResolver(FULFILLED); reject = makeResolver(REJECTED); try { callback(fulfil, reject); } catch (err) { reject(err); } promise = { // `then()` returns a Promise - 2.2.7 then: function (onFulfilled, onRejected) { var promise2 = new _Promise(function (fulfil, reject) { var processResolutionHandler = function (handler, handlers, forward) { // 2.2.1.1 if (typeof handler === "function") { handlers.push(function (p1result) { var x; try { x = handler(p1result); utils_Promise__resolve(promise2, x, fulfil, reject); } catch (err) { reject(err); } }); } else { // Forward the result of promise1 to promise2, if resolution handlers // are not given handlers.push(forward); } }; // 2.2 processResolutionHandler(onFulfilled, fulfilledHandlers, fulfil); processResolutionHandler(onRejected, rejectedHandlers, reject); if (state !== PENDING) { // If the promise has resolved already, dispatch the appropriate handlers asynchronously wait(dispatchHandlers); } }); return promise2; } }; promise["catch"] = function (onRejected) { return this.then(null, onRejected); }; return promise; }; _Promise.all = function (promises) { return new _Promise(function (fulfil, reject) { var result = [], pending, i, processPromise; if (!promises.length) { fulfil(result); return; } processPromise = function (promise, i) { if (promise && typeof promise.then === "function") { promise.then(function (value) { result[i] = value; --pending || fulfil(result); }, reject); } else { result[i] = promise; --pending || fulfil(result); } }; pending = i = promises.length; while (i--) { processPromise(promises[i], i); } }); }; _Promise.resolve = function (value) { return new _Promise(function (fulfil) { fulfil(value); }); }; _Promise.reject = function (reason) { return new _Promise(function (fulfil, reject) { reject(reason); }); }; } var utils_Promise = _Promise; // TODO use MutationObservers or something to simulate setImmediate function wait(callback) { setTimeout(callback, 0); } function makeDispatcher(handlers, result) { return function () { var handler; while (handler = handlers.shift()) { handler(result); } }; } function utils_Promise__resolve(promise, x, fulfil, reject) { // Promise Resolution Procedure var then; // 2.3.1 if (x === promise) { throw new TypeError("A promise's fulfillment handler cannot return the same promise"); } // 2.3.2 if (x instanceof _Promise) { x.then(fulfil, reject); } // 2.3.3 else if (x && (typeof x === "object" || typeof x === "function")) { try { then = x.then; // 2.3.3.1 } catch (e) { reject(e); // 2.3.3.2 return; } // 2.3.3.3 if (typeof then === "function") { var called, resolvePromise, rejectPromise; resolvePromise = function (y) { if (called) { return; } called = true; utils_Promise__resolve(promise, y, fulfil, reject); }; rejectPromise = function (r) { if (called) { return; } called = true; reject(r); }; try { then.call(x, resolvePromise, rejectPromise); } catch (e) { if (!called) { // 2.3.3.3.4.1 reject(e); // 2.3.3.3.4.2 called = true; return; } } } else { fulfil(x); } } else { fulfil(x); } } var getInnerContext = function (fragment) { do { if (fragment.context !== undefined) { return fragment.context; } } while (fragment = fragment.parent); return rootKeypath; }; var shared_resolveRef = resolveRef; function resolveRef(ractive, ref, fragment) { var keypath; ref = normalise(ref); // If a reference begins '~/', it's a top-level reference if (ref.substr(0, 2) === "~/") { keypath = getKeypath(ref.substring(2)); createMappingIfNecessary(ractive, keypath.firstKey, fragment); } // If a reference begins with '.', it's either a restricted reference or // an ancestor reference... else if (ref[0] === ".") { keypath = resolveAncestorRef(getInnerContext(fragment), ref); if (keypath) { createMappingIfNecessary(ractive, keypath.firstKey, fragment); } } // ...otherwise we need to figure out the keypath based on context else { keypath = resolveAmbiguousReference(ractive, getKeypath(ref), fragment); } return keypath; } function resolveAncestorRef(baseContext, ref) { var contextKeys; // TODO... if (baseContext != undefined && typeof baseContext !== "string") { baseContext = baseContext.str; } // {{.}} means 'current context' if (ref === ".") return getKeypath(baseContext); contextKeys = baseContext ? baseContext.split(".") : []; // ancestor references (starting "../") go up the tree if (ref.substr(0, 3) === "../") { while (ref.substr(0, 3) === "../") { if (!contextKeys.length) { throw new Error("Could not resolve reference - too many \"../\" prefixes"); } contextKeys.pop(); ref = ref.substring(3); } contextKeys.push(ref); return getKeypath(contextKeys.join(".")); } // not an ancestor reference - must be a restricted reference (prepended with "." or "./") if (!baseContext) { return getKeypath(ref.replace(/^\.\/?/, "")); } return getKeypath(baseContext + ref.replace(/^\.\//, ".")); } function resolveAmbiguousReference(ractive, ref, fragment, isParentLookup) { var context, key, parentValue, hasContextChain, parentKeypath; if (ref.isRoot) { return ref; } key = ref.firstKey; while (fragment) { context = fragment.context; fragment = fragment.parent; if (!context) { continue; } hasContextChain = true; parentValue = ractive.viewmodel.get(context); if (parentValue && (typeof parentValue === "object" || typeof parentValue === "function") && key in parentValue) { return context.join(ref.str); } } // Root/computed/mapped property? if (isRootProperty(ractive.viewmodel, key)) { return ref; } // If this is an inline component, and it's not isolated, we // can try going up the scope chain if (ractive.parent && !ractive.isolated) { hasContextChain = true; fragment = ractive.component.parentFragment; key = getKeypath(key); if (parentKeypath = resolveAmbiguousReference(ractive.parent, key, fragment, true)) { // We need to create an inter-component binding ractive.viewmodel.map(key, { origin: ractive.parent.viewmodel, keypath: parentKeypath }); return ref; } } // If there's no context chain, and the instance is either a) isolated or // b) an orphan, then we know that the keypath is identical to the reference if (!isParentLookup && !hasContextChain) { // the data object needs to have a property by this name, // to prevent future failed lookups ractive.viewmodel.set(ref, undefined); return ref; } } function createMappingIfNecessary(ractive, key) { var parentKeypath; if (!ractive.parent || ractive.isolated || isRootProperty(ractive.viewmodel, key)) { return; } key = getKeypath(key); if (parentKeypath = resolveAmbiguousReference(ractive.parent, key, ractive.component.parentFragment, true)) { ractive.viewmodel.map(key, { origin: ractive.parent.viewmodel, keypath: parentKeypath }); } } function isRootProperty(viewmodel, key) { // special case for reference to root return key === "" || key in viewmodel.data || key in viewmodel.computations || key in viewmodel.mappings; } function teardown(x) { x.teardown(); } function methodCallers__unbind(x) { x.unbind(); } function methodCallers__unrender(x) { x.unrender(); } function cancel(x) { x.cancel(); } var TransitionManager = function (callback, parent) { this.callback = callback; this.parent = parent; this.intros = []; this.outros = []; this.children = []; this.totalChildren = this.outroChildren = 0; this.detachQueue = []; this.decoratorQueue = []; this.outrosComplete = false; if (parent) { parent.addChild(this); } }; TransitionManager.prototype = { addChild: function (child) { this.children.push(child); this.totalChildren += 1; this.outroChildren += 1; }, decrementOutros: function () { this.outroChildren -= 1; check(this); }, decrementTotal: function () { this.totalChildren -= 1; check(this); }, add: function (transition) { var list = transition.isIntro ? this.intros : this.outros; list.push(transition); }, addDecorator: function (decorator) { this.decoratorQueue.push(decorator); }, remove: function (transition) { var list = transition.isIntro ? this.intros : this.outros; removeFromArray(list, transition); check(this); }, init: function () { this.ready = true; check(this); }, detachNodes: function () { this.decoratorQueue.forEach(teardown); this.detachQueue.forEach(detach); this.children.forEach(detachNodes); } }; function detach(element) { element.detach(); } function detachNodes(tm) { tm.detachNodes(); } function check(tm) { if (!tm.ready || tm.outros.length || tm.outroChildren) return; // If all outros are complete, and we haven't already done this, // we notify the parent if there is one, otherwise // start detaching nodes if (!tm.outrosComplete) { if (tm.parent) { tm.parent.decrementOutros(tm); } else { tm.detachNodes(); } tm.outrosComplete = true; } // Once everything is done, we can notify parent transition // manager and call the callback if (!tm.intros.length && !tm.totalChildren) { if (typeof tm.callback === "function") { tm.callback(); } if (tm.parent) { tm.parent.decrementTotal(); } } } var global_TransitionManager = TransitionManager; var batch, runloop, unresolved = [], changeHook = new hooks_Hook("change"); runloop = { start: function (instance, returnPromise) { var promise, fulfilPromise; if (returnPromise) { promise = new utils_Promise(function (f) { return fulfilPromise = f; }); } batch = { previousBatch: batch, transitionManager: new global_TransitionManager(fulfilPromise, batch && batch.transitionManager), views: [], tasks: [], ractives: [], instance: instance }; if (instance) { batch.ractives.push(instance); } return promise; }, end: function () { flushChanges(); batch.transitionManager.init(); if (!batch.previousBatch && !!batch.instance) batch.instance.viewmodel.changes = []; batch = batch.previousBatch; }, addRactive: function (ractive) { if (batch) { addToArray(batch.ractives, ractive); } }, registerTransition: function (transition) { transition._manager = batch.transitionManager; batch.transitionManager.add(transition); }, registerDecorator: function (decorator) { batch.transitionManager.addDecorator(decorator); }, addView: function (view) { batch.views.push(view); }, addUnresolved: function (thing) { unresolved.push(thing); }, removeUnresolved: function (thing) { removeFromArray(unresolved, thing); }, // synchronise node detachments with transition ends detachWhenReady: function (thing) { batch.transitionManager.detachQueue.push(thing); }, scheduleTask: function (task, postRender) { var _batch; if (!batch) { task(); } else { _batch = batch; while (postRender && _batch.previousBatch) { // this can't happen until the DOM has been fully updated // otherwise in some situations (with components inside elements) // transitions and decorators will initialise prematurely _batch = _batch.previousBatch; } _batch.tasks.push(task); } } }; var global_runloop = runloop; function flushChanges() { var i, thing, changeHash; while (batch.ractives.length) { thing = batch.ractives.pop(); changeHash = thing.viewmodel.applyChanges(); if (changeHash) { changeHook.fire(thing, changeHash); } } attemptKeypathResolution(); // Now that changes have been fully propagated, we can update the DOM // and complete other tasks for (i = 0; i < batch.views.length; i += 1) { batch.views[i].update(); } batch.views.length = 0; for (i = 0; i < batch.tasks.length; i += 1) { batch.tasks[i](); } batch.tasks.length = 0; // If updating the view caused some model blowback - e.g. a triple // containing