text: (function () {
  
  	var notifyArrayDependants,
  
  		wrapArray,
  		unwrapArray,
  		WrappedArrayProto,
  		testObj,
  		mutatorMethods;
  
  
  	// Register a keypath to this array. When any of this array's mutator methods are called,
  	// it will `set` that keypath on the given Ractive instance
  	registerKeypathToArray = function ( array, keypath, root ) {
  		var roots, keypathsByGuid, rootIndex, keypaths;
  
  		// If this array hasn't been wrapped, we need to wrap it
  		if ( !array._ractive ) {
  			defineProperty( array, '_ractive', {
  				value: {
  					roots: [ root ], // there may be more than one Ractive instance depending on this
  					keypathsByGuid: {}
  				},
  				configurable: true
  			});
  
  			array._ractive.keypathsByGuid[ root._guid ] = [ keypath ];
  
  			wrapArray( array );
  		}
  
  		else {
  			roots = array._ractive.roots;
  			keypathsByGuid = array._ractive.keypathsByGuid;
  
  			// Does this Ractive instance currently depend on this array?
  			// If not, associate them
  			if ( !keypathsByGuid[ root._guid ] ) {
  				roots[ roots.length ] = root;
  				keypathsByGuid[ root._guid ] = [];
  			}
  
  			keypaths = keypathsByGuid[ root._guid ];
  
  			// If the current keypath isn't among them, add it
  			if ( keypaths.indexOf( keypath ) === -1 ) {
  				keypaths[ keypaths.length ] = keypath;
  			}
  		}
  	};
  
  
  	// Unregister keypath from array
  	unregisterKeypathFromArray = function ( array, keypath, root ) {
  		var roots, keypathsByGuid, rootIndex, keypaths, keypathIndex;
  
  		if ( !array._ractive ) {
  			throw new Error( 'Attempted to remove keypath from non-wrapped array. This error is unexpected - please send a bug report to @rich_harris' );
  		}
  
  		roots = array._ractive.roots;
  		keypathsByGuid = array._ractive.keypathsByGuid;
  
  		if ( !keypathsByGuid[ root._guid ] ) {
  			throw new Error( 'Ractive instance was not listed as a dependent of this array. This error is unexpected - please send a bug report to @rich_harris' );
  		}
  
  		keypaths = keypathsByGuid[ root._guid ];
  		keypathIndex = keypaths.indexOf( keypath );
  
  		if ( keypathIndex === -1 ) {
  			throw new Error( 'Attempted to unlink non-linked keypath from array. This error is unexpected - please send a bug report to @rich_harris' );
  		}
  
  		keypaths.splice( keypathIndex, 1 );
  
  		if ( !keypaths.length ) {
  			roots.splice( roots.indexOf( root ), 1 );
  			keypathsByGuid[ root._guid ] = null;
  		}
  
  		if ( !roots.length ) {
  			unwrapArray( array ); // It's good to clean up after ourselves
  		}
  	};
  
  
  	notifyArrayDependants = function ( array, methodName, args ) {
  		var processRoots,
  			processRoot,
  			processKeypaths,
  			processKeypath,
  			queueAllDependants,
  			queueDependants,
  			keypathsByGuid;
  
  		keypathsByGuid = array._ractive.keypathsByGuid;
  
  		processRoots = function ( roots ) {
  			var i = roots.length;
  			while ( i-- ) {
  				processRoot( roots[i] );
  			}
  		};
  
  		processRoot = function ( root ) {
  			var previousTransitionManager = root._transitionManager, transitionManager;
  
  			root._transitionManager = transitionManager = makeTransitionManager( root, noop );
  			processKeypaths( root, keypathsByGuid[ root._guid ] );
  			root._transitionManager = previousTransitionManager;
  
  			transitionManager.ready();
  		};
  
  		processKeypaths = function ( root, keypaths ) {
  			var i = keypaths.length;
  			while ( i-- ) {
  				processKeypath( root, keypaths[i] );
  			}
  		};
  
  		processKeypath = function ( root, keypath ) {
  			var depsByKeypath, deps, keys, upstreamQueue, smartUpdateQueue, dumbUpdateQueue, i, j, item;
  
  			// We don't do root.set(), because we don't want to update DOM sections
  			// using the normal method - we want to do a smart update whereby elements
  			// are removed from the right place. But we do need to clear the cache
  			clearCache( root, keypath );
  
  			// find dependants. If any are DOM sections, we do a smart update
  			// rather than a ractive.set() blunderbuss
  			smartUpdateQueue = [];
  			dumbUpdateQueue = [];
  
  			for ( i=0; i<root._deps.length; i+=1 ) { // we can't cache root._deps.length as it may change!
  				depsByKeypath = root._deps[i];
  
  				if ( !depsByKeypath ) {
  					continue;
  				}
  
  				deps = depsByKeypath[ keypath ];
  				
  				if ( deps ) {
  					queueDependants( root, keypath, deps, smartUpdateQueue, dumbUpdateQueue );
  
  					// we may have some deferred evaluators to process
  					processDeferredUpdates( root );
  					
  					while ( smartUpdateQueue.length ) {
  						smartUpdateQueue.pop().smartUpdate( methodName, args );
  					}
  
  					while ( dumbUpdateQueue.length ) {
  						dumbUpdateQueue.pop().update();
  					}
  				}
  			}
  
  			// we may have some deferred attributes to process
  			processDeferredUpdates( root );
  
  			// Finally, notify direct dependants of upstream keypaths...
  			upstreamQueue = [];
  
  			keys = splitKeypath( keypath );
  			while ( keys.length ) {
  				keys.pop();
  				upstreamQueue[ upstreamQueue.length ] = keys.join( '.' );
  			}
  
  			notifyMultipleDependants( root, upstreamQueue, true );
  
  			// length property has changed - notify dependants
  			// TODO in some cases (e.g. todo list example, when marking all as complete, then
  			// adding a new item (which should deactivate the 'all complete' checkbox
  			// but doesn't) this needs to happen before other updates. But doing so causes
  			// other mental problems. not sure what's going on...
  			notifyDependants( root, keypath + '.length', true );
  		};
  
  		// TODO can we get rid of this whole queueing nonsense?
  		queueDependants = function ( root, keypath, deps, smartUpdateQueue, dumbUpdateQueue ) {
  			var k, dependant;
  
  			k = deps.length;
  			while ( k-- ) {
  				dependant = deps[k];
  
  				// references need to get processed before mustaches
  				if ( dependant.type === REFERENCE ) {
  					dependant.update();
  					//dumbUpdateQueue[ dumbUpdateQueue.length ] = dependant;
  				}
  
  				// is this a DOM section?
  				else if ( dependant.keypath === keypath && dependant.type === SECTION /*&& dependant.parentNode*/ ) {
  					smartUpdateQueue[ smartUpdateQueue.length ] = dependant;
  
  				} else {
  					dumbUpdateQueue[ dumbUpdateQueue.length ] = dependant;
  				}
  			}
  		};
  
  		processRoots( array._ractive.roots );
  	};
  
  
  
  
  
  		
  	WrappedArrayProto = [];
  	mutatorMethods = [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift' ];
  
  	mutatorMethods.forEach( function ( methodName ) {
  		var method = function () {
  			var result = Array.prototype[ methodName ].apply( this, arguments );
  
  			this._ractive.setting = true;
  			notifyArrayDependants( this, methodName, arguments );
  			this._ractive.setting = false;
  
  			return result;
  		};
  
  		defineProperty( WrappedArrayProto, methodName, {
  			value: method
  		});
  	});
  
  	
  	// can we use prototype chain injection?
  	// http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/#wrappers_prototype_chain_injection
  	testObj = {};
  	if ( testObj.__proto__ ) {
  		// yes, we can
  		wrapArray = function ( array ) {
  			array.__proto__ = WrappedArrayProto;
  		};
  
  		unwrapArray = function ( array ) {
  			delete array._ractive;
  			array.__proto__ = Array.prototype;
  		};
  	}
  
  	else {
  		// no, we can't
  		wrapArray = function ( array ) {
  			var i, methodName;
  
  			i = mutatorMethods.length;
  			while ( i-- ) {
  				methodName = mutatorMethods[i];
  				defineProperty( array, methodName, {
  					value: WrappedArrayProto[ methodName ],
  					configurable: true
  				});
  			}
  		};
  
  		unwrapArray = function ( array ) {
  			var i;
  
  			i = mutatorMethods.length;
  			while ( i-- ) {
  				delete array[ mutatorMethods[i] ];
  			}
  
  			delete array._ractive;
  		};
  	}
  
  }());