diff --git a/README.md b/README.md index ef2c953..f935914 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Ractive.js sortable decorator plugin ==================================== -*Find more Ractive.js plugins at [ractivejs.org/plugins](http://ractivejs.org/plugins)* +*Find more Ractive.js plugins at [docs.ractivejs.org/latest/plugins](http://docs.ractivejs.org/latest/plugins)* [See the demo here.](http://ractivejs.github.io/Ractive-decorators-sortable/) diff --git a/Ractive-decorators-sortable.js b/Ractive-decorators-sortable.js index bfd8ad6..f511c5c 100644 --- a/Ractive-decorators-sortable.js +++ b/Ractive-decorators-sortable.js @@ -3,7 +3,7 @@ Ractive-decorators-sortable =========================== - Version 0.1.0. + Version 0.2.0. This plugin adds a 'sortable' decorator to Ractive, which enables elements that correspond to array members to be re-ordered using @@ -58,18 +58,18 @@ */ -(function ( global, factory ) { +var sortableDecorator = (function ( global, factory ) { 'use strict'; // Common JS (i.e. browserify) environment if ( typeof module !== 'undefined' && module.exports && typeof require === 'function' ) { - factory( require( 'Ractive' ) ); + factory( require( 'ractive' ) ); } // AMD? else if ( typeof define === 'function' && define.amd ) { - define([ 'Ractive' ], factory ); + define([ 'ractive' ], factory ); } // browser global @@ -126,7 +126,7 @@ dragstartHandler = function ( event ) { var storage = this._ractive, lastDotIndex; - sourceKeypath = storage.keypath; + sourceKeypath = storage.keypath.str; // this decorator only works with array members! lastDotIndex = sourceKeypath.lastIndexOf( '.' ); @@ -156,7 +156,7 @@ return; } - targetKeypath = this._ractive.keypath; + targetKeypath = this._ractive.keypath.str; // this decorator only works with array members! lastDotIndex = targetKeypath.lastIndexOf( '.' ); @@ -173,13 +173,16 @@ return; } + // add droptarget class to hovered element + this.classList.add( sortable.targetClass ); + // if it's the same index, add droptarget class then abort if ( targetIndex === sourceIndex ) { - this.classList.add( sortable.targetClass ); return; } - array = ractive.get( sourceArray ); + // cloning array avoids issues on some generated arrays (TODO investigate) + array = ractive.get( sourceArray ).slice(); // remove source from array source = array.splice( sourceIndex, 1 )[0]; @@ -189,6 +192,9 @@ // add source back to array in new location array.splice( sourceIndex, 0, source ); + + // replace source with updated order from cloned-array (due to .slice()) + ractive.set( sourceArray, array ); }; removeTargetClass = function () { @@ -198,5 +204,11 @@ preventDefault = function ( event ) { event.preventDefault(); }; Ractive.decorators.sortable = sortable; - -})); \ No newline at end of file + + return sortable; +})); + +// Common JS (i.e. browserify) environment +if ( typeof module !== 'undefined' && module.exports) { + module.exports = sortableDecorator; +} diff --git a/Ractive-decorators-sortable.min.js b/Ractive-decorators-sortable.min.js index 90fa355..f0db60f 100644 --- a/Ractive-decorators-sortable.min.js +++ b/Ractive-decorators-sortable.min.js @@ -1 +1 @@ -!function(a,b){"use strict";if("undefined"!=typeof module&&module.exports&&"function"==typeof require)b(require("Ractive"));else if("function"==typeof define&&define.amd)define(["Ractive"],b);else{if(!a.Ractive)throw new Error("Could not find Ractive! It must be loaded before the Ractive-decorators-sortable plugin");b(a.Ractive)}}("undefined"!=typeof window?window:this,function(a){"use strict";var b,c,d,e,f,g,h,i,j,k;b=function(a){return a.draggable=!0,a.addEventListener("dragstart",g,!1),a.addEventListener("dragenter",h,!1),a.addEventListener("dragleave",i,!1),a.addEventListener("drop",i,!1),a.addEventListener("dragover",j,!1),{teardown:function(){a.removeEventListener("dragstart",g,!1),a.removeEventListener("dragenter",h,!1),a.removeEventListener("dragleave",i,!1),a.removeEventListener("drop",i,!1),a.removeEventListener("dragover",j,!1)}}},b.targetClass="droptarget",k="The sortable decorator only works with elements that correspond to array members",g=function(a){var b,g=this._ractive;if(d=g.keypath,b=d.lastIndexOf("."),-1===b)throw new Error(k);if(e=d.substr(0,b),f=+d.substring(b+1),isNaN(f))throw new Error(k);a.dataTransfer.setData("foo",!0),c=g.root},h=function(){var a,d,g,h,i,j;if(this._ractive.root===c){if(a=this._ractive.keypath,d=a.lastIndexOf("."),-1===d)throw new Error(k);if(g=a.substr(0,d),h=+a.substring(d+1),g===e){if(h===f)return this.classList.add(b.targetClass),void 0;i=c.get(e),j=i.splice(f,1)[0],f=h,i.splice(f,0,j)}}},i=function(){this.classList.remove(b.targetClass)},j=function(a){a.preventDefault()},a.decorators.sortable=b}); \ No newline at end of file +var sortableDecorator=function(a,b){"use strict";if("undefined"!=typeof module&&module.exports&&"function"==typeof require)b(require("ractive"));else if("function"==typeof define&&define.amd)define(["ractive"],b);else{if(!a.Ractive)throw new Error("Could not find Ractive! It must be loaded before the Ractive-decorators-sortable plugin");b(a.Ractive)}}("undefined"!=typeof window?window:this,function(a){"use strict";var b,c,d,e,f,g,h,i,j,k;return b=function(a){return a.draggable=!0,a.addEventListener("dragstart",g,!1),a.addEventListener("dragenter",h,!1),a.addEventListener("dragleave",i,!1),a.addEventListener("drop",i,!1),a.addEventListener("dragover",j,!1),{teardown:function(){a.removeEventListener("dragstart",g,!1),a.removeEventListener("dragenter",h,!1),a.removeEventListener("dragleave",i,!1),a.removeEventListener("drop",i,!1),a.removeEventListener("dragover",j,!1)}}},b.targetClass="droptarget",k="The sortable decorator only works with elements that correspond to array members",g=function(a){var b,g=this._ractive;if(d=g.keypath.str,b=d.lastIndexOf("."),-1===b)throw new Error(k);if(e=d.substr(0,b),f=+d.substring(b+1),isNaN(f))throw new Error(k);a.dataTransfer.setData("foo",!0),c=g.root},h=function(){var a,d,g,h,i,j;if(this._ractive.root===c){if(a=this._ractive.keypath.str,d=a.lastIndexOf("."),-1===d)throw new Error(k);g=a.substr(0,d),h=+a.substring(d+1),g===e&&(this.classList.add(b.targetClass),h!==f&&(i=c.get(e).slice(),j=i.splice(f,1)[0],f=h,i.splice(f,0,j),c.set(e,i)))}},i=function(){this.classList.remove(b.targetClass)},j=function(a){a.preventDefault()},a.decorators.sortable=b,b});"undefined"!=typeof module&&module.exports&&(module.exports=sortableDecorator); \ No newline at end of file diff --git a/index.html b/index.html index cc30046..7695a13 100644 --- a/index.html +++ b/index.html @@ -75,7 +75,7 @@

Ractive.js sortable decorator plugin

download: Ractive-decorators-sortable.js - more plugins at ractivejs.org/plugins + more plugins at docs.ractivejs.org/latest/plugins

diff --git a/lib/Ractive-legacy.js b/lib/Ractive-legacy.js index 8482370..bfc07cb 100644 --- a/lib/Ractive-legacy.js +++ b/lib/Ractive-legacy.js @@ -1,9339 +1,16618 @@ /* + Ractive.js v0.7.3 + Sat Apr 25 2015 13:52:38 GMT-0400 (EDT) - commit da40f81c660ba2f09c45a09a9c20fdd34ee36d80 - Ractive - v0.3.9 - 2013-12-26 - ============================================================== + http://ractivejs.org + http://twitter.com/RactiveJS - Next-generation DOM manipulation - http://ractivejs.org - Follow @RactiveJS for updates - - -------------------------------------------------------------- + Released under the MIT License. +*/ - Copyright 2013 2013 Rich Harris and contributors +(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); + } - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: + 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); + }; + }; - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. + dom__i = methodNames.length; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. + 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); -(function ( win ) { - - 'use strict'; - - var doc = win.document; - - if ( !doc ) { - return; - } - - // Shims for older browsers - - if ( !Date.now ) { - Date.now = function () { return +new Date(); }; - } - - if ( !String.prototype.trim ) { - String.prototype.trim = function () { - return this.replace(/^\s+/, '').replace(/\s+$/, ''); - }; - } - - - // Polyfill for Object.keys - // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys - if ( !Object.keys ) { - Object.keys = (function () { - var hasOwnProperty = Object.prototype.hasOwnProperty, - hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), - dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ], - dontEnumsLength = dontEnums.length; - - return function ( obj ) { - if ( typeof obj !== 'object' && typeof obj !== 'function' || obj === null ) { - throw new TypeError( 'Object.keys called on non-object' ); - } - - var result = []; - - for ( var prop in obj ) { - if ( hasOwnProperty.call( obj, prop ) ){ - result.push( prop ); - } - } - - if ( hasDontEnumBug ) { - for ( var i=0; i < dontEnumsLength; i++ ) { - if ( hasOwnProperty.call( obj, dontEnums[i] ) ){ - result.push( dontEnums[i] ); - } - } - } - return result; - }; - }()); - } - - - // Array extras - if ( !Array.prototype.indexOf ) { - Array.prototype.indexOf = function ( needle, i ) { - var len; - - if ( i === undefined ) { - i = 0; - } - - if ( i < 0 ) { - i+= this.length; - } - - if ( i < 0 ) { - i = 0; - } - - for ( len = this.length; i + dom__div.innerHTML = ""; + + parentNode = dom__div; + node = node.cloneNode(); + + dom__div.appendChild(node); } - }(config_svg, config_namespaces); -var config_isClient = function () { - if (typeof document === 'object') { + nodes = parentNode.querySelectorAll(selector); + + i = nodes.length; + while (i--) { + if (nodes[i] === node) { return true; + } } + return false; - }(); -var utils_defineProperty = function (isClient) { + }; + } + } - try { - Object.defineProperty({}, 'test', { value: 0 }); - if (isClient) { - Object.defineProperty(document.createElement('div'), 'test', { value: 0 }); - } - return Object.defineProperty; - } catch (err) { - return function (obj, prop, desc) { - obj[prop] = desc.value; - }; + 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 {}; } - }(config_isClient); -var utils_defineProperties = function (createElement, defineProperty, isClient) { - try { - try { - Object.defineProperties({}, { test: { value: 0 } }); - } catch (err) { - throw err; + 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; } - if (isClient) { - Object.defineProperties(createElement('div'), { test: { value: 0 } }); + + var node = undefined; + if (node = options.node || ractive.fragment && ractive.fragment.rendered && ractive.find("*")) { + args.push(node); } - return Object.defineProperties; - } catch (err) { - return function (obj, props) { - var prop; - for (prop in props) { - if (props.hasOwnProperty(prop)) { - defineProperty(obj, prop, props[prop]); - } - } - }; + } } - }(utils_createElement, utils_defineProperty, config_isClient); -var utils_normaliseKeypath = function () { - var regex = /\[\s*(\*|[0-9]|[1-9][0-9]+)\s*\]/g; - return function (keypath) { - return (keypath || '').replace(regex, '.$1'); - }; - }(); -var registries_adaptors = {}; -var config_types = { - TEXT: 1, - INTERPOLATOR: 2, - TRIPLE: 3, - SECTION: 4, - INVERTED: 5, - CLOSING: 6, - ELEMENT: 7, - PARTIAL: 8, - COMMENT: 9, - DELIMCHANGE: 10, - MUSTACHE: 11, - TAG: 12, - ATTRIBUTE: 13, - COMPONENT: 15, - NUMBER_LITERAL: 20, - STRING_LITERAL: 21, - ARRAY_LITERAL: 22, - OBJECT_LITERAL: 23, - BOOLEAN_LITERAL: 24, - GLOBAL: 26, - KEY_VALUE_PAIR: 27, - REFERENCE: 30, - REFINEMENT: 31, - MEMBER: 32, - PREFIX_OPERATOR: 33, - BRACKETED: 34, - CONDITIONAL: 35, - INFIX_OPERATOR: 36, - INVOCATION: 40 + 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 utils_isArray = function () { + } - var toString = Object.prototype.toString; - return function (thing) { - return toString.call(thing) === '[object Array]'; - }; - }(); -var shared_clearCache = function () { - - return function clearCache(ractive, keypath) { - var cacheMap, wrappedProperty; - if (wrappedProperty = ractive._wrapped[keypath]) { - if (wrappedProperty.teardown() !== false) { - ractive._wrapped[keypath] = null; - } - } - ractive._cache[keypath] = undefined; - if (cacheMap = ractive._cacheMap[keypath]) { - while (cacheMap.length) { - clearCache(ractive, cacheMap.pop()); - } - } - }; - }(); -var shared_getValueFromCheckboxes = function () { - - return function (ractive, keypath) { - var value, checkboxes, checkbox, len, i, rootEl; - value = []; - rootEl = ractive.rendered ? ractive.el : ractive.fragment.docFrag; - checkboxes = rootEl.querySelectorAll('input[type="checkbox"][name="{{' + keypath + '}}"]'); - len = checkboxes.length; - for (i = 0; i < len; i += 1) { - checkbox = checkboxes[i]; - if (checkbox.hasAttribute('checked') || checkbox.checked) { - value[value.length] = checkbox._ractive.value; - } - } - return value; - }; - }(); -var shared_preDomUpdate = function (getValueFromCheckboxes) { - - return function (ractive) { - var deferred, evaluator, selectValue, attribute, keypath, radio; - deferred = ractive._deferred; - while (evaluator = deferred.evals.pop()) { - evaluator.update().deferred = false; - } - while (selectValue = deferred.selectValues.pop()) { - selectValue.deferredUpdate(); - } - while (attribute = deferred.attrs.pop()) { - attribute.update().deferred = false; - } - while (keypath = deferred.checkboxes.pop()) { - ractive.set(keypath, getValueFromCheckboxes(ractive, keypath)); - } - while (radio = deferred.radios.pop()) { - radio.update(); - } - }; - }(shared_getValueFromCheckboxes); -var shared_postDomUpdate = function () { - - return function (ractive) { - var deferred, focusable, query, decorator, transition, observer; - deferred = ractive._deferred; - if (focusable = deferred.focusable) { - focusable.focus(); - deferred.focusable = null; - } - while (query = deferred.liveQueries.pop()) { - query._sort(); - } - while (decorator = deferred.decorators.pop()) { - decorator.init(); - } - while (transition = deferred.transitions.pop()) { - transition.init(); - } - while (observer = deferred.observers.pop()) { - observer.update(); - } - }; - }(); -var shared_makeTransitionManager = function () { + var interpolators = { + number: function (from, to) { + var delta; - var makeTransitionManager = function (root, callback) { - var transitionManager, elementsToDetach, detachNodes, nodeHasNoTransitioningChildren; - if (root._parent && root._parent._transitionManager) { - return root._parent._transitionManager; - } - elementsToDetach = []; - detachNodes = function () { - var i, element; - i = elementsToDetach.length; - while (i--) { - element = elementsToDetach[i]; - if (nodeHasNoTransitioningChildren(element.node)) { - element.detach(); - elementsToDetach.splice(i, 1); - } - } - }; - nodeHasNoTransitioningChildren = function (node) { - var i, candidate; - i = transitionManager.active.length; - while (i--) { - candidate = transitionManager.active[i]; - if (node.contains(candidate)) { - return false; - } - } - return true; - }; - transitionManager = { - active: [], - push: function (node) { - transitionManager.active[transitionManager.active.length] = node; - }, - pop: function (node) { - var index; - index = transitionManager.active.indexOf(node); - if (index === -1) { - return; - } - transitionManager.active.splice(index, 1); - detachNodes(); - if (!transitionManager.active.length && transitionManager._ready) { - transitionManager.complete(); - } - }, - complete: function () { - if (callback) { - callback.call(root); - } - }, - ready: function () { - detachNodes(); - transitionManager._ready = true; - if (!transitionManager.active.length) { - transitionManager.complete(); - } - }, - detachWhenReady: function (element) { - elementsToDetach[elementsToDetach.length] = element; - } - }; - return transitionManager; - }; - return makeTransitionManager; - }(); -var shared_notifyDependants = function () { - - var notifyDependants, lastKey, starMaps = {}; - lastKey = /[^\.]+$/; - notifyDependants = function (ractive, keypath, onlyDirect) { - var i; - if (ractive._patternObservers.length) { - notifyPatternObservers(ractive, keypath, keypath, onlyDirect, true); - } - for (i = 0; i < ractive._deps.length; i += 1) { - notifyDependantsAtPriority(ractive, keypath, i, onlyDirect); - } - }; - notifyDependants.multiple = function (ractive, keypaths, onlyDirect) { - var i, j, len; - len = keypaths.length; - if (ractive._patternObservers.length) { - i = len; - while (i--) { - notifyPatternObservers(ractive, keypaths[i], keypaths[i], onlyDirect, true); - } - } - for (i = 0; i < ractive._deps.length; i += 1) { - if (ractive._deps[i]) { - j = len; - while (j--) { - notifyDependantsAtPriority(ractive, keypaths[j], i, onlyDirect); - } - } - } + if (!is__isNumeric(from) || !is__isNumeric(to)) { + return null; + } + + from = +from; + to = +to; + + delta = to - from; + + if (!delta) { + return function () { + return from; }; - return notifyDependants; - function notifyDependantsAtPriority(ractive, keypath, priority, onlyDirect) { - var depsByKeypath = ractive._deps[priority]; - if (!depsByKeypath) { - return; - } - updateAll(depsByKeypath[keypath]); - if (onlyDirect) { - return; - } - cascade(ractive._depsMap[keypath], ractive, priority); - } - function updateAll(deps) { - var i, len; - if (deps) { - len = deps.length; - for (i = 0; i < len; i += 1) { - deps[i].update(); - } - } + } + + 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); } - function cascade(childDeps, ractive, priority, onlyDirect) { - var i; - if (childDeps) { - i = childDeps.length; - while (i--) { - notifyDependantsAtPriority(ractive, childDeps[i], priority, onlyDirect); - } - } + + 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]; + } } - function notifyPatternObservers(ractive, registeredKeypath, actualKeypath, isParentOfChangedKeypath, isTopLevelCall) { - var i, patternObserver, children, child, key, childActualKeypath, potentialWildcardMatches, cascade; - i = ractive._patternObservers.length; - while (i--) { - patternObserver = ractive._patternObservers[i]; - if (patternObserver.regex.test(actualKeypath)) { - patternObserver.update(actualKeypath); - } - } - if (isParentOfChangedKeypath) { - return; - } - cascade = function (keypath) { - if (children = ractive._depsMap[keypath]) { - i = children.length; - while (i--) { - child = children[i]; - key = lastKey.exec(child)[0]; - childActualKeypath = actualKeypath + '.' + key; - notifyPatternObservers(ractive, child, childActualKeypath); - } - } - }; - if (isTopLevelCall) { - potentialWildcardMatches = getPotentialWildcardMatches(actualKeypath); - potentialWildcardMatches.forEach(cascade); - } else { - cascade(registeredKeypath); - } + } + + for (prop in to) { + if (hasOwn.call(to, prop) && !hasOwn.call(from, prop)) { + intermediate[prop] = to[prop]; } - function getPotentialWildcardMatches(keypath) { - var keys, starMap, mapper, i, result, wildcardKeypath; - keys = keypath.split('.'); - 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[wildcardKeypath]) { - result[result.length] = wildcardKeypath; - result[wildcardKeypath] = true; - } - } - return result; - } - function getStarMap(num) { - var ones = '', max, binary, starMap, mapper, i; - 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; - } - starMap[i] = Array.prototype.map.call(binary, mapper); - } - starMaps[num] = starMap; - } - return starMaps[num]; - } - }(); -var Ractive_prototype_get_arrayAdaptor = function (types, defineProperty, isArray, clearCache, preDomUpdate, postDomUpdate, makeTransitionManager, notifyDependants) { - - var arrayAdaptor, notifyArrayDependants, ArrayWrapper, patchArrayMethods, unpatchArrayMethods, patchedArrayProto, testObj, mutatorMethods, noop, errorMessage; - arrayAdaptor = { - filter: function (object) { - return isArray(object) && (!object._ractive || !object._ractive.setting); - }, - wrap: function (ractive, array, keypath) { - return new ArrayWrapper(ractive, array, keypath); - } - }; - ArrayWrapper = function (ractive, array, keypath) { - this.root = ractive; - this.value = array; - this.keypath = keypath; - if (!array._ractive) { - defineProperty(array, '_ractive', { - value: { - wrappers: [], - instances: [], - setting: false - }, - configurable: true - }); - patchArrayMethods(array); - } - if (!array._ractive.instances[ractive._guid]) { - array._ractive.instances[ractive._guid] = 0; - array._ractive.instances.push(ractive); - } - array._ractive.instances[ractive._guid] += 1; - array._ractive.wrappers.push(this); - }; - ArrayWrapper.prototype = { - get: function () { - return this.value; - }, - teardown: function () { - var array, storage, wrappers, instances, index; - array = this.value; - storage = array._ractive; - wrappers = storage.wrappers; - instances = storage.instances; - if (storage.setting) { - return false; - } - index = wrappers.indexOf(this); - if (index === -1) { - throw new Error(errorMessage); - } - wrappers.splice(index, 1); - if (!wrappers.length) { - delete array._ractive; - unpatchArrayMethods(this.value); - } else { - instances[this.root._guid] -= 1; - if (!instances[this.root._guid]) { - index = instances.indexOf(this.root); - if (index === -1) { - throw new Error(errorMessage); - } - instances.splice(index, 1); - } - } - } - }; - notifyArrayDependants = function (array, methodName, args) { - var notifyKeypathDependants, queueDependants, wrappers, wrapper, i; - notifyKeypathDependants = function (root, keypath) { - var depsByKeypath, deps, keys, upstreamQueue, smartUpdateQueue, dumbUpdateQueue, i, changed, start, end, childKeypath, lengthUnchanged; - if (methodName === 'sort' || methodName === 'reverse') { - root.set(keypath, array); - return; - } - clearCache(root, keypath); - smartUpdateQueue = []; - dumbUpdateQueue = []; - for (i = 0; i < root._deps.length; i += 1) { - depsByKeypath = root._deps[i]; - if (!depsByKeypath) { - continue; - } - deps = depsByKeypath[keypath]; - if (deps) { - queueDependants(keypath, deps, smartUpdateQueue, dumbUpdateQueue); - preDomUpdate(root); - while (smartUpdateQueue.length) { - smartUpdateQueue.pop().smartUpdate(methodName, args); - } - while (dumbUpdateQueue.length) { - dumbUpdateQueue.pop().update(); - } - } - } - if (methodName === 'splice' && args.length > 2 && args[1]) { - changed = Math.min(args[1], args.length - 2); - start = args[0]; - end = start + changed; - if (args[1] === args.length - 2) { - lengthUnchanged = true; - } - for (i = start; i < end; i += 1) { - childKeypath = keypath + '.' + i; - notifyDependants(root, childKeypath); - } - } - preDomUpdate(root); - upstreamQueue = []; - keys = keypath.split('.'); - while (keys.length) { - keys.pop(); - upstreamQueue[upstreamQueue.length] = keys.join('.'); - } - notifyDependants.multiple(root, upstreamQueue, true); - if (!lengthUnchanged) { - notifyDependants(root, keypath + '.length', true); - } - }; - queueDependants = function (keypath, deps, smartUpdateQueue, dumbUpdateQueue) { - var k, dependant; - k = deps.length; - while (k--) { - dependant = deps[k]; - if (dependant.type === types.REFERENCE) { - dependant.update(); - } else if (dependant.keypath === keypath && dependant.type === types.SECTION && !dependant.inverted && dependant.docFrag) { - smartUpdateQueue[smartUpdateQueue.length] = dependant; - } else { - dumbUpdateQueue[dumbUpdateQueue.length] = dependant; - } - } - }; - wrappers = array._ractive.wrappers; - i = wrappers.length; - while (i--) { - wrapper = wrappers[i]; - notifyKeypathDependants(wrapper.root, wrapper.keypath); - } - }; - patchedArrayProto = []; - mutatorMethods = [ - 'pop', - 'push', - 'reverse', - 'shift', - 'sort', - 'splice', - 'unshift' - ]; - noop = function () { - }; - mutatorMethods.forEach(function (methodName) { - var method = function () { - var result, instances, instance, i, previousTransitionManagers = {}, transitionManagers = {}; - result = Array.prototype[methodName].apply(this, arguments); - instances = this._ractive.instances; - i = instances.length; - while (i--) { - instance = instances[i]; - previousTransitionManagers[instance._guid] = instance._transitionManager; - instance._transitionManager = transitionManagers[instance._guid] = makeTransitionManager(instance, noop); - } - this._ractive.setting = true; - notifyArrayDependants(this, methodName, arguments); - this._ractive.setting = false; - i = instances.length; - while (i--) { - instance = instances[i]; - instance._transitionManager = previousTransitionManagers[instance._guid]; - transitionManagers[instance._guid].ready(); - preDomUpdate(instance); - postDomUpdate(instance); - } - return result; - }; - defineProperty(patchedArrayProto, methodName, { value: method }); - }); - testObj = {}; - if (testObj.__proto__) { - patchArrayMethods = function (array) { - array.__proto__ = patchedArrayProto; - }; - unpatchArrayMethods = function (array) { - array.__proto__ = Array.prototype; - }; - } else { - patchArrayMethods = function (array) { - var i, methodName; - i = mutatorMethods.length; - while (i--) { - methodName = mutatorMethods[i]; - defineProperty(array, methodName, { - value: patchedArrayProto[methodName], - configurable: true - }); - } - }; - unpatchArrayMethods = function (array) { - var i; - i = mutatorMethods.length; - while (i--) { - delete array[mutatorMethods[i]]; - } - }; + } + + len = properties.length; + + return function (t) { + var i = len, + prop; + + while (i--) { + prop = properties[i]; + + intermediate[prop] = interpolators[prop](t); } - errorMessage = 'Something went wrong in a rather interesting way'; - return arrayAdaptor; - }(config_types, utils_defineProperty, utils_isArray, shared_clearCache, shared_preDomUpdate, shared_postDomUpdate, shared_makeTransitionManager, shared_notifyDependants); -var Ractive_prototype_get_magicAdaptor = function () { - var magicAdaptor, MagicWrapper; - try { - Object.defineProperty({}, 'test', { value: 0 }); - } catch (err) { - return false; - } - magicAdaptor = { - filter: function (object, keypath) { - return !!keypath; - }, - wrap: function (ractive, object, keypath) { - return new MagicWrapper(ractive, object, keypath); - } - }; - MagicWrapper = function (ractive, object, keypath) { - var wrapper = this, keys, prop, objKeypath, descriptor, wrappers, oldGet, oldSet, get, set; - this.ractive = ractive; - this.keypath = keypath; - keys = keypath.split('.'); - this.prop = keys.pop(); - objKeypath = keys.join('.'); - this.obj = objKeypath ? ractive.get(objKeypath) : ractive.data; - descriptor = this.originalDescriptor = Object.getOwnPropertyDescriptor(this.obj, this.prop); - if (descriptor && descriptor.set && (wrappers = descriptor.set._ractiveWrappers)) { - if (wrappers.indexOf(this) === -1) { - wrappers.push(this); - } - return; - } - if (descriptor && !descriptor.configurable) { - throw new Error('Cannot use magic mode with property "' + prop + '" - object is not configurable'); - } - if (descriptor) { - this.value = descriptor.value; - oldGet = descriptor.get; - oldSet = descriptor.set; - } - get = oldGet || function () { - return wrapper.value; - }; - set = function (value) { - var wrappers, wrapper, i; - if (oldSet) { - oldSet(value); - } - wrappers = set._ractiveWrappers; - i = wrappers.length; - while (i--) { - wrapper = wrappers[i]; - if (!wrapper.resetting) { - wrapper.ractive.set(wrapper.keypath, value); - } - } - }; - set._ractiveWrappers = [this]; - Object.defineProperty(this.obj, this.prop, { - get: get, - set: set, - enumerable: true, - configurable: true - }); - }; - MagicWrapper.prototype = { - get: function () { - return this.value; - }, - reset: function (value) { - this.resetting = true; - this.value = value; - this.resetting = false; - }, - teardown: function () { - var descriptor, set, value, wrappers; - descriptor = Object.getOwnPropertyDescriptor(this.obj, this.prop); - set = descriptor.set; - wrappers = set._ractiveWrappers; - wrappers.splice(wrappers.indexOf(this), 1); - if (!wrappers.length) { - value = this.obj[this.prop]; - Object.defineProperty(this.obj, this.prop, this.originalDescriptor || { - writable: true, - enumerable: true, - configrable: true - }); - this.obj[this.prop] = value; - } - } - }; - return magicAdaptor; - }(); -var shared_adaptIfNecessary = function (adaptorRegistry, arrayAdaptor, magicAdaptor) { - - var prefixers = {}; - return function (ractive, keypath, value, isExpressionResult) { - var len, i, adaptor, wrapped; - len = ractive.adaptors.length; - for (i = 0; i < len; i += 1) { - adaptor = ractive.adaptors[i]; - if (typeof adaptor === 'string') { - if (!adaptorRegistry[adaptor]) { - throw new Error('Missing adaptor "' + adaptor + '"'); - } - adaptor = ractive.adaptors[i] = adaptorRegistry[adaptor]; - } - if (adaptor.filter(value, keypath, ractive)) { - wrapped = ractive._wrapped[keypath] = adaptor.wrap(ractive, value, keypath, getPrefixer(keypath)); - wrapped.value = value; - return; - } - } - if (!isExpressionResult) { - if (ractive.magic && magicAdaptor.filter(value, keypath, ractive)) { - ractive._wrapped[keypath] = magicAdaptor.wrap(ractive, value, keypath); - } else if (ractive.modifyArrays && arrayAdaptor.filter(value, keypath, ractive)) { - ractive._wrapped[keypath] = arrayAdaptor.wrap(ractive, value, keypath); - } - } - }; - function prefixKeypath(obj, prefix) { - var prefixed = {}, key; - if (!prefix) { - return obj; - } - prefix += '.'; - for (key in obj) { - if (obj.hasOwnProperty(key)) { - prefixed[prefix + key] = obj[key]; - } - } - return prefixed; - } - function getPrefixer(rootKeypath) { - var rootDot; - if (!prefixers[rootKeypath]) { - rootDot = rootKeypath ? rootKeypath + '.' : ''; - prefixers[rootKeypath] = function (relativeKeypath, value) { - var obj; - if (typeof relativeKeypath === 'string') { - obj = {}; - obj[rootDot + relativeKeypath] = value; - return obj; - } - if (typeof relativeKeypath === 'object') { - return rootDot ? prefixKeypath(relativeKeypath, rootKeypath) : relativeKeypath; - } - }; - } - return prefixers[rootKeypath]; + 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; } - }(registries_adaptors, Ractive_prototype_get_arrayAdaptor, Ractive_prototype_get_magicAdaptor); -var Ractive_prototype_get__get = function (normaliseKeypath, adaptorRegistry, adaptIfNecessary) { - var get, _get, retrieve; - get = function (keypath) { - return _get(this, keypath); - }; - _get = function (ractive, keypath) { - var cache, cached, value, wrapped, evaluator; - keypath = normaliseKeypath(keypath); - cache = ractive._cache; - if ((cached = cache[keypath]) !== undefined) { - return cached; - } - if (wrapped = ractive._wrapped[keypath]) { - value = wrapped.value; - } else if (!keypath) { - adaptIfNecessary(ractive, '', ractive.data); - value = ractive.data; - } else if (evaluator = ractive._evaluators[keypath]) { - value = evaluator.value; - } else { - value = retrieve(ractive, keypath); - } - cache[keypath] = value; - return value; - }; - retrieve = function (ractive, keypath) { - var keys, key, parentKeypath, parentValue, cacheMap, value, wrapped; - keys = keypath.split('.'); - key = keys.pop(); - parentKeypath = keys.join('.'); - parentValue = _get(ractive, parentKeypath); - if (wrapped = ractive._wrapped[parentKeypath]) { - parentValue = wrapped.get(); - } - if (parentValue === null || parentValue === undefined) { - return; - } - if (!(cacheMap = ractive._cacheMap[parentKeypath])) { - ractive._cacheMap[parentKeypath] = [keypath]; - } else { - if (cacheMap.indexOf(keypath) === -1) { - cacheMap[cacheMap.length] = keypath; - } - } - value = parentValue[key]; - adaptIfNecessary(ractive, keypath, value); - ractive._cache[keypath] = value; - return value; - }; - return get; - }(utils_normaliseKeypath, registries_adaptors, shared_adaptIfNecessary); -var utils_isObject = function () { + map = []; + l = binary.length; + for (j = 0; j < l; j++) { + map.push(mapper(binary[j])); + } + starMap[i] = map; + } - var toString = Object.prototype.toString; - return function (thing) { - return typeof thing === 'object' && toString.call(thing) === '[object Object]'; - }; - }(); -var utils_isEqual = function () { + starMaps[num] = starMap; + } - return function (a, b) { - if (a === null && b === null) { - return true; - } - if (typeof a === 'object' || typeof b === 'object') { - return false; - } - return a === b; - }; - }(); -var shared_resolveRef = function () { - - var resolveRef; - resolveRef = function (ractive, ref, contextStack) { - var keypath, keys, lastKey, contextKeys, innerMostContext, postfix, parentKeypath, parentValue, wrapped, context, ancestorErrorMessage; - ancestorErrorMessage = 'Could not resolve reference - too many "../" prefixes'; - if (ref === '.') { - if (!contextStack.length) { - return ''; - } - keypath = contextStack[contextStack.length - 1]; - } else if (ref.charAt(0) === '.') { - context = contextStack[contextStack.length - 1]; - contextKeys = context ? context.split('.') : []; - if (ref.substr(0, 3) === '../') { - while (ref.substr(0, 3) === '../') { - if (!contextKeys.length) { - throw new Error(ancestorErrorMessage); - } - contextKeys.pop(); - ref = ref.substring(3); - } - contextKeys.push(ref); - keypath = contextKeys.join('.'); - } else if (!context) { - keypath = ref.substring(1); - } else { - keypath = context + ref; - } - } else { - keys = ref.split('.'); - lastKey = keys.pop(); - postfix = keys.length ? '.' + keys.join('.') : ''; - contextStack = contextStack.concat(); - while (contextStack.length) { - innerMostContext = contextStack.pop(); - parentKeypath = innerMostContext + postfix; - parentValue = ractive.get(parentKeypath); - if (wrapped = ractive._wrapped[parentKeypath]) { - parentValue = wrapped.get(); - } - if (typeof parentValue === 'object' && parentValue !== null && parentValue.hasOwnProperty(lastKey)) { - keypath = innerMostContext + '.' + ref; - break; - } - } - if (!keypath && ractive.get(ref) !== undefined) { - keypath = ref; - } - } - return keypath ? keypath.replace(/^\./, '') : keypath; - }; - return resolveRef; - }(); -var shared_attemptKeypathResolution = function (resolveRef) { - - var push = Array.prototype.push; - return function (ractive) { - var unresolved, keypath, leftover; - while (unresolved = ractive._pendingResolution.pop()) { - keypath = resolveRef(ractive, unresolved.ref, unresolved.contextStack); - if (keypath !== undefined) { - unresolved.resolve(keypath); - } else { - (leftover || (leftover = [])).push(unresolved); - } - } - if (leftover) { - push.apply(ractive._pendingResolution, leftover); - } - }; - }(shared_resolveRef); -var shared_processDeferredUpdates = function (preDomUpdate, postDomUpdate) { + return starMaps[num]; + } - return function (ractive) { - preDomUpdate(ractive); - postDomUpdate(ractive); - }; - }(shared_preDomUpdate, shared_postDomUpdate); -var Ractive_prototype_shared_replaceData = function () { - - return function (ractive, keypath, value) { - var keys, accumulated, wrapped, obj, key, currentKeypath, keypathToClear; - keys = keypath.split('.'); - accumulated = []; - if (wrapped = ractive._wrapped['']) { - if (wrapped.set) { - wrapped.set(keys.join('.'), value); - } - obj = wrapped.get(); - } else { - obj = ractive.data; - } - while (keys.length > 1) { - key = accumulated[accumulated.length] = keys.shift(); - currentKeypath = accumulated.join('.'); - if (wrapped = ractive._wrapped[currentKeypath]) { - if (wrapped.set) { - wrapped.set(keys.join('.'), value); - } - obj = wrapped.get(); - } else { - if (!obj.hasOwnProperty(key)) { - if (!keypathToClear) { - keypathToClear = currentKeypath; - } - obj[key] = /^\s*[0-9]+\s*$/.test(keys[0]) ? [] : {}; - } - obj = obj[key]; - } - } - key = keys[0]; - obj[key] = value; - return keypathToClear; - }; - }(); -var Ractive_prototype_set = function (isObject, isEqual, normaliseKeypath, clearCache, notifyDependants, attemptKeypathResolution, makeTransitionManager, processDeferredUpdates, replaceData) { - - var set, updateModel, getUpstreamChanges, resetWrapped; - set = function (keypath, value, complete) { - var map, changes, upstreamChanges, previousTransitionManager, transitionManager, i, changeHash; - changes = []; - if (isObject(keypath)) { - map = keypath; - complete = value; - } - if (map) { - for (keypath in map) { - if (map.hasOwnProperty(keypath)) { - value = map[keypath]; - keypath = normaliseKeypath(keypath); - updateModel(this, keypath, value, changes); - } - } - } else { - keypath = normaliseKeypath(keypath); - updateModel(this, keypath, value, changes); - } - if (!changes.length) { - return; - } - previousTransitionManager = this._transitionManager; - this._transitionManager = transitionManager = makeTransitionManager(this, complete); - upstreamChanges = getUpstreamChanges(changes); - if (upstreamChanges.length) { - notifyDependants.multiple(this, upstreamChanges, true); - } - notifyDependants.multiple(this, changes); - if (this._pendingResolution.length) { - attemptKeypathResolution(this); - } - processDeferredUpdates(this); - this._transitionManager = previousTransitionManager; - transitionManager.ready(); - if (!this.firingChangeEvent) { - this.firingChangeEvent = true; - changeHash = {}; - i = changes.length; - while (i--) { - changeHash[changes[i]] = this.get(changes[i]); - } - this.fire('change', changeHash); - this.firingChangeEvent = false; - } - return this; - }; - updateModel = function (ractive, keypath, value, changes) { - var cached, previous, wrapped, keypathToClear, evaluator; - if ((wrapped = ractive._wrapped[keypath]) && wrapped.reset) { - if (resetWrapped(ractive, keypath, value, wrapped, changes) !== false) { - return; - } - } - if (evaluator = ractive._evaluators[keypath]) { - evaluator.value = value; - } - cached = ractive._cache[keypath]; - previous = ractive.get(keypath); - if (previous !== value && !evaluator) { - keypathToClear = replaceData(ractive, keypath, value); - } else { - if (value === cached && typeof value !== 'object') { - return; - } - } - clearCache(ractive, keypathToClear || keypath); - changes[changes.length] = keypath; - }; - getUpstreamChanges = function (changes) { - var upstreamChanges = [''], i, keypath, keys, upstreamKeypath; - i = changes.length; - while (i--) { - keypath = changes[i]; - keys = keypath.split('.'); - while (keys.length > 1) { - keys.pop(); - upstreamKeypath = keys.join('.'); - if (!upstreamChanges[upstreamKeypath]) { - upstreamChanges[upstreamChanges.length] = upstreamKeypath; - upstreamChanges[upstreamKeypath] = true; - } - } - } - return upstreamChanges; - }; - resetWrapped = function (ractive, keypath, value, wrapped, changes) { - var previous, cached, cacheMap, i; - previous = wrapped.get(); - if (!isEqual(previous, value)) { - if (wrapped.reset(value) === false) { - return false; - } - } - value = wrapped.get(); - cached = ractive._cache[keypath]; - if (!isEqual(cached, value)) { - ractive._cache[keypath] = value; - cacheMap = ractive._cacheMap[keypath]; - if (cacheMap) { - i = cacheMap.length; - while (i--) { - clearCache(ractive, cacheMap[i]); - } - } - changes[changes.length] = keypath; - } - }; - return set; - }(utils_isObject, utils_isEqual, utils_normaliseKeypath, shared_clearCache, shared_notifyDependants, shared_attemptKeypathResolution, shared_makeTransitionManager, shared_processDeferredUpdates, Ractive_prototype_shared_replaceData); -var Ractive_prototype_update = function (makeTransitionManager, attemptKeypathResolution, clearCache, notifyDependants, processDeferredUpdates) { - - return function (keypath, complete) { - var transitionManager, previousTransitionManager; - if (typeof keypath === 'function') { - complete = keypath; - keypath = ''; - } - previousTransitionManager = this._transitionManager; - this._transitionManager = transitionManager = makeTransitionManager(this, complete); - attemptKeypathResolution(this); - clearCache(this, keypath || ''); - notifyDependants(this, keypath || ''); - processDeferredUpdates(this); - this._transitionManager = previousTransitionManager; - transitionManager.ready(); - if (typeof keypath === 'string') { - this.fire('update', keypath); - } else { - this.fire('update'); - } - return this; - }; - }(shared_makeTransitionManager, shared_attemptKeypathResolution, shared_clearCache, shared_notifyDependants, shared_processDeferredUpdates); -var utils_arrayContentsMatch = function (isArray) { + var refPattern = /\[\s*(\*|[0-9]|[1-9][0-9]+)\s*\]/g; + var patternPattern = /\*/; + var keypathCache = {}; - return function (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; - }; - }(utils_isArray); -var Ractive_prototype_updateModel = function (getValueFromCheckboxes, arrayContentsMatch, isEqual) { - - return function (keypath, cascade) { - var values, deferredCheckboxes, i; - if (typeof keypath !== 'string') { - keypath = ''; - cascade = true; - } - consolidateChangedValues(this, keypath, values = {}, deferredCheckboxes = [], cascade); - if (i = deferredCheckboxes.length) { - while (i--) { - keypath = deferredCheckboxes[i]; - values[keypath] = getValueFromCheckboxes(this, keypath); - } - } - this.set(values); - }; - function consolidateChangedValues(ractive, keypath, values, deferredCheckboxes, cascade) { - var bindings, childDeps, i, binding, oldValue, newValue; - bindings = ractive._twowayBindings[keypath]; - if (bindings) { - i = bindings.length; - while (i--) { - binding = bindings[i]; - if (binding.radioName && !binding.node.checked) { - continue; - } - if (binding.checkboxName) { - if (binding.changed() && !deferredCheckboxes[keypath]) { - deferredCheckboxes[keypath] = true; - deferredCheckboxes[deferredCheckboxes.length] = keypath; - } - continue; - } - oldValue = binding.attr.value; - newValue = binding.value(); - if (arrayContentsMatch(oldValue, newValue)) { - continue; - } - if (!isEqual(oldValue, newValue)) { - values[keypath] = newValue; - } - } - } - if (!cascade) { - return; - } - childDeps = ractive._depsMap[keypath]; - if (childDeps) { - i = childDeps.length; - while (i--) { - consolidateChangedValues(ractive, childDeps[i], values, deferredCheckboxes, cascade); - } - } - } - }(shared_getValueFromCheckboxes, utils_arrayContentsMatch, utils_isEqual); -var Ractive_prototype_animate_requestAnimationFrame = function () { + var Keypath = function (str) { + var keys = str.split("."); - if (typeof window === 'undefined') { - return; + 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)); } - (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; - }; - } - }([ - 'ms', - 'moz', - 'webkit', - 'o' - ], 0, window)); - return window.requestAnimationFrame; - }(); -var Ractive_prototype_animate_animations = function (rAF) { - - var queue = []; - var animations = { - tick: function () { - var i, animation; - for (i = 0; i < queue.length; i += 1) { - animation = queue[i]; - if (!animation.tick()) { - queue.splice(i--, 1); - } - } - if (queue.length) { - rAF(animations.tick); - } else { - animations.running = false; - } - }, - add: function (animation) { - queue[queue.length] = animation; - if (!animations.running) { - animations.running = true; - animations.tick(); - } - }, - abort: function (keypath, root) { - var i = queue.length, animation; - while (i--) { - animation = queue[i]; - if (animation.root === root && animation.keypath === keypath) { - animation.stop(); - } - } - } - }; - return animations; - }(Ractive_prototype_animate_requestAnimationFrame); -var utils_warn = function () { + } + } - if (typeof console !== 'undefined' && typeof console.warn === 'function' && typeof console.warn.apply === 'function') { - return function () { - console.warn.apply(console, arguments); - }; + 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); } - return function () { - }; - }(); -var utils_isNumeric = function () { - return function (thing) { - return !isNaN(parseFloat(thing)) && isFinite(thing); + 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; }; - }(); -var shared_interpolate = function (isArray, isObject, isNumeric) { + } + })(vendors, 0, window); - var interpolate = function (from, to) { - if (isNumeric(from) && isNumeric(to)) { - return makeNumberInterpolator(+from, +to); - } - if (isArray(from) && isArray(to)) { - return makeArrayInterpolator(from, to); - } - if (isObject(from) && isObject(to)) { - return makeObjectInterpolator(from, to); - } - return function () { - return to; - }; + 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); }; - return interpolate; - function makeNumberInterpolator(from, to) { - var delta = to - from; - if (!delta) { - return function () { - return from; - }; - } - return function (t) { - return from + t * delta; + }; + + 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); + } }; - } - function makeArrayInterpolator(from, to) { - var intermediate, interpolators, len, i; - intermediate = []; - interpolators = []; - i = len = Math.min(from.length, to.length); - while (i--) { - interpolators[i] = interpolate(from[i], to[i]); - } - for (i = len; i < from.length; i += 1) { - intermediate[i] = from[i]; - } - for (i = len; i < to.length; i += 1) { - intermediate[i] = to[i]; + + // 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 function (t) { - var i = len; - while (i--) { - intermediate[i] = interpolators[i](t); - } - return intermediate; - }; + }); + + return promise2; } - function makeObjectInterpolator(from, to) { - var properties = [], len, interpolators, intermediate, prop; - intermediate = {}; - interpolators = {}; - for (prop in from) { - if (from.hasOwnProperty(prop)) { - if (to.hasOwnProperty(prop)) { - properties[properties.length] = prop; - interpolators[prop] = interpolate(from[prop], to[prop]); - } else { - intermediate[prop] = from[prop]; - } - } - } - for (prop in to) { - if (to.hasOwnProperty(prop) && !from.hasOwnProperty(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; - }; + }; + + 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; } - }(utils_isArray, utils_isObject, utils_isNumeric); -var Ractive_prototype_animate_Animation = function (warn, interpolate) { - var Animation = function (options) { - var key; - this.startTime = Date.now(); - for (key in options) { - if (options.hasOwnProperty(key)) { - this[key] = options[key]; - } - } - this.interpolator = interpolate(this.from, this.to); - this.running = true; - }; - Animation.prototype = { - tick: function () { - var elapsed, t, value, timeNow, index, keypath; - keypath = this.keypath; - if (this.running) { - timeNow = Date.now(); - elapsed = timeNow - this.startTime; - if (elapsed >= this.duration) { - if (keypath !== null) { - this.root.set(keypath, this.to); - } - if (this.step) { - this.step(1, this.to); - } - if (this.complete) { - this.complete(1, this.to); - } - index = this.root._animations.indexOf(this); - if (index === -1) { - warn('Animation was not found'); - } - this.root._animations.splice(index, 1); - this.running = false; - return false; - } - t = this.easing ? this.easing(elapsed / this.duration) : elapsed / this.duration; - if (keypath !== null) { - value = this.interpolator(t); - this.root.set(keypath, value); - } - if (this.step) { - this.step(t, value); - } - return true; - } - return false; - }, - stop: function () { - var index; - this.running = false; - index = this.root._animations.indexOf(this); - if (index === -1) { - warn('Animation was not found'); - } - this.root._animations.splice(index, 1); - } + 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); + } }; - return Animation; - }(utils_warn, shared_interpolate); -var registries_easing = function () { - return { - 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); - } - }; - }(); -var Ractive_prototype_animate__animate = function (isEqual, animations, Animation, easingRegistry) { + pending = i = promises.length; + while (i--) { + processPromise(promises[i], i); + } + }); + }; - var noAnimation = { - stop: function () { - } - }; - return function (keypath, to, options) { - var k, animation, animations, easing, duration, step, complete, makeValueCollector, currentValues, collectValue, dummy, dummyOptions; - if (typeof keypath === 'object') { - options = to || {}; - easing = options.easing; - duration = options.duration; - animations = []; - step = options.step; - complete = options.complete; - if (step || complete) { - currentValues = {}; - options.step = null; - options.complete = null; - makeValueCollector = function (keypath) { - return function (t, value) { - currentValues[keypath] = value; - }; - }; - } - for (k in keypath) { - if (keypath.hasOwnProperty(k)) { - if (step || complete) { - collectValue = makeValueCollector(k); - options = { - easing: easing, - duration: duration - }; - if (step) { - options.step = collectValue; - } - if (complete) { - options.complete = collectValue; - } - } - animations[animations.length] = animate(this, k, keypath[k], options); - } - } - if (step || complete) { - dummyOptions = { - easing: easing, - duration: duration - }; - if (step) { - dummyOptions.step = function (t) { - step(t, currentValues); - }; - } - if (complete) { - dummyOptions.complete = function (t) { - complete(t, currentValues); - }; - } - animations[animations.length] = dummy = animate(this, null, null, dummyOptions); - } - return { - stop: function () { - while (animations.length) { - animations.pop().stop(); - } - if (dummy) { - dummy.stop(); - } - } - }; - } - options = options || {}; - animation = animate(this, keypath, to, options); - return { - stop: function () { - animation.stop(); - } - }; - }; - function animate(root, keypath, to, options) { - var easing, duration, animation, from; - if (keypath !== null) { - from = root.get(keypath); - } - animations.abort(keypath, root); - if (isEqual(from, to)) { - if (options.complete) { - options.complete(1, options.to); - } - return noAnimation; - } - if (options.easing) { - if (typeof options.easing === 'function') { - easing = options.easing; - } else { - if (root.easing && root.easing[options.easing]) { - easing = root.easing[options.easing]; - } else { - easing = easingRegistry[options.easing]; - } - } - if (typeof easing !== 'function') { - easing = null; - } - } - duration = options.duration === undefined ? 400 : options.duration; - animation = new Animation({ - keypath: keypath, - from: from, - to: to, - root: root, - duration: duration, - easing: easing, - step: options.step, - complete: options.complete - }); - animations.add(animation); - root._animations[root._animations.length] = animation; - return animation; - } - }(utils_isEqual, Ractive_prototype_animate_animations, Ractive_prototype_animate_Animation, registries_easing); -var Ractive_prototype_on = function () { - - return function (eventName, callback) { - var self = this, listeners, n; - if (typeof eventName === 'object') { - listeners = []; - for (n in eventName) { - if (eventName.hasOwnProperty(n)) { - listeners[listeners.length] = this.on(n, eventName[n]); - } - } - return { - cancel: function () { - while (listeners.length) { - listeners.pop().cancel(); - } - } - }; - } - if (!this._subs[eventName]) { - this._subs[eventName] = [callback]; - } else { - this._subs[eventName].push(callback); - } - return { - cancel: function () { - self.off(eventName, callback); - } - }; - }; - }(); -var Ractive_prototype_off = function () { - - return function (eventName, callback) { - var subscribers, index; - if (!callback) { - if (!eventName) { - this._subs = {}; - } else { - this._subs[eventName] = []; - } - } - subscribers = this._subs[eventName]; - if (subscribers) { - index = subscribers.indexOf(callback); - if (index !== -1) { - subscribers.splice(index, 1); - } - } - }; - }(); -var shared_registerDependant = function () { - - return function (dependant) { - var depsByKeypath, deps, keys, parentKeypath, map, ractive, keypath, priority; - ractive = dependant.root; - keypath = dependant.keypath; - priority = dependant.priority; - depsByKeypath = ractive._deps[priority] || (ractive._deps[priority] = {}); - deps = depsByKeypath[keypath] || (depsByKeypath[keypath] = []); - deps[deps.length] = dependant; - dependant.registered = true; - if (!keypath) { - return; - } - keys = keypath.split('.'); - while (keys.length) { - keys.pop(); - parentKeypath = keys.join('.'); - map = ractive._depsMap[parentKeypath] || (ractive._depsMap[parentKeypath] = []); - if (map[keypath] === undefined) { - map[keypath] = 0; - map[map.length] = keypath; - } - map[keypath] += 1; - keypath = parentKeypath; - } - }; - }(); -var shared_unregisterDependant = function () { - - return function (dependant) { - var deps, index, keys, parentKeypath, map, ractive, keypath, priority; - ractive = dependant.root; - keypath = dependant.keypath; - priority = dependant.priority; - deps = ractive._deps[priority][keypath]; - index = deps.indexOf(dependant); - if (index === -1 || !dependant.registered) { - throw new Error('Attempted to remove a dependant that was no longer registered! This should not happen. If you are seeing this bug in development please raise an issue at https://github.com/RactiveJS/Ractive/issues - thanks'); - } - deps.splice(index, 1); - dependant.registered = false; - if (!keypath) { - return; - } - keys = keypath.split('.'); - while (keys.length) { - keys.pop(); - parentKeypath = keys.join('.'); - map = ractive._depsMap[parentKeypath]; - map[keypath] -= 1; - if (!map[keypath]) { - map.splice(map.indexOf(keypath), 1); - map[keypath] = undefined; - } - keypath = parentKeypath; - } - }; - }(); -var Ractive_prototype_observe_Observer = function (isEqual) { - - var Observer = function (ractive, keypath, callback, options) { - var self = this; - this.root = ractive; - this.keypath = keypath; - this.callback = callback; - this.defer = options.defer; - this.debug = options.debug; - this.proxy = { - update: function () { - self.reallyUpdate(); - } - }; - this.priority = 0; - this.context = options && options.context ? options.context : ractive; - }; - Observer.prototype = { - init: function (immediate) { - if (immediate !== false) { - this.update(); - } else { - this.value = this.root.get(this.keypath); - } - }, - update: function () { - if (this.defer && this.ready) { - this.root._deferred.observers.push(this.proxy); - return; - } - this.reallyUpdate(); - }, - reallyUpdate: function () { - var oldValue, newValue; - oldValue = this.value; - newValue = this.root.get(this.keypath); - this.value = newValue; - if (this.updating) { - return; - } - this.updating = true; - if (!isEqual(newValue, oldValue) || !this.ready) { - try { - this.callback.call(this.context, newValue, oldValue, this.keypath); - } catch (err) { - if (this.debug || this.root.debug) { - throw err; - } - } - } - this.updating = false; - } - }; - return Observer; - }(utils_isEqual); -var Ractive_prototype_observe_getPattern = function () { - - return function (ractive, pattern) { - var keys, key, values, toGet, newToGet, expand, concatenate; - keys = pattern.split('.'); - toGet = []; - expand = function (keypath) { - var value, key; - value = ractive._wrapped[keypath] ? ractive._wrapped[keypath].get() : ractive.get(keypath); - for (key in value) { - newToGet.push(keypath + '.' + key); - } - }; - concatenate = function (keypath) { - return keypath + '.' + key; - }; - while (key = keys.shift()) { - if (key === '*') { - newToGet = []; - toGet.forEach(expand); - toGet = newToGet; - } else { - if (!toGet[0]) { - toGet[0] = key; - } else { - toGet = toGet.map(concatenate); - } - } - } - values = {}; - toGet.forEach(function (keypath) { - values[keypath] = ractive.get(keypath); - }); - return values; - }; - }(); -var Ractive_prototype_observe_PatternObserver = function (isEqual, getPattern) { - - var PatternObserver, wildcard = /\*/; - PatternObserver = function (ractive, keypath, callback, options) { - this.root = ractive; - this.callback = callback; - this.defer = options.defer; - this.debug = options.debug; - this.keypath = keypath; - this.regex = new RegExp('^' + keypath.replace(/\./g, '\\.').replace(/\*/g, '[^\\.]+') + '$'); - this.values = {}; - if (this.defer) { - this.proxies = []; - } - this.priority = 'pattern'; - this.context = options && options.context ? options.context : ractive; - }; - PatternObserver.prototype = { - init: function (immediate) { - var values, keypath; - values = getPattern(this.root, this.keypath); - if (immediate !== false) { - for (keypath in values) { - if (values.hasOwnProperty(keypath)) { - this.update(keypath); - } - } - } else { - this.values = values; - } - }, - update: function (keypath) { - var values; - if (wildcard.test(keypath)) { - values = getPattern(this.root, keypath); - for (keypath in values) { - if (values.hasOwnProperty(keypath)) { - this.update(keypath); - } - } - return; - } - if (this.defer && this.ready) { - this.root._deferred.observers.push(this.getProxy(keypath)); - return; - } - this.reallyUpdate(keypath); - }, - reallyUpdate: function (keypath) { - var value = this.root.get(keypath); - if (this.updating) { - this.values[keypath] = value; - return; - } - this.updating = true; - if (!isEqual(value, this.values[keypath]) || !this.ready) { - try { - this.callback.call(this.context, value, this.values[keypath], keypath); - } catch (err) { - if (this.debug || this.root.debug) { - throw err; - } - } - this.values[keypath] = value; - } - this.updating = false; - }, - getProxy: function (keypath) { - var self = this; - if (!this.proxies[keypath]) { - this.proxies[keypath] = { - update: function () { - self.reallyUpdate(keypath); - } - }; - } - return this.proxies[keypath]; - } - }; - return PatternObserver; - }(utils_isEqual, Ractive_prototype_observe_getPattern); -var Ractive_prototype_observe_getObserverFacade = function (normaliseKeypath, registerDependant, unregisterDependant, Observer, PatternObserver) { - - var wildcard = /\*/, emptyObject = {}; - return function getObserverFacade(ractive, keypath, callback, options) { - var observer, isPatternObserver; - keypath = normaliseKeypath(keypath); - options = options || emptyObject; - if (wildcard.test(keypath)) { - observer = new PatternObserver(ractive, keypath, callback, options); - ractive._patternObservers.push(observer); - isPatternObserver = true; - } else { - observer = new Observer(ractive, keypath, callback, options); - } - registerDependant(observer); - observer.init(options.init); - observer.ready = true; - return { - cancel: function () { - var index; - if (isPatternObserver) { - index = ractive._patternObservers.indexOf(observer); - if (index !== -1) { - ractive._patternObservers.splice(index, 1); - } - } - unregisterDependant(observer); - } - }; - }; - }(utils_normaliseKeypath, shared_registerDependant, shared_unregisterDependant, Ractive_prototype_observe_Observer, Ractive_prototype_observe_PatternObserver); -var Ractive_prototype_observe__observe = function (isObject, getObserverFacade) { - - return function observe(keypath, callback, options) { - var observers = [], k; - if (isObject(keypath)) { - options = callback; - for (k in keypath) { - if (keypath.hasOwnProperty(k)) { - callback = keypath[k]; - observers[observers.length] = getObserverFacade(this, k, callback, options); - } - } - return { - cancel: function () { - while (observers.length) { - observers.pop().cancel(); - } - } - }; - } - return getObserverFacade(this, keypath, callback, options); - }; - }(utils_isObject, Ractive_prototype_observe_getObserverFacade); -var Ractive_prototype_fire = function () { + _Promise.resolve = function (value) { + return new _Promise(function (fulfil) { + fulfil(value); + }); + }; - return function (eventName) { - var args, i, len, subscribers = this._subs[eventName]; - if (!subscribers) { - return; - } - args = Array.prototype.slice.call(arguments, 1); - for (i = 0, len = subscribers.length; i < len; i += 1) { - subscribers[i].apply(this, args); - } + _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); }; - }(); -var Ractive_prototype_find = function () { - return function (selector) { - if (!this.el) { - return null; - } - return this.fragment.find(selector); + rejectPromise = function (r) { + if (called) { + return; + } + called = true; + reject(r); }; - }(); -var utils_matches = function (isClient, createElement) { - var div, methodNames, unprefixed, prefixed, vendors, i, j, makeFunction; - if (!isClient) { + 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; + } } - div = createElement('div'); - methodNames = [ - 'matches', - 'matchesSelector' - ]; - vendors = [ - 'o', - 'ms', - 'moz', - 'webkit' - ]; - makeFunction = function (methodName) { - return function (node, selector) { - return node[methodName](selector); - }; - }; - i = methodNames.length; - while (i--) { - unprefixed = methodNames[i]; - if (div[unprefixed]) { - return makeFunction(unprefixed); - } - j = vendors.length; - while (j--) { - prefixed = vendors[i] + unprefixed.substr(0, 1).toUpperCase() + unprefixed.substring(1); - if (div[prefixed]) { - return makeFunction(prefixed); - } - } + } 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"); } - return function (node, selector) { - var nodes, i; - nodes = (node.parentNode || node.document).querySelectorAll(selector); - i = nodes.length; - while (i--) { - if (nodes[i] === node) { - return true; - } - } - return false; - }; - }(config_isClient, utils_createElement); -var Ractive_prototype_shared_makeQuery_test = function (matches) { - - return function (item, noDirty) { - var itemMatches = this._isComponentQuery ? !this.selector || item.name === this.selector : matches(item.node, this.selector); - if (itemMatches) { - this.push(item.node || item.instance); - if (!noDirty) { - this._makeDirty(); - } - return true; - } - }; - }(utils_matches); -var Ractive_prototype_shared_makeQuery_cancel = function () { - return function () { - var liveQueries, selector, index; - liveQueries = this._root[this._isComponentQuery ? 'liveComponentQueries' : 'liveQueries']; - selector = this.selector; - index = liveQueries.indexOf(selector); - if (index !== -1) { - liveQueries.splice(index, 1); - liveQueries[selector] = null; - } - }; - }(); -var Ractive_prototype_shared_makeQuery_sortByItemPosition = function () { - - return function (a, b) { - var ancestryA, ancestryB, oldestA, oldestB, mutualAncestor, indexA, indexB, fragments, fragmentA, fragmentB; - ancestryA = getAncestry(a.component || a._ractive.proxy); - ancestryB = getAncestry(b.component || b._ractive.proxy); - oldestA = ancestryA[ancestryA.length - 1]; - oldestB = ancestryB[ancestryB.length - 1]; - while (oldestA && oldestA === oldestB) { - ancestryA.pop(); - ancestryB.pop(); - mutualAncestor = oldestA; - oldestA = ancestryA[ancestryA.length - 1]; - oldestB = ancestryB[ancestryB.length - 1]; - } - oldestA = oldestA.component || oldestA; - oldestB = oldestB.component || oldestB; - fragmentA = oldestA.parentFragment; - fragmentB = oldestB.parentFragment; - if (fragmentA === fragmentB) { - indexA = fragmentA.items.indexOf(oldestA); - indexB = fragmentB.items.indexOf(oldestB); - return indexA - indexB || ancestryA.length - ancestryB.length; - } - if (fragments = mutualAncestor.fragments) { - indexA = fragments.indexOf(fragmentA); - indexB = fragments.indexOf(fragmentB); - return indexA - indexB || ancestryA.length - ancestryB.length; - } - throw new Error('An unexpected condition was met while comparing the position of two components. Please file an issue at https://github.com/RactiveJS/Ractive/issues - thanks!'); - }; - function getParent(item) { - var parentFragment; - if (parentFragment = item.parentFragment) { - return parentFragment.owner; - } - if (item.component && (parentFragment = item.component.parentFragment)) { - return parentFragment.owner; - } + 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; } - function getAncestry(item) { - var ancestry, ancestor; - ancestry = [item]; - ancestor = getParent(item); - while (ancestor) { - ancestry.push(ancestor); - ancestor = getParent(ancestor); - } - return ancestry; + + _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