UNPKG

ractive

Version:

Next-generation DOM manipulation

1,778 lines (1,659 loc) 406 kB
/* ractive.js v0.5.0 2014-07-05 - commit 1c20c21b http://ractivejs.org http://twitter.com/RactiveJS Released under the MIT License. */ ( function( global ) { 'use strict'; var noConflict = global.Ractive; /* config/defaults/options.js */ var options = function() { // These are both the values for Ractive.defaults // as well as the determination for whether an option // value will be placed on Component.defaults // (versus directly on Component) during an extend operation var defaultOptions = { // render placement: el: void 0, append: false, // template: template: { v: 1, t: [] }, // parse: preserveWhitespace: false, sanitize: false, stripComments: true, delimiters: [ '{{', '}}' ], tripleDelimiters: [ '{{{', '}}}' ], // data & binding: data: {}, computed: {}, magic: false, modifyArrays: true, adapt: [], isolated: false, twoway: true, lazy: false, // transitions: noIntro: false, transitionsEnabled: true, complete: void 0, // css: noCssTransform: false, // debug: debug: false }; return defaultOptions; }(); /* config/defaults/easing.js */ var easing = { linear: function( pos ) { return pos; }, easeIn: function( pos ) { return Math.pow( pos, 3 ); }, easeOut: function( pos ) { return Math.pow( pos - 1, 3 ) + 1; }, easeInOut: function( pos ) { if ( ( pos /= 0.5 ) < 1 ) { return 0.5 * Math.pow( pos, 3 ); } return 0.5 * ( Math.pow( pos - 2, 3 ) + 2 ); } }; /* circular.js */ var circular = []; /* utils/hasOwnProperty.js */ var hasOwn = Object.prototype.hasOwnProperty; /* utils/isArray.js */ var isArray = function() { var toString = Object.prototype.toString; // thanks, http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/ return function( thing ) { return toString.call( thing ) === '[object Array]'; }; }(); /* utils/isObject.js */ var isObject = function() { var toString = Object.prototype.toString; return function( thing ) { return thing && toString.call( thing ) === '[object Object]'; }; }(); /* utils/isNumeric.js */ var isNumeric = function( thing ) { return !isNaN( parseFloat( thing ) ) && isFinite( thing ); }; /* config/defaults/interpolators.js */ var interpolators = function( circular, hasOwnProperty, isArray, isObject, isNumeric ) { var interpolators, interpolate, cssLengthPattern; circular.push( function() { interpolate = circular.interpolate; } ); cssLengthPattern = /^([+-]?[0-9]+\.?(?:[0-9]+)?)(px|em|ex|%|in|cm|mm|pt|pc)$/; interpolators = { number: function( from, to ) { var delta; if ( !isNumeric( from ) || !isNumeric( to ) ) { return null; } from = +from; to = +to; delta = to - from; if ( !delta ) { return function() { return from; }; } return function( t ) { return from + t * delta; }; }, array: function( from, to ) { var intermediate, interpolators, len, i; if ( !isArray( from ) || !isArray( to ) ) { return null; } intermediate = []; interpolators = []; i = len = Math.min( from.length, to.length ); while ( i-- ) { interpolators[ i ] = interpolate( from[ i ], to[ i ] ); } // surplus values - don't interpolate, but don't exclude them either for ( i = len; i < from.length; i += 1 ) { intermediate[ i ] = from[ i ]; } for ( i = len; i < to.length; i += 1 ) { intermediate[ i ] = to[ i ]; } return function( t ) { var i = len; while ( i-- ) { intermediate[ i ] = interpolators[ i ]( t ); } return intermediate; }; }, object: function( from, to ) { var properties, len, interpolators, intermediate, prop; if ( !isObject( from ) || !isObject( to ) ) { return null; } properties = []; intermediate = {}; interpolators = {}; for ( prop in from ) { if ( hasOwnProperty.call( from, prop ) ) { if ( hasOwnProperty.call( to, prop ) ) { properties.push( prop ); interpolators[ prop ] = interpolate( from[ prop ], to[ prop ] ); } else { intermediate[ prop ] = from[ prop ]; } } } for ( prop in to ) { if ( hasOwnProperty.call( to, prop ) && !hasOwnProperty.call( from, prop ) ) { intermediate[ prop ] = to[ prop ]; } } len = properties.length; return function( t ) { var i = len, prop; while ( i-- ) { prop = properties[ i ]; intermediate[ prop ] = interpolators[ prop ]( t ); } return intermediate; }; }, cssLength: function( from, to ) { var fromMatch, toMatch, fromUnit, toUnit, fromValue, toValue, unit, delta; if ( from !== 0 && typeof from !== 'string' || to !== 0 && typeof to !== 'string' ) { return null; } fromMatch = cssLengthPattern.exec( from ); toMatch = cssLengthPattern.exec( to ); fromUnit = fromMatch ? fromMatch[ 2 ] : ''; toUnit = toMatch ? toMatch[ 2 ] : ''; if ( fromUnit && toUnit && fromUnit !== toUnit ) { return null; } unit = fromUnit || toUnit; fromValue = fromMatch ? +fromMatch[ 1 ] : 0; toValue = toMatch ? +toMatch[ 1 ] : 0; delta = toValue - fromValue; if ( !delta ) { return function() { return fromValue + unit; }; } return function( t ) { return fromValue + t * delta + unit; }; } }; return interpolators; }( circular, hasOwn, isArray, isObject, isNumeric ); /* config/svg.js */ var svg = function() { var svg; if ( typeof document === 'undefined' ) { svg = false; } else { svg = document && document.implementation.hasFeature( 'http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1' ); } return svg; }(); /* utils/removeFromArray.js */ var removeFromArray = function( array, member ) { var index = array.indexOf( member ); if ( index !== -1 ) { array.splice( index, 1 ); } }; /* utils/Promise.js */ var Promise = function() { var _Promise, PENDING = {}, FULFILLED = {}, REJECTED = {}; if ( typeof Promise === 'function' ) { // use native Promise _Promise = Promise; } else { _Promise = function( callback ) { var fulfilledHandlers = [], rejectedHandlers = [], state = PENDING, result, dispatchHandlers, makeResolver, fulfil, reject, promise; makeResolver = function( newState ) { return function( value ) { if ( state !== PENDING ) { return; } result = value; state = newState; dispatchHandlers = makeDispatcher( state === FULFILLED ? fulfilledHandlers : rejectedHandlers, result ); // dispatch onFulfilled and onRejected handlers asynchronously wait( dispatchHandlers ); }; }; fulfil = makeResolver( FULFILLED ); reject = makeResolver( REJECTED ); try { callback( fulfil, reject ); } catch ( err ) { reject( err ); } promise = { // `then()` returns a Promise - 2.2.7 then: function( onFulfilled, onRejected ) { var promise2 = new _Promise( function( fulfil, reject ) { var processResolutionHandler = function( handler, handlers, forward ) { // 2.2.1.1 if ( typeof handler === 'function' ) { handlers.push( function( p1result ) { var x; try { x = handler( p1result ); resolve( promise2, x, fulfil, reject ); } catch ( err ) { reject( err ); } } ); } else { // Forward the result of promise1 to promise2, if resolution handlers // are not given handlers.push( forward ); } }; // 2.2 processResolutionHandler( onFulfilled, fulfilledHandlers, fulfil ); processResolutionHandler( onRejected, rejectedHandlers, reject ); if ( state !== PENDING ) { // If the promise has resolved already, dispatch the appropriate handlers asynchronously wait( dispatchHandlers ); } } ); return promise2; } }; promise[ 'catch' ] = function( onRejected ) { return this.then( null, onRejected ); }; return promise; }; _Promise.all = function( promises ) { return new _Promise( function( fulfil, reject ) { var result = [], pending, i, processPromise; if ( !promises.length ) { fulfil( result ); return; } processPromise = function( i ) { promises[ i ].then( function( value ) { result[ i ] = value; if ( !--pending ) { fulfil( result ); } }, reject ); }; pending = i = promises.length; while ( i-- ) { processPromise( i ); } } ); }; _Promise.resolve = function( value ) { return new _Promise( function( fulfil ) { fulfil( value ); } ); }; _Promise.reject = function( reason ) { return new _Promise( function( fulfil, reject ) { reject( reason ); } ); }; } return _Promise; // TODO use MutationObservers or something to simulate setImmediate function wait( callback ) { setTimeout( callback, 0 ); } function makeDispatcher( handlers, result ) { return function() { var handler; while ( handler = handlers.shift() ) { handler( result ); } }; } function resolve( promise, x, fulfil, reject ) { // Promise Resolution Procedure var then; // 2.3.1 if ( x === promise ) { throw new TypeError( 'A promise\'s fulfillment handler cannot return the same promise' ); } // 2.3.2 if ( x instanceof _Promise ) { x.then( fulfil, reject ); } else if ( x && ( typeof x === 'object' || typeof x === 'function' ) ) { try { then = x.then; } catch ( e ) { reject( e ); // 2.3.3.2 return; } // 2.3.3.3 if ( typeof then === 'function' ) { var called, resolvePromise, rejectPromise; resolvePromise = function( y ) { if ( called ) { return; } called = true; resolve( promise, y, fulfil, reject ); }; rejectPromise = function( r ) { if ( called ) { return; } called = true; reject( r ); }; try { then.call( x, resolvePromise, rejectPromise ); } catch ( e ) { if ( !called ) { // 2.3.3.3.4.1 reject( e ); // 2.3.3.3.4.2 called = true; return; } } } else { fulfil( x ); } } else { fulfil( x ); } } }(); /* utils/normaliseRef.js */ var normaliseRef = function() { var regex = /\[\s*(\*|[0-9]|[1-9][0-9]+)\s*\]/g; return function normaliseRef( ref ) { return ( ref || '' ).replace( regex, '.$1' ); }; }(); /* shared/getInnerContext.js */ var getInnerContext = function( fragment ) { do { if ( fragment.context ) { return fragment.context; } } while ( fragment = fragment.parent ); return ''; }; /* utils/isEqual.js */ var isEqual = function( a, b ) { if ( a === null && b === null ) { return true; } if ( typeof a === 'object' || typeof b === 'object' ) { return false; } return a === b; }; /* shared/createComponentBinding.js */ var createComponentBinding = function( circular, isArray, isEqual ) { var runloop; circular.push( function() { return runloop = circular.runloop; } ); var Binding = function( ractive, keypath, otherInstance, otherKeypath, priority ) { this.root = ractive; this.keypath = keypath; this.priority = priority; this.otherInstance = otherInstance; this.otherKeypath = otherKeypath; this.bind(); this.value = this.root.viewmodel.get( this.keypath ); }; Binding.prototype = { setValue: function( value ) { var this$0 = this; // Only *you* can prevent infinite loops if ( this.updating || this.counterpart && this.counterpart.updating ) { this.value = value; return; } // Is this a smart array update? If so, it'll update on its // own, we shouldn't do anything if ( isArray( value ) && value._ractive && value._ractive.setting ) { return; } if ( !isEqual( value, this.value ) ) { this.updating = true; // TODO maybe the case that `value === this.value` - should that result // in an update rather than a set? runloop.addViewmodel( this.otherInstance.viewmodel ); this.otherInstance.viewmodel.set( this.otherKeypath, value ); this.value = value; // TODO will the counterpart update after this line, during // the runloop end cycle? may be a problem... runloop.scheduleTask( function() { return this$0.updating = false; } ); } }, bind: function() { this.root.viewmodel.register( this.keypath, this ); }, rebind: function( newKeypath ) { this.unbind(); this.keypath = newKeypath; this.counterpart.otherKeypath = newKeypath; this.bind(); }, unbind: function() { this.root.viewmodel.unregister( this.keypath, this ); } }; return function createComponentBinding( component, parentInstance, parentKeypath, childKeypath ) { var hash, childInstance, bindings, priority, parentToChildBinding, childToParentBinding; hash = parentKeypath + '=' + childKeypath; bindings = component.bindings; if ( bindings[ hash ] ) { // TODO does this ever happen? return; } bindings[ hash ] = true; childInstance = component.instance; priority = component.parentFragment.priority; parentToChildBinding = new Binding( parentInstance, parentKeypath, childInstance, childKeypath, priority ); bindings.push( parentToChildBinding ); if ( childInstance.twoway ) { childToParentBinding = new Binding( childInstance, childKeypath, parentInstance, parentKeypath, 1 ); bindings.push( childToParentBinding ); parentToChildBinding.counterpart = childToParentBinding; childToParentBinding.counterpart = parentToChildBinding; } }; }( circular, isArray, isEqual ); /* shared/resolveRef.js */ var resolveRef = function( normaliseRef, getInnerContext, createComponentBinding ) { var ancestorErrorMessage, getOptions; ancestorErrorMessage = 'Could not resolve reference - too many "../" prefixes'; getOptions = { evaluateWrapped: true }; return function resolveRef( ractive, ref, fragment ) { var context, key, index, keypath, parentValue, hasContextChain; ref = normaliseRef( ref ); // If a reference begins '~/', it's a top-level reference if ( ref.substr( 0, 2 ) === '~/' ) { return ref.substring( 2 ); } // If a reference begins with '.', it's either a restricted reference or // an ancestor reference... if ( ref.charAt( 0 ) === '.' ) { return resolveAncestorReference( getInnerContext( fragment ), ref ); } // ...otherwise we need to find the keypath key = ref.split( '.' )[ 0 ]; do { context = fragment.context; if ( !context ) { continue; } hasContextChain = true; parentValue = ractive.viewmodel.get( context, getOptions ); if ( parentValue && ( typeof parentValue === 'object' || typeof parentValue === 'function' ) && key in parentValue ) { return context + '.' + ref; } } while ( fragment = fragment.parent ); // Root/computed property? if ( key in ractive.data || key in ractive.viewmodel.computations ) { return ref; } // If this is an inline component, and it's not isolated, we // can try going up the scope chain if ( ractive._parent && !ractive.isolated ) { fragment = ractive.component.parentFragment; // Special case - index refs if ( fragment.indexRefs && ( index = fragment.indexRefs[ ref ] ) !== undefined ) { // Create an index ref binding, so that it can be rebound letter if necessary. // It doesn't have an alias since it's an implicit binding, hence `...[ ref ] = ref` ractive.component.indexRefBindings[ ref ] = ref; ractive.viewmodel.set( ref, index, true ); return; } keypath = resolveRef( ractive._parent, ref, fragment ); if ( keypath ) { // Need to create an inter-component binding ractive.viewmodel.set( ref, ractive._parent.viewmodel.get( keypath ), true ); createComponentBinding( ractive.component, ractive._parent, keypath, ref ); } } // If there's no context chain, and the instance is either a) isolated or // b) an orphan, then we know that the keypath is identical to the reference if ( !hasContextChain ) { return ref; } if ( ractive.viewmodel.get( ref ) !== undefined ) { return ref; } }; function resolveAncestorReference( baseContext, ref ) { var contextKeys; // {{.}} means 'current context' if ( ref === '.' ) return baseContext; contextKeys = baseContext ? baseContext.split( '.' ) : []; // ancestor references (starting "../") go up the tree if ( ref.substr( 0, 3 ) === '../' ) { while ( ref.substr( 0, 3 ) === '../' ) { if ( !contextKeys.length ) { throw new Error( ancestorErrorMessage ); } contextKeys.pop(); ref = ref.substring( 3 ); } contextKeys.push( ref ); return contextKeys.join( '.' ); } // not an ancestor reference - must be a restricted reference (prepended with "." or "./") if ( !baseContext ) { return ref.replace( /^\.\/?/, '' ); } return baseContext + ref.replace( /^\.\//, '.' ); } }( normaliseRef, getInnerContext, createComponentBinding ); /* global/TransitionManager.js */ var TransitionManager = function( removeFromArray ) { var TransitionManager = function( callback, parent ) { this.callback = callback; this.parent = parent; this.intros = []; this.outros = []; this.children = []; this.totalChildren = this.outroChildren = 0; this.detachQueue = []; this.outrosComplete = false; if ( parent ) { parent.addChild( this ); } }; TransitionManager.prototype = { addChild: function( child ) { this.children.push( child ); this.totalChildren += 1; this.outroChildren += 1; }, decrementOutros: function() { this.outroChildren -= 1; check( this ); }, decrementTotal: function() { this.totalChildren -= 1; check( this ); }, add: function( transition ) { var list = transition.isIntro ? this.intros : this.outros; list.push( transition ); }, remove: function( transition ) { var list = transition.isIntro ? this.intros : this.outros; removeFromArray( list, transition ); check( this ); }, init: function() { this.ready = true; check( this ); }, detachNodes: function() { this.detachQueue.forEach( detach ); this.children.forEach( detachNodes ); } }; function detach( element ) { element.detach(); } function detachNodes( tm ) { tm.detachNodes(); } function check( tm ) { if ( !tm.ready || tm.outros.length || tm.outroChildren ) return; // If all outros are complete, and we haven't already done this, // we notify the parent if there is one, otherwise // start detaching nodes if ( !tm.outrosComplete ) { if ( tm.parent ) { tm.parent.decrementOutros( tm ); } else { tm.detachNodes(); } tm.outrosComplete = true; } // Once everything is done, we can notify parent transition // manager and call the callback if ( !tm.intros.length && !tm.totalChildren ) { if ( typeof tm.callback === 'function' ) { tm.callback(); } if ( tm.parent ) { tm.parent.decrementTotal(); } } } return TransitionManager; }( removeFromArray ); /* global/runloop.js */ var runloop = function( circular, removeFromArray, Promise, resolveRef, TransitionManager ) { var batch, runloop, unresolved = []; runloop = { start: function( instance, returnPromise ) { var promise, fulfilPromise; if ( returnPromise ) { promise = new Promise( function( f ) { return fulfilPromise = f; } ); } batch = { previousBatch: batch, transitionManager: new TransitionManager( fulfilPromise, batch && batch.transitionManager ), views: [], tasks: [], viewmodels: [] }; if ( instance ) { batch.viewmodels.push( instance.viewmodel ); } return promise; }, end: function() { flushChanges(); batch.transitionManager.init(); batch = batch.previousBatch; }, addViewmodel: function( viewmodel ) { if ( batch ) { if ( batch.viewmodels.indexOf( viewmodel ) === -1 ) { batch.viewmodels.push( viewmodel ); } } else { viewmodel.applyChanges(); } }, registerTransition: function( transition ) { transition._manager = batch.transitionManager; batch.transitionManager.add( transition ); }, addView: function( view ) { batch.views.push( view ); }, addUnresolved: function( thing ) { unresolved.push( thing ); }, removeUnresolved: function( thing ) { removeFromArray( unresolved, thing ); }, // synchronise node detachments with transition ends detachWhenReady: function( thing ) { batch.transitionManager.detachQueue.push( thing ); }, scheduleTask: function( task ) { if ( !batch ) { task(); } else { batch.tasks.push( task ); } } }; circular.runloop = runloop; return runloop; function flushChanges() { var i, thing, changeHash; for ( i = 0; i < batch.viewmodels.length; i += 1 ) { thing = batch.viewmodels[ i ]; changeHash = thing.applyChanges(); if ( changeHash ) { thing.ractive.fire( 'change', changeHash ); } } batch.viewmodels.length = 0; attemptKeypathResolution(); // Now that changes have been fully propagated, we can update the DOM // and complete other tasks for ( i = 0; i < batch.views.length; i += 1 ) { batch.views[ i ].update(); } batch.views.length = 0; for ( i = 0; i < batch.tasks.length; i += 1 ) { batch.tasks[ i ](); } batch.tasks.length = 0; // If updating the view caused some model blowback - e.g. a triple // containing <option> elements caused the binding on the <select> // to update - then we start over if ( batch.viewmodels.length ) return flushChanges(); } function attemptKeypathResolution() { var array, thing, keypath; if ( !unresolved.length ) { return; } // see if we can resolve any unresolved references array = unresolved.splice( 0, unresolved.length ); while ( thing = array.pop() ) { if ( thing.keypath ) { continue; } keypath = resolveRef( thing.root, thing.ref, thing.parentFragment ); if ( keypath !== undefined ) { // If we've resolved the keypath, we can initialise this item thing.resolve( keypath ); } else { // If we can't resolve the reference, try again next time unresolved.push( thing ); } } } }( circular, removeFromArray, Promise, resolveRef, TransitionManager ); /* utils/createBranch.js */ var createBranch = function() { var numeric = /^\s*[0-9]+\s*$/; return function( key ) { return numeric.test( key ) ? [] : {}; }; }(); /* viewmodel/prototype/get/magicAdaptor.js */ var viewmodel$get_magicAdaptor = function( runloop, createBranch, isArray ) { var magicAdaptor, MagicWrapper; try { Object.defineProperty( {}, 'test', { value: 0 } ); magicAdaptor = { filter: function( object, keypath, ractive ) { var keys, key, parentKeypath, parentWrapper, parentValue; if ( !keypath ) { return false; } keys = keypath.split( '.' ); key = keys.pop(); parentKeypath = keys.join( '.' ); // If the parent value is a wrapper, other than a magic wrapper, // we shouldn't wrap this property if ( ( parentWrapper = ractive.viewmodel.wrapped[ parentKeypath ] ) && !parentWrapper.magic ) { return false; } parentValue = ractive.get( parentKeypath ); // if parentValue is an array that doesn't include this member, // we should return false otherwise lengths will get messed up if ( isArray( parentValue ) && /^[0-9]+$/.test( key ) ) { return false; } return parentValue && ( typeof parentValue === 'object' || typeof parentValue === 'function' ); }, wrap: function( ractive, property, keypath ) { return new MagicWrapper( ractive, property, keypath ); } }; MagicWrapper = function( ractive, value, keypath ) { var keys, objKeypath, template, siblings; this.magic = true; this.ractive = ractive; this.keypath = keypath; this.value = value; keys = keypath.split( '.' ); this.prop = keys.pop(); objKeypath = keys.join( '.' ); this.obj = objKeypath ? ractive.get( objKeypath ) : ractive.data; template = this.originalDescriptor = Object.getOwnPropertyDescriptor( this.obj, this.prop ); // Has this property already been wrapped? if ( template && template.set && ( siblings = template.set._ractiveWrappers ) ) { // Yes. Register this wrapper to this property, if it hasn't been already if ( siblings.indexOf( this ) === -1 ) { siblings.push( this ); } return; } // No, it hasn't been wrapped createAccessors( this, value, template ); }; MagicWrapper.prototype = { get: function() { return this.value; }, reset: function( value ) { if ( this.updating ) { return; } this.updating = true; this.obj[ this.prop ] = value; // trigger set() accessor runloop.addViewmodel( this.ractive.viewmodel ); this.ractive.viewmodel.mark( this.keypath ); this.updating = false; }, set: function( key, value ) { if ( this.updating ) { return; } if ( !this.obj[ this.prop ] ) { this.updating = true; this.obj[ this.prop ] = createBranch( key ); this.updating = false; } this.obj[ this.prop ][ key ] = value; }, teardown: function() { var template, set, value, wrappers, index; // If this method was called because the cache was being cleared as a // result of a set()/update() call made by this wrapper, we return false // so that it doesn't get torn down if ( this.updating ) { return false; } template = Object.getOwnPropertyDescriptor( this.obj, this.prop ); set = template && template.set; if ( !set ) { // most likely, this was an array member that was spliced out return; } wrappers = set._ractiveWrappers; index = wrappers.indexOf( this ); if ( index !== -1 ) { wrappers.splice( index, 1 ); } // Last one out, turn off the lights if ( !wrappers.length ) { value = this.obj[ this.prop ]; Object.defineProperty( this.obj, this.prop, this.originalDescriptor || { writable: true, enumerable: true, configurable: true } ); this.obj[ this.prop ] = value; } } }; } catch ( err ) { magicAdaptor = false; } return magicAdaptor; function createAccessors( originalWrapper, value, template ) { var object, property, oldGet, oldSet, get, set; object = originalWrapper.obj; property = originalWrapper.prop; // Is this template configurable? if ( template && !template.configurable ) { // Special case - array length if ( property === 'length' ) { return; } throw new Error( 'Cannot use magic mode with property "' + property + '" - object is not configurable' ); } // Time to wrap this property if ( template ) { oldGet = template.get; oldSet = template.set; } get = oldGet || function() { return value; }; set = function( v ) { if ( oldSet ) { oldSet( v ); } value = oldGet ? oldGet() : v; set._ractiveWrappers.forEach( updateWrapper ); }; function updateWrapper( wrapper ) { var keypath, ractive; wrapper.value = value; if ( wrapper.updating ) { return; } ractive = wrapper.ractive; keypath = wrapper.keypath; wrapper.updating = true; runloop.start( ractive ); ractive.viewmodel.mark( keypath ); runloop.end(); wrapper.updating = false; } // Create an array of wrappers, in case other keypaths/ractives depend on this property. // Handily, we can store them as a property of the set function. Yay JavaScript. set._ractiveWrappers = [ originalWrapper ]; Object.defineProperty( object, property, { get: get, set: set, enumerable: true, configurable: true } ); } }( runloop, createBranch, isArray ); /* config/magic.js */ var magic = function( magicAdaptor ) { return !!magicAdaptor; }( viewmodel$get_magicAdaptor ); /* config/namespaces.js */ var namespaces = { html: 'http://www.w3.org/1999/xhtml', mathml: 'http://www.w3.org/1998/Math/MathML', svg: 'http://www.w3.org/2000/svg', xlink: 'http://www.w3.org/1999/xlink', xml: 'http://www.w3.org/XML/1998/namespace', xmlns: 'http://www.w3.org/2000/xmlns/' }; /* utils/createElement.js */ var createElement = function( svg, namespaces ) { var createElement; // Test for SVG support if ( !svg ) { createElement = function( type, ns ) { if ( ns && ns !== namespaces.html ) { throw 'This browser does not support namespaces other than http://www.w3.org/1999/xhtml. The most likely cause of this error is that you\'re trying to render SVG in an older browser. See http://docs.ractivejs.org/latest/svg-and-older-browsers for more information'; } return document.createElement( type ); }; } else { createElement = function( type, ns ) { if ( !ns || ns === namespaces.html ) { return document.createElement( type ); } return document.createElementNS( ns, type ); }; } return createElement; }( svg, namespaces ); /* config/isClient.js */ var isClient = function() { var isClient = typeof document === 'object'; return isClient; }(); /* utils/defineProperty.js */ var defineProperty = function( isClient ) { var defineProperty; try { Object.defineProperty( {}, 'test', { value: 0 } ); if ( isClient ) { Object.defineProperty( document.createElement( 'div' ), 'test', { value: 0 } ); } defineProperty = Object.defineProperty; } catch ( err ) { // Object.defineProperty doesn't exist, or we're in IE8 where you can // only use it with DOM objects (what the fuck were you smoking, MSFT?) defineProperty = function( obj, prop, desc ) { obj[ prop ] = desc.value; }; } return defineProperty; }( isClient ); /* utils/defineProperties.js */ var defineProperties = function( createElement, defineProperty, isClient ) { var defineProperties; try { try { Object.defineProperties( {}, { test: { value: 0 } } ); } catch ( err ) { // TODO how do we account for this? noMagic = true; throw err; } if ( isClient ) { Object.defineProperties( createElement( 'div' ), { test: { value: 0 } } ); } defineProperties = Object.defineProperties; } catch ( err ) { defineProperties = function( obj, props ) { var prop; for ( prop in props ) { if ( props.hasOwnProperty( prop ) ) { defineProperty( obj, prop, props[ prop ] ); } } }; } return defineProperties; }( createElement, defineProperty, isClient ); /* Ractive/prototype/shared/add.js */ var Ractive$shared_add = function( isNumeric ) { return function add( root, keypath, d ) { var value; if ( typeof keypath !== 'string' || !isNumeric( d ) ) { throw new Error( 'Bad arguments' ); } value = +root.get( keypath ) || 0; if ( !isNumeric( value ) ) { throw new Error( 'Cannot add to a non-numeric value' ); } return root.set( keypath, value + d ); }; }( isNumeric ); /* Ractive/prototype/add.js */ var Ractive$add = function( add ) { return function Ractive$add( keypath, d ) { return add( this, keypath, d === undefined ? 1 : +d ); }; }( Ractive$shared_add ); /* utils/normaliseKeypath.js */ var normaliseKeypath = function( normaliseRef ) { var leadingDot = /^\.+/; return function normaliseKeypath( keypath ) { return normaliseRef( keypath ).replace( leadingDot, '' ); }; }( normaliseRef ); /* config/vendors.js */ var vendors = [ 'o', 'ms', 'moz', 'webkit' ]; /* utils/requestAnimationFrame.js */ var requestAnimationFrame = function( vendors ) { var requestAnimationFrame; // If window doesn't exist, we don't need requestAnimationFrame if ( typeof window === 'undefined' ) { requestAnimationFrame = null; } else { // https://gist.github.com/paulirish/1579671 ( function( vendors, lastTime, window ) { var x, setTimeout; if ( window.requestAnimationFrame ) { return; } for ( x = 0; x < vendors.length && !window.requestAnimationFrame; ++x ) { window.requestAnimationFrame = window[ vendors[ x ] + 'RequestAnimationFrame' ]; } if ( !window.requestAnimationFrame ) { setTimeout = window.setTimeout; window.requestAnimationFrame = function( callback ) { var currTime, timeToCall, id; currTime = Date.now(); timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); id = setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall ); lastTime = currTime + timeToCall; return id; }; } }( vendors, 0, window ) ); requestAnimationFrame = window.requestAnimationFrame; } return requestAnimationFrame; }( vendors ); /* utils/getTime.js */ var getTime = function() { var getTime; if ( typeof window !== 'undefined' && window.performance && typeof window.performance.now === 'function' ) { getTime = function() { return window.performance.now(); }; } else { getTime = function() { return Date.now(); }; } return getTime; }(); /* shared/animations.js */ var animations = function( rAF, getTime, runloop ) { var queue = []; var animations = { tick: function() { var i, animation, now; now = getTime(); runloop.start(); for ( i = 0; i < queue.length; i += 1 ) { animation = queue[ i ]; if ( !animation.tick( now ) ) { // animation is complete, remove it from the stack, and decrement i so we don't miss one queue.splice( i--, 1 ); } } runloop.end(); if ( queue.length ) { rAF( animations.tick ); } else { animations.running = false; } }, add: function( animation ) { queue.push( animation ); if ( !animations.running ) { animations.running = true; rAF( animations.tick ); } }, // TODO optimise this abort: function( keypath, root ) { var i = queue.length, animation; while ( i-- ) { animation = queue[ i ]; if ( animation.root === root && animation.keypath === keypath ) { animation.stop(); } } } }; return animations; }( requestAnimationFrame, getTime, runloop ); /* utils/warn.js */ var warn = function() { /* global console */ var warn, warned = {}; if ( typeof console !== 'undefined' && typeof console.warn === 'function' && typeof console.warn.apply === 'function' ) { warn = function( message, allowDuplicates ) { if ( !allowDuplicates ) { if ( warned[ message ] ) { return; } warned[ message ] = true; } console.warn( message ); }; } else { warn = function() {}; } return warn; }(); /* config/options/css/transform.js */ var transform = function() { var selectorsPattern = /(?:^|\})?\s*([^\{\}]+)\s*\{/g, commentsPattern = /\/\*.*?\*\//g, selectorUnitPattern = /((?:(?:\[[^\]+]\])|(?:[^\s\+\>\~:]))+)((?::[^\s\+\>\~]+)?\s*[\s\+\>\~]?)\s*/g, mediaQueryPattern = /^@media/, dataRvcGuidPattern = /\[data-rvcguid="[a-z0-9-]+"]/g; return function transformCss( css, guid ) { var transformed, addGuid; addGuid = function( selector ) { var selectorUnits, match, unit, dataAttr, base, prepended, appended, i, transformed = []; selectorUnits = []; while ( match = selectorUnitPattern.exec( selector ) ) { selectorUnits.push( { str: match[ 0 ], base: match[ 1 ], modifiers: match[ 2 ] } ); } // For each simple selector within the selector, we need to create a version // that a) combines with the guid, and b) is inside the guid dataAttr = '[data-rvcguid="' + guid + '"]'; base = selectorUnits.map( extractString ); i = selectorUnits.length; while ( i-- ) { appended = base.slice(); // Pseudo-selectors should go after the attribute selector unit = selectorUnits[ i ]; appended[ i ] = unit.base + dataAttr + unit.modifiers || ''; prepended = base.slice(); prepended[ i ] = dataAttr + ' ' + prepended[ i ]; transformed.push( appended.join( ' ' ), prepended.join( ' ' ) ); } return transformed.join( ', ' ); }; if ( dataRvcGuidPattern.test( css ) ) { transformed = css.replace( dataRvcGuidPattern, '[data-rvcguid="' + guid + '"]' ); } else { transformed = css.replace( commentsPattern, '' ).replace( selectorsPattern, function( match, $1 ) { var selectors, transformed; // don't transform media queries! if ( mediaQueryPattern.test( $1 ) ) return match; selectors = $1.split( ',' ).map( trim ); transformed = selectors.map( addGuid ).join( ', ' ) + ' '; return match.replace( $1, transformed ); } ); } return transformed; }; function trim( str ) { if ( str.trim ) { return str.trim(); } return str.replace( /^\s+/, '' ).replace( /\s+$/, '' ); } function extractString( unit ) { return unit.str; } }(); /* config/options/css/css.js */ var css = function( transformCss ) { var cssConfig = { name: 'css', extend: extend, init: function() {} }; function extend( Parent, proto, options ) { var guid = proto.constructor._guid, css; if ( css = getCss( options.css, options, guid ) || getCss( Parent.css, Parent, guid ) ) { proto.constructor.css = css; } } function getCss( css, target, guid ) { if ( !css ) { return; } return target.noCssTransform ? css : transformCss( css, guid ); } return cssConfig; }( transform ); /* utils/wrapMethod.js */ var wrapMethod = function() { return function( method, superMethod, force ) { if ( force || needsSuper( method, superMethod ) ) { return function() { var hasSuper = '_super' in this, _super = this._super, result; this._super = superMethod; result = method.apply( this, arguments ); if ( hasSuper ) { this._super = _super; } return result; }; } else { return method; } }; function needsSuper( method, superMethod ) { return typeof superMethod === 'function' && /_super/.test( method ); } }(); /* config/options/data.js */ var data = function( wrap ) { var dataConfig = { name: 'data', extend: extend, init: init, reset: reset }; return dataConfig; function combine( Parent, target, options ) { var value = options.data || {}, parentValue = getAddedKeys( Parent.prototype.data ); return dispatch( parentValue, value ); } function extend( Parent, proto, options ) { proto.data = combine( Parent, proto, options ); } function init( Parent, ractive, options ) { var value = options.data, result = combine( Parent, ractive, options ); if ( typeof result === 'function' ) { result = result.call( ractive, value ) || value; } return ractive.data = result || {}; } function reset( ractive ) { var result = this.init( ractive.constructor, ractive, ractive ); if ( result ) { ractive.data = result; return true; } } function getAddedKeys( parent ) { // only for functions that had keys added if ( typeof parent !== 'function' || !Object.keys( parent ).length ) { return parent; } // copy the added keys to temp 'object', otherwise // parent would be interpreted as 'function' by dispatch var temp = {}; copy( parent, temp ); // roll in added keys return dispatch( parent, temp ); } function dispatch( parent, child ) { if ( typeof child === 'function' ) { return extendFn( child, parent ); } else if ( typeof parent === 'function' ) { return fromFn( child, parent ); } else { return fromProperties( child, parent ); } } function copy( from, to, fillOnly ) { for ( var key in from ) { if ( fillOnly && key in to ) { continue; } to[ key ] = from[ key ]; } } function fromProperties( child, parent ) { child = child || {}; if ( !parent ) { return child; } copy( parent, child, true ); return child; } function fromFn( child, parentFn ) { return function( data ) { var keys; if ( child ) { // Track the keys that our on the child, // but not on the data. We'll need to apply these // after the parent function returns. keys = []; for ( var key in child ) { if ( !data || !( key in data ) ) { keys.push( key ); } } } // call the parent fn, use data if no return value data = parentFn.call( this, data ) || data; // Copy child keys back onto data. The child keys // should take precedence over whatever the // parent did with the data. if ( keys && keys.length ) { data = data || {}; keys.forEach( function( key ) { data[ key ] = child[ key ]; } ); } return data; }; } function extendFn( childFn, parent ) { var parentFn; if ( typeof parent !== 'function' ) { // copy props to data parentFn = function( data ) { fromProperties( data, parent ); }; } else { parentFn = function( data ) { // give parent function it's own this._super context, // otherwise this._super is from child and // causes infinite loop parent = wrap( parent, function() {}, true ); return parent.call( this, data ) || data; }; } return wrap( childFn, parentFn ); } }( wrapMethod ); /* config/errors.js */ var errors = { missingParser: 'Missing Ractive.parse - cannot parse template. Either preparse or use the version that includes the parser', mergeComparisonFail: 'Merge operation: comparison failed. Falling back to identity checking', noComponentEventArguments: 'Components currently only support simple events - you cannot include arguments. Sorry!', noTemplateForPartial: 'Could not find template for partial "{name}"', noNestedPartials: 'Partials ({{>{name}}}) cannot contain nested inline partials', evaluationError: 'Error evaluating "{uniqueString}": {err}', badArguments: 'Bad arguments "{arguments}". I\'m not allowed to argue unless you\'ve paid.', failedComputation: 'Failed to compute "{key}": {err}', missingPlugin: 'Missing "{name}" {plugin} plugin. You may need to download a {plugin} via http://docs.ractivejs.org/latest/plugins#{plugin}s', badRadioInputBinding: 'A radio input can have two-way binding on its name attribute, or its checked attribute - not both', noRegistryFunctionReturn: 'A function was specified for "{name}" {registry}, but no {registry} was returned' }; /* config/types.js */ var types = { TEXT: 1, INTERPOLATOR: 2, TRIPLE: 3, SECTION: 4, INVERTED: 5, CLOSING: 6, ELEMENT: 7, PARTIAL: 8, COMMENT: 9, DELIMCHANGE: 10, MUSTACHE: 11, TAG: 12, ATTRIBUTE: 13, CLOSING_TAG: 14, COMPONENT: 15, NUMBER_LITERAL: 20, STRING_LITERAL: 21, ARRAY_LITERAL: 22, OBJECT_LITERAL: 23, BOOLEAN_LITERAL: 24, GLOBAL: 26, KEY_VALUE_PAIR: 27, REFERENCE: 30, REFINEMENT: 31, MEMBER: 32, PREFIX_OPERATOR: 33, BRACKETED: 34, CONDITIONAL: 35, INFIX_OPERATOR: 36, INVOCATION: 40, SECTION_IF: 50, SECTION_UNLESS: 51, SECTION_EACH: 52, SECTION_WITH: 53 }; /* utils/create.js */ var create = function() { var create; try { Object.create( null ); create = Object.create; } catch ( err ) { // sigh create = function() { var F = function() {}; return function( proto, props ) { var obj; if ( proto === null ) { return {}; } F.prototype = proto; obj = new F(); if ( props ) { Object.defineProperties( obj, props ); } return obj; }; }(); } return create; }(); /* parse/Parser/expressions/shared/errors.js */ var parse_Parser_expressions_shared_errors = { expectedExpression: 'Expected a JavaScript expression', expectedParen: 'Expected closing paren' }; /* parse/Parser/expressions/primary/literal/numberLiteral.js */ var numberLiteral = function( types ) { // bulletproof number regex from https://gist.github.com/Rich-Harris/7544330 var numberPattern = /^(?:[+-]?)(?:(?:(?:0|[1-9]\d*)?\.\d+)|(?:(?:0|[1-9]\d*)\.)|(?:0|[1-9]\d*))(?:[eE][+-]?\d+)?/; return function( parser ) { var result; if ( result = parser.matchPattern( numberPattern ) ) { return { t: types.NUMBER_LITERAL, v: result }; } return null; }; }( types ); /* parse/Parser/expressions/primary/literal/booleanLiteral.js */ var booleanLiteral = function( types ) { return function( parser ) { var remaining = parser.remaining(); if ( remaining.substr( 0, 4 ) === 'true' ) { parser.pos += 4; return { t: types.BOOLEAN_LITERAL, v: 'true' }; } if ( remaining.substr( 0, 5 ) === 'false' ) { parser.pos += 5; return { t: types.BOOLEAN_LITERAL, v: 'false' }; } return null; }; }( types ); /* parse/Parser/expressions/primary/literal/stringLiteral/makeQuotedStringMatcher.js */ var makeQuotedStringMatcher = function() { var stringMiddlePattern, escapeSequencePattern, lineContinuationPattern; // Match one or more characters until: ", ', \, or EOL/EOF. // EOL/EOF is written as (?!.) (meaning there's no non-newline char next). stringMiddlePattern = /^(?=.)[^"'\\]+?(?:(?!.)|(?=["'\\]))/; // Match one escape sequence, including the backslash. escapeSequencePattern = /^\\(?:['"\\bfnrt]|0(?![0-9])|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|(?=.)[^ux0-9])/; // Match one ES5 line continuation (backslash + line terminator). lineContinuationPattern = /^\\(?:\r\n|[\u000A\u000D\u2028\u2029])/; // Helper for defining getDoubleQuotedString and getSingleQuotedString. return function( okQuote ) { return function( parser ) { var start, literal, done, next; start = parser.pos; literal = '"'; done = false; while ( !done ) { next = parser.matchPattern( stringMiddlePattern ) || parser.matchPattern( escapeSequencePattern ) || parser.matchString( okQuote ); if ( next ) { if ( next === '"' ) { literal += '\\"'; } else if ( next === '\\\'' ) { literal += '\''; } else { literal += next; } } else { next = parser.matchPattern( lineContinuationPattern ); if ( next ) { // convert \(newline-like) into a \u escape, which is allowed in JSON literal += '\\u' + ( '000' + next.charCodeAt( 1 ).toString( 16 ) ).slice( -4 ); } else { done = true; } } } literal += '"'; // use JSON.parse to interpret escapes return JSON.parse( literal ); }; }; }(); /* parse/Parser/expressions/primary/literal/stringLiteral/singleQuotedString.js */ var singleQuotedString = function( makeQuotedStringMatcher ) { return makeQuotedStringMatcher( '"' ); }( makeQuotedStringMatcher ); /* parse/Parser/expressions/primary/literal/stringLiteral/doubleQuotedString.js */ var doubleQuotedString = function( makeQuotedStringMatcher ) { return makeQuotedStringMatcher( '\'' ); }( makeQuotedStringMatcher ); /* parse/Parser/expressions/primary/literal/stringLiteral/_stringLiteral.js */ var stringLiteral = function( types, getSingleQuotedString, getDoubleQuotedString ) { return function( parser ) { var start, string; start = parser.pos; if ( parser.matchString( '"' ) ) { string = getDoubleQuotedString( parser ); if ( !parser.matchString( '"' ) ) { parser.pos = start; return null; } return { t: types.STRING_LITERAL, v: string }; } if ( parser.matchString( '\'' ) ) { string = getSingleQuotedString( parser ); if ( !parser.matchString( '\'' ) ) { parser.pos = start; return null; } return { t: types.STRING_LITERAL, v: string }; } return null; }; }( types, singleQuotedString, doubleQuotedString ); /* parse/Parser/expressions/shared/patterns.js */ var patterns = { name: /^[a-zA-Z_$][a-zA-Z_$0-9]*/ }; /* parse/Parser/expressions/shared/key.js */ var key = function( getStringLiteral, getNumberLiteral, patterns ) { var identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/; // http://mathiasbynens.be/notes/javascript-properties // can be any name, string literal, or number literal return function( parser ) { var token; if ( token = getStringLiteral( parser ) ) { return identifier.test( token.v ) ? token.v : '"' + token.v.replace( /"/g, '\\"' ) + '"'; } if ( token = getNumberLiteral( parser ) ) { return token.v; } if ( token = parser.matchPattern( patterns.name ) ) { return token; } }; }( stringLiteral, numberLiteral, patterns ); /* parse/Parser/expressions/primary/literal/objectLiteral/keyValuePair.js */ var keyValuePair = function( types, getKey ) { return function( parser ) { var start, key, value; start = parser.pos; // allow whitespace between '{' and key parser.allowWhitespace(); key = getKey( parser ); if ( key === null ) { parser.pos = start; return null; } // allow whitespace between key and ':' parser.allowWhitespace(); // next character must be ':' if ( !parser.matchString( ':' ) ) { parser.pos = start; return null; } // allow whitespace betwee