PageSourceSearch

https://theterfs.com/wp-content/plugins/custom-ratings/public/js/r…2Fcustom-ratings%2Fpublic%2Fjs%2Fractive.js

js theterfs.com collected 2026-10-02 08:02:07 UTC 422,534 bytes, 16,619 lines download raw bytes

1/*
2	Ractive.js v0.7.3
3	Sat Apr 25 2015 13:52:38 GMT-0400 (EDT) - commit da40f81c660ba2f09c45a09a9c20fdd34ee36d80
4
5	http://ractivejs.org
6	http://twitter.com/RactiveJS
7
8	Released under the MIT License.
9*/
10
11(function (global, factory) {
12  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
13  typeof define === 'function' && define.amd ? define(factory) :
14  global.Ractive = factory()
15}(this, function () { 'use strict';
16
17  var TEMPLATE_VERSION = 3;
18
19  var defaultOptions = {
20
21  	// render placement:
22  	el: void 0,
23  	append: false,
24
25  	// template:
26  	template: { v: TEMPLATE_VERSION, t: [] },
27
28  	// parse:     // TODO static delimiters?
29  	preserveWhitespace: false,
30  	sanitize: false,
31  	stripComments: true,
32  	delimiters: ["{{", "}}"],
33  	tripleDelimiters: ["{{{", "}}}"],
34  	interpolate: false,
35
36  	// data & binding:
37  	data: {},
38  	computed: {},
39  	magic: false,
40  	modifyArrays: true,
41  	adapt: [],
42  	isolated: false,
43  	twoway: true,
44  	lazy: false,
45
46  	// transitions:
47  	noIntro: false,
48  	transitionsEnabled: true,
49  	complete: void 0,
50
51  	// css:
52  	css: null,
53  	noCssTransform: false
54  };
55
56  var config_defaults = defaultOptions;
57
58  // These are a subset of the easing equations found at
59  // https://raw.github.com/danro/easing-js - license info
60  // follows:
61
62  // --------------------------------------------------
63  // easing.js v0.5.4
64  // Generic set of easing functions with AMD support
65  // https://github.com/danro/easing-js
66  // This code may be freely distributed under the MIT license
67  // http://danro.mit-license.org/
68  // --------------------------------------------------
69  // All functions adapted from Thomas Fuchs & Jeremy Kahn
70  // Easing Equations (c) 2003 Robert Penner, BSD license
71  // https://raw.github.com/danro/easing-js/master/LICENSE
72  // --------------------------------------------------
73
74  // In that library, the functions named easeIn, easeOut, and
75  // easeInOut below are named easeInCubic, easeOutCubic, and
76  // (you guessed it) easeInOutCubic.
77  //
78  // You can add additional easing functions to this list, and they
79  // will be globally available.
80
81  var static_easing = {
82  	linear: function (pos) {
83  		return pos;
84  	},
85  	easeIn: function (pos) {
86  		return Math.pow(pos, 3);
87  	},
88  	easeOut: function (pos) {
89  		return Math.pow(pos - 1, 3) + 1;
90  	},
91  	easeInOut: function (pos) {
92  		if ((pos /= 0.5) < 1) {
93  			return 0.5 * Math.pow(pos, 3);
94  		}
95  		return 0.5 * (Math.pow(pos - 2, 3) + 2);
96  	}
97  };
98
99  /*global console, navigator */
100  var isClient, isJsdom, hasConsole, environment__magic, namespaces, svg, vendors;
101
102  isClient = typeof document === "object";
103
104  isJsdom = typeof navigator !== "undefined" && /jsDom/.test(navigator.appName);
105
106  hasConsole = typeof console !== "undefined" && typeof console.warn === "function" && typeof console.warn.apply === "function";
107
108  try {
109  	Object.defineProperty({}, "test", { value: 0 });
110  	environment__magic = true;
111  } catch (e) {
112  	environment__magic = false;
113  }
114
115  namespaces = {
116  	html: "http://www.w3.org/1999/xhtml",
117  	mathml: "http://www.w3.org/1998/Math/MathML",
118  	svg: "http://www.w3.org/2000/svg",
119  	xlink: "http://www.w3.org/1999/xlink",
120  	xml: "http://www.w3.org/XML/1998/namespace",
121  	xmlns: "http://www.w3.org/2000/xmlns/"
122  };
123
124  if (typeof document === "undefined") {
125  	svg = false;
126  } else {
127  	svg = document && document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1");
128  }
129
130  vendors = ["o", "ms", "moz", "webkit"];
131
132  var createElement, matches, dom__div, methodNames, unprefixed, prefixed, dom__i, j, makeFunction;
133
134  // Test for SVG support
135  if (!svg) {
136  	createElement = function (type, ns) {
137  		if (ns && ns !== namespaces.html) {
138  			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";
139  		}
140
141  		return document.createElement(type);
142  	};
143  } else {
144  	createElement = function (type, ns) {
145  		if (!ns || ns === namespaces.html) {
146  			return document.createElement(type);
147  		}
148
149  		return document.createElementNS(ns, type);
150  	};
151  }
152
153  function getElement(input) {
154  	var output;
155
156  	if (!input || typeof input === "boolean") {
157  		return;
158  	}
159
160  	if (typeof window === "undefined" || !document || !input) {
161  		return null;
162  	}
163
164  	// We already have a DOM node - no work to do. (Duck typing alert!)
165  	if (input.nodeType) {
166  		return input;
167  	}
168
169  	// Get node from string
170  	if (typeof input === "string") {
171  		// try ID first
172  		output = document.getElementById(input);
173
174  		// then as selector, if possible
175  		if (!output && document.querySelector) {
176  			output = document.querySelector(input);
177  		}
178
179  		// did it work?
180  		if (output && output.nodeType) {
181  			return output;
182  		}
183  	}
184
185  	// If we've been given a collection (jQuery, Zepto etc), extract the first item
186  	if (input[0] && input[0].nodeType) {
187  		return input[0];
188  	}
189
190  	return null;
191  }
192
193  if (!isClient) {
194  	matches = null;
195  } else {
196  	dom__div = createElement("div");
197  	methodNames = ["matches", "matchesSelector"];
198
199  	makeFunction = function (methodName) {
200  		return function (node, selector) {
201  			return node[methodName](selector);
202  		};
203  	};
204
205  	dom__i = methodNames.length;
206
207  	while (dom__i-- && !matches) {
208  		unprefixed = methodNames[dom__i];
209
210  		if (dom__div[unprefixed]) {
211  			matches = makeFunction(unprefixed);
212  		} else {
213  			j = vendors.length;
214  			while (j--) {
215  				prefixed = vendors[dom__i] + unprefixed.substr(0, 1).toUpperCase() + unprefixed.substring(1);
216
217  				if (dom__div[prefixed]) {
218  					matches = makeFunction(prefixed);
219  					break;
220  				}
221  			}
222  		}
223  	}
224
225  	// IE8...
226  	if (!matches) {
227  		matches = function (node, selector) {
228  			var nodes, parentNode, i;
229
230  			parentNode = node.parentNode;
231
232  			if (!parentNode) {
233  				// empty dummy <div>
234  				dom__div.innerHTML = "";
235
236  				parentNode = dom__div;
237  				node = node.cloneNode();
238
239  				dom__div.appendChild(node);
240  			}
241
242  			nodes = parentNode.querySelectorAll(selector);
243
244  			i = nodes.length;
245  			while (i--) {
246  				if (nodes[i] === node) {
247  					return true;
248  				}
249  			}
250
251  			return false;
252  		};
253  	}
254  }
255
256  function detachNode(node) {
257  	if (node && typeof node.parentNode !== "unknown" && node.parentNode) {
258  		node.parentNode.removeChild(node);
259  	}
260
261  	return node;
262  }
263
264  function safeToStringValue(value) {
265  	return value == null || !value.toString ? "" : value;
266  }
267
268  var legacy = null;
269
270  var create, defineProperty, defineProperties;
271
272  try {
273  	Object.defineProperty({}, "test", { value: 0 });
274
275  	if (isClient) {
276  		Object.defineProperty(document.createElement("div"), "test", { value: 0 });
277  	}
278
279  	defineProperty = Object.defineProperty;
280  } catch (err) {
281  	// Object.defineProperty doesn't exist, or we're in IE8 where you can
282  	// only use it with DOM objects (what were you smoking, MSFT?)
283  	defineProperty = function (obj, prop, desc) {
284  		obj[prop] = desc.value;
285  	};
286  }
287
288  try {
289  	try {
290  		Object.defineProperties({}, { test: { value: 0 } });
291  	} catch (err) {
292  		// TODO how do we account for this? noMagic = true;
293  		throw err;
294  	}
295
296  	if (isClient) {
297  		Object.defineProperties(createElement("div"), { test: { value: 0 } });
298  	}
299
300  	defineProperties = Object.defineProperties;
301  } catch (err) {
302  	defineProperties = function (obj, props) {
303  		var prop;
304
305  		for (prop in props) {
306  			if (props.hasOwnProperty(prop)) {
307  				defineProperty(obj, prop, props[prop]);
308  			}
309  		}
310  	};
311  }
312
313  try {
314  	Object.create(null);
315
316  	create = Object.create;
317  } catch (err) {
318  	// sigh
319  	create = (function () {
320  		var F = function () {};
321
322  		return function (proto, props) {
323  			var obj;
324
325  			if (proto === null) {
326  				return {};
327  			}
328
329  			F.prototype = proto;
330  			obj = new F();
331
332  			if (props) {
333  				Object.defineProperties(obj, props);
334  			}
335
336  			return obj;
337  		};
338  	})();
339  }
340
341  function utils_object__extend(target) {
342  	for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
343  		sources[_key - 1] = arguments[_key];
344  	}
345
346  	var prop, source;
347
348  	while (source = sources.shift()) {
349  		for (prop in source) {
350  			if (hasOwn.call(source, prop)) {
351  				target[prop] = source[prop];
352  			}
353  		}
354  	}
355
356  	return target;
357  }
358
359  function fillGaps(target) {
360  	for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
361  		sources[_key - 1] = arguments[_key];
362  	}
363
364  	sources.forEach(function (s) {
365  		for (var key in s) {
366  			if (s.hasOwnProperty(key) && !(key in target)) {
367  				target[key] = s[key];
368  			}
369  		}
370  	});
371
372  	return target;
373  }
374
375  var hasOwn = Object.prototype.hasOwnProperty;
376
377  // thanks, http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
378  var is__toString = Object.prototype.toString,
379      arrayLikePattern = /^\[object (?:Array|FileList)\]$/;
380  function isArray(thing) {
381  	return is__toString.call(thing) === "[object Array]";
382  }
383
384  function isArrayLike(obj) {
385  	return arrayLikePattern.test(is__toString.call(obj));
386  }
387
388  function isEqual(a, b) {
389  	if (a === null && b === null) {
390  		return true;
391  	}
392
393  	if (typeof a === "object" || typeof b === "object") {
394  		return false;
395  	}
396
397  	return a === b;
398  }
399
400  function is__isNumeric(thing) {
401  	return !isNaN(parseFloat(thing)) && isFinite(thing);
402  }
403
404  function isObject(thing) {
405  	return thing && is__toString.call(thing) === "[object Object]";
406  }
407
408  var noop = function () {};
409
410  /* global console */
411  var alreadyWarned = {},
412      log,
413      printWarning,
414      welcome;
415
416  if (hasConsole) {
417  	(function () {
418  		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;"];
419  		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;
419\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";
420
421  		welcome = function () {
422  			var hasGroup = !!console.groupCollapsed;
423  			console[hasGroup ? "groupCollapsed" : "log"].apply(console, welcomeIntro);
424  			console.log(welcomeMessage);
425  			if (hasGroup) {
426  				console.groupEnd(welcomeIntro);
427  			}
428
429  			welcome = noop;
430  		};
431
432  		printWarning = function (message, args) {
433  			welcome();
434
435  			// extract information about the instance this message pertains to, if applicable
436  			if (typeof args[args.length - 1] === "object") {
437  				var options = args.pop();
438  				var ractive = options ? options.ractive : null;
439
440  				if (ractive) {
441  					// if this is an instance of a component that we know the name of, add
442  					// it to the message
443  					var _name = undefined;
444  					if (ractive.component && (_name = ractive.component.name)) {
445  						message = "<" + _name + "> " + message;
446  					}
447
448  					var node = undefined;
449  					if (node = options.node || ractive.fragment && ractive.fragment.rendered && ractive.find("*")) {
450  						args.push(node);
451  					}
452  				}
453  			}
454
455  			console.warn.apply(console, ["%cRactive.js: %c" + message, "color: rgb(114, 157, 52);", "color: rgb(85, 85, 85);"].concat(args));
456  		};
457
458  		log = function () {
459  			console.log.apply(console, arguments);
460  		};
461  	})();
462  } else {
463  	printWarning = log = welcome = noop;
464  }
465
466  function format(message, args) {
467  	return message.replace(/%s/g, function () {
468  		return args.shift();
469  	});
470  }
471
472  function fatal(message) {
473  	for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
474  		args[_key - 1] = arguments[_key];
475  	}
476
477  	message = format(message, args);
478  	throw new Error(message);
479  }
480
481  function logIfDebug() {
482  	if (_Ractive.DEBUG) {
483  		log.apply(null, arguments);
484  	}
485  }
486
487  function warn(message) {
488  	for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
489  		args[_key - 1] = arguments[_key];
490  	}
491
492  	message = format(message, args);
493  	printWarning(message, args);
494  }
495
496  function warnOnce(message) {
497  	for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
498  		args[_key - 1] = arguments[_key];
499  	}
500
501  	message = format(message, args);
502
503  	if (alreadyWarned[message]) {
504  		return;
505  	}
506
507  	alreadyWarned[message] = true;
508  	printWarning(message, args);
509  }
510
511  function warnIfDebug() {
512  	if (_Ractive.DEBUG) {
513  		warn.apply(null, arguments);
514  	}
515  }
516
517  function warnOnceIfDebug() {
518  	if (_Ractive.DEBUG) {
519  		warnOnce.apply(null, arguments);
520  	}
521  }
522
523  // Error messages that are used (or could be) in multiple places
524  var badArguments = "Bad arguments";
525  var noRegistryFunctionReturn = "A function was specified for \"%s\" %s, but no %s was returned";
526  var missingPlugin = function (name, type) {
527    return "Missing \"" + name + "\" " + type + " plugin. You may need to download a plugin via http://docs.ractivejs.org/latest/plugins#" + type + "s";
528  };
529
530  function findInViewHierarchy(registryName, ractive, name) {
531  	var instance = findInstance(registryName, ractive, name);
532  	return instance ? instance[registryName][name] : null;
533  }
534
535  function findInstance(registryName, ractive, name) {
536  	while (ractive) {
537  		if (name in ractive[registryName]) {
538  			return ractive;
539  		}
540
541  		if (ractive.isolated) {
542  			return null;
543  		}
544
545  		ractive = ractive.parent;
546  	}
547  }
548
549  var interpolate = function (from, to, ractive, type) {
550  	if (from === to) {
551  		return snap(to);
552  	}
553
554  	if (type) {
555
556  		var interpol = findInViewHierarchy("interpolators", ractive, type);
557  		if (interpol) {
558  			return interpol(from, to) || snap(to);
559  		}
560
561  		fatal(missingPlugin(type, "interpolator"));
562  	}
563
564  	return static_interpolators.number(from, to) || static_interpolators.array(from, to) || static_interpolators.object(from, to) || snap(to);
565  };
566
567  var shared_interpolate = interpolate;
568
569  function snap(to) {
570  	return function () {
571  		return to;
572  	};
573  }
574
575  var interpolators = {
576  	number: function (from, to) {
577  		var delta;
578
579  		if (!is__isNumeric(from) || !is__isNumeric(to)) {
580  			return null;
581  		}
582
583  		from = +from;
584  		to = +to;
585
586  		delta = to - from;
587
588  		if (!delta) {
589  			return function () {
590  				return from;
591  			};
592  		}
593
594  		return function (t) {
595  			return from + t * delta;
596  		};
597  	},
598
599  	array: function (from, to) {
600  		var intermediate, interpolators, len, i;
601
602  		if (!isArray(from) || !isArray(to)) {
603  			return null;
604  		}
605
606  		intermediate = [];
607  		interpolators = [];
608
609  		i = len = Math.min(from.length, to.length);
610  		while (i--) {
611  			interpolators[i] = shared_interpolate(from[i], to[i]);
612  		}
613
614  		// surplus values - don't interpolate, but don't exclude them either
615  		for (i = len; i < from.length; i += 1) {
616  			intermediate[i] = from[i];
617  		}
618
619  		for (i = len; i < to.length; i += 1) {
620  			intermediate[i] = to[i];
621  		}
622
623  		return function (t) {
624  			var i = len;
625
626  			while (i--) {
627  				intermediate[i] = interpolators[i](t);
628  			}
629
630  			return intermediate;
631  		};
632  	},
633
634  	object: function (from, to) {
635  		var properties, len, interpolators, intermediate, prop;
636
637  		if (!isObject(from) || !isObject(to)) {
638  			return null;
639  		}
640
641  		properties = [];
642  		intermediate = {};
643  		interpolators = {};
644
645  		for (prop in from) {
646  			if (hasOwn.call(from, prop)) {
647  				if (hasOwn.call(to, prop)) {
648  					properties.push(prop);
649  					interpolators[prop] = shared_interpolate(from[prop], to[prop]);
650  				} else {
651  					intermediate[prop] = from[prop];
652  				}
653  			}
654  		}
655
656  		for (prop in to) {
657  			if (hasOwn.call(to, prop) && !hasOwn.call(from, prop)) {
658  				intermediate[prop] = to[prop];
659  			}
660  		}
661
662  		len = properties.length;
663
664  		return function (t) {
665  			var i = len,
666  			    prop;
667
668  			while (i--) {
669  				prop = properties[i];
670
671  				intermediate[prop] = interpolators[prop](t);
672  			}
673
674  			return intermediate;
675  		};
676  	}
677  };
678
679  var static_interpolators = interpolators;
680
681  // This function takes a keypath such as 'foo.bar.baz', and returns
682  // all the variants of that keypath that include a wildcard in place
683  // of a key, such as 'foo.bar.*', 'foo.*.baz', 'foo.*.*' and so on.
684  // These are then checked against the dependants map (ractive.viewmodel.depsMap)
685  // to see if any pattern observers are downstream of one or more of
686  // these wildcard keypaths (e.g. 'foo.bar.*.status')
687  var utils_getPotentialWildcardMatches = getPotentialWildcardMatches;
688
689  var starMaps = {};
690  function getPotentialWildcardMatches(keypath) {
691  	var keys, starMap, mapper, i, result, wildcardKeypath;
692
693  	keys = keypath.split(".");
694  	if (!(starMap = starMaps[keys.length])) {
695  		starMap = getStarMap(keys.length);
696  	}
697
698  	result = [];
699
700  	mapper = function (star, i) {
701  		return star ? "*" : keys[i];
702  	};
703
704  	i = starMap.length;
705  	while (i--) {
706  		wildcardKeypath = starMap[i].map(mapper).join(".");
707
708  		if (!result.hasOwnProperty(wildcardKeypath)) {
709  			result.push(wildcardKeypath);
710  			result[wildcardKeypath] = true;
711  		}
712  	}
713
714  	return result;
715  }
716
717  // This function returns all the possible true/false combinations for
718  // a given number - e.g. for two, the possible combinations are
719  // [ true, true ], [ true, false ], [ false, true ], [ false, false ].
720  // It does so by getting all the binary values between 0 and e.g. 11
721  function getStarMap(num) {
722  	var ones = "",
723  	    max,
724  	    binary,
725  	    starMap,
726  	    mapper,
727  	    i,
728  	    j,
729  	    l,
730  	    map;
731
732  	if (!starMaps[num]) {
733  		starMap = [];
734
735  		while (ones.length < num) {
736  			ones += 1;
737  		}
738
739  		max = parseInt(ones, 2);
740
741  		mapper = function (digit) {
742  			return digit === "1";
743  		};
744
745  		for (i = 0; i <= max; i += 1) {
746  			binary = i.toString(2);
747  			while (binary.length < num) {
748  				binary = "0" + binary;
749  			}
750
751  			map = [];
752  			l = binary.length;
753  			for (j = 0; j < l; j++) {
754  				map.push(mapper(binary[j]));
755  			}
756  			starMap[i] = map;
757  		}
758
759  		starMaps[num] = starMap;
760  	}
761
762  	return starMaps[num];
763  }
764
765  var refPattern = /\[\s*(\*|[0-9]|[1-9][0-9]+)\s*\]/g;
766  var patternPattern = /\*/;
767  var keypathCache = {};
768
769  var Keypath = function (str) {
770  	var keys = str.split(".");
771
772  	this.str = str;
773
774  	if (str[0] === "@") {
775  		this.isSpecial = true;
776  		this.value = decodeKeypath(str);
777  	}
778
779  	this.firstKey = keys[0];
780  	this.lastKey = keys.pop();
781
782  	this.isPattern = patternPattern.test(str);
783
784  	this.parent = str === "" ? null : getKeypath(keys.join("."));
785  	this.isRoot = !str;
786  };
787
788  Keypath.prototype = {
789  	equalsOrStartsWith: function (keypath) {
790  		return keypath === this || this.startsWith(keypath);
791  	},
792
793  	join: function (str) {
794  		return getKeypath(this.isRoot ? String(str) : this.str + "." + str);
795  	},
796
797  	replace: function (oldKeypath, newKeypath) {
798  		if (this === oldKeypath) {
799  			return newKeypath;
800  		}
801
802  		if (this.startsWith(oldKeypath)) {
803  			return newKeypath === null ? newKeypath : getKeypath(this.str.replace(oldKeypath.str + ".", newKeypath.str + "."));
804  		}
805  	},
806
807  	startsWith: function (keypath) {
808  		if (!keypath) {
809  			// TODO under what circumstances does this happen?
810  			return false;
811  		}
812
813  		return keypath && this.str.substr(0, keypath.str.length + 1) === keypath.str + ".";
814  	},
815
816  	toString: function () {
817  		throw new Error("Bad coercion");
818  	},
819
820  	valueOf: function () {
821  		throw new Error("Bad coercion");
822  	},
823
824  	wildcardMatches: function () {
825  		return this._wildcardMatches || (this._wildcardMatches = utils_getPotentialWildcardMatches(this.str));
826  	}
827  };
828  function assignNewKeypath(target, property, oldKeypath, newKeypath) {
829  	var existingKeypath = target[property];
830
831  	if (existingKeypath && (existingKeypath.equalsOrStartsWith(newKeypath) || !existingKeypath.equalsOrStartsWith(oldKeypath))) {
832  		return;
833  	}
834
835  	target[property] = existingKeypath ? existingKeypath.replace(oldKeypath, newKeypath) : newKeypath;
836  	return true;
837  }
838
839  function decodeKeypath(keypath) {
840  	var value = keypath.slice(2);
841
842  	if (keypath[1] === "i") {
843  		return is__isNumeric(value) ? +value : value;
844  	} else {
845  		return value;
846  	}
847  }
848
849  function getKeypath(str) {
850  	if (str == null) {
851  		return str;
852  	}
853
854  	// TODO it *may* be worth having two versions of this function - one where
855  	// keypathCache inherits from null, and one for IE8. Depends on how
856  	// much of an overhead hasOwnProperty is - probably negligible
857  	if (!keypathCache.hasOwnProperty(str)) {
858  		keypathCache[str] = new Keypath(str);
859  	}
860
861  	return keypathCache[str];
862  }
863
864  function getMatchingKeypaths(ractive, keypath) {
865  	var keys, key, matchingKeypaths;
866
867  	keys = keypath.str.split(".");
868  	matchingKeypaths = [rootKeypath];
869
870  	while (key = keys.shift()) {
871  		if (key === "*") {
872  			// expand to find all valid child keypaths
873  			matchingKeypaths = matchingKeypaths.reduce(expand, []);
874  		} else {
875  			if (matchingKeypaths[0] === rootKeypath) {
876  				// first key
877  				matchingKeypaths[0] = getKeypath(key);
878  			} else {
879  				matchingKeypaths = matchingKeypaths.map(concatenate(key));
880  			}
881  		}
882  	}
883
884  	return matchingKeypaths;
885
886  	function expand(matchingKeypaths, keypath) {
887  		var wrapper, value, keys;
888
889  		if (keypath.isRoot) {
890  			keys = [].concat(Object.keys(ractive.viewmodel.data), Object.keys(ractive.viewmodel.mappings), Object.keys(ractive.viewmodel.computations));
891  		} else {
892  			wrapper = ractive.viewmodel.wrapped[keypath.str];
893  			value = wrapper ? wrapper.get() : ractive.viewmodel.get(keypath);
894
895  			keys = value ? Object.keys(value) : null;
896  		}
897
898  		if (keys) {
899  			keys.forEach(function (key) {
900  				if (key !== "_ractive" || !isArray(value)) {
901  					matchingKeypaths.push(keypath.join(key));
902  				}
903  			});
904  		}
905
906  		return matchingKeypaths;
907  	}
908  }
909
910  function concatenate(key) {
911  	return function (keypath) {
912  		return keypath.join(key);
913  	};
914  }
915  function normalise(ref) {
916  	return ref ? ref.replace(refPattern, ".$1") : "";
917  }
918
919  var rootKeypath = getKeypath("");
920
921  var shared_add = add;
922  var shared_add__errorMessage = "Cannot add to a non-numeric value";
923  function add(root, keypath, d) {
924  	if (typeof keypath !== "string" || !is__isNumeric(d)) {
925  		throw new Error("Bad arguments");
926  	}
927
928  	var value = undefined,
929  	    changes = undefined;
930
931  	if (/\*/.test(keypath)) {
932  		changes = {};
933
934  		getMatchingKeypaths(root, getKeypath(normalise(keypath))).forEach(function (keypath) {
935  			var value = root.viewmodel.get(keypath);
936
937  			if (!is__isNumeric(value)) {
938  				throw new Error(shared_add__errorMessage);
939  			}
940
941  			changes[keypath.str] = value + d;
942  		});
943
944  		return root.set(changes);
945  	}
946
947  	value = root.get(keypath);
948
949  	if (!is__isNumeric(value)) {
950  		throw new Error(shared_add__errorMessage);
951  	}
952
953  	return root.set(keypath, +value + d);
954  }
955
956  var prototype_add = Ractive$add;
957  function Ractive$add(keypath, d) {
958  	return shared_add(this, keypath, d === undefined ? 1 : +d);
959  }
960
961  var requestAnimationFrame;
962
963  // If window doesn't exist, we don't need requestAnimationFrame
964  if (typeof window === "undefined") {
965  	requestAnimationFrame = null;
966  } else {
967  	// https://gist.github.com/paulirish/1579671
968  	(function (vendors, lastTime, window) {
969
970  		var x, setTimeout;
971
972  		if (window.requestAnimationFrame) {
973  			return;
974  		}
975
976  		for (x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
977  			window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"];
978  		}
979
980  		if (!window.requestAnimationFrame) {
981  			setTimeout = window.setTimeout;
982
983  			window.requestAnimationFrame = function (callback) {
984  				var currTime, timeToCall, id;
985
986  				currTime = Date.now();
987  				timeToCall = Math.max(0, 16 - (currTime - lastTime));
988  				id = setTimeout(function () {
989  					callback(currTime + timeToCall);
990  				}, timeToCall);
991
992  				lastTime = currTime + timeToCall;
993  				return id;
994  			};
995  		}
996  	})(vendors, 0, window);
997
998  	requestAnimationFrame = window.requestAnimationFrame;
999  }
1000
1001  var rAF = requestAnimationFrame;
1002
1003  var getTime;
1004
1005  if (typeof window !== "undefined" && window.performance && typeof window.performance.now === "function") {
1006  	getTime = function () {
1007  		return window.performance.now();
1008  	};
1009  } else {
1010  	getTime = function () {
1011  		return Date.now();
1012  	};
1013  }
1014
1015  var utils_getTime = getTime;
1016
1017  var deprecations = {
1018  	construct: {
1019  		deprecated: "beforeInit",
1020  		replacement: "onconstruct"
1021  	},
1022  	render: {
1023  		deprecated: "init",
1024  		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."
1025  	},
1026  	complete: {
1027  		deprecated: "complete",
1028  		replacement: "oncomplete"
1029  	}
1030  };
1031
1032  function Hook(event) {
1033  	this.event = event;
1034  	this.method = "on" + event;
1035  	this.deprecate = deprecations[event];
1036  }
1037
1038  Hook.prototype.fire = function (ractive, arg) {
1039  	function call(method) {
1040  		if (ractive[method]) {
1041  			arg ? ractive[method](arg) : ractive[method]();
1042  			return true;
1043  		}
1044  	}
1045
1046  	call(this.method);
1047
1048  	if (!ractive[this.method] && this.deprecate && call(this.deprecate.deprecated)) {
1049  		if (this.deprecate.message) {
1050  			warnIfDebug(this.deprecate.message);
1051  		} else {
1052  			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);
1053  		}
1054  	}
1055
1056  	arg ? ractive.fire(this.event, arg) : ractive.fire(this.event);
1057  };
1058
1059  var hooks_Hook = Hook;
1060
1061  function addToArray(array, value) {
1062  	var index = array.indexOf(value);
1063
1064  	if (index === -1) {
1065  		array.push(value);
1066  	}
1067  }
1068
1069  function arrayContains(array, value) {
1070  	for (var i = 0, c = array.length; i < c; i++) {
1071  		if (array[i] == value) {
1072  			return true;
1073  		}
1074  	}
1075
1076  	return false;
1077  }
1078
1079  function arrayContentsMatch(a, b) {
1080  	var i;
1081
1082  	if (!isArray(a) || !isArray(b)) {
1083  		return false;
1084  	}
1085
1086  	if (a.length !== b.length) {
1087  		return false;
1088  	}
1089
1090  	i = a.length;
1091  	while (i--) {
1092  		if (a[i] !== b[i]) {
1093  			return false;
1094  		}
1095  	}
1096
1097  	return true;
1098  }
1099
1100  function ensureArray(x) {
1101  	if (typeof x === "string") {
1102  		return [x];
1103  	}
1104
1105  	if (x === undefined) {
1106  		return [];
1107  	}
1108
1109  	return x;
1110  }
1111
1112  function lastItem(array) {
1113  	return array[array.length - 1];
1114  }
1115
1116  function removeFromArray(array, member) {
1117  	var index = array.indexOf(member);
1118
1119  	if (index !== -1) {
1120  		array.splice(index, 1);
1121  	}
1122  }
1123
1124  function toArray(arrayLike) {
1125  	var array = [],
1126  	    i = arrayLike.length;
1127  	while (i--) {
1128  		array[i] = arrayLike[i];
1129  	}
1130
1131  	return array;
1132  }
1133
1134  var _Promise,
1135      PENDING = {},
1136      FULFILLED = {},
1137      REJECTED = {};
1138
1139  if (typeof Promise === "function") {
1140  	// use native Promise
1141  	_Promise = Promise;
1142  } else {
1143  	_Promise = function (callback) {
1144  		var fulfilledHandlers = [],
1145  		    rejectedHandlers = [],
1146  		    state = PENDING,
1147  		    result,
1148  		    dispatchHandlers,
1149  		    makeResolver,
1150  		    fulfil,
1151  		    reject,
1152  		    promise;
1153
1154  		makeResolver = function (newState) {
1155  			return function (value) {
1156  				if (state !== PENDING) {
1157  					return;
1158  				}
1159
1160  				result = value;
1161  				state = newState;
1162
1163  				dispatchHandlers = makeDispatcher(state === FULFILLED ? fulfilledHandlers : rejectedHandlers, result);
1164
1165  				// dispatch onFulfilled and onRejected handlers asynchronously
1166  				wait(dispatchHandlers);
1167  			};
1168  		};
1169
1170  		fulfil = makeResolver(FULFILLED);
1171  		reject = makeResolver(REJECTED);
1172
1173  		try {
1174  			callback(fulfil, reject);
1175  		} catch (err) {
1176  			reject(err);
1177  		}
1178
1179  		promise = {
1180  			// `then()` returns a Promise - 2.2.7
1181  			then: function (onFulfilled, onRejected) {
1182  				var promise2 = new _Promise(function (fulfil, reject) {
1183
1184  					var processResolutionHandler = function (handler, handlers, forward) {
1185
1186  						// 2.2.1.1
1187  						if (typeof handler === "function") {
1188  							handlers.push(function (p1result) {
1189  								var x;
1190
1191  								try {
1192  									x = handler(p1result);
1193  									utils_Promise__resolve(promise2, x, fulfil, reject);
1194  								} catch (err) {
1195  									reject(err);
1196  								}
1197  							});
1198  						} else {
1199  							// Forward the result of promise1 to promise2, if resolution handlers
1200  							// are not given
1201  							handlers.push(forward);
1202  						}
1203  					};
1204
1205  					// 2.2
1206  					processResolutionHandler(onFulfilled, fulfilledHandlers, fulfil);
1207  					processResolutionHandler(onRejected, rejectedHandlers, reject);
1208
1209  					if (state !== PENDING) {
1210  						// If the promise has resolved already, dispatch the appropriate handlers asynchronously
1211  						wait(dispatchHandlers);
1212  					}
1213  				});
1214
1215  				return promise2;
1216  			}
1217  		};
1218
1219  		promise["catch"] = function (onRejected) {
1220  			return this.then(null, onRejected);
1221  		};
1222
1223  		return promise;
1224  	};
1225
1226  	_Promise.all = function (promises) {
1227  		return new _Promise(function (fulfil, reject) {
1228  			var result = [],
1229  			    pending,
1230  			    i,
1231  			    processPromise;
1232
1233  			if (!promises.length) {
1234  				fulfil(result);
1235  				return;
1236  			}
1237
1238  			processPromise = function (promise, i) {
1239  				if (promise && typeof promise.then === "function") {
1240  					promise.then(function (value) {
1241  						result[i] = value;
1242  						--pending || fulfil(result);
1243  					}, reject);
1244  				} else {
1245  					result[i] = promise;
1246  					--pending || fulfil(result);
1247  				}
1248  			};
1249
1250  			pending = i = promises.length;
1251  			while (i--) {
1252  				processPromise(promises[i], i);
1253  			}
1254  		});
1255  	};
1256
1257  	_Promise.resolve = function (value) {
1258  		return new _Promise(function (fulfil) {
1259  			fulfil(value);
1260  		});
1261  	};
1262
1263  	_Promise.reject = function (reason) {
1264  		return new _Promise(function (fulfil, reject) {
1265  			reject(reason);
1266  		});
1267  	};
1268  }
1269
1270  var utils_Promise = _Promise;
1271
1272  // TODO use MutationObservers or something to simulate setImmediate
1273  function wait(callback) {
1274  	setTimeout(callback, 0);
1275  }
1276
1277  function makeDispatcher(handlers, result) {
1278  	return function () {
1279  		var handler;
1280
1281  		while (handler = handlers.shift()) {
1282  			handler(result);
1283  		}
1284  	};
1285  }
1286
1287  function utils_Promise__resolve(promise, x, fulfil, reject) {
1288  	// Promise Resolution Procedure
1289  	var then;
1290
1291  	// 2.3.1
1292  	if (x === promise) {
1293  		throw new TypeError("A promise's fulfillment handler cannot return the same promise");
1294  	}
1295
1296  	// 2.3.2
1297  	if (x instanceof _Promise) {
1298  		x.then(fulfil, reject);
1299  	}
1300
1301  	// 2.3.3
1302  	else if (x && (typeof x === "object" || typeof x === "function")) {
1303  		try {
1304  			then = x.then; // 2.3.3.1
1305  		} catch (e) {
1306  			reject(e); // 2.3.3.2
1307  			return;
1308  		}
1309
1310  		// 2.3.3.3
1311  		if (typeof then === "function") {
1312  			var called, resolvePromise, rejectPromise;
1313
1314  			resolvePromise = function (y) {
1315  				if (called) {
1316  					return;
1317  				}
1318  				called = true;
1319  				utils_Promise__resolve(promise, y, fulfil, reject);
1320  			};
1321
1322  			rejectPromise = function (r) {
1323  				if (called) {
1324  					return;
1325  				}
1326  				called = true;
1327  				reject(r);
1328  			};
1329
1330  			try {
1331  				then.call(x, resolvePromise, rejectPromise);
1332  			} catch (e) {
1333  				if (!called) {
1334  					// 2.3.3.3.4.1
1335  					reject(e); // 2.3.3.3.4.2
1336  					called = true;
1337  					return;
1338  				}
1339  			}
1340  		} else {
1341  			fulfil(x);
1342  		}
1343  	} else {
1344  		fulfil(x);
1345  	}
1346  }
1347
1348  var getInnerContext = function (fragment) {
1349  	do {
1350  		if (fragment.context !== undefined) {
1351  			return fragment.context;
1352  		}
1353  	} while (fragment = fragment.parent);
1354
1355  	return rootKeypath;
1356  };
1357
1358  var shared_resolveRef = resolveRef;
1359
1360  function resolveRef(ractive, ref, fragment) {
1361  	var keypath;
1362
1363  	ref = normalise(ref);
1364
1365  	// If a reference begins '~/', it's a top-level reference
1366  	if (ref.substr(0, 2) === "~/") {
1367  		keypath = getKeypath(ref.substring(2));
1368  		createMappingIfNecessary(ractive, keypath.firstKey, fragment);
1369  	}
1370
1371  	// If a reference begins with '.', it's either a restricted reference or
1372  	// an ancestor reference...
1373  	else if (ref[0] === ".") {
1374  		keypath = resolveAncestorRef(getInnerContext(fragment), ref);
1375
1376  		if (keypath) {
1377  			createMappingIfNecessary(ractive, keypath.firstKey, fragment);
1378  		}
1379  	}
1380
1381  	// ...otherwise we need to figure out the keypath based on context
1382  	else {
1383  		keypath = resolveAmbiguousReference(ractive, getKeypath(ref), fragment);
1384  	}
1385
1386  	return keypath;
1387  }
1388
1389  function resolveAncestorRef(baseContext, ref) {
1390  	var contextKeys;
1391
1392  	// TODO...
1393  	if (baseContext != undefined && typeof baseContext !== "string") {
1394  		baseContext = baseContext.str;
1395  	}
1396
1397  	// {{.}} means 'current context'
1398  	if (ref === ".") return getKeypath(baseContext);
1399
1400  	contextKeys = baseContext ? baseContext.split(".") : [];
1401
1402  	// ancestor references (starting "../") go up the tree
1403  	if (ref.substr(0, 3) === "../") {
1404  		while (ref.substr(0, 3) === "../") {
1405  			if (!contextKeys.length) {
1406  				throw new Error("Could not resolve reference - too many \"../\" prefixes");
1407  			}
1408
1409  			contextKeys.pop();
1410  			ref = ref.substring(3);
1411  		}
1412
1413  		contextKeys.push(ref);
1414  		return getKeypath(contextKeys.join("."));
1415  	}
1416
1417  	// not an ancestor reference - must be a restricted reference (prepended with "." or "./")
1418  	if (!baseContext) {
1419  		return getKeypath(ref.replace(/^\.\/?/, ""));
1420  	}
1421
1422  	return getKeypath(baseContext + ref.replace(/^\.\//, "."));
1423  }
1424
1425  function resolveAmbiguousReference(ractive, ref, fragment, isParentLookup) {
1426  	var context, key, parentValue, hasContextChain, parentKeypath;
1427
1428  	if (ref.isRoot) {
1429  		return ref;
1430  	}
1431
1432  	key = ref.firstKey;
1433
1434  	while (fragment) {
1435  		context = fragment.context;
1436  		fragment = fragment.parent;
1437
1438  		if (!context) {
1439  			continue;
1440  		}
1441
1442  		hasContextChain = true;
1443  		parentValue = ractive.viewmodel.get(context);
1444
1445  		if (parentValue && (typeof parentValue === "object" || typeof parentValue === "function") && key in parentValue) {
1446  			return context.join(ref.str);
1447  		}
1448  	}
1449
1450  	// Root/computed/mapped property?
1451  	if (isRootProperty(ractive.viewmodel, key)) {
1452  		return ref;
1453  	}
1454
1455  	// If this is an inline component, and it's not isolated, we
1456  	// can try going up the scope chain
1457  	if (ractive.parent && !ractive.isolated) {
1458  		hasContextChain = true;
1459  		fragment = ractive.component.parentFragment;
1460
1461  		key = getKeypath(key);
1462
1463  		if (parentKeypath = resolveAmbiguousReference(ractive.parent, key, fragment, true)) {
1464  			// We need to create an inter-component binding
1465  			ractive.viewmodel.map(key, {
1466  				origin: ractive.parent.viewmodel,
1467  				keypath: parentKeypath
1468  			});
1469
1470  			return ref;
1471  		}
1472  	}
1473
1474  	// If there's no context chain, and the instance is either a) isolated or
1475  	// b) an orphan, then we know that the keypath is identical to the reference
1476  	if (!isParentLookup && !hasContextChain) {
1477  		// the data object needs to have a property by this name,
1478  		// to prevent future failed lookups
1479  		ractive.viewmodel.set(ref, undefined);
1480  		return ref;
1481  	}
1482  }
1483
1484  function createMappingIfNecessary(ractive, key) {
1485  	var parentKeypath;
1486
1487  	if (!ractive.parent || ractive.isolated || isRootProperty(ractive.viewmodel, key)) {
1488  		return;
1489  	}
1490
1491  	key = getKeypath(key);
1492
1493  	if (parentKeypath = resolveAmbiguousReference(ractive.parent, key, ractive.component.parentFragment, true)) {
1494  		ractive.viewmodel.map(key, {
1495  			origin: ractive.parent.viewmodel,
1496  			keypath: parentKeypath
1497  		});
1498  	}
1499  }
1500
1501  function isRootProperty(viewmodel, key) {
1502  	// special case for reference to root
1503  	return key === "" || key in viewmodel.data || key in viewmodel.computations || key in viewmodel.mappings;
1504  }
1505
1506  function teardown(x) {
1507    x.teardown();
1508  }
1509
1510  function methodCallers__unbind(x) {
1511    x.unbind();
1512  }
1513
1514  function methodCallers__unrender(x) {
1515    x.unrender();
1516  }
1517
1518  function cancel(x) {
1519    x.cancel();
1520  }
1521
1522  var TransitionManager = function (callback, parent) {
1523  	this.callback = callback;
1524  	this.parent = parent;
1525
1526  	this.intros = [];
1527  	this.outros = [];
1528
1529  	this.children = [];
1530  	this.totalChildren = this.outroChildren = 0;
1531
1532  	this.detachQueue = [];
1533  	this.decoratorQueue = [];
1534  	this.outrosComplete = false;
1535
1536  	if (parent) {
1537  		parent.addChild(this);
1538  	}
1539  };
1540
1541  TransitionManager.prototype = {
1542  	addChild: function (child) {
1543  		this.children.push(child);
1544
1545  		this.totalChildren += 1;
1546  		this.outroChildren += 1;
1547  	},
1548
1549  	decrementOutros: function () {
1550  		this.outroChildren -= 1;
1551  		check(this);
1552  	},
1553
1554  	decrementTotal: function () {
1555  		this.totalChildren -= 1;
1556  		check(this);
1557  	},
1558
1559  	add: function (transition) {
1560  		var list = transition.isIntro ? this.intros : this.outros;
1561  		list.push(transition);
1562  	},
1563
1564  	addDecorator: function (decorator) {
1565  		this.decoratorQueue.push(decorator);
1566  	},
1567
1568  	remove: function (transition) {
1569  		var list = transition.isIntro ? this.intros : this.outros;
1570  		removeFromArray(list, transition);
1571  		check(this);
1572  	},
1573
1574  	init: function () {
1575  		this.ready = true;
1576  		check(this);
1577  	},
1578
1579  	detachNodes: function () {
1580  		this.decoratorQueue.forEach(teardown);
1581  		this.detachQueue.forEach(detach);
1582  		this.children.forEach(detachNodes);
1583  	}
1584  };
1585
1586  function detach(element) {
1587  	element.detach();
1588  }
1589
1590  function detachNodes(tm) {
1591  	tm.detachNodes();
1592  }
1593
1594  function check(tm) {
1595  	if (!tm.ready || tm.outros.length || tm.outroChildren) return;
1596
1597  	// If all outros are complete, and we haven't already done this,
1598  	// we notify the parent if there is one, otherwise
1599  	// start detaching nodes
1600  	if (!tm.outrosComplete) {
1601  		if (tm.parent) {
1602  			tm.parent.decrementOutros(tm);
1603  		} else {
1604  			tm.detachNodes();
1605  		}
1606
1607  		tm.outrosComplete = true;
1608  	}
1609
1610  	// Once everything is done, we can notify parent transition
1611  	// manager and call the callback
1612  	if (!tm.intros.length && !tm.totalChildren) {
1613  		if (typeof tm.callback === "function") {
1614  			tm.callback();
1615  		}
1616
1617  		if (tm.parent) {
1618  			tm.parent.decrementTotal();
1619  		}
1620  	}
1621  }
1622
1623  var global_TransitionManager = TransitionManager;
1624
1625  var batch,
1626      runloop,
1627      unresolved = [],
1628      changeHook = new hooks_Hook("change");
1629
1630  runloop = {
1631  	start: function (instance, returnPromise) {
1632  		var promise, fulfilPromise;
1633
1634  		if (returnPromise) {
1635  			promise = new utils_Promise(function (f) {
1636  				return fulfilPromise = f;
1637  			});
1638  		}
1639
1640  		batch = {
1641  			previousBatch: batch,
1642  			transitionManager: new global_TransitionManager(fulfilPromise, batch && batch.transitionManager),
1643  			views: [],
1644  			tasks: [],
1645  			ractives: [],
1646  			instance: instance
1647  		};
1648
1649  		if (instance) {
1650  			batch.ractives.push(instance);
1651  		}
1652
1653  		return promise;
1654  	},
1655
1656  	end: function () {
1657  		flushChanges();
1658
1659  		batch.transitionManager.init();
1660  		if (!batch.previousBatch && !!batch.instance) batch.instance.viewmodel.changes = [];
1661  		batch = batch.previousBatch;
1662  	},
1663
1664  	addRactive: function (ractive) {
1665  		if (batch) {
1666  			addToArray(batch.ractives, ractive);
1667  		}
1668  	},
1669
1670  	registerTransition: function (transition) {
1671  		transition._manager = batch.transitionManager;
1672  		batch.transitionManager.add(transition);
1673  	},
1674
1675  	registerDecorator: function (decorator) {
1676  		batch.transitionManager.addDecorator(decorator);
1677  	},
1678
1679  	addView: function (view) {
1680  		batch.views.push(view);
1681  	},
1682
1683  	addUnresolved: function (thing) {
1684  		unresolved.push(thing);
1685  	},
1686
1687  	removeUnresolved: function (thing) {
1688  		removeFromArray(unresolved, thing);
1689  	},
1690
1691  	// synchronise node detachments with transition ends
1692  	detachWhenReady: function (thing) {
1693  		batch.transitionManager.detachQueue.push(thing);
1694  	},
1695
1696  	scheduleTask: function (task, postRender) {
1697  		var _batch;
1698
1699  		if (!batch) {
1700  			task();
1701  		} else {
1702  			_batch = batch;
1703  			while (postRender && _batch.previousBatch) {
1704  				// this can't happen until the DOM has been fully updated
1705  				// otherwise in some situations (with components inside elements)
1706  				// transitions and decorators will initialise prematurely
1707  				_batch = _batch.previousBatch;
1708  			}
1709
1710  			_batch.tasks.push(task);
1711  		}
1712  	}
1713  };
1714
1715  var global_runloop = runloop;
1716
1717  function flushChanges() {
1718  	var i, thing, changeHash;
1719
1720  	while (batch.ractives.length) {
1721  		thing = batch.ractives.pop();
1722  		changeHash = thing.viewmodel.applyChanges();
1723
1724  		if (changeHash) {
1725  			changeHook.fire(thing, changeHash);
1726  		}
1727  	}
1728
1729  	attemptKeypathResolution();
1730
1731  	// Now that changes have been fully propagated, we can update the DOM
1732  	// and complete other tasks
1733  	for (i = 0; i < batch.views.length; i += 1) {
1734  		batch.views[i].update();
1735  	}
1736  	batch.views.length = 0;
1737
1738  	for (i = 0; i < batch.tasks.length; i += 1) {
1739  		batch.tasks[i]();
1740  	}
1741  	batch.tasks.length = 0;
1742
1743  	// If updating the view caused some model blowback - e.g. a triple
1744  	// containing <option> elements caused the binding on the <select>
1745  	// to update - then we start over
1746  	if (batch.ractives.length) return flushChanges();
1747  }
1748
1749  function attemptKeypathResolution() {
1750  	var i, item, keypath, resolved;
1751
1752  	i = unresolved.length;
1753
1754  	// see if we can resolve any unresolved references
1755  	while (i--) {
1756  		item = unresolved[i];
1757
1758  		if (item.keypath) {
1759  			// it resolved some other way. TODO how? two-way binding? Seems
1760  			// weird that we'd still end up here
1761  			unresolved.splice(i, 1);
1762  			continue; // avoid removing the wrong thing should the next condition be true
1763  		}
1764
1765  		if (keypath = shared_resolveRef(item.root, item.ref, item.parentFragment)) {
1766  			(resolved || (resolved = [])).push({
1767  				item: item,
1768  				keypath: keypath
1769  			});
1770
1771  			unresolved.splice(i, 1);
1772  		}
1773  	}
1774
1775  	if (resolved) {
1776  		resolved.forEach(global_runloop__resolve);
1777  	}
1778  }
1779
1780  function global_runloop__resolve(resolved) {
1781  	resolved.item.resolve(resolved.keypath);
1782  }
1783
1784  var queue = [];
1785
1786  var animations = {
1787  	tick: function () {
1788  		var i, animation, now;
1789
1790  		now = utils_getTime();
1791
1792  		global_runloop.start();
1793
1794  		for (i = 0; i < queue.length; i += 1) {
1795  			animation = queue[i];
1796
1797  			if (!animation.tick(now)) {
1798  				// animation is complete, remove it from the stack, and decrement i so we don't miss one
1799  				queue.splice(i--, 1);
1800  			}
1801  		}
1802
1803  		global_runloop.end();
1804
1805  		if (queue.length) {
1806  			rAF(animations.tick);
1807  		} else {
1808  			animations.running = false;
1809  		}
1810  	},
1811
1812  	add: function (animation) {
1813  		queue.push(animation);
1814
1815  		if (!animations.running) {
1816  			animations.running = true;
1817  			rAF(animations.tick);
1818  		}
1819  	},
1820
1821  	// TODO optimise this
1822  	abort: function (keypath, root) {
1823  		var i = queue.length,
1824  		    animation;
1825
1826  		while (i--) {
1827  			animation = queue[i];
1828
1829  			if (animation.root === root && animation.keypath === keypath) {
1830  				animation.stop();
1831  			}
1832  		}
1833  	}
1834  };
1835
1836  var shared_animations = animations;
1837
1838  var Animation = function (options) {
1839  	var key;
1840
1841  	this.startTime = Date.now();
1842
1843  	// from and to
1844  	for (key in options) {
1845  		if (options.hasOwnProperty(key)) {
1846  			this[key] = options[key];
1847  		}
1848  	}
1849
1850  	this.interpolator = shared_interpolate(this.from, this.to, this.root, this.interpolator);
1851  	this.running = true;
1852
1853  	this.tick();
1854  };
1855
1856  Animation.prototype = {
1857  	tick: function () {
1858  		var elapsed, t, value, timeNow, index, keypath;
1859
1860  		keypath = this.keypath;
1861
1862  		if (this.running) {
1863  			timeNow = Date.now();
1864  			elapsed = timeNow - this.startTime;
1865
1866  			if (elapsed >= this.duration) {
1867  				if (keypath !== null) {
1868  					global_runloop.start(this.root);
1869  					this.root.viewmodel.set(keypath, this.to);
1870  					global_runloop.end();
1871  				}
1872
1873  				if (this.step) {
1874  					this.step(1, this.to);
1875  				}
1876
1877  				this.complete(this.to);
1878
1879  				index = this.root._animations.indexOf(this);
1880
1881  				// TODO investigate why this happens
1882  				if (index === -1) {
1883  					warnIfDebug("Animation was not found");
1884  				}
1885
1886  				this.root._animations.splice(index, 1);
1887
1888  				this.running = false;
1889  				return false; // remove from the stack
1890  			}
1891
1892  			t = this.easing ? this.easing(elapsed / this.duration) : elapsed / this.duration;
1893
1894  			if (keypath !== null) {
1895  				value = this.interpolator(t);
1896  				global_runloop.start(this.root);
1897  				this.root.viewmodel.set(keypath, value);
1898  				global_runloop.end();
1899  			}
1900
1901  			if (this.step) {
1902  				this.step(t, value);
1903  			}
1904
1905  			return true; // keep in the stack
1906  		}
1907
1908  		return false; // remove from the stack
1909  	},
1910
1911  	stop: function () {
1912  		var index;
1913
1914  		this.running = false;
1915
1916  		index = this.root._animations.indexOf(this);
1917
1918  		// TODO investigate why this happens
1919  		if (index === -1) {
1920  			warnIfDebug("Animation was not found");
1921  		}
1922
1923  		this.root._animations.splice(index, 1);
1924  	}
1925  };
1926
1927  var animate_Animation = Animation;
1928
1929  var prototype_animate = Ractive$animate;
1930
1931  var noAnimation = { stop: noop };
1932  function Ractive$animate(keypath, to, options) {
1933  	var promise, fulfilPromise, k, animation, animations, easing, duration, step, complete, makeValueCollector, currentValues, collectValue, dummy, dummyOptions;
1934
1935  	promise = new utils_Promise(function (fulfil) {
1936  		return fulfilPromise = fulfil;
1937  	});
1938
1939  	// animate multiple keypaths
1940  	if (typeof keypath === "object") {
1941  		options = to || {};
1942  		easing = options.easing;
1943  		duration = options.duration;
1944
1945  		animations = [];
1946
1947  		// we don't want to pass the `step` and `complete` handlers, as they will
1948  		// run for each animation! So instead we'll store the handlers and create
1949  		// our own...
1950  		step = options.step;
1951  		complete = options.complete;
1952
1953  		if (step || complete) {
1954  			currentValues = {};
1955
1956  			options.step = null;
1957  			options.complete = null;
1958
1959  			makeValueCollector = function (keypath) {
1960  				return function (t, value) {
1961  					currentValues[keypath] = value;
1962  				};
1963  			};
1964  		}
1965
1966  		for (k in keypath) {
1967  			if (keypath.hasOwnProperty(k)) {
1968  				if (step || complete) {
1969  					collectValue = makeValueCollector(k);
1970  					options = { easing: easing, duration: duration };
1971
1972  					if (step) {
1973  						options.step = collectValue;
1974  					}
1975  				}
1976
1977  				options.complete = complete ? collectValue : noop;
1978  				animations.push(animate(this, k, keypath[k], options));
1979  			}
1980  		}
1981
1982  		// Create a dummy animation, to facilitate step/complete
1983  		// callbacks, and Promise fulfilment
1984  		dummyOptions = { easing: easing, duration: duration };
1985
1986  		if (step) {
1987  			dummyOptions.step = function (t) {
1988  				return step(t, currentValues);
1989  			};
1990  		}
1991
1992  		if (complete) {
1993  			promise.then(function (t) {
1994  				return complete(t, currentValues);
1995  			});
1996  		}
1997
1998  		dummyOptions.complete = fulfilPromise;
1999
2000  		dummy = animate(this, null, null, dummyOptions);
2001  		animations.push(dummy);
2002
2003  		promise.stop = function () {
2004  			var animation;
2005
2006  			while (animation = animations.pop()) {
2007  				animation.stop();
2008  			}
2009
2010  			if (dummy) {
2011  				dummy.stop();
2012  			}
2013  		};
2014
2015  		return promise;
2016  	}
2017
2018  	// animate a single keypath
2019  	options = options || {};
2020
2021  	if (options.complete) {
2022  		promise.then(options.complete);
2023  	}
2024
2025  	options.complete = fulfilPromise;
2026  	animation = animate(this, keypath, to, options);
2027
2028  	promise.stop = function () {
2029  		return animation.stop();
2030  	};
2031  	return promise;
2032  }
2033
2034  function animate(root, keypath, to, options) {
2035  	var easing, duration, animation, from;
2036
2037  	if (keypath) {
2038  		keypath = getKeypath(normalise(keypath));
2039  	}
2040
2041  	if (keypath !== null) {
2042  		from = root.viewmodel.get(keypath);
2043  	}
2044
2045  	// cancel any existing animation
2046  	// TODO what about upstream/downstream keypaths?
2047  	shared_animations.abort(keypath, root);
2048
2049  	// don't bother animating values that stay the same
2050  	if (isEqual(from, to)) {
2051  		if (options.complete) {
2052  			options.complete(options.to);
2053  		}
2054
2055  		return noAnimation;
2056  	}
2057
2058  	// easing function
2059  	if (options.easing) {
2060  		if (typeof options.easing === "function") {
2061  			easing = options.easing;
2062  		} else {
2063  			easing = root.easing[options.easing];
2064  		}
2065
2066  		if (typeof easing !== "function") {
2067  			easing = null;
2068  		}
2069  	}
2070
2071  	// duration
2072  	duration = options.duration === undefined ? 400 : options.duration;
2073
2074  	// TODO store keys, use an internal set method
2075  	animation = new animate_Animation({
2076  		keypath: keypath,
2077  		from: from,
2078  		to: to,
2079  		root: root,
2080  		duration: duration,
2081  		easing: easing,
2082  		interpolator: options.interpolator,
2083
2084  		// TODO wrap callbacks if necessary, to use instance as context
2085  		step: options.step,
2086  		complete: options.complete
2087  	});
2088
2089  	shared_animations.add(animation);
2090  	root._animations.push(animation);
2091
2092  	return animation;
2093  }
2094
2095  var prototype_detach = Ractive$detach;
2096  var prototype_detach__detachHook = new hooks_Hook("detach");
2097  function Ractive$detach() {
2098  	if (this.detached) {
2099  		return this.detached;
2100  	}
2101
2102  	if (this.el) {
2103  		removeFromArray(this.el.__ractive_instances__, this);
2104  	}
2105  	this.detached = this.fragment.detach();
2106  	prototype_detach__detachHook.fire(this);
2107  	return this.detached;
2108  }
2109
2110  var prototype_find = Ractive$find;
2111
2112  function Ractive$find(selector) {
2113  	if (!this.el) {
2114  		return null;
2115  	}
2116
2117  	return this.fragment.find(selector);
2118  }
2119
2120  var test = Query$test;
2121  function Query$test(item, noDirty) {
2122  	var itemMatches;
2123
2124  	if (this._isComponentQuery) {
2125  		itemMatches = !this.selector || item.name === this.selector;
2126  	} else {
2127  		itemMatches = item.node ? matches(item.node, this.selector) : null;
2128  	}
2129
2130  	if (itemMatches) {
2131  		this.push(item.node || item.instance);
2132
2133  		if (!noDirty) {
2134  			this._makeDirty();
2135  		}
2136
2137  		return true;
2138  	}
2139  }
2140
2141  var makeQuery_cancel = function () {
2142  	var liveQueries, selector, index;
2143
2144  	liveQueries = this._root[this._isComponentQuery ? "liveComponentQueries" : "liveQueries"];
2145  	selector = this.selector;
2146
2147  	index = liveQueries.indexOf(selector);
2148
2149  	if (index !== -1) {
2150  		liveQueries.splice(index, 1);
2151  		liveQueries[selector] = null;
2152  	}
2153  };
2154
2155  var sortByItemPosition = function (a, b) {
2156  	var ancestryA, ancestryB, oldestA, oldestB, mutualAncestor, indexA, indexB, fragments, fragmentA, fragmentB;
2157
2158  	ancestryA = getAncestry(a.component || a._ractive.proxy);
2159  	ancestryB = getAncestry(b.component || b._ractive.proxy);
2160
2161  	oldestA = lastItem(ancestryA);
2162  	oldestB = lastItem(ancestryB);
2163
2164  	// remove items from the end of both ancestries as long as they are identical
2165  	// - the final one removed is the closest mutual ancestor
2166  	while (oldestA && oldestA === oldestB) {
2167  		ancestryA.pop();
2168  		ancestryB.pop();
2169
2170  		mutualAncestor = oldestA;
2171
2172  		oldestA = lastItem(ancestryA);
2173  		oldestB = lastItem(ancestryB);
2174  	}
2175
2176  	// now that we have the mutual ancestor, we can find which is earliest
2177  	oldestA = oldestA.component || oldestA;
2178  	oldestB = oldestB.component || oldestB;
2179
2180  	fragmentA = oldestA.parentFragment;
2181  	fragmentB = oldestB.parentFragment;
2182
2183  	// if both items share a parent fragment, our job is easy
2184  	if (fragmentA === fragmentB) {
2185  		indexA = fragmentA.items.indexOf(oldestA);
2186  		indexB = fragmentB.items.indexOf(oldestB);
2187
2188  		// if it's the same index, it means one contains the other,
2189  		// so we see which has the longest ancestry
2190  		return indexA - indexB || ancestryA.length - ancestryB.length;
2191  	}
2192
2193  	// if mutual ancestor is a section, we first test to see which section
2194  	// fragment comes first
2195  	if (fragments = mutualAncestor.fragments) {
2196  		indexA = fragments.indexOf(fragmentA);
2197  		indexB = fragments.indexOf(fragmentB);
2198
2199  		return indexA - indexB || ancestryA.length - ancestryB.length;
2200  	}
2201
2202  	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!");
2203  };
2204
2205  function getParent(item) {
2206  	var parentFragment;
2207
2208  	if (parentFragment = item.parentFragment) {
2209  		return parentFragment.owner;
2210  	}
2211
2212  	if (item.component && (parentFragment = item.component.parentFragment)) {
2213  		return parentFragment.owner;
2214  	}
2215  }
2216
2217  function getAncestry(item) {
2218  	var ancestry, ancestor;
2219
2220  	ancestry = [item];
2221
2222  	ancestor = getParent(item);
2223
2224  	while (ancestor) {
2225  		ancestry.push(ancestor);
2226  		ancestor = getParent(ancestor);
2227  	}
2228
2229  	return ancestry;
2230  }
2231
2232  var sortByDocumentPosition = function (node, otherNode) {
2233  	var bitmask;
2234
2235  	if (node.compareDocumentPosition) {
2236  		bitmask = node.compareDocumentPosition(otherNode);
2237  		return bitmask & 2 ? 1 : -1;
2238  	}
2239
2240  	// In old IE, we can piggy back on the mechanism for
2241  	// comparing component positions
2242  	return sortByItemPosition(node, otherNode);
2243  };
2244
2245  var sort = function () {
2246  	this.sort(this._isComponentQuery ? sortByItemPosition : sortByDocumentPosition);
2247  	this._dirty = false;
2248  };
2249
2250  var makeQuery_dirty = function () {
2251  	var _this = this;
2252
2253  	if (!this._dirty) {
2254  		this._dirty = true;
2255
2256  		// Once the DOM has been updated, ensure the query
2257  		// is correctly ordered
2258  		global_runloop.scheduleTask(function () {
2259  			_this._sort();
2260  		});
2261  	}
2262  };
2263
2264  var remove = function (nodeOrComponent) {
2265  	var index = this.indexOf(this._isComponentQuery ? nodeOrComponent.instance : nodeOrComponent);
2266
2267  	if (index !== -1) {
2268  		this.splice(index, 1);
2269  	}
2270  };
2271
2272  var _makeQuery = makeQuery;
2273  function makeQuery(ractive, selector, live, isComponentQuery) {
2274  	var query = [];
2275
2276  	defineProperties(query, {
2277  		selector: { value: selector },
2278  		live: { value: live },
2279
2280  		_isComponentQuery: { value: isComponentQuery },
2281  		_test: { value: test }
2282  	});
2283
2284  	if (!live) {
2285  		return query;
2286  	}
2287
2288  	defineProperties(query, {
2289  		cancel: { value: makeQuery_cancel },
2290
2291  		_root: { value: ractive },
2292  		_sort: { value: sort },
2293  		_makeDirty: { value: makeQuery_dirty },
2294  		_remove: { value: remove },
2295
2296  		_dirty: { value: false, writable: true }
2297  	});
2298
2299  	return query;
2300  }
2301
2302  var prototype_findAll = Ractive$findAll;
2303  function Ractive$findAll(selector, options) {
2304  	var liveQueries, query;
2305
2306  	if (!this.el) {
2307  		return [];
2308  	}
2309
2310  	options = options || {};
2311  	liveQueries = this._liveQueries;
2312
2313  	// Shortcut: if we're maintaining a live query with this
2314  	// selector, we don't need to traverse the parallel DOM
2315  	if (query = liveQueries[selector]) {
2316
2317  		// Either return the exact same query, or (if not live) a snapshot
2318  		return options && options.live ? query : query.slice();
2319  	}
2320
2321  	query = _makeQuery(this, selector, !!options.live, false);
2322
2323  	// Add this to the list of live queries Ractive needs to maintain,
2324  	// if applicable
2325  	if (query.live) {
2326  		liveQueries.push(selector);
2327  		liveQueries["_" + selector] = query;
2328  	}
2329
2330  	this.fragment.findAll(selector, query);
2331  	return query;
2332  }
2333
2334  var prototype_findAllComponents = Ractive$findAllComponents;
2335  function Ractive$findAllComponents(selector, options) {
2336  	var liveQueries, query;
2337
2338  	options = options || {};
2339  	liveQueries = this._liveComponentQueries;
2340
2341  	// Shortcut: if we're maintaining a live query with this
2342  	// selector, we don't need to traverse the parallel DOM
2343  	if (query = liveQueries[selector]) {
2344
2345  		// Either return the exact same query, or (if not live) a snapshot
2346  		return options && options.live ? query : query.slice();
2347  	}
2348
2349  	query = _makeQuery(this, selector, !!options.live, true);
2350
2351  	// Add this to the list of live queries Ractive needs to maintain,
2352  	// if applicable
2353  	if (query.live) {
2354  		liveQueries.push(selector);
2355  		liveQueries["_" + selector] = query;
2356  	}
2357
2358  	this.fragment.findAllComponents(selector, query);
2359  	return query;
2360  }
2361
2362  var prototype_findComponent = Ractive$findComponent;
2363
2364  function Ractive$findComponent(selector) {
2365  	return this.fragment.findComponent(selector);
2366  }
2367
2368  var findContainer = Ractive$findContainer;
2369
2370  function Ractive$findContainer(selector) {
2371  	if (this.container) {
2372  		if (this.container.component && this.container.component.name === selector) {
2373  			return this.container;
2374  		} else {
2375  			return this.container.findContainer(selector);
2376  		}
2377  	}
2378
2379  	return null;
2380  }
2381
2382  var findParent = Ractive$findParent;
2383
2384  function Ractive$findParent(selector) {
2385
2386  	if (this.parent) {
2387  		if (this.parent.component && this.parent.component.name === selector) {
2388  			return this.parent;
2389  		} else {
2390  			return this.parent.findParent(selector);
2391  		}
2392  	}
2393
2394  	return null;
2395  }
2396
2397  var eventStack = {
2398  	enqueue: function (ractive, event) {
2399  		if (ractive.event) {
2400  			ractive._eventQueue = ractive._eventQueue || [];
2401  			ractive._eventQueue.push(ractive.event);
2402  		}
2403  		ractive.event = event;
2404  	},
2405  	dequeue: function (ractive) {
2406  		if (ractive._eventQueue && ractive._eventQueue.length) {
2407  			ractive.event = ractive._eventQueue.pop();
2408  		} else {
2409  			delete ractive.event;
2410  		}
2411  	}
2412  };
2413
2414  var shared_eventStack = eventStack;
2415
2416  var shared_fireEvent = fireEvent;
2417
2418  function fireEvent(ractive, eventName) {
2419  	var options = arguments[2] === undefined ? {} : arguments[2];
2420
2421  	if (!eventName) {
2422  		return;
2423  	}
2424
2425  	if (!options.event) {
2426  		options.event = {
2427  			name: eventName,
2428  			// until event not included as argument default
2429  			_noArg: true
2430  		};
2431  	} else {
2432  		options.event.name = eventName;
2433  	}
2434
2435  	var eventNames = getKeypath(eventName).wildcardMatches();
2436  	fireEventAs(ractive, eventNames, options.event, options.args, true);
2437  }
2438
2439  function fireEventAs(ractive, eventNames, event, args) {
2440  	var initialFire = arguments[4] === undefined ? false : arguments[4];
2441
2442  	var subscribers,
2443  	    i,
2444  	    bubble = true;
2445
2446  	shared_eventStack.enqueue(ractive, event);
2447
2448  	for (i = eventNames.length; i >= 0; i--) {
2449  		subscribers = ractive._subs[eventNames[i]];
2450
2451  		if (subscribers) {
2452  			bubble = notifySubscribers(ractive, subscribers, event, args) && bubble;
2453  		}
2454  	}
2455
2456  	shared_eventStack.dequeue(ractive);
2457
2458  	if (ractive.parent && bubble) {
2459
2460  		if (initialFire && ractive.component) {
2461  			var fullName = ractive.component.name + "." + eventNames[eventNames.length - 1];
2462  			eventNames = getKeypath(fullName).wildcardMatches();
2463
2464  			if (event) {
2465  				event.component = ractive;
2466  			}
2467  		}
2468
2469  		fireEventAs(ractive.parent, eventNames, event, args);
2470  	}
2471  }
2472
2473  function notifySubscribers(ractive, subscribers, event, args) {
2474  	var originalEvent = null,
2475  	    stopEvent = false;
2476
2477  	if (event && !event._noArg) {
2478  		args = [event].concat(args);
2479  	}
2480
2481  	// subscribers can be modified inflight, e.g. "once" functionality
2482  	// so we need to copy to make sure everyone gets called
2483  	subscribers = subscribers.slice();
2484
2485  	for (var i = 0, len = subscribers.length; i < len; i += 1) {
2486  		if (subscribers[i].apply(ractive, args) === false) {
2487  			stopEvent = true;
2488  		}
2489  	}
2490
2491  	if (event && !event._noArg && stopEvent && (originalEvent = event.original)) {
2492  		originalEvent.preventDefault && originalEvent.preventDefault();
2493  		originalEvent.stopPropagation && originalEvent.stopPropagation();
2494  	}
2495
2496  	return !stopEvent;
2497  }
2498
2499  var prototype_fire = Ractive$fire;
2500  function Ractive$fire(eventName) {
2501
2502  	var options = {
2503  		args: Array.prototype.slice.call(arguments, 1)
2504  	};
2505
2506  	shared_fireEvent(this, eventName, options);
2507  }
2508
2509  var prototype_get = Ractive$get;
2510  var options = {
2511  	capture: true, // top-level calls should be intercepted
2512  	noUnwrap: true, // wrapped values should NOT be unwrapped
2513  	fullRootGet: true // root get should return mappings
2514  };
2515  function Ractive$get(keypath) {
2516  	var value;
2517
2518  	keypath = getKeypath(normalise(keypath));
2519  	value = this.viewmodel.get(keypath, options);
2520
2521  	// Create inter-component binding, if necessary
2522  	if (value === undefined && this.parent && !this.isolated) {
2523  		if (shared_resolveRef(this, keypath.str, this.component.parentFragment)) {
2524  			// creates binding as side-effect, if appropriate
2525  			value = this.viewmodel.get(keypath);
2526  		}
2527  	}
2528
2529  	return value;
2530  }
2531
2532  var insert = Ractive$insert;
2533
2534  var insertHook = new hooks_Hook("insert");
2535  function Ractive$insert(target, anchor) {
2536  	if (!this.fragment.rendered) {
2537  		// TODO create, and link to, documentation explaining this
2538  		throw new Error("The API has changed - you must call `ractive.render(target[, anchor])` to render your Ractive instance. Once rendered you can use `ractive.insert()`.");
2539  	}
2540
2541  	target = getElement(target);
2542  	anchor = getElement(anchor) || null;
2543
2544  	if (!target) {
2545  		throw new Error("You must specify a valid target to insert into");
2546  	}
2547
2548  	target.insertBefore(this.detach(), anchor);
2549  	this.el = target;
2550
2551  	(target.__ractive_instances__ || (target.__ractive_instances__ = [])).push(this);
2552  	this.detached = null;
2553
2554  	fireInsertHook(this);
2555  }
2556
2557  function fireInsertHook(ractive) {
2558  	insertHook.fire(ractive);
2559
2560  	ractive.findAllComponents("*").forEach(function (child) {
2561  		fireInsertHook(child.instance);
2562  	});
2563  }
2564
2565  var prototype_merge = Ractive$merge;
2566  function Ractive$merge(keypath, array, options) {
2567  	var currentArray, promise;
2568
2569  	keypath = getKeypath(normalise(keypath));
2570  	currentArray = this.viewmodel.get(keypath);
2571
2572  	// If either the existing value or the new value isn't an
2573  	// array, just do a regular set
2574  	if (!isArray(currentArray) || !isArray(array)) {
2575  		return this.set(keypath, array, options && options.complete);
2576  	}
2577
2578  	// Manage transitions
2579  	promise = global_runloop.start(this, true);
2580  	this.viewmodel.merge(keypath, currentArray, array, options);
2581  	global_runloop.end();
2582
2583  	return promise;
2584  }
2585
2586  var Observer = function (ractive, keypath, callback, options) {
2587  	this.root = ractive;
2588  	this.keypath = keypath;
2589  	this.callback = callback;
2590  	this.defer = options.defer;
2591
2592  	// default to root as context, but allow it to be overridden
2593  	this.context = options && options.context ? options.context : ractive;
2594  };
2595
2596  Observer.prototype = {
2597  	init: function (immediate) {
2598  		this.value = this.root.get(this.keypath.str);
2599
2600  		if (immediate !== false) {
2601  			this.update();
2602  		} else {
2603  			this.oldValue = this.value;
2604  		}
2605  	},
2606
2607  	setValue: function (value) {
2608  		var _this = this;
2609
2610  		if (!isEqual(value, this.value)) {
2611  			this.value = value;
2612
2613  			if (this.defer && this.ready) {
2614  				global_runloop.scheduleTask(function () {
2615  					return _this.update();
2616  				});
2617  			} else {
2618  				this.update();
2619  			}
2620  		}
2621  	},
2622
2623  	update: function () {
2624  		// Prevent infinite loops
2625  		if (this.updating) {
2626  			return;
2627  		}
2628
2629  		this.updating = true;
2630
2631  		this.callback.call(this.context, this.value, this.oldValue, this.keypath.str);
2632  		this.oldValue = this.value;
2633
2634  		this.updating = false;
2635  	}
2636  };
2637
2638  var observe_Observer = Observer;
2639
2640  var observe_getPattern = getPattern;
2641  function getPattern(ractive, pattern) {
2642  	var matchingKeypaths, values;
2643
2644  	matchingKeypaths = getMatchingKeypaths(ractive, pattern);
2645
2646  	values = {};
2647  	matchingKeypaths.forEach(function (keypath) {
2648  		values[keypath.str] = ractive.get(keypath.str);
2649  	});
2650
2651  	return values;
2652  }
2653
2654  var PatternObserver,
2655      slice = Array.prototype.slice;
2656
2657  PatternObserver = function (ractive, keypath, callback, options) {
2658  	this.root = ractive;
2659
2660  	this.callback = callback;
2661  	this.defer = options.defer;
2662
2663  	this.keypath = keypath;
2664  	this.regex = new RegExp("^" + keypath.str.replace(/\./g, "\\.").replace(/\*/g, "([^\\.]+)") + "$");
2665  	this.values = {};
2666
2667  	if (this.defer) {
2668  		this.proxies = [];
2669  	}
2670
2671  	// default to root as context, but allow it to be overridden
2672  	this.context = options && options.context ? options.context : ractive;
2673  };
2674
2675  PatternObserver.prototype = {
2676  	init: function (immediate) {
2677  		var values, keypath;
2678
2679  		values = observe_getPattern(this.root, this.keypath);
2680
2681  		if (immediate !== false) {
2682  			for (keypath in values) {
2683  				if (values.hasOwnProperty(keypath)) {
2684  					this.update(getKeypath(keypath));
2685  				}
2686  			}
2687  		} else {
2688  			this.values = values;
2689  		}
2690  	},
2691
2692  	update: function (keypath) {
2693  		var _this = this;
2694
2695  		var values;
2696
2697  		if (keypath.isPattern) {
2698  			values = observe_getPattern(this.root, keypath);
2699
2700  			for (keypath in values) {
2701  				if (values.hasOwnProperty(keypath)) {
2702  					this.update(getKeypath(keypath));
2703  				}
2704  			}
2705
2706  			return;
2707  		}
2708
2709  		// special case - array mutation should not trigger `array.*`
2710  		// pattern observer with `array.length`
2711  		if (this.root.viewmodel.implicitChanges[keypath.str]) {
2712  			return;
2713  		}
2714
2715  		if (this.defer && this.ready) {
2716  			global_runloop.scheduleTask(function () {
2717  				return _this.getProxy(keypath).update();
2718  			});
2719  			return;
2720  		}
2721
2722  		this.reallyUpdate(keypath);
2723  	},
2724
2725  	reallyUpdate: function (keypath) {
2726  		var keypathStr, value, keys, args;
2727
2728  		keypathStr = keypath.str;
2729  		value = this.root.viewmodel.get(keypath);
2730
2731  		// Prevent infinite loops
2732  		if (this.updating) {
2733  			this.values[keypathStr] = value;
2734  			return;
2735  		}
2736
2737  		this.updating = true;
2738
2739  		if (!isEqual(value, this.values[keypathStr]) || !this.ready) {
2740  			keys = slice.call(this.regex.exec(keypathStr), 1);
2741  			args = [value, this.values[keypathStr], keypathStr].concat(keys);
2742
2743  			this.values[keypathStr] = value;
2744  			this.callback.apply(this.context, args);
2745  		}
2746
2747  		this.updating = false;
2748  	},
2749
2750  	getProxy: function (keypath) {
2751  		var _this = this;
2752
2753  		if (!this.proxies[keypath.str]) {
2754  			this.proxies[keypath.str] = {
2755  				update: function () {
2756  					return _this.reallyUpdate(keypath);
2757  				}
2758  			};
2759  		}
2760
2761  		return this.proxies[keypath.str];
2762  	}
2763  };
2764
2765  var observe_PatternObserver = PatternObserver;
2766
2767  var observe_getObserverFacade = getObserverFacade;
2768  var emptyObject = {};
2769  function getObserverFacade(ractive, keypath, callback, options) {
2770  	var observer, isPatternObserver, cancelled;
2771
2772  	keypath = getKeypath(normalise(keypath));
2773  	options = options || emptyObject;
2774
2775  	// pattern observers are treated differently
2776  	if (keypath.isPattern) {
2777  		observer = new observe_PatternObserver(ractive, keypath, callback, options);
2778  		ractive.viewmodel.patternObservers.push(observer);
2779  		isPatternObserver = true;
2780  	} else {
2781  		observer = new observe_Observer(ractive, keypath, callback, options);
2782  	}
2783
2784  	observer.init(options.init);
2785  	ractive.viewmodel.register(keypath, observer, isPatternObserver ? "patternObservers" : "observers");
2786
2787  	// This flag allows observers to initialise even with undefined values
2788  	observer.ready = true;
2789
2790  	var facade = {
2791  		cancel: function () {
2792  			var index;
2793
2794  			if (cancelled) {
2795  				return;
2796  			}
2797
2798  			if (isPatternObserver) {
2799  				index = ractive.viewmodel.patternObservers.indexOf(observer);
2800
2801  				ractive.viewmodel.patternObservers.splice(index, 1);
2802  				ractive.viewmodel.unregister(keypath, observer, "patternObservers");
2803  			} else {
2804  				ractive.viewmodel.unregister(keypath, observer, "observers");
2805  			}
2806  			cancelled = true;
2807  		}
2808  	};
2809
2810  	ractive._observers.push(facade);
2811  	return facade;
2812  }
2813
2814  var observe = Ractive$observe;
2815  function Ractive$observe(keypath, callback, options) {
2816
2817  	var observers, map, keypaths, i;
2818
2819  	// Allow a map of keypaths to handlers
2820  	if (isObject(keypath)) {
2821  		options = callback;
2822  		map = keypath;
2823
2824  		observers = [];
2825
2826  		for (keypath in map) {
2827  			if (map.hasOwnProperty(keypath)) {
2828  				callback = map[keypath];
2829  				observers.push(this.observe(keypath, callback, options));
2830  			}
2831  		}
2832
2833  		return {
2834  			cancel: function () {
2835  				while (observers.length) {
2836  					observers.pop().cancel();
2837  				}
2838  			}
2839  		};
2840  	}
2841
2842  	// Allow `ractive.observe( callback )` - i.e. observe entire model
2843  	if (typeof keypath === "function") {
2844  		options = callback;
2845  		callback = keypath;
2846  		keypath = "";
2847
2848  		return observe_getObserverFacade(this, keypath, callback, options);
2849  	}
2850
2851  	keypaths = keypath.split(" ");
2852
2853  	// Single keypath
2854  	if (keypaths.length === 1) {
2855  		return observe_getObserverFacade(this, keypath, callback, options);
2856  	}
2857
2858  	// Multiple space-separated keypaths
2859  	observers = [];
2860
2861  	i = keypaths.length;
2862  	while (i--) {
2863  		keypath = keypaths[i];
2864
2865  		if (keypath) {
2866  			observers.push(observe_getObserverFacade(this, keypath, callback, options));
2867  		}
2868  	}
2869
2870  	return {
2871  		cancel: function () {
2872  			while (observers.length) {
2873  				observers.pop().cancel();
2874  			}
2875  		}
2876  	};
2877  }
2878
2879  var observeOnce = Ractive$observeOnce;
2880
2881  function Ractive$observeOnce(property, callback, options) {
2882
2883  	var observer = this.observe(property, function () {
2884  		callback.apply(this, arguments);
2885  		observer.cancel();
2886  	}, { init: false, defer: options && options.defer });
2887
2888  	return observer;
2889  }
2890
2891  var shared_trim = function (str) {
2892    return str.trim();
2893  };
2894
2895  var notEmptyString = function (str) {
2896    return str !== "";
2897  };
2898
2899  var off = Ractive$off;
2900  function Ractive$off(eventName, callback) {
2901  	var _this = this;
2902
2903  	var eventNames;
2904
2905  	// if no arguments specified, remove all callbacks
2906  	if (!eventName) {
2907  		// TODO use this code instead, once the following issue has been resolved
2908  		// in PhantomJS (tests are unpassable otherwise!)
2909  		// https://github.com/ariya/phantomjs/issues/11856
2910  		// defineProperty( this, '_subs', { value: create( null ), configurable: true });
2911  		for (eventName in this._subs) {
2912  			delete this._subs[eventName];
2913  		}
2914  	} else {
2915  		// Handle multiple space-separated event names
2916  		eventNames = eventName.split(" ").map(shared_trim).filter(notEmptyString);
2917
2918  		eventNames.forEach(function (eventName) {
2919  			var subscribers, index;
2920
2921  			// If we have subscribers for this event...
2922  			if (subscribers = _this._subs[eventName]) {
2923  				// ...if a callback was specified, only remove that
2924  				if (callback) {
2925  					index = subscribers.indexOf(callback);
2926  					if (index !== -1) {
2927  						subscribers.splice(index, 1);
2928  					}
2929  				}
2930
2931  				// ...otherwise remove all callbacks
2932  				else {
2933  					_this._subs[eventName] = [];
2934  				}
2935  			}
2936  		});
2937  	}
2938
2939  	return this;
2940  }
2941
2942  var on = Ractive$on;
2943  function Ractive$on(eventName, callback) {
2944  	var _this = this;
2945
2946  	var listeners, n, eventNames;
2947
2948  	// allow mutliple listeners to be bound in one go
2949  	if (typeof eventName === "object") {
2950  		listeners = [];
2951
2952  		for (n in eventName) {
2953  			if (eventName.hasOwnProperty(n)) {
2954  				listeners.push(this.on(n, eventName[n]));
2955  			}
2956  		}
2957
2958  		return {
2959  			cancel: function () {
2960  				var listener;
2961
2962  				while (listener = listeners.pop()) {
2963  					listener.cancel();
2964  				}
2965  			}
2966  		};
2967  	}
2968
2969  	// Handle multiple space-separated event names
2970  	eventNames = eventName.split(" ").map(shared_trim).filter(notEmptyString);
2971
2972  	eventNames.forEach(function (eventName) {
2973  		(_this._subs[eventName] || (_this._subs[eventName] = [])).push(callback);
2974  	});
2975
2976  	return {
2977  		cancel: function () {
2978  			return _this.off(eventName, callback);
2979  		}
2980  	};
2981  }
2982
2983  var once = Ractive$once;
2984
2985  function Ractive$once(eventName, handler) {
2986
2987  	var listener = this.on(eventName, function () {
2988  		handler.apply(this, arguments);
2989  		listener.cancel();
2990  	});
2991
2992  	// so we can still do listener.cancel() manually
2993  	return listener;
2994  }
2995
2996  // This function takes an array, the name of a mutator method, and the
2997  // arguments to call that mutator method with, and returns an array that
2998  // maps the old indices to their new indices.
2999
3000  // So if you had something like this...
3001  //
3002  //     array = [ 'a', 'b', 'c', 'd' ];
3003  //     array.push( 'e' );
3004  //
3005  // ...you'd get `[ 0, 1, 2, 3 ]` - in other words, none of the old indices
3006  // have changed. If you then did this...
3007  //
3008  //     array.unshift( 'z' );
3009  //
3010  // ...the indices would be `[ 1, 2, 3, 4, 5 ]` - every item has been moved
3011  // one higher to make room for the 'z'. If you removed an item, the new index
3012  // would be -1...
3013  //
3014  //     array.splice( 2, 2 );
3015  //
3016  // ...this would result in [ 0, 1, -1, -1, 2, 3 ].
3017  //
3018  // This information is used to enable fast, non-destructive shuffling of list
3019  // sections when you do e.g. `ractive.splice( 'items', 2, 2 );
3020
3021  var shared_getNewIndices = getNewIndices;
3022
3023  function getNewIndices(array, methodName, args) {
3024  	var spliceArguments,
3025  	    len,
3026  	    newIndices = [],
3027  	    removeStart,
3028  	    removeEnd,
3029  	    balance,
3030  	    i;
3031
3032  	spliceArguments = getSpliceEquivalent(array, methodName, args);
3033
3034  	if (!spliceArguments) {
3035  		return null; // TODO support reverse and sort?
3036  	}
3037
3038  	len = array.length;
3039  	balance = spliceArguments.length - 2 - spliceArguments[1];
3040
3041  	removeStart = Math.min(len, spliceArguments[0]);
3042  	removeEnd = removeStart + spliceArguments[1];
3043
3044  	for (i = 0; i < removeStart; i += 1) {
3045  		newIndices.push(i);
3046  	}
3047
3048  	for (; i < removeEnd; i += 1) {
3049  		newIndices.push(-1);
3050  	}
3051
3052  	for (; i < len; i += 1) {
3053  		newIndices.push(i + balance);
3054  	}
3055
3056  	// there is a net shift for the rest of the array starting with index + balance
3057  	if (balance !== 0) {
3058  		newIndices.touchedFrom = spliceArguments[0];
3059  	} else {
3060  		newIndices.touchedFrom = array.length;
3061  	}
3062
3063  	return newIndices;
3064  }
3065
3066  // The pop, push, shift an unshift methods can all be represented
3067  // as an equivalent splice
3068  function getSpliceEquivalent(array, methodName, args) {
3069  	switch (methodName) {
3070  		case "splice":
3071  			if (args[0] !== undefined && args[0] < 0) {
3072  				args[0] = array.length + Math.max(args[0], -array.length);
3073  			}
3074
3075  			while (args.length < 2) {
3076  				args.push(0);
3077  			}
3078
3079  			// ensure we only remove elements that exist
3080  			args[1] = Math.min(args[1], array.length - args[0]);
3081
3082  			return args;
3083
3084  		case "sort":
3085  		case "reverse":
3086  			return null;
3087
3088  		case "pop":
3089  			if (array.length) {
3090  				return [array.length - 1, 1];
3091  			}
3092  			return [0, 0];
3093
3094  		case "push":
3095  			return [array.length, 0].concat(args);
3096
3097  		case "shift":
3098  			return [0, array.length ? 1 : 0];
3099
3100  		case "unshift":
3101  			return [0, 0].concat(args);
3102  	}
3103  }
3104
3105  var arrayProto = Array.prototype;
3106
3107  var makeArrayMethod = function (methodName) {
3108  	return function (keypath) {
3109  		for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
3110  			args[_key - 1] = arguments[_key];
3111  		}
3112
3113  		var array,
3114  		    newIndices = [],
3115  		    len,
3116  		    promise,
3117  		    result;
3118
3119  		keypath = getKeypath(normalise(keypath));
3120
3121  		array = this.viewmodel.get(keypath);
3122  		len = array.length;
3123
3124  		if (!isArray(array)) {
3125  			throw new Error("Called ractive." + methodName + "('" + keypath.str + "'), but '" + keypath.str + "' does not refer to an array");
3126  		}
3127
3128  		newIndices = shared_getNewIndices(array, methodName, args);
3129
3130  		result = arrayProto[methodName].apply(array, args);
3131  		promise = global_runloop.start(this, true).then(function () {
3132  			return result;
3133  		});
3134
3135  		if (!!newIndices) {
3136  			this.viewmodel.smartUpdate(keypath, array, newIndices);
3137  		} else {
3138  			this.viewmodel.mark(keypath);
3139  		}
3140
3141  		global_runloop.end();
3142
3143  		return promise;
3144  	};
3145  };
3146
3147  var pop = makeArrayMethod("pop");
3148
3149  var push = makeArrayMethod("push");
3150
3151  var css,
3152      update,
3153      styleElement,
3154      head,
3155      styleSheet,
3156      inDom,
3157      global_css__prefix = "/* Ractive.js component styles */\n",
3158      styles = [],
3159      dirty = false;
3160
3161  if (!isClient) {
3162  	// TODO handle encapsulated CSS in server-rendered HTML!
3163  	css = {
3164  		add: noop,
3165  		apply: noop
3166  	};
3167  } else {
3168  	styleElement = document.createElement("style");
3169  	styleElement.type = "text/css";
3170
3171  	head = document.getElementsByTagName("head")[0];
3172
3173  	inDom = false;
3174
3175  	// Internet Exploder won't let you use styleSheet.innerHTML - we have to
3176  	// use styleSheet.cssText instead
3177  	styleSheet = styleElement.styleSheet;
3178
3179  	update = function () {
3180  		var css = global_css__prefix + styles.map(function (s) {
3181  			return "\n/* {" + s.id + "} */\n" + s.styles;
3182  		}).join("\n");
3183
3184  		if (styleSheet) {
3185  			styleSheet.cssText = css;
3186  		} else {
3187  			styleElement.innerHTML = css;
3188  		}
3189
3190  		if (!inDom) {
3191  			head.appendChild(styleElement);
3192  			inDom = true;
3193  		}
3194  	};
3195
3196  	css = {
3197  		add: function (s) {
3198  			styles.push(s);
3199  			dirty = true;
3200  		},
3201
3202  		apply: function () {
3203  			if (dirty) {
3204  				update();
3205  				dirty = false;
3206  			}
3207  		}
3208  	};
3209  }
3210
3211  var global_css = css;
3212
3213  var prototype_render = Ractive$render;
3214
3215  var renderHook = new hooks_Hook("render"),
3216      completeHook = new hooks_Hook("complete");
3217  function Ractive$render(target, anchor) {
3218  	var _this = this;
3219
3220  	var promise, instances, transitionsEnabled;
3221
3222  	// if `noIntro` is `true`, temporarily disable transitions
3223  	transitionsEnabled = this.transitionsEnabled;
3224  	if (this.noIntro) {
3225  		this.transitionsEnabled = false;
3226  	}
3227
3228  	promise = global_runloop.start(this, true);
3229  	global_runloop.scheduleTask(function () {
3230  		return renderHook.fire(_this);
3231  	}, true);
3232
3233  	if (this.fragment.rendered) {
3234  		throw new Error("You cannot call ractive.render() on an already rendered instance! Call ractive.unrender() first");
3235  	}
3236
3237  	target = getElement(target) || this.el;
3238  	anchor = getElement(anchor) || this.anchor;
3239
3240  	this.el = target;
3241  	this.anchor = anchor;
3242
3243  	if (!this.append && target) {
3244  		// Teardown any existing instances *before* trying to set up the new one -
3245  		// avoids certain weird bugs
3246  		var others = target.__ractive_instances__;
3247  		if (others && others.length) {
3248  			removeOtherInstances(others);
3249  		}
3250
3251  		// make sure we are the only occupants
3252  		target.innerHTML = ""; // TODO is this quicker than removeChild? Initial research inconclusive
3253  	}
3254
3255  	if (this.cssId) {
3256  		// ensure encapsulated CSS is up-to-date
3257  		global_css.apply();
3258  	}
3259
3260  	if (target) {
3261  		if (!(instances = target.__ractive_instances__)) {
3262  			target.__ractive_instances__ = [this];
3263  		} else {
3264  			instances.push(this);
3265  		}
3266
3267  		if (anchor) {
3268  			target.insertBefore(this.fragment.render(), anchor);
3269  		} else {
3270  			target.appendChild(this.fragment.render());
3271  		}
3272  	}
3273
3274  	global_runloop.end();
3275
3276  	this.transitionsEnabled = transitionsEnabled;
3277
3278  	return promise.then(function () {
3279  		return completeHook.fire(_this);
3280  	});
3281  }
3282
3283  function removeOtherInstances(others) {
3284  	others.splice(0, others.length).forEach(teardown);
3285  }
3286
3287  var adaptConfigurator = {
3288  	extend: function (Parent, proto, options) {
3289  		proto.adapt = custom_adapt__combine(proto.adapt, ensureArray(options.adapt));
3290  	},
3291
3292  	init: function () {}
3293  };
3294
3295  var custom_adapt = adaptConfigurator;
3296
3297  function custom_adapt__combine(a, b) {
3298  	var c = a.slice(),
3299  	    i = b.length;
3300
3301  	while (i--) {
3302  		if (! ~c.indexOf(b[i])) {
3303  			c.push(b[i]);
3304  		}
3305  	}
3306
3307  	return c;
3308  }
3309
3310  var transform = transformCss;
3311
3312  var selectorsPattern = /(?:^|\})?\s*([^\{\}]+)\s*\{/g,
3313      commentsPattern = /\/\*.*?\*\//g,
3314      selectorUnitPattern = /((?:(?:\[[^\]+]\])|(?:[^\s\+\>\~:]))+)((?::[^\s\+\>\~\(]+(?:\([^\)]+\))?)?\s*[\s\+\>\~]?)\s*/g,
3315      mediaQueryPattern = /^@media/,
3316      dataRvcGuidPattern = /\[data-ractive-css~="\{[a-z0-9-]+\}"]/g;
3317  function transformCss(css, id) {
3318  	var transformed, dataAttr, addGuid;
3319
3320  	dataAttr = "[data-ractive-css~=\"{" + id + "}\"]";
3321
3322  	addGuid = function (selector) {
3323  		var selectorUnits,
3324  		    match,
3325  		    unit,
3326  		    base,
3327  		    prepended,
3328  		    appended,
3329  		    i,
3330  		    transformed = [];
3331
3332  		selectorUnits = [];
3333
3334  		while (match = selectorUnitPattern.exec(selector)) {
3335  			selectorUnits.push({
3336  				str: match[0],
3337  				base: match[1],
3338  				modifiers: match[2]
3339  			});
3340  		}
3341
3342  		// For each simple selector within the selector, we need to create a version
3343  		// that a) combines with the id, and b) is inside the id
3344  		base = selectorUnits.map(extractString);
3345
3346  		i = selectorUnits.length;
3347  		while (i--) {
3348  			appended = base.slice();
3349
3350  			// Pseudo-selectors should go after the attribute selector
3351  			unit = selectorUnits[i];
3352  			appended[i] = unit.base + dataAttr + unit.modifiers || "";
3353
3354  			prepended = base.slice();
3355  			prepended[i] = dataAttr + " " + prepended[i];
3356
3357  			transformed.push(appended.join(" "), prepended.join(" "));
3358  		}
3359
3360  		return transformed.join(", ");
3361  	};
3362
3363  	if (dataRvcGuidPattern.test(css)) {
3364  		transformed = css.replace(dataRvcGuidPattern, dataAttr);
3365  	} else {
3366  		transformed = css.replace(commentsPattern, "").replace(selectorsPattern, function (match, $1) {
3367  			var selectors, transformed;
3368
3369  			// don't transform media queries!
3370  			if (mediaQueryPattern.test($1)) return match;
3371
3372  			selectors = $1.split(",").map(trim);
3373  			transformed = selectors.map(addGuid).join(", ") + " ";
3374
3375  			return match.replace($1, transformed);
3376  		});
3377  	}
3378
3379  	return transformed;
3380  }
3381
3382  function trim(str) {
3383  	if (str.trim) {
3384  		return str.trim();
3385  	}
3386
3387  	return str.replace(/^\s+/, "").replace(/\s+$/, "");
3388  }
3389
3390  function extractString(unit) {
3391  	return unit.str;
3392  }
3393
3394  var css_css__uid = 1;
3395
3396  var cssConfigurator = {
3397  	name: "css",
3398
3399  	extend: function (Parent, proto, options) {
3400  		if (options.css) {
3401  			var id = css_css__uid++;
3402  			var styles = options.noCssTransform ? options.css : transform(options.css, id);
3403
3404  			proto.cssId = id;
3405  			global_css.add({ id: id, styles: styles });
3406  		}
3407  	},
3408
3409  	init: function () {}
3410  };
3411
3412  var css_css = cssConfigurator;
3413
3414  function validate(data) {
3415  	// Warn if userOptions.data is a non-POJO
3416  	if (data && data.constructor !== Object) {
3417  		if (typeof data === "function") {} else if (typeof data !== "object") {
3418  			fatal("data option must be an object or a function, `" + data + "` is not valid");
3419  		} else {
3420  			warnIfDebug("If supplied, options.data should be a plain JavaScript object - using a non-POJO as the root object may work, but is discouraged");
3421  		}
3422  	}
3423  }
3424
3425  var dataConfigurator = {
3426  	name: "data",
3427
3428  	extend: function (Parent, proto, options) {
3429  		var key = undefined,
3430  		    value = undefined;
3431
3432  		// check for non-primitives, which could cause mutation-related bugs
3433  		if (options.data && isObject(options.data)) {
3434  			for (key in options.data) {
3435  				value = options.data[key];
3436
3437  				if (value && typeof value === "object") {
3438  					if (isObject(value) || isArray(value)) {
3439  						warnIfDebug("Passing a `data` option with object and array properties to Ractive.extend() is discouraged, as mutating them is likely to cause bugs. Consider using a data function instead:\n\n  // this...\n  data: function () {\n    return {\n      myObject: {}\n    };\n  })\n\n  // instead of this:\n  data: {\n    myObject: {}\n  }");
3440  					}
3441  				}
3442  			}
3443  		}
3444
3445  		proto.data = custom_data__combine(proto.data, options.data);
3446  	},
3447
3448  	init: function (Parent, ractive, options) {
3449  		var result = custom_data__combine(Parent.prototype.data, options.data);
3450
3451  		if (typeof result === "function") {
3452  			result = result.call(ractive);
3453  		}
3454
3455  		return result || {};
3456  	},
3457
3458  	reset: function (ractive) {
3459  		var result = this.init(ractive.constructor, ractive, ractive.viewmodel);
3460
3461  		ractive.viewmodel.reset(result);
3462  		return true;
3463  	}
3464  };
3465
3466  var custom_data = dataConfigurator;
3467
3468  function custom_data__combine(parentValue, childValue) {
3469  	validate(childValue);
3470
3471  	var parentIsFn = typeof parentValue === "function";
3472  	var childIsFn = typeof childValue === "function";
3473
3474  	// Very important, otherwise child instance can become
3475  	// the default data object on Ractive or a component.
3476  	// then ractive.set() ends up setting on the prototype!
3477  	if (!childValue && !parentIsFn) {
3478  		childValue = {};
3479  	}
3480
3481  	// Fast path, where we just need to copy properties from
3482  	// parent to child
3483  	if (!parentIsFn && !childIsFn) {
3484  		return fromProperties(childValue, parentValue);
3485  	}
3486
3487  	return function () {
3488  		var child = childIsFn ? callDataFunction(childValue, this) : childValue;
3489  		var parent = parentIsFn ? callDataFunction(parentValue, this) : parentValue;
3490
3491  		return fromProperties(child, parent);
3492  	};
3493  }
3494
3495  function callDataFunction(fn, context) {
3496  	var data = fn.call(context);
3497
3498  	if (!data) return;
3499
3500  	if (typeof data !== "object") {
3501  		fatal("Data function must return an object");
3502  	}
3503
3504  	if (data.constructor !== Object) {
3505  		warnOnceIfDebug("Data function returned something other than a plain JavaScript object. This might work, but is strongly discouraged");
3506  	}
3507
3508  	return data;
3509  }
3510
3511  function fromProperties(primary, secondary) {
3512  	if (primary && secondary) {
3513  		for (var key in secondary) {
3514  			if (!(key in primary)) {
3515  				primary[key] = secondary[key];
3516  			}
3517  		}
3518
3519  		return primary;
3520  	}
3521
3522  	return primary || secondary;
3523  }
3524
3525  // TODO do we need to support this in the new Ractive() case?
3526
3527  var Parser,
3528      ParseError,
3529      parse_Parser__leadingWhitespace = /^\s+/;
3530
3531  ParseError = function (message) {
3532  	this.name = "ParseError";
3533  	this.message = message;
3534  	try {
3535  		throw new Error(message);
3536  	} catch (e) {
3537  		this.stack = e.stack;
3538  	}
3539  };
3540
3541  ParseError.prototype = Error.prototype;
3542
3543  Parser = function (str, options) {
3544  	var items,
3545  	    item,
3546  	    lineStart = 0;
3547
3548  	this.str = str;
3549  	this.options = options || {};
3550  	this.pos = 0;
3551
3552  	this.lines = this.str.split("\n");
3553  	this.lineEnds = this.lines.map(function (line) {
3554  		var lineEnd = lineStart + line.length + 1; // +1 for the newline
3555
3556  		lineStart = lineEnd;
3557  		return lineEnd;
3558  	}, 0);
3559
3560  	// Custom init logic
3561  	if (this.init) this.init(str, options);
3562
3563  	items = [];
3564
3565  	while (this.pos < this.str.length && (item = this.read())) {
3566  		items.push(item);
3567  	}
3568
3569  	this.leftover = this.remaining();
3570  	this.result = this.postProcess ? this.postProcess(items, options) : items;
3571  };
3572
3573  Parser.prototype = {
3574  	read: function (converters) {
3575  		var pos, i, len, item;
3576
3577  		if (!converters) converters = this.converters;
3578
3579  		pos = this.pos;
3580
3581  		len = converters.length;
3582  		for (i = 0; i < len; i += 1) {
3583  			this.pos = pos; // reset for each attempt
3584
3585  			if (item = converters[i](this)) {
3586  				return item;
3587  			}
3588  		}
3589
3590  		return null;
3591  	},
3592
3593  	getLinePos: function (char) {
3594  		var lineNum = 0,
3595  		    lineStart = 0,
3596  		    columnNum;
3597
3598  		while (char >= this.lineEnds[lineNum]) {
3599  			lineStart = this.lineEnds[lineNum];
3600  			lineNum += 1;
3601  		}
3602
3603  		columnNum = char - lineStart;
3604  		return [lineNum + 1, columnNum + 1, char]; // line/col should be one-based, not zero-based!
3605  	},
3606
3607  	error: function (message) {
3608  		var pos = this.getLinePos(this.pos);
3609  		var lineNum = pos[0];
3610  		var columnNum = pos[1];
3611
3612  		var line = this.lines[pos[0] - 1];
3613  		var numTabs = 0;
3614  		var annotation = line.replace(/\t/g, function (match, char) {
3615  			if (char < pos[1]) {
3616  				numTabs += 1;
3617  			}
3618
3619  			return "  ";
3620  		}) + "\n" + new Array(pos[1] + numTabs).join(" ") + "^----";
3621
3622  		var error = new ParseError("" + message + " at line " + lineNum + " character " + columnNum + ":\n" + annotation);
3623
3624  		error.line = pos[0];
3625  		error.character = pos[1];
3626  		error.shortMessage = message;
3627
3628  		throw error;
3629  	},
3630
3631  	matchString: function (string) {
3632  		if (this.str.substr(this.pos, string.length) === string) {
3633  			this.pos += string.length;
3634  			return string;
3635  		}
3636  	},
3637
3638  	matchPattern: function (pattern) {
3639  		var match;
3640
3641  		if (match = pattern.exec(this.remaining())) {
3642  			this.pos += match[0].length;
3643  			return match[1] || match[0];
3644  		}
3645  	},
3646
3647  	allowWhitespace: function () {
3648  		this.matchPattern(parse_Parser__leadingWhitespace);
3649  	},
3650
3651  	remaining: function () {
3652  		return this.str.substring(this.pos);
3653  	},
3654
3655  	nextChar: function () {
3656  		return this.str.charAt(this.pos);
3657  	}
3658  };
3659
3660  Parser.extend = function (proto) {
3661  	var Parent = this,
3662  	    Child,
3663  	    key;
3664
3665  	Child = function (str, options) {
3666  		Parser.call(this, str, options);
3667  	};
3668
3669  	Child.prototype = create(Parent.prototype);
3670
3671  	for (key in proto) {
3672  		if (hasOwn.call(proto, key)) {
3673  			Child.prototype[key] = proto[key];
3674  		}
3675  	}
3676
3677  	Child.extend = Parser.extend;
3678  	return Child;
3679  };
3680
3681  var parse_Parser = Parser;
3682
3683  var TEXT = 1;
3684  var INTERPOLATOR = 2;
3685  var TRIPLE = 3;
3686  var SECTION = 4;
3687  var INVERTED = 5;
3688  var CLOSING = 6;
3689  var ELEMENT = 7;
3690  var PARTIAL = 8;
3691  var COMMENT = 9;
3692  var DELIMCHANGE = 10;
3693  var ATTRIBUTE = 13;
3694  var CLOSING_TAG = 14;
3695  var COMPONENT = 15;
3696  var YIELDER = 16;
3697  var INLINE_PARTIAL = 17;
3698  var DOCTYPE = 18;
3699
3700  var NUMBER_LITERAL = 20;
3701  var STRING_LITERAL = 21;
3702  var ARRAY_LITERAL = 22;
3703  var OBJECT_LITERAL = 23;
3704  var BOOLEAN_LITERAL = 24;
3705  var REGEXP_LITERAL = 25;
3706
3707  var GLOBAL = 26;
3708  var KEY_VALUE_PAIR = 27;
3709
3710  var REFERENCE = 30;
3711  var REFINEMENT = 31;
3712  var MEMBER = 32;
3713  var PREFIX_OPERATOR = 33;
3714  var BRACKETED = 34;
3715  var CONDITIONAL = 35;
3716  var INFIX_OPERATOR = 36;
3717
3718  var INVOCATION = 40;
3719
3720  var SECTION_IF = 50;
3721  var SECTION_UNLESS = 51;
3722  var SECTION_EACH = 52;
3723  var SECTION_WITH = 53;
3724  var SECTION_IF_WITH = 54;
3725
3726  var ELSE = 60;
3727  var ELSEIF = 61;
3728
3729  var mustache_readDelimiterChange = readDelimiterChange;
3730  var delimiterChangePattern = /^[^\s=]+/,
3731      whitespacePattern = /^\s+/;
3732  function readDelimiterChange(parser) {
3733  	var start, opening, closing;
3734
3735  	if (!parser.matchString("=")) {
3736  		return null;
3737  	}
3738
3739  	start = parser.pos;
3740
3741  	// allow whitespace before new opening delimiter
3742  	parser.allowWhitespace();
3743
3744  	opening = parser.matchPattern(delimiterChangePattern);
3745  	if (!opening) {
3746  		parser.pos = start;
3747  		return null;
3748  	}
3749
3750  	// allow whitespace (in fact, it's necessary...)
3751  	if (!parser.matchPattern(whitespacePattern)) {
3752  		return null;
3753  	}
3754
3755  	closing = parser.matchPattern(delimiterChangePattern);
3756  	if (!closing) {
3757  		parser.pos = start;
3758  		return null;
3759  	}
3760
3761  	// allow whitespace before closing '='
3762  	parser.allowWhitespace();
3763
3764  	if (!parser.matchString("=")) {
3765  		parser.pos = start;
3766  		return null;
3767  	}
3768
3769  	return [opening, closing];
3770  }
3771
3772  var readRegexpLiteral = readRegexpLiteral__readNumberLiteral;
3773  var regexpPattern = /^(\/(?:[^\n\r\u2028\u2029/\\[]|\\.|\[(?:[^\n\r\u2028\u2029\]\\]|\\.)*])+\/(?:([gimuy])(?![a-z]*\2))*(?![a-zA-Z_$0-9]))/;
3774  function readRegexpLiteral__readNumberLiteral(parser) {
3775  	var result;
3776
3777  	if (result = parser.matchPattern(regexpPattern)) {
3778  		return {
3779  			t: REGEXP_LITERAL,
3780  			v: result
3781  		};
3782  	}
3783
3784  	return null;
3785  }
3786
3787  var converters_readMustache = readMustache;
3788
3789  var delimiterChangeToken = { t: DELIMCHANGE, exclude: true };
3790  function readMustache(parser) {
3791  	var mustache, i;
3792
3793  	// If we're inside a <script> or <style> tag, and we're not
3794  	// interpolating, bug out
3795  	if (parser.interpolate[parser.inside] === false) {
3796  		return null;
3797  	}
3798
3799  	for (i = 0; i < parser.tags.length; i += 1) {
3800  		if (mustache = readMustacheOfType(parser, parser.tags[i])) {
3801  			return mustache;
3802  		}
3803  	}
3804  }
3805
3806  function readMustacheOfType(parser, tag) {
3807  	var start, mustache, reader, i;
3808
3809  	start = parser.pos;
3810
3811  	if (parser.matchString("\\" + tag.open)) {
3812  		if (start === 0 || parser.str[start - 1] !== "\\") {
3813  			return tag.open;
3814  		}
3815  	} else if (!parser.matchString(tag.open)) {
3816  		return null;
3817  	}
3818
3819  	// delimiter change?
3820  	if (mustache = mustache_readDelimiterChange(parser)) {
3821  		// find closing delimiter or abort...
3822  		if (!parser.matchString(tag.close)) {
3823  			return null;
3824  		}
3825
3826  		// ...then make the switch
3827  		tag.open = mustache[0];
3828  		tag.close = mustache[1];
3829  		parser.sortMustacheTags();
3830
3831  		return delimiterChangeToken;
3832  	}
3833
3834  	parser.allowWhitespace();
3835
3836  	// illegal section closer
3837  	if (parser.matchString("/")) {
3838  		parser.pos -= 1;
3839  		var rewind = parser.pos;
3840  		if (!readRegexpLiteral(parser)) {
3841  			parser.pos = rewind - tag.close.length;
3842  			parser.error("Attempted to close a section that wasn't open");
3843  		} else {
3844  			parser.pos = rewind;
3845  		}
3846  	}
3847
3848  	for (i = 0; i < tag.readers.length; i += 1) {
3849  		reader = tag.readers[i];
3850
3851  		if (mustache = reader(parser, tag)) {
3852  			if (tag.isStatic) {
3853  				mustache.s = true; // TODO make this `1` instead - more compact
3854  			}
3855
3856  			if (parser.includeLinePositions) {
3857  				mustache.p = parser.getLinePos(start);
3858  			}
3859
3860  			return mustache;
3861  		}
3862  	}
3863
3864  	parser.pos = start;
3865  	return null;
3866  }
3867
3868  var expectedExpression = "Expected a JavaScript expression";
3869  var expectedParen = "Expected closing paren";
3870
3871  var literal_readNumberLiteral = literal_readNumberLiteral__readNumberLiteral;
3872  var literal_readNumberLiteral__numberPattern = /^(?:[+-]?)0*(?:(?:(?:[1-9]\d*)?\.\d+)|(?:(?:0|[1-9]\d*)\.)|(?:0|[1-9]\d*))(?:[eE][+-]?\d+)?/;
3873  function literal_readNumberLiteral__readNumberLiteral(parser) {
3874  	var result;
3875
3876  	if (result = parser.matchPattern(literal_readNumberLiteral__numberPattern)) {
3877  		return {
3878  			t: NUMBER_LITERAL,
3879  			v: result
3880  		};
3881  	}
3882
3883  	return null;
3884  }
3885
3886  var literal_readBooleanLiteral = readBooleanLiteral;
3887  function readBooleanLiteral(parser) {
3888  	var remaining = parser.remaining();
3889
3890  	if (remaining.substr(0, 4) === "true") {
3891  		parser.pos += 4;
3892  		return {
3893  			t: BOOLEAN_LITERAL,
3894  			v: "true"
3895  		};
3896  	}
3897
3898  	if (remaining.substr(0, 5) === "false") {
3899  		parser.pos += 5;
3900  		return {
3901  			t: BOOLEAN_LITERAL,
3902  			v: "false"
3903  		};
3904  	}
3905
3906  	return null;
3907  }
3908
3909  var stringMiddlePattern, escapeSequencePattern, lineContinuationPattern;
3910
3911  // Match one or more characters until: ", ', \, or EOL/EOF.
3912  // EOL/EOF is written as (?!.) (meaning there's no non-newline char next).
3913  stringMiddlePattern = /^(?=.)[^"'\\]+?(?:(?!.)|(?=["'\\]))/;
3914
3915  // Match one escape sequence, including the backslash.
3916  escapeSequencePattern = /^\\(?:['"\\bfnrt]|0(?![0-9])|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|(?=.)[^ux0-9])/;
3917
3918  // Match one ES5 line continuation (backslash + line terminator).
3919  lineContinuationPattern = /^\\(?:\r\n|[\u000A\u000D\u2028\u2029])/;
3920
3921  // Helper for defining getDoubleQuotedString and getSingleQuotedString.
3922  var makeQuotedStringMatcher = function (okQuote) {
3923  	return function (parser) {
3924  		var start, literal, done, next;
3925
3926  		start = parser.pos;
3927  		literal = "\"";
3928  		done = false;
3929
3930  		while (!done) {
3931  			next = parser.matchPattern(stringMiddlePattern) || parser.matchPattern(escapeSequencePattern) || parser.matchString(okQuote);
3932  			if (next) {
3933  				if (next === "\"") {
3934  					literal += "\\\"";
3935  				} else if (next === "\\'") {
3936  					literal += "'";
3937  				} else {
3938  					literal += next;
3939  				}
3940  			} else {
3941  				next = parser.matchPattern(lineContinuationPattern);
3942  				if (next) {
3943  					// convert \(newline-like) into a \u escape, which is allowed in JSON
3944  					literal += "\\u" + ("000" + next.charCodeAt(1).toString(16)).slice(-4);
3945  				} else {
3946  					done = true;
3947  				}
3948  			}
3949  		}
3950
3951  		literal += "\"";
3952
3953  		// use JSON.parse to interpret escapes
3954  		return JSON.parse(literal);
3955  	};
3956  };
3957
3958  var getSingleQuotedString = makeQuotedStringMatcher("\"");
3959  var getDoubleQuotedString = makeQuotedStringMatcher("'");
3960
3961  var readStringLiteral = function (parser) {
3962  	var start, string;
3963
3964  	start = parser.pos;
3965
3966  	if (parser.matchString("\"")) {
3967  		string = getDoubleQuotedString(parser);
3968
3969  		if (!parser.matchString("\"")) {
3970  			parser.pos = start;
3971  			return null;
3972  		}
3973
3974  		return {
3975  			t: STRING_LITERAL,
3976  			v: string
3977  		};
3978  	}
3979
3980  	if (parser.matchString("'")) {
3981  		string = getSingleQuotedString(parser);
3982
3983  		if (!parser.matchString("'")) {
3984  			parser.pos = start;
3985  			return null;
3986  		}
3987
3988  		return {
3989  			t: STRING_LITERAL,
3990  			v: string
3991  		};
3992  	}
3993
3994  	return null;
3995  };
3996
3997  var patterns__name = /^[a-zA-Z_$][a-zA-Z_$0-9]*/;
3998
3999  // http://mathiasbynens.be/notes/javascript-properties
4000  // can be any name, string literal, or number literal
4001  var shared_readKey = readKey;
4002  var identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
4003  function readKey(parser) {
4004  	var token;
4005
4006  	if (token = readStringLiteral(parser)) {
4007  		return identifier.test(token.v) ? token.v : "\"" + token.v.replace(/"/g, "\\\"") + "\"";
4008  	}
4009
4010  	if (token = literal_readNumberLiteral(parser)) {
4011  		return token.v;
4012  	}
4013
4014  	if (token = parser.matchPattern(patterns__name)) {
4015  		return token;
4016  	}
4017  }
4018
4019  var keyValuePair = readKeyValuePair;
4020  function readKeyValuePair(parser) {
4021  	var start, key, value;
4022
4023  	start = parser.pos;
4024
4025  	// allow whitespace between '{' and key
4026  	parser.allowWhitespace();
4027
4028  	key = shared_readKey(parser);
4029  	if (key === null) {
4030  		parser.pos = start;
4031  		return null;
4032  	}
4033
4034  	// allow whitespace between key and ':'
4035  	parser.allowWhitespace();
4036
4037  	// next character must be ':'
4038  	if (!parser.matchString(":")) {
4039  		parser.pos = start;
4040  		return null;
4041  	}
4042
4043  	// allow whitespace between ':' and value
4044  	parser.allowWhitespace();
4045
4046  	// next expression must be a, well... expression
4047  	value = converters_readExpression(parser);
4048  	if (value === null) {
4049  		parser.pos = start;
4050  		return null;
4051  	}
4052
4053  	return {
4054  		t: KEY_VALUE_PAIR,
4055  		k: key,
4056  		v: value
4057  	};
4058  }
4059
4060  var objectLiteral_keyValuePairs = readKeyValuePairs;
4061  function readKeyValuePairs(parser) {
4062  	var start, pairs, pair, keyValuePairs;
4063
4064  	start = parser.pos;
4065
4066  	pair = keyValuePair(parser);
4067  	if (pair === null) {
4068  		return null;
4069  	}
4070
4071  	pairs = [pair];
4072
4073  	if (parser.matchString(",")) {
4074  		keyValuePairs = readKeyValuePairs(parser);
4075
4076  		if (!keyValuePairs) {
4077  			parser.pos = start;
4078  			return null;
4079  		}
4080
4081  		return pairs.concat(keyValuePairs);
4082  	}
4083
4084  	return pairs;
4085  }
4086
4087  var readObjectLiteral = function (parser) {
4088  	var start, keyValuePairs;
4089
4090  	start = parser.pos;
4091
4092  	// allow whitespace
4093  	parser.allowWhitespace();
4094
4095  	if (!parser.matchString("{")) {
4096  		parser.pos = start;
4097  		return null;
4098  	}
4099
4100  	keyValuePairs = objectLiteral_keyValuePairs(parser);
4101
4102  	// allow whitespace between final value and '}'
4103  	parser.allowWhitespace();
4104
4105  	if (!parser.matchString("}")) {
4106  		parser.pos = start;
4107  		return null;
4108  	}
4109
4110  	return {
4111  		t: OBJECT_LITERAL,
4112  		m: keyValuePairs
4113  	};
4114  };
4115
4116  var shared_readExpressionList = readExpressionList;
4117  function readExpressionList(parser) {
4118  	var start, expressions, expr, next;
4119
4120  	start = parser.pos;
4121
4122  	parser.allowWhitespace();
4123
4124  	expr = converters_readExpression(parser);
4125
4126  	if (expr === null) {
4127  		return null;
4128  	}
4129
4130  	expressions = [expr];
4131
4132  	// allow whitespace between expression and ','
4133  	parser.allowWhitespace();
4134
4135  	if (parser.matchString(",")) {
4136  		next = readExpressionList(parser);
4137  		if (next === null) {
4138  			parser.error(expectedExpression);
4139  		}
4140
4141  		next.forEach(append);
4142  	}
4143
4144  	function append(expression) {
4145  		expressions.push(expression);
4146  	}
4147
4148  	return expressions;
4149  }
4150
4151  var readArrayLiteral = function (parser) {
4152  	var start, expressionList;
4153
4154  	start = parser.pos;
4155
4156  	// allow whitespace before '['
4157  	parser.allowWhitespace();
4158
4159  	if (!parser.matchString("[")) {
4160  		parser.pos = start;
4161  		return null;
4162  	}
4163
4164  	expressionList = shared_readExpressionList(parser);
4165
4166  	if (!parser.matchString("]")) {
4167  		parser.pos = start;
4168  		return null;
4169  	}
4170
4171  	return {
4172  		t: ARRAY_LITERAL,
4173  		m: expressionList
4174  	};
4175  };
4176
4177  var primary_readLiteral = readLiteral;
4178  function readLiteral(parser) {
4179  	return literal_readNumberLiteral(parser) || literal_readBooleanLiteral(parser) || readStringLiteral(parser) || readObjectLiteral(parser) || readArrayLiteral(parser) || readRegexpLiteral(parser);
4180  }
4181
4182  var primary_readReference = readReference;
4183  var prefixPattern = /^(?:~\/|(?:\.\.\/)+|\.\/(?:\.\.\/)*|\.)/,
4184      globals,
4185      keywords;
4186
4187  // if a reference is a browser global, we don't deference it later, so it needs special treatment
4188  globals = /^(?:Array|console|Date|RegExp|decodeURIComponent|decodeURI|encodeURIComponent|encodeURI|isFinite|isNaN|parseFloat|parseInt|JSON|Math|NaN|undefined|null)\b/;
4189
4190  // keywords are not valid references, with the exception of `this`
4191  keywords = /^(?:break|case|catch|continue|debugger|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|var|void|while|with)$/;
4192
4193  var legalReference = /^[a-zA-Z$_0-9]+(?:(?:\.[a-zA-Z$_0-9]+)|(?:\[[0-9]+\]))*/;
4194  var relaxedName = /^[a-zA-Z_$][-a-zA-Z_$0-9]*/;
4195  function readReference(parser) {
4196  	var startPos, prefix, name, global, reference, lastDotIndex;
4197
4198  	startPos = parser.pos;
4199
4200  	name = parser.matchPattern(/^@(?:keypath|index|key)/);
4201
4202  	if (!name) {
4203  		prefix = parser.matchPattern(prefixPattern) || "";
4204  		name = !prefix && parser.relaxedNames && parser.matchPattern(relaxedName) || parser.matchPattern(legalReference);
4205
4206  		if (!name && prefix === ".") {
4207  			prefix = "";
4208  			name = ".";
4209  		}
4210  	}
4211
4212  	if (!name) {
4213  		return null;
4214  	}
4215
4216  	// bug out if it's a keyword (exception for ancestor/restricted refs - see https://github.com/ractivejs/ractive/issues/1497)
4217  	if (!prefix && !parser.relaxedNames && keywords.test(name)) {
4218  		parser.pos = startPos;
4219  		return null;
4220  	}
4221
4222  	// if this is a browser global, stop here
4223  	if (!prefix && globals.test(name)) {
4224  		global = globals.exec(name)[0];
4225  		parser.pos = startPos + global.length;
4226
4227  		return {
4228  			t: GLOBAL,
4229  			v: global
4230  		};
4231  	}
4232
4233  	reference = (prefix || "") + normalise(name);
4234
4235  	if (parser.matchString("(")) {
4236  		// if this is a method invocation (as opposed to a function) we need
4237  		// to strip the method name from the reference combo, else the context
4238  		// will be wrong
4239  		lastDotIndex = reference.lastIndexOf(".");
4240  		if (lastDotIndex !== -1) {
4241  			reference = reference.substr(0, lastDotIndex);
4242  			parser.pos = startPos + reference.length;
4243  		} else {
4244  			parser.pos -= 1;
4245  		}
4246  	}
4247
4248  	return {
4249  		t: REFERENCE,
4250  		n: reference.replace(/^this\./, "./").replace(/^this$/, ".")
4251  	};
4252  }
4253
4254  var primary_readBracketedExpression = readBracketedExpression;
4255  function readBracketedExpression(parser) {
4256  	var start, expr;
4257
4258  	start = parser.pos;
4259
4260  	if (!parser.matchString("(")) {
4261  		return null;
4262  	}
4263
4264  	parser.allowWhitespace();
4265
4266  	expr = converters_readExpression(parser);
4267  	if (!expr) {
4268  		parser.error(expectedExpression);
4269  	}
4270
4271  	parser.allowWhitespace();
4272
4273  	if (!parser.matchString(")")) {
4274  		parser.error(expectedParen);
4275  	}
4276
4277  	return {
4278  		t: BRACKETED,
4279  		x: expr
4280  	};
4281  }
4282
4283  var readPrimary = function (parser) {
4284  	return primary_readLiteral(parser) || primary_readReference(parser) || primary_readBracketedExpression(parser);
4285  };
4286
4287  var shared_readRefinement = readRefinement;
4288  function readRefinement(parser) {
4289  	var start, name, expr;
4290
4291  	start = parser.pos;
4292
4293  	parser.allowWhitespace();
4294
4295  	// "." name
4296  	if (parser.matchString(".")) {
4297  		parser.allowWhitespace();
4298
4299  		if (name = parser.matchPattern(patterns__name)) {
4300  			return {
4301  				t: REFINEMENT,
4302  				n: name
4303  			};
4304  		}
4305
4306  		parser.error("Expected a property name");
4307  	}
4308
4309  	// "[" expression "]"
4310  	if (parser.matchString("[")) {
4311  		parser.allowWhitespace();
4312
4313  		expr = converters_readExpression(parser);
4314  		if (!expr) {
4315  			parser.error(expectedExpression);
4316  		}
4317
4318  		parser.allowWhitespace();
4319
4320  		if (!parser.matchString("]")) {
4321  			parser.error("Expected ']'");
4322  		}
4323
4324  		return {
4325  			t: REFINEMENT,
4326  			x: expr
4327  		};
4328  	}
4329
4330  	return null;
4331  }
4332
4333  var readMemberOrInvocation = function (parser) {
4334  	var current, expression, refinement, expressionList;
4335
4336  	expression = readPrimary(parser);
4337
4338  	if (!expression) {
4339  		return null;
4340  	}
4341
4342  	while (expression) {
4343  		current = parser.pos;
4344
4345  		if (refinement = shared_readRefinement(parser)) {
4346  			expression = {
4347  				t: MEMBER,
4348  				x: expression,
4349  				r: refinement
4350  			};
4351  		} else if (parser.matchString("(")) {
4352  			parser.allowWhitespace();
4353  			expressionList = shared_readExpressionList(parser);
4354
4355  			parser.allowWhitespace();
4356
4357  			if (!parser.matchString(")")) {
4358  				parser.error(expectedParen);
4359  			}
4360
4361  			expression = {
4362  				t: INVOCATION,
4363  				x: expression
4364  			};
4365
4366  			if (expressionList) {
4367  				expression.o = expressionList;
4368  			}
4369  		} else {
4370  			break;
4371  		}
4372  	}
4373
4374  	return expression;
4375  };
4376
4377  var readTypeOf, makePrefixSequenceMatcher;
4378
4379  makePrefixSequenceMatcher = function (symbol, fallthrough) {
4380  	return function (parser) {
4381  		var expression;
4382
4383  		if (expression = fallthrough(parser)) {
4384  			return expression;
4385  		}
4386
4387  		if (!parser.matchString(symbol)) {
4388  			return null;
4389  		}
4390
4391  		parser.allowWhitespace();
4392
4393  		expression = converters_readExpression(parser);
4394  		if (!expression) {
4395  			parser.error(expectedExpression);
4396  		}
4397
4398  		return {
4399  			s: symbol,
4400  			o: expression,
4401  			t: PREFIX_OPERATOR
4402  		};
4403  	};
4404  };
4405
4406  // create all prefix sequence matchers, return readTypeOf
4407  (function () {
4408  	var i, len, matcher, prefixOperators, fallthrough;
4409
4410  	prefixOperators = "! ~ + - typeof".split(" ");
4411
4412  	fallthrough = readMemberOrInvocation;
4413  	for (i = 0, len = prefixOperators.length; i < len; i += 1) {
4414  		matcher = makePrefixSequenceMatcher(prefixOperators[i], fallthrough);
4415  		fallthrough = matcher;
4416  	}
4417
4418  	// typeof operator is higher precedence than multiplication, so provides the
4419  	// fallthrough for the multiplication sequence matcher we're about to create
4420  	// (we're skipping void and delete)
4421  	readTypeOf = fallthrough;
4422  })();
4423
4424  var readTypeof = readTypeOf;
4425
4426  var readLogicalOr, makeInfixSequenceMatcher;
4427
4428  makeInfixSequenceMatcher = function (symbol, fallthrough) {
4429  	return function (parser) {
4430  		var start, left, right;
4431
4432  		left = fallthrough(parser);
4433  		if (!left) {
4434  			return null;
4435  		}
4436
4437  		// Loop to handle left-recursion in a case like `a * b * c` and produce
4438  		// left association, i.e. `(a * b) * c`.  The matcher can't call itself
4439  		// to parse `left` because that would be infinite regress.
4440  		while (true) {
4441  			start = parser.pos;
4442
4443  			parser.allowWhitespace();
4444
4445  			if (!parser.matchString(symbol)) {
4446  				parser.pos = start;
4447  				return left;
4448  			}
4449
4450  			// special case - in operator must not be followed by [a-zA-Z_$0-9]
4451  			if (symbol === "in" && /[a-zA-Z_$0-9]/.test(parser.remaining().charAt(0))) {
4452  				parser.pos = start;
4453  				return left;
4454  			}
4455
4456  			parser.allowWhitespace();
4457
4458  			// right operand must also consist of only higher-precedence operators
4459  			right = fallthrough(parser);
4460  			if (!right) {
4461  				parser.pos = start;
4462  				return left;
4463  			}
4464
4465  			left = {
4466  				t: INFIX_OPERATOR,
4467  				s: symbol,
4468  				o: [left, right]
4469  			};
4470
4471  			// Loop back around.  If we don't see another occurrence of the symbol,
4472  			// we'll return left.
4473  		}
4474  	};
4475  };
4476
4477  // create all infix sequence matchers, and return readLogicalOr
4478  (function () {
4479  	var i, len, matcher, infixOperators, fallthrough;
4480
4481  	// All the infix operators on order of precedence (source: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Operator_Precedence)
4482  	// Each sequence matcher will initially fall through to its higher precedence
4483  	// neighbour, and only attempt to match if one of the higher precedence operators
4484  	// (or, ultimately, a literal, reference, or bracketed expression) already matched
4485  	infixOperators = "* / % + - << >> >>> < <= > >= in instanceof == != === !== & ^ | && ||".split(" ");
4486
4487  	// A typeof operator is higher precedence than multiplication
4488  	fallthrough = readTypeof;
4489  	for (i = 0, len = infixOperators.length; i < len; i += 1) {
4490  		matcher = makeInfixSequenceMatcher(infixOperators[i], fallthrough);
4491  		fallthrough = matcher;
4492  	}
4493
4494  	// Logical OR is the fallthrough for the conditional matcher
4495  	readLogicalOr = fallthrough;
4496  })();
4497
4498  var expressions_readLogicalOr = readLogicalOr;
4499
4500  // The conditional operator is the lowest precedence operator, so we start here
4501  var readConditional = getConditional;
4502  function getConditional(parser) {
4503  	var start, expression, ifTrue, ifFalse;
4504
4505  	expression = expressions_readLogicalOr(parser);
4506  	if (!expression) {
4507  		return null;
4508  	}
4509
4510  	start = parser.pos;
4511
4512  	parser.allowWhitespace();
4513
4514  	if (!parser.matchString("?")) {
4515  		parser.pos = start;
4516  		return expression;
4517  	}
4518
4519  	parser.allowWhitespace();
4520
4521  	ifTrue = converters_readExpression(parser);
4522  	if (!ifTrue) {
4523  		parser.error(expectedExpression);
4524  	}
4525
4526  	parser.allowWhitespace();
4527
4528  	if (!parser.matchString(":")) {
4529  		parser.error("Expected \":\"");
4530  	}
4531
4532  	parser.allowWhitespace();
4533
4534  	ifFalse = converters_readExpression(parser);
4535  	if (!ifFalse) {
4536  		parser.error(expectedExpression);
4537  	}
4538
4539  	return {
4540  		t: CONDITIONAL,
4541  		o: [expression, ifTrue, ifFalse]
4542  	};
4543  }
4544
4545  var converters_readExpression = readExpression;
4546  function readExpression(parser) {
4547  	// The conditional operator is the lowest precedence operator (except yield,
4548  	// assignment operators, and commas, none of which are supported), so we
4549  	// start there. If it doesn't match, it 'falls through' to progressively
4550  	// higher precedence operators, until it eventually matches (or fails to
4551  	// match) a 'primary' - a literal or a reference. This way, the abstract syntax
4552  	// tree has everything in its proper place, i.e. 2 + 3 * 4 === 14, not 20.
4553  	return readConditional(parser);
4554  }
4555
4556  var utils_flattenExpression = flattenExpression;
4557
4558  function flattenExpression(expression) {
4559  	var refs;
4560
4561  	extractRefs(expression, refs = []);
4562
4563  	return {
4564  		r: refs,
4565  		s: stringify(expression)
4566  	};
4567
4568  	function stringify(node) {
4569  		switch (node.t) {
4570  			case BOOLEAN_LITERAL:
4571  			case GLOBAL:
4572  			case NUMBER_LITERAL:
4573  			case REGEXP_LITERAL:
4574  				return node.v;
4575
4576  			case STRING_LITERAL:
4577  				return JSON.stringify(String(node.v));
4578
4579  			case ARRAY_LITERAL:
4580  				return "[" + (node.m ? node.m.map(stringify).join(",") : "") + "]";
4581
4582  			case OBJECT_LITERAL:
4583  				return "{" + (node.m ? node.m.map(stringify).join(",") : "") + "}";
4584
4585  			case KEY_VALUE_PAIR:
4586  				return node.k + ":" + stringify(node.v);
4587
4588  			case PREFIX_OPERATOR:
4589  				return (node.s === "typeof" ? "typeof " : node.s) + stringify(node.o);
4590
4591  			case INFIX_OPERATOR:
4592  				return stringify(node.o[0]) + (node.s.substr(0, 2) === "in" ? " " + node.s + " " : node.s) + stringify(node.o[1]);
4593
4594  			case INVOCATION:
4595  				return stringify(node.x) + "(" + (node.o ? node.o.map(stringify).join(",") : "") + ")";
4596
4597  			case BRACKETED:
4598  				return "(" + stringify(node.x) + ")";
4599
4600  			case MEMBER:
4601  				return stringify(node.x) + stringify(node.r);
4602
4603  			case REFINEMENT:
4604  				return node.n ? "." + node.n : "[" + stringify(node.x) + "]";
4605
4606  			case CONDITIONAL:
4607  				return stringify(node.o[0]) + "?" + stringify(node.o[1]) + ":" + stringify(node.o[2]);
4608
4609  			case REFERENCE:
4610  				return "_" + refs.indexOf(node.n);
4611
4612  			default:
4613  				throw new Error("Expected legal JavaScript");
4614  		}
4615  	}
4616  }
4617
4618  // TODO maybe refactor this?
4619  function extractRefs(node, refs) {
4620  	var i, list;
4621
4622  	if (node.t === REFERENCE) {
4623  		if (refs.indexOf(node.n) === -1) {
4624  			refs.unshift(node.n);
4625  		}
4626  	}
4627
4628  	list = node.o || node.m;
4629  	if (list) {
4630  		if (isObject(list)) {
4631  			extractRefs(list, refs);
4632  		} else {
4633  			i = list.length;
4634  			while (i--) {
4635  				extractRefs(list[i], refs);
4636  			}
4637  		}
4638  	}
4639
4640  	if (node.x) {
4641  		extractRefs(node.x, refs);
4642  	}
4643
4644  	if (node.r) {
4645  		extractRefs(node.r, refs);
4646  	}
4647
4648  	if (node.v) {
4649  		extractRefs(node.v, refs);
4650  	}
4651  }
4652
4653  var utils_refineExpression = refineExpression;
4654
4655  var arrayMemberPattern = /^[0-9][1-9]*$/;
4656  function refineExpression(expression, mustache) {
4657  	var referenceExpression;
4658
4659  	if (expression) {
4660  		while (expression.t === BRACKETED && expression.x) {
4661  			expression = expression.x;
4662  		}
4663
4664  		// special case - integers should be treated as array members references,
4665  		// rather than as expressions in their own right
4666  		if (expression.t === REFERENCE) {
4667  			mustache.r = expression.n;
4668  		} else {
4669  			if (expression.t === NUMBER_LITERAL && arrayMemberPattern.test(expression.v)) {
4670  				mustache.r = expression.v;
4671  			} else if (referenceExpression = getReferenceExpression(expression)) {
4672  				mustache.rx = referenceExpression;
4673  			} else {
4674  				mustache.x = utils_flattenExpression(expression);
4675  			}
4676  		}
4677
4678  		return mustache;
4679  	}
4680  }
4681
4682  // TODO refactor this! it's bewildering
4683  function getReferenceExpression(expression) {
4684  	var members = [],
4685  	    refinement;
4686
4687  	while (expression.t === MEMBER && expression.r.t === REFINEMENT) {
4688  		refinement = expression.r;
4689
4690  		if (refinement.x) {
4691  			if (refinement.x.t === REFERENCE) {
4692  				members.unshift(refinement.x);
4693  			} else {
4694  				members.unshift(utils_flattenExpression(refinement.x));
4695  			}
4696  		} else {
4697  			members.unshift(refinement.n);
4698  		}
4699
4700  		expression = expression.x;
4701  	}
4702
4703  	if (expression.t !== REFERENCE) {
4704  		return null;
4705  	}
4706
4707  	return {
4708  		r: expression.n,
4709  		m: members
4710  	};
4711  }
4712
4713  var mustache_readTriple = readTriple;
4714  function readTriple(parser, tag) {
4715  	var expression = converters_readExpression(parser),
4716  	    triple;
4717
4718  	if (!expression) {
4719  		return null;
4720  	}
4721
4722  	if (!parser.matchString(tag.close)) {
4723  		parser.error("Expected closing delimiter '" + tag.close + "'");
4724  	}
4725
4726  	triple = { t: TRIPLE };
4727  	utils_refineExpression(expression, triple); // TODO handle this differently - it's mysterious
4728
4729  	return triple;
4730  }
4731
4732  var mustache_readUnescaped = readUnescaped;
4733  function readUnescaped(parser, tag) {
4734  	var expression, triple;
4735
4736  	if (!parser.matchString("&")) {
4737  		return null;
4738  	}
4739
4740  	parser.allowWhitespace();
4741
4742  	expression = converters_readExpression(parser);
4743
4744  	if (!expression) {
4745  		return null;
4746  	}
4747
4748  	if (!parser.matchString(tag.close)) {
4749  		parser.error("Expected closing delimiter '" + tag.close + "'");
4750  	}
4751
4752  	triple = { t: TRIPLE };
4753  	utils_refineExpression(expression, triple); // TODO handle this differently - it's mysterious
4754
4755  	return triple;
4756  }
4757
4758  var mustache_readPartial = readPartial;
4759  function readPartial(parser, tag) {
4760  	var start, nameStart, expression, context, partial;
4761
4762  	start = parser.pos;
4763
4764  	if (!parser.matchString(">")) {
4765  		return null;
4766  	}
4767
4768  	parser.allowWhitespace();
4769  	nameStart = parser.pos;
4770
4771  	// Partial names can include hyphens, so we can't use readExpression
4772  	// blindly. Instead, we use the `relaxedNames` flag to indicate that
4773  	// `foo-bar` should be read as a single name, rather than 'subtract
4774  	// bar from foo'
4775  	parser.relaxedNames = true;
4776  	expression = converters_readExpression(parser);
4777  	parser.relaxedNames = false;
4778
4779  	parser.allowWhitespace();
4780  	context = converters_readExpression(parser);
4781  	parser.allowWhitespace();
4782
4783  	if (!expression) {
4784  		return null;
4785  	}
4786
4787  	partial = { t: PARTIAL };
4788  	utils_refineExpression(expression, partial); // TODO...
4789
4790  	parser.allowWhitespace();
4791
4792  	// if we have another expression - e.g. `{{>foo bar}}` - then
4793  	// we turn it into `{{#with bar}}{{>foo}}{{/with}}`
4794  	if (context) {
4795  		partial = {
4796  			t: SECTION,
4797  			n: SECTION_WITH,
4798  			f: [partial]
4799  		};
4800
4801  		utils_refineExpression(context, partial);
4802  	}
4803
4804  	if (!parser.matchString(tag.close)) {
4805  		parser.error("Expected closing delimiter '" + tag.close + "'");
4806  	}
4807
4808  	return partial;
4809  }
4810
4811  var readMustacheComment = readComment;
4812  function readComment(parser, tag) {
4813  	var index;
4814
4815  	if (!parser.matchString("!")) {
4816  		return null;
4817  	}
4818
4819  	index = parser.remaining().indexOf(tag.close);
4820
4821  	if (index !== -1) {
4822  		parser.pos += index + tag.close.length;
4823  		return { t: COMMENT };
4824  	}
4825  }
4826
4827  var converters_readExpressionOrReference = readExpressionOrReference;
4828  function readExpressionOrReference(parser, expectedFollowers) {
4829  	var start, expression, i;
4830
4831  	start = parser.pos;
4832  	expression = converters_readExpression(parser);
4833
4834  	if (!expression) {
4835  		return null;
4836  	}
4837
4838  	for (i = 0; i < expectedFollowers.length; i += 1) {
4839  		if (parser.remaining().substr(0, expectedFollowers[i].length) === expectedFollowers[i]) {
4840  			return expression;
4841  		}
4842  	}
4843
4844  	parser.pos = start;
4845  	return primary_readReference(parser);
4846  }
4847
4848  var mustache_readInterpolator = readInterpolator;
4849  function readInterpolator(parser, tag) {
4850  	var start, expression, interpolator, err;
4851
4852  	start = parser.pos;
4853
4854  	// TODO would be good for perf if we could do away with the try-catch
4855  	try {
4856  		expression = converters_readExpressionOrReference(parser, [tag.close]);
4857  	} catch (e) {
4858  		err = e;
4859  	}
4860
4861  	if (!expression) {
4862  		if (parser.str.charAt(start) === "!") {
4863  			// special case - comment
4864  			parser.pos = start;
4865  			return null;
4866  		}
4867
4868  		if (err) {
4869  			throw err;
4870  		}
4871  	}
4872
4873  	if (!parser.matchString(tag.close)) {
4874  		parser.error("Expected closing delimiter '" + tag.close + "' after reference");
4875
4876  		if (!expression) {
4877  			// special case - comment
4878  			if (parser.nextChar() === "!") {
4879  				return null;
4880  			}
4881
4882  			parser.error("Expected expression or legal reference");
4883  		}
4884  	}
4885
4886  	interpolator = { t: INTERPOLATOR };
4887  	utils_refineExpression(expression, interpolator); // TODO handle this differently - it's mysterious
4888
4889  	return interpolator;
4890  }
4891
4892  var mustache_readYielder = readYielder;
4893  var yieldPattern = /^yield\s*/;
4894  function readYielder(parser, tag) {
4895  	var start, name, yielder;
4896
4897  	if (!parser.matchPattern(yieldPattern)) {
4898  		return null;
4899  	}
4900
4901  	start = parser.pos;
4902  	name = parser.matchPattern(/^[a-zA-Z_$][a-zA-Z_$0-9\-]*/);
4903
4904  	parser.allowWhitespace();
4905
4906  	if (!parser.matchString(tag.close)) {
4907  		parser.error("expected legal partial name");
4908  	}
4909
4910  	yielder = { t: YIELDER };
4911
4912  	if (name) {
4913  		yielder.n = name;
4914  	}
4915
4916  	return yielder;
4917  }
4918
4919  var section_readClosing = readClosing;
4920  function readClosing(parser, tag) {
4921  	var start, remaining, index, closing;
4922
4923  	start = parser.pos;
4924
4925  	if (!parser.matchString(tag.open)) {
4926  		return null;
4927  	}
4928
4929  	parser.allowWhitespace();
4930
4931  	if (!parser.matchString("/")) {
4932  		parser.pos = start;
4933  		return null;
4934  	}
4935
4936  	parser.allowWhitespace();
4937
4938  	remaining = parser.remaining();
4939  	index = remaining.indexOf(tag.close);
4940
4941  	if (index !== -1) {
4942  		closing = {
4943  			t: CLOSING,
4944  			r: remaining.substr(0, index).split(" ")[0]
4945  		};
4946
4947  		parser.pos += index;
4948
4949  		if (!parser.matchString(tag.close)) {
4950  			parser.error("Expected closing delimiter '" + tag.close + "'");
4951  		}
4952
4953  		return closing;
4954  	}
4955
4956  	parser.pos = start;
4957  	return null;
4958  }
4959
4960  var section_readElse = section_readElse__readElse;
4961  var section_readElse__elsePattern = /^\s*else\s*/;
4962  function section_readElse__readElse(parser, tag) {
4963  	var start = parser.pos;
4964
4965  	if (!parser.matchString(tag.open)) {
4966  		return null;
4967  	}
4968
4969  	if (!parser.matchPattern(section_readElse__elsePattern)) {
4970  		parser.pos = start;
4971  		return null;
4972  	}
4973
4974  	if (!parser.matchString(tag.close)) {
4975  		parser.error("Expected closing delimiter '" + tag.close + "'");
4976  	}
4977
4978  	return {
4979  		t: ELSE
4980  	};
4981  }
4982
4983  var readElseIf = readElseIf__readElse;
4984  var readElseIf__elsePattern = /^\s*elseif\s+/;
4985  function readElseIf__readElse(parser, tag) {
4986  	var start = parser.pos,
4987  	    expression;
4988
4989  	if (!parser.matchString(tag.open)) {
4990  		return null;
4991  	}
4992
4993  	if (!parser.matchPattern(readElseIf__elsePattern)) {
4994  		parser.pos = start;
4995  		return null;
4996  	}
4997
4998  	expression = converters_readExpression(parser);
4999
5000  	if (!parser.matchString(tag.close)) {
5001  		parser.error("Expected closing delimiter '" + tag.close + "'");
5002  	}
5003
5004  	return {
5005  		t: ELSEIF,
5006  		x: expression
5007  	};
5008  }
5009
5010  var handlebarsBlockCodes = {
5011  	each: SECTION_EACH,
5012  	"if": SECTION_IF,
5013  	"if-with": SECTION_IF_WITH,
5014  	"with": SECTION_WITH,
5015  	unless: SECTION_UNLESS
5016  };
5017
5018  var mustache_readSection = readSection;
5019
5020  var indexRefPattern = /^\s*:\s*([a-zA-Z_$][a-zA-Z_$0-9]*)/,
5021      keyIndexRefPattern = /^\s*,\s*([a-zA-Z_$][a-zA-Z_$0-9]*)/,
5022      handlebarsBlockPattern = new RegExp("^(" + Object.keys(handlebarsBlockCodes).join("|") + ")\\b");
5023  function readSection(parser, tag) {
5024  	var start, expression, section, child, children, hasElse, block, unlessBlock, conditions, closed, i, expectedClose;
5025
5026  	start = parser.pos;
5027
5028  	if (parser.matchString("^")) {
5029  		section = { t: SECTION, f: [], n: SECTION_UNLESS };
5030  	} else if (parser.matchString("#")) {
5031  		section = { t: SECTION, f: [] };
5032
5033  		if (parser.matchString("partial")) {
5034  			parser.pos = start - parser.standardDelimiters[0].length;
5035  			parser.error("Partial definitions can only be at the top level of the template, or immediately inside components");
5036  		}
5037
5038  		if (block = parser.matchPattern(handlebarsBlockPattern)) {
5039  			expectedClose = block;
5040  			section.n = handlebarsBlockCodes[block];
5041  		}
5042  	} else {
5043  		return null;
5044  	}
5045
5046  	parser.allowWhitespace();
5047
5048  	expression = converters_readExpression(parser);
5049
5050  	if (!expression) {
5051  		parser.error("Expected expression");
5052  	}
5053
5054  	// optional index and key references
5055  	if (i = parser.matchPattern(indexRefPattern)) {
5056  		var extra = undefined;
5057
5058  		if (extra = parser.matchPattern(keyIndexRefPattern)) {
5059  			section.i = i + "," + extra;
5060  		} else {
5061  			section.i = i;
5062  		}
5063  	}
5064
5065  	parser.allowWhitespace();
5066
5067  	if (!parser.matchString(tag.close)) {
5068  		parser.error("Expected closing delimiter '" + tag.close + "'");
5069  	}
5070
5071  	parser.sectionDepth += 1;
5072  	children = section.f;
5073
5074  	conditions = [];
5075
5076  	do {
5077  		if (child = section_readClosing(parser, tag)) {
5078  			if (expectedClose && child.r !== expectedClose) {
5079  				parser.error("Expected " + tag.open + "/" + expectedClose + "" + tag.close);
5080  			}
5081
5082  			parser.sectionDepth -= 1;
5083  			closed = true;
5084  		} else if (child = readElseIf(parser, tag)) {
5085  			if (section.n === SECTION_UNLESS) {
5086  				parser.error("{{else}} not allowed in {{#unless}}");
5087  			}
5088
5089  			if (hasElse) {
5090  				parser.error("illegal {{elseif...}} after {{else}}");
5091  			}
5092
5093  			if (!unlessBlock) {
5094  				unlessBlock = createUnlessBlock(expression, section.n);
5095  			}
5096
5097  			unlessBlock.f.push({
5098  				t: SECTION,
5099  				n: SECTION_IF,
5100  				x: utils_flattenExpression(mustache_readSection__combine(conditions.concat(child.x))),
5101  				f: children = []
5102  			});
5103
5104  			conditions.push(invert(child.x));
5105  		} else if (child = section_readElse(parser, tag)) {
5106  			if (section.n === SECTION_UNLESS) {
5107  				parser.error("{{else}} not allowed in {{#unless}}");
5108  			}
5109
5110  			if (hasElse) {
5111  				parser.error("there can only be one {{else}} block, at the end of a section");
5112  			}
5113
5114  			hasElse = true;
5115
5116  			// use an unless block if there's no elseif
5117  			if (!unlessBlock) {
5118  				unlessBlock = createUnlessBlock(expression, section.n);
5119  				children = unlessBlock.f;
5120  			} else {
5121  				unlessBlock.f.push({
5122  					t: SECTION,
5123  					n: SECTION_IF,
5124  					x: utils_flattenExpression(mustache_readSection__combine(conditions)),
5125  					f: children = []
5126  				});
5127  			}
5128  		} else {
5129  			child = parser.read(READERS);
5130
5131  			if (!child) {
5132  				break;
5133  			}
5134
5135  			children.push(child);
5136  		}
5137  	} while (!closed);
5138
5139  	if (unlessBlock) {
5140  		// special case - `with` should become `if-with` (TODO is this right?
5141  		// seems to me that `with` ought to behave consistently, regardless
5142  		// of the presence/absence of `else`. In other words should always
5143  		// be `if-with`
5144  		if (section.n === SECTION_WITH) {
5145  			section.n = SECTION_IF_WITH;
5146  		}
5147
5148  		section.l = unlessBlock;
5149  	}
5150
5151  	utils_refineExpression(expression, section);
5152
5153  	// TODO if a section is empty it should be discarded. Don't do
5154  	// that here though - we need to clean everything up first, as
5155  	// it may contain removeable whitespace. As a temporary measure,
5156  	// to pass the existing tests, remove empty `f` arrays
5157  	if (!section.f.length) {
5158  		delete section.f;
5159  	}
5160
5161  	return section;
5162  }
5163
5164  function createUnlessBlock(expression, sectionType) {
5165  	var unlessBlock;
5166
5167  	if (sectionType === SECTION_WITH) {
5168  		// special case - a `{{#with foo}}` section will render if `foo` is
5169  		// truthy, so the `{{else}}` section needs to render if `foo` is falsy,
5170  		// rather than adhering to the normal `{{#unless foo}}` logic (which
5171  		// treats empty arrays/objects as falsy)
5172  		unlessBlock = {
5173  			t: SECTION,
5174  			n: SECTION_IF,
5175  			f: []
5176  		};
5177
5178  		utils_refineExpression(invert(expression), unlessBlock);
5179  	} else {
5180  		unlessBlock = {
5181  			t: SECTION,
5182  			n: SECTION_UNLESS,
5183  			f: []
5184  		};
5185
5186  		utils_refineExpression(expression, unlessBlock);
5187  	}
5188
5189  	return unlessBlock;
5190  }
5191
5192  function invert(expression) {
5193  	if (expression.t === PREFIX_OPERATOR && expression.s === "!") {
5194  		return expression.o;
5195  	}
5196
5197  	return {
5198  		t: PREFIX_OPERATOR,
5199  		s: "!",
5200  		o: parensIfNecessary(expression)
5201  	};
5202  }
5203
5204  function mustache_readSection__combine(expressions) {
5205  	if (expressions.length === 1) {
5206  		return expressions[0];
5207  	}
5208
5209  	return {
5210  		t: INFIX_OPERATOR,
5211  		s: "&&",
5212  		o: [parensIfNecessary(expressions[0]), parensIfNecessary(mustache_readSection__combine(expressions.slice(1)))]
5213  	};
5214  }
5215
5216  function parensIfNecessary(expression) {
5217  	// TODO only wrap if necessary
5218  	return {
5219  		t: BRACKETED,
5220  		x: expression
5221  	};
5222  }
5223
5224  var converters_readHtmlComment = readHtmlComment;
5225  var OPEN_COMMENT = "<!--",
5226      CLOSE_COMMENT = "-->";
5227  function readHtmlComment(parser) {
5228  	var start, content, remaining, endIndex, comment;
5229
5230  	start = parser.pos;
5231
5232  	if (!parser.matchString(OPEN_COMMENT)) {
5233  		return null;
5234  	}
5235
5236  	remaining = parser.remaining();
5237  	endIndex = remaining.indexOf(CLOSE_COMMENT);
5238
5239  	if (endIndex === -1) {
5240  		parser.error("Illegal HTML - expected closing comment sequence ('-->')");
5241  	}
5242
5243  	content = remaining.substr(0, endIndex);
5244  	parser.pos += endIndex + 3;
5245
5246  	comment = {
5247  		t: COMMENT,
5248  		c: content
5249  	};
5250
5251  	if (parser.includeLinePositions) {
5252  		comment.p = parser.getLinePos(start);
5253  	}
5254
5255  	return comment;
5256  }
5257
5258  var booleanAttributes, voidElementNames, htmlEntities, controlCharacters, entityPattern, lessThan, greaterThan, amp;
5259
5260  // https://github.com/kangax/html-minifier/issues/63#issuecomment-37763316
5261  booleanAttributes = /^(allowFullscreen|async|autofocus|autoplay|checked|compact|controls|declare|default|defaultChecked|defaultMuted|defaultSelected|defer|disabled|enabled|formNoValidate|hidden|indeterminate|inert|isMap|itemScope|loop|multiple|muted|noHref|noResize|noShade|noValidate|noWrap|open|pauseOnExit|readOnly|required|reversed|scoped|seamless|selected|sortable|translate|trueSpeed|typeMustMatch|visible)$/i;
5262  voidElementNames = /^(?:area|base|br|col|command|doctype|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
5263
5264  htmlEntities = { quot: 34, amp: 38, apos: 39, lt: 60, gt: 62, nbsp: 160, iexcl: 161, cent: 162, pound: 163, curren: 164, yen: 165, brvbar: 166, sect: 167, uml: 168, copy: 169, ordf: 170, laquo: 171, not: 172, shy: 173, reg: 174, macr: 175, deg: 176, plusmn: 177, sup2: 178, sup3: 179, acute: 180, micro: 181, para: 182, middot: 183, cedil: 184, sup1: 185, ordm: 186, raquo: 187, frac14: 188, frac12: 189, frac34: 190, iquest: 191, Agrave: 192, Aacute: 193, Acirc: 194, Atilde: 195, Auml: 196, Aring: 197, AElig: 198, Ccedil: 199, Egrave: 200, Eacute: 201, Ecirc: 202, Euml: 203, Igrave: 204, Iacute: 205, Icirc: 206, Iuml: 207, ETH: 208, Ntilde: 209, Ograve: 210, Oacute: 211, Ocirc: 212, Otilde: 213, Ouml: 214, times: 215, Oslash: 216, Ugrave: 217, Uacute: 218, Ucirc: 219, Uuml: 220, Yacute: 221, THORN: 222, szlig: 223, agrave: 224, aacute: 225, acirc: 226, atilde: 227, auml: 228, aring: 229, aelig: 230, ccedil: 231, egrave: 232, eacute: 233, ecirc: 234, euml: 235, igrave: 236, iacute: 237, icirc: 238, iuml: 239, eth: 240, ntilde: 241, ograve: 242, oacute: 243, ocirc: 244, otilde: 245, ouml: 246, divide: 247, oslash: 248, ugrave: 249, uacute: 250, ucirc: 251, uuml: 252, yacute: 253, thorn: 254, yuml: 255, OElig: 338, oelig: 339, Scaron: 352, scaron: 353, Yuml: 376, fnof: 402, circ: 710, tilde: 732, Alpha: 913, Beta: 914, Gamma: 915, Delta: 916, Epsilon: 917, Zeta: 918, Eta: 919, Theta: 920, Iota: 921, Kappa: 922, Lambda: 923, Mu: 924, Nu: 925, Xi: 926, Omicron: 927, Pi: 928, Rho: 929, Sigma: 931, Tau: 932, Upsilon: 933, Phi: 934, Chi: 935, Psi: 936, Omega: 937, alpha: 945, beta: 946, gamma: 947, delta: 948, epsilon: 949, zeta: 950, eta: 951, theta: 952, iota: 953, kappa: 954, lambda: 955, mu: 956, nu: 957, xi: 958, omicron: 959, pi: 960, rho: 961, sigmaf: 962, sigma: 963, tau: 964, upsilon: 965, phi: 966, chi: 967, psi: 968, omega: 969, thetasym: 977, upsih: 978, piv: 982, ensp: 8194, emsp: 8195, thinsp: 8201, zwnj: 8204, zwj: 8205, lrm: 8206, rlm: 8207, ndash: 8211, mdash: 8212, lsquo: 8216, rsquo: 8217, sbquo: 8218, ldquo: 8220, rdquo: 8221, bdquo: 8222, dagger: 8224, Dagger: 8225, bull: 8226, hellip: 8230, permil: 8240, prime: 8242, Prime: 8243, lsaquo: 8249, rsaquo: 8250, oline: 8254, frasl: 8260, euro: 8364, image: 8465, weierp: 8472, real: 8476, trade: 8482, alefsym: 8501, larr: 8592, uarr: 8593, rarr: 8594, darr: 8595, harr: 8596, crarr: 8629, lArr: 8656, uArr: 8657, rArr: 8658, dArr: 8659, hArr: 8660, forall: 8704, part: 8706, exist: 8707, empty: 8709, nabla: 8711, isin: 8712, notin: 8713, ni: 8715, prod: 8719, sum: 8721, minus: 8722, lowast: 8727, radic: 8730, prop: 8733, infin: 8734, ang: 8736, and: 8743, or: 8744, cap: 8745, cup: 8746, int: 8747, there4
5264: 8756, sim: 8764, cong: 8773, asymp: 8776, ne: 8800, equiv: 8801, le: 8804, ge: 8805, sub: 8834, sup: 8835, nsub: 8836, sube: 8838, supe: 8839, oplus: 8853, otimes: 8855, perp: 8869, sdot: 8901, lceil: 8968, rceil: 8969, lfloor: 8970, rfloor: 8971, lang: 9001, rang: 9002, loz: 9674, spades: 9824, clubs: 9827, hearts: 9829, diams: 9830 };
5265  controlCharacters = [8364, 129, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 141, 381, 143, 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250, 339, 157, 382, 376];
5266  entityPattern = new RegExp("&(#?(?:x[\\w\\d]+|\\d+|" + Object.keys(htmlEntities).join("|") + "));?", "g");
5267
5268  function decodeCharacterReferences(html) {
5269  	return html.replace(entityPattern, function (match, entity) {
5270  		var code;
5271
5272  		// Handle named entities
5273  		if (entity[0] !== "#") {
5274  			code = htmlEntities[entity];
5275  		} else if (entity[1] === "x") {
5276  			code = parseInt(entity.substring(2), 16);
5277  		} else {
5278  			code = parseInt(entity.substring(1), 10);
5279  		}
5280
5281  		if (!code) {
5282  			return match;
5283  		}
5284
5285  		return String.fromCharCode(validateCode(code));
5286  	});
5287  }
5288
5289  // some code points are verboten. If we were inserting HTML, the browser would replace the illegal
5290  // code points with alternatives in some cases - since we're bypassing that mechanism, we need
5291  // to replace them ourselves
5292  //
5293  // Source: http://en.wikipedia.org/wiki/Character_encodings_in_HTML#Illegal_characters
5294  function validateCode(code) {
5295  	if (!code) {
5296  		return 65533;
5297  	}
5298
5299  	// line feed becomes generic whitespace
5300  	if (code === 10) {
5301  		return 32;
5302  	}
5303
5304  	// ASCII range. (Why someone would use HTML entities for ASCII characters I don't know, but...)
5305  	if (code < 128) {
5306  		return code;
5307  	}
5308
5309  	// code points 128-159 are dealt with leniently by browsers, but they're incorrect. We need
5310  	// to correct the mistake or we'll end up with missing € signs and so on
5311  	if (code <= 159) {
5312  		return controlCharacters[code - 128];
5313  	}
5314
5315  	// basic multilingual plane
5316  	if (code < 55296) {
5317  		return code;
5318  	}
5319
5320  	// UTF-16 surrogate halves
5321  	if (code <= 57343) {
5322  		return 65533;
5323  	}
5324
5325  	// rest of the basic multilingual plane
5326  	if (code <= 65535) {
5327  		return code;
5328  	}
5329
5330  	return 65533;
5331  }
5332
5333  lessThan = /</g;
5334  greaterThan = />/g;
5335  amp = /&/g;
5336
5337  function escapeHtml(str) {
5338  	return str.replace(amp, "&amp;").replace(lessThan, "&lt;").replace(greaterThan, "&gt;");
5339  }
5340
5341  var leadingLinebreak = /^\s*\r?\n/,
5342      trailingLinebreak = /\r?\n\s*$/;
5343
5344  var stripStandalones = function (items) {
5345  	var i, current, backOne, backTwo, lastSectionItem;
5346
5347  	for (i = 1; i < items.length; i += 1) {
5348  		current = items[i];
5349  		backOne = items[i - 1];
5350  		backTwo = items[i - 2];
5351
5352  		// if we're at the end of a [text][comment][text] sequence...
5353  		if (isString(current) && isComment(backOne) && isString(backTwo)) {
5354
5355  			// ... and the comment is a standalone (i.e. line breaks either side)...
5356  			if (trailingLinebreak.test(backTwo) && leadingLinebreak.test(current)) {
5357
5358  				// ... then we want to remove the whitespace after the first line break
5359  				items[i - 2] = backTwo.replace(trailingLinebreak, "\n");
5360
5361  				// and the leading line break of the second text token
5362  				items[i] = current.replace(leadingLinebreak, "");
5363  			}
5364  		}
5365
5366  		// if the current item is a section, and it is preceded by a linebreak, and
5367  		// its first item is a linebreak...
5368  		if (isSection(current) && isString(backOne)) {
5369  			if (trailingLinebreak.test(backOne) && isString(current.f[0]) && leadingLinebreak.test(current.f[0])) {
5370  				items[i - 1] = backOne.replace(trailingLinebreak, "\n");
5371  				current.f[0] = current.f[0].replace(leadingLinebreak, "");
5372  			}
5373  		}
5374
5375  		// if the last item was a section, and it is followed by a linebreak, and
5376  		// its last item is a linebreak...
5377  		if (isString(current) && isSection(backOne)) {
5378  			lastSectionItem = lastItem(backOne.f);
5379
5380  			if (isString(lastSectionItem) && trailingLinebreak.test(lastSectionItem) && leadingLinebreak.test(current)) {
5381  				backOne.f[backOne.f.length - 1] = lastSectionItem.replace(trailingLinebreak, "\n");
5382  				items[i] = current.replace(leadingLinebreak, "");
5383  			}
5384  		}
5385  	}
5386
5387  	return items;
5388  };
5389
5390  function isString(item) {
5391  	return typeof item === "string";
5392  }
5393
5394  function isComment(item) {
5395  	return item.t === COMMENT || item.t === DELIMCHANGE;
5396  }
5397
5398  function isSection(item) {
5399  	return (item.t === SECTION || item.t === INVERTED) && item.f;
5400  }
5401
5402  var trimWhitespace = function (items, leadingPattern, trailingPattern) {
5403  	var item;
5404
5405  	if (leadingPattern) {
5406  		item = items[0];
5407  		if (typeof item === "string") {
5408  			item = item.replace(leadingPattern, "");
5409
5410  			if (!item) {
5411  				items.shift();
5412  			} else {
5413  				items[0] = item;
5414  			}
5415  		}
5416  	}
5417
5418  	if (trailingPattern) {
5419  		item = lastItem(items);
5420  		if (typeof item === "string") {
5421  			item = item.replace(trailingPattern, "");
5422
5423  			if (!item) {
5424  				items.pop();
5425  			} else {
5426  				items[items.length - 1] = item;
5427  			}
5428  		}
5429  	}
5430  };
5431
5432  var utils_cleanup = cleanup;
5433  var contiguousWhitespace = /[ \t\f\r\n]+/g;
5434  var preserveWhitespaceElements = /^(?:pre|script|style|textarea)$/i;
5435  var utils_cleanup__leadingWhitespace = /^[ \t\f\r\n]+/;
5436  var trailingWhitespace = /[ \t\f\r\n]+$/;
5437  var leadingNewLine = /^(?:\r\n|\r|\n)/;
5438  var trailingNewLine = /(?:\r\n|\r|\n)$/;
5439  function cleanup(items, stripComments, preserveWhitespace, removeLeadingWhitespace, removeTrailingWhitespace) {
5440  	var i, item, previousItem, nextItem, preserveWhitespaceInsideFragment, removeLeadingWhitespaceInsideFragment, removeTrailingWhitespaceInsideFragment, key;
5441
5442  	// First pass - remove standalones and comments etc
5443  	stripStandalones(items);
5444
5445  	i = items.length;
5446  	while (i--) {
5447  		item = items[i];
5448
5449  		// Remove delimiter changes, unsafe elements etc
5450  		if (item.exclude) {
5451  			items.splice(i, 1);
5452  		}
5453
5454  		// Remove comments, unless we want to keep them
5455  		else if (stripComments && item.t === COMMENT) {
5456  			items.splice(i, 1);
5457  		}
5458  	}
5459
5460  	// If necessary, remove leading and trailing whitespace
5461  	trimWhitespace(items, removeLeadingWhitespace ? utils_cleanup__leadingWhitespace : null, removeTrailingWhitespace ? trailingWhitespace : null);
5462
5463  	i = items.length;
5464  	while (i--) {
5465  		item = items[i];
5466
5467  		// Recurse
5468  		if (item.f) {
5469  			var isPreserveWhitespaceElement = item.t === ELEMENT && preserveWhitespaceElements.test(item.e);
5470  			preserveWhitespaceInsideFragment = preserveWhitespace || isPreserveWhitespaceElement;
5471
5472  			if (!preserveWhitespace && isPreserveWhitespaceElement) {
5473  				trimWhitespace(item.f, leadingNewLine, trailingNewLine);
5474  			}
5475
5476  			if (!preserveWhitespaceInsideFragment) {
5477  				previousItem = items[i - 1];
5478  				nextItem = items[i + 1];
5479
5480  				// if the previous item was a text item with trailing whitespace,
5481  				// remove leading whitespace inside the fragment
5482  				if (!previousItem || typeof previousItem === "string" && trailingWhitespace.test(previousItem)) {
5483  					removeLeadingWhitespaceInsideFragment = true;
5484  				}
5485
5486  				// and vice versa
5487  				if (!nextItem || typeof nextItem === "string" && utils_cleanup__leadingWhitespace.test(nextItem)) {
5488  					removeTrailingWhitespaceInsideFragment = true;
5489  				}
5490  			}
5491
5492  			cleanup(item.f, stripComments, preserveWhitespaceInsideFragment, removeLeadingWhitespaceInsideFragment, removeTrailingWhitespaceInsideFragment);
5493  		}
5494
5495  		// Split if-else blocks into two (an if, and an unless)
5496  		if (item.l) {
5497  			cleanup(item.l.f, stripComments, preserveWhitespace, removeLeadingWhitespaceInsideFragment, removeTrailingWhitespaceInsideFragment);
5498
5499  			items.splice(i + 1, 0, item.l);
5500  			delete item.l; // TODO would be nice if there was a way around this
5501  		}
5502
5503  		// Clean up element attributes
5504  		if (item.a) {
5505  			for (key in item.a) {
5506  				if (item.a.hasOwnProperty(key) && typeof item.a[key] !== "string") {
5507  					cleanup(item.a[key], stripComments, preserveWhitespace, removeLeadingWhitespaceInsideFragment, removeTrailingWhitespaceInsideFragment);
5508  				}
5509  			}
5510  		}
5511
5512  		// Clean up conditional attributes
5513  		if (item.m) {
5514  			cleanup(item.m, stripComments, preserveWhitespace, removeLeadingWhitespaceInsideFragment, removeTrailingWhitespaceInsideFragment);
5515  		}
5516
5517  		// Clean up event handlers
5518  		if (item.v) {
5519  			for (key in item.v) {
5520  				if (item.v.hasOwnProperty(key)) {
5521  					// clean up names
5522  					if (isArray(item.v[key].n)) {
5523  						cleanup(item.v[key].n, stripComments, preserveWhitespace, removeLeadingWhitespaceInsideFragment, removeTrailingWhitespaceInsideFragment);
5524  					}
5525
5526  					// clean up params
5527  					if (isArray(item.v[key].d)) {
5528  						cleanup(item.v[key].d, stripComments, preserveWhitespace, removeLeadingWhitespaceInsideFragment, removeTrailingWhitespaceInsideFragment);
5529  					}
5530  				}
5531  			}
5532  		}
5533  	}
5534
5535  	// final pass - fuse text nodes together
5536  	i = items.length;
5537  	while (i--) {
5538  		if (typeof items[i] === "string") {
5539  			if (typeof items[i + 1] === "string") {
5540  				items[i] = items[i] + items[i + 1];
5541  				items.splice(i + 1, 1);
5542  			}
5543
5544  			if (!preserveWhitespace) {
5545  				items[i] = items[i].replace(contiguousWhitespace, " ");
5546  			}
5547
5548  			if (items[i] === "") {
5549  				items.splice(i, 1);
5550  			}
5551  		}
5552  	}
5553  }
5554
5555  var element_readClosingTag = readClosingTag;
5556  var closingTagPattern = /^([a-zA-Z]{1,}:?[a-zA-Z0-9\-]*)\s*\>/;
5557  function readClosingTag(parser) {
5558  	var start, tag;
5559
5560  	start = parser.pos;
5561
5562  	// are we looking at a closing tag?
5563  	if (!parser.matchString("</")) {
5564  		return null;
5565  	}
5566
5567  	if (tag = parser.matchPattern(closingTagPattern)) {
5568  		if (parser.inside && tag !== parser.inside) {
5569  			parser.pos = start;
5570  			return null;
5571  		}
5572
5573  		return {
5574  			t: CLOSING_TAG,
5575  			e: tag
5576  		};
5577  	}
5578
5579  	// We have an illegal closing tag, report it
5580  	parser.pos -= 2;
5581  	parser.error("Illegal closing tag");
5582  }
5583
5584  var getLowestIndex = function (haystack, needles) {
5585  	var i, index, lowest;
5586
5587  	i = needles.length;
5588  	while (i--) {
5589  		index = haystack.indexOf(needles[i]);
5590
5591  		// short circuit
5592  		if (!index) {
5593  			return 0;
5594  		}
5595
5596  		if (index === -1) {
5597  			continue;
5598  		}
5599
5600  		if (!lowest || index < lowest) {
5601  			lowest = index;
5602  		}
5603  	}
5604
5605  	return lowest || -1;
5606  };
5607
5608  var element_readAttribute = readAttribute;
5609
5610  var attributeNamePattern = /^[^\s"'>\/=]+/,
5611      unquotedAttributeValueTextPattern = /^[^\s"'=<>`]+/;
5612  function readAttribute(parser) {
5613  	var attr, name, value;
5614
5615  	parser.allowWhitespace();
5616
5617  	name = parser.matchPattern(attributeNamePattern);
5618  	if (!name) {
5619  		return null;
5620  	}
5621
5622  	attr = { name: name };
5623
5624  	value = readAttributeValue(parser);
5625  	if (value != null) {
5626  		// not null/undefined
5627  		attr.value = value;
5628  	}
5629
5630  	return attr;
5631  }
5632
5633  function readAttributeValue(parser) {
5634  	var start, valueStart, startDepth, value;
5635
5636  	start = parser.pos;
5637
5638  	// next character must be `=`, `/`, `>` or whitespace
5639  	if (!/[=\/>\s]/.test(parser.nextChar())) {
5640  		parser.error("Expected `=`, `/`, `>` or whitespace");
5641  	}
5642
5643  	parser.allowWhitespace();
5644
5645  	if (!parser.matchString("=")) {
5646  		parser.pos = start;
5647  		return null;
5648  	}
5649
5650  	parser.allowWhitespace();
5651
5652  	valueStart = parser.pos;
5653  	startDepth = parser.sectionDepth;
5654
5655  	value = readQuotedAttributeValue(parser, "'") || readQuotedAttributeValue(parser, "\"") || readUnquotedAttributeValue(parser);
5656
5657  	if (value === null) {
5658  		parser.error("Expected valid attribute value");
5659  	}
5660
5661  	if (parser.sectionDepth !== startDepth) {
5662  		parser.pos = valueStart;
5663  		parser.error("An attribute value must contain as many opening section tags as closing section tags");
5664  	}
5665
5666  	if (!value.length) {
5667  		return "";
5668  	}
5669
5670  	if (value.length === 1 && typeof value[0] === "string") {
5671  		return decodeCharacterReferences(value[0]);
5672  	}
5673
5674  	return value;
5675  }
5676
5677  function readUnquotedAttributeValueToken(parser) {
5678  	var start, text, haystack, needles, index;
5679
5680  	start = parser.pos;
5681
5682  	text = parser.matchPattern(unquotedAttributeValueTextPattern);
5683
5684  	if (!text) {
5685  		return null;
5686  	}
5687
5688  	haystack = text;
5689  	needles = parser.tags.map(function (t) {
5690  		return t.open;
5691  	}); // TODO refactor... we do this in readText.js as well
5692
5693  	if ((index = getLowestIndex(haystack, needles)) !== -1) {
5694  		text = text.substr(0, index);
5695  		parser.pos = start + text.length;
5696  	}
5697
5698  	return text;
5699  }
5700
5701  function readUnquotedAttributeValue(parser) {
5702  	var tokens, token;
5703
5704  	parser.inAttribute = true;
5705
5706  	tokens = [];
5707
5708  	token = converters_readMustache(parser) || readUnquotedAttributeValueToken(parser);
5709  	while (token !== null) {
5710  		tokens.push(token);
5711  		token = converters_readMustache(parser) || readUnquotedAttributeValueToken(parser);
5712  	}
5713
5714  	if (!tokens.length) {
5715  		return null;
5716  	}
5717
5718  	parser.inAttribute = false;
5719  	return tokens;
5720  }
5721
5722  function readQuotedAttributeValue(parser, quoteMark) {
5723  	var start, tokens, token;
5724
5725  	start = parser.pos;
5726
5727  	if (!parser.matchString(quoteMark)) {
5728  		return null;
5729  	}
5730
5731  	parser.inAttribute = quoteMark;
5732
5733  	tokens = [];
5734
5735  	token = converters_readMustache(parser) || readQuotedStringToken(parser, quoteMark);
5736  	while (token !== null) {
5737  		tokens.push(token);
5738  		token = converters_readMustache(parser) || readQuotedStringToken(parser, quoteMark);
5739  	}
5740
5741  	if (!parser.matchString(quoteMark)) {
5742  		parser.pos = start;
5743  		return null;
5744  	}
5745
5746  	parser.inAttribute = false;
5747
5748  	return tokens;
5749  }
5750
5751  function readQuotedStringToken(parser, quoteMark) {
5752  	var start, index, haystack, needles;
5753
5754  	start = parser.pos;
5755  	haystack = parser.remaining();
5756
5757  	needles = parser.tags.map(function (t) {
5758  		return t.open;
5759  	}); // TODO refactor... we do this in readText.js as well
5760  	needles.push(quoteMark);
5761
5762  	index = getLowestIndex(haystack, needles);
5763
5764  	if (index === -1) {
5765  		parser.error("Quoted attribute value must have a closing quote");
5766  	}
5767
5768  	if (!index) {
5769  		return null;
5770  	}
5771
5772  	parser.pos += index;
5773  	return haystack.substr(0, index);
5774  }
5775
5776  var JsonParser, specials, specialsPattern, parseJSON__numberPattern, placeholderPattern, placeholderAtStartPattern, onlyWhitespace;
5777
5778  specials = {
5779  	"true": true,
5780  	"false": false,
5781  	undefined: undefined,
5782  	"null": null
5783  };
5784
5785  specialsPattern = new RegExp("^(?:" + Object.keys(specials).join("|") + ")");
5786  parseJSON__numberPattern = /^(?:[+-]?)(?:(?:(?:0|[1-9]\d*)?\.\d+)|(?:(?:0|[1-9]\d*)\.)|(?:0|[1-9]\d*))(?:[eE][+-]?\d+)?/;
5787  placeholderPattern = /\$\{([^\}]+)\}/g;
5788  placeholderAtStartPattern = /^\$\{([^\}]+)\}/;
5789  onlyWhitespace = /^\s*$/;
5790
5791  JsonParser = parse_Parser.extend({
5792  	init: function (str, options) {
5793  		this.values = options.values;
5794  		this.allowWhitespace();
5795  	},
5796
5797  	postProcess: function (result) {
5798  		if (result.length !== 1 || !onlyWhitespace.test(this.leftover)) {
5799  			return null;
5800  		}
5801
5802  		return { value: result[0].v };
5803  	},
5804
5805  	converters: [function getPlaceholder(parser) {
5806  		var placeholder;
5807
5808  		if (!parser.values) {
5809  			return null;
5810  		}
5811
5812  		placeholder = parser.matchPattern(placeholderAtStartPattern);
5813
5814  		if (placeholder && parser.values.hasOwnProperty(placeholder)) {
5815  			return { v: parser.values[placeholder] };
5816  		}
5817  	}, function getSpecial(parser) {
5818  		var special;
5819
5820  		if (special = parser.matchPattern(specialsPattern)) {
5821  			return { v: specials[special] };
5822  		}
5823  	}, function getNumber(parser) {
5824  		var number;
5825
5826  		if (number = parser.matchPattern(parseJSON__numberPattern)) {
5827  			return { v: +number };
5828  		}
5829  	}, function getString(parser) {
5830  		var stringLiteral = readStringLiteral(parser),
5831  		    values;
5832
5833  		if (stringLiteral && (values = parser.values)) {
5834  			return {
5835  				v: stringLiteral.v.replace(placeholderPattern, function (match, $1) {
5836  					return $1 in values ? values[$1] : $1;
5837  				})
5838  			};
5839  		}
5840
5841  		return stringLiteral;
5842  	}, function getObject(parser) {
5843  		var result, pair;
5844
5845  		if (!parser.matchString("{")) {
5846  			return null;
5847  		}
5848
5849  		result = {};
5850
5851  		parser.allowWhitespace();
5852
5853  		if (parser.matchString("}")) {
5854  			return { v: result };
5855  		}
5856
5857  		while (pair = getKeyValuePair(parser)) {
5858  			result[pair.key] = pair.value;
5859
5860  			parser.allowWhitespace();
5861
5862  			if (parser.matchString("}")) {
5863  				return { v: result };
5864  			}
5865
5866  			if (!parser.matchString(",")) {
5867  				return null;
5868  			}
5869  		}
5870
5871  		return null;
5872  	}, function getArray(parser) {
5873  		var result, valueToken;
5874
5875  		if (!parser.matchString("[")) {
5876  			return null;
5877  		}
5878
5879  		result = [];
5880
5881  		parser.allowWhitespace();
5882
5883  		if (parser.matchString("]")) {
5884  			return { v: result };
5885  		}
5886
5887  		while (valueToken = parser.read()) {
5888  			result.push(valueToken.v);
5889
5890  			parser.allowWhitespace();
5891
5892  			if (parser.matchString("]")) {
5893  				return { v: result };
5894  			}
5895
5896  			if (!parser.matchString(",")) {
5897  				return null;
5898  			}
5899
5900  			parser.allowWhitespace();
5901  		}
5902
5903  		return null;
5904  	}]
5905  });
5906
5907  function getKeyValuePair(parser) {
5908  	var key, valueToken, pair;
5909
5910  	parser.allowWhitespace();
5911
5912  	key = shared_readKey(parser);
5913
5914  	if (!key) {
5915  		return null;
5916  	}
5917
5918  	pair = { key: key };
5919
5920  	parser.allowWhitespace();
5921  	if (!parser.matchString(":")) {
5922  		return null;
5923  	}
5924  	parser.allowWhitespace();
5925
5926  	valueToken = parser.read();
5927  	if (!valueToken) {
5928  		return null;
5929  	}
5930
5931  	pair.value = valueToken.v;
5932
5933  	return pair;
5934  }
5935
5936  var parseJSON = function (str, values) {
5937  	var parser = new JsonParser(str, {
5938  		values: values
5939  	});
5940
5941  	return parser.result;
5942  };
5943
5944  // TODO clean this up, it's shocking
5945  var element_processDirective = processDirective;
5946  var methodCallPattern = /^([a-zA-Z_$][a-zA-Z_$0-9]*)\(/,
5947      methodCallExcessPattern = /\)\s*$/,
5948      ExpressionParser;
5949
5950  ExpressionParser = parse_Parser.extend({
5951  	converters: [converters_readExpression]
5952  });
5953  function processDirective(tokens, parentParser) {
5954  	var result, match, parser, args, token, colonIndex, directiveName, directiveArgs, parsed;
5955
5956  	if (typeof tokens === "string") {
5957  		if (match = methodCallPattern.exec(tokens)) {
5958  			var end = tokens.lastIndexOf(")");
5959
5960  			// check for invalid method calls
5961  			if (!methodCallExcessPattern.test(tokens)) {
5962  				parentParser.error("Invalid input after method call expression '" + tokens.slice(end + 1) + "'");
5963  			}
5964
5965  			result = { m: match[1] };
5966  			args = "[" + tokens.slice(result.m.length + 1, end) + "]";
5967
5968  			parser = new ExpressionParser(args);
5969  			result.a = utils_flattenExpression(parser.result[0]);
5970
5971  			return result;
5972  		}
5973
5974  		if (tokens.indexOf(":") === -1) {
5975  			return tokens.trim();
5976  		}
5977
5978  		tokens = [tokens];
5979  	}
5980
5981  	result = {};
5982
5983  	directiveName = [];
5984  	directiveArgs = [];
5985
5986  	if (tokens) {
5987  		while (tokens.length) {
5988  			token = tokens.shift();
5989
5990  			if (typeof token === "string") {
5991  				colonIndex = token.indexOf(":");
5992
5993  				if (colonIndex === -1) {
5994  					directiveName.push(token);
5995  				} else {
5996
5997  					// is the colon the first character?
5998  					if (colonIndex) {
5999  						// no
6000  						directiveName.push(token.substr(0, colonIndex));
6001  					}
6002
6003  					// if there is anything after the colon in this token, treat
6004  					// it as the first token of the directiveArgs fragment
6005  					if (token.length > colonIndex + 1) {
6006  						directiveArgs[0] = token.substring(colonIndex + 1);
6007  					}
6008
6009  					break;
6010  				}
6011  			} else {
6012  				directiveName.push(token);
6013  			}
6014  		}
6015
6016  		directiveArgs = directiveArgs.concat(tokens);
6017  	}
6018
6019  	if (!directiveName.length) {
6020  		result = "";
6021  	} else if (directiveArgs.length || typeof directiveName !== "string") {
6022  		result = {
6023  			// TODO is this really necessary? just use the array
6024  			n: directiveName.length === 1 && typeof directiveName[0] === "string" ? directiveName[0] : directiveName
6025  		};
6026
6027  		if (directiveArgs.length === 1 && typeof directiveArgs[0] === "string") {
6028  			parsed = parseJSON("[" + directiveArgs[0] + "]");
6029  			result.a = parsed ? parsed.value : directiveArgs[0].trim();
6030  		} else {
6031  			result.d = directiveArgs;
6032  		}
6033  	} else {
6034  		result = directiveName;
6035  	}
6036
6037  	return result;
6038  }
6039
6040  var tagNamePattern = /^[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/,
6041      validTagNameFollower = /^[\s\n\/>]/,
6042      onPattern = /^on/,
6043      proxyEventPattern = /^on-([a-zA-Z\\*\\.$_][a-zA-Z\\*\\.$_0-9\-]+)$/,
6044      reservedEventNames = /^(?:change|reset|teardown|update|construct|config|init|render|unrender|detach|insert)$/,
6045      directives = { "intro-outro": "t0", intro: "t1", outro: "t2", decorator: "o" },
6046      exclude = { exclude: true },
6047      disallowedContents;
6048
6049  // based on http://developers.whatwg.org/syntax.html#syntax-tag-omission
6050  disallowedContents = {
6051  	li: ["li"],
6052  	dt: ["dt", "dd"],
6053  	dd: ["dt", "dd"],
6054  	p: "address article aside blockquote div dl fieldset footer form h1 h2 h3 h4 h5 h6 header hgroup hr main menu nav ol p pre section table ul".split(" "),
6055  	rt: ["rt", "rp"],
6056  	rp: ["rt", "rp"],
6057  	optgroup: ["optgroup"],
6058  	option: ["option", "optgroup"],
6059  	thead: ["tbody", "tfoot"],
6060  	tbody: ["tbody", "tfoot"],
6061  	tfoot: ["tbody"],
6062  	tr: ["tr", "tbody"],
6063  	td: ["td", "th", "tr"],
6064  	th: ["td", "th", "tr"]
6065  };
6066
6067  var converters_readElement = readElement;
6068
6069  function readElement(parser) {
6070  	var start, element, directiveName, match, addProxyEvent, attribute, directive, selfClosing, children, partials, hasPartials, child, closed, pos, remaining, closingTag;
6071
6072  	start = parser.pos;
6073
6074  	if (parser.inside || parser.inAttribute) {
6075  		return null;
6076  	}
6077
6078  	if (!parser.matchString("<")) {
6079  		return null;
6080  	}
6081
6082  	// if this is a closing tag, abort straight away
6083  	if (parser.nextChar() === "/") {
6084  		return null;
6085  	}
6086
6087  	element = {};
6088  	if (parser.includeLinePositions) {
6089  		element.p = parser.getLinePos(start);
6090  	}
6091
6092  	if (parser.matchString("!")) {
6093  		element.t = DOCTYPE;
6094  		if (!parser.matchPattern(/^doctype/i)) {
6095  			parser.error("Expected DOCTYPE declaration");
6096  		}
6097
6098  		element.a = parser.matchPattern(/^(.+?)>/);
6099  		return element;
6100  	}
6101
6102  	element.t = ELEMENT;
6103
6104  	// element name
6105  	element.e = parser.matchPattern(tagNamePattern);
6106  	if (!element.e) {
6107  		return null;
6108  	}
6109
6110  	// next character must be whitespace, closing solidus or '>'
6111  	if (!validTagNameFollower.test(parser.nextChar())) {
6112  		parser.error("Illegal tag name");
6113  	}
6114
6115  	addProxyEvent = function (name, directive) {
6116  		var directiveName = directive.n || directive;
6117
6118  		if (reservedEventNames.test(directiveName)) {
6119  			parser.pos -= directiveName.length;
6120  			parser.error("Cannot use reserved event names (change, reset, teardown, update, construct, config, init, render, unrender, detach, insert)");
6121  		}
6122
6123  		element.v[name] = directive;
6124  	};
6125
6126  	parser.allowWhitespace();
6127
6128  	// directives and attributes
6129  	while (attribute = converters_readMustache(parser) || element_readAttribute(parser)) {
6130  		// regular attributes
6131  		if (attribute.name) {
6132  			// intro, outro, decorator
6133  			if (directiveName = directives[attribute.name]) {
6134  				element[directiveName] = element_processDirective(attribute.value, parser);
6135  			}
6136
6137  			// on-click etc
6138  			else if (match = proxyEventPattern.exec(attribute.name)) {
6139  				if (!element.v) element.v = {};
6140  				directive = element_processDirective(attribute.value, parser);
6141  				addProxyEvent(match[1], directive);
6142  			} else {
6143  				if (!parser.sanitizeEventAttributes || !onPattern.test(attribute.name)) {
6144  					if (!element.a) element.a = {};
6145  					element.a[attribute.name] = attribute.value || (attribute.value === "" ? "" : 0);
6146  				}
6147  			}
6148  		}
6149
6150  		// {{#if foo}}class='foo'{{/if}}
6151  		else {
6152  			if (!element.m) element.m = [];
6153  			element.m.push(attribute);
6154  		}
6155
6156  		parser.allowWhitespace();
6157  	}
6158
6159  	// allow whitespace before closing solidus
6160  	parser.allowWhitespace();
6161
6162  	// self-closing solidus?
6163  	if (parser.matchString("/")) {
6164  		selfClosing = true;
6165  	}
6166
6167  	// closing angle bracket
6168  	if (!parser.matchString(">")) {
6169  		return null;
6170  	}
6171
6172  	var lowerCaseName = element.e.toLowerCase();
6173  	var preserveWhitespace = parser.preserveWhitespace;
6174
6175  	if (!selfClosing && !voidElementNames.test(element.e)) {
6176  		parser.elementStack.push(lowerCaseName);
6177
6178  		// Special case - if we open a script element, further tags should
6179  		// be ignored unless they're a closing script element
6180  		if (lowerCaseName === "script" || lowerCaseName === "style") {
6181  			parser.inside = lowerCaseName;
6182  		}
6183
6184  		children = [];
6185  		partials = create(null);
6186
6187  		do {
6188  			pos = parser.pos;
6189  			remaining = parser.remaining();
6190
6191  			// if for example we're in an <li> element, and we see another
6192  			// <li> tag, close the first so they become siblings
6193  			if (!canContain(lowerCaseName, remaining)) {
6194  				closed = true;
6195  			}
6196
6197  			// closing tag
6198  			else if (closingTag = element_readClosingTag(parser)) {
6199  				closed = true;
6200
6201  				var closingTagName = closingTag.e.toLowerCase();
6202
6203  				// if this *isn't* the closing tag for the current element...
6204  				if (closingTagName !== lowerCaseName) {
6205  					// rewind parser
6206  					parser.pos = pos;
6207
6208  					// if it doesn't close a parent tag, error
6209  					if (! ~parser.elementStack.indexOf(closingTagName)) {
6210  						var errorMessage = "Unexpected closing tag";
6211
6212  						// add additional help for void elements, since component names
6213  						// might clash with them
6214  						if (voidElementNames.test(closingTagName)) {
6215  							errorMessage += " (<" + closingTagName + "> is a void element - it cannot contain children)";
6216  						}
6217
6218  						parser.error(errorMessage);
6219  					}
6220  				}
6221  			}
6222
6223  			// implicit close by closing section tag. TODO clean this up
6224  			else if (child = section_readClosing(parser, { open: parser.standardDelimiters[0], close: parser.standardDelimiters[1] })) {
6225  				closed = true;
6226  				parser.pos = pos;
6227  			} else {
6228  				if (child = parser.read(PARTIAL_READERS)) {
6229  					if (partials[child.n]) {
6230  						parser.pos = pos;
6231  						parser.error("Duplicate partial definition");
6232  					}
6233
6234  					utils_cleanup(child.f, parser.stripComments, preserveWhitespace, !preserveWhitespace, !preserveWhitespace);
6235
6236  					partials[child.n] = child.f;
6237  					hasPartials = true;
6238  				} else {
6239  					if (child = parser.read(READERS)) {
6240  						children.push(child);
6241  					} else {
6242  						closed = true;
6243  					}
6244  				}
6245  			}
6246  		} while (!closed);
6247
6248  		if (children.length) {
6249  			element.f = children;
6250  		}
6251
6252  		if (hasPartials) {
6253  			element.p = partials;
6254  		}
6255
6256  		parser.elementStack.pop();
6257  	}
6258
6259  	parser.inside = null;
6260
6261  	if (parser.sanitizeElements && parser.sanitizeElements.indexOf(lowerCaseName) !== -1) {
6262  		return exclude;
6263  	}
6264
6265  	return element;
6266  }
6267
6268  function canContain(name, remaining) {
6269  	var match, disallowed;
6270
6271  	match = /^<([a-zA-Z][a-zA-Z0-9]*)/.exec(remaining);
6272  	disallowed = disallowedContents[name];
6273
6274  	if (!match || !disallowed) {
6275  		return true;
6276  	}
6277
6278  	return ! ~disallowed.indexOf(match[1].toLowerCase());
6279  }
6280
6281  var converters_readText = readText;
6282  function readText(parser) {
6283  	var index, remaining, disallowed, barrier;
6284
6285  	remaining = parser.remaining();
6286
6287  	barrier = parser.inside ? "</" + parser.inside : "<";
6288
6289  	if (parser.inside && !parser.interpolate[parser.inside]) {
6290  		index = remaining.indexOf(barrier);
6291  	} else {
6292  		disallowed = parser.tags.map(function (t) {
6293  			return t.open;
6294  		});
6295  		disallowed = disallowed.concat(parser.tags.map(function (t) {
6296  			return "\\" + t.open;
6297  		}));
6298
6299  		// http://developers.whatwg.org/syntax.html#syntax-attributes
6300  		if (parser.inAttribute === true) {
6301  			// we're inside an unquoted attribute value
6302  			disallowed.push("\"", "'", "=", "<", ">", "`");
6303  		} else if (parser.inAttribute) {
6304  			// quoted attribute value
6305  			disallowed.push(parser.inAttribute);
6306  		} else {
6307  			disallowed.push(barrier);
6308  		}
6309
6310  		index = getLowestIndex(remaining, disallowed);
6311  	}
6312
6313  	if (!index) {
6314  		return null;
6315  	}
6316
6317  	if (index === -1) {
6318  		index = remaining.length;
6319  	}
6320
6321  	parser.pos += index;
6322
6323  	return parser.inside ? remaining.substr(0, index) : decodeCharacterReferences(remaining.substr(0, index));
6324  }
6325
6326  var utils_escapeRegExp = escapeRegExp;
6327  var utils_escapeRegExp__pattern = /[-/\\^$*+?.()|[\]{}]/g;
6328  function escapeRegExp(str) {
6329  	return str.replace(utils_escapeRegExp__pattern, "\\$&");
6330  }
6331
6332  var converters_readPartialDefinitionComment = readPartialDefinitionComment;
6333
6334  var startPattern = /^<!--\s*/,
6335      namePattern = /s*>\s*([a-zA-Z_$][-a-zA-Z_$0-9]*)\s*/,
6336      finishPattern = /\s*-->/,
6337      child;
6338
6339  function readPartialDefinitionComment(parser) {
6340  	var firstPos = parser.pos,
6341  	    open = parser.standardDelimiters[0],
6342  	    close = parser.standardDelimiters[1],
6343  	    content = undefined,
6344  	    closed = undefined;
6345
6346  	if (!parser.matchPattern(startPattern) || !parser.matchString(open)) {
6347  		parser.pos = firstPos;
6348  		return null;
6349  	}
6350
6351  	var name = parser.matchPattern(namePattern);
6352
6353  	warnOnceIfDebug("Inline partial comments are deprecated.\nUse this...\n  {{#partial " + name + "}} ... {{/partial}}\n\n...instead of this:\n  <!-- {{>" + name + "}} --> ... <!-- {{/" + name + "}} -->'");
6354
6355  	// make sure the rest of the comment is in the correct place
6356  	if (!parser.matchString(close) || !parser.matchPattern(finishPattern)) {
6357  		parser.pos = firstPos;
6358  		return null;
6359  	}
6360
6361  	content = [];
6362
6363  	var endPattern = new RegExp("^<!--\\s*" + utils_escapeRegExp(open) + "\\s*\\/\\s*" + name + "\\s*" + utils_escapeRegExp(close) + "\\s*-->");
6364
6365  	do {
6366  		if (parser.matchPattern(endPattern)) {
6367  			closed = true;
6368  		} else {
6369  			child = parser.read(READERS);
6370  			if (!child) {
6371  				parser.error("expected closing comment ('<!-- " + open + "/" + name + "" + close + " -->')");
6372  			}
6373
6374  			content.push(child);
6375  		}
6376  	} while (!closed);
6377
6378  	return {
6379  		t: INLINE_PARTIAL,
6380  		f: content,
6381  		n: name
6382  	};
6383  }
6384
6385  var converters_readPartialDefinitionSection = readPartialDefinitionSection;
6386  var partialDefinitionSectionPattern = /^#\s*partial\s+/;
6387  function readPartialDefinitionSection(parser) {
6388  	var start, name, content, child, closed;
6389
6390  	start = parser.pos;
6391
6392  	var delimiters = parser.standardDelimiters;
6393
6394  	if (!parser.matchString(delimiters[0])) {
6395  		return null;
6396  	}
6397
6398  	if (!parser.matchPattern(partialDefinitionSectionPattern)) {
6399  		parser.pos = start;
6400  		return null;
6401  	}
6402
6403  	name = parser.matchPattern(/^[a-zA-Z_$][a-zA-Z_$0-9\-]*/);
6404
6405  	if (!name) {
6406  		parser.error("expected legal partial name");
6407  	}
6408
6409  	if (!parser.matchString(delimiters[1])) {
6410  		parser.error("Expected closing delimiter '" + delimiters[1] + "'");
6411  	}
6412
6413  	content = [];
6414
6415  	do {
6416  		// TODO clean this up
6417  		if (child = section_readClosing(parser, { open: parser.standardDelimiters[0], close: parser.standardDelimiters[1] })) {
6418  			if (!child.r === "partial") {
6419  				parser.error("Expected " + delimiters[0] + "/partial" + delimiters[1]);
6420  			}
6421
6422  			closed = true;
6423  		} else {
6424  			child = parser.read(READERS);
6425
6426  			if (!child) {
6427  				parser.error("Expected " + delimiters[0] + "/partial" + delimiters[1]);
6428  			}
6429
6430  			content.push(child);
6431  		}
6432  	} while (!closed);
6433
6434  	return {
6435  		t: INLINE_PARTIAL,
6436  		n: name,
6437  		f: content
6438  	};
6439  }
6440
6441  var converters_readTemplate = readTemplate;
6442  function readTemplate(parser) {
6443  	var fragment = [];
6444  	var partials = create(null);
6445  	var hasPartials = false;
6446
6447  	var preserveWhitespace = parser.preserveWhitespace;
6448
6449  	while (parser.pos < parser.str.length) {
6450  		var pos = parser.pos,
6451  		    item = undefined,
6452  		    partial = undefined;
6453
6454  		if (partial = parser.read(PARTIAL_READERS)) {
6455  			if (partials[partial.n]) {
6456  				parser.pos = pos;
6457  				parser.error("Duplicated partial definition");
6458  			}
6459
6460  			utils_cleanup(partial.f, parser.stripComments, preserveWhitespace, !preserveWhitespace, !preserveWhitespace);
6461
6462  			partials[partial.n] = partial.f;
6463  			hasPartials = true;
6464  		} else if (item = parser.read(READERS)) {
6465  			fragment.push(item);
6466  		} else {
6467  			parser.error("Unexpected template content");
6468  		}
6469  	}
6470
6471  	var result = {
6472  		v: TEMPLATE_VERSION,
6473  		t: fragment
6474  	};
6475
6476  	if (hasPartials) {
6477  		result.p = partials;
6478  	}
6479
6480  	return result;
6481  }
6482
6483  var _parse = parse;
6484
6485  var STANDARD_READERS = [mustache_readPartial, mustache_readUnescaped, mustache_readSection, mustache_readYielder, mustache_readInterpolator, readMustacheComment];
6486  var TRIPLE_READERS = [mustache_readTriple];
6487  var STATIC_READERS = [mustache_readUnescaped, mustache_readSection, mustache_readInterpolator]; // TODO does it make sense to have a static section?
6488
6489  var StandardParser = undefined;
6490  function parse(template, options) {
6491  	return new StandardParser(template, options || {}).result;
6492  }
6493
6494  var READERS = [converters_readMustache, converters_readHtmlComment, converters_readElement, converters_readText];
6495  var PARTIAL_READERS = [converters_readPartialDefinitionComment, converters_readPartialDefinitionSection];
6496
6497  StandardParser = parse_Parser.extend({
6498  	init: function (str, options) {
6499  		var tripleDelimiters = options.tripleDelimiters || ["{{{", "}}}"],
6500  		    staticDelimiters = options.staticDelimiters || ["[[", "]]"],
6501  		    staticTripleDelimiters = options.staticTripleDelimiters || ["[[[", "]]]"];
6502
6503  		this.standardDelimiters = options.delimiters || ["{{", "}}"];
6504
6505  		this.tags = [{ isStatic: false, isTriple: false, open: this.standardDelimiters[0], close: this.standardDelimiters[1], readers: STANDARD_READERS }, { isStatic: false, isTriple: true, open: tripleDelimiters[0], close: tripleDelimiters[1], readers: TRIPLE_READERS }, { isStatic: true, isTriple: false, open: staticDelimiters[0], close: staticDelimiters[1], readers: STATIC_READERS }, { isStatic: true, isTriple: true, open: staticTripleDelimiters[0], close: staticTripleDelimiters[1], readers: TRIPLE_READERS }];
6506
6507  		this.sortMustacheTags();
6508
6509  		this.sectionDepth = 0;
6510  		this.elementStack = [];
6511
6512  		this.interpolate = {
6513  			script: !options.interpolate || options.interpolate.script !== false,
6514  			style: !options.interpolate || options.interpolate.style !== false
6515  		};
6516
6517  		if (options.sanitize === true) {
6518  			options.sanitize = {
6519  				// blacklist from https://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/lang/html/html4-elements-whitelist.json
6520  				elements: "applet base basefont body frame frameset head html isindex link meta noframes noscript object param script style title".split(" "),
6521  				eventAttributes: true
6522  			};
6523  		}
6524
6525  		this.stripComments = options.stripComments !== false;
6526  		this.preserveWhitespace = options.preserveWhitespace;
6527  		this.sanitizeElements = options.sanitize && options.sanitize.elements;
6528  		this.sanitizeEventAttributes = options.sanitize && options.sanitize.eventAttributes;
6529  		this.includeLinePositions = options.includeLinePositions;
6530  	},
6531
6532  	postProcess: function (result) {
6533  		// special case - empty string
6534  		if (!result.length) {
6535  			return { t: [], v: TEMPLATE_VERSION };
6536  		}
6537
6538  		if (this.sectionDepth > 0) {
6539  			this.error("A section was left open");
6540  		}
6541
6542  		utils_cleanup(result[0].t, this.stripComments, this.preserveWhitespace, !this.preserveWhitespace, !this.preserveWhitespace);
6543
6544  		return result[0];
6545  	},
6546
6547  	converters: [converters_readTemplate],
6548
6549  	sortMustacheTags: function () {
6550  		// Sort in order of descending opening delimiter length (longer first),
6551  		// to protect against opening delimiters being substrings of each other
6552  		this.tags.sort(function (a, b) {
6553  			return b.open.length - a.open.length;
6554  		});
6555  	}
6556  });
6557
6558  var parseOptions = ["preserveWhitespace", "sanitize", "stripComments", "delimiters", "tripleDelimiters", "interpolate"];
6559
6560  var parser = {
6561  	fromId: fromId, isHashedId: isHashedId, isParsed: isParsed, getParseOptions: getParseOptions, createHelper: template_parser__createHelper,
6562  	parse: doParse
6563  };
6564
6565  function template_parser__createHelper(parseOptions) {
6566  	var helper = create(parser);
6567  	helper.parse = function (template, options) {
6568  		return doParse(template, options || parseOptions);
6569  	};
6570  	return helper;
6571  }
6572
6573  function doParse(template, parseOptions) {
6574  	if (!_parse) {
6575  		throw new Error("Missing Ractive.parse - cannot parse template. Either preparse or use the version that includes the parser");
6576  	}
6577
6578  	return _parse(template, parseOptions || this.options);
6579  }
6580
6581  function fromId(id, options) {
6582  	var template;
6583
6584  	if (!isClient) {
6585  		if (options && options.noThrow) {
6586  			return;
6587  		}
6588  		throw new Error("Cannot retrieve template #" + id + " as Ractive is not running in a browser.");
6589  	}
6590
6591  	if (isHashedId(id)) {
6592  		id = id.substring(1);
6593  	}
6594
6595  	if (!(template = document.getElementById(id))) {
6596  		if (options && options.noThrow) {
6597  			return;
6598  		}
6599  		throw new Error("Could not find template element with id #" + id);
6600  	}
6601
6602  	if (template.tagName.toUpperCase() !== "SCRIPT") {
6603  		if (options && options.noThrow) {
6604  			return;
6605  		}
6606  		throw new Error("Template element with id #" + id + ", must be a <script> element");
6607  	}
6608
6609  	return "textContent" in template ? template.textContent : template.innerHTML;
6610  }
6611
6612  function isHashedId(id) {
6613  	return id && id[0] === "#";
6614  }
6615
6616  function isParsed(template) {
6617  	return !(typeof template === "string");
6618  }
6619
6620  function getParseOptions(ractive) {
6621  	// Could be Ractive or a Component
6622  	if (ractive.defaults) {
6623  		ractive = ractive.defaults;
6624  	}
6625
6626  	return parseOptions.reduce(function (val, key) {
6627  		val[key] = ractive[key];
6628  		return val;
6629  	}, {});
6630  }
6631
6632  var template_parser = parser;
6633
6634  var templateConfigurator = {
6635  	name: "template",
6636
6637  	extend: function extend(Parent, proto, options) {
6638  		var template;
6639
6640  		// only assign if exists
6641  		if ("template" in options) {
6642  			template = options.template;
6643
6644  			if (typeof template === "function") {
6645  				proto.template = template;
6646  			} else {
6647  				proto.template = parseIfString(template, proto);
6648  			}
6649  		}
6650  	},
6651
6652  	init: function init(Parent, ractive, options) {
6653  		var template, fn;
6654
6655  		// TODO because of prototypal inheritance, we might just be able to use
6656  		// ractive.template, and not bother passing through the Parent object.
6657  		// At present that breaks the test mocks' expectations
6658  		template = "template" in options ? options.template : Parent.prototype.template;
6659
6660  		if (typeof template === "function") {
6661  			fn = template;
6662  			template = getDynamicTemplate(ractive, fn);
6663
6664  			ractive._config.template = {
6665  				fn: fn,
6666  				result: template
6667  			};
6668  		}
6669
6670  		template = parseIfString(template, ractive);
6671
6672  		// TODO the naming of this is confusing - ractive.template refers to [...],
6673  		// but Component.prototype.template refers to {v:1,t:[],p:[]}...
6674  		// it's unnecessary, because the developer never needs to access
6675  		// ractive.template
6676  		ractive.template = template.t;
6677
6678  		if (template.p) {
6679  			extendPartials(ractive.partials, template.p);
6680  		}
6681  	},
6682
6683  	reset: function (ractive) {
6684  		var result = resetValue(ractive),
6685  		    parsed;
6686
6687  		if (result) {
6688  			parsed = parseIfString(result, ractive);
6689
6690  			ractive.template = parsed.t;
6691  			extendPartials(ractive.partials, parsed.p, true);
6692
6693  			return true;
6694  		}
6695  	}
6696  };
6697
6698  function resetValue(ractive) {
6699  	var initial = ractive._config.template,
6700  	    result;
6701
6702  	// If this isn't a dynamic template, there's nothing to do
6703  	if (!initial || !initial.fn) {
6704  		return;
6705  	}
6706
6707  	result = getDynamicTemplate(ractive, initial.fn);
6708
6709  	// TODO deep equality check to prevent unnecessary re-rendering
6710  	// in the case of already-parsed templates
6711  	if (result !== initial.result) {
6712  		initial.result = result;
6713  		result = parseIfString(result, ractive);
6714  		return result;
6715  	}
6716  }
6717
6718  function getDynamicTemplate(ractive, fn) {
6719  	var helper = template_template__createHelper(template_parser.getParseOptions(ractive));
6720  	return fn.call(ractive, helper);
6721  }
6722
6723  function template_template__createHelper(parseOptions) {
6724  	var helper = create(template_parser);
6725  	helper.parse = function (template, options) {
6726  		return template_parser.parse(template, options || parseOptions);
6727  	};
6728  	return helper;
6729  }
6730
6731  function parseIfString(template, ractive) {
6732  	if (typeof template === "string") {
6733  		// ID of an element containing the template?
6734  		if (template[0] === "#") {
6735  			template = template_parser.fromId(template);
6736  		}
6737
6738  		template = _parse(template, template_parser.getParseOptions(ractive));
6739  	}
6740
6741  	// Check that the template even exists
6742  	else if (template == undefined) {
6743  		throw new Error("The template cannot be " + template + ".");
6744  	}
6745
6746  	// Check the parsed template has a version at all
6747  	else if (typeof template.v !== "number") {
6748  		throw new Error("The template parser was passed a non-string template, but the template doesn't have a version.  Make sure you're passing in the template you think you are.");
6749  	}
6750
6751  	// Check we're using the correct version
6752  	else if (template.v !== TEMPLATE_VERSION) {
6753  		throw new Error("Mismatched template version (expected " + TEMPLATE_VERSION + ", got " + template.v + ") Please ensure you are using the latest version of Ractive.js in your build process as well as in your app");
6754  	}
6755
6756  	return template;
6757  }
6758
6759  function extendPartials(existingPartials, newPartials, overwrite) {
6760  	if (!newPartials) return;
6761
6762  	// TODO there's an ambiguity here - we need to overwrite in the `reset()`
6763  	// case, but not initially...
6764
6765  	for (var key in newPartials) {
6766  		if (overwrite || !existingPartials.hasOwnProperty(key)) {
6767  			existingPartials[key] = newPartials[key];
6768  		}
6769  	}
6770  }
6771
6772  var template_template = templateConfigurator;
6773
6774  var config_registries__registryNames, Registry, registries;
6775
6776  config_registries__registryNames = ["adaptors", "components", "computed", "decorators", "easing", "events", "interpolators", "partials", "transitions"];
6777
6778  Registry = function (name, useDefaults) {
6779  	this.name = name;
6780  	this.useDefaults = useDefaults;
6781  };
6782
6783  Registry.prototype = {
6784  	constructor: Registry,
6785
6786  	extend: function (Parent, proto, options) {
6787  		this.configure(this.useDefaults ? Parent.defaults : Parent, this.useDefaults ? proto : proto.constructor, options);
6788  	},
6789
6790  	init: function () {},
6791
6792  	configure: function (Parent, target, options) {
6793  		var name = this.name,
6794  		    option = options[name],
6795  		    registry;
6796
6797  		registry = create(Parent[name]);
6798
6799  		for (var key in option) {
6800  			registry[key] = option[key];
6801  		}
6802
6803  		target[name] = registry;
6804  	},
6805
6806  	reset: function (ractive) {
6807  		var registry = ractive[this.name];
6808  		var changed = false;
6809  		Object.keys(registry).forEach(function (key) {
6810  			var item = registry[key];
6811  			if (item._fn) {
6812  				if (item._fn.isOwner) {
6813  					registry[key] = item._fn;
6814  				} else {
6815  					delete registry[key];
6816  				}
6817  				changed = true;
6818  			}
6819  		});
6820  		return changed;
6821  	}
6822  };
6823
6824  registries = config_registries__registryNames.map(function (name) {
6825  	return new Registry(name, name === "computed");
6826  });
6827
6828  var config_registries = registries;
6829
6830  /*this.configure(
6831  	this.useDefaults ? Parent.defaults : Parent,
6832  	ractive,
6833  	options );*/
6834
6835  var wrapPrototype = wrap;
6836
6837  function wrap(parent, name, method) {
6838  	if (!/_super/.test(method)) {
6839  		return method;
6840  	}
6841
6842  	var wrapper = function wrapSuper() {
6843  		var superMethod = getSuperMethod(wrapper._parent, name),
6844  		    hasSuper = ("_super" in this),
6845  		    oldSuper = this._super,
6846  		    result;
6847
6848  		this._super = superMethod;
6849
6850  		result = method.apply(this, arguments);
6851
6852  		if (hasSuper) {
6853  			this._super = oldSuper;
6854  		} else {
6855  			delete this._super;
6856  		}
6857
6858  		return result;
6859  	};
6860
6861  	wrapper._parent = parent;
6862  	wrapper._method = method;
6863
6864  	return wrapper;
6865  }
6866
6867  function getSuperMethod(parent, name) {
6868  	var value, method;
6869
6870  	if (name in parent) {
6871  		value = parent[name];
6872
6873  		if (typeof value === "function") {
6874  			method = value;
6875  		} else {
6876  			method = function returnValue() {
6877  				return value;
6878  			};
6879  		}
6880  	} else {
6881  		method = noop;
6882  	}
6883
6884  	return method;
6885  }
6886
6887  var config_deprecate = deprecate;
6888  function getMessage(deprecated, correct, isError) {
6889  	return "options." + deprecated + " has been deprecated in favour of options." + correct + "." + (isError ? " You cannot specify both options, please use options." + correct + "." : "");
6890  }
6891
6892  function deprecateOption(options, deprecatedOption, correct) {
6893  	if (deprecatedOption in options) {
6894  		if (!(correct in options)) {
6895  			warnIfDebug(getMessage(deprecatedOption, correct));
6896  			options[correct] = options[deprecatedOption];
6897  		} else {
6898  			throw new Error(getMessage(deprecatedOption, correct, true));
6899  		}
6900  	}
6901  }
6902  function deprecate(options) {
6903  	deprecateOption(options, "beforeInit", "onconstruct");
6904  	deprecateOption(options, "init", "onrender");
6905  	deprecateOption(options, "complete", "oncomplete");
6906  	deprecateOption(options, "eventDefinitions", "events");
6907
6908  	// Using extend with Component instead of options,
6909  	// like Human.extend( Spider ) means adaptors as a registry
6910  	// gets copied to options. So we have to check if actually an array
6911  	if (isArray(options.adaptors)) {
6912  		deprecateOption(options, "adaptors", "adapt");
6913  	}
6914  }
6915
6916  var config, order, defaultKeys, custom, isBlacklisted, isStandardKey;
6917
6918  custom = {
6919  	adapt: custom_adapt,
6920  	css: css_css,
6921  	data: custom_data,
6922  	template: template_template
6923  };
6924
6925  defaultKeys = Object.keys(config_defaults);
6926
6927  isStandardKey = makeObj(defaultKeys.filter(function (key) {
6928  	return !custom[key];
6929  }));
6930
6931  // blacklisted keys that we don't double extend
6932  isBlacklisted = makeObj(defaultKeys.concat(config_registries.map(function (r) {
6933  	return r.name;
6934  })));
6935
6936  order = [].concat(defaultKeys.filter(function (key) {
6937  	return !config_registries[key] && !custom[key];
6938  }), config_registries, custom.data, custom.template, custom.css);
6939
6940  config = {
6941  	extend: function (Parent, proto, options) {
6942  		return configure("extend", Parent, proto, options);
6943  	},
6944
6945  	init: function (Parent, ractive, options) {
6946  		return configure("init", Parent, ractive, options);
6947  	},
6948
6949  	reset: function (ractive) {
6950  		return order.filter(function (c) {
6951  			return c.reset && c.reset(ractive);
6952  		}).map(function (c) {
6953  			return c.name;
6954  		});
6955  	},
6956
6957  	// this defines the order. TODO this isn't used anywhere in the codebase,
6958  	// only in the test suite - should get rid of it
6959  	order: order };
6960
6961  function configure(method, Parent, target, options) {
6962  	config_deprecate(options);
6963
6964  	for (var key in options) {
6965  		if (isStandardKey.hasOwnProperty(key)) {
6966  			var value = options[key];
6967
6968  			// warn the developer if they passed a function and ignore its value
6969
6970  			// NOTE: we allow some functions on "el" because we duck type element lists
6971  			// and some libraries or ef'ed-up virtual browsers (phantomJS) return a
6972  			// function object as the result of querySelector methods
6973  			if (key !== "el" && typeof value === "function") {
6974  				warnIfDebug("" + key + " is a Ractive option that does not expect a function and will be ignored", method === "init" ? target : null);
6975  			} else {
6976  				target[key] = value;
6977  			}
6978  		}
6979  	}
6980
6981  	config_registries.forEach(function (registry) {
6982  		registry[method](Parent, target, options);
6983  	});
6984
6985  	custom_adapt[method](Parent, target, options);
6986  	template_template[method](Parent, target, options);
6987  	css_css[method](Parent, target, options);
6988
6989  	extendOtherMethods(Parent.prototype, target, options);
6990  }
6991
6992  function extendOtherMethods(parent, target, options) {
6993  	for (var key in options) {
6994  		if (!isBlacklisted[key] && options.hasOwnProperty(key)) {
6995  			var member = options[key];
6996
6997  			// if this is a method that overwrites a method, wrap it:
6998  			if (typeof member === "function") {
6999  				member = wrapPrototype(parent, key, member);
7000  			}
7001
7002  			target[key] = member;
7003  		}
7004  	}
7005  }
7006
7007  function makeObj(array) {
7008  	var obj = {};
7009  	array.forEach(function (x) {
7010  		return obj[x] = true;
7011  	});
7012  	return obj;
7013  }
7014
7015  var config_config = config;
7016
7017  var prototype_bubble = Fragment$bubble;
7018
7019  function Fragment$bubble() {
7020  	this.dirtyValue = this.dirtyArgs = true;
7021
7022  	if (this.bound && typeof this.owner.bubble === "function") {
7023  		this.owner.bubble();
7024  	}
7025  }
7026
7027  var Fragment_prototype_detach = Fragment$detach;
7028
7029  function Fragment$detach() {
7030  	var docFrag;
7031
7032  	if (this.items.length === 1) {
7033  		return this.items[0].detach();
7034  	}
7035
7036  	docFrag = document.createDocumentFragment();
7037
7038  	this.items.forEach(function (item) {
7039  		var node = item.detach();
7040
7041  		// TODO The if {...} wasn't previously required - it is now, because we're
7042  		// forcibly detaching everything to reorder sections after an update. That's
7043  		// a non-ideal brute force approach, implemented to get all the tests to pass
7044  		// - as soon as it's replaced with something more elegant, this should
7045  		// revert to `docFrag.appendChild( item.detach() )`
7046  		if (node) {
7047  			docFrag.appendChild(node);
7048  		}
7049  	});
7050
7051  	return docFrag;
7052  }
7053
7054  var Fragment_prototype_find = Fragment$find;
7055
7056  function Fragment$find(selector) {
7057  	var i, len, item, queryResult;
7058
7059  	if (this.items) {
7060  		len = this.items.length;
7061  		for (i = 0; i < len; i += 1) {
7062  			item = this.items[i];
7063
7064  			if (item.find && (queryResult = item.find(selector))) {
7065  				return queryResult;
7066  			}
7067  		}
7068
7069  		return null;
7070  	}
7071  }
7072
7073  var Fragment_prototype_findAll = Fragment$findAll;
7074
7075  function Fragment$findAll(selector, query) {
7076  	var i, len, item;
7077
7078  	if (this.items) {
7079  		len = this.items.length;
7080  		for (i = 0; i < len; i += 1) {
7081  			item = this.items[i];
7082
7083  			if (item.findAll) {
7084  				item.findAll(selector, query);
7085  			}
7086  		}
7087  	}
7088
7089  	return query;
7090  }
7091
7092  var Fragment_prototype_findAllComponents = Fragment$findAllComponents;
7093
7094  function Fragment$findAllComponents(selector, query) {
7095  	var i, len, item;
7096
7097  	if (this.items) {
7098  		len = this.items.length;
7099  		for (i = 0; i < len; i += 1) {
7100  			item = this.items[i];
7101
7102  			if (item.findAllComponents) {
7103  				item.findAllComponents(selector, query);
7104  			}
7105  		}
7106  	}
7107
7108  	return query;
7109  }
7110
7111  var Fragment_prototype_findComponent = Fragment$findComponent;
7112
7113  function Fragment$findComponent(selector) {
7114  	var len, i, item, queryResult;
7115
7116  	if (this.items) {
7117  		len = this.items.length;
7118  		for (i = 0; i < len; i += 1) {
7119  			item = this.items[i];
7120
7121  			if (item.findComponent && (queryResult = item.findComponent(selector))) {
7122  				return queryResult;
7123  			}
7124  		}
7125
7126  		return null;
7127  	}
7128  }
7129
7130  var prototype_findNextNode = Fragment$findNextNode;
7131
7132  function Fragment$findNextNode(item) {
7133  	var index = item.index,
7134  	    node;
7135
7136  	if (this.items[index + 1]) {
7137  		node = this.items[index + 1].firstNode();
7138  	}
7139
7140  	// if this is the root fragment, and there are no more items,
7141  	// it means we're at the end...
7142  	else if (this.owner === this.root) {
7143  		if (!this.owner.component) {
7144  			// TODO but something else could have been appended to
7145  			// this.root.el, no?
7146  			node = null;
7147  		}
7148
7149  		// ...unless this is a component
7150  		else {
7151  			node = this.owner.component.findNextNode();
7152  		}
7153  	} else {
7154  		node = this.owner.findNextNode(this);
7155  	}
7156
7157  	return node;
7158  }
7159
7160  var prototype_firstNode = Fragment$firstNode;
7161
7162  function Fragment$firstNode() {
7163  	if (this.items && this.items[0]) {
7164  		return this.items[0].firstNode();
7165  	}
7166
7167  	return null;
7168  }
7169
7170  var shared_processItems = processItems;
7171
7172  function processItems(items, values, guid, counter) {
7173  	counter = counter || 0;
7174
7175  	return items.map(function (item) {
7176  		var placeholderId, wrapped, value;
7177
7178  		if (item.text) {
7179  			return item.text;
7180  		}
7181
7182  		if (item.fragments) {
7183  			return item.fragments.map(function (fragment) {
7184  				return processItems(fragment.items, values, guid, counter);
7185  			}).join("");
7186  		}
7187
7188  		placeholderId = guid + "-" + counter++;
7189
7190  		if (item.keypath && (wrapped = item.root.viewmodel.wrapped[item.keypath.str])) {
7191  			value = wrapped.value;
7192  		} else {
7193  			value = item.getValue();
7194  		}
7195
7196  		values[placeholderId] = value;
7197
7198  		return "${" + placeholderId + "}";
7199  	}).join("");
7200  }
7201
7202  var getArgsList = Fragment$getArgsList;
7203  function Fragment$getArgsList() {
7204  	var values, source, parsed, result;
7205
7206  	if (this.dirtyArgs) {
7207  		source = shared_processItems(this.items, values = {}, this.root._guid);
7208  		parsed = parseJSON("[" + source + "]", values);
7209
7210  		if (!parsed) {
7211  			result = [this.toString()];
7212  		} else {
7213  			result = parsed.value;
7214  		}
7215
7216  		this.argsList = result;
7217  		this.dirtyArgs = false;
7218  	}
7219
7220  	return this.argsList;
7221  }
7222
7223  var getNode = Fragment$getNode;
7224
7225  function Fragment$getNode() {
7226  	var fragment = this;
7227
7228  	do {
7229  		if (fragment.pElement) {
7230  			return fragment.pElement.node;
7231  		}
7232  	} while (fragment = fragment.parent);
7233
7234  	return this.root.detached || this.root.el;
7235  }
7236
7237  var prototype_getValue = Fragment$getValue;
7238  function Fragment$getValue() {
7239  	var values, source, parsed, result;
7240
7241  	if (this.dirtyValue) {
7242  		source = shared_processItems(this.items, values = {}, this.root._guid);
7243  		parsed = parseJSON(source, values);
7244
7245  		if (!parsed) {
7246  			result = this.toString();
7247  		} else {
7248  			result = parsed.value;
7249  		}
7250
7251  		this.value = result;
7252  		this.dirtyValue = false;
7253  	}
7254
7255  	return this.value;
7256  }
7257
7258  var shared_detach = function () {
7259  	return detachNode(this.node);
7260  };
7261
7262  var Text = function (options) {
7263  	this.type = TEXT;
7264  	this.text = options.template;
7265  };
7266
7267  Text.prototype = {
7268  	detach: shared_detach,
7269
7270  	firstNode: function () {
7271  		return this.node;
7272  	},
7273
7274  	render: function () {
7275  		if (!this.node) {
7276  			this.node = document.createTextNode(this.text);
7277  		}
7278
7279  		return this.node;
7280  	},
7281
7282  	toString: function (escape) {
7283  		return escape ? escapeHtml(this.text) : this.text;
7284  	},
7285
7286  	unrender: function (shouldDestroy) {
7287  		if (shouldDestroy) {
7288  			return this.detach();
7289  		}
7290  	}
7291  };
7292
7293  var items_Text = Text;
7294
7295  var shared_unbind = shared_unbind__unbind;
7296
7297  function shared_unbind__unbind() {
7298  	if (this.registered) {
7299  		// this was registered as a dependant
7300  		this.root.viewmodel.unregister(this.keypath, this);
7301  	}
7302
7303  	if (this.resolver) {
7304  		this.resolver.unbind();
7305  	}
7306  }
7307
7308  var Mustache_getValue = Mustache$getValue;
7309
7310  function Mustache$getValue() {
7311  	return this.value;
7312  }
7313
7314  var ReferenceResolver = function (owner, ref, callback) {
7315  	var keypath;
7316
7317  	this.ref = ref;
7318  	this.resolved = false;
7319
7320  	this.root = owner.root;
7321  	this.parentFragment = owner.parentFragment;
7322  	this.callback = callback;
7323
7324  	keypath = shared_resolveRef(owner.root, ref, owner.parentFragment);
7325  	if (keypath != undefined) {
7326  		this.resolve(keypath);
7327  	} else {
7328  		global_runloop.addUnresolved(this);
7329  	}
7330  };
7331
7332  ReferenceResolver.prototype = {
7333  	resolve: function (keypath) {
7334  		if (this.keypath && !keypath) {
7335  			// it was resolved, and now it's not. Can happen if e.g. `bar` in
7336  			// `{{foo[bar]}}` becomes undefined
7337  			global_runloop.addUnresolved(this);
7338  		}
7339
7340  		this.resolved = true;
7341
7342  		this.keypath = keypath;
7343  		this.callback(keypath);
7344  	},
7345
7346  	forceResolution: function () {
7347  		this.resolve(getKeypath(this.ref));
7348  	},
7349
7350  	rebind: function (oldKeypath, newKeypath) {
7351  		var keypath;
7352
7353  		if (this.keypath != undefined) {
7354  			keypath = this.keypath.replace(oldKeypath, newKeypath);
7355  			// was a new keypath created?
7356  			if (keypath !== undefined) {
7357  				// resolve it
7358  				this.resolve(keypath);
7359  			}
7360  		}
7361  	},
7362
7363  	unbind: function () {
7364  		if (!this.resolved) {
7365  			global_runloop.removeUnresolved(this);
7366  		}
7367  	}
7368  };
7369
7370  var Resolvers_ReferenceResolver = ReferenceResolver;
7371
7372  var SpecialResolver = function (owner, ref, callback) {
7373  	this.parentFragment = owner.parentFragment;
7374  	this.ref = ref;
7375  	this.callback = callback;
7376
7377  	this.rebind();
7378  };
7379
7380  var props = {
7381  	"@keypath": { prefix: "c", prop: ["context"] },
7382  	"@index": { prefix: "i", prop: ["index"] },
7383  	"@key": { prefix: "k", prop: ["key", "index"] }
7384  };
7385
7386  function getProp(target, prop) {
7387  	var value;
7388  	for (var i = 0; i < prop.prop.length; i++) {
7389  		if ((value = target[prop.prop[i]]) !== undefined) {
7390  			return value;
7391  		}
7392  	}
7393  }
7394
7395  SpecialResolver.prototype = {
7396  	rebind: function () {
7397  		var ref = this.ref,
7398  		    fragment = this.parentFragment,
7399  		    prop = props[ref],
7400  		    value;
7401
7402  		if (!prop) {
7403  			throw new Error("Unknown special reference \"" + ref + "\" - valid references are @index, @key and @keypath");
7404  		}
7405
7406  		// have we already found the nearest parent?
7407  		if (this.cached) {
7408  			return this.callback(getKeypath("@" + prop.prefix + getProp(this.cached, prop)));
7409  		}
7410
7411  		// special case for indices, which may cross component boundaries
7412  		if (prop.prop.indexOf("index") !== -1 || prop.prop.indexOf("key") !== -1) {
7413  			while (fragment) {
7414  				if (fragment.owner.currentSubtype === SECTION_EACH && (value = getProp(fragment, prop)) !== undefined) {
7415  					this.cached = fragment;
7416
7417  					fragment.registerIndexRef(this);
7418
7419  					return this.callback(getKeypath("@" + prop.prefix + value));
7420  				}
7421
7422  				// watch for component boundaries
7423  				if (!fragment.parent && fragment.owner && fragment.owner.component && fragment.owner.component.parentFragment && !fragment.owner.component.instance.isolated) {
7424  					fragment = fragment.owner.component.parentFragment;
7425  				} else {
7426  					fragment = fragment.parent;
7427  				}
7428  			}
7429  		} else {
7430  			while (fragment) {
7431  				if ((value = getProp(fragment, prop)) !== undefined) {
7432  					return this.callback(getKeypath("@" + prop.prefix + value.str));
7433  				}
7434
7435  				fragment = fragment.parent;
7436  			}
7437  		}
7438  	},
7439
7440  	unbind: function () {
7441  		if (this.cached) {
7442  			this.cached.unregisterIndexRef(this);
7443  		}
7444  	}
7445  };
7446
7447  var Resolvers_SpecialResolver = SpecialResolver;
7448
7449  var IndexResolver = function (owner, ref, callback) {
7450  	this.parentFragment = owner.parentFragment;
7451  	this.ref = ref;
7452  	this.callback = callback;
7453
7454  	ref.ref.fragment.registerIndexRef(this);
7455
7456  	this.rebind();
7457  };
7458
7459  IndexResolver.prototype = {
7460  	rebind: function () {
7461  		var index,
7462  		    ref = this.ref.ref;
7463
7464  		if (ref.ref.t === "k") {
7465  			index = "k" + ref.fragment.key;
7466  		} else {
7467  			index = "i" + ref.fragment.index;
7468  		}
7469
7470  		if (index !== undefined) {
7471  			this.callback(getKeypath("@" + index));
7472  		}
7473  	},
7474
7475  	unbind: function () {
7476  		this.ref.ref.fragment.unregisterIndexRef(this);
7477  	}
7478  };
7479
7480  var Resolvers_IndexResolver = IndexResolver;
7481
7482  var Resolvers_findIndexRefs = findIndexRefs;
7483
7484  function findIndexRefs(fragment, refName) {
7485  	var result = {},
7486  	    refs,
7487  	    fragRefs,
7488  	    ref,
7489  	    i,
7490  	    owner,
7491  	    hit = false;
7492
7493  	if (!refName) {
7494  		result.refs = refs = {};
7495  	}
7496
7497  	while (fragment) {
7498  		if ((owner = fragment.owner) && (fragRefs = owner.indexRefs)) {
7499
7500  			// we're looking for a particular ref, and it's here
7501  			if (refName && (ref = owner.getIndexRef(refName))) {
7502  				result.ref = {
7503  					fragment: fragment,
7504  					ref: ref
7505  				};
7506  				return result;
7507  			}
7508
7509  			// we're collecting refs up-tree
7510  			else if (!refName) {
7511  				for (i in fragRefs) {
7512  					ref = fragRefs[i];
7513
7514  					// don't overwrite existing refs - they should shadow parents
7515  					if (!refs[ref.n]) {
7516  						hit = true;
7517  						refs[ref.n] = {
7518  							fragment: fragment,
7519  							ref: ref
7520  						};
7521  					}
7522  				}
7523  			}
7524  		}
7525
7526  		// watch for component boundaries
7527  		if (!fragment.parent && fragment.owner && fragment.owner.component && fragment.owner.component.parentFragment && !fragment.owner.component.instance.isolated) {
7528  			result.componentBoundary = true;
7529  			fragment = fragment.owner.component.parentFragment;
7530  		} else {
7531  			fragment = fragment.parent;
7532  		}
7533  	}
7534
7535  	if (!hit) {
7536  		return undefined;
7537  	} else {
7538  		return result;
7539  	}
7540  }
7541
7542  findIndexRefs.resolve = function resolve(indices) {
7543  	var refs = {},
7544  	    k,
7545  	    ref;
7546
7547  	for (k in indices.refs) {
7548  		ref = indices.refs[k];
7549  		refs[ref.ref.n] = ref.ref.t === "k" ? ref.fragment.key : ref.fragment.index;
7550  	}
7551
7552  	return refs;
7553  };
7554
7555  var Resolvers_createReferenceResolver = createReferenceResolver;
7556  function createReferenceResolver(owner, ref, callback) {
7557  	var indexRef;
7558
7559  	if (ref.charAt(0) === "@") {
7560  		return new Resolvers_SpecialResolver(owner, ref, callback);
7561  	}
7562
7563  	if (indexRef = Resolvers_findIndexRefs(owner.parentFragment, ref)) {
7564  		return new Resolvers_IndexResolver(owner, indexRef, callback);
7565  	}
7566
7567  	return new Resolvers_ReferenceResolver(owner, ref, callback);
7568  }
7569
7570  var shared_getFunctionFromString = getFunctionFromString;
7571  var cache = {};
7572  function getFunctionFromString(str, i) {
7573  	var fn, args;
7574
7575  	if (cache[str]) {
7576  		return cache[str];
7577  	}
7578
7579  	args = [];
7580  	while (i--) {
7581  		args[i] = "_" + i;
7582  	}
7583
7584  	fn = new Function(args.join(","), "return(" + str + ")");
7585
7586  	cache[str] = fn;
7587  	return fn;
7588  }
7589
7590  var ExpressionResolver,
7591      Resolvers_ExpressionResolver__bind = Function.prototype.bind;
7592
7593  ExpressionResolver = function (owner, parentFragment, expression, callback) {
7594  	var _this = this;
7595
7596  	var ractive;
7597
7598  	ractive = owner.root;
7599
7600  	this.root = ractive;
7601  	this.parentFragment = parentFragment;
7602  	this.callback = callback;
7603  	this.owner = owner;
7604  	this.str = expression.s;
7605  	this.keypaths = [];
7606
7607  	// Create resolvers for each reference
7608  	this.pending = expression.r.length;
7609  	this.refResolvers = expression.r.map(function (ref, i) {
7610  		return Resolvers_createReferenceResolver(_this, ref, function (keypath) {
7611  			_this.resolve(i, keypath);
7612  		});
7613  	});
7614
7615  	this.ready = true;
7616  	this.bubble();
7617  };
7618
7619  ExpressionResolver.prototype = {
7620  	bubble: function () {
7621  		if (!this.ready) {
7622  			return;
7623  		}
7624
7625  		this.uniqueString = getUniqueString(this.str, this.keypaths);
7626  		this.keypath = createExpressionKeypath(this.uniqueString);
7627
7628  		this.createEvaluator();
7629  		this.callback(this.keypath);
7630  	},
7631
7632  	unbind: function () {
7633  		var resolver;
7634
7635  		while (resolver = this.refResolvers.pop()) {
7636  			resolver.unbind();
7637  		}
7638  	},
7639
7640  	resolve: function (index, keypath) {
7641  		this.keypaths[index] = keypath;
7642  		this.bubble();
7643  	},
7644
7645  	createEvaluator: function () {
7646  		var _this = this;
7647
7648  		var computation, valueGetters, signature, keypath, fn;
7649
7650  		keypath = this.keypath;
7651  		computation = this.root.viewmodel.computations[keypath.str];
7652
7653  		// only if it doesn't exist yet!
7654  		if (!computation) {
7655  			fn = shared_getFunctionFromString(this.str, this.refResolvers.length);
7656
7657  			valueGetters = this.keypaths.map(function (keypath) {
7658  				var value;
7659
7660  				if (keypath === "undefined") {
7661  					return function () {
7662  						return undefined;
7663  					};
7664  				}
7665
7666  				// 'special' keypaths encode a value
7667  				if (keypath.isSpecial) {
7668  					value = keypath.value;
7669  					return function () {
7670  						return value;
7671  					};
7672  				}
7673
7674  				return function () {
7675  					var value = _this.root.viewmodel.get(keypath, { noUnwrap: true, fullRootGet: true });
7676  					if (typeof value === "function") {
7677  						value = wrapFunction(value, _this.root);
7678  					}
7679  					return value;
7680  				};
7681  			});
7682
7683  			signature = {
7684  				deps: this.keypaths.filter(isValidDependency),
7685  				getter: function () {
7686  					var args = valueGetters.map(call);
7687  					return fn.apply(null, args);
7688  				}
7689  			};
7690
7691  			computation = this.root.viewmodel.compute(keypath, signature);
7692  		} else {
7693  			this.root.viewmodel.mark(keypath);
7694  		}
7695  	},
7696
7697  	rebind: function (oldKeypath, newKeypath) {
7698  		// TODO only bubble once, no matter how many references are affected by the rebind
7699  		this.refResolvers.forEach(function (r) {
7700  			return r.rebind(oldKeypath, newKeypath);
7701  		});
7702  	}
7703  };
7704
7705  var Resolvers_ExpressionResolver = ExpressionResolver;
7706
7707  function call(value) {
7708  	return value.call();
7709  }
7710
7711  function getUniqueString(str, keypaths) {
7712  	// get string that is unique to this expression
7713  	return str.replace(/_([0-9]+)/g, function (match, $1) {
7714  		var keypath, value;
7715
7716  		// make sure we're not replacing a non-keypath _[0-9]
7717  		if (+$1 >= keypaths.length) {
7718  			return "_" + $1;
7719  		}
7720
7721  		keypath = keypaths[$1];
7722
7723  		if (keypath === undefined) {
7724  			return "undefined";
7725  		}
7726
7727  		if (keypath.isSpecial) {
7728  			value = keypath.value;
7729  			return typeof value === "number" ? value : "\"" + value + "\"";
7730  		}
7731
7732  		return keypath.str;
7733  	});
7734  }
7735
7736  function createExpressionKeypath(uniqueString) {
7737  	// Sanitize by removing any periods or square brackets. Otherwise
7738  	// we can't split the keypath into keys!
7739  	// Remove asterisks too, since they mess with pattern observers
7740  	return getKeypath("${" + uniqueString.replace(/[\.\[\]]/g, "-").replace(/\*/, "#MUL#") + "}");
7741  }
7742
7743  function isValidDependency(keypath) {
7744  	return keypath !== undefined && keypath[0] !== "@";
7745  }
7746
7747  function wrapFunction(fn, ractive) {
7748  	var wrapped, prop, key;
7749
7750  	if (fn.__ractive_nowrap) {
7751  		return fn;
7752  	}
7753
7754  	prop = "__ractive_" + ractive._guid;
7755  	wrapped = fn[prop];
7756
7757  	if (wrapped) {
7758  		return wrapped;
7759  	} else if (/this/.test(fn.toString())) {
7760  		defineProperty(fn, prop, {
7761  			value: Resolvers_ExpressionResolver__bind.call(fn, ractive),
7762  			configurable: true
7763  		});
7764
7765  		// Add properties/methods to wrapped function
7766  		for (key in fn) {
7767  			if (fn.hasOwnProperty(key)) {
7768  				fn[prop][key] = fn[key];
7769  			}
7770  		}
7771
7772  		ractive._boundFunctions.push({
7773  			fn: fn,
7774  			prop: prop
7775  		});
7776
7777  		return fn[prop];
7778  	}
7779
7780  	defineProperty(fn, "__ractive_nowrap", {
7781  		value: fn
7782  	});
7783
7784  	return fn.__ractive_nowrap;
7785  }
7786
7787  var MemberResolver = function (template, resolver, parentFragment) {
7788  	var _this = this;
7789
7790  	this.resolver = resolver;
7791  	this.root = resolver.root;
7792  	this.parentFragment = parentFragment;
7793  	this.viewmodel = resolver.root.viewmodel;
7794
7795  	if (typeof template === "string") {
7796  		this.value = template;
7797  	}
7798
7799  	// Simple reference?
7800  	else if (template.t === REFERENCE) {
7801  		this.refResolver = Resolvers_createReferenceResolver(this, template.n, function (keypath) {
7802  			_this.resolve(keypath);
7803  		});
7804  	}
7805
7806  	// Otherwise we have an expression in its own right
7807  	else {
7808  		new Resolvers_ExpressionResolver(resolver, parentFragment, template, function (keypath) {
7809  			_this.resolve(keypath);
7810  		});
7811  	}
7812  };
7813
7814  MemberResolver.prototype = {
7815  	resolve: function (keypath) {
7816  		if (this.keypath) {
7817  			this.viewmodel.unregister(this.keypath, this);
7818  		}
7819
7820  		this.keypath = keypath;
7821  		this.value = this.viewmodel.get(keypath);
7822
7823  		this.bind();
7824
7825  		this.resolver.bubble();
7826  	},
7827
7828  	bind: function () {
7829  		this.viewmodel.register(this.keypath, this);
7830  	},
7831
7832  	rebind: function (oldKeypath, newKeypath) {
7833  		if (this.refResolver) {
7834  			this.refResolver.rebind(oldKeypath, newKeypath);
7835  		}
7836  	},
7837
7838  	setValue: function (value) {
7839  		this.value = value;
7840  		this.resolver.bubble();
7841  	},
7842
7843  	unbind: function () {
7844  		if (this.keypath) {
7845  			this.viewmodel.unregister(this.keypath, this);
7846  		}
7847
7848  		if (this.refResolver) {
7849  			this.refResolver.unbind();
7850  		}
7851  	},
7852
7853  	forceResolution: function () {
7854  		if (this.refResolver) {
7855  			this.refResolver.forceResolution();
7856  		}
7857  	}
7858  };
7859
7860  var ReferenceExpressionResolver_MemberResolver = MemberResolver;
7861
7862  var ReferenceExpressionResolver = function (mustache, template, callback) {
7863  	var _this = this;
7864
7865  	var ractive, ref, keypath, parentFragment;
7866
7867  	this.parentFragment = parentFragment = mustache.parentFragment;
7868  	this.root = ractive = mustache.root;
7869  	this.mustache = mustache;
7870
7871  	this.ref = ref = template.r;
7872  	this.callback = callback;
7873
7874  	this.unresolved = [];
7875
7876  	// Find base keypath
7877  	if (keypath = shared_resolveRef(ractive, ref, parentFragment)) {
7878  		this.base = keypath;
7879  	} else {
7880  		this.baseResolver = new Resolvers_ReferenceResolver(this, ref, function (keypath) {
7881  			_this.base = keypath;
7882  			_this.baseResolver = null;
7883  			_this.bubble();
7884  		});
7885  	}
7886
7887  	// Find values for members, or mark them as unresolved
7888  	this.members = template.m.map(function (template) {
7889  		return new ReferenceExpressionResolver_MemberResolver(template, _this, parentFragment);
7890  	});
7891
7892  	this.ready = true;
7893  	this.bubble(); // trigger initial resolution if possible
7894  };
7895
7896  ReferenceExpressionResolver.prototype = {
7897  	getKeypath: function () {
7898  		var values = this.members.map(ReferenceExpressionResolver_ReferenceExpressionResolver__getValue);
7899
7900  		if (!values.every(isDefined) || this.baseResolver) {
7901  			return null;
7902  		}
7903
7904  		return this.base.join(values.join("."));
7905  	},
7906
7907  	bubble: function () {
7908  		if (!this.ready || this.baseResolver) {
7909  			return;
7910  		}
7911
7912  		this.callback(this.getKeypath());
7913  	},
7914
7915  	unbind: function () {
7916  		this.members.forEach(methodCallers__unbind);
7917  	},
7918
7919  	rebind: function (oldKeypath, newKeypath) {
7920  		var changed;
7921
7922  		if (this.base) {
7923  			var newBase = this.base.replace(oldKeypath, newKeypath);
7924  			if (newBase && newBase !== this.base) {
7925  				this.base = newBase;
7926  				changed = true;
7927  			}
7928  		}
7929
7930  		this.members.forEach(function (members) {
7931  			if (members.rebind(oldKeypath, newKeypath)) {
7932  				changed = true;
7933  			}
7934  		});
7935
7936  		if (changed) {
7937  			this.bubble();
7938  		}
7939  	},
7940
7941  	forceResolution: function () {
7942  		if (this.baseResolver) {
7943  			this.base = getKeypath(this.ref);
7944
7945  			this.baseResolver.unbind();
7946  			this.baseResolver = null;
7947  		}
7948
7949  		this.members.forEach(forceResolution);
7950  		this.bubble();
7951  	}
7952  };
7953
7954  function ReferenceExpressionResolver_ReferenceExpressionResolver__getValue(member) {
7955  	return member.value;
7956  }
7957
7958  function isDefined(value) {
7959  	return value != undefined;
7960  }
7961
7962  function forceResolution(member) {
7963  	member.forceResolution();
7964  }
7965
7966  var ReferenceExpressionResolver_ReferenceExpressionResolver = ReferenceExpressionResolver;
7967
7968  var Mustache_initialise = Mustache$init;
7969  function Mustache$init(mustache, options) {
7970
7971  	var ref, parentFragment, template;
7972
7973  	parentFragment = options.parentFragment;
7974  	template = options.template;
7975
7976  	mustache.root = parentFragment.root;
7977  	mustache.parentFragment = parentFragment;
7978  	mustache.pElement = parentFragment.pElement;
7979
7980  	mustache.template = options.template;
7981  	mustache.index = options.index || 0;
7982  	mustache.isStatic = options.template.s;
7983
7984  	mustache.type = options.template.t;
7985
7986  	mustache.registered = false;
7987
7988  	// if this is a simple mustache, with a reference, we just need to resolve
7989  	// the reference to a keypath
7990  	if (ref = template.r) {
7991  		mustache.resolver = Resolvers_createReferenceResolver(mustache, ref, resolve);
7992  	}
7993
7994  	// if it's an expression, we have a bit more work to do
7995  	if (options.template.x) {
7996  		mustache.resolver = new Resolvers_ExpressionResolver(mustache, parentFragment, options.template.x, resolveAndRebindChildren);
7997  	}
7998
7999  	if (options.template.rx) {
8000  		mustache.resolver = new ReferenceExpressionResolver_ReferenceExpressionResolver(mustache, options.template.rx, resolveAndRebindChildren);
8001  	}
8002
8003  	// Special case - inverted sections
8004  	if (mustache.template.n === SECTION_UNLESS && !mustache.hasOwnProperty("value")) {
8005  		mustache.setValue(undefined);
8006  	}
8007
8008  	function resolve(keypath) {
8009  		mustache.resolve(keypath);
8010  	}
8011
8012  	function resolveAndRebindChildren(newKeypath) {
8013  		var oldKeypath = mustache.keypath;
8014
8015  		if (newKeypath != oldKeypath) {
8016  			mustache.resolve(newKeypath);
8017
8018  			if (oldKeypath !== undefined) {
8019  				mustache.fragments && mustache.fragments.forEach(function (f) {
8020  					f.rebind(oldKeypath, newKeypath);
8021  				});
8022  			}
8023  		}
8024  	}
8025  }
8026
8027  var Mustache_resolve = Mustache$resolve;
8028
8029  function Mustache$resolve(keypath) {
8030  	var wasResolved, value, twowayBinding;
8031
8032  	// 'Special' keypaths, e.g. @foo or @7, encode a value
8033  	if (keypath && keypath.isSpecial) {
8034  		this.keypath = keypath;
8035  		this.setValue(keypath.value);
8036  		return;
8037  	}
8038
8039  	// If we resolved previously, we need to unregister
8040  	if (this.registered) {
8041  		// undefined or null
8042  		this.root.viewmodel.unregister(this.keypath, this);
8043  		this.registered = false;
8044
8045  		wasResolved = true;
8046  	}
8047
8048  	this.keypath = keypath;
8049
8050  	// If the new keypath exists, we need to register
8051  	// with the viewmodel
8052  	if (keypath != undefined) {
8053  		// undefined or null
8054  		value = this.root.viewmodel.get(keypath);
8055  		this.root.viewmodel.register(keypath, this);
8056
8057  		this.registered = true;
8058  	}
8059
8060  	// Either way we need to queue up a render (`value`
8061  	// will be `undefined` if there's no keypath)
8062  	this.setValue(value);
8063
8064  	// Two-way bindings need to point to their new target keypath
8065  	if (wasResolved && (twowayBinding = this.twowayBinding)) {
8066  		twowayBinding.rebound();
8067  	}
8068  }
8069
8070  var Mustache_rebind = Mustache$rebind;
8071
8072  function Mustache$rebind(oldKeypath, newKeypath) {
8073  	// Children first
8074  	if (this.fragments) {
8075  		this.fragments.forEach(function (f) {
8076  			return f.rebind(oldKeypath, newKeypath);
8077  		});
8078  	}
8079
8080  	// Expression mustache?
8081  	if (this.resolver) {
8082  		this.resolver.rebind(oldKeypath, newKeypath);
8083  	}
8084  }
8085
8086  var Mustache = {
8087  	getValue: Mustache_getValue,
8088  	init: Mustache_initialise,
8089  	resolve: Mustache_resolve,
8090  	rebind: Mustache_rebind
8091  };
8092
8093  var Interpolator = function (options) {
8094  	this.type = INTERPOLATOR;
8095  	Mustache.init(this, options);
8096  };
8097
8098  Interpolator.prototype = {
8099  	update: function () {
8100  		this.node.data = this.value == undefined ? "" : this.value;
8101  	},
8102  	resolve: Mustache.resolve,
8103  	rebind: Mustache.rebind,
8104  	detach: shared_detach,
8105
8106  	unbind: shared_unbind,
8107
8108  	render: function () {
8109  		if (!this.node) {
8110  			this.node = document.createTextNode(safeToStringValue(this.value));
8111  		}
8112
8113  		return this.node;
8114  	},
8115
8116  	unrender: function (shouldDestroy) {
8117  		if (shouldDestroy) {
8118  			detachNode(this.node);
8119  		}
8120  	},
8121
8122  	getValue: Mustache.getValue,
8123
8124  	// TEMP
8125  	setValue: function (value) {
8126  		var wrapper;
8127
8128  		// TODO is there a better way to approach this?
8129  		if (this.keypath && (wrapper = this.root.viewmodel.wrapped[this.keypath.str])) {
8130  			value = wrapper.get();
8131  		}
8132
8133  		if (!isEqual(value, this.value)) {
8134  			this.value = value;
8135  			this.parentFragment.bubble();
8136
8137  			if (this.node) {
8138  				global_runloop.addView(this);
8139  			}
8140  		}
8141  	},
8142
8143  	firstNode: function () {
8144  		return this.node;
8145  	},
8146
8147  	toString: function (escape) {
8148  		var string = "" + safeToStringValue(this.value);
8149  		return escape ? escapeHtml(string) : string;
8150  	}
8151  };
8152
8153  var items_Interpolator = Interpolator;
8154
8155  var Section_prototype_bubble = Section$bubble;
8156
8157  function Section$bubble() {
8158  	this.parentFragment.bubble();
8159  }
8160
8161  var Section_prototype_detach = Section$detach;
8162
8163  function Section$detach() {
8164  	var docFrag;
8165
8166  	if (this.fragments.length === 1) {
8167  		return this.fragments[0].detach();
8168  	}
8169
8170  	docFrag = document.createDocumentFragment();
8171
8172  	this.fragments.forEach(function (item) {
8173  		docFrag.appendChild(item.detach());
8174  	});
8175
8176  	return docFrag;
8177  }
8178
8179  var find = Section$find;
8180
8181  function Section$find(selector) {
8182  	var i, len, queryResult;
8183
8184  	len = this.fragments.length;
8185  	for (i = 0; i < len; i += 1) {
8186  		if (queryResult = this.fragments[i].find(selector)) {
8187  			return queryResult;
8188  		}
8189  	}
8190
8191  	return null;
8192  }
8193
8194  var findAll = Section$findAll;
8195
8196  function Section$findAll(selector, query) {
8197  	var i, len;
8198
8199  	len = this.fragments.length;
8200  	for (i = 0; i < len; i += 1) {
8201  		this.fragments[i].findAll(selector, query);
8202  	}
8203  }
8204
8205  var findAllComponents = Section$findAllComponents;
8206
8207  function Section$findAllComponents(selector, query) {
8208  	var i, len;
8209
8210  	len = this.fragments.length;
8211  	for (i = 0; i < len; i += 1) {
8212  		this.fragments[i].findAllComponents(selector, query);
8213  	}
8214  }
8215
8216  var findComponent = Section$findComponent;
8217
8218  function Section$findComponent(selector) {
8219  	var i, len, queryResult;
8220
8221  	len = this.fragments.length;
8222  	for (i = 0; i < len; i += 1) {
8223  		if (queryResult = this.fragments[i].findComponent(selector)) {
8224  			return queryResult;
8225  		}
8226  	}
8227
8228  	return null;
8229  }
8230
8231  var findNextNode = Section$findNextNode;
8232
8233  function Section$findNextNode(fragment) {
8234  	if (this.fragments[fragment.index + 1]) {
8235  		return this.fragments[fragment.index + 1].firstNode();
8236  	}
8237
8238  	return this.parentFragment.findNextNode(this);
8239  }
8240
8241  var firstNode = Section$firstNode;
8242
8243  function Section$firstNode() {
8244  	var len, i, node;
8245
8246  	if (len = this.fragments.length) {
8247  		for (i = 0; i < len; i += 1) {
8248  			if (node = this.fragments[i].firstNode()) {
8249  				return node;
8250  			}
8251  		}
8252  	}
8253
8254  	return this.parentFragment.findNextNode(this);
8255  }
8256
8257  var shuffle = Section$shuffle;
8258
8259  function Section$shuffle(newIndices) {
8260  	var _this = this;
8261
8262  	var parentFragment, firstChange, i, newLength, reboundFragments, fragmentOptions, fragment;
8263
8264  	// short circuit any double-updates, and ensure that this isn't applied to
8265  	// non-list sections
8266  	if (this.shuffling || this.unbound || this.currentSubtype !== SECTION_EACH) {
8267  		return;
8268  	}
8269
8270  	this.shuffling = true;
8271  	global_runloop.scheduleTask(function () {
8272  		return _this.shuffling = false;
8273  	});
8274
8275  	parentFragment = this.parentFragment;
8276
8277  	reboundFragments = [];
8278
8279  	// TODO: need to update this
8280  	// first, rebind existing fragments
8281  	newIndices.forEach(function (newIndex, oldIndex) {
8282  		var fragment, by, oldKeypath, newKeypath, deps;
8283
8284  		if (newIndex === oldIndex) {
8285  			reboundFragments[newIndex] = _this.fragments[oldIndex];
8286  			return;
8287  		}
8288
8289  		fragment = _this.fragments[oldIndex];
8290
8291  		if (firstChange === undefined) {
8292  			firstChange = oldIndex;
8293  		}
8294
8295  		// does this fragment need to be torn down?
8296  		if (newIndex === -1) {
8297  			_this.fragmentsToUnrender.push(fragment);
8298  			fragment.unbind();
8299  			return;
8300  		}
8301
8302  		// Otherwise, it needs to be rebound to a new index
8303  		by = newIndex - oldIndex;
8304  		oldKeypath = _this.keypath.join(oldIndex);
8305  		newKeypath = _this.keypath.join(newIndex);
8306
8307  		fragment.index = newIndex;
8308
8309  		// notify any registered index refs directly
8310  		if (deps = fragment.registeredIndexRefs) {
8311  			deps.forEach(shuffle__blindRebind);
8312  		}
8313
8314  		fragment.rebind(oldKeypath, newKeypath);
8315  		reboundFragments[newIndex] = fragment;
8316  	});
8317
8318  	newLength = this.root.viewmodel.get(this.keypath).length;
8319
8320  	// If nothing changed with the existing fragments, then we start adding
8321  	// new fragments at the end...
8322  	if (firstChange === undefined) {
8323  		// ...unless there are no new fragments to add
8324  		if (this.length === newLength) {
8325  			return;
8326  		}
8327
8328  		firstChange = this.length;
8329  	}
8330
8331  	this.length = this.fragments.length = newLength;
8332
8333  	if (this.rendered) {
8334  		global_runloop.addView(this);
8335  	}
8336
8337  	// Prepare new fragment options
8338  	fragmentOptions = {
8339  		template: this.template.f,
8340  		root: this.root,
8341  		owner: this
8342  	};
8343
8344  	// Add as many new fragments as we need to, or add back existing
8345  	// (detached) fragments
8346  	for (i = firstChange; i < newLength; i += 1) {
8347  		fragment = reboundFragments[i];
8348
8349  		if (!fragment) {
8350  			this.fragmentsToCreate.push(i);
8351  		}
8352
8353  		this.fragments[i] = fragment;
8354  	}
8355  }
8356
8357  function shuffle__blindRebind(dep) {
8358  	// the keypath doesn't actually matter here as it won't have changed
8359  	dep.rebind("", "");
8360  }
8361
8362  var prototype_rebind = function (oldKeypath, newKeypath) {
8363  	Mustache.rebind.call(this, oldKeypath, newKeypath);
8364  };
8365
8366  var Section_prototype_render = Section$render;
8367
8368  function Section$render() {
8369  	var _this = this;
8370
8371  	this.docFrag = document.createDocumentFragment();
8372
8373  	this.fragments.forEach(function (f) {
8374  		return _this.docFrag.appendChild(f.render());
8375  	});
8376
8377  	this.renderedFragments = this.fragments.slice();
8378  	this.fragmentsToRender = [];
8379
8380  	this.rendered = true;
8381  	return this.docFrag;
8382  }
8383
8384  var setValue = Section$setValue;
8385
8386  function Section$setValue(value) {
8387  	var _this = this;
8388
8389  	var wrapper, fragmentOptions;
8390
8391  	if (this.updating) {
8392  		// If a child of this section causes a re-evaluation - for example, an
8393  		// expression refers to a function that mutates the array that this
8394  		// section depends on - we'll end up with a double rendering bug (see
8395  		// https://github.com/ractivejs/ractive/issues/748). This prevents it.
8396  		return;
8397  	}
8398
8399  	this.updating = true;
8400
8401  	// with sections, we need to get the fake value if we have a wrapped object
8402  	if (this.keypath && (wrapper = this.root.viewmodel.wrapped[this.keypath.str])) {
8403  		value = wrapper.get();
8404  	}
8405
8406  	// If any fragments are awaiting creation after a splice,
8407  	// this is the place to do it
8408  	if (this.fragmentsToCreate.length) {
8409  		fragmentOptions = {
8410  			template: this.template.f || [],
8411  			root: this.root,
8412  			pElement: this.pElement,
8413  			owner: this
8414  		};
8415
8416  		this.fragmentsToCreate.forEach(function (index) {
8417  			var fragment;
8418
8419  			fragmentOptions.context = _this.keypath.join(index);
8420  			fragmentOptions.index = index;
8421
8422  			fragment = new virtualdom_Fragment(fragmentOptions);
8423  			_this.fragmentsToRender.push(_this.fragments[index] = fragment);
8424  		});
8425
8426  		this.fragmentsToCreate.length = 0;
8427  	} else if (reevaluateSection(this, value)) {
8428  		this.bubble();
8429
8430  		if (this.rendered) {
8431  			global_runloop.addView(this);
8432  		}
8433  	}
8434
8435  	this.value = value;
8436  	this.updating = false;
8437  }
8438
8439  function changeCurrentSubtype(section, value, obj) {
8440  	if (value === SECTION_EACH) {
8441  		// make sure ref type is up to date for key or value indices
8442  		if (section.indexRefs && section.indexRefs[0]) {
8443  			var ref = section.indexRefs[0];
8444
8445  			// when switching flavors, make sure the section gets updated
8446  			if (obj && ref.t === "i" || !obj && ref.t === "k") {
8447  				// if switching from object to list, unbind all of the old fragments
8448  				if (!obj) {
8449  					section.length = 0;
8450  					section.fragmentsToUnrender = section.fragments.slice(0);
8451  					section.fragmentsToUnrender.forEach(function (f) {
8452  						return f.unbind();
8453  					});
8454  				}
8455  			}
8456
8457  			ref.t = obj ? "k" : "i";
8458  		}
8459  	}
8460
8461  	section.currentSubtype = value;
8462  }
8463
8464  function reevaluateSection(section, value) {
8465  	var fragmentOptions = {
8466  		template: section.template.f || [],
8467  		root: section.root,
8468  		pElement: section.parentFragment.pElement,
8469  		owner: section
8470  	};
8471
8472  	section.hasContext = true;
8473
8474  	// If we already know the section type, great
8475  	// TODO can this be optimised? i.e. pick an reevaluateSection function during init
8476  	// and avoid doing this each time?
8477  	if (section.subtype) {
8478  		switch (section.subtype) {
8479  			case SECTION_IF:
8480  				section.hasContext = false;
8481  				return reevaluateConditionalSection(section, value, false, fragmentOptions);
8482
8483  			case SECTION_UNLESS:
8484  				section.hasContext = false;
8485  				return reevaluateConditionalSection(section, value, true, fragmentOptions);
8486
8487  			case SECTION_WITH:
8488  				return reevaluateContextSection(section, fragmentOptions);
8489
8490  			case SECTION_IF_WITH:
8491  				return reevaluateConditionalContextSection(section, value, fragmentOptions);
8492
8493  			case SECTION_EACH:
8494  				if (isObject(value)) {
8495  					changeCurrentSubtype(section, section.subtype, true);
8496  					return reevaluateListObjectSection(section, value, fragmentOptions);
8497  				}
8498
8499  				// Fallthrough - if it's a conditional or an array we need to continue
8500  		}
8501  	}
8502
8503  	// Otherwise we need to work out what sort of section we're dealing with
8504  	section.ordered = !!isArrayLike(value);
8505
8506  	// Ordered list section
8507  	if (section.ordered) {
8508  		changeCurrentSubtype(section, SECTION_EACH, false);
8509  		return reevaluateListSection(section, value, fragmentOptions);
8510  	}
8511
8512  	// Unordered list, or context
8513  	if (isObject(value) || typeof value === "function") {
8514  		// Index reference indicates section should be treated as a list
8515  		if (section.template.i) {
8516  			changeCurrentSubtype(section, SECTION_EACH, true);
8517  			return reevaluateListObjectSection(section, value, fragmentOptions);
8518  		}
8519
8520  		// Otherwise, object provides context for contents
8521  		changeCurrentSubtype(section, SECTION_WITH, false);
8522  		return reevaluateContextSection(section, fragmentOptions);
8523  	}
8524
8525  	// Conditional section
8526  	changeCurrentSubtype(section, SECTION_IF, false);
8527  	section.hasContext = false;
8528  	return reevaluateConditionalSection(section, value, false, fragmentOptions);
8529  }
8530
8531  function reevaluateListSection(section, value, fragmentOptions) {
8532  	var i, length, fragment;
8533
8534  	length = value.length;
8535
8536  	if (length === section.length) {
8537  		// Nothing to do
8538  		return false;
8539  	}
8540
8541  	// if the array is shorter than it was previously, remove items
8542  	if (length < section.length) {
8543  		section.fragmentsToUnrender = section.fragments.splice(length, section.length - length);
8544  		section.fragmentsToUnrender.forEach(methodCallers__unbind);
8545  	}
8546
8547  	// otherwise...
8548  	else {
8549  		if (length > section.length) {
8550  			// add any new ones
8551  			for (i = section.length; i < length; i += 1) {
8552  				// append list item to context stack
8553  				fragmentOptions.context = section.keypath.join(i);
8554  				fragmentOptions.index = i;
8555
8556  				fragment = new virtualdom_Fragment(fragmentOptions);
8557  				section.fragmentsToRender.push(section.fragments[i] = fragment);
8558  			}
8559  		}
8560  	}
8561
8562  	section.length = length;
8563  	return true;
8564  }
8565
8566  function reevaluateListObjectSection(section, value, fragmentOptions) {
8567  	var id, i, hasKey, fragment, changed, deps;
8568
8569  	hasKey = section.hasKey || (section.hasKey = {});
8570
8571  	// remove any fragments that should no longer exist
8572  	i = section.fragments.length;
8573  	while (i--) {
8574  		fragment = section.fragments[i];
8575
8576  		if (!(fragment.key in value)) {
8577  			changed = true;
8578
8579  			fragment.unbind();
8580  			section.fragmentsToUnrender.push(fragment);
8581  			section.fragments.splice(i, 1);
8582
8583  			hasKey[fragment.key] = false;
8584  		}
8585  	}
8586
8587  	// notify any dependents about changed indices
8588  	i = section.fragments.length;
8589  	while (i--) {
8590  		fragment = section.fragments[i];
8591
8592  		if (fragment.index !== i) {
8593  			fragment.index = i;
8594  			if (deps = fragment.registeredIndexRefs) {
8595  				deps.forEach(setValue__blindRebind);
8596  			}
8597  		}
8598  	}
8599
8600  	// add any that haven't been created yet
8601  	i = section.fragments.length;
8602  	for (id in value) {
8603  		if (!hasKey[id]) {
8604  			changed = true;
8605
8606  			fragmentOptions.context = section.keypath.join(id);
8607  			fragmentOptions.key = id;
8608  			fragmentOptions.index = i++;
8609
8610  			fragment = new virtualdom_Fragment(fragmentOptions);
8611
8612  			section.fragmentsToRender.push(fragment);
8613  			section.fragments.push(fragment);
8614  			hasKey[id] = true;
8615  		}
8616  	}
8617
8618  	section.length = section.fragments.length;
8619  	return changed;
8620  }
8621
8622  function reevaluateConditionalContextSection(section, value, fragmentOptions) {
8623  	if (value) {
8624  		return reevaluateContextSection(section, fragmentOptions);
8625  	} else {
8626  		return removeSectionFragments(section);
8627  	}
8628  }
8629
8630  function reevaluateContextSection(section, fragmentOptions) {
8631  	var fragment;
8632
8633  	// ...then if it isn't rendered, render it, adding section.keypath to the context stack
8634  	// (if it is already rendered, then any children dependent on the context stack
8635  	// will update themselves without any prompting)
8636  	if (!section.length) {
8637  		// append this section to the context stack
8638  		fragmentOptions.context = section.keypath;
8639  		fragmentOptions.index = 0;
8640
8641  		fragment = new virtualdom_Fragment(fragmentOptions);
8642
8643  		section.fragmentsToRender.push(section.fragments[0] = fragment);
8644  		section.length = 1;
8645
8646  		return true;
8647  	}
8648  }
8649
8650  function reevaluateConditionalSection(section, value, inverted, fragmentOptions) {
8651  	var doRender, emptyArray, emptyObject, fragment, name;
8652
8653  	emptyArray = isArrayLike(value) && value.length === 0;
8654  	emptyObject = false;
8655  	if (!isArrayLike(value) && isObject(value)) {
8656  		emptyObject = true;
8657  		for (name in value) {
8658  			emptyObject = false;
8659  			break;
8660  		}
8661  	}
8662
8663  	if (inverted) {
8664  		doRender = emptyArray || emptyObject || !value;
8665  	} else {
8666  		doRender = value && !emptyArray && !emptyObject;
8667  	}
8668
8669  	if (doRender) {
8670  		if (!section.length) {
8671  			// no change to context stack
8672  			fragmentOptions.index = 0;
8673
8674  			fragment = new virtualdom_Fragment(fragmentOptions);
8675  			section.fragmentsToRender.push(section.fragments[0] = fragment);
8676  			section.length = 1;
8677
8678  			return true;
8679  		}
8680
8681  		if (section.length > 1) {
8682  			section.fragmentsToUnrender = section.fragments.splice(1);
8683  			section.fragmentsToUnrender.forEach(methodCallers__unbind);
8684
8685  			return true;
8686  		}
8687  	} else {
8688  		return removeSectionFragments(section);
8689  	}
8690  }
8691
8692  function removeSectionFragments(section) {
8693  	if (section.length) {
8694  		section.fragmentsToUnrender = section.fragments.splice(0, section.fragments.length).filter(isRendered);
8695  		section.fragmentsToUnrender.forEach(methodCallers__unbind);
8696  		section.length = section.fragmentsToRender.length = 0;
8697  		return true;
8698  	}
8699  }
8700
8701  function isRendered(fragment) {
8702  	return fragment.rendered;
8703  }
8704
8705  function setValue__blindRebind(dep) {
8706  	// the keypath doesn't actually matter here as it won't have changed
8707  	dep.rebind("", "");
8708  }
8709
8710  var prototype_toString = Section$toString;
8711
8712  function Section$toString(escape) {
8713  	var str, i, len;
8714
8715  	str = "";
8716
8717  	i = 0;
8718  	len = this.length;
8719
8720  	for (i = 0; i < len; i += 1) {
8721  		str += this.fragments[i].toString(escape);
8722  	}
8723
8724  	return str;
8725  }
8726
8727  var prototype_unbind = Section$unbind;
8728  function Section$unbind() {
8729  	var _this = this;
8730
8731  	this.fragments.forEach(methodCallers__unbind);
8732  	this.fragmentsToRender.forEach(function (f) {
8733  		return removeFromArray(_this.fragments, f);
8734  	});
8735  	this.fragmentsToRender = [];
8736  	shared_unbind.call(this);
8737
8738  	this.length = 0;
8739  	this.unbound = true;
8740  }
8741
8742  var prototype_unrender = Section$unrender;
8743
8744  function Section$unrender(shouldDestroy) {
8745  	this.fragments.forEach(shouldDestroy ? unrenderAndDestroy : prototype_unrender__unrender);
8746  	this.renderedFragments = [];
8747  	this.rendered = false;
8748  }
8749
8750  function unrenderAndDestroy(fragment) {
8751  	fragment.unrender(true);
8752  }
8753
8754  function prototype_unrender__unrender(fragment) {
8755  	fragment.unrender(false);
8756  }
8757
8758  var prototype_update = Section$update;
8759
8760  function Section$update() {
8761  	var fragment, renderIndex, renderedFragments, anchor, target, i, len;
8762
8763  	// `this.renderedFragments` is in the order of the previous render.
8764  	// If fragments have shuffled about, this allows us to quickly
8765  	// reinsert them in the correct place
8766  	renderedFragments = this.renderedFragments;
8767
8768  	// Remove fragments that have been marked for destruction
8769  	while (fragment = this.fragmentsToUnrender.pop()) {
8770  		fragment.unrender(true);
8771  		renderedFragments.splice(renderedFragments.indexOf(fragment), 1);
8772  	}
8773
8774  	// Render new fragments (but don't insert them yet)
8775  	while (fragment = this.fragmentsToRender.shift()) {
8776  		fragment.render();
8777  	}
8778
8779  	if (this.rendered) {
8780  		target = this.parentFragment.getNode();
8781  	}
8782
8783  	len = this.fragments.length;
8784  	for (i = 0; i < len; i += 1) {
8785  		fragment = this.fragments[i];
8786  		renderIndex = renderedFragments.indexOf(fragment, i); // search from current index - it's guaranteed to be the same or higher
8787
8788  		if (renderIndex === i) {
8789  			// already in the right place. insert accumulated nodes (if any) and carry on
8790  			if (this.docFrag.childNodes.length) {
8791  				anchor = fragment.firstNode();
8792  				target.insertBefore(this.docFrag, anchor);
8793  			}
8794
8795  			continue;
8796  		}
8797
8798  		this.docFrag.appendChild(fragment.detach());
8799
8800  		// update renderedFragments
8801  		if (renderIndex !== -1) {
8802  			renderedFragments.splice(renderIndex, 1);
8803  		}
8804  		renderedFragments.splice(i, 0, fragment);
8805  	}
8806
8807  	if (this.rendered && this.docFrag.childNodes.length) {
8808  		anchor = this.parentFragment.findNextNode(this);
8809  		target.insertBefore(this.docFrag, anchor);
8810  	}
8811
8812  	// Save the rendering order for next time
8813  	this.renderedFragments = this.fragments.slice();
8814  }
8815
8816  var Section = function (options) {
8817  	this.type = SECTION;
8818  	this.subtype = this.currentSubtype = options.template.n;
8819  	this.inverted = this.subtype === SECTION_UNLESS;
8820
8821  	this.pElement = options.pElement;
8822
8823  	this.fragments = [];
8824  	this.fragmentsToCreate = [];
8825  	this.fragmentsToRender = [];
8826  	this.fragmentsToUnrender = [];
8827
8828  	if (options.template.i) {
8829  		this.indexRefs = options.template.i.split(",").map(function (k, i) {
8830  			return { n: k, t: i === 0 ? "k" : "i" };
8831  		});
8832  	}
8833
8834  	this.renderedFragments = [];
8835
8836  	this.length = 0; // number of times this section is rendered
8837
8838  	Mustache.init(this, options);
8839  };
8840
8841  Section.prototype = {
8842  	bubble: Section_prototype_bubble,
8843  	detach: Section_prototype_detach,
8844  	find: find,
8845  	findAll: findAll,
8846  	findAllComponents: findAllComponents,
8847  	findComponent: findComponent,
8848  	findNextNode: findNextNode,
8849  	firstNode: firstNode,
8850  	getIndexRef: function (name) {
8851  		if (this.indexRefs) {
8852  			var i = this.indexRefs.length;
8853  			while (i--) {
8854  				var ref = this.indexRefs[i];
8855  				if (ref.n === name) {
8856  					return ref;
8857  				}
8858  			}
8859  		}
8860  	},
8861  	getValue: Mustache.getValue,
8862  	shuffle: shuffle,
8863  	rebind: prototype_rebind,
8864  	render: Section_prototype_render,
8865  	resolve: Mustache.resolve,
8866  	setValue: setValue,
8867  	toString: prototype_toString,
8868  	unbind: prototype_unbind,
8869  	unrender: prototype_unrender,
8870  	update: prototype_update
8871  };
8872
8873  var _Section = Section;
8874
8875  var Triple_prototype_detach = Triple$detach;
8876
8877  function Triple$detach() {
8878  	var len, i;
8879
8880  	if (this.docFrag) {
8881  		len = this.nodes.length;
8882  		for (i = 0; i < len; i += 1) {
8883  			this.docFrag.appendChild(this.nodes[i]);
8884  		}
8885
8886  		return this.docFrag;
8887  	}
8888  }
8889
8890  var Triple_prototype_find = Triple$find;
8891  function Triple$find(selector) {
8892  	var i, len, node, queryResult;
8893
8894  	len = this.nodes.length;
8895  	for (i = 0; i < len; i += 1) {
8896  		node = this.nodes[i];
8897
8898  		if (node.nodeType !== 1) {
8899  			continue;
8900  		}
8901
8902  		if (matches(node, selector)) {
8903  			return node;
8904  		}
8905
8906  		if (queryResult = node.querySelector(selector)) {
8907  			return queryResult;
8908  		}
8909  	}
8910
8911  	return null;
8912  }
8913
8914  var Triple_prototype_findAll = Triple$findAll;
8915  function Triple$findAll(selector, queryResult) {
8916  	var i, len, node, queryAllResult, numNodes, j;
8917
8918  	len = this.nodes.length;
8919  	for (i = 0; i < len; i += 1) {
8920  		node = this.nodes[i];
8921
8922  		if (node.nodeType !== 1) {
8923  			continue;
8924  		}
8925
8926  		if (matches(node, selector)) {
8927  			queryResult.push(node);
8928  		}
8929
8930  		if (queryAllResult = node.querySelectorAll(selector)) {
8931  			numNodes = queryAllResult.length;
8932  			for (j = 0; j < numNodes; j += 1) {
8933  				queryResult.push(queryAllResult[j]);
8934  			}
8935  		}
8936  	}
8937  }
8938
8939  var Triple_prototype_firstNode = Triple$firstNode;
8940
8941  function Triple$firstNode() {
8942  	if (this.rendered && this.nodes[0]) {
8943  		return this.nodes[0];
8944  	}
8945
8946  	return this.parentFragment.findNextNode(this);
8947  }
8948
8949  var elementCache = {},
8950      ieBug,
8951      ieBlacklist;
8952
8953  try {
8954  	createElement("table").innerHTML = "foo";
8955  } catch (err) {
8956  	ieBug = true;
8957
8958  	ieBlacklist = {
8959  		TABLE: ["<table class=\"x\">", "</table>"],
8960  		THEAD: ["<table><thead class=\"x\">", "</thead></table>"],
8961  		TBODY: ["<table><tbody class=\"x\">", "</tbody></table>"],
8962  		TR: ["<table><tr class=\"x\">", "</tr></table>"],
8963  		SELECT: ["<select class=\"x\">", "</select>"]
8964  	};
8965  }
8966
8967  var insertHtml = function (html, node, docFrag) {
8968  	var container,
8969  	    nodes = [],
8970  	    wrapper,
8971  	    selectedOption,
8972  	    child,
8973  	    i;
8974
8975  	// render 0 and false
8976  	if (html != null && html !== "") {
8977  		if (ieBug && (wrapper = ieBlacklist[node.tagName])) {
8978  			container = element("DIV");
8979  			container.innerHTML = wrapper[0] + html + wrapper[1];
8980  			container = container.querySelector(".x");
8981
8982  			if (container.tagName === "SELECT") {
8983  				selectedOption = container.options[container.selectedIndex];
8984  			}
8985  		} else if (node.namespaceURI === namespaces.svg) {
8986  			container = element("DIV");
8987  			container.innerHTML = "<svg class=\"x\">" + html + "</svg>";
8988  			container = container.querySelector(".x");
8989  		} else {
8990  			container = element(node.tagName);
8991  			container.innerHTML = html;
8992
8993  			if (container.tagName === "SELECT") {
8994  				selectedOption = container.options[container.selectedIndex];
8995  			}
8996  		}
8997
8998  		while (child = container.firstChild) {
8999  			nodes.push(child);
9000  			docFrag.appendChild(child);
9001  		}
9002
9003  		// This is really annoying. Extracting <option> nodes from the
9004  		// temporary container <select> causes the remaining ones to
9005  		// become selected. So now we have to deselect them. IE8, you
9006  		// amaze me. You really do
9007  		// ...and now Chrome too
9008  		if (node.tagName === "SELECT") {
9009  			i = nodes.length;
9010  			while (i--) {
9011  				if (nodes[i] !== selectedOption) {
9012  					nodes[i].selected = false;
9013  				}
9014  			}
9015  		}
9016  	}
9017
9018  	return nodes;
9019  };
9020
9021  function element(tagName) {
9022  	return elementCache[tagName] || (elementCache[tagName] = createElement(tagName));
9023  }
9024
9025  var helpers_updateSelect = updateSelect;
9026
9027  function updateSelect(parentElement) {
9028  	var selectedOptions, option, value;
9029
9030  	if (!parentElement || parentElement.name !== "select" || !parentElement.binding) {
9031  		return;
9032  	}
9033
9034  	selectedOptions = toArray(parentElement.node.options).filter(isSelected);
9035
9036  	// If one of them had a `selected` attribute, we need to sync
9037  	// the model to the view
9038  	if (parentElement.getAttribute("multiple")) {
9039  		value = selectedOptions.map(function (o) {
9040  			return o.value;
9041  		});
9042  	} else if (option = selectedOptions[0]) {
9043  		value = option.value;
9044  	}
9045
9046  	if (value !== undefined) {
9047  		parentElement.binding.setValue(value);
9048  	}
9049
9050  	parentElement.bubble();
9051  }
9052
9053  function isSelected(option) {
9054  	return option.selected;
9055  }
9056
9057  var Triple_prototype_render = Triple$render;
9058  function Triple$render() {
9059  	if (this.rendered) {
9060  		throw new Error("Attempted to render an item that was already rendered");
9061  	}
9062
9063  	this.docFrag = document.createDocumentFragment();
9064  	this.nodes = insertHtml(this.value, this.parentFragment.getNode(), this.docFrag);
9065
9066  	// Special case - we're inserting the contents of a <select>
9067  	helpers_updateSelect(this.pElement);
9068
9069  	this.rendered = true;
9070  	return this.docFrag;
9071  }
9072
9073  var prototype_setValue = Triple$setValue;
9074  function Triple$setValue(value) {
9075  	var wrapper;
9076
9077  	// TODO is there a better way to approach this?
9078  	if (wrapper = this.root.viewmodel.wrapped[this.keypath.str]) {
9079  		value = wrapper.get();
9080  	}
9081
9082  	if (value !== this.value) {
9083  		this.value = value;
9084  		this.parentFragment.bubble();
9085
9086  		if (this.rendered) {
9087  			global_runloop.addView(this);
9088  		}
9089  	}
9090  }
9091
9092  var Triple_prototype_toString = Triple$toString;
9093  function Triple$toString() {
9094  	return this.value != undefined ? decodeCharacterReferences("" + this.value) : "";
9095  }
9096
9097  var Triple_prototype_unrender = Triple$unrender;
9098  function Triple$unrender(shouldDestroy) {
9099  	if (this.rendered && shouldDestroy) {
9100  		this.nodes.forEach(detachNode);
9101  		this.rendered = false;
9102  	}
9103
9104  	// TODO update live queries
9105  }
9106
9107  var Triple_prototype_update = Triple$update;
9108  function Triple$update() {
9109  	var node, parentNode;
9110
9111  	if (!this.rendered) {
9112  		return;
9113  	}
9114
9115  	// Remove existing nodes
9116  	while (this.nodes && this.nodes.length) {
9117  		node = this.nodes.pop();
9118  		node.parentNode.removeChild(node);
9119  	}
9120
9121  	// Insert new nodes
9122  	parentNode = this.parentFragment.getNode();
9123
9124  	this.nodes = insertHtml(this.value, parentNode, this.docFrag);
9125  	parentNode.insertBefore(this.docFrag, this.parentFragment.findNextNode(this));
9126
9127  	// Special case - we're inserting the contents of a <select>
9128  	helpers_updateSelect(this.pElement);
9129  }
9130
9131  var Triple = function (options) {
9132  	this.type = TRIPLE;
9133  	Mustache.init(this, options);
9134  };
9135
9136  Triple.prototype = {
9137  	detach: Triple_prototype_detach,
9138  	find: Triple_prototype_find,
9139  	findAll: Triple_prototype_findAll,
9140  	firstNode: Triple_prototype_firstNode,
9141  	getValue: Mustache.getValue,
9142  	rebind: Mustache.rebind,
9143  	render: Triple_prototype_render,
9144  	resolve: Mustache.resolve,
9145  	setValue: prototype_setValue,
9146  	toString: Triple_prototype_toString,
9147  	unbind: shared_unbind,
9148  	unrender: Triple_prototype_unrender,
9149  	update: Triple_prototype_update
9150  };
9151
9152  var _Triple = Triple;
9153
9154  var Element_prototype_bubble = function () {
9155  	this.parentFragment.bubble();
9156  };
9157
9158  var Element_prototype_detach = Element$detach;
9159
9160  function Element$detach() {
9161  	var node = this.node,
9162  	    parentNode;
9163
9164  	if (node) {
9165  		// need to check for parent node - DOM may have been altered
9166  		// by something other than Ractive! e.g. jQuery UI...
9167  		if (parentNode = node.parentNode) {
9168  			parentNode.removeChild(node);
9169  		}
9170
9171  		return node;
9172  	}
9173  }
9174
9175  var Element_prototype_find = function (selector) {
9176  	if (!this.node) {
9177  		// this element hasn't been rendered yet
9178  		return null;
9179  	}
9180
9181  	if (matches(this.node, selector)) {
9182  		return this.node;
9183  	}
9184
9185  	if (this.fragment && this.fragment.find) {
9186  		return this.fragment.find(selector);
9187  	}
9188  };
9189
9190  var Element_prototype_findAll = function (selector, query) {
9191  	// Add this node to the query, if applicable, and register the
9192  	// query on this element
9193  	if (query._test(this, true) && query.live) {
9194  		(this.liveQueries || (this.liveQueries = [])).push(query);
9195  	}
9196
9197  	if (this.fragment) {
9198  		this.fragment.findAll(selector, query);
9199  	}
9200  };
9201
9202  var Element_prototype_findAllComponents = function (selector, query) {
9203  	if (this.fragment) {
9204  		this.fragment.findAllComponents(selector, query);
9205  	}
9206  };
9207
9208  var Element_prototype_findComponent = function (selector) {
9209  	if (this.fragment) {
9210  		return this.fragment.findComponent(selector);
9211  	}
9212  };
9213
9214  var Element_prototype_findNextNode = Element$findNextNode;
9215
9216  function Element$findNextNode() {
9217  	return null;
9218  }
9219
9220  var Element_prototype_firstNode = Element$firstNode;
9221
9222  function Element$firstNode() {
9223  	return this.node;
9224  }
9225
9226  var getAttribute = Element$getAttribute;
9227
9228  function Element$getAttribute(name) {
9229  	if (!this.attributes || !this.attributes[name]) {
9230  		return;
9231  	}
9232
9233  	return this.attributes[name].value;
9234  }
9235
9236  var truthy = /^true|on|yes|1$/i;
9237  var processBindingAttributes__isNumeric = /^[0-9]+$/;
9238
9239  var processBindingAttributes = function (element, template) {
9240  	var val, attrs, attributes;
9241
9242  	attributes = template.a || {};
9243  	attrs = {};
9244
9245  	// attributes that are present but don't have a value (=)
9246  	// will be set to the number 0, which we condider to be true
9247  	// the string '0', however is false
9248
9249  	val = attributes.twoway;
9250  	if (val !== undefined) {
9251  		attrs.twoway = val === 0 || truthy.test(val);
9252  	}
9253
9254  	val = attributes.lazy;
9255  	if (val !== undefined) {
9256  		// check for timeout value
9257  		if (val !== 0 && processBindingAttributes__isNumeric.test(val)) {
9258  			attrs.lazy = parseInt(val);
9259  		} else {
9260  			attrs.lazy = val === 0 || truthy.test(val);
9261  		}
9262  	}
9263
9264  	return attrs;
9265  };
9266
9267  var Attribute_prototype_bubble = Attribute$bubble;
9268  function Attribute$bubble() {
9269  	var value = this.useProperty || !this.rendered ? this.fragment.getValue() : this.fragment.toString();
9270
9271  	// TODO this can register the attribute multiple times (see render test
9272  	// 'Attribute with nested mustaches')
9273  	if (!isEqual(value, this.value)) {
9274
9275  		// Need to clear old id from ractive.nodes
9276  		if (this.name === "id" && this.value) {
9277  			delete this.root.nodes[this.value];
9278  		}
9279
9280  		this.value = value;
9281
9282  		if (this.name === "value" && this.node) {
9283  			// We need to store the value on the DOM like this so we
9284  			// can retrieve it later without it being coerced to a string
9285  			this.node._ractive.value = value;
9286  		}
9287
9288  		if (this.rendered) {
9289  			global_runloop.addView(this);
9290  		}
9291  	}
9292  }
9293
9294  var svgCamelCaseElements, svgCamelCaseAttributes, createMap, map;
9295  svgCamelCaseElements = "altGlyph altGlyphDef altGlyphItem animateColor animateMotion animateTransform clipPath feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feDistantLight feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting feSpotLight feTile feTurbulence foreignObject glyphRef linearGradient radialGradient textPath vkern".split(" ");
9296  svgCamelCaseAttributes = "attributeName attributeType baseFrequency baseProfile calcMode clipPathUnits contentScriptType contentStyleType diffuseConstant edgeMode externalResourcesRequired filterRes filterUnits glyphRef gradientTransform gradientUnits kernelMatrix kernelUnitLength keyPoints keySplines keyTimes lengthAdjust limitingConeAngle markerHeight markerUnits markerWidth maskContentUnits maskUnits numOctaves pathLength patternContentUnits patternTransform patternUnits pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits refX refY repeatCount repeatDur requiredExtensions requiredFeatures specularConstant specularExponent spreadMethod startOffset stdDeviation stitchTiles surfaceScale systemLanguage tableValues targetX targetY textLength viewBox viewTarget xChannelSelector yChannelSelector zoomAndPan".split(" ");
9297
9298  createMap = function (items) {
9299  	var map = {},
9300  	    i = items.length;
9301  	while (i--) {
9302  		map[items[i].toLowerCase()] = items[i];
9303  	}
9304  	return map;
9305  };
9306
9307  map = createMap(svgCamelCaseElements.concat(svgCamelCaseAttributes));
9308
9309  var enforceCase = function (elementName) {
9310  	var lowerCaseElementName = elementName.toLowerCase();
9311  	return map[lowerCaseElementName] || lowerCaseElementName;
9312  };
9313
9314  var determineNameAndNamespace = function (attribute, name) {
9315  	var colonIndex, namespacePrefix;
9316
9317  	// are we dealing with a namespaced attribute, e.g. xlink:href?
9318  	colonIndex = name.indexOf(":");
9319  	if (colonIndex !== -1) {
9320
9321  		// looks like we are, yes...
9322  		namespacePrefix = name.substr(0, colonIndex);
9323
9324  		// ...unless it's a namespace *declaration*, which we ignore (on the assumption
9325  		// that only valid namespaces will be used)
9326  		if (namespacePrefix !== "xmlns") {
9327  			name = name.substring(colonIndex + 1);
9328
9329  			attribute.name = enforceCase(name);
9330  			attribute.namespace = namespaces[namespacePrefix.toLowerCase()];
9331  			attribute.namespacePrefix = namespacePrefix;
9332
9333  			if (!attribute.namespace) {
9334  				throw "Unknown namespace (\"" + namespacePrefix + "\")";
9335  			}
9336
9337  			return;
9338  		}
9339  	}
9340
9341  	// SVG attribute names are case sensitive
9342  	attribute.name = attribute.element.namespace !== namespaces.html ? enforceCase(name) : name;
9343  };
9344
9345  var helpers_getInterpolator = getInterpolator;
9346  function getInterpolator(attribute) {
9347  	var items = attribute.fragment.items;
9348
9349  	if (items.length !== 1) {
9350  		return;
9351  	}
9352
9353  	if (items[0].type === INTERPOLATOR) {
9354  		return items[0];
9355  	}
9356  }
9357
9358  var prototype_init = Attribute$init;
9359  function Attribute$init(options) {
9360  	this.type = ATTRIBUTE;
9361  	this.element = options.element;
9362  	this.root = options.root;
9363
9364  	determineNameAndNamespace(this, options.name);
9365  	this.isBoolean = booleanAttributes.test(this.name);
9366
9367  	// if it's an empty attribute, or just a straight key-value pair, with no
9368  	// mustache shenanigans, set the attribute accordingly and go home
9369  	if (!options.value || typeof options.value === "string") {
9370  		this.value = this.isBoolean ? true : options.value || "";
9371  		return;
9372  	}
9373
9374  	// otherwise we need to do some work
9375
9376  	// share parentFragment with parent element
9377  	this.parentFragment = this.element.parentFragment;
9378
9379  	this.fragment = new virtualdom_Fragment({
9380  		template: options.value,
9381  		root: this.root,
9382  		owner: this
9383  	});
9384
9385  	// TODO can we use this.fragment.toString() in some cases? It's quicker
9386  	this.value = this.fragment.getValue();
9387
9388  	// Store a reference to this attribute's interpolator, if its fragment
9389  	// takes the form `{{foo}}`. This is necessary for two-way binding and
9390  	// for correctly rendering HTML later
9391  	this.interpolator = helpers_getInterpolator(this);
9392  	this.isBindable = !!this.interpolator && !this.interpolator.isStatic;
9393
9394  	// mark as ready
9395  	this.ready = true;
9396  }
9397
9398  var Attribute_prototype_rebind = Attribute$rebind;
9399
9400  function Attribute$rebind(oldKeypath, newKeypath) {
9401  	if (this.fragment) {
9402  		this.fragment.rebind(oldKeypath, newKeypath);
9403  	}
9404  }
9405
9406  var Attribute_prototype_render = Attribute$render;
9407  var propertyNames = {
9408  	"accept-charset": "acceptCharset",
9409  	accesskey: "accessKey",
9410  	bgcolor: "bgColor",
9411  	"class": "className",
9412  	codebase: "codeBase",
9413  	colspan: "colSpan",
9414  	contenteditable: "contentEditable",
9415  	datetime: "dateTime",
9416  	dirname: "dirName",
9417  	"for": "htmlFor",
9418  	"http-equiv": "httpEquiv",
9419  	ismap: "isMap",
9420  	maxlength: "maxLength",
9421  	novalidate: "noValidate",
9422  	pubdate: "pubDate",
9423  	readonly: "readOnly",
9424  	rowspan: "rowSpan",
9425  	tabindex: "tabIndex",
9426  	usemap: "useMap"
9427  };
9428  function Attribute$render(node) {
9429  	var propertyName;
9430
9431  	this.node = node;
9432
9433  	// should we use direct property access, or setAttribute?
9434  	if (!node.namespaceURI || node.namespaceURI === namespaces.html) {
9435  		propertyName = propertyNames[this.name] || this.name;
9436
9437  		if (node[propertyName] !== undefined) {
9438  			this.propertyName = propertyName;
9439  		}
9440
9441  		// is attribute a boolean attribute or 'value'? If so we're better off doing e.g.
9442  		// node.selected = true rather than node.setAttribute( 'selected', '' )
9443  		if (this.isBoolean || this.isTwoway) {
9444  			this.useProperty = true;
9445  		}
9446
9447  		if (propertyName === "value") {
9448  			node._ractive.value = this.value;
9449  		}
9450  	}
9451
9452  	this.rendered = true;
9453  	this.update();
9454  }
9455
9456  var Attribute_prototype_toString = Attribute$toString;
9457
9458  function Attribute$toString() {
9459  	var _ref = this;
9460
9461  	var name = _ref.name;
9462  	var namespacePrefix = _ref.namespacePrefix;
9463  	var value = _ref.value;
9464  	var interpolator = _ref.interpolator;
9465  	var fragment = _ref.fragment;
9466
9467  	// Special case - select and textarea values (should not be stringified)
9468  	if (name === "value" && (this.element.name === "select" || this.element.name === "textarea")) {
9469  		return;
9470  	}
9471
9472  	// Special case - content editable
9473  	if (name === "value" && this.element.getAttribute("contenteditable") !== undefined) {
9474  		return;
9475  	}
9476
9477  	// Special case - radio names
9478  	if (name === "name" && this.element.name === "input" && interpolator) {
9479  		return "name={{" + (interpolator.keypath.str || interpolator.ref) + "}}";
9480  	}
9481
9482  	// Boolean attributes
9483  	if (this.isBoolean) {
9484  		return value ? name : "";
9485  	}
9486
9487  	if (fragment) {
9488  		// special case - this catches undefined/null values (#1211)
9489  		if (fragment.items.length === 1 && fragment.items[0].value == null) {
9490  			return "";
9491  		}
9492
9493  		value = fragment.toString();
9494  	}
9495
9496  	if (namespacePrefix) {
9497  		name = namespacePrefix + ":" + name;
9498  	}
9499
9500  	return value ? name + "=\"" + Attribute_prototype_toString__escape(value) + "\"" : name;
9501  }
9502
9503  function Attribute_prototype_toString__escape(value) {
9504  	return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
9505  }
9506
9507  var Attribute_prototype_unbind = Attribute$unbind;
9508
9509  function Attribute$unbind() {
9510  	// ignore non-dynamic attributes
9511  	if (this.fragment) {
9512  		this.fragment.unbind();
9513  	}
9514
9515  	if (this.name === "id") {
9516  		delete this.root.nodes[this.value];
9517  	}
9518  }
9519
9520  var updateSelectValue = Attribute$updateSelect;
9521
9522  function Attribute$updateSelect() {
9523  	var value = this.value,
9524  	    options,
9525  	    option,
9526  	    optionValue,
9527  	    i;
9528
9529  	if (!this.locked) {
9530  		this.node._ractive.value = value;
9531
9532  		options = this.node.options;
9533  		i = options.length;
9534
9535  		while (i--) {
9536  			option = options[i];
9537  			optionValue = option._ractive ? option._ractive.value : option.value; // options inserted via a triple don't have _ractive
9538
9539  			if (optionValue == value) {
9540  				// double equals as we may be comparing numbers with strings
9541  				option.selected = true;
9542  				break;
9543  			}
9544  		}
9545  	}
9546
9547  	// if we're still here, it means the new value didn't match any of the options...
9548  	// TODO figure out what to do in this situation
9549  }
9550
9551  var updateMultipleSelectValue = Attribute$updateMultipleSelect;
9552  function Attribute$updateMultipleSelect() {
9553  	var value = this.value,
9554  	    options,
9555  	    i,
9556  	    option,
9557  	    optionValue;
9558
9559  	if (!isArray(value)) {
9560  		value = [value];
9561  	}
9562
9563  	options = this.node.options;
9564  	i = options.length;
9565
9566  	while (i--) {
9567  		option = options[i];
9568  		optionValue = option._ractive ? option._ractive.value : option.value; // options inserted via a triple don't have _ractive
9569  		option.selected = arrayContains(value, optionValue);
9570  	}
9571  }
9572
9573  var updateRadioName = Attribute$updateRadioName;
9574
9575  function Attribute$updateRadioName() {
9576  	var _ref = this;
9577
9578  	var node = _ref.node;
9579  	var value = _ref.value;
9580
9581  	node.checked = value == node._ractive.value;
9582  }
9583
9584  var updateRadioValue = Attribute$updateRadioValue;
9585  function Attribute$updateRadioValue() {
9586  	var wasChecked,
9587  	    node = this.node,
9588  	    binding,
9589  	    bindings,
9590  	    i;
9591
9592  	wasChecked = node.checked;
9593
9594  	node.value = this.element.getAttribute("value");
9595  	node.checked = this.element.getAttribute("value") === this.element.getAttribute("name");
9596
9597  	// This is a special case - if the input was checked, and the value
9598  	// changed so that it's no longer checked, the twoway binding is
9599  	// most likely out of date. To fix it we have to jump through some
9600  	// hoops... this is a little kludgy but it works
9601  	if (wasChecked && !node.checked && this.element.binding) {
9602  		bindings = this.element.binding.siblings;
9603
9604  		if (i = bindings.length) {
9605  			while (i--) {
9606  				binding = bindings[i];
9607
9608  				if (!binding.element.node) {
9609  					// this is the initial render, siblings are still rendering!
9610  					// we'll come back later...
9611  					return;
9612  				}
9613
9614  				if (binding.element.node.checked) {
9615  					global_runloop.addRactive(binding.root);
9616  					return binding.handleChange();
9617  				}
9618  			}
9619
9620  			this.root.viewmodel.set(binding.keypath, undefined);
9621  		}
9622  	}
9623  }
9624
9625  var updateCheckboxName = Attribute$updateCheckboxName;
9626  function Attribute$updateCheckboxName() {
9627  	var _ref = this;
9628
9629  	var element = _ref.element;
9630  	var node = _ref.node;
9631  	var value = _ref.value;var binding = element.binding;var valueAttribute;var i;
9632
9633  	valueAttribute = element.getAttribute("value");
9634
9635  	if (!isArray(value)) {
9636  		binding.isChecked = node.checked = value == valueAttribute;
9637  	} else {
9638  		i = value.length;
9639  		while (i--) {
9640  			if (valueAttribute == value[i]) {
9641  				binding.isChecked = node.checked = true;
9642  				return;
9643  			}
9644  		}
9645  		binding.isChecked = node.checked = false;
9646  	}
9647  }
9648
9649  var updateClassName = Attribute$updateClassName;
9650  function Attribute$updateClassName() {
9651  	this.node.className = safeToStringValue(this.value);
9652  }
9653
9654  var updateIdAttribute = Attribute$updateIdAttribute;
9655
9656  function Attribute$updateIdAttribute() {
9657  	var _ref = this;
9658
9659  	var node = _ref.node;
9660  	var value = _ref.value;
9661
9662  	this.root.nodes[value] = node;
9663  	node.id = value;
9664  }
9665
9666  var updateIEStyleAttribute = Attribute$updateIEStyleAttribute;
9667
9668  function Attribute$updateIEStyleAttribute() {
9669  	var node, value;
9670
9671  	node = this.node;
9672  	value = this.value;
9673
9674  	if (value === undefined) {
9675  		value = "";
9676  	}
9677
9678  	node.style.setAttribute("cssText", value);
9679  }
9680
9681  var updateContentEditableValue = Attribute$updateContentEditableValue;
9682
9683  function Attribute$updateContentEditableValue() {
9684  	var value = this.value;
9685
9686  	if (value === undefined) {
9687  		value = "";
9688  	}
9689
9690  	if (!this.locked) {
9691  		this.node.innerHTML = value;
9692  	}
9693  }
9694
9695  var updateValue = Attribute$updateValue;
9696
9697  function Attribute$updateValue() {
9698  	var _ref = this;
9699
9700  	var node = _ref.node;
9701  	var value = _ref.value;
9702
9703  	// store actual value, so it doesn't get coerced to a string
9704  	node._ractive.value = value;
9705
9706  	// with two-way binding, only update if the change wasn't initiated by the user
9707  	// otherwise the cursor will often be sent to the wrong place
9708  	if (!this.locked) {
9709  		node.value = value == undefined ? "" : value;
9710  	}
9711  }
9712
9713  var updateBoolean = Attribute$updateBooleanAttribute;
9714
9715  function Attribute$updateBooleanAttribute() {
9716  	// with two-way binding, only update if the change wasn't initiated by the user
9717  	// otherwise the cursor will often be sent to the wrong place
9718  	if (!this.locked) {
9719  		this.node[this.propertyName] = this.value;
9720  	}
9721  }
9722
9723  var updateEverythingElse = Attribute$updateEverythingElse;
9724
9725  function Attribute$updateEverythingElse() {
9726  	var _ref = this;
9727
9728  	var node = _ref.node;
9729  	var namespace = _ref.namespace;
9730  	var name = _ref.name;
9731  	var value = _ref.value;
9732  	var fragment = _ref.fragment;
9733
9734  	if (namespace) {
9735  		node.setAttributeNS(namespace, name, (fragment || value).toString());
9736  	} else if (!this.isBoolean) {
9737  		if (value == null) {
9738  			node.removeAttribute(name);
9739  		} else {
9740  			node.setAttribute(name, (fragment || value).toString());
9741  		}
9742  	}
9743
9744  	// Boolean attributes - truthy becomes '', falsy means 'remove attribute'
9745  	else {
9746  		if (value) {
9747  			node.setAttribute(name, "");
9748  		} else {
9749  			node.removeAttribute(name);
9750  		}
9751  	}
9752  }
9753
9754  // There are a few special cases when it comes to updating attributes. For this reason,
9755  // the prototype .update() method points to this method, which waits until the
9756  // attribute has finished initialising, then replaces the prototype method with a more
9757  // suitable one. That way, we save ourselves doing a bunch of tests on each call
9758  var Attribute_prototype_update = Attribute$update;
9759  function Attribute$update() {
9760  	var _ref = this;
9761
9762  	var name = _ref.name;
9763  	var element = _ref.element;
9764  	var node = _ref.node;var type;var updateMethod;
9765
9766  	if (name === "id") {
9767  		updateMethod = updateIdAttribute;
9768  	} else if (name === "value") {
9769  		// special case - selects
9770  		if (element.name === "select" && name === "value") {
9771  			updateMethod = element.getAttribute("multiple") ? updateMultipleSelectValue : updateSelectValue;
9772  		} else if (element.name === "textarea") {
9773  			updateMethod = updateValue;
9774  		}
9775
9776  		// special case - contenteditable
9777  		else if (element.getAttribute("contenteditable") != null) {
9778  			updateMethod = updateContentEditableValue;
9779  		}
9780
9781  		// special case - <input>
9782  		else if (element.name === "input") {
9783  			type = element.getAttribute("type");
9784
9785  			// type='file' value='{{fileList}}'>
9786  			if (type === "file") {
9787  				updateMethod = noop; // read-only
9788  			}
9789
9790  			// type='radio' name='{{twoway}}'
9791  			else if (type === "radio" && element.binding && element.binding.name === "name") {
9792  				updateMethod = updateRadioValue;
9793  			} else {
9794  				updateMethod = updateValue;
9795  			}
9796  		}
9797  	}
9798
9799  	// special case - <input type='radio' name='{{twoway}}' value='foo'>
9800  	else if (this.isTwoway && name === "name") {
9801  		if (node.type === "radio") {
9802  			updateMethod = updateRadioName;
9803  		} else if (node.type === "checkbox") {
9804  			updateMethod = updateCheckboxName;
9805  		}
9806  	}
9807
9808  	// special case - style attributes in Internet Exploder
9809  	else if (name === "style" && node.style.setAttribute) {
9810  		updateMethod = updateIEStyleAttribute;
9811  	}
9812
9813  	// special case - class names. IE fucks things up, again
9814  	else if (name === "class" && (!node.namespaceURI || node.namespaceURI === namespaces.html)) {
9815  		updateMethod = updateClassName;
9816  	} else if (this.useProperty) {
9817  		updateMethod = updateBoolean;
9818  	}
9819
9820  	if (!updateMethod) {
9821  		updateMethod = updateEverythingElse;
9822  	}
9823
9824  	this.update = updateMethod;
9825  	this.update();
9826  }
9827
9828  var Attribute = function (options) {
9829  	this.init(options);
9830  };
9831
9832  Attribute.prototype = {
9833  	bubble: Attribute_prototype_bubble,
9834  	init: prototype_init,
9835  	rebind: Attribute_prototype_rebind,
9836  	render: Attribute_prototype_render,
9837  	toString: Attribute_prototype_toString,
9838  	unbind: Attribute_prototype_unbind,
9839  	update: Attribute_prototype_update
9840  };
9841
9842  var _Attribute = Attribute;
9843
9844  var createAttributes = function (element, attributes) {
9845  	var name,
9846  	    attribute,
9847  	    result = [];
9848
9849  	for (name in attributes) {
9850  		// skip binding attributes
9851  		if (name === "twoway" || name === "lazy") {
9852  			continue;
9853  		}
9854
9855  		if (attributes.hasOwnProperty(name)) {
9856  			attribute = new _Attribute({
9857  				element: element,
9858  				name: name,
9859  				value: attributes[name],
9860  				root: element.root
9861  			});
9862
9863  			result[name] = attribute;
9864
9865  			if (name !== "value") {
9866  				result.push(attribute);
9867  			}
9868  		}
9869  	}
9870
9871  	// value attribute goes last. This is because it
9872  	// may get clamped on render otherwise, e.g. in
9873  	// `<input type='range' value='999' min='0' max='1000'>`
9874  	// since default max is 100
9875  	if (attribute = result.value) {
9876  		result.push(attribute);
9877  	}
9878
9879  	return result;
9880  };
9881
9882  var _ConditionalAttribute__div;
9883
9884  if (typeof document !== "undefined") {
9885  	_ConditionalAttribute__div = createElement("div");
9886  }
9887
9888  var ConditionalAttribute = function (element, template) {
9889  	this.element = element;
9890  	this.root = element.root;
9891  	this.parentFragment = element.parentFragment;
9892
9893  	this.attributes = [];
9894
9895  	this.fragment = new virtualdom_Fragment({
9896  		root: element.root,
9897  		owner: this,
9898  		template: [template]
9899  	});
9900  };
9901
9902  ConditionalAttribute.prototype = {
9903  	bubble: function () {
9904  		if (this.node) {
9905  			this.update();
9906  		}
9907
9908  		this.element.bubble();
9909  	},
9910
9911  	rebind: function (oldKeypath, newKeypath) {
9912  		this.fragment.rebind(oldKeypath, newKeypath);
9913  	},
9914
9915  	render: function (node) {
9916  		this.node = node;
9917  		this.isSvg = node.namespaceURI === namespaces.svg;
9918
9919  		this.update();
9920  	},
9921
9922  	unbind: function () {
9923  		this.fragment.unbind();
9924  	},
9925
9926  	update: function () {
9927  		var _this = this;
9928
9929  		var str, attrs;
9930
9931  		str = this.fragment.toString();
9932  		attrs = parseAttributes(str, this.isSvg);
9933
9934  		// any attributes that previously existed but no longer do
9935  		// must be removed
9936  		this.attributes.filter(function (a) {
9937  			return notIn(attrs, a);
9938  		}
9938).forEach(function (a) {
9939  			_this.node.removeAttribute(a.name);
9940  		});
9941
9942  		attrs.forEach(function (a) {
9943  			_this.node.setAttribute(a.name, a.value);
9944  		});
9945
9946  		this.attributes = attrs;
9947  	},
9948
9949  	toString: function () {
9950  		return this.fragment.toString();
9951  	}
9952  };
9953
9954  var _ConditionalAttribute = ConditionalAttribute;
9955
9956  function parseAttributes(str, isSvg) {
9957  	var tag = isSvg ? "svg" : "div";
9958  	_ConditionalAttribute__div.innerHTML = "<" + tag + " " + str + "></" + tag + ">";
9959
9960  	return toArray(_ConditionalAttribute__div.childNodes[0].attributes);
9961  }
9962
9963  function notIn(haystack, needle) {
9964  	var i = haystack.length;
9965
9966  	while (i--) {
9967  		if (haystack[i].name === needle.name) {
9968  			return false;
9969  		}
9970  	}
9971
9972  	return true;
9973  }
9974
9975  var createConditionalAttributes = function (element, attributes) {
9976  	if (!attributes) {
9977  		return [];
9978  	}
9979
9980  	return attributes.map(function (a) {
9981  		return new _ConditionalAttribute(element, a);
9982  	});
9983  };
9984
9985  var Binding = function (element) {
9986  	var interpolator, keypath, value, parentForm;
9987
9988  	this.element = element;
9989  	this.root = element.root;
9990  	this.attribute = element.attributes[this.name || "value"];
9991
9992  	interpolator = this.attribute.interpolator;
9993  	interpolator.twowayBinding = this;
9994
9995  	if (keypath = interpolator.keypath) {
9996  		if (keypath.str.slice(-1) === "}") {
9997  			warnOnceIfDebug("Two-way binding does not work with expressions (`%s` on <%s>)", interpolator.resolver.uniqueString, element.name, { ractive: this.root });
9998  			return false;
9999  		}
10000
10001  		if (keypath.isSpecial) {
10002  			warnOnceIfDebug("Two-way binding does not work with %s", interpolator.resolver.ref, { ractive: this.root });
10003  			return false;
10004  		}
10005  	} else {
10006  		// A mustache may be *ambiguous*. Let's say we were given
10007  		// `value="{{bar}}"`. If the context was `foo`, and `foo.bar`
10008  		// *wasn't* `undefined`, the keypath would be `foo.bar`.
10009  		// Then, any user input would result in `foo.bar` being updated.
10010  		//
10011  		// If, however, `foo.bar` *was* undefined, and so was `bar`, we would be
10012  		// left with an unresolved partial keypath - so we are forced to make an
10013  		// assumption. That assumption is that the input in question should
10014  		// be forced to resolve to `bar`, and any user input would affect `bar`
10015  		// and not `foo.bar`.
10016  		//
10017  		// Did that make any sense? No? Oh. Sorry. Well the moral of the story is
10018  		// be explicit when using two-way data-binding about what keypath you're
10019  		// updating. Using it in lists is probably a recipe for confusion...
10020  		var ref = interpolator.template.r ? "'" + interpolator.template.r + "' reference" : "expression";
10021  		warnIfDebug("The %s being used for two-way binding is ambiguous, and may cause unexpected results. Consider initialising your data to eliminate the ambiguity", ref, { ractive: this.root });
10022  		interpolator.resolver.forceResolution();
10023  		keypath = interpolator.keypath;
10024  	}
10025
10026  	this.attribute.isTwoway = true;
10027  	this.keypath = keypath;
10028
10029  	// initialise value, if it's undefined
10030  	value = this.root.viewmodel.get(keypath);
10031
10032  	if (value === undefined && this.getInitialValue) {
10033  		value = this.getInitialValue();
10034
10035  		if (value !== undefined) {
10036  			this.root.viewmodel.set(keypath, value);
10037  		}
10038  	}
10039
10040  	if (parentForm = findParentForm(element)) {
10041  		this.resetValue = value;
10042  		parentForm.formBindings.push(this);
10043  	}
10044  };
10045
10046  Binding.prototype = {
10047  	handleChange: function () {
10048  		var _this = this;
10049
10050  		global_runloop.start(this.root);
10051  		this.attribute.locked = true;
10052  		this.root.viewmodel.set(this.keypath, this.getValue());
10053  		global_runloop.scheduleTask(function () {
10054  			return _this.attribute.locked = false;
10055  		});
10056  		global_runloop.end();
10057  	},
10058
10059  	rebound: function () {
10060  		var bindings, oldKeypath, newKeypath;
10061
10062  		oldKeypath = this.keypath;
10063  		newKeypath = this.attribute.interpolator.keypath;
10064
10065  		// The attribute this binding is linked to has already done the work
10066  		if (oldKeypath === newKeypath) {
10067  			return;
10068  		}
10069
10070  		removeFromArray(this.root._twowayBindings[oldKeypath.str], this);
10071
10072  		this.keypath = newKeypath;
10073
10074  		bindings = this.root._twowayBindings[newKeypath.str] || (this.root._twowayBindings[newKeypath.str] = []);
10075  		bindings.push(this);
10076  	},
10077
10078  	unbind: function () {}
10079  };
10080
10081  Binding.extend = function (properties) {
10082  	var Parent = this,
10083  	    SpecialisedBinding;
10084
10085  	SpecialisedBinding = function (element) {
10086  		Binding.call(this, element);
10087
10088  		if (this.init) {
10089  			this.init();
10090  		}
10091  	};
10092
10093  	SpecialisedBinding.prototype = create(Parent.prototype);
10094  	utils_object__extend(SpecialisedBinding.prototype, properties);
10095
10096  	SpecialisedBinding.extend = Binding.extend;
10097
10098  	return SpecialisedBinding;
10099  };
10100
10101  var Binding_Binding = Binding;
10102
10103  function findParentForm(element) {
10104  	while (element = element.parent) {
10105  		if (element.name === "form") {
10106  			return element;
10107  		}
10108  	}
10109  }
10110
10111  // this is called when the element is unbound.
10112  // Specialised bindings can override it
10113
10114  // This is the handler for DOM events that would lead to a change in the model
10115  // (i.e. change, sometimes, input, and occasionally click and keyup)
10116  var handleDomEvent = handleChange;
10117
10118  function handleChange() {
10119  	this._ractive.binding.handleChange();
10120  }
10121
10122  var GenericBinding;
10123
10124  GenericBinding = Binding_Binding.extend({
10125  	getInitialValue: function () {
10126  		return "";
10127  	},
10128
10129  	getValue: function () {
10130  		return this.element.node.value;
10131  	},
10132
10133  	render: function () {
10134  		var node = this.element.node,
10135  		    lazy,
10136  		    timeout = false;
10137  		this.rendered = true;
10138
10139  		// any lazy setting for this element overrides the root
10140  		// if the value is a number, it's a timeout
10141  		lazy = this.root.lazy;
10142  		if (this.element.lazy === true) {
10143  			lazy = true;
10144  		} else if (this.element.lazy === false) {
10145  			lazy = false;
10146  		} else if (is__isNumeric(this.element.lazy)) {
10147  			lazy = false;
10148  			timeout = +this.element.lazy;
10149  		} else if (is__isNumeric(lazy || "")) {
10150  			timeout = +lazy;
10151  			lazy = false;
10152
10153  			// make sure the timeout is available to the handler
10154  			this.element.lazy = timeout;
10155  		}
10156
10157  		this.handler = timeout ? handleDelay : handleDomEvent;
10158
10159  		node.addEventListener("change", handleDomEvent, false);
10160
10161  		if (!lazy) {
10162  			node.addEventListener("input", this.handler, false);
10163
10164  			if (node.attachEvent) {
10165  				node.addEventListener("keyup", this.handler, false);
10166  			}
10167  		}
10168
10169  		node.addEventListener("blur", handleBlur, false);
10170  	},
10171
10172  	unrender: function () {
10173  		var node = this.element.node;
10174  		this.rendered = false;
10175
10176  		node.removeEventListener("change", handleDomEvent, false);
10177  		node.removeEventListener("input", this.handler, false);
10178  		node.removeEventListener("keyup", this.handler, false);
10179  		node.removeEventListener("blur", handleBlur, false);
10180  	}
10181  });
10182
10183  var Binding_GenericBinding = GenericBinding;
10184
10185  function handleBlur() {
10186  	var value;
10187
10188  	handleDomEvent.call(this);
10189
10190  	value = this._ractive.root.viewmodel.get(this._ractive.binding.keypath);
10191  	this.value = value == undefined ? "" : value;
10192  }
10193
10194  function handleDelay() {
10195  	var binding = this._ractive.binding,
10196  	    el = this;
10197
10198  	if (!!binding._timeout) clearTimeout(binding._timeout);
10199
10200  	binding._timeout = setTimeout(function () {
10201  		if (binding.rendered) handleDomEvent.call(el);
10202  		binding._timeout = undefined;
10203  	}, binding.element.lazy);
10204  }
10205
10206  var ContentEditableBinding = Binding_GenericBinding.extend({
10207  	getInitialValue: function () {
10208  		return this.element.fragment ? this.element.fragment.toString() : "";
10209  	},
10210
10211  	getValue: function () {
10212  		return this.element.node.innerHTML;
10213  	}
10214  });
10215
10216  var Binding_ContentEditableBinding = ContentEditableBinding;
10217
10218  var shared_getSiblings = getSiblings;
10219  var sets = {};
10220  function getSiblings(id, group, keypath) {
10221  	var hash = id + group + keypath;
10222  	return sets[hash] || (sets[hash] = []);
10223  }
10224
10225  var RadioBinding = Binding_Binding.extend({
10226  	name: "checked",
10227
10228  	init: function () {
10229  		this.siblings = shared_getSiblings(this.root._guid, "radio", this.element.getAttribute("name"));
10230  		this.siblings.push(this);
10231  	},
10232
10233  	render: function () {
10234  		var node = this.element.node;
10235
10236  		node.addEventListener("change", handleDomEvent, false);
10237
10238  		if (node.attachEvent) {
10239  			node.addEventListener("click", handleDomEvent, false);
10240  		}
10241  	},
10242
10243  	unrender: function () {
10244  		var node = this.element.node;
10245
10246  		node.removeEventListener("change", handleDomEvent, false);
10247  		node.removeEventListener("click", handleDomEvent, false);
10248  	},
10249
10250  	handleChange: function () {
10251  		global_runloop.start(this.root);
10252
10253  		this.siblings.forEach(function (binding) {
10254  			binding.root.viewmodel.set(binding.keypath, binding.getValue());
10255  		});
10256
10257  		global_runloop.end();
10258  	},
10259
10260  	getValue: function () {
10261  		return this.element.node.checked;
10262  	},
10263
10264  	unbind: function () {
10265  		removeFromArray(this.siblings, this);
10266  	}
10267  });
10268
10269  var Binding_RadioBinding = RadioBinding;
10270
10271  var RadioNameBinding = Binding_Binding.extend({
10272  	name: "name",
10273
10274  	init: function () {
10275  		this.siblings = shared_getSiblings(this.root._guid, "radioname", this.keypath.str);
10276  		this.siblings.push(this);
10277
10278  		this.radioName = true; // so that ractive.updateModel() knows what to do with this
10279  	},
10280
10281  	getInitialValue: function () {
10282  		if (this.element.getAttribute("checked")) {
10283  			return this.element.getAttribute("value");
10284  		}
10285  	},
10286
10287  	render: function () {
10288  		var node = this.element.node;
10289
10290  		node.name = "{{" + this.keypath.str + "}}";
10291  		node.checked = this.root.viewmodel.get(this.keypath) == this.element.getAttribute("value");
10292
10293  		node.addEventListener("change", handleDomEvent, false);
10294
10295  		if (node.attachEvent) {
10296  			node.addEventListener("click", handleDomEvent, false);
10297  		}
10298  	},
10299
10300  	unrender: function () {
10301  		var node = this.element.node;
10302
10303  		node.removeEventListener("change", handleDomEvent, false);
10304  		node.removeEventListener("click", handleDomEvent, false);
10305  	},
10306
10307  	getValue: function () {
10308  		var node = this.element.node;
10309  		return node._ractive ? node._ractive.value : node.value;
10310  	},
10311
10312  	handleChange: function () {
10313  		// If this <input> is the one that's checked, then the value of its
10314  		// `name` keypath gets set to its value
10315  		if (this.element.node.checked) {
10316  			Binding_Binding.prototype.handleChange.call(this);
10317  		}
10318  	},
10319
10320  	rebound: function (oldKeypath, newKeypath) {
10321  		var node;
10322
10323  		Binding_Binding.prototype.rebound.call(this, oldKeypath, newKeypath);
10324
10325  		if (node = this.element.node) {
10326  			node.name = "{{" + this.keypath.str + "}}";
10327  		}
10328  	},
10329
10330  	unbind: function () {
10331  		removeFromArray(this.siblings, this);
10332  	}
10333  });
10334
10335  var Binding_RadioNameBinding = RadioNameBinding;
10336
10337  var CheckboxNameBinding = Binding_Binding.extend({
10338  	name: "name",
10339
10340  	getInitialValue: function () {
10341  		// This only gets called once per group (of inputs that
10342  		// share a name), because it only gets called if there
10343  		// isn't an initial value. By the same token, we can make
10344  		// a note of that fact that there was no initial value,
10345  		// and populate it using any `checked` attributes that
10346  		// exist (which users should avoid, but which we should
10347  		// support anyway to avoid breaking expectations)
10348  		this.noInitialValue = true;
10349  		return [];
10350  	},
10351
10352  	init: function () {
10353  		var existingValue, bindingValue;
10354
10355  		this.checkboxName = true; // so that ractive.updateModel() knows what to do with this
10356
10357  		// Each input has a reference to an array containing it and its
10358  		// siblings, as two-way binding depends on being able to ascertain
10359  		// the status of all inputs within the group
10360  		this.siblings = shared_getSiblings(this.root._guid, "checkboxes", this.keypath.str);
10361  		this.siblings.push(this);
10362
10363  		if (this.noInitialValue) {
10364  			this.siblings.noInitialValue = true;
10365  		}
10366
10367  		// If no initial value was set, and this input is checked, we
10368  		// update the model
10369  		if (this.siblings.noInitialValue && this.element.getAttribute("checked")) {
10370  			existingValue = this.root.viewmodel.get(this.keypath);
10371  			bindingValue = this.element.getAttribute("value");
10372
10373  			existingValue.push(bindingValue);
10374  		}
10375  	},
10376
10377  	unbind: function () {
10378  		removeFromArray(this.siblings, this);
10379  	},
10380
10381  	render: function () {
10382  		var node = this.element.node,
10383  		    existingValue,
10384  		    bindingValue;
10385
10386  		existingValue = this.root.viewmodel.get(this.keypath);
10387  		bindingValue = this.element.getAttribute("value");
10388
10389  		if (isArray(existingValue)) {
10390  			this.isChecked = arrayContains(existingValue, bindingValue);
10391  		} else {
10392  			this.isChecked = existingValue == bindingValue;
10393  		}
10394
10395  		node.name = "{{" + this.keypath.str + "}}";
10396  		node.checked = this.isChecked;
10397
10398  		node.addEventListener("change", handleDomEvent, false);
10399
10400  		// in case of IE emergency, bind to click event as well
10401  		if (node.attachEvent) {
10402  			node.addEventListener("click", handleDomEvent, false);
10403  		}
10404  	},
10405
10406  	unrender: function () {
10407  		var node = this.element.node;
10408
10409  		node.removeEventListener("change", handleDomEvent, false);
10410  		node.removeEventListener("click", handleDomEvent, false);
10411  	},
10412
10413  	changed: function () {
10414  		var wasChecked = !!this.isChecked;
10415  		this.isChecked = this.element.node.checked;
10416  		return this.isChecked === wasChecked;
10417  	},
10418
10419  	handleChange: function () {
10420  		this.isChecked = this.element.node.checked;
10421  		Binding_Binding.prototype.handleChange.call(this);
10422  	},
10423
10424  	getValue: function () {
10425  		return this.siblings.filter(isChecked).map(Binding_CheckboxNameBinding__getValue);
10426  	}
10427  });
10428
10429  function isChecked(binding) {
10430  	return binding.isChecked;
10431  }
10432
10433  function Binding_CheckboxNameBinding__getValue(binding) {
10434  	return binding.element.getAttribute("value");
10435  }
10436
10437  var Binding_CheckboxNameBinding = CheckboxNameBinding;
10438
10439  var CheckboxBinding = Binding_Binding.extend({
10440  	name: "checked",
10441
10442  	render: function () {
10443  		var node = this.element.node;
10444
10445  		node.addEventListener("change", handleDomEvent, false);
10446
10447  		if (node.attachEvent) {
10448  			node.addEventListener("click", handleDomEvent, false);
10449  		}
10450  	},
10451
10452  	unrender: function () {
10453  		var node = this.element.node;
10454
10455  		node.removeEventListener("change", handleDomEvent, false);
10456  		node.removeEventListener("click", handleDomEvent, false);
10457  	},
10458
10459  	getValue: function () {
10460  		return this.element.node.checked;
10461  	}
10462  });
10463
10464  var Binding_CheckboxBinding = CheckboxBinding;
10465
10466  var SelectBinding = Binding_Binding.extend({
10467  	getInitialValue: function () {
10468  		var options = this.element.options,
10469  		    len,
10470  		    i,
10471  		    value,
10472  		    optionWasSelected;
10473
10474  		if (this.element.getAttribute("value") !== undefined) {
10475  			return;
10476  		}
10477
10478  		i = len = options.length;
10479
10480  		if (!len) {
10481  			return;
10482  		}
10483
10484  		// take the final selected option...
10485  		while (i--) {
10486  			if (options[i].getAttribute("selected")) {
10487  				value = options[i].getAttribute("value");
10488  				optionWasSelected = true;
10489  				break;
10490  			}
10491  		}
10492
10493  		// or the first non-disabled option, if none are selected
10494  		if (!optionWasSelected) {
10495  			while (++i < len) {
10496  				if (!options[i].getAttribute("disabled")) {
10497  					value = options[i].getAttribute("value");
10498  					break;
10499  				}
10500  			}
10501  		}
10502
10503  		// This is an optimisation (aka hack) that allows us to forgo some
10504  		// other more expensive work
10505  		if (value !== undefined) {
10506  			this.element.attributes.value.value = value;
10507  		}
10508
10509  		return value;
10510  	},
10511
10512  	render: function () {
10513  		this.element.node.addEventListener("change", handleDomEvent, false);
10514  	},
10515
10516  	unrender: function () {
10517  		this.element.node.removeEventListener("change", handleDomEvent, false);
10518  	},
10519
10520  	// TODO this method is an anomaly... is it necessary?
10521  	setValue: function (value) {
10522  		this.root.viewmodel.set(this.keypath, value);
10523  	},
10524
10525  	getValue: function () {
10526  		var options, i, len, option, optionValue;
10527
10528  		options = this.element.node.options;
10529  		len = options.length;
10530
10531  		for (i = 0; i < len; i += 1) {
10532  			option = options[i];
10533
10534  			if (options[i].selected) {
10535  				optionValue = option._ractive ? option._ractive.value : option.value;
10536  				return optionValue;
10537  			}
10538  		}
10539  	},
10540
10541  	forceUpdate: function () {
10542  		var _this = this;
10543
10544  		var value = this.getValue();
10545
10546  		if (value !== undefined) {
10547  			this.attribute.locked = true;
10548  			global_runloop.scheduleTask(function () {
10549  				return _this.attribute.locked = false;
10550  			});
10551  			this.root.viewmodel.set(this.keypath, value);
10552  		}
10553  	}
10554  });
10555
10556  var Binding_SelectBinding = SelectBinding;
10557
10558  var MultipleSelectBinding = Binding_SelectBinding.extend({
10559  	getInitialValue: function () {
10560  		return this.element.options.filter(function (option) {
10561  			return option.getAttribute("selected");
10562  		}).map(function (option) {
10563  			return option.getAttribute("value");
10564  		});
10565  	},
10566
10567  	render: function () {
10568  		var valueFromModel;
10569
10570  		this.element.node.addEventListener("change", handleDomEvent, false);
10571
10572  		valueFromModel = this.root.viewmodel.get(this.keypath);
10573
10574  		if (valueFromModel === undefined) {
10575  			// get value from DOM, if possible
10576  			this.handleChange();
10577  		}
10578  	},
10579
10580  	unrender: function () {
10581  		this.element.node.removeEventListener("change", handleDomEvent, false);
10582  	},
10583
10584  	setValue: function () {
10585  		throw new Error("TODO not implemented yet");
10586  	},
10587
10588  	getValue: function () {
10589  		var selectedValues, options, i, len, option, optionValue;
10590
10591  		selectedValues = [];
10592  		options = this.element.node.options;
10593  		len = options.length;
10594
10595  		for (i = 0; i < len; i += 1) {
10596  			option = options[i];
10597
10598  			if (option.selected) {
10599  				optionValue = option._ractive ? option._ractive.value : option.value;
10600  				selectedValues.push(optionValue);
10601  			}
10602  		}
10603
10604  		return selectedValues;
10605  	},
10606
10607  	handleChange: function () {
10608  		var attribute, previousValue, value;
10609
10610  		attribute = this.attribute;
10611  		previousValue = attribute.value;
10612
10613  		value = this.getValue();
10614
10615  		if (previousValue === undefined || !arrayContentsMatch(value, previousValue)) {
10616  			Binding_SelectBinding.prototype.handleChange.call(this);
10617  		}
10618
10619  		return this;
10620  	},
10621
10622  	forceUpdate: function () {
10623  		var _this = this;
10624
10625  		var value = this.getValue();
10626
10627  		if (value !== undefined) {
10628  			this.attribute.locked = true;
10629  			global_runloop.scheduleTask(function () {
10630  				return _this.attribute.locked = false;
10631  			});
10632  			this.root.viewmodel.set(this.keypath, value);
10633  		}
10634  	},
10635
10636  	updateModel: function () {
10637  		if (this.attribute.value === undefined || !this.attribute.value.length) {
10638  			this.root.viewmodel.set(this.keypath, this.initialValue);
10639  		}
10640  	}
10641  });
10642
10643  var Binding_MultipleSelectBinding = MultipleSelectBinding;
10644
10645  var FileListBinding = Binding_Binding.extend({
10646  	render: function () {
10647  		this.element.node.addEventListener("change", handleDomEvent, false);
10648  	},
10649
10650  	unrender: function () {
10651  		this.element.node.removeEventListener("change", handleDomEvent, false);
10652  	},
10653
10654  	getValue: function () {
10655  		return this.element.node.files;
10656  	}
10657  });
10658
10659  var Binding_FileListBinding = FileListBinding;
10660
10661  var NumericBinding = Binding_GenericBinding.extend({
10662  	getInitialValue: function () {
10663  		return undefined;
10664  	},
10665
10666  	getValue: function () {
10667  		var value = parseFloat(this.element.node.value);
10668  		return isNaN(value) ? undefined : value;
10669  	}
10670  });
10671
10672  var init_createTwowayBinding = createTwowayBinding;
10673
10674  function createTwowayBinding(element) {
10675  	var attributes = element.attributes,
10676  	    type,
10677  	    Binding,
10678  	    bindName,
10679  	    bindChecked,
10680  	    binding;
10681
10682  	// if this is a late binding, and there's already one, it
10683  	// needs to be torn down
10684  	if (element.binding) {
10685  		element.binding.teardown();
10686  		element.binding = null;
10687  	}
10688
10689  	// contenteditable
10690  	if (
10691  	// if the contenteditable attribute is true or is bindable and may thus become true
10692  	(element.getAttribute("contenteditable") || !!attributes.contenteditable && isBindable(attributes.contenteditable)) && isBindable(attributes.value)) {
10693  		Binding = Binding_ContentEditableBinding;
10694  	}
10695
10696  	// <input>
10697  	else if (element.name === "input") {
10698  		type = element.getAttribute("type");
10699
10700  		if (type === "radio" || type === "checkbox") {
10701  			bindName = isBindable(attributes.name);
10702  			bindChecked = isBindable(attributes.checked);
10703
10704  			// we can either bind the name attribute, or the checked attribute - not both
10705  			if (bindName && bindChecked) {
10706  				warnIfDebug("A radio input can have two-way binding on its name attribute, or its checked attribute - not both", { ractive: element.root });
10707  			}
10708
10709  			if (bindName) {
10710  				Binding = type === "radio" ? Binding_RadioNameBinding : Binding_CheckboxNameBinding;
10711  			} else if (bindChecked) {
10712  				Binding = type === "radio" ? Binding_RadioBinding : Binding_CheckboxBinding;
10713  			}
10714  		} else if (type === "file" && isBindable(attributes.value)) {
10715  			Binding = Binding_FileListBinding;
10716  		} else if (isBindable(attributes.value)) {
10717  			Binding = type === "number" || type === "range" ? NumericBinding : Binding_GenericBinding;
10718  		}
10719  	}
10720
10721  	// <select>
10722  	else if (element.name === "select" && isBindable(attributes.value)) {
10723  		Binding = element.getAttribute("multiple") ? Binding_MultipleSelectBinding : Binding_SelectBinding;
10724  	}
10725
10726  	// <textarea>
10727  	else if (element.name === "textarea" && isBindable(attributes.value)) {
10728  		Binding = Binding_GenericBinding;
10729  	}
10730
10731  	if (Binding && (binding = new Binding(element)) && binding.keypath) {
10732  		return binding;
10733  	}
10734  }
10735
10736  function isBindable(attribute) {
10737  	return attribute && attribute.isBindable;
10738  }
10739
10740  // and this element also has a value attribute to bind
10741
10742  var EventHandler_prototype_bubble = EventHandler$bubble;
10743
10744  function EventHandler$bubble() {
10745  	var hasAction = this.getAction();
10746
10747  	if (hasAction && !this.hasListener) {
10748  		this.listen();
10749  	} else if (!hasAction && this.hasListener) {
10750  		this.unrender();
10751  	}
10752  }
10753
10754  // This function may be overwritten, if the event directive
10755  // includes parameters
10756  var EventHandler_prototype_fire = EventHandler$fire;
10757  function EventHandler$fire(event) {
10758  	shared_fireEvent(this.root, this.getAction(), { event: event });
10759  }
10760
10761  var getAction = EventHandler$getAction;
10762
10763  function EventHandler$getAction() {
10764  	return this.action.toString().trim();
10765  }
10766
10767  var EventHandler_prototype_init = EventHandler$init;
10768
10769  var eventPattern = /^event(?:\.(.+))?/;
10770  function EventHandler$init(element, name, template) {
10771  	var _this = this;
10772
10773  	var action, refs, ractive;
10774
10775  	this.element = element;
10776  	this.root = element.root;
10777  	this.parentFragment = element.parentFragment;
10778  	this.name = name;
10779
10780  	if (name.indexOf("*") !== -1) {
10781  		fatal("Only component proxy-events may contain \"*\" wildcards, <%s on-%s=\"...\"/> is not valid", element.name, name);
10782  		this.invalid = true;
10783  	}
10784
10785  	if (template.m) {
10786  		refs = template.a.r;
10787
10788  		// This is a method call
10789  		this.method = template.m;
10790  		this.keypaths = [];
10791  		this.fn = shared_getFunctionFromString(template.a.s, refs.length);
10792
10793  		this.parentFragment = element.parentFragment;
10794  		ractive = this.root;
10795
10796  		// Create resolvers for each reference
10797  		this.refResolvers = [];
10798  		refs.forEach(function (ref, i) {
10799  			var match = undefined;
10800
10801  			// special case - the `event` object
10802  			if (match = eventPattern.exec(ref)) {
10803  				_this.keypaths[i] = {
10804  					eventObject: true,
10805  					refinements: match[1] ? match[1].split(".") : []
10806  				};
10807  			} else {
10808  				_this.refResolvers.push(Resolvers_createReferenceResolver(_this, ref, function (keypath) {
10809  					return _this.resolve(i, keypath);
10810  				}));
10811  			}
10812  		});
10813
10814  		this.fire = fireMethodCall;
10815  	} else {
10816  		// Get action ('foo' in 'on-click='foo')
10817  		action = template.n || template;
10818  		if (typeof action !== "string") {
10819  			action = new virtualdom_Fragment({
10820  				template: action,
10821  				root: this.root,
10822  				owner: this
10823  			});
10824  		}
10825
10826  		this.action = action;
10827
10828  		// Get parameters
10829  		if (template.d) {
10830  			this.dynamicParams = new virtualdom_Fragment({
10831  				template: template.d,
10832  				root: this.root,
10833  				owner: this.element
10834  			});
10835
10836  			this.fire = fireEventWithDynamicParams;
10837  		} else if (template.a) {
10838  			this.params = template.a;
10839  			this.fire = fireEventWithParams;
10840  		}
10841  	}
10842  }
10843
10844  function fireMethodCall(event) {
10845  	var ractive, values, args;
10846
10847  	ractive = this.root;
10848
10849  	if (typeof ractive[this.method] !== "function") {
10850  		throw new Error("Attempted to call a non-existent method (\"" + this.method + "\")");
10851  	}
10852
10853  	values = this.keypaths.map(function (keypath) {
10854  		var value, len, i;
10855
10856  		if (keypath === undefined) {
10857  			// not yet resolved
10858  			return undefined;
10859  		}
10860
10861  		// TODO the refinements stuff would be better handled at parse time
10862  		if (keypath.eventObject) {
10863  			value = event;
10864
10865  			if (len = keypath.refinements.length) {
10866  				for (i = 0; i < len; i += 1) {
10867  					value = value[keypath.refinements[i]];
10868  				}
10869  			}
10870  		} else {
10871  			value = ractive.viewmodel.get(keypath);
10872  		}
10873
10874  		return value;
10875  	});
10876
10877  	shared_eventStack.enqueue(ractive, event);
10878
10879  	args = this.fn.apply(null, values);
10880  	ractive[this.method].apply(ractive, args);
10881
10882  	shared_eventStack.dequeue(ractive);
10883  }
10884
10885  function fireEventWithParams(event) {
10886  	shared_fireEvent(this.root, this.getAction(), { event: event, args: this.params });
10887  }
10888
10889  function fireEventWithDynamicParams(event) {
10890  	var args = this.dynamicParams.getArgsList();
10891
10892  	// need to strip [] from ends if a string!
10893  	if (typeof args === "string") {
10894  		args = args.substr(1, args.length - 2);
10895  	}
10896
10897  	shared_fireEvent(this.root, this.getAction(), { event: event, args: args });
10898  }
10899
10900  var shared_genericHandler = genericHandler;
10901  function genericHandler(event) {
10902  	var storage,
10903  	    handler,
10904  	    indices,
10905  	    index = {};
10906
10907  	storage = this._ractive;
10908  	handler = storage.events[event.type];
10909
10910  	if (indices = Resolvers_findIndexRefs(handler.element.parentFragment)) {
10911  		index = Resolvers_findIndexRefs.resolve(indices);
10912  	}
10913
10914  	handler.fire({
10915  		node: this,
10916  		original: event,
10917  		index: index,
10918  		keypath: storage.keypath.str,
10919  		context: storage.root.viewmodel.get(storage.keypath)
10920  	});
10921  }
10922
10923  var listen = EventHandler$listen;
10924
10925  var customHandlers = {},
10926      touchEvents = {
10927  	touchstart: true,
10928  	touchmove: true,
10929  	touchend: true,
10930  	touchcancel: true,
10931  	//not w3c, but supported in some browsers
10932  	touchleave: true
10933  };
10934  function EventHandler$listen() {
10935  	var definition,
10936  	    name = this.name;
10937
10938  	if (this.invalid) {
10939  		return;
10940  	}
10941
10942  	if (definition = findInViewHierarchy("events", this.root, name)) {
10943  		this.custom = definition(this.node, getCustomHandler(name));
10944  	} else {
10945  		// Looks like we're dealing with a standard DOM event... but let's check
10946  		if (!("on" + name in this.node) && !(window && "on" + name in window) && !isJsdom) {
10947
10948  			// okay to use touch events if this browser doesn't support them
10949  			if (!touchEvents[name]) {
10950  				warnOnceIfDebug(missingPlugin(name, "event"), { node: this.node });
10951  			}
10952
10953  			return;
10954  		}
10955
10956  		this.node.addEventListener(name, shared_genericHandler, false);
10957  	}
10958
10959  	this.hasListener = true;
10960  }
10961
10962  function getCustomHandler(name) {
10963  	if (!customHandlers[name]) {
10964  		customHandlers[name] = function (event) {
10965  			var storage = event.node._ractive;
10966
10967  			event.index = storage.index;
10968  			event.keypath = storage.keypath.str;
10969  			event.context = storage.root.viewmodel.get(storage.keypath);
10970
10971  			storage.events[name].fire(event);
10972  		};
10973  	}
10974
10975  	return customHandlers[name];
10976  }
10977
10978  var EventHandler_prototype_rebind = EventHandler$rebind;
10979
10980  function EventHandler$rebind(oldKeypath, newKeypath) {
10981  	var fragment;
10982  	if (this.method) {
10983  		fragment = this.element.parentFragment;
10984  		this.refResolvers.forEach(rebind);
10985
10986  		return;
10987  	}
10988
10989  	if (typeof this.action !== "string") {
10990  		rebind(this.action);
10991  	}
10992
10993  	if (this.dynamicParams) {
10994  		rebind(this.dynamicParams);
10995  	}
10996
10997  	function rebind(thing) {
10998  		thing && thing.rebind(oldKeypath, newKeypath);
10999  	}
11000  }
11001
11002  var EventHandler_prototype_render = EventHandler$render;
11003
11004  function EventHandler$render() {
11005  	this.node = this.element.node;
11006  	// store this on the node itself, so it can be retrieved by a
11007  	// universal handler
11008  	this.node._ractive.events[this.name] = this;
11009
11010  	if (this.method || this.getAction()) {
11011  		this.listen();
11012  	}
11013  }
11014
11015  var prototype_resolve = EventHandler$resolve;
11016
11017  function EventHandler$resolve(index, keypath) {
11018  	this.keypaths[index] = keypath;
11019  }
11020
11021  var EventHandler_prototype_unbind = EventHandler$unbind;
11022  function EventHandler$unbind() {
11023  	if (this.method) {
11024  		this.refResolvers.forEach(methodCallers__unbind);
11025  		return;
11026  	}
11027
11028  	// Tear down dynamic name
11029  	if (typeof this.action !== "string") {
11030  		this.action.unbind();
11031  	}
11032
11033  	// Tear down dynamic parameters
11034  	if (this.dynamicParams) {
11035  		this.dynamicParams.unbind();
11036  	}
11037  }
11038
11039  var EventHandler_prototype_unrender = EventHandler$unrender;
11040  function EventHandler$unrender() {
11041
11042  	if (this.custom) {
11043  		this.custom.teardown();
11044  	} else {
11045  		this.node.removeEventListener(this.name, shared_genericHandler, false);
11046  	}
11047
11048  	this.hasListener = false;
11049  }
11050
11051  var EventHandler = function (element, name, template) {
11052  	this.init(element, name, template);
11053  };
11054
11055  EventHandler.prototype = {
11056  	bubble: EventHandler_prototype_bubble,
11057  	fire: EventHandler_prototype_fire,
11058  	getAction: getAction,
11059  	init: EventHandler_prototype_init,
11060  	listen: listen,
11061  	rebind: EventHandler_prototype_rebind,
11062  	render: EventHandler_prototype_render,
11063  	resolve: prototype_resolve,
11064  	unbind: EventHandler_prototype_unbind,
11065  	unrender: EventHandler_prototype_unrender
11066  };
11067
11068  var _EventHandler = EventHandler;
11069
11070  var createEventHandlers = function (element, template) {
11071  	var i,
11072  	    name,
11073  	    names,
11074  	    handler,
11075  	    result = [];
11076
11077  	for (name in template) {
11078  		if (template.hasOwnProperty(name)) {
11079  			names = name.split("-");
11080  			i = names.length;
11081
11082  			while (i--) {
11083  				handler = new _EventHandler(element, names[i], template[name]);
11084  				result.push(handler);
11085  			}
11086  		}
11087  	}
11088
11089  	return result;
11090  };
11091
11092  var Decorator = function (element, template) {
11093  	var self = this,
11094  	    ractive,
11095  	    name,
11096  	    fragment;
11097
11098  	this.element = element;
11099  	this.root = ractive = element.root;
11100
11101  	name = template.n || template;
11102
11103  	if (typeof name !== "string") {
11104  		fragment = new virtualdom_Fragment({
11105  			template: name,
11106  			root: ractive,
11107  			owner: element
11108  		});
11109
11110  		name = fragment.toString();
11111  		fragment.unbind();
11112
11113  		if (name === "") {
11114  			// empty string okay, just no decorator
11115  			return;
11116  		}
11117  	}
11118
11119  	if (template.a) {
11120  		this.params = template.a;
11121  	} else if (template.d) {
11122  		this.fragment = new virtualdom_Fragment({
11123  			template: template.d,
11124  			root: ractive,
11125  			owner: element
11126  		});
11127
11128  		this.params = this.fragment.getArgsList();
11129
11130  		this.fragment.bubble = function () {
11131  			this.dirtyArgs = this.dirtyValue = true;
11132  			self.params = this.getArgsList();
11133
11134  			if (self.ready) {
11135  				self.update();
11136  			}
11137  		};
11138  	}
11139
11140  	this.fn = findInViewHierarchy("decorators", ractive, name);
11141
11142  	if (!this.fn) {
11143  		fatal(missingPlugin(name, "decorator"));
11144  	}
11145  };
11146
11147  Decorator.prototype = {
11148  	init: function () {
11149  		var node, result, args;
11150
11151  		node = this.element.node;
11152
11153  		if (this.params) {
11154  			args = [node].concat(this.params);
11155  			result = this.fn.apply(this.root, args);
11156  		} else {
11157  			result = this.fn.call(this.root, node);
11158  		}
11159
11160  		if (!result || !result.teardown) {
11161  			throw new Error("Decorator definition must return an object with a teardown method");
11162  		}
11163
11164  		// TODO does this make sense?
11165  		this.actual = result;
11166  		this.ready = true;
11167  	},
11168
11169  	update: function () {
11170  		if (this.actual.update) {
11171  			this.actual.update.apply(this.root, this.params);
11172  		} else {
11173  			this.actual.teardown(true);
11174  			this.init();
11175  		}
11176  	},
11177
11178  	rebind: function (oldKeypath, newKeypath) {
11179  		if (this.fragment) {
11180  			this.fragment.rebind(oldKeypath, newKeypath);
11181  		}
11182  	},
11183
11184  	teardown: function (updating) {
11185  		this.torndown = true;
11186  		if (this.ready) {
11187  			this.actual.teardown();
11188  		}
11189
11190  		if (!updating && this.fragment) {
11191  			this.fragment.unbind();
11192  		}
11193  	}
11194  };
11195
11196  var _Decorator = Decorator;
11197
11198  function select__bubble() {
11199  	var _this = this;
11200
11201  	if (!this.dirty) {
11202  		this.dirty = true;
11203
11204  		global_runloop.scheduleTask(function () {
11205  			sync(_this);
11206  			_this.dirty = false;
11207  		});
11208  	}
11209
11210  	this.parentFragment.bubble(); // default behaviour
11211  }
11212
11213  function sync(selectElement) {
11214  	var selectNode, selectValue, isMultiple, options, optionWasSelected;
11215
11216  	selectNode = selectElement.node;
11217
11218  	if (!selectNode) {
11219  		return;
11220  	}
11221
11222  	options = toArray(selectNode.options);
11223
11224  	selectValue = selectElement.getAttribute("value");
11225  	isMultiple = selectElement.getAttribute("multiple");
11226
11227  	// If the <select> has a specified value, that should override
11228  	// these options
11229  	if (selectValue !== undefined) {
11230  		options.forEach(function (o) {
11231  			var optionValue, shouldSelect;
11232
11233  			optionValue = o._ractive ? o._ractive.value : o.value;
11234  			shouldSelect = isMultiple ? valueContains(selectValue, optionValue) : selectValue == optionValue;
11235
11236  			if (shouldSelect) {
11237  				optionWasSelected = true;
11238  			}
11239
11240  			o.selected = shouldSelect;
11241  		});
11242
11243  		if (!optionWasSelected) {
11244  			if (options[0]) {
11245  				options[0].selected = true;
11246  			}
11247
11248  			if (selectElement.binding) {
11249  				selectElement.binding.forceUpdate();
11250  			}
11251  		}
11252  	}
11253
11254  	// Otherwise the value should be initialised according to which
11255  	// <option>
11255 element is selected, if twoway binding is in effect
11256  	else if (selectElement.binding) {
11257  		selectElement.binding.forceUpdate();
11258  	}
11259  }
11260
11261  function valueContains(selectValue, optionValue) {
11262  	var i = selectValue.length;
11263  	while (i--) {
11264  		if (selectValue[i] == optionValue) {
11265  			return true;
11266  		}
11267  	}
11268  }
11269
11270  function special_option__init(option, template) {
11271  	option.select = findParentSelect(option.parent);
11272
11273  	// we might be inside a <datalist> element
11274  	if (!option.select) {
11275  		return;
11276  	}
11277
11278  	option.select.options.push(option);
11279
11280  	// If the value attribute is missing, use the element's content
11281  	if (!template.a) {
11282  		template.a = {};
11283  	}
11284
11285  	// ...as long as it isn't disabled
11286  	if (template.a.value === undefined && !template.a.hasOwnProperty("disabled")) {
11287  		template.a.value = template.f;
11288  	}
11289
11290  	// If there is a `selected` attribute, but the <select>
11291  	// already has a value, delete it
11292  	if ("selected" in template.a && option.select.getAttribute("value") !== undefined) {
11293  		delete template.a.selected;
11294  	}
11295  }
11296
11297  function special_option__unbind(option) {
11298  	if (option.select) {
11299  		removeFromArray(option.select.options, option);
11300  	}
11301  }
11302
11303  function findParentSelect(element) {
11304  	if (!element) {
11305  		return;
11306  	}
11307
11308  	do {
11309  		if (element.name === "select") {
11310  			return element;
11311  		}
11312  	} while (element = element.parent);
11313  }
11314
11315  var Element_prototype_init = Element$init;
11316  function Element$init(options) {
11317  	var parentFragment, template, ractive, binding, bindings, twoway, bindingAttrs;
11318
11319  	this.type = ELEMENT;
11320
11321  	// stuff we'll need later
11322  	parentFragment = this.parentFragment = options.parentFragment;
11323  	template = this.template = options.template;
11324
11325  	this.parent = options.pElement || parentFragment.pElement;
11326
11327  	this.root = ractive = parentFragment.root;
11328  	this.index = options.index;
11329  	this.key = options.key;
11330
11331  	this.name = enforceCase(template.e);
11332
11333  	// Special case - <option> elements
11334  	if (this.name === "option") {
11335  		special_option__init(this, template);
11336  	}
11337
11338  	// Special case - <select> elements
11339  	if (this.name === "select") {
11340  		this.options = [];
11341  		this.bubble = select__bubble; // TODO this is a kludge
11342  	}
11343
11344  	// Special case - <form> elements
11345  	if (this.name === "form") {
11346  		this.formBindings = [];
11347  	}
11348
11349  	// handle binding attributes first (twoway, lazy)
11350  	bindingAttrs = processBindingAttributes(this, template);
11351
11352  	// create attributes
11353  	this.attributes = createAttributes(this, template.a);
11354  	this.conditionalAttributes = createConditionalAttributes(this, template.m);
11355
11356  	// append children, if there are any
11357  	if (template.f) {
11358  		this.fragment = new virtualdom_Fragment({
11359  			template: template.f,
11360  			root: ractive,
11361  			owner: this,
11362  			pElement: this,
11363  			cssIds: null
11364  		});
11365  	}
11366
11367  	// the element setting should override the ractive setting
11368  	twoway = ractive.twoway;
11369  	if (bindingAttrs.twoway === false) twoway = false;else if (bindingAttrs.twoway === true) twoway = true;
11370
11371  	this.twoway = twoway;
11372  	this.lazy = bindingAttrs.lazy;
11373
11374  	// create twoway binding
11375  	if (twoway && (binding = init_createTwowayBinding(this, template.a))) {
11376  		this.binding = binding;
11377
11378  		// register this with the root, so that we can do ractive.updateModel()
11379  		bindings = this.root._twowayBindings[binding.keypath.str] || (this.root._twowayBindings[binding.keypath.str] = []);
11380  		bindings.push(binding);
11381  	}
11382
11383  	// create event proxies
11384  	if (template.v) {
11385  		this.eventHandlers = createEventHandlers(this, template.v);
11386  	}
11387
11388  	// create decorator
11389  	if (template.o) {
11390  		this.decorator = new _Decorator(this, template.o);
11391  	}
11392
11393  	// create transitions
11394  	this.intro = template.t0 || template.t1;
11395  	this.outro = template.t0 || template.t2;
11396  }
11397
11398  var Element_prototype_rebind = Element$rebind;
11399  function Element$rebind(oldKeypath, newKeypath) {
11400  	var i, storage, liveQueries, ractive;
11401
11402  	if (this.attributes) {
11403  		this.attributes.forEach(rebind);
11404  	}
11405
11406  	if (this.conditionalAttributes) {
11407  		this.conditionalAttributes.forEach(rebind);
11408  	}
11409
11410  	if (this.eventHandlers) {
11411  		this.eventHandlers.forEach(rebind);
11412  	}
11413
11414  	if (this.decorator) {
11415  		rebind(this.decorator);
11416  	}
11417
11418  	// rebind children
11419  	if (this.fragment) {
11420  		rebind(this.fragment);
11421  	}
11422
11423  	// Update live queries, if necessary
11424  	if (liveQueries = this.liveQueries) {
11425  		ractive = this.root;
11426
11427  		i = liveQueries.length;
11428  		while (i--) {
11429  			liveQueries[i]._makeDirty();
11430  		}
11431  	}
11432
11433  	if (this.node && (storage = this.node._ractive)) {
11434
11435  		// adjust keypath if needed
11436  		assignNewKeypath(storage, "keypath", oldKeypath, newKeypath);
11437  	}
11438
11439  	function rebind(thing) {
11440  		thing.rebind(oldKeypath, newKeypath);
11441  	}
11442  }
11443
11444  function special_img__render(img) {
11445  	var loadHandler;
11446
11447  	// if this is an <img>, and we're in a crap browser, we may need to prevent it
11448  	// from overriding width and height when it loads the src
11449  	if (img.attributes.width || img.attributes.height) {
11450  		img.node.addEventListener("load", loadHandler = function () {
11451  			var width = img.getAttribute("width"),
11452  			    height = img.getAttribute("height");
11453
11454  			if (width !== undefined) {
11455  				img.node.setAttribute("width", width);
11456  			}
11457
11458  			if (height !== undefined) {
11459  				img.node.setAttribute("height", height);
11460  			}
11461
11462  			img.node.removeEventListener("load", loadHandler, false);
11463  		}, false);
11464  	}
11465  }
11466
11467  function form__render(element) {
11468  	element.node.addEventListener("reset", handleReset, false);
11469  }
11470
11471  function form__unrender(element) {
11472  	element.node.removeEventListener("reset", handleReset, false);
11473  }
11474
11475  function handleReset() {
11476  	var element = this._ractive.proxy;
11477
11478  	global_runloop.start();
11479  	element.formBindings.forEach(updateModel);
11480  	global_runloop.end();
11481  }
11482
11483  function updateModel(binding) {
11484  	binding.root.viewmodel.set(binding.keypath, binding.resetValue);
11485  }
11486
11487  var Transition_prototype_init = Transition$init;
11488  function Transition$init(element, template, isIntro) {
11489  	var ractive, name, fragment;
11490
11491  	this.element = element;
11492  	this.root = ractive = element.root;
11493  	this.isIntro = isIntro;
11494
11495  	name = template.n || template;
11496
11497  	if (typeof name !== "string") {
11498  		fragment = new virtualdom_Fragment({
11499  			template: name,
11500  			root: ractive,
11501  			owner: element
11502  		});
11503
11504  		name = fragment.toString();
11505  		fragment.unbind();
11506
11507  		if (name === "") {
11508  			// empty string okay, just no transition
11509  			return;
11510  		}
11511  	}
11512
11513  	this.name = name;
11514
11515  	if (template.a) {
11516  		this.params = template.a;
11517  	} else if (template.d) {
11518  		// TODO is there a way to interpret dynamic arguments without all the
11519  		// 'dependency thrashing'?
11520  		fragment = new virtualdom_Fragment({
11521  			template: template.d,
11522  			root: ractive,
11523  			owner: element
11524  		});
11525
11526  		this.params = fragment.getArgsList();
11527  		fragment.unbind();
11528  	}
11529
11530  	this._fn = findInViewHierarchy("transitions", ractive, name);
11531
11532  	if (!this._fn) {
11533  		warnOnceIfDebug(missingPlugin(name, "transition"), { ractive: this.root });
11534  	}
11535  }
11536
11537  var camelCase = function (hyphenatedStr) {
11538  	return hyphenatedStr.replace(/-([a-zA-Z])/g, function (match, $1) {
11539  		return $1.toUpperCase();
11540  	});
11541  };
11542
11543  var helpers_prefix__prefix, prefixCache, helpers_prefix__testStyle;
11544
11545  if (!isClient) {
11546  	helpers_prefix__prefix = null;
11547  } else {
11548  	prefixCache = {};
11549  	helpers_prefix__testStyle = createElement("div").style;
11550
11551  	helpers_prefix__prefix = function (prop) {
11552  		var i, vendor, capped;
11553
11554  		prop = camelCase(prop);
11555
11556  		if (!prefixCache[prop]) {
11557  			if (helpers_prefix__testStyle[prop] !== undefined) {
11558  				prefixCache[prop] = prop;
11559  			} else {
11560  				// test vendors...
11561  				capped = prop.charAt(0).toUpperCase() + prop.substring(1);
11562
11563  				i = vendors.length;
11564  				while (i--) {
11565  					vendor = vendors[i];
11566  					if (helpers_prefix__testStyle[vendor + capped] !== undefined) {
11567  						prefixCache[prop] = vendor + capped;
11568  						break;
11569  					}
11570  				}
11571  			}
11572  		}
11573
11574  		return prefixCache[prop];
11575  	};
11576  }
11577
11578  var helpers_prefix = helpers_prefix__prefix;
11579
11580  var getStyle, prototype_getStyle__getComputedStyle;
11581
11582  if (!isClient) {
11583  	getStyle = null;
11584  } else {
11585  	prototype_getStyle__getComputedStyle = window.getComputedStyle || legacy.getComputedStyle;
11586
11587  	getStyle = function (props) {
11588  		var computedStyle, styles, i, prop, value;
11589
11590  		computedStyle = prototype_getStyle__getComputedStyle(this.node);
11591
11592  		if (typeof props === "string") {
11593  			value = computedStyle[helpers_prefix(props)];
11594  			if (value === "0px") {
11595  				value = 0;
11596  			}
11597  			return value;
11598  		}
11599
11600  		if (!isArray(props)) {
11601  			throw new Error("Transition$getStyle must be passed a string, or an array of strings representing CSS properties");
11602  		}
11603
11604  		styles = {};
11605
11606  		i = props.length;
11607  		while (i--) {
11608  			prop = props[i];
11609  			value = computedStyle[helpers_prefix(prop)];
11610  			if (value === "0px") {
11611  				value = 0;
11612  			}
11613  			styles[prop] = value;
11614  		}
11615
11616  		return styles;
11617  	};
11618  }
11619
11620  var prototype_getStyle = getStyle;
11621
11622  var setStyle = function (style, value) {
11623  	var prop;
11624
11625  	if (typeof style === "string") {
11626  		this.node.style[helpers_prefix(style)] = value;
11627  	} else {
11628  		for (prop in style) {
11629  			if (style.hasOwnProperty(prop)) {
11630  				this.node.style[helpers_prefix(prop)] = style[prop];
11631  			}
11632  		}
11633  	}
11634
11635  	return this;
11636  };
11637
11638  var Ticker = function (options) {
11639  	var easing;
11640
11641  	this.duration = options.duration;
11642  	this.step = options.step;
11643  	this.complete = options.complete;
11644
11645  	// easing
11646  	if (typeof options.easing === "string") {
11647  		easing = options.root.easing[options.easing];
11648
11649  		if (!easing) {
11650  			warnOnceIfDebug(missingPlugin(options.easing, "easing"));
11651  			easing = linear;
11652  		}
11653  	} else if (typeof options.easing === "function") {
11654  		easing = options.easing;
11655  	} else {
11656  		easing = linear;
11657  	}
11658
11659  	this.easing = easing;
11660
11661  	this.start = utils_getTime();
11662  	this.end = this.start + this.duration;
11663
11664  	this.running = true;
11665  	shared_animations.add(this);
11666  };
11667
11668  Ticker.prototype = {
11669  	tick: function (now) {
11670  		var elapsed, eased;
11671
11672  		if (!this.running) {
11673  			return false;
11674  		}
11675
11676  		if (now > this.end) {
11677  			if (this.step) {
11678  				this.step(1);
11679  			}
11680
11681  			if (this.complete) {
11682  				this.complete(1);
11683  			}
11684
11685  			return false;
11686  		}
11687
11688  		elapsed = now - this.start;
11689  		eased = this.easing(elapsed / this.duration);
11690
11691  		if (this.step) {
11692  			this.step(eased);
11693  		}
11694
11695  		return true;
11696  	},
11697
11698  	stop: function () {
11699  		if (this.abort) {
11700  			this.abort();
11701  		}
11702
11703  		this.running = false;
11704  	}
11705  };
11706
11707  var shared_Ticker = Ticker;
11708  function linear(t) {
11709  	return t;
11710  }
11711
11712  var unprefixPattern = new RegExp("^-(?:" + vendors.join("|") + ")-");
11713
11714  var unprefix = function (prop) {
11715  	return prop.replace(unprefixPattern, "");
11716  };
11717
11718  var vendorPattern = new RegExp("^(?:" + vendors.join("|") + ")([A-Z])");
11719
11720  var hyphenate = function (str) {
11721  	var hyphenated;
11722
11723  	if (!str) {
11724  		return ""; // edge case
11725  	}
11726
11727  	if (vendorPattern.test(str)) {
11728  		str = "-" + str;
11729  	}
11730
11731  	hyphenated = str.replace(/[A-Z]/g, function (match) {
11732  		return "-" + match.toLowerCase();
11733  	});
11734
11735  	return hyphenated;
11736  };
11737
11738  var createTransitions,
11739      animateStyle_createTransitions__testStyle,
11740      TRANSITION,
11741      TRANSITIONEND,
11742      CSS_TRANSITIONS_ENABLED,
11743      TRANSITION_DURATION,
11744      TRANSITION_PROPERTY,
11745      TRANSITION_TIMING_FUNCTION,
11746      canUseCssTransitions = {},
11747      cannotUseCssTransitions = {};
11748
11749  if (!isClient) {
11750  	createTransitions = null;
11751  } else {
11752  	animateStyle_createTransitions__testStyle = createElement("div").style;
11753
11754  	// determine some facts about our environment
11755  	(function () {
11756  		if (animateStyle_createTransitions__testStyle.transition !== undefined) {
11757  			TRANSITION = "transition";
11758  			TRANSITIONEND = "transitionend";
11759  			CSS_TRANSITIONS_ENABLED = true;
11760  		} else if (animateStyle_createTransitions__testStyle.webkitTransition !== undefined) {
11761  			TRANSITION = "webkitTransition";
11762  			TRANSITIONEND = "webkitTransitionEnd";
11763  			CSS_TRANSITIONS_ENABLED = true;
11764  		} else {
11765  			CSS_TRANSITIONS_ENABLED = false;
11766  		}
11767  	})();
11768
11769  	if (TRANSITION) {
11770  		TRANSITION_DURATION = TRANSITION + "Duration";
11771  		TRANSITION_PROPERTY = TRANSITION + "Property";
11772  		TRANSITION_TIMING_FUNCTION = TRANSITION + "TimingFunction";
11773  	}
11774
11775  	createTransitions = function (t, to, options, changedProperties, resolve) {
11776
11777  		// Wait a beat (otherwise the target styles will be applied immediately)
11778  		// TODO use a fastdom-style mechanism?
11779  		setTimeout(function () {
11780
11781  			var hashPrefix, jsTransitionsComplete, cssTransitionsComplete, checkComplete, transitionEndHandler;
11782
11783  			checkComplete = function () {
11784  				if (jsTransitionsComplete && cssTransitionsComplete) {
11785  					// will changes to events and fire have an unexpected consequence here?
11786  					t.root.fire(t.name + ":end", t.node, t.isIntro);
11787  					resolve();
11788  				}
11789  			};
11790
11791  			// this is used to keep track of which elements can use CSS to animate
11792  			// which properties
11793  			hashPrefix = (t.node.namespaceURI || "") + t.node.tagName;
11794
11795  			t.node.style[TRANSITION_PROPERTY] = changedProperties.map(helpers_prefix).map(hyphenate).join(",");
11796  			t.node.style[TRANSITION_TIMING_FUNCTION] = hyphenate(options.easing || "linear");
11797  			t.node.style[TRANSITION_DURATION] = options.duration / 1000 + "s";
11798
11799  			transitionEndHandler = function (event) {
11800  				var index;
11801
11802  				index = changedProperties.indexOf(camelCase(unprefix(event.propertyName)));
11803  				if (index !== -1) {
11804  					changedProperties.splice(index, 1);
11805  				}
11806
11807  				if (changedProperties.length) {
11808  					// still transitioning...
11809  					return;
11810  				}
11811
11812  				t.node.removeEventListener(TRANSITIONEND, transitionEndHandler, false);
11813
11814  				cssTransitionsComplete = true;
11815  				checkComplete();
11816  			};
11817
11818  			t.node.addEventListener(TRANSITIONEND, transitionEndHandler, false);
11819
11820  			setTimeout(function () {
11821  				var i = changedProperties.length,
11822  				    hash,
11823  				    originalValue,
11824  				    index,
11825  				    propertiesToTransitionInJs = [],
11826  				    prop,
11827  				    suffix;
11828
11829  				while (i--) {
11830  					prop = changedProperties[i];
11831  					hash = hashPrefix + prop;
11832
11833  					if (CSS_TRANSITIONS_ENABLED && !cannotUseCssTransitions[hash]) {
11834  						t.node.style[helpers_prefix(prop)] = to[prop];
11835
11836  						// If we're not sure if CSS transitions are supported for
11837  						// this tag/property combo, find out now
11838  						if (!canUseCssTransitions[hash]) {
11839  							originalValue = t.getStyle(prop);
11840
11841  							// if this property is transitionable in this browser,
11842  							// the current style will be different from the target style
11843  							canUseCssTransitions[hash] = t.getStyle(prop) != to[prop];
11844  							cannotUseCssTransitions[hash] = !canUseCssTransitions[hash];
11845
11846  							// Reset, if we're going to use timers after all
11847  							if (cannotUseCssTransitions[hash]) {
11848  								t.node.style[helpers_prefix(prop)] = originalValue;
11849  							}
11850  						}
11851  					}
11852
11853  					if (!CSS_TRANSITIONS_ENABLED || cannotUseCssTransitions[hash]) {
11854  						// we need to fall back to timer-based stuff
11855  						if (originalValue === undefined) {
11856  							originalValue = t.getStyle(prop);
11857  						}
11858
11859  						// need to remove this from changedProperties, otherwise transitionEndHandler
11860  						// will get confused
11861  						index = changedProperties.indexOf(prop);
11862  						if (index === -1) {
11863  							warnIfDebug("Something very strange happened with transitions. Please raise an issue at https://github.com/ractivejs/ractive/issues - thanks!", { node: t.node });
11864  						} else {
11865  							changedProperties.splice(index, 1);
11866  						}
11867
11868  						// TODO Determine whether this property is animatable at all
11869
11870  						suffix = /[^\d]*$/.exec(to[prop])[0];
11871
11872  						// ...then kick off a timer-based transition
11873  						propertiesToTransitionInJs.push({
11874  							name: helpers_prefix(prop),
11875  							interpolator: shared_interpolate(parseFloat(originalValue), parseFloat(to[prop])),
11876  							suffix: suffix
11877  						});
11878  					}
11879  				}
11880
11881  				// javascript transitions
11882  				if (propertiesToTransitionInJs.length) {
11883  					new shared_Ticker({
11884  						root: t.root,
11885  						duration: options.duration,
11886  						easing: camelCase(options.easing || ""),
11887  						step: function (pos) {
11888  							var prop, i;
11889
11890  							i = propertiesToTransitionInJs.length;
11891  							while (i--) {
11892  								prop = propertiesToTransitionInJs[i];
11893  								t.node.style[prop.name] = prop.interpolator(pos) + prop.suffix;
11894  							}
11895  						},
11896  						complete: function () {
11897  							jsTransitionsComplete = true;
11898  							checkComplete();
11899  						}
11900  					});
11901  				} else {
11902  					jsTransitionsComplete = true;
11903  				}
11904
11905  				if (!changedProperties.length) {
11906  					// We need to cancel the transitionEndHandler, and deal with
11907  					// the fact that it will never fire
11908  					t.node.removeEventListener(TRANSITIONEND, transitionEndHandler, false);
11909  					cssTransitionsComplete = true;
11910  					checkComplete();
11911  				}
11912  			}, 0);
11913  		}, options.delay || 0);
11914  	};
11915  }
11916
11917  var animateStyle_createTransitions = createTransitions;
11918
11919  var hidden, vendor, animateStyle_visibility__prefix, animateStyle_visibility__i, visibility;
11920
11921  if (typeof document !== "undefined") {
11922  	hidden = "hidden";
11923
11924  	visibility = {};
11925
11926  	if (hidden in document) {
11927  		animateStyle_visibility__prefix = "";
11928  	} else {
11929  		animateStyle_visibility__i = vendors.length;
11930  		while (animateStyle_visibility__i--) {
11931  			vendor = vendors[animateStyle_visibility__i];
11932  			hidden = vendor + "Hidden";
11933
11934  			if (hidden in document) {
11935  				animateStyle_visibility__prefix = vendor;
11936  			}
11937  		}
11938  	}
11939
11940  	if (animateStyle_visibility__prefix !== undefined) {
11941  		document.addEventListener(animateStyle_visibility__prefix + "visibilitychange", onChange);
11942
11943  		// initialise
11944  		onChange();
11945  	} else {
11946  		// gah, we're in an old browser
11947  		if ("onfocusout" in document) {
11948  			document.addEventListener("focusout", onHide);
11949  			document.addEventListener("focusin", onShow);
11950  		} else {
11951  			window.addEventListener("pagehide", onHide);
11952  			window.addEventListener("blur", onHide);
11953
11954  			window.addEventListener("pageshow", onShow);
11955  			window.addEventListener("focus", onShow);
11956  		}
11957
11958  		visibility.hidden = false; // until proven otherwise. Not ideal but hey
11959  	}
11960  }
11961
11962  function onChange() {
11963  	visibility.hidden = document[hidden];
11964  }
11965
11966  function onHide() {
11967  	visibility.hidden = true;
11968  }
11969
11970  function onShow() {
11971  	visibility.hidden = false;
11972  }
11973
11974  var animateStyle_visibility = visibility;
11975
11976  var animateStyle, _animateStyle__getComputedStyle, resolved;
11977
11978  if (!isClient) {
11979  	animateStyle = null;
11980  } else {
11981  	_animateStyle__getComputedStyle = window.getComputedStyle || legacy.getComputedStyle;
11982
11983  	animateStyle = function (style, value, options) {
11984  		var _this = this;
11985
11986  		var to;
11987
11988  		if (arguments.length === 4) {
11989  			throw new Error("t.animateStyle() returns a promise - use .then() instead of passing a callback");
11990  		}
11991
11992  		// Special case - page isn't visible. Don't animate anything, because
11993  		// that way you'll never get CSS transitionend events
11994  		if (animateStyle_visibility.hidden) {
11995  			this.setStyle(style, value);
11996  			return resolved || (resolved = utils_Promise.resolve());
11997  		}
11998
11999  		if (typeof style === "string") {
12000  			to = {};
12001  			to[style] = value;
12002  		} else {
12003  			to = style;
12004
12005  			// shuffle arguments
12006  			options = value;
12007  		}
12008
12009  		// As of 0.3.9, transition authors should supply an `option` object with
12010  		// `duration` and `easing` properties (and optional `delay`), plus a
12011  		// callback function that gets called after the animation completes
12012
12013  		// TODO remove this check in a future version
12014  		if (!options) {
12015  			warnOnceIfDebug("The \"%s\" transition does not supply an options object to `t.animateStyle()`. This will break in a future version of Ractive. For more info see https://github.com/RactiveJS/Ractive/issues/340", this.name);
12016  			options = this;
12017  		}
12018
12019  		var promise = new utils_Promise(function (resolve) {
12020  			var propertyNames, changedProperties, computedStyle, current, from, i, prop;
12021
12022  			// Edge case - if duration is zero, set style synchronously and complete
12023  			if (!options.duration) {
12024  				_this.setStyle(to);
12025  				resolve();
12026  				return;
12027  			}
12028
12029  			// Get a list of the properties we're animating
12030  			propertyNames = Object.keys(to);
12031  			changedProperties = [];
12032
12033  			// Store the current styles
12034  			computedStyle = _animateStyle__getComputedStyle(_this.node);
12035
12036  			from = {};
12037  			i = propertyNames.length;
12038  			while (i--) {
12039  				prop = propertyNames[i];
12040  				current = computedStyle[helpers_prefix(prop)];
12041
12042  				if (current === "0px") {
12043  					current = 0;
12044  				}
12045
12046  				// we need to know if we're actually changing anything
12047  				if (current != to[prop]) {
12048  					// use != instead of !==, so we can compare strings with numbers
12049  					changedProperties.push(prop);
12050
12051  					// make the computed style explicit, so we can animate where
12052  					// e.g. height='auto'
12053  					_this.node.style[helpers_prefix(prop)] = current;
12054  				}
12055  			}
12056
12057  			// If we're not actually changing anything, the transitionend event
12058  			// will never fire! So we complete early
12059  			if (!changedProperties.length) {
12060  				resolve();
12061  				return;
12062  			}
12063
12064  			animateStyle_createTransitions(_this, to, options, changedProperties, resolve);
12065  		});
12066
12067  		return promise;
12068  	};
12069  }
12070
12071  var _animateStyle = animateStyle;
12072
12073  var processParams = function (params, defaults) {
12074  	if (typeof params === "number") {
12075  		params = { duration: params };
12076  	} else if (typeof params === "string") {
12077  		if (params === "slow") {
12078  			params = { duration: 600 };
12079  		} else if (params === "fast") {
12080  			params = { duration: 200 };
12081  		} else {
12082  			params = { duration: 400 };
12083  		}
12084  	} else if (!params) {
12085  		params = {};
12086  	}
12087
12088  	return fillGaps({}, params, defaults);
12089  };
12090
12091  var prototype_start = Transition$start;
12092
12093  function Transition$start() {
12094  	var _this = this;
12095
12096  	var node, originalStyle, completed;
12097
12098  	node = this.node = this.element.node;
12099  	originalStyle = node.getAttribute("style");
12100
12101  	// create t.complete() - we don't want this on the prototype,
12102  	// because we don't want `this` silliness when passing it as
12103  	// an argument
12104  	this.complete = function (noReset) {
12105  		if (completed) {
12106  			return;
12107  		}
12108
12109  		if (!noReset && _this.isIntro) {
12110  			resetStyle(node, originalStyle);
12111  		}
12112
12113  		node._ractive.transition = null;
12114  		_this._manager.remove(_this);
12115
12116  		completed = true;
12117  	};
12118
12119  	// If the transition function doesn't exist, abort
12120  	if (!this._fn) {
12121  		this.complete();
12122  		return;
12123  	}
12124
12125  	this._fn.apply(this.root, [this].concat(this.params));
12126  }
12127
12128  function resetStyle(node, style) {
12129  	if (style) {
12130  		node.setAttribute("style", style);
12131  	} else {
12132
12133  		// Next line is necessary, to remove empty style attribute!
12134  		// See http://stackoverflow.com/a/7167553
12135  		node.getAttribute("style");
12136  		node.removeAttribute("style");
12137  	}
12138  }
12139
12140  var Transition = function (owner, template, isIntro) {
12141  	this.init(owner, template, isIntro);
12142  };
12143
12144  Transition.prototype = {
12145  	init: Transition_prototype_init,
12146  	start: prototype_start,
12147  	getStyle: prototype_getStyle,
12148  	setStyle: setStyle,
12149  	animateStyle: _animateStyle,
12150  	processParams: processParams
12151  };
12152
12153  var _Transition = Transition;
12154
12155  var Element_prototype_render = Element$render;
12156
12157  var updateCss, updateScript;
12158
12159  updateCss = function () {
12160  	var node = this.node,
12161  	    content = this.fragment.toString(false);
12162
12163  	// IE8 has no styleSheet unless there's a type text/css
12164  	if (window && window.appearsToBeIELessEqual8) {
12165  		node.type = "text/css";
12166  	}
12167
12168  	if (node.styleSheet) {
12169  		node.styleSheet.cssText = content;
12170  	} else {
12171
12172  		while (node.hasChildNodes()) {
12173  			node.removeChild(node.firstChild);
12174  		}
12175
12176  		node.appendChild(document.createTextNode(content));
12177  	}
12178  };
12179
12180  updateScript = function () {
12181  	if (!this.node.type || this.node.type === "text/javascript") {
12182  		warnIfDebug("Script tag was updated. This does not cause the code to be re-evaluated!", { ractive: this.root });
12183  		// As it happens, we ARE in a position to re-evaluate the code if we wanted
12184  		// to - we could eval() it, or insert it into a fresh (temporary) script tag.
12185  		// But this would be a terrible idea with unpredictable results, so let's not.
12186  	}
12187
12188  	this.node.text = this.fragment.toString(false);
12189  };
12190  function Element$render() {
12191  	var _this = this;
12192
12193  	var root = this.root,
12194  	    namespace,
12195  	    node,
12196  	    transition;
12197
12198  	namespace = getNamespace(this);
12199  	node = this.node = createElement(this.name, namespace);
12200
12201  	// Is this a top-level node of a component? If so, we may need to add
12202  	// a data-ractive-css attribute, for CSS encapsulation
12203  	if (this.parentFragment.cssIds) {
12204  		this.node.setAttribute("data-ractive-css", this.parentFragment.cssIds.map(function (x) {
12205  			return "{" + x + "}";
12206  		}).join(" "));
12207  	}
12208
12209  	// Add _ractive property to the node - we use this object to store stuff
12210  	// related to proxy events, two-way bindings etc
12211  	defineProperty(this.node, "_ractive", {
12212  		value: {
12213  			proxy: this,
12214  			keypath: getInnerContext(this.parentFragment),
12215  			events: create(null),
12216  			root: root
12217  		}
12218  	});
12219
12220  	// Render attributes
12221  	this.attributes.forEach(function (a) {
12222  		return a.render(node);
12223  	});
12224  	this.conditionalAttributes.forEach(function (a) {
12225  		return a.render(node);
12226  	});
12227
12228  	// Render children
12229  	if (this.fragment) {
12230  		// Special case - <script> element
12231  		if (this.name === "script") {
12232  			this.bubble = updateScript;
12233  			this.node.text = this.fragment.toString(false); // bypass warning initially
12234  			this.fragment.unrender = noop; // TODO this is a kludge
12235  		}
12236
12237  		// Special case - <style> element
12238  		else if (this.name === "style") {
12239  			this.bubble = updateCss;
12240  			this.bubble();
12241  			this.fragment.unrender = noop;
12242  		}
12243
12244  		// Special case - contenteditable
12245  		else if (this.binding && this.getAttribute("contenteditable")) {
12246  			this.fragment.unrender = noop;
12247  		} else {
12248  			this.node.appendChild(this.fragment.render());
12249  		}
12250  	}
12251
12252  	// deal with two-way bindings
12253  	if (this.binding) {
12254  		this.binding.render();
12255  		this.node._ractive.binding = this.binding;
12256  	}
12257
12258  	// Add proxy event handlers
12259  	if (this.eventHandlers) {
12260  		this.eventHandlers.forEach(function (h) {
12261  			return h.render();
12262  		});
12263  	}
12264
12265  	if (this.name === "option") {
12266  		processOption(this);
12267  	}
12268
12269  	// Special cases
12270  	if (this.name === "img") {
12271  		// if this is an <img>, and we're in a crap browser, we may
12272  		// need to prevent it from overriding width and height when
12273  		// it loads the src
12274  		special_img__render(this);
12275  	} else if (this.name === "form") {
12276  		// forms need to keep track of their bindings, in case of reset
12277  		form__render(this);
12278  	} else if (this.name === "input" || this.name === "textarea") {
12279  		// inputs and textareas should store their initial value as
12280  		// `defaultValue` in case of reset
12281  		this.node.defaultValue = this.node.value;
12282  	} else if (this.name === "option") {
12283  		// similarly for option nodes
12284  		this.node.defaultSelected = this.node.selected;
12285  	}
12286
12287  	// apply decorator(s)
12288  	if (this.decorator && this.decorator.fn) {
12289  		global_runloop.scheduleTask(function () {
12290  			if (!_this.decorator.torndown) {
12291  				_this.decorator.init();
12292  			}
12293  		}, true);
12294  	}
12295
12296  	// trigger intro transition
12297  	if (root.transitionsEnabled && this.intro) {
12298  		transition = new _Transition(this, this.intro, true);
12299  		global_runloop.registerTransition(transition);
12300  		global_runloop.scheduleTask(function () {
12301  			return transition.start();
12302  		}, true);
12303
12304  		this.transition = transition;
12305  	}
12306
12307  	if (this.node.autofocus) {
12308  		// Special case. Some browsers (*cough* Firefix *cough*) have a problem
12309  		// with dynamically-generated elements having autofocus, and they won't
12310  		// allow you to programmatically focus the element until it's in the DOM
12311  		global_runloop.scheduleTask(function () {
12312  			return _this.node.focus();
12313  		}, true);
12314  	}
12315
12316  	updateLiveQueries(this);
12317  	return this.node;
12318  }
12319
12320  function getNamespace(element) {
12321  	var namespace, xmlns, parent;
12322
12323  	// Use specified namespace...
12324  	if (xmlns = element.getAttribute("xmlns")) {
12325  		namespace = xmlns;
12326  	}
12327
12328  	// ...or SVG namespace, if this is an <svg> element
12329  	else if (element.name === "svg") {
12330  		namespace = namespaces.svg;
12331  	} else if (parent = element.parent) {
12332  		// ...or HTML, if the parent is a <foreignObject>
12333  		if (parent.name === "foreignObject") {
12334  			namespace = namespaces.html;
12335  		}
12336
12337  		// ...or inherit from the parent node
12338  		else {
12339  			namespace = parent.node.namespaceURI;
12340  		}
12341  	} else {
12342  		namespace = element.root.el.namespaceURI;
12343  	}
12344
12345  	return namespace;
12346  }
12347
12348  function processOption(option) {
12349  	var optionValue, selectValue, i;
12350
12351  	if (!option.select) {
12352  		return;
12353  	}
12354
12355  	selectValue = option.select.getAttribute("value");
12356  	if (selectValue === undefined) {
12357  		return;
12358  	}
12359
12360  	optionValue = option.getAttribute("value");
12361
12362  	if (option.select.node.multiple && isArray(selectValue)) {
12363  		i = selectValue.length;
12364  		while (i--) {
12365  			if (optionValue == selectValue[i]) {
12366  				option.node.selected = true;
12367  				break;
12368  			}
12369  		}
12370  	} else {
12371  		option.node.selected = optionValue == selectValue;
12372  	}
12373  }
12374
12375  function updateLiveQueries(element) {
12376  	var instance, liveQueries, i, selector, query;
12377
12378  	// Does this need to be added to any live queries?
12379  	instance = element.root;
12380
12381  	do {
12382  		liveQueries = instance._liveQueries;
12383
12384  		i = liveQueries.length;
12385  		while (i--) {
12386  			selector = liveQueries[i];
12387  			query = liveQueries["_" + selector];
12388
12389  			if (query._test(element)) {
12390  				// keep register of applicable selectors, for when we teardown
12391  				(element.liveQueries || (element.liveQueries = [])).push(query);
12392  			}
12393  		}
12394  	} while (instance = instance.parent);
12395  }
12396
12397  var Element_prototype_toString = function () {
12398  	var str, escape;
12399
12400  	if (this.template.y) {
12401  		// DOCTYPE declaration
12402  		return "<!DOCTYPE" + this.template.dd + ">";
12403  	}
12404
12405  	str = "<" + this.template.e;
12406
12407  	str += this.attributes.map(stringifyAttribute).join("") + this.conditionalAttributes.map(stringifyAttribute).join("");
12408
12409  	// Special case - selected options
12410  	if (this.name === "option" && optionIsSelected(this)) {
12411  		str += " selected";
12412  	}
12413
12414  	// Special case - two-way radio name bindings
12415  	if (this.name === "input" && inputIsCheckedRadio(this)) {
12416  		str += " checked";
12417  	}
12418
12419  	str += ">";
12420
12421  	// Special case - textarea
12422  	if (this.name === "textarea" && this.getAttribute("value") !== undefined) {
12423  		str += escapeHtml(this.getAttribute("value"));
12424  	}
12425
12426  	// Special case - contenteditable
12427  	else if (this.getAttribute("contenteditable") !== undefined) {
12428  		str += this.getAttribute("value") || "";
12429  	}
12430
12431  	if (this.fragment) {
12432  		escape = this.name !== "script" && this.name !== "style";
12433  		str += this.fragment.toString(escape);
12434  	}
12435
12436  	// add a closing tag if this isn't a void element
12437  	if (!voidElementNames.test(this.template.e)) {
12438  		str += "</" + this.template.e + ">";
12439  	}
12440
12441  	return str;
12442  };
12443
12444  function optionIsSelected(element) {
12445  	var optionValue, selectValue, i;
12446
12447  	optionValue = element.getAttribute("value");
12448
12449  	if (optionValue === undefined || !element.select) {
12450  		return false;
12451  	}
12452
12453  	selectValue = element.select.getAttribute("value");
12454
12455  	if (selectValue == optionValue) {
12456  		return true;
12457  	}
12458
12459  	if (element.select.getAttribute("multiple") && isArray(selectValue)) {
12460  		i = selectValue.length;
12461  		while (i--) {
12462  			if (selectValue[i] == optionValue) {
12463  				return true;
12464  			}
12465  		}
12466  	}
12467  }
12468
12469  function inputIsCheckedRadio(element) {
12470  	var attributes, typeAttribute, valueAttribute, nameAttribute;
12471
12472  	attributes = element.attributes;
12473
12474  	typeAttribute = attributes.type;
12475  	valueAttribute = attributes.value;
12476  	nameAttribute = attributes.name;
12477
12478  	if (!typeAttribute || typeAttribute.value !== "radio" || !valueAttribute || !nameAttribute.interpolator) {
12479  		return;
12480  	}
12481
12482  	if (valueAttribute.value === nameAttribute.interpolator.value) {
12483  		return true;
12484  	}
12485  }
12486
12487  function stringifyAttribute(attribute) {
12488  	var str = attribute.toString();
12489  	return str ? " " + str : "";
12490  }
12491
12492  var Element_prototype_unbind = Element$unbind;
12493  function Element$unbind() {
12494  	if (this.fragment) {
12495  		this.fragment.unbind();
12496  	}
12497
12498  	if (this.binding) {
12499  		this.binding.unbind();
12500  	}
12501
12502  	if (this.eventHandlers) {
12503  		this.eventHandlers.forEach(methodCallers__unbind);
12504  	}
12505
12506  	// Special case - <option>
12507  	if (this.name === "option") {
12508  		special_option__unbind(this);
12509  	}
12510
12511  	this.attributes.forEach(methodCallers__unbind);
12512  	this.conditionalAttributes.forEach(methodCallers__unbind);
12513  }
12514
12515  var Element_prototype_unrender = Element$unrender;
12516
12517  function Element$unrender(shouldDestroy) {
12518  	var binding, bindings, transition;
12519
12520  	if (transition = this.transition) {
12521  		transition.complete();
12522  	}
12523
12524  	// Detach as soon as we can
12525  	if (this.name === "option") {
12526  		// <option> elements detach immediately, so that
12527  		// their parent <select> element syncs correctly, and
12528  		// since option elements can't have transitions anyway
12529  		this.detach();
12530  	} else if (shouldDestroy) {
12531  		global_runloop.detachWhenReady(this);
12532  	}
12533
12534  	// Children first. that way, any transitions on child elements will be
12535  	// handled by the current transitionManager
12536  	if (this.fragment) {
12537  		this.fragment.unrender(false);
12538  	}
12539
12540  	if (binding = this.binding) {
12541  		this.binding.unrender();
12542
12543  		this.node._ractive.binding = null;
12544  		bindings = this.root._twowayBindings[binding.keypath.str];
12545  		bindings.splice(bindings.indexOf(binding), 1);
12546  	}
12547
12548  	// Remove event handlers
12549  	if (this.eventHandlers) {
12550  		this.eventHandlers.forEach(methodCallers__unrender);
12551  	}
12552
12553  	if (this.decorator) {
12554  		global_runloop.registerDecorator(this.decorator);
12555  	}
12556
12557  	// trigger outro transition if necessary
12558  	if (this.root.transitionsEnabled && this.outro) {
12559  		transition = new _Transition(this, this.outro, false);
12560  		global_runloop.registerTransition(transition);
12561  		global_runloop.scheduleTask(function () {
12562  			return transition.start();
12563  		});
12564  	}
12565
12566  	// Remove this node from any live queries
12567  	if (this.liveQueries) {
12568  		removeFromLiveQueries(this);
12569  	}
12570
12571  	if (this.name === "form") {
12572  		form__unrender(this);
12573  	}
12574  }
12575
12576  function removeFromLiveQueries(element) {
12577  	var query, selector, i;
12578
12579  	i = element.liveQueries.length;
12580  	while (i--) {
12581  		query = element.liveQueries[i];
12582  		selector = query.selector;
12583
12584  		query._remove(element.node);
12585  	}
12586  }
12587
12588  var Element = function (options) {
12589  	this.init(options);
12590  };
12591
12592  Element.prototype = {
12593  	bubble: Element_prototype_bubble,
12594  	detach: Element_prototype_detach,
12595  	find: Element_prototype_find,
12596  	findAll: Element_prototype_findAll,
12597  	findAllComponents: Element_prototype_findAllComponents,
12598  	findComponent: Element_prototype_findComponent,
12599  	findNextNode: Element_prototype_findNextNode,
12600  	firstNode: Element_prototype_firstNode,
12601  	getAttribute: getAttribute,
12602  	init: Element_prototype_init,
12603  	rebind: Element_prototype_rebind,
12604  	render: Element_prototype_render,
12605  	toString: Element_prototype_toString,
12606  	unbind: Element_prototype_unbind,
12607  	unrender: Element_prototype_unrender
12608  };
12609
12610  var _Element = Element;
12611
12612  var deIndent__empty = /^\s*$/,
12613      deIndent__leadingWhitespace = /^\s*/;
12614
12615  var deIndent = function (str) {
12616  	var lines, firstLine, lastLine, minIndent;
12617
12618  	lines = str.split("\n");
12619
12620  	// remove first and last line, if they only contain whitespace
12621  	firstLine = lines[0];
12622  	if (firstLine !== undefined && deIndent__empty.test(firstLine)) {
12623  		lines.shift();
12624  	}
12625
12626  	lastLine = lastItem(lines);
12627  	if (lastLine !== undefined && deIndent__empty.test(lastLine)) {
12628  		lines.pop();
12629  	}
12630
12631  	minIndent = lines.reduce(reducer, null);
12632
12633  	if (minIndent) {
12634  		str = lines.map(function (line) {
12635  			return line.replace(minIndent, "");
12636  		}).join("\n");
12637  	}
12638
12639  	return str;
12640  };
12641
12642  function reducer(previous, line) {
12643  	var lineIndent = deIndent__leadingWhitespace.exec(line)[0];
12644
12645  	if (previous === null || lineIndent.length < previous.length) {
12646  		return lineIndent;
12647  	}
12648
12649  	return previous;
12650  }
12651
12652  var Partial_getPartialTemplate = getPartialTemplate;
12653
12654  function getPartialTemplate(ractive, name, parentFragment) {
12655  	var partial;
12656
12657  	// If the partial in instance or view heirarchy instances, great
12658  	if (partial = getPartialFromRegistry(ractive, name, parentFragment || {})) {
12659  		return partial;
12660  	}
12661
12662  	// Does it exist on the page as a script tag?
12663  	partial = template_parser.fromId(name, { noThrow: true });
12664
12665  	if (partial) {
12666  		// is this necessary?
12667  		partial = deIndent(partial);
12668
12669  		// parse and register to this ractive instance
12670  		var parsed = template_parser.parse(partial, template_parser.getParseOptions(ractive));
12671
12672  		// register (and return main partial if there are others in the template)
12673  		return ractive.partials[name] = parsed.t;
12674  	}
12675  }
12676
12677  function getPartialFromRegistry(ractive, name, parentFragment) {
12678  	var fn = undefined,
12679  	    partial = findParentPartial(name, parentFragment.owner);
12680
12681  	// if there was an instance up-hierarchy, cool
12682  	if (partial) return partial;
12683
12684  	// find first instance in the ractive or view hierarchy that has this partial
12685  	var instance = findInstance("partials", ractive, name);
12686
12687  	if (!instance) {
12688  		return;
12689  	}
12690
12691  	partial = instance.partials[name];
12692
12693  	// partial is a function?
12694  	if (typeof partial === "function") {
12695  		fn = partial.bind(instance);
12696  		fn.isOwner = instance.partials.hasOwnProperty(name);
12697  		partial = fn.call(ractive, template_parser);
12698  	}
12699
12700  	if (!partial && partial !== "") {
12701  		warnIfDebug(noRegistryFunctionReturn, name, "partial", "partial", { ractive: ractive });
12702  		return;
12703  	}
12704
12705  	// If this was added manually to the registry,
12706  	// but hasn't been parsed, parse it now
12707  	if (!template_parser.isParsed(partial)) {
12708
12709  		// use the parseOptions of the ractive instance on which it was found
12710  		var parsed = template_parser.parse(partial, template_parser.getParseOptions(instance));
12711
12712  		// Partials cannot contain nested partials!
12713  		// TODO add a test for this
12714  		if (parsed.p) {
12715  			warnIfDebug("Partials ({{>%s}}) cannot contain nested inline partials", name, { ractive: ractive });
12716  		}
12717
12718  		// if fn, use instance to store result, otherwise needs to go
12719  		// in the correct point in prototype chain on instance or constructor
12720  		var target = fn ? instance : findOwner(instance, name);
12721
12722  		// may be a template with partials, which need to be registered and main template extracted
12723  		target.partials[name] = partial = parsed.t;
12724  	}
12725
12726  	// store for reset
12727  	if (fn) {
12728  		partial._fn = fn;
12729  	}
12730
12731  	return partial.v ? partial.t : partial;
12732  }
12733
12734  function findOwner(ractive, key) {
12735  	return ractive.partials.hasOwnProperty(key) ? ractive : findConstructor(ractive.constructor, key);
12736  }
12737
12738  function findConstructor(constructor, key) {
12739  	if (!constructor) {
12740  		return;
12741  	}
12742  	return constructor.partials.hasOwnProperty(key) ? constructor : findConstructor(constructor._Parent, key);
12743  }
12744
12745  function findParentPartial(name, parent) {
12746  	if (parent) {
12747  		if (parent.template && parent.template.p && parent.template.p[name]) {
12748  			return parent.template.p[name];
12749  		} else if (parent.parentFragment && parent.parentFragment.owner) {
12750  			return findParentPartial(name, parent.parentFragment.owner);
12751  		}
12752  	}
12753  }
12754
12755  var applyIndent = function (string, indent) {
12756  	var indented;
12757
12758  	if (!indent) {
12759  		return string;
12760  	}
12761
12762  	indented = string.split("\n").map(function (line, notFirstLine) {
12763  		return notFirstLine ? indent + line : line;
12764  	}).join("\n");
12765
12766  	return indented;
12767  };
12768
12769  var missingPartialMessage = "Could not find template for partial \"%s\"";
12770
12771  var Partial = function (options) {
12772  	var parentFragment, template;
12773
12774  	parentFragment = this.parentFragment = options.parentFragment;
12775
12776  	this.root = parentFragment.root;
12777  	this.type = PARTIAL;
12778  	this.index = options.index;
12779  	this.name = options.template.r;
12780  	this.rendered = false;
12781
12782  	this.fragment = this.fragmentToRender = this.fragmentToUnrender = null;
12783
12784  	Mustache.init(this, options);
12785
12786  	// If this didn't resolve, it most likely means we have a named partial
12787  	// (i.e. `{{>foo}}` means 'use the foo partial', not 'use the partial
12788  	// whose name is the value of `foo`')
12789  	if (!this.keypath) {
12790  		if (template = Partial_getPartialTemplate(this.root, this.name, parentFragment)) {
12791  			shared_unbind.call(this); // prevent any further changes
12792  			this.isNamed = true;
12793  			this.setTemplate(template);
12794  		} else {
12795  			warnOnceIfDebug(missingPartialMessage, this.name);
12796  		}
12797  	}
12798  };
12799
12800  Partial.prototype = {
12801  	bubble: function () {
12802  		this.parentFragment.bubble();
12803  	},
12804
12805  	detach: function () {
12806  		return this.fragment.detach();
12807  	},
12808
12809  	find: function (selector) {
12810  		return this.fragment.find(selector);
12811  	},
12812
12813  	findAll: function (selector, query) {
12814  		return this.fragment.findAll(selector, query);
12815  	},
12816
12817  	findComponent: function (selector) {
12818  		return this.fragment.findComponent(selector);
12819  	},
12820
12821  	findAllComponents: function (selector, query) {
12822  		return this.fragment.findAllComponents(selector, query);
12823  	},
12824
12825  	firstNode: function () {
12826  		return this.fragment.firstNode();
12827  	},
12828
12829  	findNextNode: function () {
12830  		return this.parentFragment.findNextNode(this);
12831  	},
12832
12833  	getPartialName: function () {
12834  		if (this.isNamed && this.name) return this.name;else if (this.value === undefined) return this.name;else return this.value;
12835  	},
12836
12837  	getValue: function () {
12838  		return this.fragment.getValue();
12839  	},
12840
12841  	rebind: function (oldKeypath, newKeypath) {
12842  		// named partials aren't bound, so don't rebind
12843  		if (!this.isNamed) {
12844  			Mustache_rebind.call(this, oldKeypath, newKeypath);
12845  		}
12846
12847  		if (this.fragment) {
12848  			this.fragment.rebind(oldKeypath, newKeypath);
12849  		}
12850  	},
12851
12852  	render: function () {
12853  		this.docFrag = document.createDocumentFragment();
12854  		this.update();
12855
12856  		this.rendered = true;
12857  		return this.docFrag;
12858  	},
12859
12860  	resolve: Mustache.resolve,
12861
12862  	setValue: function (value) {
12863  		var template;
12864
12865  		if (value !== undefined && value === this.value) {
12866  			// nothing has changed, so no work to be done
12867  			return;
12868  		}
12869
12870  		if (value !== undefined) {
12871  			template = Partial_getPartialTemplate(this.root, "" + value, this.parentFragment);
12872  		}
12873
12874  		// we may be here if we have a partial like `{{>foo}}` and `foo` is the
12875  		// name of both a data property (whose value ISN'T the name of a partial)
12876  		// and a partial. In those cases, this becomes a named partial
12877  		if (!template && this.name && (template = Partial_getPartialTemplate(this.root, this.name, this.parentFragment))) {
12878  			shared_unbind.call(this);
12879  			this.isNamed = true;
12880  		}
12881
12882  		if (!template) {
12883  			warnOnceIfDebug(missingPartialMessage, this.name, { ractive: this.root });
12884  		}
12885
12886  		this.value = value;
12887
12888  		this.setTemplate(template || []);
12889
12890  		this.bubble();
12891
12892  		if (this.rendered) {
12893  			global_runloop.addView(this);
12894  		}
12895  	},
12896
12897  	setTemplate: function (template) {
12898  		if (this.fragment) {
12899  			this.fragment.unbind();
12900  			if (this.rendered) {
12901  				this.fragmentToUnrender = this.fragment;
12902  			}
12903  		}
12904
12905  		this.fragment = new virtualdom_Fragment({
12906  			template: template,
12907  			root: this.root,
12908  			owner: this,
12909  			pElement: this.parentFragment.pElement
12910  		});
12911
12912  		this.fragmentToRender = this.fragment;
12913  	},
12914
12915  	toString: function (toString) {
12916  		var string, previousItem, lastLine, match;
12917
12918  		string = this.fragment.toString(toString);
12919
12920  		previousItem = this.parentFragment.items[this.index - 1];
12921
12922  		if (!previousItem || previousItem.type !== TEXT) {
12923  			return string;
12924  		}
12925
12926  		lastLine = previousItem.text.split("\n").pop();
12927
12928  		if (match = /^\s+$/.exec(lastLine)) {
12929  			return applyIndent(string, match[0]);
12930  		}
12931
12932  		return string;
12933  	},
12934
12935  	unbind: function () {
12936  		if (!this.isNamed) {
12937  			// dynamic partial - need to unbind self
12938  			shared_unbind.call(this);
12939  		}
12940
12941  		if (this.fragment) {
12942  			this.fragment.unbind();
12943  		}
12944  	},
12945
12946  	unrender: function (shouldDestroy) {
12947  		if (this.rendered) {
12948  			if (this.fragment) {
12949  				this.fragment.unrender(shouldDestroy);
12950  			}
12951  			this.rendered = false;
12952  		}
12953  	},
12954
12955  	update: function () {
12956  		var target, anchor;
12957
12958  		if (this.fragmentToUnrender) {
12959  			this.fragmentToUnrender.unrender(true);
12960  			this.fragmentToUnrender = null;
12961  		}
12962
12963  		if (this.fragmentToRender) {
12964  			this.docFrag.appendChild(this.fragmentToRender.render());
12965  			this.fragmentToRender = null;
12966  		}
12967
12968  		if (this.rendered) {
12969  			target = this.parentFragment.getNode();
12970  			anchor = this.parentFragment.findNextNode(this);
12971  			target.insertBefore(this.docFrag, anchor);
12972  		}
12973  	}
12974  };
12975
12976  var _Partial = Partial;
12977
12978  // finds the component constructor in the registry or view hierarchy registries
12979
12980  var Component_getComponent = getComponent;
12981  function getComponent(ractive, name) {
12982
12983  	var Component,
12984  	    instance = findInstance("components", ractive, name);
12985
12986  	if (instance) {
12987  		Component = instance.components[name];
12988
12989  		// best test we have for not Ractive.extend
12990  		if (!Component._Parent) {
12991  			// function option, execute and store for reset
12992  			var fn = Component.bind(instance);
12993  			fn.isOwner = instance.components.hasOwnProperty(name);
12994  			Component = fn();
12995
12996  			if (!Component) {
12997  				warnIfDebug(noRegistryFunctionReturn, name, "component", "component", { ractive: ractive });
12998
12999  				return;
13000  			}
13001
13002  			if (typeof Component === "string") {
13003  				// allow string lookup
13004  				Component = getComponent(ractive, Component);
13005  			}
13006
13007  			Component._fn = fn;
13008  			instance.components[name] = Component;
13009  		}
13010  	}
13011
13012  	return Component;
13013  }
13014
13015  var Component_prototype_detach = Component$detach;
13016  var Component_prototype_detach__detachHook = new hooks_Hook("detach");
13017  function Component$detach() {
13018  	var detached = this.instance.fragment.detach();
13019  	Component_prototype_detach__detachHook.fire(this.instance);
13020  	return detached;
13021  }
13022
13023  var Component_prototype_find = Component$find;
13024
13025  function Component$find(selector) {
13026  	return this.instance.fragment.find(selector);
13027  }
13028
13029  var Component_prototype_findAll = Component$findAll;
13030
13031  function Component$findAll(selector, query) {
13032  	return this.instance.fragment.findAll(selector, query);
13033  }
13034
13035  var Component_prototype_findAllComponents = Component$findAllComponents;
13036
13037  function Component$findAllComponents(selector, query) {
13038  	query._test(this, true);
13039
13040  	if (this.instance.fragment) {
13041  		this.instance.fragment.findAllComponents(selector, query);
13042  	}
13043  }
13044
13045  var Component_prototype_findComponent = Component$findComponent;
13046
13047  function Component$findComponent(selector) {
13048  	if (!selector || selector === this.name) {
13049  		return this.instance;
13050  	}
13051
13052  	if (this.instance.fragment) {
13053  		return this.instance.fragment.findComponent(selector);
13054  	}
13055
13056  	return null;
13057  }
13058
13059  var Component_prototype_findNextNode = Component$findNextNode;
13060
13061  function Component$findNextNode() {
13062  	return this.parentFragment.findNextNode(this);
13063  }
13064
13065  var Component_prototype_firstNode = Component$firstNode;
13066
13067  function Component$firstNode() {
13068  	if (this.rendered) {
13069  		return this.instance.fragment.firstNode();
13070  	}
13071
13072  	return null;
13073  }
13074
13075  var processWrapper = function (wrapper, array, methodName, newIndices) {
13076  	var root = wrapper.root;
13077  	var keypath = wrapper.keypath;
13078
13079  	if (!!newIndices) {
13080  		root.viewmodel.smartUpdate(keypath, array, newIndices);
13081  	} else {
13082  		// If this is a sort or reverse, we just do root.set()...
13083  		// TODO use merge logic?
13084  		root.viewmodel.mark(keypath);
13085  	}
13086  };
13087
13088  var patchedArrayProto = [],
13089      mutatorMethods = ["pop", "push", "reverse", "shift", "sort", "splice", "unshift"],
13090      testObj,
13091      patchArrayMethods,
13092      unpatchArrayMethods;
13093
13094  mutatorMethods.forEach(function (methodName) {
13095  	var method = function () {
13096  		for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
13097  			args[_key] = arguments[_key];
13098  		}
13099
13100  		var newIndices, result, wrapper, i;
13101
13102  		newIndices = shared_getNewIndices(this, methodName, args);
13103
13104  		// apply the underlying method
13105  		result = Array.prototype[methodName].apply(this, arguments);
13106
13107  		// trigger changes
13108  		global_runloop.start();
13109
13110  		this._ractive.setting = true;
13111  		i = this._ractive.wrappers.length;
13112  		while (i--) {
13113  			wrapper = this._ractive.wrappers[i];
13114
13115  			global_runloop.addRactive(wrapper.root);
13116  			processWrapper(wrapper, this, methodName, newIndices);
13117  		}
13118
13119  		global_runloop.end();
13120
13121  		this._ractive.setting = false;
13122  		return result;
13123  	};
13124
13125  	defineProperty(patchedArrayProto, methodName, {
13126  		value: method
13127  	});
13128  });
13129
13130  // can we use prototype chain injection?
13131  // http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/#wrappers_prototype_chain_injection
13132  testObj = {};
13133
13134  if (testObj.__proto__) {
13135  	// yes, we can
13136  	patchArrayMethods = function (array) {
13137  		array.__proto__ = patchedArrayProto;
13138  	};
13139
13140  	unpatchArrayMethods = function (array) {
13141  		array.__proto__ = Array.prototype;
13142  	};
13143  } else {
13144  	// no, we can't
13145  	patchArrayMethods = function (array) {
13146  		var i, methodName;
13147
13148  		i = mutatorMethods.length;
13149  		while (i--) {
13150  			methodName = mutatorMethods[i];
13151  			defineProperty(array, methodName, {
13152  				value: patchedArrayProto[methodName],
13153  				configurable: true
13154  			});
13155  		}
13156  	};
13157
13158  	unpatchArrayMethods = function (array) {
13159  		var i;
13160
13161  		i = mutatorMethods.length;
13162  		while (i--) {
13163  			delete array[mutatorMethods[i]];
13164  		}
13165  	};
13166  }
13167
13168  patchArrayMethods.unpatch = unpatchArrayMethods;
13169  var patch = patchArrayMethods;
13170
13171  var arrayAdaptor,
13172
13173  // helpers
13174  ArrayWrapper, array_index__errorMessage;
13175
13176  arrayAdaptor = {
13177  	filter: function (object) {
13178  		// wrap the array if a) b) it's an array, and b) either it hasn't been wrapped already,
13179  		// or the array didn't trigger the get() itself
13180  		return isArray(object) && (!object._ractive || !object._ractive.setting);
13181  	},
13182  	wrap: function (ractive, array, keypath) {
13183  		return new ArrayWrapper(ractive, array, keypath);
13184  	}
13185  };
13186
13187  ArrayWrapper = function (ractive, array, keypath) {
13188  	this.root = ractive;
13189  	this.value = array;
13190  	this.keypath = getKeypath(keypath);
13191
13192  	// if this array hasn't already been ractified, ractify it
13193  	if (!array._ractive) {
13194
13195  		// define a non-enumerable _ractive property to store the wrappers
13196  		defineProperty(array, "_ractive", {
13197  			value: {
13198  				wrappers: [],
13199  				instances: [],
13200  				setting: false
13201  			},
13202  			configurable: true
13203  		});
13204
13205  		patch(array);
13206  	}
13207
13208  	// store the ractive instance, so we can handle transitions later
13209  	if (!array._ractive.instances[ractive._guid]) {
13210  		array._ractive.instances[ractive._guid] = 0;
13211  		array._ractive.instances.push(ractive);
13212  	}
13213
13214  	array._ractive.instances[ractive._guid] += 1;
13215  	array._ractive.wrappers.push(this);
13216  };
13217
13218  ArrayWrapper.prototype = {
13219  	get: function () {
13220  		return this.value;
13221  	},
13222  	teardown: function () {
13223  		var array, storage, wrappers, instances, index;
13224
13225  		array = this.value;
13226  		storage = array._ractive;
13227  		wrappers = storage.wrappers;
13228  		instances = storage.instances;
13229
13230  		// if teardown() was invoked because we're clearing the cache as a result of
13231  		// a change that the array itself triggered, we can save ourselves the teardown
13232  		// and immediate setup
13233  		if (storage.setting) {
13234  			return false; // so that we don't remove it from this.root.viewmodel.wrapped
13235  		}
13236
13237  		index = wrappers.indexOf(this);
13238  		if (index === -1) {
13239  			throw new Error(array_index__errorMessage);
13240  		}
13241
13242  		wrappers.splice(index, 1);
13243
13244  		// if nothing else depends on this array, we can revert it to its
13245  		// natural state
13246  		if (!wrappers.length) {
13247  			delete array._ractive;
13248  			patch.unpatch(this.value);
13249  		} else {
13250  			// remove ractive instance if possible
13251  			instances[this.root._guid] -= 1;
13252  			if (!instances[this.root._guid]) {
13253  				index = instances.indexOf(this.root);
13254
13255  				if (index === -1) {
13256  					throw new Error(array_index__errorMessage);
13257  				}
13258
13259  				instances.splice(index, 1);
13260  			}
13261  		}
13262  	}
13263  };
13264
13265  array_index__errorMessage = "Something went wrong in a rather interesting way";
13266  var array_index = arrayAdaptor;
13267
13268  var numeric = /^\s*[0-9]+\s*$/;
13269
13270  var createBranch = function (key) {
13271  	return numeric.test(key) ? [] : {};
13272  };
13273
13274  var magicAdaptor, MagicWrapper;
13275
13276  try {
13277  	Object.defineProperty({}, "test", { value: 0 });
13278
13279  	magicAdaptor = {
13280  		filter: function (object, keypath, ractive) {
13281  			var parentWrapper, parentValue;
13282
13283  			if (!keypath) {
13284  				return false;
13285  			}
13286
13287  			keypath = getKeypath(keypath);
13288
13289  			// If the parent value is a wrapper, other than a magic wrapper,
13290  			// we shouldn't wrap this property
13291  			if ((parentWrapper = ractive.viewmodel.wrapped[keypath.parent.str]) && !parentWrapper.magic) {
13292  				return false;
13293  			}
13294
13295  			parentValue = ractive.viewmodel.get(keypath.parent);
13296
13297  			// if parentValue is an array that doesn't include this member,
13298  			// we should return false otherwise lengths will get messed up
13299  			if (isArray(parentValue) && /^[0-9]+$/.test(keypath.lastKey)) {
13300  				return false;
13301  			}
13302
13303  			return parentValue && (typeof parentValue === "object" || typeof parentValue === "function");
13304  		},
13305  		wrap: function (ractive, property, keypath) {
13306  			return new MagicWrapper(ractive, property, keypath);
13307  		}
13308  	};
13309
13310  	MagicWrapper = function (ractive, value, keypath) {
13311  		var objKeypath, template, siblings;
13312
13313  		keypath = getKeypath(keypath);
13314
13315  		this.magic = true;
13316
13317  		this.ractive = ractive;
13318  		this.keypath = keypath;
13319  		this.value = value;
13320
13321  		this.prop = keypath.lastKey;
13322
13323  		objKeypath = keypath.parent;
13324  		this.obj = objKeypath.isRoot ? ractive.viewmodel.data : ractive.viewmodel.get(objKeypath);
13325
13326  		template = this.originalDescriptor = Object.getOwnPropertyDescriptor(this.obj, this.prop);
13327
13328  		// Has this property already been wrapped?
13329  		if (template && template.set && (siblings = template.set._ractiveWrappers)) {
13330
13331  			// Yes. Register this wrapper to this property, if it hasn't been already
13332  			if (siblings.indexOf(this) === -1) {
13333  				siblings.push(this);
13334  			}
13335
13336  			return; // already wrapped
13337  		}
13338
13339  		// No, it hasn't been wrapped
13340  		createAccessors(this, value, template);
13341  	};
13342
13343  	MagicWrapper.prototype = {
13344  		get: function () {
13345  			return this.value;
13346  		},
13347  		reset: function (value) {
13348  			if (this.updating) {
13349  				return;
13350  			}
13351
13352  			this.updating = true;
13353  			this.obj[this.prop] = value; // trigger set() accessor
13354  			global_runloop.addRactive(this.ractive);
13355  			this.ractive.viewmodel.mark(this.keypath, { keepExistingWrapper: true });
13356  			this.updating = false;
13357  			return true;
13358  		},
13359  		set: function (key, value) {
13360  			if (this.updating) {
13361  				return;
13362  			}
13363
13364  			if (!this.obj[this.prop]) {
13365  				this.updating = true;
13366  				this.obj[this.prop] = createBranch(key);
13367  				this.updating = false;
13368  			}
13369
13370  			this.obj[this.prop][key] = value;
13371  		},
13372  		teardown: function () {
13373  			var template, set, value, wrappers, index;
13374
13375  			// If this method was called because the cache was being cleared as a
13376  			// result of a set()/update() call made by this wrapper, we return false
13377  			// so that it doesn't get torn down
13378  			if (this.updating) {
13379  				return false;
13380  			}
13381
13382  			template = Object.getOwnPropertyDescriptor(this.obj, this.prop);
13383  			set = template && template.set;
13384
13385  			if (!set) {
13386  				// most likely, this was an array member that was spliced out
13387  				return;
13388  			}
13389
13390  			wrappers = set._ractiveWrappers;
13391
13392  			index = wrappers.indexOf(this);
13393  			if (index !== -1) {
13394  				wrappers.splice(index, 1);
13395  			}
13396
13397  			// Last one out, turn off the lights
13398  			if (!wrappers.length) {
13399  				value = this.obj[this.prop];
13400
13401  				Object.defineProperty(this.obj, this.prop, this.originalDescriptor || {
13402  					writable: true,
13403  					enumerable: true,
13404  					configurable: true
13405  				});
13406
13407  				this.obj[this.prop] = value;
13408  			}
13409  		}
13410  	};
13411  } catch (err) {
13412  	magicAdaptor = false; // no magic in this browser
13413  }
13414
13415  var adaptors_magic = magicAdaptor;
13416
13417  function createAccessors(originalWrapper, value, template) {
13418
13419  	var object, property, oldGet, oldSet, get, set;
13420
13421  	object = originalWrapper.obj;
13422  	property = originalWrapper.prop;
13423
13424  	// Is this template configurable?
13425  	if (template && !template.configurable) {
13426  		// Special case - array length
13427  		if (property === "length") {
13428  			return;
13429  		}
13430
13431  		throw new Error("Cannot use magic mode with property \"" + property + "\" - object is not configurable");
13432  	}
13433
13434  	// Time to wrap this property
13435  	if (template) {
13436  		oldGet = template.get;
13437  		oldSet = template.set;
13438  	}
13439
13440  	get = oldGet || function () {
13441  		return value;
13442  	};
13443
13444  	set = function (v) {
13445  		if (oldSet) {
13446  			oldSet(v);
13447  		}
13448
13449  		value = oldGet ? oldGet() : v;
13450  		set._ractiveWrappers.forEach(updateWrapper);
13451  	};
13452
13453  	function updateWrapper(wrapper) {
13454  		var keypath, ractive;
13455
13456  		wrapper.value = value;
13457
13458  		if (wrapper.updating) {
13459  			return;
13460  		}
13461
13462  		ractive = wrapper.ractive;
13463  		keypath = wrapper.keypath;
13464
13465  		wrapper.updating = true;
13466  		global_runloop.start(ractive);
13467
13468  		ractive.viewmodel.mark(keypath);
13469
13470  		global_runloop.end();
13471  		wrapper.updating = false;
13472  	}
13473
13474  	// Create an array of wrappers, in case other keypaths/ractives depend on this property.
13475  	// Handily, we can store them as a property of the set function. Yay JavaScript.
13476  	set._ractiveWrappers = [originalWrapper];
13477  	Object.defineProperty(object, property, { get: get, set: set, enumerable: true, configurable: true });
13478  }
13479
13480  var magicArrayAdaptor, MagicArrayWrapper;
13481
13482  if (adaptors_magic) {
13483  	magicArrayAdaptor = {
13484  		filter: function (object, keypath, ractive) {
13485  			return adaptors_magic.filter(object, keypath, ractive) && array_index.filter(object);
13486  		},
13487
13488  		wrap: function (ractive, array, keypath) {
13489  			return new MagicArrayWrapper(ractive, array, keypath);
13490  		}
13491  	};
13492
13493  	MagicArrayWrapper = function (ractive, array, keypath) {
13494  		this.value = array;
13495
13496  		this.magic = true;
13497
13498  		this.magicWrapper = adaptors_magic.wrap(ractive, array, keypath);
13499  		this.arrayWrapper = array_index.wrap(ractive, array, keypath);
13500  	};
13501
13502  	MagicArrayWrapper.prototype = {
13503  		get: function () {
13504  			return this.value;
13505  		},
13506  		teardown: function () {
13507  			this.arrayWrapper.teardown();
13508  			this.magicWrapper.teardown();
13509  		},
13510  		reset: function (value) {
13511  			return this.magicWrapper.reset(value);
13512  		}
13513  	};
13514  }
13515
13516  var magicArray = magicArrayAdaptor;
13517
13518  var prototype_adapt = Viewmodel$adapt;
13519
13520  var prefixers = {};
13521  function Viewmodel$adapt(keypath, value) {
13522  	var len, i, adaptor, wrapped;
13523
13524  	if (!this.adaptors) return;
13525
13526  	// Do we have an adaptor for this value?
13527  	len = this.adaptors.length;
13528  	for (i = 0; i < len; i += 1) {
13529  		adaptor = this.adaptors[i];
13530
13531  		if (adaptor.filter(value, keypath, this.ractive)) {
13532  			wrapped = this.wrapped[keypath] = adaptor.wrap(this.ractive, value, keypath, getPrefixer(keypath));
13533  			wrapped.value = value;
13534  			return;
13535  		}
13536  	}
13537  }
13538
13539  function prefixKeypath(obj, prefix) {
13540  	var prefixed = {},
13541  	    key;
13542
13543  	if (!prefix) {
13544  		return obj;
13545  	}
13546
13547  	prefix += ".";
13548
13549  	for (key in obj) {
13550  		if (obj.hasOwnProperty(key)) {
13551  			prefixed[prefix + key] = obj[key];
13552  		}
13553  	}
13554
13555  	return prefixed;
13556  }
13557
13558  function getPrefixer(rootKeypath) {
13559  	var rootDot;
13560
13561  	if (!prefixers[rootKeypath]) {
13562  		rootDot = rootKeypath ? rootKeypath + "." : "";
13563
13564  		prefixers[rootKeypath] = function (relativeKeypath, value) {
13565  			var obj;
13566
13567  			if (typeof relativeKeypath === "string") {
13568  				obj = {};
13569  				obj[rootDot + relativeKeypath] = value;
13570  				return obj;
13571  			}
13572
13573  			if (typeof relativeKeypath === "object") {
13574  				// 'relativeKeypath' is in fact a hash, not a keypath
13575  				return rootDot ? prefixKeypath(relativeKeypath, rootKeypath) : relativeKeypath;
13576  			}
13577  		};
13578  	}
13579
13580  	return prefixers[rootKeypath];
13581  }
13582
13583  // TEMP
13584
13585  var helpers_getUpstreamChanges = getUpstreamChanges;
13586  function getUpstreamChanges(changes) {
13587  	var upstreamChanges = [rootKeypath],
13588  	    i,
13589  	    keypath;
13590
13591  	i = changes.length;
13592  	while (i--) {
13593  		keypath = changes[i].parent;
13594
13595  		while (keypath && !keypath.isRoot) {
13596  			if (changes.indexOf(keypath) === -1) {
13597  				addToArray(upstreamChanges, keypath);
13598  			}
13599  			keypath = keypath.parent;
13600  		}
13601  	}
13602
13603  	return upstreamChanges;
13604  }
13605
13606  var applyChanges_notifyPatternObservers = notifyPatternObservers;
13607
13608  function notifyPatternObservers(viewmodel, keypath, onlyDirect) {
13609  	var potentialWildcardMatches;
13610
13611  	updateMatchingPatternObservers(viewmodel, keypath);
13612
13613  	if (onlyDirect) {
13614  		return;
13615  	}
13616
13617  	potentialWildcardMatches = keypath.wildcardMatches();
13618  	potentialWildcardMatches.forEach(function (upstreamPattern) {
13619  		cascade(viewmodel, upstreamPattern, keypath);
13620  	});
13621  }
13622
13623  function cascade(viewmodel, upstreamPattern, keypath) {
13624  	var group, map, actualChildKeypath;
13625
13626  	// TODO should be one or the other
13627  	upstreamPattern = upstreamPattern.str || upstreamPattern;
13628
13629  	group = viewmodel.depsMap.patternObservers;
13630  	map = group && group[upstreamPattern];
13631
13632  	if (!map) {
13633  		return;
13634  	}
13635
13636  	map.forEach(function (childKeypath) {
13637  		actualChildKeypath = keypath.join(childKeypath.lastKey); // 'foo.bar.baz'
13638
13639  		updateMatchingPatternObservers(viewmodel, actualChildKeypath);
13640  		cascade(viewmodel, childKeypath, actualChildKeypath);
13641  	});
13642  }
13643
13644  function updateMatchingPatternObservers(viewmodel, keypath) {
13645  	viewmodel.patternObservers.forEach(function (observer) {
13646  		if (observer.regex.test(keypath.str)) {
13647  			observer.update(keypath);
13648  		}
13649  	});
13650  }
13651
13652  var applyChanges = Viewmodel$applyChanges;
13653
13654  function Viewmodel$applyChanges() {
13655  	var _this = this;
13656
13657  	var self = this,
13658  	    changes,
13659  	    upstreamChanges,
13660  	    hash = {},
13661  	    bindings;
13662
13663  	changes = this.changes;
13664
13665  	if (!changes.length) {
13666  		// TODO we end up here on initial render. Perhaps we shouldn't?
13667  		return;
13668  	}
13669
13670  	function invalidateComputation(computation) {
13671  		var key = computation.key;
13672
13673  		if (computation.viewmodel === self) {
13674  			self.clearCache(key.str);
13675  			computation.invalidate();
13676
13677  			changes.push(key);
13678  			cascade(key);
13679  		} else {
13680  			computation.viewmodel.mark(key);
13681  		}
13682  	}
13683
13684  	function cascade(keypath) {
13685  		var map, computations;
13686
13687  		if (self.noCascade.hasOwnProperty(keypath.str)) {
13688  			return;
13689  		}
13690
13691  		if (computations = self.deps.computed[keypath.str]) {
13692  			computations.forEach(invalidateComputation);
13693  		}
13694
13695  		if (map = self.depsMap.computed[keypath.str]) {
13696  			map.forEach(cascade);
13697  		}
13698  	}
13699
13700  	changes.slice().forEach(cascade);
13701
13702  	upstreamChanges = helpers_getUpstreamChanges(changes);
13703  	upstreamChanges.forEach(function (keypath) {
13704  		var computations;
13705
13706  		// make sure we haven't already been down this particular keypath in this turn
13707  		if (changes.indexOf(keypath) === -1 && (computations = self.deps.computed[keypath.str])) {
13708  			computations.forEach(invalidateComputation);
13709  		}
13710  	});
13711
13712  	this.changes = [];
13713
13714  	// Pattern observers are a weird special case
13715  	if (this.patternObservers.length) {
13716  		upstreamChanges.forEach(function (keypath) {
13717  			return applyChanges_notifyPatternObservers(_this, keypath, true);
13718  		});
13719  		changes.forEach(function (keypath) {
13720  			return applyChanges_notifyPatternObservers(_this, keypath);
13721  		});
13722  	}
13723
13724  	if (this.deps.observers) {
13725  		upstreamChanges.forEach(function (keypath) {
13726  			return notifyUpstreamDependants(_this, null, keypath, "observers");
13727  		});
13728  		notifyAllDependants(this, changes, "observers");
13729  	}
13730
13731  	if (this.deps["default"]) {
13732  		bindings = [];
13733  		upstreamChanges.forEach(function (keypath) {
13734  			return notifyUpstreamDependants(_this, bindings, keypath, "default");
13735  		});
13736
13737  		if (bindings.length) {
13738  			notifyBindings(this, bindings, changes);
13739  		}
13740
13741  		notifyAllDependants(this, changes, "default");
13742  	}
13743
13744  	// Return a hash of keypaths to updated values
13745  	changes.forEach(function (keypath) {
13746  		hash[keypath.str] = _this.get(keypath);
13747  	});
13748
13749  	this.implicitChanges = {};
13750  	this.noCascade = {};
13751
13752  	return hash;
13753  }
13754
13755  function notifyUpstreamDependants(viewmodel, bindings, keypath, groupName) {
13756  	var dependants, value;
13757
13758  	if (dependants = findDependants(viewmodel, keypath, groupName)) {
13759  		value = viewmodel.get(keypath);
13760
13761  		dependants.forEach(function (d) {
13762  			// don't "set" the parent value, refine it
13763  			// i.e. not data = value, but data[foo] = fooValue
13764  			if (bindings && d.refineValue) {
13765  				bindings.push(d);
13766  			} else {
13767  				d.setValue(value);
13768  			}
13769  		});
13770  	}
13771  }
13772
13773  function notifyBindings(viewmodel, bindings, changes) {
13774
13775  	bindings.forEach(function (binding) {
13776  		var useSet = false,
13777  		    i = 0,
13778  		    length = changes.length,
13779  		    refinements = [];
13780
13781  		while (i < length) {
13782  			var keypath = changes[i];
13783
13784  			if (keypath === binding.keypath) {
13785  				useSet = true;
13786  				break;
13787  			}
13788
13789  			if (keypath.slice(0, binding.keypath.length) === binding.keypath) {
13790  				refinements.push(keypath);
13791  			}
13792
13793  			i++;
13794  		}
13795
13796  		if (useSet) {
13797  			binding.setValue(viewmodel.get(binding.keypath));
13798  		}
13799
13800  		if (refinements.length) {
13801  			binding.refineValue(refinements);
13802  		}
13803  	});
13804  }
13805
13806  function notifyAllDependants(viewmodel, keypaths, groupName) {
13807  	var queue = [];
13808
13809  	addKeypaths(keypaths);
13810  	queue.forEach(dispatch);
13811
13812  	function addKeypaths(keypaths) {
13813  		keypaths.forEach(addKeypath);
13814  		keypaths.forEach(cascade);
13815  	}
13816
13817  	function addKeypath(keypath) {
13818  		var deps = findDependants(viewmodel, keypath, groupName);
13819
13820  		if (deps) {
13821  			queue.push({
13822  				keypath: keypath,
13823  				deps: deps
13824  			});
13825  		}
13826  	}
13827
13828  	function cascade(keypath) {
13829  		var childDeps;
13830
13831  		if (childDeps = viewmodel.depsMap[groupName][keypath.str]) {
13832  			addKeypaths(childDeps);
13833  		}
13834  	}
13835
13836  	function dispatch(set) {
13837  		var value = viewmodel.get(set.keypath);
13838  		set.deps.forEach(function (d) {
13839  			return d.setValue(value);
13840  		});
13841  	}
13842  }
13843
13844  function findDependants(viewmodel, keypath, groupName) {
13845  	var group = viewmodel.deps[groupName];
13846  	return group ? group[keypath.str] : null;
13847  }
13848
13849  var capture = Viewmodel$capture;
13850
13851  function Viewmodel$capture() {
13852  	this.captureGroups.push([]);
13853  }
13854
13855  var clearCache = Viewmodel$clearCache;
13856
13857  function Viewmodel$clearCache(keypath, keepExistingWrapper) {
13858  	var cacheMap, wrapper;
13859
13860  	if (!keepExistingWrapper) {
13861  		// Is there a wrapped property at this keypath?
13862  		if (wrapper = this.wrapped[keypath]) {
13863  			// Did we unwrap it?
13864  			if (wrapper.teardown() !== false) {
13865  				// Is this right?
13866  				// What's the meaning of returning false from teardown?
13867  				// Could there be a GC ramification if this is a "real" ractive.teardown()?
13868  				this.wrapped[keypath] = null;
13869  			}
13870  		}
13871  	}
13872
13873  	this.cache[keypath] = undefined;
13874
13875  	if (cacheMap = this.cacheMap[keypath]) {
13876  		while (cacheMap.length) {
13877  			this.clearCache(cacheMap.pop());
13878  		}
13879  	}
13880  }
13881
13882  var UnresolvedDependency = function (computation, ref) {
13883  	this.computation = computation;
13884  	this.viewmodel = computation.viewmodel;
13885  	this.ref = ref;
13886
13887  	// TODO this seems like a red flag!
13888  	this.root = this.viewmodel.ractive;
13889  	this.parentFragment = this.root.component && this.root.component.parentFragment;
13890  };
13891
13892  UnresolvedDependency.prototype = {
13893  	resolve: function (keypath) {
13894  		this.computation.softDeps.push(keypath);
13895  		this.computation.unresolvedDeps[keypath.str] = null;
13896  		this.viewmodel.register(keypath, this.computation, "computed");
13897  	}
13898  };
13899
13900  var Computation_UnresolvedDependency = UnresolvedDependency;
13901
13902  var Computation = function (key, signature) {
13903  	this.key = key;
13904
13905  	this.getter = signature.getter;
13906  	this.setter = signature.setter;
13907
13908  	this.hardDeps = signature.deps || [];
13909  	this.softDeps = [];
13910  	this.unresolvedDeps = {};
13911
13912  	this.depValues = {};
13913
13914  	this._dirty = this._firstRun = true;
13915  };
13916
13917  Computation.prototype = {
13918  	constructor: Computation,
13919
13920  	init: function (viewmodel) {
13921  		var _this = this;
13922
13923  		var initial;
13924
13925  		this.viewmodel = viewmodel;
13926  		this.bypass = true;
13927
13928  		initial = viewmodel.get(this.key);
13929  		viewmodel.clearCache(this.key.str);
13930
13931  		this.bypass = false;
13932
13933  		if (this.setter && initial !== undefined) {
13934  			this.set(initial);
13935  		}
13936
13937  		if (this.hardDeps) {
13938  			this.hardDeps.forEach(function (d) {
13939  				return viewmodel.register(d, _this, "computed");
13940  			});
13941  		}
13942  	},
13943
13944  	invalidate: function () {
13945  		this._dirty = true;
13946  	},
13947
13948  	get: function () {
13949  		var _this = this;
13950
13951  		var newDeps,
13952  		    dependenciesChanged,
13953  		    dependencyValuesChanged = false;
13954
13955  		if (this.getting) {
13956  			// prevent double-computation (e.g. caused by array mutation inside computation)
13957  			var msg = "The " + this.key.str + " computation indirectly called itself. This probably indicates a bug in the computation. It is commonly caused by `array.sort(...)` - if that's the case, clone the array first with `array.slice().sort(...)`";
13958  			warnOnce(msg);
13959  			return this.value;
13960  		}
13961
13962  		this.getting = true;
13963
13964  		if (this._dirty) {
13965  			// determine whether the inputs have changed, in case this depends on
13966  			// other computed values
13967  			if (this._firstRun || !this.hardDeps.length && !this.softDeps.length) {
13968  				dependencyValuesChanged = true;
13969  			} else {
13970  				[this.hardDeps, this.softDeps].forEach(function (deps) {
13971  					var keypath, value, i;
13972
13973  					if (dependencyValuesChanged) {
13974  						return;
13975  					}
13976
13977  					i = deps.length;
13978  					while (i--) {
13979  						keypath = deps[i];
13980  						value = _this.viewmodel.get(keypath);
13981
13982  						if (!isEqual(value, _this.depValues[keypath.str])) {
13983  							_this.depValues[keypath.str] = value;
13984  							dependencyValuesChanged = true;
13985
13986  							return;
13987  						}
13988  					}
13989  				});
13990  			}
13991
13992  			if (dependencyValuesChanged) {
13993  				this.viewmodel.capture();
13994
13995  				try {
13996  					this.value = this.getter();
13997  				} catch (err) {
13998  					warnIfDebug("Failed to compute \"%s\"", this.key.str);
13999  					logIfDebug(err.stack || err);
14000
14001  					this.value = void 0;
14002  				}
14003
14004  				newDeps = this.viewmodel.release();
14005  				dependenciesChanged = this.updateDependencies(newDeps);
14006
14007  				if (dependenciesChanged) {
14008  					[this.hardDeps, this.softDeps].forEach(function (deps) {
14009  						deps.forEach(function (keypath) {
14010  							_this.depValues[keypath.str] = _this.viewmodel.get(keypath);
14011  						});
14012  					});
14013  				}
14014  			}
14015
14016  			this._dirty = false;
14017  		}
14018
14019  		this.getting = this._firstRun = false;
14020  		return this.value;
14021  	},
14022
14023  	set: function (value) {
14024  		if (this.setting) {
14025  			this.value = value;
14026  			return;
14027  		}
14028
14029  		if (!this.setter) {
14030  			throw new Error("Computed properties without setters are read-only. (This may change in a future version of Ractive!)");
14031  		}
14032
14033  		this.setter(value);
14034  	},
14035
14036  	updateDependencies: function (newDeps) {
14037  		var i, oldDeps, keypath, dependenciesChanged, unresolved;
14038
14039  		oldDeps = this.softDeps;
14040
14041  		// remove dependencies that are no longer used
14042  		i = oldDeps.length;
14043  		while (i--) {
14044  			keypath = oldDeps[i];
14045
14046  			if (newDeps.indexOf(keypath) === -1) {
14047  				dependenciesChanged = true;
14048  				this.viewmodel.unregister(keypath, this, "computed");
14049  			}
14050  		}
14051
14052  		// create references for any new dependencies
14053  		i = newDeps.length;
14054  		while (i--) {
14055  			keypath = newDeps[i];
14056
14057  			if (oldDeps.indexOf(keypath) === -1 && (!this.hardDeps || this.hardDeps.indexOf(keypath) === -1)) {
14058  				dependenciesChanged = true;
14059
14060  				// if this keypath is currently unresolved, we need to mark
14061  				// it as such. TODO this is a bit muddy...
14062  				if (isUnresolved(this.viewmodel, keypath) && !this.unresolvedDeps[keypath.str]) {
14063  					unresolved = new Computation_UnresolvedDependency(this, keypath.str);
14064  					newDeps.splice(i, 1);
14065
14066  					this.unresolvedDeps[keypath.str] = unresolved;
14067  					global_runloop.addUnresolved(unresolved);
14068  				} else {
14069  					this.viewmodel.register(keypath, this, "computed");
14070  				}
14071  			}
14072  		}
14073
14074  		if (dependenciesChanged) {
14075  			this.softDeps = newDeps.slice();
14076  		}
14077
14078  		return dependenciesChanged;
14079  	}
14080  };
14081
14082  function isUnresolved(viewmodel, keypath) {
14083  	var key = keypath.firstKey;
14084
14085  	return !(key in viewmodel.data) && !(key in viewmodel.computations) && !(key in viewmodel.mappings);
14086  }
14087
14088  var Computation_Computation = Computation;
14089
14090  var compute = Viewmodel$compute;
14091  function Viewmodel$compute(key, signature) {
14092  	var computation = new Computation_Computation(key, signature);
14093
14094  	if (this.ready) {
14095  		computation.init(this);
14096  	}
14097
14098  	return this.computations[key.str] = computation;
14099  }
14100
14101  var FAILED_LOOKUP = { FAILED_LOOKUP: true };
14102
14103  var viewmodel_prototype_get = Viewmodel$get;
14104
14105  var viewmodel_prototype_get__empty = {};
14106  function Viewmodel$get(keypath, options) {
14107  	var cache = this.cache,
14108  	    value,
14109  	    computation,
14110  	    wrapped,
14111  	    captureGroup,
14112  	    keypathStr = keypath.str,
14113  	    key;
14114
14115  	options = options || viewmodel_prototype_get__empty;
14116
14117  	// capture the keypath, if we're inside a computation
14118  	if (options.capture && (captureGroup = lastItem(this.captureGroups))) {
14119  		if (! ~captureGroup.indexOf(keypath)) {
14120  			captureGroup.push(keypath);
14121  		}
14122  	}
14123
14124  	if (hasOwn.call(this.mappings, keypath.firstKey)) {
14125  		return this.mappings[keypath.firstKey].get(keypath, options);
14126  	}
14127
14128  	if (keypath.isSpecial) {
14129  		return keypath.value;
14130  	}
14131
14132  	if (cache[keypathStr] === undefined) {
14133
14134  		// Is this a computed property?
14135  		if ((computation = this.computations[keypathStr]) && !computation.bypass) {
14136  			value = computation.get();
14137  			this.adapt(keypathStr, value);
14138  		}
14139
14140  		// Is this a wrapped property?
14141  		else if (wrapped = this.wrapped[keypathStr]) {
14142  			value = wrapped.value;
14143  		}
14144
14145  		// Is it the root?
14146  		else if (keypath.isRoot) {
14147  			this.adapt("", this.data);
14148  			value = this.data;
14149  		}
14150
14151  		// No? Then we need to retrieve the value one key at a time
14152  		else {
14153  			value = retrieve(this, keypath);
14154  		}
14155
14156  		cache[keypathStr] = value;
14157  	} else {
14158  		value = cache[keypathStr];
14159  	}
14160
14161  	if (!options.noUnwrap && (wrapped = this.wrapped[keypathStr])) {
14162  		value = wrapped.get();
14163  	}
14164
14165  	if (keypath.isRoot && options.fullRootGet) {
14166  		for (key in this.mappings) {
14167  			value[key] = this.mappings[key].getValue();
14168  		}
14169  	}
14170
14171  	return value === FAILED_LOOKUP ? void 0 : value;
14172  }
14173
14174  function retrieve(viewmodel, keypath) {
14175
14176  	var parentValue, cacheMap, value, wrapped;
14177
14178  	parentValue = viewmodel.get(keypath.parent);
14179
14180  	if (wrapped = viewmodel.wrapped[keypath.parent.str]) {
14181  		parentValue = wrapped.get();
14182  	}
14183
14184  	if (parentValue === null || parentValue === undefined) {
14185  		return;
14186  	}
14187
14188  	// update cache map
14189  	if (!(cacheMap = viewmodel.cacheMap[keypath.parent.str])) {
14190  		viewmodel.cacheMap[keypath.parent.str] = [keypath.str];
14191  	} else {
14192  		if (cacheMap.indexOf(keypath.str) === -1) {
14193  			cacheMap.push(keypath.str);
14194  		}
14195  	}
14196
14197  	// If this property doesn't exist, we return a sentinel value
14198  	// so that we know to query parent scope (if such there be)
14199  	if (typeof parentValue === "object" && !(keypath.lastKey in parentValue)) {
14200  		return viewmodel.cache[keypath.str] = FAILED_LOOKUP;
14201  	}
14202
14203  	value = parentValue[keypath.lastKey];
14204
14205  	// Do we have an adaptor for this value?
14206  	viewmodel.adapt(keypath.str, value, false);
14207
14208  	// Update cache
14209  	viewmodel.cache[keypath.str] = value;
14210  	return value;
14211  }
14212
14213  var viewmodel_prototype_init = Viewmodel$init;
14214
14215  function Viewmodel$init() {
14216  	var key;
14217
14218  	for (key in this.computations) {
14219  		this.computations[key].init(this);
14220  	}
14221  }
14222
14223  var prototype_map = Viewmodel$map;
14224
14225  function Viewmodel$map(key, options) {
14226  	var mapping = this.mappings[key.str] = new Mapping(key, options);
14227  	mapping.initViewmodel(this);
14228  	return mapping;
14229  }
14230
14231  var Mapping = function (localKey, options) {
14232  	this.localKey = localKey;
14233  	this.keypath = options.keypath;
14234  	this.origin = options.origin;
14235
14236  	this.deps = [];
14237  	this.unresolved = [];
14238
14239  	this.resolved = false;
14240  };
14241
14242  Mapping.prototype = {
14243  	forceResolution: function () {
14244  		// TODO warn, as per #1692?
14245  		this.keypath = this.localKey;
14246  		this.setup();
14247  	},
14248
14249  	get: function (keypath, options) {
14250  		if (!this.resolved) {
14251  			return undefined;
14252  		}
14253  		return this.origin.get(this.map(keypath), options);
14254  	},
14255
14256  	getValue: function () {
14257  		if (!this.keypath) {
14258  			return undefined;
14259  		}
14260  		return this.origin.get(this.keypath);
14261  	},
14262
14263  	initViewmodel: function (viewmodel) {
14264  		this.local = viewmodel;
14265  		this.setup();
14266  	},
14267
14268  	map: function (keypath) {
14269  		if (typeof this.keypath === undefined) {
14270  			return this.localKey;
14271  		}
14272  		return keypath.replace(this.localKey, this.keypath);
14273  	},
14274
14275  	register: function (keypath, dependant, group) {
14276  		this.deps.push({ keypath: keypath, dep: dependant, group: group });
14277
14278  		if (this.resolved) {
14279  			this.origin.register(this.map(keypath), dependant, group);
14280  		}
14281  	},
14282
14283  	resolve: function (keypath) {
14284  		if (this.keypath !== undefined) {
14285  			this.unbind(true);
14286  		}
14287
14288  		this.keypath = keypath;
14289  		this.setup();
14290  	},
14291
14292  	set: function (keypath, value) {
14293  		if (!this.resolved) {
14294  			this.forceResolution();
14295  		}
14296
14297  		this.origin.set(this.map(keypath), value);
14298  	},
14299
14300  	setup: function () {
14301  		var _this = this;
14302
14303  		if (this.keypath === undefined) {
14304  			return;
14305  		}
14306
14307  		this.resolved = true;
14308
14309  		// accumulated dependants can now be registered
14310  		if (this.deps.length) {
14311  			this.deps.forEach(function (d) {
14312  				var keypath = _this.map(d.keypath);
14313  				_this.origin.register(keypath, d.dep, d.group);
14314
14315  				// TODO this is a bit of a red flag... all deps should be the same?
14316  				if (d.dep.setValue) {
14317  					d.dep.setValue(_this.origin.get(keypath));
14318  				} else if (d.dep.invalidate) {
14319  					d.dep.invalidate();
14320  				} else {
14321  					throw new Error("An unexpected error occurred. Please raise an issue at https://github.com/ractivejs/ractive/issues - thanks!");
14322  				}
14323  			});
14324
14325  			this.origin.mark(this.keypath);
14326  		}
14327  	},
14328
14329  	setValue: function (value) {
14330  		if (!this.keypath) {
14331  			throw new Error("Mapping does not have keypath, cannot set value. Please raise an issue at https://github.com/ractivejs/ractive/issues - thanks!");
14332  		}
14333
14334  		this.origin.set(this.keypath, value);
14335  	},
14336
14337  	unbind: function (keepLocal) {
14338  		var _this = this;
14339
14340  		if (!keepLocal) {
14341  			delete this.local.mappings[this.localKey];
14342  		}
14343
14344  		if (!this.resolved) {
14345  			return;
14346  		}
14347
14348  		this.deps.forEach(function (d) {
14349  			_this.origin.unregister(_this.map(d.keypath), d.dep, d.group);
14350  		});
14351
14352  		if (this.tracker) {
14353  			this.origin.unregister(this.keypath, this.tracker);
14354  		}
14355  	},
14356
14357  	unregister: function (keypath, dependant, group) {
14358  		var deps, i;
14359
14360  		if (!this.resolved) {
14361  			return;
14362  		}
14363
14364  		deps = this.deps;
14365  		i = deps.length;
14366
14367  		while (i--) {
14368  			if (deps[i].dep === dependant) {
14369  				deps.splice(i, 1);
14370  				break;
14371  			}
14372  		}
14373  		this.origin.unregister(this.map(keypath), dependant, group);
14374  	}
14375  };
14376
14377  var mark = Viewmodel$mark;
14378
14379  function Viewmodel$mark(keypath, options) {
14380  	var computation,
14381  	    keypathStr = keypath.str;
14382
14383  	// implicit changes (i.e. `foo.length` on `ractive.push('foo',42)`)
14384  	// should not be picked up by pattern observers
14385  	if (options) {
14386  		if (options.implicit) {
14387  			this.implicitChanges[keypathStr] = true;
14388  		}
14389  		if (options.noCascade) {
14390  			this.noCascade[keypathStr] = true;
14391  		}
14392  	}
14393
14394  	if (computation = this.computations[keypathStr]) {
14395  		computation.invalidate();
14396  	}
14397
14398  	if (this.changes.indexOf(keypath) === -1) {
14399  		this.changes.push(keypath);
14400  	}
14401
14402  	// pass on keepExistingWrapper, if we can
14403  	var keepExistingWrapper = options ? options.keepExistingWrapper : false;
14404
14405  	this.clearCache(keypathStr, keepExistingWrapper);
14406
14407  	if (this.ready) {
14408  		this.onchange();
14409  	}
14410  }
14411
14412  var mapOldToNewIndex = function (oldArray, newArray) {
14413  	var usedIndices, firstUnusedIndex, newIndices, changed;
14414
14415  	usedIndices = {};
14416  	firstUnusedIndex = 0;
14417
14418  	newIndices = oldArray.map(function (item, i) {
14419  		var index, start, len;
14420
14421  		start = firstUnusedIndex;
14422  		len = newArray.length;
14423
14424  		do {
14425  			index = newArray.indexOf(item, start);
14426
14427  			if (index === -1) {
14428  				changed = true;
14429  				return -1;
14430  			}
14431
14432  			start = index + 1;
14433  		} while (usedIndices[index] && start < len);
14434
14435  		// keep track of the first unused index, so we don't search
14436  		// the whole of newArray for each item in oldArray unnecessarily
14437  		if (index === firstUnusedIndex) {
14438  			firstUnusedIndex += 1;
14439  		}
14440
14441  		if (index !== i) {
14442  			changed = true;
14443  		}
14444
14445  		usedIndices[index] = true;
14446  		return index;
14447  	});
14448
14449  	return newIndices;
14450  };
14451
14452  var merge = Viewmodel$merge;
14453
14454  var comparators = {};
14455  function Viewmodel$merge(keypath, currentArray, array, options) {
14456  	var oldArray, newArray, comparator, newIndices;
14457
14458  	this.mark(keypath);
14459
14460  	if (options && options.compare) {
14461
14462  		comparator = getComparatorFunction(options.compare);
14463
14464  		try {
14465  			oldArray = currentArray.map(comparator);
14466  			newArray = array.map(comparator);
14467  		} catch (err) {
14468  			// fallback to an identity check - worst case scenario we have
14469  			// to do more DOM manipulation than we thought...
14470  			warnIfDebug("merge(): \"%s\" comparison failed. Falling back to identity checking", keypath);
14471
14472  			oldArray = currentArray;
14473  			newArray = array;
14474  		}
14475  	} else {
14476  		oldArray = currentArray;
14477  		newArray = array;
14478  	}
14479
14480  	// find new indices for members of oldArray
14481  	newIndices = mapOldToNewIndex(oldArray, newArray);
14482
14483  	this.smartUpdate(keypath, array, newIndices, currentArray.length !== array.length);
14484  }
14485
14486  function stringify(item) {
14487  	return JSON.stringify(item);
14488  }
14489
14490  function getComparatorFunction(comparator) {
14491  	// If `compare` is `true`, we use JSON.stringify to compare
14492  	// objects that are the same shape, but non-identical - i.e.
14493  	// { foo: 'bar' } !== { foo: 'bar' }
14494  	if (comparator === true) {
14495  		return stringify;
14496  	}
14497
14498  	if (typeof comparator === "string") {
14499  		if (!comparators[comparator]) {
14500  			comparators[comparator] = function (item) {
14501  				return item[comparator];
14502  			};
14503  		}
14504
14505  		return comparators[comparator];
14506  	}
14507
14508  	if (typeof comparator === "function") {
14509  		return comparator;
14510  	}
14511
14512  	throw new Error("The `compare` option must be a function, or a string representing an identifying field (or `true` to use JSON.stringify)");
14513  }
14514
14515  var register = Viewmodel$register;
14516
14517  function Viewmodel$register(keypath, dependant) {
14518  	var group = arguments[2] === undefined ? "default" : arguments[2];
14519
14520  	var mapping, depsByKeypath, deps;
14521
14522  	if (dependant.isStatic) {
14523  		return; // TODO we should never get here if a dependant is static...
14524  	}
14525
14526  	if (mapping = this.mappings[keypath.firstKey]) {
14527  		mapping.register(keypath, dependant, group);
14528  	} else {
14529  		depsByKeypath = this.deps[group] || (this.deps[group] = {});
14530  		deps = depsByKeypath[keypath.str] || (depsByKeypath[keypath.str] = []);
14531
14532  		deps.push(dependant);
14533
14534  		if (!this.depsMap[group]) {
14535  			this.depsMap[group] = {};
14536  		}
14537
14538  		if (!keypath.isRoot) {
14539  			register__updateDependantsMap(this, keypath, group);
14540  		}
14541  	}
14542  }
14543
14544  function register__updateDependantsMap(viewmodel, keypath, group) {
14545  	var map, parent, keypathStr;
14546
14547  	// update dependants map
14548  	while (!keypath.isRoot) {
14549  		map = viewmodel.depsMap[group];
14550  		parent = map[keypath.parent.str] || (map[keypath.parent.str] = []);
14551
14552  		keypathStr = keypath.str;
14553
14554  		// TODO find an alternative to this nasty approach
14555  		if (parent["_" + keypathStr] === undefined) {
14556  			parent["_" + keypathStr] = 0;
14557  			parent.push(keypath);
14558  		}
14559
14560  		parent["_" + keypathStr] += 1;
14561  		keypath = keypath.parent;
14562  	}
14563  }
14564
14565  var release = Viewmodel$release;
14566
14567  function Viewmodel$release() {
14568  	return this.captureGroups.pop();
14569  }
14570
14571  var reset = Viewmodel$reset;
14572
14573  function Viewmodel$reset(data) {
14574  	this.data = data;
14575  	this.clearCache("");
14576  }
14577
14578  var prototype_set = Viewmodel$set;
14579
14580  function Viewmodel$set(keypath, value) {
14581  	var options = arguments[2] === undefined ? {} : arguments[2];
14582
14583  	var mapping, computation, wrapper, keepExistingWrapper;
14584
14585  	// unless data is being set for data tracking purposes
14586  	if (!options.noMapping) {
14587  		// If this data belongs to a different viewmodel,
14588  		// pass the change along
14589  		if (mapping = this.mappings[keypath.firstKey]) {
14590  			return mapping.set(keypath, value);
14591  		}
14592  	}
14593
14594  	computation = this.computations[keypath.str];
14595  	if (computation) {
14596  		if (computation.setting) {
14597  			// let the other computation set() handle things...
14598  			return;
14599  		}
14600  		computation.set(value);
14601  		value = computation.get();
14602  	}
14603
14604  	if (isEqual(this.cache[keypath.str], value)) {
14605  		return;
14606  	}
14607
14608  	wrapper = this.wrapped[keypath.str];
14609
14610  	// If we have a wrapper with a `reset()` method, we try and use it. If the
14611  	// `reset()` method returns false, the wrapper should be torn down, and
14612  	// (most likely) a new one should be created later
14613  	if (wrapper && wrapper.reset) {
14614  		keepExistingWrapper = wrapper.reset(value) !== false;
14615
14616  		if (keepExistingWrapper) {
14617  			value = wrapper.get();
14618  		}
14619  	}
14620
14621  	if (!computation && !keepExistingWrapper) {
14622  		resolveSet(this, keypath, value);
14623  	}
14624
14625  	if (!options.silent) {
14626  		this.mark(keypath);
14627  	} else {
14628  		// We're setting a parent of the original target keypath (i.e.
14629  		// creating a fresh branch) - we need to clear the cache, but
14630  		// not mark it as a change
14631  		this.clearCache(keypath.str);
14632  	}
14633  }
14634
14635  function resolveSet(viewmodel, keypath, value) {
14636  	var wrapper, parentValue, wrapperSet, valueSet;
14637
14638  	wrapperSet = function () {
14639  		if (wrapper.set) {
14640  			wrapper.set(keypath.lastKey, value);
14641  		} else {
14642  			parentValue = wrapper.get();
14643  			valueSet();
14644  		}
14645  	};
14646
14647  	valueSet = function () {
14648  		if (!parentValue) {
14649  			parentValue = createBranch(keypath.lastKey);
14650  			viewmodel.set(keypath.parent, parentValue, { silent: true });
14651  		}
14652  		parentValue[keypath.lastKey] = value;
14653  	};
14654
14655  	wrapper = viewmodel.wrapped[keypath.parent.str];
14656
14657  	if (wrapper) {
14658  		wrapperSet();
14659  	} else {
14660  		parentValue = viewmodel.get(keypath.parent);
14661
14662  		// may have been wrapped via the above .get()
14663  		// call on viewmodel if this is first access via .set()!
14664  		if (wrapper = viewmodel.wrapped[keypath.parent.str]) {
14665  			wrapperSet();
14666  		} else {
14667  			valueSet();
14668  		}
14669  	}
14670  }
14671
14672  var smartUpdate = Viewmodel$smartUpdate;
14673
14674  var implicitOption = { implicit: true },
14675      noCascadeOption = { noCascade: true };
14676  function Viewmodel$smartUpdate(keypath, array, newIndices) {
14677  	var _this = this;
14678
14679  	var dependants, oldLength, i;
14680
14681  	oldLength = newIndices.length;
14682
14683  	// Indices that are being removed should be marked as dirty
14684  	newIndices.forEach(function (newIndex, oldIndex) {
14685  		if (newIndex === -1) {
14686  			_this.mark(keypath.join(oldIndex), noCascadeOption);
14687  		}
14688  	});
14689
14690  	// Update the model
14691  	// TODO allow existing array to be updated in place, rather than replaced?
14692  	this.set(keypath, array, { silent: true });
14693
14694  	if (dependants = this.deps["default"][keypath.str]) {
14695  		dependants.filter(canShuffle).forEach(function (d) {
14696  			return d.shuffle(newIndices, array);
14697  		});
14698  	}
14699
14700  	if (oldLength !== array.length) {
14701  		this.mark(keypath.join("length"), implicitOption);
14702
14703  		for (i = newIndices.touchedFrom; i < array.length; i += 1) {
14704  			this.mark(keypath.join(i));
14705  		}
14706
14707  		// don't allow removed indexes beyond end of new array to trigger recomputations
14708  		// TODO is this still necessary, now that computations are lazy?
14709  		for (i = array.length; i < oldLength; i += 1) {
14710  			this.mark(keypath.join(i), noCascadeOption);
14711  		}
14712  	}
14713  }
14714
14715  function canShuffle(dependant) {
14716  	return typeof dependant.shuffle === "function";
14717  }
14718
14719  var prototype_teardown = Viewmodel$teardown;
14720
14721  function Viewmodel$teardown() {
14722  	var _this = this;
14723
14724  	var unresolvedImplicitDependency;
14725
14726  	// Clear entire cache - this has the desired side-effect
14727  	// of unwrapping adapted values (e.g. arrays)
14728  	Object.keys(this.cache).forEach(function (keypath) {
14729  		return _this.clearCache(keypath);
14730  	});
14731
14732  	// Teardown any failed lookups - we don't need them to resolve any more
14733  	while (unresolvedImplicitDependency = this.unresolvedImplicitDependencies.pop()) {
14734  		unresolvedImplicitDependency.teardown();
14735  	}
14736  }
14737
14738  var unregister = Viewmodel$unregister;
14739
14740  function Viewmodel$unregister(keypath, dependant) {
14741  	var group = arguments[2] === undefined ? "default" : arguments[2];
14742
14743  	var mapping, deps, index;
14744
14745  	if (dependant.isStatic) {
14746  		return;
14747  	}
14748
14749  	if (mapping = this.mappings[keypath.firstKey]) {
14750  		return mapping.unregister(keypath, dependant, group);
14751  	}
14752
14753  	deps = this.deps[group][keypath.str];
14754  	index = deps.indexOf(dependant);
14755
14756  	if (index === -1) {
14757  		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");
14758  	}
14759
14760  	deps.splice(index, 1);
14761
14762  	if (keypath.isRoot) {
14763  		return;
14764  	}
14765
14766  	unregister__updateDependantsMap(this, keypath, group);
14767  }
14768
14769  function unregister__updateDependantsMap(viewmodel, keypath, group) {
14770  	var map, parent;
14771
14772  	// update dependants map
14773  	while (!keypath.isRoot) {
14774  		map = viewmodel.depsMap[group];
14775  		parent = map[keypath.parent.str];
14776
14777  		parent["_" + keypath.str] -= 1;
14778
14779  		if (!parent["_" + keypath.str]) {
14780  			// remove from parent deps map
14781  			removeFromArray(parent, keypath);
14782  			parent["_" + keypath.str] = undefined;
14783  		}
14784
14785  		keypath = keypath.parent;
14786  	}
14787  }
14788
14789  var Viewmodel = function (options) {
14790  	var adapt = options.adapt;
14791  	var data = options.data;
14792  	var ractive = options.ractive;
14793  	var computed = options.computed;
14794  	var mappings = options.mappings;
14795  	var key;
14796  	var mapping;
14797
14798  	// TODO is it possible to remove this reference?
14799  	this.ractive = ractive;
14800
14801  	this.adaptors = adapt;
14802  	this.onchange = options.onchange;
14803
14804  	this.cache = {}; // we need to be able to use hasOwnProperty, so can't inherit from null
14805  	this.cacheMap = create(null);
14806
14807  	this.deps = {
14808  		computed: create(null),
14809  		"default": create(null)
14810  	};
14811  	this.depsMap = {
14812  		computed: create(null),
14813  		"default": create(null)
14814  	};
14815
14816  	this.patternObservers = [];
14817
14818  	this.specials = create(null);
14819
14820  	this.wrapped = create(null);
14821  	this.computations = create(null);
14822
14823  	this.captureGroups = [];
14824  	this.unresolvedImplicitDependencies = [];
14825
14826  	this.changes = [];
14827  	this.implicitChanges = {};
14828  	this.noCascade = {};
14829
14830  	this.data = data;
14831
14832  	// set up explicit mappings
14833  	this.mappings = create(null);
14834  	for (key in mappings) {
14835  		this.map(getKeypath(key), mappings[key]);
14836  	}
14837
14838  	if (data) {
14839  		// if data exists locally, but is missing on the parent,
14840  		// we transfer ownership to the parent
14841  		for (key in data) {
14842  			if ((mapping = this.mappings[key]) && mapping.getValue() === undefined) {
14843  				mapping.setValue(data[key]);
14844  			}
14845  		}
14846  	}
14847
14848  	for (key in computed) {
14849  		if (mappings && key in mappings) {
14850  			fatal("Cannot map to a computed property ('%s')", key);
14851  		}
14852
14853  		this.compute(getKeypath(key), computed[key]);
14854  	}
14855
14856  	this.ready = true;
14857  };
14858
14859  Viewmodel.prototype = {
14860  	adapt: prototype_adapt,
14861  	applyChanges: applyChanges,
14862  	capture: capture,
14863  	clearCache: clearCache,
14864  	compute: compute,
14865  	get: viewmodel_prototype_get,
14866  	init: viewmodel_prototype_init,
14867  	map: prototype_map,
14868  	mark: mark,
14869  	merge: merge,
14870  	register: register,
14871  	release: release,
14872  	reset: reset,
14873  	set: prototype_set,
14874  	smartUpdate: smartUpdate,
14875  	teardown: prototype_teardown,
14876  	unregister: unregister
14877  };
14878
14879  var viewmodel_Viewmodel = Viewmodel;
14880
14881  function HookQueue(event) {
14882  	this.hook = new hooks_Hook(event);
14883  	this.inProcess = {};
14884  	this.queue = {};
14885  }
14886
14887  HookQueue.prototype = {
14888
14889  	constructor: HookQueue,
14890
14891  	begin: function (ractive) {
14892  		this.inProcess[ractive._guid] = true;
14893  	},
14894
14895  	end: function (ractive) {
14896
14897  		var parent = ractive.parent;
14898
14899  		// If this is *isn't* a child of a component that's in process,
14900  		// it should call methods or fire at this point
14901  		if (!parent || !this.inProcess[parent._guid]) {
14902  			fire(this, ractive);
14903  		}
14904  		// elsewise, handoff to parent to fire when ready
14905  		else {
14906  			getChildQueue(this.queue, parent).push(ractive);
14907  		}
14908
14909  		delete this.inProcess[ractive._guid];
14910  	}
14911  };
14912
14913  function getChildQueue(queue, ractive) {
14914  	return queue[ractive._guid] || (queue[ractive._guid] = []);
14915  }
14916
14917  function fire(hookQueue, ractive) {
14918
14919  	var childQueue = getChildQueue(hookQueue.queue, ractive);
14920
14921  	hookQueue.hook.fire(ractive);
14922
14923  	// queue is "live" because components can end up being
14924  	// added while hooks fire on parents that modify data values.
14925  	while (childQueue.length) {
14926  		fire(hookQueue, childQueue.shift());
14927  	}
14928
14929  	delete hookQueue.queue[ractive._guid];
14930  }
14931
14932  var hooks_HookQueue = HookQueue;
14933
14934  var helpers_getComputationSignatures = getComputationSignatures;
14935
14936  var helpers_getComputationSignatures__pattern = /\$\{([^\}]+)\}/g;
14937  function getComputationSignatures(ractive, computed) {
14938  	var signatures = {},
14939  	    key;
14940
14941  	for (key in computed) {
14942  		signatures[key] = getComputationSignature(ractive, key, computed[key]);
14943  	}
14944
14945  	return signatures;
14946  }
14947
14948  function getComputationSignature(ractive, key, signature) {
14949  	var getter, setter;
14950
14951  	if (typeof signature === "function") {
14952  		getter = helpers_getComputationSignatures__bind(signature, ractive);
14953  	}
14954
14955  	if (typeof signature === "string") {
14956  		getter = createFunctionFromString(ractive, signature);
14957  	}
14958
14959  	if (typeof signature === "object") {
14960  		if (typeof signature.get === "string") {
14961  			getter = createFunctionFromString(ractive, signature.get);
14962  		} else if (typeof signature.get === "function") {
14963  			getter = helpers_getComputationSignatures__bind(signature.get, ractive);
14964  		} else {
14965  			fatal("`%s` computation must have a `get()` method", key);
14966  		}
14967
14968  		if (typeof signature.set === "function") {
14969  			setter = helpers_getComputationSignatures__bind(signature.set, ractive);
14970  		}
14971  	}
14972
14973  	return { getter: getter, setter: setter };
14974  }
14975
14976  function createFunctionFromString(ractive, str) {
14977  	var functionBody, hasThis, fn;
14978
14979  	functionBody = "return (" + str.replace(helpers_getComputationSignatures__pattern, function (match, keypath) {
14980  		hasThis = true;
14981  		return "__ractive.get(\"" + keypath + "\")";
14982  	}) + ");";
14983
14984  	if (hasThis) {
14985  		functionBody = "var __ractive = this; " + functionBody;
14986  	}
14987
14988  	fn = new Function(functionBody);
14989  	return hasThis ? fn.bind(ractive) : fn;
14990  }
14991
14992  function helpers_getComputationSignatures__bind(fn, context) {
14993  	return /this/.test(fn.toString()) ? fn.bind(context) : fn;
14994  }
14995
14996  var constructHook = new hooks_Hook("construct");
14997  var configHook = new hooks_Hook("config");
14998  var initHook = new hooks_HookQueue("init");
14999  var initialise__uid = 0;
15000
15001  var initialise__registryNames = ["adaptors", "components", "decorators", "easing", "events", "interpolators", "partials", "transitions"];
15002
15003  var initialise = initialiseRactiveInstance;
15004
15005  function initialiseRactiveInstance(ractive) {
15006  	var userOptions = arguments[1] === undefined ? {} : arguments[1];
15007  	var options = arguments[2] === undefined ? {} : arguments[2];
15008
15009  	var el, viewmodel;
15010
15011  	if (_Ractive.DEBUG) {
15012  		welcome();
15013  	}
15014
15015  	initialiseProperties(ractive, options);
15016
15017  	// TODO remove this, eventually
15018  	defineProperty(ractive, "data", { get: deprecateRactiveData });
15019
15020  	// TODO don't allow `onconstruct` with `new Ractive()`, there's no need for it
15021  	constructHook.fire(ractive, userOptions);
15022
15023  	// Add registries
15024  	initialise__registryNames.forEach(function (name) {
15025  		ractive[name] = utils_object__extend(create(ractive.constructor[name] || null), userOptions[name]);
15026  	});
15027
15028  	// Create a viewmodel
15029  	viewmodel = new viewmodel_Viewmodel({
15030  		adapt: getAdaptors(ractive, ractive.adapt, userOptions),
15031  		data: custom_data.init(ractive.constructor, ractive, userOptions),
15032  		computed: helpers_getComputationSignatures(ractive, utils_object__extend(create(ractive.constructor.prototype.computed), userOptions.computed)),
15033  		mappings: options.mappings,
15034  		ractive: ractive,
15035  		onchange: function () {
15036  			return global_runloop.addRactive(ractive);
15037  		}
15038  	});
15039
15040  	ractive.viewmodel = viewmodel;
15041
15042  	// This can't happen earlier, because computed properties may call `ractive.get()`, etc
15043  	viewmodel.init();
15044
15045  	// init config from Parent and options
15046  	config_config.init(ractive.constructor, ractive, userOptions);
15047
15048  	configHook.fire(ractive);
15049  	initHook.begin(ractive);
15050
15051  	// // If this is a component with a function `data` property, call the function
15052  	// // with `ractive` as context (unless the child was also a function)
15053  	// if ( typeof ractive.constructor.prototype.data === 'function' && typeof userOptions.data !== 'function' ) {
15054  	// 	viewmodel.reset( ractive.constructor.prototype.data.call( ractive ) || fatal( '`data` functions must return a data object' ) );
15055  	// }
15056
15057  	// Render virtual DOM
15058  	if (ractive.template) {
15059  		var cssIds = undefined;
15060
15061  		if (options.cssIds || ractive.cssId) {
15062  			cssIds = options.cssIds ? options.cssIds.slice() : [];
15063
15064  			if (ractive.cssId) {
15065  				cssIds.push(ractive.cssId);
15066  			}
15067  		}
15068
15069  		ractive.fragment = new virtualdom_Fragment({
15070  			template: ractive.template,
15071  			root: ractive,
15072  			owner: ractive, // saves doing `if ( this.parent ) { /*...*/ }` later on
15073  			cssIds: cssIds
15074  		});
15075  	}
15076
15077  	initHook.end(ractive);
15078
15079  	// render automatically ( if `el` is specified )
15080  	if (el = getElement(ractive.el)) {
15081  		var promise = ractive.render(el, ractive.append);
15082
15083  		if (_Ractive.DEBUG_PROMISES) {
15084  			promise["catch"](function (err) {
15085  				warnOnceIfDebug("Promise debugging is enabled, to help solve errors that happen asynchronously. Some browsers will log unhandled promise rejections, in which case you can safely disable promise debugging:\n  Ractive.DEBUG_PROMISES = false;");
15086  				warnIfDebug("An error happened during rendering", { ractive: ractive });
15087  				err.stack && logIfDebug(err.stack);
15088
15089  				throw err;
15090  			});
15091  		}
15092  	}
15093  }
15094
15095  function getAdaptors(ractive, protoAdapt, userOptions) {
15096  	var adapt, magic, modifyArrays;
15097
15098  	protoAdapt = protoAdapt.map(lookup);
15099  	adapt = ensureArray(userOptions.adapt).map(lookup);
15100
15101  	adapt = initialise__combine(protoAdapt, adapt);
15102
15103  	magic = "magic" in userOptions ? userOptions.magic : ractive.magic;
15104  	modifyArrays = "modifyArrays" in userOptions ? userOptions.modifyArrays : ractive.modifyArrays;
15105
15106  	if (magic) {
15107  		if (!environment__magic) {
15108  			throw new Error("Getters and setters (magic mode) are not supported in this browser");
15109  		}
15110
15111  		if (modifyArrays) {
15112  			adapt.push(magicArray);
15113  		}
15114
15115  		adapt.push(adaptors_magic);
15116  	}
15117
15118  	if (modifyArrays) {
15119  		adapt.push(array_index);
15120  	}
15121
15122  	return adapt;
15123
15124  	function lookup(adaptor) {
15125  		if (typeof adaptor === "string") {
15126  			adaptor = findInViewHierarchy("adaptors", ractive, adaptor);
15127
15128  			if (!adaptor) {
15129  				fatal(missingPlugin(adaptor, "adaptor"));
15130  			}
15131  		}
15132
15133  		return adaptor;
15134  	}
15135  }
15136
15137  function initialise__combine(a, b) {
15138  	var c = a.slice(),
15139  	    i = b.length;
15140
15141  	while (i--) {
15142  		if (! ~c.indexOf(b[i])) {
15143  			c.push(b[i]);
15144  		}
15145  	}
15146
15147  	return c;
15148  }
15149
15150  function initialiseProperties(ractive, options) {
15151  	// Generate a unique identifier, for places where you'd use a weak map if it
15152  	// existed
15153  	ractive._guid = "r-" + initialise__uid++;
15154
15155  	// events
15156  	ractive._subs = create(null);
15157
15158  	// storage for item configuration from instantiation to reset,
15159  	// like dynamic functions or original values
15160  	ractive._config = {};
15161
15162  	// two-way bindings
15163  	ractive._twowayBindings = create(null);
15164
15165  	// animations (so we can stop any in progress at teardown)
15166  	ractive._animations = [];
15167
15168  	// nodes registry
15169  	ractive.nodes = {};
15170
15171  	// live queries
15172  	ractive._liveQueries = [];
15173  	ractive._liveComponentQueries = [];
15174
15175  	// bound data functions
15176  	ractive._boundFunctions = [];
15177
15178  	// observers
15179  	ractive._observers = [];
15180
15181  	// properties specific to inline components
15182  	if (options.component) {
15183  		ractive.parent = options.parent;
15184  		ractive.container = options.container || null;
15185  		ractive.root = ractive.parent.root;
15186
15187  		ractive.component = options.component;
15188  		options.component.instance = ractive;
15189
15190  		// for hackability, this could be an open option
15191  		// for any ractive instance, but for now, just
15192  		// for components and just for ractive...
15193  		ractive._inlinePartials = options.inlinePartials;
15194  	} else {
15195  		ractive.root = ractive;
15196  		ractive.parent = ractive.container = null;
15197  	}
15198  }
15199
15200  function deprecateRactiveData() {
15201  	throw new Error("Using `ractive.data` is no longer supported - you must use the `ractive.get()` API instead");
15202  }
15203
15204  function ComplexParameter(component, template, callback) {
15205  	this.parentFragment = component.parentFragment;
15206  	this.callback = callback;
15207
15208  	this.fragment = new virtualdom_Fragment({
15209  		template: template,
15210  		root: component.root,
15211  		owner: this
15212  	});
15213
15214  	this.update();
15215  }
15216
15217  var initialise_ComplexParameter = ComplexParameter;
15218
15219  ComplexParameter.prototype = {
15220  	bubble: function () {
15221  		if (!this.dirty) {
15222  			this.dirty = true;
15223  			global_runloop.addView(this);
15224  		}
15225  	},
15226
15227  	update: function () {
15228  		this.callback(this.fragment.getValue());
15229  		this.dirty = false;
15230  	},
15231
15232  	rebind: function (oldKeypath, newKeypath) {
15233  		this.fragment.rebind(oldKeypath, newKeypath);
15234  	},
15235
15236  	unbind: function () {
15237  		this.fragment.unbind();
15238  	}
15239  };
15240
15241  var createInstance = function (component, Component, attributes, yieldTemplate, partials) {
15242  	var instance,
15243  	    parentFragment,
15244  	    ractive,
15245  	    fragment,
15246  	    container,
15247  	    inlinePartials = {},
15248  	    data = {},
15249  	    mappings = {},
15250  	    ready,
15251  	    resolvers = [];
15252
15253  	parentFragment = component.parentFragment;
15254  	ractive = component.root;
15255
15256  	partials = partials || {};
15257  	utils_object__extend(inlinePartials, partials);
15258
15259  	// Make contents available as a {{>content}} partial
15260  	partials.content = yieldTemplate || [];
15261
15262  	// set a default partial for yields with no name
15263  	inlinePartials[""] = partials.content;
15264
15265  	if (Component.defaults.el) {
15266  		warnIfDebug("The <%s/> component has a default `el` property; it has been disregarded", component.name);
15267  	}
15268
15269  	// find container
15270  	fragment = parentFragment;
15271  	while (fragment) {
15272  		if (fragment.owner.type === YIELDER) {
15273  			container = fragment.owner.container;
15274  			break;
15275  		}
15276
15277  		fragment = fragment.parent;
15278  	}
15279
15280  	// each attribute represents either a) data or b) a mapping
15281  	if (attributes) {
15282  		Object.keys(attributes).forEach(function (key) {
15283  			var attribute = attributes[key],
15284  			    parsed,
15285  			    resolver;
15286
15287  			if (typeof attribute === "string") {
15288  				// it's static data
15289  				parsed = parseJSON(attribute);
15290  				data[key] = parsed ? parsed.value : attribute;
15291  			} else if (attribute === 0) {
15292  				// it had no '=', so we'll call it true
15293  				data[key] = true;
15294  			} else if (isArray(attribute)) {
15295  				// this represents dynamic data
15296  				if (isSingleInterpolator(attribute)) {
15297  					mappings[key] = {
15298  						origin: component.root.viewmodel,
15299  						keypath: undefined
15300  					};
15301
15302  					resolver = createResolver(component, attribute[0], function (keypath) {
15303  						if (keypath.isSpecial) {
15304  							if (ready) {
15305  								instance.set(key, keypath.value); // TODO use viewmodel?
15306  							} else {
15307  								data[key] = keypath.value;
15308
15309  								// TODO errr.... would be better if we didn't have to do this
15310  								delete mappings[key];
15311  							}
15312  						} else {
15313  							if (ready) {
15314  								instance.viewmodel.mappings[key].resolve(keypath);
15315  							} else {
15316  								// resolved immediately
15317  								mappings[key].keypath = keypath;
15318  							}
15319  						}
15320  					});
15321  				} else {
15322  					resolver = new initialise_ComplexParameter(component, attribute, function (value) {
15323  						if (ready) {
15324  							instance.set(key, value); // TODO use viewmodel?
15325  						} else {
15326  							data[key] = value;
15327  						}
15328  					});
15329  				}
15330
15331  				resolvers.push(resolver);
15332  			} else {
15333  				throw new Error("erm wut");
15334  			}
15335  		});
15336  	}
15337
15338  	instance = create(Component.prototype);
15339
15340  	initialise(instance, {
15341  		el: null,
15342  		append: true,
15343  		data: data,
15344  		partials: partials,
15345  		magic: ractive.magic || Component.defaults.magic,
15346  		modifyArrays: ractive.modifyArrays,
15347  		// need to inherit runtime parent adaptors
15348  		adapt: ractive.adapt
15349  	}, {
15350  		parent: ractive,
15351  		component: component,
15352  		container: container,
15353  		mappings: mappings,
15354  		inlinePartials: inlinePartials,
15355  		cssIds: parentFragment.cssIds
15356  	});
15357
15358  	ready = true;
15359  	component.resolvers = resolvers;
15360
15361  	return instance;
15362  };
15363
15364  function createResolver(component, template, callback) {
15365  	var resolver;
15366
15367  	if (template.r) {
15368  		resolver = Resolvers_createReferenceResolver(component, template.r, callback);
15369  	} else if (template.x) {
15370  		resolver = new Resolvers_ExpressionResolver(component, component.parentFragment, template.x, callback);
15371  	} else if (template.rx) {
15372  		resolver = new ReferenceExpressionResolver_ReferenceExpressionResolver(component, template.rx, callback);
15373  	}
15374
15375  	return resolver;
15376  }
15377
15378  function isSingleInterpolator(template) {
15379  	return template.length === 1 && template[0].t === INTERPOLATOR;
15380  }
15381
15382  // TODO how should event arguments be handled? e.g.
15383  // <widget on-foo='bar:1,2,3'/>
15384  // The event 'bar' will be fired on the parent instance
15385  // when 'foo' fires on the child, but the 1,2,3 arguments
15386  // will be lost
15387
15388  var initialise_propagateEvents = propagateEvents;
15389
15390  function propagateEvents(component, eventsDescriptor) {
15391  	var eventName;
15392
15393  	for (eventName in eventsDescriptor) {
15394  		if (eventsDescriptor.hasOwnProperty(eventName)) {
15395  			propagateEvent(component.instance, component.root, eventName, eventsDescriptor[eventName]);
15396  		}
15397  	}
15398  }
15399
15400  function propagateEvent(childInstance, parentInstance, eventName, proxyEventName) {
15401  	if (typeof proxyEventName !== "string") {
15402  		fatal("Components currently only support simple events - you cannot include arguments. Sorry!");
15403  	}
15404
15405  	childInstance.on(eventName, function () {
15406  		var event, args;
15407
15408  		// semi-weak test, but what else? tag the event obj ._isEvent ?
15409  		if (arguments.length && arguments[0] && arguments[0].node) {
15410  			event = Array.prototype.shift.call(arguments);
15411  		}
15412
15413  		args = Array.prototype.slice.call(arguments);
15414
15415  		shared_fireEvent(parentInstance, proxyEventName, { event: event, args: args });
15416
15417  		// cancel bubbling
15418  		return false;
15419  	});
15420  }
15421
15422  var initialise_updateLiveQueries = function (component) {
15423  	var ancestor, query;
15424
15425  	// If there's a live query for this component type, add it
15426  	ancestor = component.root;
15427  	while (ancestor) {
15428  		if (query = ancestor._liveComponentQueries["_" + component.name]) {
15429  			query.push(component.instance);
15430  		}
15431
15432  		ancestor = ancestor.parent;
15433  	}
15434  };
15435
15436  var Component_prototype_init = Component$init;
15437  function Component$init(options, Component) {
15438  	var parentFragment, root;
15439
15440  	if (!Component) {
15441  		throw new Error("Component \"" + this.name + "\" not found");
15442  	}
15443
15444  	parentFragment = this.parentFragment = options.parentFragment;
15445  	root = parentFragment.root;
15446
15447  	this.root = root;
15448  	this.type = COMPONENT;
15449  	this.name = options.template.e;
15450  	this.index = options.index;
15451  	this.indexRefBindings = {};
15452  	this.yielders = {};
15453  	this.resolvers = [];
15454
15455  	createInstance(this, Component, options.template.a, options.template.f, options.template.p);
15456  	initialise_propagateEvents(this, options.template.v);
15457
15458  	// intro, outro and decorator directives have no effect
15459  	if (options.template.t0 || options.template.t1 || options.template.t2 || options.template.o) {
15460  		warnIfDebug("The \"intro\", \"outro\" and \"decorator\" directives have no effect on components", { ractive: this.instance });
15461  	}
15462
15463  	initialise_updateLiveQueries(this);
15464  }
15465
15466  var Component_prototype_rebind = Component$rebind;
15467
15468  function Component$rebind(oldKeypath, newKeypath) {
15469  	var query;
15470
15471  	this.resolvers.forEach(rebind);
15472
15473  	for (var k in this.yielders) {
15474  		if (this.yielders[k][0]) {
15475  			rebind(this.yielders[k][0]);
15476  		}
15477  	}
15478
15479  	if (query = this.root._liveComponentQueries["_" + this.name]) {
15480  		query._makeDirty();
15481  	}
15482
15483  	function rebind(x) {
15484  		x.rebind(oldKeypath, newKeypath);
15485  	}
15486  }
15487
15488  var Component_prototype_render = Component$render;
15489
15490  function Component$render() {
15491  	var instance = this.instance;
15492
15493  	instance.render(this.parentFragment.getNode());
15494
15495  	this.rendered = true;
15496  	return instance.fragment.detach();
15497  }
15498
15499  var Component_prototype_toString = Component$toString;
15500
15501  function Component$toString() {
15502  	return this.instance.fragment.toString();
15503  }
15504
15505  var Component_prototype_unbind = Component$unbind;
15506
15507  var Component_prototype_unbind__teardownHook = new hooks_Hook("teardown");
15508  function Component$unbind() {
15509  	var instance = this.instance;
15510
15511  	this.resolvers.forEach(methodCallers__unbind);
15512
15513  	removeFromLiveComponentQueries(this);
15514
15515  	instance._observers.forEach(cancel);
15516
15517  	// teardown the instance
15518  	instance.fragment.unbind();
15519  	instance.viewmodel.teardown();
15520
15521  	if (instance.fragment.rendered && instance.el.__ractive_instances__) {
15522  		removeFromArray(instance.el.__ractive_instances__, instance);
15523  	}
15524
15525  	Component_prototype_unbind__teardownHook.fire(instance);
15526  }
15527
15528  function removeFromLiveComponentQueries(component) {
15529  	var instance, query;
15530
15531  	instance = component.root;
15532
15533  	do {
15534  		if (query = instance._liveComponentQueries["_" + component.name]) {
15535  			query._remove(component);
15536  		}
15537  	} while (instance = instance.parent);
15538  }
15539
15540  var Component_prototype_unrender = Component$unrender;
15541
15542  function Component$unrender(shouldDestroy) {
15543  	this.shouldDestroy = shouldDestroy;
15544  	this.instance.unrender();
15545  }
15546
15547  var Component = function (options, Constructor) {
15548  	this.init(options, Constructor);
15549  };
15550
15551  Component.prototype = {
15552  	detach: Component_prototype_detach,
15553  	find: Component_prototype_find,
15554  	findAll: Component_prototype_findAll,
15555  	findAllComponents: Component_prototype_findAllComponents,
15556  	findComponent: Component_prototype_findComponent,
15557  	findNextNode: Component_prototype_findNextNode,
15558  	firstNode: Component_prototype_firstNode,
15559  	init: Component_prototype_init,
15560  	rebind: Component_prototype_rebind,
15561  	render: Component_prototype_render,
15562  	toString: Component_prototype_toString,
15563  	unbind: Component_prototype_unbind,
15564  	unrender: Component_prototype_unrender
15565  };
15566
15567  var _Component = Component;
15568
15569  var Comment = function (options) {
15570  	this.type = COMMENT;
15571  	this.value = options.template.c;
15572  };
15573
15574  Comment.prototype = {
15575  	detach: shared_detach,
15576
15577  	firstNode: function () {
15578  		return this.node;
15579  	},
15580
15581  	render: function () {
15582  		if (!this.node) {
15583  			this.node = document.createComment(this.value);
15584  		}
15585
15586  		return this.node;
15587  	},
15588
15589  	toString: function () {
15590  		return "<!--" + this.value + "-->";
15591  	},
15592
15593  	unrender: function (shouldDestroy) {
15594  		if (shouldDestroy) {
15595  			this.node.parentNode.removeChild(this.node);
15596  		}
15597  	}
15598  };
15599
15600  var items_Comment = Comment;
15601
15602  var Yielder = function (options) {
15603  	var container, component;
15604
15605  	this.type = YIELDER;
15606
15607  	this.container = container = options.parentFragment.root;
15608  	this.component = component = container.component;
15609
15610  	this.container = container;
15611  	this.containerFragment = options.parentFragment;
15612  	this.parentFragment = component.parentFragment;
15613
15614  	var name = this.name = options.template.n || "";
15615
15616  	var template = container._inlinePartials[name];
15617
15618  	if (!template) {
15619  		warnIfDebug("Could not find template for partial \"" + name + "\"", { ractive: options.root });
15620  		template = [];
15621  	}
15622
15623  	this.fragment = new virtualdom_Fragment({
15624  		owner: this,
15625  		root: container.parent,
15626  		template: template,
15627  		pElement: this.containerFragment.pElement
15628  	});
15629
15630  	// even though only one yielder is allowed, we need to have an array of them
15631  	// as it's possible to cause a yielder to be created before the last one
15632  	// was destroyed in the same turn of the runloop
15633  	if (!isArray(component.yielders[name])) {
15634  		component.yielders[name] = [this];
15635  	} else {
15636  		component.yielders[name].push(this);
15637  	}
15638
15639  	global_runloop.scheduleTask(function () {
15640  		if (component.yielders[name].length > 1) {
15641  			throw new Error("A component template can only have one {{yield" + (name ? " " + name : "") + "}} declaration at a time");
15642  		}
15643  	});
15644  };
15645
15646  Yielder.prototype = {
15647  	detach: function () {
15648  		return this.fragment.detach();
15649  	},
15650
15651  	find: function (selector) {
15652  		return this.fragment.find(selector);
15653  	},
15654
15655  	findAll: function (selector, query) {
15656  		return this.fragment.findAll(selector, query);
15657  	},
15658
15659  	findComponent: function (selector) {
15660  		return this.fragment.findComponent(selector);
15661  	},
15662
15663  	findAllComponents: function (selector, query) {
15664  		return this.fragment.findAllComponents(selector, query);
15665  	},
15666
15667  	findNextNode: function () {
15668  		return this.containerFragment.findNextNode(this);
15669  	},
15670
15671  	firstNode: function () {
15672  		return this.fragment.firstNode();
15673  	},
15674
15675  	getValue: function (options) {
15676  		return this.fragment.getValue(options);
15677  	},
15678
15679  	render: function () {
15680  		return this.fragment.render();
15681  	},
15682
15683  	unbind: function () {
15684  		this.fragment.unbind();
15685  	},
15686
15687  	unrender: function (shouldDestroy) {
15688  		this.fragment.unrender(shouldDestroy);
15689  		removeFromArray(this.component.yielders[this.name], this);
15690  	},
15691
15692  	rebind: function (oldKeypath, newKeypath) {
15693  		this.fragment.rebind(oldKeypath, newKeypath);
15694  	},
15695
15696  	toString: function () {
15697  		return this.fragment.toString();
15698  	}
15699  };
15700
15701  var items_Yielder = Yielder;
15702
15703  var Doctype = function (options) {
15704  	this.declaration = options.template.a;
15705  };
15706
15707  Doctype.prototype = {
15708  	init: noop,
15709  	render: noop,
15710  	unrender: noop,
15711  	teardown: noop,
15712  	toString: function () {
15713  		return "<!DOCTYPE" + this.declaration + ">";
15714  	}
15715  };
15716
15717  var items_Doctype = Doctype;
15718
15719  var Fragment_prototype_init = Fragment$init;
15720
15721  function Fragment$init(options) {
15722  	var _this = this;
15723
15724  	this.owner = options.owner; // The item that owns this fragment - an element, section, partial, or attribute
15725  	this.parent = this.owner.parentFragment;
15726
15727  	// inherited properties
15728  	this.root = options.root;
15729  	this.pElement = options.pElement;
15730  	this.context = options.context;
15731  	this.index = options.index;
15732  	this.key = options.key;
15733  	this.registeredIndexRefs = [];
15734
15735  	// encapsulated styles should be inherited until they get applied by an element
15736  	this.cssIds = "cssIds" in options ? options.cssIds : this.parent ? this.parent.cssIds : null;
15737
15738  	this.items = options.template.map(function (template, i) {
15739  		return createItem({
15740  			parentFragment: _this,
15741  			pElement: options.pElement,
15742  			template: template,
15743  			index: i
15744  		});
15745  	});
15746
15747  	this.value = this.argsList = null;
15748  	this.dirtyArgs = this.dirtyValue = true;
15749
15750  	this.bound = true;
15751  }
15752
15753  function createItem(options) {
15754  	if (typeof options.template === "string") {
15755  		return new items_Text(options);
15756  	}
15757
15758  	switch (options.template.t) {
15759  		case YIELDER:
15760  			return new items_Yielder(options);
15761  		case INTERPOLATOR:
15762  			return new items_Interpolator(options);
15763  		case SECTION:
15764  			return new _Section(options);
15765  		case TRIPLE:
15766  			return new _Triple(options);
15767  		case ELEMENT:
15768  			var constructor = undefined;
15769  			if (constructor = Component_getComponent(options.parentFragment.root, options.template.e)) {
15770  				return new _Component(options, constructor);
15771  			}
15772  			return new _Element(options);
15773  		case PARTIAL:
15774  			return new _Partial(options);
15775  		case COMMENT:
15776  			return new items_Comment(options);
15777  		case DOCTYPE:
15778  			return new items_Doctype(options);
15779
15780  		default:
15781  			throw new Error("Something very strange happened. Please file an issue at https://github.com/ractivejs/ractive/issues. Thanks!");
15782  	}
15783  }
15784
15785  var Fragment_prototype_rebind = Fragment$rebind;
15786  function Fragment$rebind(oldKeypath, newKeypath) {
15787
15788  	// assign new context keypath if needed
15789  	if (!this.owner || this.owner.hasContext) {
15790  		assignNewKeypath(this, "context", oldKeypath, newKeypath);
15791  	}
15792
15793  	this.items.forEach(function (item) {
15794  		if (item.rebind) {
15795  			item.rebind(oldKeypath, newKeypath);
15796  		}
15797  	});
15798  }
15799
15800  var Fragment_prototype_render = Fragment$render;
15801
15802  function Fragment$render() {
15803  	var result;
15804
15805  	if (this.items.length === 1) {
15806  		result = this.items[0].render();
15807  	} else {
15808  		result = document.createDocumentFragment();
15809
15810  		this.items.forEach(function (item) {
15811  			result.appendChild(item.render());
15812  		});
15813  	}
15814
15815  	this.rendered = true;
15816  	return result;
15817  }
15818
15819  var Fragment_prototype_toString = Fragment$toString;
15820
15821  function Fragment$toString(escape) {
15822  	if (!this.items) {
15823  		return "";
15824  	}
15825
15826  	return this.items.map(escape ? toEscapedString : Fragment_prototype_toString__toString).join("");
15827  }
15828
15829  function Fragment_prototype_toString__toString(item) {
15830  	return item.toString();
15831  }
15832
15833  function toEscapedString(item) {
15834  	return item.toString(true);
15835  }
15836
15837  var Fragment_prototype_unbind = Fragment$unbind;
15838
15839  function Fragment$unbind() {
15840  	if (!this.bound) {
15841  		return;
15842  	}
15843
15844  	this.items.forEach(unbindItem);
15845  	this.bound = false;
15846  }
15847
15848  function unbindItem(item) {
15849  	if (item.unbind) {
15850  		item.unbind();
15851  	}
15852  }
15853
15854  var Fragment_prototype_unrender = Fragment$unrender;
15855
15856  function Fragment$unrender(shouldDestroy) {
15857  	if (!this.rendered) {
15858  		throw new Error("Attempted to unrender a fragment that was not rendered");
15859  	}
15860
15861  	this.items.forEach(function (i) {
15862  		return i.unrender(shouldDestroy);
15863  	});
15864  	this.rendered = false;
15865  }
15866
15867  var Fragment = function (options) {
15868  	this.init(options);
15869  };
15870
15871  Fragment.prototype = {
15872  	bubble: prototype_bubble,
15873  	detach: Fragment_prototype_detach,
15874  	find: Fragment_prototype_find,
15875  	findAll: Fragment_prototype_findAll,
15876  	findAllComponents: Fragment_prototype_findAllComponents,
15877  	findComponent: Fragment_prototype_findComponent,
15878  	findNextNode: prototype_findNextNode,
15879  	firstNode: prototype_firstNode,
15880  	getArgsList: getArgsList,
15881  	getNode: getNode,
15882  	getValue: prototype_getValue,
15883  	init: Fragment_prototype_init,
15884  	rebind: Fragment_prototype_rebind,
15885  	registerIndexRef: function (idx) {
15886  		var idxs = this.registeredIndexRefs;
15887  		if (idxs.indexOf(idx) === -1) {
15888  			idxs.push(idx);
15889  		}
15890  	},
15891  	render: Fragment_prototype_render,
15892  	toString: Fragment_prototype_toString,
15893  	unbind: Fragment_prototype_unbind,
15894  	unregisterIndexRef: function (idx) {
15895  		var idxs = this.registeredIndexRefs;
15896  		idxs.splice(idxs.indexOf(idx), 1);
15897  	},
15898  	unrender: Fragment_prototype_unrender
15899  };
15900
15901  var virtualdom_Fragment = Fragment;
15902
15903  var prototype_reset = Ractive$reset;
15904  var shouldRerender = ["template", "partials", "components", "decorators", "events"],
15905      resetHook = new hooks_Hook("reset");
15906  function Ractive$reset(data) {
15907  	var promise, wrapper, changes, i, rerender;
15908
15909  	data = data || {};
15910
15911  	if (typeof data !== "object") {
15912  		throw new Error("The reset method takes either no arguments, or an object containing new data");
15913  	}
15914
15915  	// If the root object is wrapped, try and use the wrapper's reset value
15916  	if ((wrapper = this.viewmodel.wrapped[""]) && wrapper.reset) {
15917  		if (wrapper.reset(data) === false) {
15918  			// reset was rejected, we need to replace the object
15919  			this.viewmodel.reset(data);
15920  		}
15921  	} else {
15922  		this.viewmodel.reset(data);
15923  	}
15924
15925  	// reset config items and track if need to rerender
15926  	changes = config_config.reset(this);
15927
15928  	i = changes.length;
15929  	while (i--) {
15930  		if (shouldRerender.indexOf(changes[i]) > -1) {
15931  			rerender = true;
15932  			break;
15933  		}
15934  	}
15935
15936  	if (rerender) {
15937  		var component = undefined;
15938
15939  		this.viewmodel.mark(rootKeypath);
15940
15941  		// Is this is a component, we need to set the `shouldDestroy`
15942  		// flag, otherwise it will assume by default that a parent node
15943  		// will be detached, and therefore it doesn't need to bother
15944  		// detaching its own nodes
15945  		if (component = this.component) {
15946  			component.shouldDestroy = true;
15947  		}
15948
15949  		this.unrender();
15950
15951  		if (component) {
15952  			component.shouldDestroy = false;
15953  		}
15954
15955  		// If the template changed, we need to destroy the parallel DOM
15956  		// TODO if we're here, presumably it did?
15957  		if (this.fragment.template !== this.template) {
15958  			this.fragment.unbind();
15959
15960  			this.fragment = new virtualdom_Fragment({
15961  				template: this.template,
15962  				root: this,
15963  				owner: this
15964  			});
15965  		}
15966
15967  		promise = this.render(this.el, this.anchor);
15968  	} else {
15969  		promise = global_runloop.start(this, true);
15970  		this.viewmodel.mark(rootKeypath);
15971  		global_runloop.end();
15972  	}
15973
15974  	resetHook.fire(this, data);
15975
15976  	return promise;
15977  }
15978
15979  var resetPartial = function (name, partial) {
15980  	var promise,
15981  	    collection = [];
15982
15983  	function collect(source, dest, ractive) {
15984  		// if this is a component and it has its own partial, bail
15985  		if (ractive && ractive.partials[name]) return;
15986
15987  		source.forEach(function (item) {
15988  			// queue to rerender if the item is a partial and the current name matches
15989  			if (item.type === PARTIAL && item.getPartialName() === name) {
15990  				dest.push(item);
15991  			}
15992
15993  			// if it has a fragment, process its items
15994  			if (item.fragment) {
15995  				collect(item.fragment.items, dest, ractive);
15996  			}
15997
15998  			// or if it has fragments
15999  			if (isArray(item.fragments)) {
16000  				collect(item.fragments, dest, ractive);
16001  			}
16002
16003  			// or if it is itself a fragment, process its items
16004  			else if (isArray(item.items)) {
16005  				collect(item.items, dest, ractive);
16006  			}
16007
16008  			// or if it is a component, step in and process its items
16009  			else if (item.type === COMPONENT && item.instance) {
16010  				collect(item.instance.fragment.items, dest, item.instance);
16011  			}
16012
16013  			// if the item is an element, process its attributes too
16014  			if (item.type === ELEMENT) {
16015  				if (isArray(item.attributes)) {
16016  					collect(item.attributes, dest, ractive);
16017  				}
16018
16019  				if (isArray(item.conditionalAttributes)) {
16020  					collect(item.conditionalAttributes, dest, ractive);
16021  				}
16022  			}
16023  		});
16024  	}
16025
16026  	collect(this.fragment.items, collection);
16027  	this.partials[name] = partial;
16028
16029  	promise = global_runloop.start(this, true);
16030
16031  	collection.forEach(function (item) {
16032  		item.value = undefined;
16033  		item.setValue(name);
16034  	});
16035
16036  	global_runloop.end();
16037
16038  	return promise;
16039  };
16040
16041  // TODO should resetTemplate be asynchronous? i.e. should it be a case
16042  // of outro, update template, intro? I reckon probably not, since that
16043  // could be achieved with unrender-resetTemplate-render. Also, it should
16044  // conceptually be similar to resetPartial, which couldn't be async
16045
16046  var resetTemplate = Ractive$resetTemplate;
16047  function Ractive$resetTemplate(template) {
16048  	var transitionsEnabled, component;
16049
16050  	template_template.init(null, this, { template: template });
16051
16052  	transitionsEnabled = this.transitionsEnabled;
16053  	this.transitionsEnabled = false;
16054
16055  	// Is this is a component, we need to set the `shouldDestroy`
16056  	// flag, otherwise it will assume by default that a parent node
16057  	// will be detached, and therefore it doesn't need to bother
16058  	// detaching its own nodes
16059  	if (component = this.component) {
16060  		component.shouldDestroy = true;
16061  	}
16062
16063  	this.unrender();
16064
16065  	if (component) {
16066  		component.shouldDestroy = false;
16067  	}
16068
16069  	// remove existing fragment and create new one
16070  	this.fragment.unbind();
16071  	this.fragment = new virtualdom_Fragment({
16072  		template: this.template,
16073  		root: this,
16074  		owner: this
16075  	});
16076
16077  	this.render(this.el, this.anchor);
16078
16079  	this.transitionsEnabled = transitionsEnabled;
16080  }
16081
16082  var reverse = makeArrayMethod("reverse");
16083
16084  var Ractive_prototype_set = Ractive$set;
16085
16086  function Ractive$set(keypath, value) {
16087  	var map, promise;
16088
16089  	promise = global_runloop.start(this, true);
16090
16091  	// Set multiple keypaths in one go
16092  	if (isObject(keypath)) {
16093  		map = keypath;
16094
16095  		for (keypath in map) {
16096  			if (map.hasOwnProperty(keypath)) {
16097  				value = map[keypath];
16098  				set(this, keypath, value);
16099  			}
16100  		}
16101  	}
16102
16103  	// Set a single keypath
16104  	else {
16105  		set(this, keypath, value);
16106  	}
16107
16108  	global_runloop.end();
16109
16110  	return promise;
16111  }
16112
16113  function set(ractive, keypath, value) {
16114  	keypath = getKeypath(normalise(keypath));
16115
16116  	if (keypath.isPattern) {
16117  		getMatchingKeypaths(ractive, keypath).forEach(function (keypath) {
16118  			ractive.viewmodel.set(keypath, value);
16119  		});
16120  	} else {
16121  		ractive.viewmodel.set(keypath, value);
16122  	}
16123  }
16124
16125  var shift = makeArrayMethod("shift");
16126
16127  var prototype_sort = makeArrayMethod("sort");
16128
16129  var splice = makeArrayMethod("splice");
16130
16131  var subtract = Ractive$subtract;
16132  function Ractive$subtract(keypath, d) {
16133  	return shared_add(this, keypath, d === undefined ? -1 : -d);
16134  }
16135
16136  // Teardown. This goes through the root fragment and all its children, removing observers
16137  // and generally cleaning up after itself
16138
16139  var Ractive_prototype_teardown = Ractive$teardown;
16140
16141  var Ractive_prototype_teardown__teardownHook = new hooks_Hook("teardown");
16142  function Ractive$teardown() {
16143  	var promise;
16144
16145  	this.fragment.unbind();
16146  	this.viewmodel.teardown();
16147
16148  	this._observers.forEach(cancel);
16149
16150  	if (this.fragment.rendered && this.el.__ractive_instances__) {
16151  		removeFromArray(this.el.__ractive_instances__, this);
16152  	}
16153
16154  	this.shouldDestroy = true;
16155  	promise = this.fragment.rendered ? this.unrender() : utils_Promise.resolve();
16156
16157  	Ractive_prototype_teardown__teardownHook.fire(this);
16158
16159  	this._boundFunctions.forEach(deleteFunctionCopy);
16160
16161  	return promise;
16162  }
16163
16164  function deleteFunctionCopy(bound) {
16165  	delete bound.fn[bound.prop];
16166  }
16167
16168  var toggle = Ractive$toggle;
16169  function Ractive$toggle(keypath) {
16170  	var _this = this;
16171
16172  	if (typeof keypath !== "string") {
16173  		throw new TypeError(badArguments);
16174  	}
16175
16176  	var changes = undefined;
16177
16178  	if (/\*/.test(keypath)) {
16179  		changes = {};
16180
16181  		getMatchingKeypaths(this, getKeypath(normalise(keypath))).forEach(function (keypath) {
16182  			changes[keypath.str] = !_this.viewmodel.get(keypath);
16183  		});
16184
16185  		return this.set(changes);
16186  	}
16187
16188  	return this.set(keypath, !this.get(keypath));
16189  }
16190
16191  var toHTML = Ractive$toHTML;
16192
16193  function Ractive$toHTML() {
16194  	return this.fragment.toString(true);
16195  }
16196
16197  var Ractive_prototype_unrender = Ractive$unrender;
16198  var unrenderHook = new hooks_Hook("unrender");
16199  function Ractive$unrender() {
16200  	var promise, shouldDestroy;
16201
16202  	if (!this.fragment.rendered) {
16203  		warnIfDebug("ractive.unrender() was called on a Ractive instance that was not rendered");
16204  		return utils_Promise.resolve();
16205  	}
16206
16207  	promise = global_runloop.start(this, true);
16208
16209  	// If this is a component, and the component isn't marked for destruction,
16210  	// don't detach nodes from the DOM unnecessarily
16211  	shouldDestroy = !this.component || this.component.shouldDestroy || this.shouldDestroy;
16212
16213  	// Cancel any animations in progress
16214  	while (this._animations[0]) {
16215  		this._animations[0].stop(); // it will remove itself from the index
16216  	}
16217
16218  	this.fragment.unrender(shouldDestroy);
16219
16220  	removeFromArray(this.el.__ractive_instances__, this);
16221
16222  	unrenderHook.fire(this);
16223
16224  	global_runloop.end();
16225  	return promise;
16226  }
16227
16228  var unshift = makeArrayMethod("unshift");
16229
16230  var Ractive_prototype_update = Ractive$update;
16231  var updateHook = new hooks_Hook("update");
16232  function Ractive$update(keypath) {
16233  	var promise;
16234
16235  	keypath = getKeypath(keypath) || rootKeypath;
16236
16237  	promise = global_runloop.start(this, true);
16238  	this.viewmodel.mark(keypath);
16239  	global_runloop.end();
16240
16241  	updateHook.fire(this, keypath);
16242
16243  	return promise;
16244  }
16245
16246  var prototype_updateModel = Ractive$updateModel;
16247
16248  function Ractive$updateModel(keypath, cascade) {
16249  	var values, key, bindings;
16250
16251  	if (typeof keypath === "string" && !cascade) {
16252  		bindings = this._twowayBindings[keypath];
16253  	} else {
16254  		bindings = [];
16255
16256  		for (key in this._twowayBindings) {
16257  			if (!keypath || getKeypath(key).equalsOrStartsWith(keypath)) {
16258  				// TODO is this right?
16259  				bindings.push.apply(bindings, this._twowayBindings[key]);
16260  			}
16261  		}
16262  	}
16263
16264  	values = consolidate(this, bindings);
16265  	return this.set(values);
16266  }
16267
16268  function consolidate(ractive, bindings) {
16269  	var values = {},
16270  	    checkboxGroups = [];
16271
16272  	bindings.forEach(function (b) {
16273  		var oldValue, newValue;
16274
16275  		// special case - radio name bindings
16276  		if (b.radioName && !b.element.node.checked) {
16277  			return;
16278  		}
16279
16280  		// special case - checkbox name bindings come in groups, so
16281  		// we want to get the value once at most
16282  		if (b.checkboxName) {
16283  			if (!checkboxGroups[b.keypath.str] && !b.changed()) {
16284  				checkboxGroups.push(b.keypath);
16285  				checkboxGroups[b.keypath.str] = b;
16286  			}
16287
16288  			return;
16289  		}
16290
16291  		oldValue = b.attribute.value;
16292  		newValue = b.getValue();
16293
16294  		if (arrayContentsMatch(oldValue, newValue)) {
16295  			return;
16296  		}
16297
16298  		if (!isEqual(oldValue, newValue)) {
16299  			values[b.keypath.str] = newValue;
16300  		}
16301  	});
16302
16303  	// Handle groups of `<input type='checkbox' name='{{foo}}' ...>`
16304  	if (checkboxGroups.length) {
16305  		checkboxGroups.forEach(function (keypath) {
16306  			var binding, oldValue, newValue;
16307
16308  			binding = checkboxGroups[keypath.str]; // one to represent the entire group
16309  			oldValue = binding.attribute.value;
16310  			newValue = binding.getValue();
16311
16312  			if (!arrayContentsMatch(oldValue, newValue)) {
16313  				values[keypath.str] = newValue;
16314  			}
16315  		});
16316  	}
16317
16318  	return values;
16319  }
16320
16321  var prototype = {
16322  	add: prototype_add,
16323  	animate: prototype_animate,
16324  	detach: prototype_detach,
16325  	find: prototype_find,
16326  	findAll: prototype_findAll,
16327  	findAllComponents: prototype_findAllComponents,
16328  	findComponent: prototype_findComponent,
16329  	findContainer: findContainer,
16330  	findParent: findParent,
16331  	fire: prototype_fire,
16332  	get: prototype_get,
16333  	insert: insert,
16334  	merge: prototype_merge,
16335  	observe: observe,
16336  	observeOnce: observeOnce,
16337  	off: off,
16338  	on: on,
16339  	once: once,
16340  	pop: pop,
16341  	push: push,
16342  	render: prototype_render,
16343  	reset: prototype_reset,
16344  	resetPartial: resetPartial,
16345  	resetTemplate: resetTemplate,
16346  	reverse: reverse,
16347  	set: Ractive_prototype_set,
16348  	shift: shift,
16349  	sort: prototype_sort,
16350  	splice: splice,
16351  	subtract: subtract,
16352  	teardown: Ractive_prototype_teardown,
16353  	toggle: toggle,
16354  	toHTML: toHTML,
16355  	toHtml: toHTML,
16356  	unrender: Ractive_prototype_unrender,
16357  	unshift: unshift,
16358  	update: Ractive_prototype_update,
16359  	updateModel: prototype_updateModel
16360  };
16361
16362  var wrapMethod = function (method, superMethod, force) {
16363
16364  	if (force || needsSuper(method, superMethod)) {
16365
16366  		return function () {
16367
16368  			var hasSuper = ("_super" in this),
16369  			    _super = this._super,
16370  			    result;
16371
16372  			this._super = superMethod;
16373
16374  			result = method.apply(this, arguments);
16375
16376  			if (hasSuper) {
16377  				this._super = _super;
16378  			}
16379
16380  			return result;
16381  		};
16382  	} else {
16383  		return method;
16384  	}
16385  };
16386
16387  function needsSuper(method, superMethod) {
16388  	return typeof superMethod === "function" && /_super/.test(method);
16389  }
16390
16391  var unwrapExtended = unwrap;
16392
16393  function unwrap(Child) {
16394  	var options = {};
16395
16396  	while (Child) {
16397  		addRegistries(Child, options);
16398  		addOtherOptions(Child, options);
16399
16400  		if (Child._Parent !== _Ractive) {
16401  			Child = Child._Parent;
16402  		} else {
16403  			Child = false;
16404  		}
16405  	}
16406
16407  	return options;
16408  }
16409
16410  function addRegistries(Child, options) {
16411  	config_registries.forEach(function (r) {
16412  		addRegistry(r.useDefaults ? Child.prototype : Child, options, r.name);
16413  	});
16414  }
16415
16416  function addRegistry(target, options, name) {
16417  	var registry,
16418  	    keys = Object.keys(target[name]);
16419
16420  	if (!keys.length) {
16421  		return;
16422  	}
16423
16424  	if (!(registry = options[name])) {
16425  		registry = options[name] = {};
16426  	}
16427
16428  	keys.filter(function (key) {
16429  		return !(key in registry);
16430  	}).forEach(function (key) {
16431  		return registry[key] = target[name][key];
16432  	});
16433  }
16434
16435  function addOtherOptions(Child, options) {
16436  	Object.keys(Child.prototype).forEach(function (key) {
16437  		if (key === "computed") {
16438  			return;
16439  		}
16440
16441  		var value = Child.prototype[key];
16442
16443  		if (!(key in options)) {
16444  			options[key] = value._method ? value._method : value;
16445  		}
16446
16447  		// is it a wrapped function?
16448  		else if (typeof options[key] === "function" && typeof value === "function" && options[key]._method) {
16449
16450  			var result = undefined,
16451  			    needsSuper = value._method;
16452
16453  			if (needsSuper) {
16454  				value = value._method;
16455  			}
16456
16457  			// rewrap bound directly to parent fn
16458  			result = wrapMethod(options[key]._method, value);
16459
16460  			if (needsSuper) {
16461  				result._method = result;
16462  			}
16463
16464  			options[key] = result;
16465  		}
16466  	});
16467  }
16468
16469  var _extend = _extend__extend;
16470
16471  function _extend__extend() {
16472  	for (var _len = arguments.length, options = Array(_len), _key = 0; _key < _len; _key++) {
16473  		options[_key] = arguments[_key];
16474  	}
16475
16476  	if (!options.length) {
16477  		return extendOne(this);
16478  	} else {
16479  		return options.reduce(extendOne, this);
16480  	}
16481  }
16482
16483  function extendOne(Parent) {
16484  	var options = arguments[1] === undefined ? {} : arguments[1];
16485
16486  	var Child, proto;
16487
16488  	// if we're extending with another Ractive instance...
16489  	//
16490  	//   var Human = Ractive.extend(...), Spider = Ractive.extend(...);
16491  	//   var Spiderman = Human.extend( Spider );
16492  	//
16493  	// ...inherit prototype methods and default options as well
16494  	if (options.prototype instanceof _Ractive) {
16495  		options = unwrapExtended(options);
16496  	}
16497
16498  	Child = function (options) {
16499  		if (!(this instanceof Child)) return new Child(options);
16500  		initialise(this, options);
16501  	};
16502
16503  	proto = create(Parent.prototype);
16504  	proto.constructor = Child;
16505
16506  	// Static properties
16507  	defineProperties(Child, {
16508  		// alias prototype as defaults
16509  		defaults: { value: proto },
16510
16511  		// extendable
16512  		extend: { value: _extend__extend, writable: true, configurable: true },
16513
16514  		// Parent - for IE8, can't use Object.getPrototypeOf
16515  		_Parent: { value: Parent }
16516  	});
16517
16518  	// extend configuration
16519  	config_config.extend(Parent, proto, options);
16520
16521  	custom_data.extend(Parent, proto, options);
16522
16523  	if (options.computed) {
16524  		proto.computed = utils_object__extend(create(Parent.prototype.computed), options.computed);
16525  	}
16526
16527  	Child.prototype = proto;
16528
16529  	return Child;
16530  }
16531
16532  var getNodeInfo = function (node) {
16533  	var info = {},
16534  	    priv,
16535  	    indices;
16536
16537  	if (!node || !(priv = node._ractive)) {
16538  		return info;
16539  	}
16540
16541  	info.ractive = priv.root;
16542  	info.keypath = priv.keypath.str;
16543  	info.index = {};
16544
16545  	// find all index references and resolve them
16546  	if (indices = Resolvers_findIndexRefs(priv.proxy.parentFragment)) {
16547  		info.index = Resolvers_findIndexRefs.resolve(indices);
16548  	}
16549
16550  	return info;
16551  };
16552
16553  var Ractive, properties;
16554
16555  // Main Ractive required object
16556  Ractive = function (options) {
16557  	if (!(this instanceof Ractive)) return new Ractive(options);
16558  	initialise(this, options);
16559  };
16560
16561  // Ractive properties
16562  properties = {
16563
16564  	// debug flag
16565  	DEBUG: { writable: true, value: true },
16566  	DEBUG_PROMISES: { writable: true, value: true },
16567
16568  	// static methods:
16569  	extend: { value: _extend },
16570  	getNodeInfo: { value: getNodeInfo },
16571  	parse: { value: _parse },
16572
16573  	// Namespaced constructors
16574  	Promise: { value: utils_Promise },
16575
16576  	// support
16577  	svg: { value: svg },
16578  	magic: { value: environment__magic },
16579
16580  	// version
16581  	VERSION: { value: "0.7.3" },
16582
16583  	// Plugins
16584  	adaptors: { writable: true, value: {} },
16585  	components: { writable: true, value: {} },
16586  	decorators: { writable: true, value: {} },
16587  	easing: { writable: true, value: static_easing },
16588  	events: { writable: true, value: {} },
16589  	interpolators: { writable: true, value: static_interpolators },
16590  	partials: { writable: true, value: {} },
16591  	transitions: { writable: true, value: {} }
16592  };
16593
16594  // Ractive properties
16595  defineProperties(Ractive, properties);
16596
16597  Ractive.prototype = utils_object__extend(prototype, config_defaults);
16598
16599  Ractive.prototype.constructor = Ractive;
16600
16601  // alias prototype as defaults
16602  Ractive.defaults = Ractive.prototype;
16603
16604  // Ractive.js makes liberal use of things like Array.prototype.indexOf. In
16605  // older browsers, these are made available via a shim - here, we do a quick
16606  // pre-flight check to make sure that either a) we're not in a shit browser,
16607  // or b) we're using a Ractive-legacy.js build
16608  var FUNCTION = "function";
16609
16610  if (typeof Date.now !== FUNCTION || typeof String.prototype.trim !== FUNCTION || typeof Object.keys !== FUNCTION || typeof Array.prototype.indexOf !== FUNCTION || typeof Array.prototype.forEach !== FUNCTION || typeof Array.prototype.map !== FUNCTION || typeof Array.prototype.filter !== FUNCTION || typeof window !== "undefined" && typeof window.addEventListener !== FUNCTION) {
16611  	throw new Error("It looks like you're attempting to use Ractive.js in an older browser. You'll need to use one of the 'legacy builds' in order to continue - see http://docs.ractivejs.org/latest/legacy-builds for more information.");
16612  }
16613
16614  var _Ractive = Ractive;
16615
16616  return _Ractive;
16617
16618}));
16619//# sourceMappingURL=ractive.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.