/* ractive.js v0.6.1 2014-10-25 - commit 3a576eb3 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() { var defaultOptions = { // render placement: el: void 0, append: false, // template: template: { v: 1, t: [] }, yield: null, // parse: preserveWhitespace: false, sanitize: false, stripComments: true, // 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; }; } }; 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/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( '%cRactive.js: %c' + message, 'color: rgb(114, 157, 52);', 'color: rgb(85, 85, 85);' ); }; } else { warn = function() {}; } return warn; }(); /* 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', defaultElSpecified: 'The <{name}/> component has a default `el` property; it has been disregarded', noElementProxyEventWildcards: 'Only component proxy-events may contain "*" wildcards, <{element} on-{event}/> is not valid.', methodDeprecated: 'The method "{deprecated}" has been deprecated in favor of "{replacement}" and will likely be removed in a future release. See http://docs.ractivejs.org/latest/migrating for more information.' }; /* utils/log.js */ var log = function( consolewarn, errors ) { var log = { warn: function( options, passthru ) { if ( !options.debug && !passthru ) { return; } this.warnAlways( options ); }, warnAlways: function( options ) { this.logger( getMessage( options ), options.allowDuplicates ); }, error: function( options ) { this.errorOnly( options ); if ( !options.debug ) { this.warn( options, true ); } }, errorOnly: function( options ) { if ( options.debug ) { this.critical( options ); } }, critical: function( options ) { var err = options.err || new Error( getMessage( options ) ); this.thrower( err ); }, logger: consolewarn, thrower: function( err ) { throw err; } }; function getMessage( options ) { var message = errors[ options.message ] || options.message || ''; return interpolate( message, options.args ); } // simple interpolation. probably quicker (and better) out there, // but log is not in golden path of execution, only exceptions function interpolate( message, args ) { return message.replace( /{([^{}]*)}/g, function( a, b ) { return args[ b ]; } ); } return log; }( warn, errors ); /* Ractive/prototype/shared/hooks/Hook.js */ var Ractive$shared_hooks_Hook = function( log ) { var deprecations = { construct: { deprecated: 'beforeInit', replacement: 'onconstruct' }, render: { deprecated: 'init', message: 'The "init" method has been deprecated ' + 'and will likely be removed in a future release. ' + 'You can either use the "oninit" method which will fire ' + 'only once prior to, and regardless of, any eventual ractive ' + 'instance being rendered, or if you need to access the ' + 'rendered DOM, use "onrender" instead. ' + 'See http://docs.ractivejs.org/latest/migrating for more information.' }, complete: { deprecated: 'complete', replacement: 'oncomplete' } }; function Hook( event ) { this.event = event; this.method = 'on' + event; this.deprecate = deprecations[ event ]; } Hook.prototype.fire = function( ractive, arg ) { function call( method ) { if ( ractive[ method ] ) { arg ? ractive[ method ]( arg ) : ractive[ method ](); return true; } } call( this.method ); if ( !ractive[ this.method ] && this.deprecate && call( this.deprecate.deprecated ) ) { log.warnAlways( { debug: ractive.debug, message: this.deprecate.message || 'methodDeprecated', args: this.deprecate } ); } arg ? ractive.fire( this.event, arg ) : ractive.fire( this.event ); }; return Hook; }( log ); /* 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 __export; 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 ); } ); }; } __export = _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 ); } } return __export; }(); /* 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 !== undefined ) { 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, isEqual ) { var runloop; circular.push( function() { return runloop = circular.runloop; } ); var Binding = function( ractive, keypath, otherInstance, otherKeypath ) { var this$0 = this; this.root = ractive; this.keypath = keypath; this.otherInstance = otherInstance; this.otherKeypath = otherKeypath; this.lock = function() { return this$0.updating = true; }; this.unlock = function() { return this$0.updating = false; }; this.bind(); this.value = this.root.viewmodel.get( this.keypath ); }; Binding.prototype = { isLocked: function() { return this.updating || this.counterpart && this.counterpart.updating; }, shuffle: function( newIndices, value ) { this.propagateChange( value, newIndices ); }, setValue: function( value ) { this.propagateChange( value ); }, propagateChange: function( value, newIndices ) { var other; // Only *you* can prevent infinite loops if ( this.isLocked() ) { this.value = value; return; } if ( !isEqual( value, this.value ) ) { this.lock(); // TODO maybe the case that `value === this.value` - should that result // in an update rather than a set? // if the other viewmodel is already locked up, need to do a deferred update if ( !runloop.addViewmodel( other = this.otherInstance.viewmodel ) && this.counterpart.value !== value ) { runloop.scheduleTask( function() { return runloop.addViewmodel( other ); } ); } if ( newIndices ) { other.smartUpdate( this.otherKeypath, value, newIndices ); } else { if ( isSettable( other, this.otherKeypath ) ) { other.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( this.unlock ); } }, refineValue: function( keypaths ) { var this$0 = this; var other; if ( this.isLocked() ) { return; } this.lock(); runloop.addViewmodel( other = this.otherInstance.viewmodel ); keypaths.map( function( keypath ) { return this$0.otherKeypath + keypath.substr( this$0.keypath.length ); } ).forEach( function( keypath ) { return other.mark( keypath ); } ); runloop.scheduleTask( this.unlock ); }, 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 ); } }; function isSettable( viewmodel, keypath ) { var computed = viewmodel.computations[ keypath ]; return !computed || computed.setter; } return function createComponentBinding( component, parentInstance, parentKeypath, childKeypath ) { var hash, childInstance, bindings, parentToChildBinding, childToParentBinding; hash = parentKeypath + '=' + childKeypath; bindings = component.bindings; if ( bindings[ hash ] ) { // TODO does this ever happen? return; } childInstance = component.instance; parentToChildBinding = new Binding( parentInstance, parentKeypath, childInstance, childKeypath ); bindings.push( parentToChildBinding ); if ( childInstance.twoway ) { childToParentBinding = new Binding( childInstance, childKeypath, parentInstance, parentKeypath ); bindings.push( childToParentBinding ); parentToChildBinding.counterpart = childToParentBinding; childToParentBinding.counterpart = parentToChildBinding; } bindings[ hash ] = parentToChildBinding; }; }( circular, isEqual ); /* shared/resolveRef.js */ var resolveRef = function( normaliseRef, getInnerContext, createComponentBinding ) { var __export; var ancestorErrorMessage, getOptions; ancestorErrorMessage = 'Could not resolve reference - too many "../" prefixes'; getOptions = { evaluateWrapped: true }; __export = function resolveRef( ractive, ref, fragment, isParentLookup ) { var context, key, index, keypath, parentValue, hasContextChain, parentKeys, childKeys, parentKeypath, childKeypath; 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 ]; // get() in viewmodel creation means no fragment (yet) fragment = fragment || {}; 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 ) { hasContextChain = true; 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, true ); if ( keypath ) { // We need to create an inter-component binding // If parent keypath is 'one.foo' and child is 'two.foo', we bind // 'one' to 'two' as it's more efficient and avoids edge cases parentKeys = keypath.split( '.' ); childKeys = ref.split( '.' ); while ( parentKeys.length > 1 && childKeys.length > 1 && parentKeys[ parentKeys.length - 1 ] === childKeys[ childKeys.length - 1 ] ) { parentKeys.pop(); childKeys.pop(); } parentKeypath = parentKeys.join( '.' ); childKeypath = childKeys.join( '.' ); ractive.viewmodel.set( childKeypath, ractive._parent.viewmodel.get( parentKeypath ), true ); createComponentBinding( ractive.component, ractive._parent, parentKeypath, childKeypath ); return ref; } } // If there's no context chain, and the instance is either a) isolated or // b) an orphan, then we know that the keypath is identical to the reference if ( !isParentLookup && !hasContextChain ) { // the data object needs to have a property by this name, // to prevent future failed lookups ractive.viewmodel.set( ref, undefined ); return ref; } 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( /^\.\//, '.' ); } return __export; }( 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, Hook, removeFromArray, Promise, resolveRef, TransitionManager ) { var __export; var batch, runloop, unresolved = [], changeHook = new Hook( 'change' ); 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: [], instance: instance }; if ( instance ) { batch.viewmodels.push( instance.viewmodel ); } return promise; }, end: function() { flushChanges(); batch.transitionManager.init(); if ( !batch.previousBatch && !!batch.instance ) batch.instance.viewmodel.changes = []; batch = batch.previousBatch; }, addViewmodel: function( viewmodel ) { if ( batch ) { if ( batch.viewmodels.indexOf( viewmodel ) === -1 ) { batch.viewmodels.push( viewmodel ); return true; } else { return false; } } else { viewmodel.applyChanges(); return false; } }, 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, postRender ) { var _batch; if ( !batch ) { task(); } else { _batch = batch; while ( postRender && _batch.previousBatch ) { // this can't happen until the DOM has been fully updated // otherwise in some situations (with components inside elements) // transitions and decorators will initialise prematurely _batch = _batch.previousBatch; } _batch.tasks.push( task ); } } }; circular.runloop = runloop; __export = 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 ) { changeHook.fire( thing.ractive, 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