From f7d4e17cf90425931eff3c589284e13189a8f47f Mon Sep 17 00:00:00 2001 From: aharpervc Date: Fri, 10 Oct 2014 17:34:46 -0400 Subject: [PATCH] Update to Ractive 0.6.0 --- vendor/assets/javascripts/ractive.js | 4578 ++++++++++++++++---------- 1 file changed, 2887 insertions(+), 1691 deletions(-) diff --git a/vendor/assets/javascripts/ractive.js b/vendor/assets/javascripts/ractive.js index 6f95d87..e853141 100644 --- a/vendor/assets/javascripts/ractive.js +++ b/vendor/assets/javascripts/ractive.js @@ -1,6 +1,6 @@ /* - ractive.js v0.5.5 - 2014-07-13 - commit 8b1d34ef + ractive.js v0.6.0 + 2014-10-08 - commit 150e82d0 http://ractivejs.org http://twitter.com/RactiveJS @@ -17,10 +17,6 @@ /* 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, @@ -30,6 +26,7 @@ v: 1, t: [] }, + yield: null, // parse: preserveWhitespace: false, sanitize: false, @@ -189,31 +186,6 @@ } 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; @@ -231,6 +203,133 @@ 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( message ); + }; + } 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.', + methodDepricated: 'The method "{depricated}" has been depricated in favor of "{replacement}" and will likely be removed in a future release.' + }; + + /* utils/log.js */ + var log = function( consolewarn, errors ) { + + var log = { + warn: function( options, passthru ) { + if ( !options.debug && !passthru ) { + return; + } + 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 deprications = { + construct: { + depricated: 'beforeInit', + replacement: 'onconstruct' + }, + render: { + depricated: 'init', + replacement: 'onrender' + }, + complete: { + depricated: 'complete', + replacement: 'oncomplete' + } + }; + + function Hook( event ) { + this.event = event; + this.method = 'on' + event; + this.depricate = deprications[ 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 ( this.depricate && call( this.depricate.depricated ) ) { + log.warn( { + debug: ractive.debug, + message: 'methodDepricated', + args: this.depricate + } ); + } + 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 ); @@ -242,6 +341,7 @@ /* utils/Promise.js */ var Promise = function() { + var __export; var _Promise, PENDING = {}, FULFILLED = {}, REJECTED = {}; @@ -344,7 +444,7 @@ } ); }; } - return _Promise; + __export = _Promise; // TODO use MutationObservers or something to simulate setImmediate function wait( callback ) { setTimeout( callback, 0 ); @@ -412,6 +512,7 @@ fulfil( x ); } } + return __export; }(); /* utils/normaliseRef.js */ @@ -426,7 +527,7 @@ /* shared/getInnerContext.js */ var getInnerContext = function( fragment ) { do { - if ( fragment.context ) { + if ( fragment.context !== undefined ) { return fragment.context; } } while ( fragment = fragment.parent ); @@ -445,46 +546,51 @@ }; /* shared/createComponentBinding.js */ - var createComponentBinding = function( circular, isArray, isEqual ) { + var createComponentBinding = function( circular, isEqual ) { var runloop; circular.push( function() { return runloop = circular.runloop; } ); - var Binding = function( ractive, keypath, otherInstance, otherKeypath, priority ) { + var Binding = function( ractive, keypath, otherInstance, otherKeypath ) { + var this$0 = this; this.root = ractive; this.keypath = keypath; - this.priority = priority; this.otherInstance = otherInstance; this.otherKeypath = otherKeypath; + this.unlock = function() { + return this$0.updating = false; + }; this.bind(); this.value = this.root.viewmodel.get( this.keypath ); }; Binding.prototype = { + shuffle: function( newIndices, value ) { + this.propagateChange( value, newIndices ); + }, setValue: function( value ) { - var this$0 = this; + this.propagateChange( value ); + }, + propagateChange: function( value, newIndices ) { // 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 ); + if ( newIndices ) { + this.otherInstance.viewmodel.smartUpdate( this.otherKeypath, value, newIndices ); + } else { + 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; - } ); + runloop.scheduleTask( this.unlock ); } }, bind: function() { @@ -501,36 +607,36 @@ } }; return function createComponentBinding( component, parentInstance, parentKeypath, childKeypath ) { - var hash, childInstance, bindings, priority, parentToChildBinding, childToParentBinding; + var hash, childInstance, bindings, 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 ); + parentToChildBinding = new Binding( parentInstance, parentKeypath, childInstance, childKeypath ); bindings.push( parentToChildBinding ); if ( childInstance.twoway ) { - childToParentBinding = new Binding( childInstance, childKeypath, parentInstance, parentKeypath, 1 ); + childToParentBinding = new Binding( childInstance, childKeypath, parentInstance, parentKeypath ); bindings.push( childToParentBinding ); parentToChildBinding.counterpart = childToParentBinding; childToParentBinding.counterpart = parentToChildBinding; } + bindings[ hash ] = parentToChildBinding; }; - }( circular, isArray, isEqual ); + }( 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 }; - return function resolveRef( ractive, ref, fragment ) { + __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 @@ -544,6 +650,8 @@ } // ...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 ) { @@ -562,6 +670,7 @@ // 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 ) { @@ -571,7 +680,7 @@ ractive.viewmodel.set( ref, index, true ); return; } - keypath = resolveRef( ractive._parent, ref, fragment ); + 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 @@ -591,7 +700,10 @@ } // 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 ) { + 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 ) { @@ -623,6 +735,7 @@ } return baseContext + ref.replace( /^\.\//, '.' ); } + return __export; }( normaliseRef, getInnerContext, createComponentBinding ); /* global/TransitionManager.js */ @@ -711,9 +824,11 @@ }( removeFromArray ); /* global/runloop.js */ - var runloop = function( circular, removeFromArray, Promise, resolveRef, TransitionManager ) { + var runloop = function( circular, Hook, removeFromArray, Promise, resolveRef, TransitionManager ) { - var batch, runloop, unresolved = []; + var __export; + var batch, runloop, unresolved = [], + changeHook = new Hook( 'change' ); runloop = { start: function( instance, returnPromise ) { var promise, fulfilPromise; @@ -774,7 +889,7 @@ } }; circular.runloop = runloop; - return runloop; + __export = runloop; function flushChanges() { var i, thing, changeHash; @@ -782,7 +897,7 @@ thing = batch.viewmodels[ i ]; changeHash = thing.applyChanges(); if ( changeHash ) { - thing.ractive.fire( 'change', changeHash ); + changeHook.fire( thing.ractive, changeHash ); } } batch.viewmodels.length = 0; @@ -805,27 +920,34 @@ } function attemptKeypathResolution() { - var array, thing, keypath; - if ( !unresolved.length ) { - return; - } + var i, item, keypath, resolved; + i = unresolved.length; // 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 ); + while ( i-- ) { + item = unresolved[ i ]; + if ( item.keypath ) { + // it resolved some other way. TODO how? two-way binding? Seems + // weird that we'd still end up here + unresolved.splice( i, 1 ); + } + if ( keypath = resolveRef( item.root, item.ref, item.parentFragment ) ) { + ( resolved || ( resolved = [] ) ).push( { + item: item, + keypath: keypath + } ); + unresolved.splice( i, 1 ); } } + if ( resolved ) { + resolved.forEach( resolve ); + } + } + + function resolve( resolved ) { + resolved.item.resolve( resolved.keypath ); } - }( circular, removeFromArray, Promise, resolveRef, TransitionManager ); + return __export; + }( circular, Ractive$shared_hooks_Hook, removeFromArray, Promise, resolveRef, TransitionManager ); /* utils/createBranch.js */ var createBranch = function() { @@ -839,6 +961,7 @@ /* viewmodel/prototype/get/magicAdaptor.js */ var viewmodel$get_magicAdaptor = function( runloop, createBranch, isArray ) { + var __export; var magicAdaptor, MagicWrapper; try { Object.defineProperty( {}, 'test', { @@ -952,7 +1075,7 @@ } catch ( err ) { magicAdaptor = false; } - return magicAdaptor; + __export = magicAdaptor; function createAccessors( originalWrapper, value, template ) { var object, property, oldGet, oldSet, get, set; @@ -1006,6 +1129,7 @@ configurable: true } ); } + return __export; }( runloop, createBranch, isArray ); /* config/magic.js */ @@ -1252,36 +1376,16 @@ 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 __export; 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 ) { + __export = function transformCss( css, guid ) { var transformed, addGuid; addGuid = function( selector ) { var selectorUnits, match, unit, dataAttr, base, prepended, appended, i, transformed = []; @@ -1335,6 +1439,7 @@ function extractString( unit ) { return unit.str; } + return __export; }(); /* config/options/css/css.js */ @@ -1366,7 +1471,8 @@ /* utils/wrapMethod.js */ var wrapMethod = function() { - return function( method, superMethod, force ) { + var __export; + __export = function( method, superMethod, force ) { if ( force || needsSuper( method, superMethod ) ) { return function() { var hasSuper = '_super' in this, @@ -1387,22 +1493,27 @@ function needsSuper( method, superMethod ) { return typeof superMethod === 'function' && /_super/.test( method ); } + return __export; }(); /* config/options/data.js */ var data = function( wrap ) { + var __export; var dataConfig = { name: 'data', extend: extend, init: init, reset: reset }; - return dataConfig; + __export = dataConfig; function combine( Parent, target, options ) { var value = options.data || {}, parentValue = getAddedKeys( Parent.prototype.data ); + if ( typeof value !== 'object' && typeof value !== 'function' ) { + throw new TypeError( 'data option must be an object or a function, "' + value + '" is not valid' ); + } return dispatch( parentValue, value ); } @@ -1515,23 +1626,9 @@ } return wrap( childFn, parentFn ); } + return __export; }( 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, @@ -1567,7 +1664,8 @@ SECTION_IF: 50, SECTION_UNLESS: 51, SECTION_EACH: 52, - SECTION_WITH: 53 + SECTION_WITH: 53, + SECTION_IF_WITH: 54 }; /* utils/create.js */ @@ -1607,7 +1705,6 @@ /* 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; @@ -2197,7 +2294,6 @@ /* parse/Parser/expressions/conditional.js */ var conditional = function( types, getLogicalOr, errors ) { - // The conditional operator is the lowest precedence operator, so we start here return function( parser ) { var start, expression, ifTrue, ifFalse; expression = getLogicalOr( parser ); @@ -2238,7 +2334,8 @@ /* parse/Parser/utils/flattenExpression.js */ var flattenExpression = function( types, isObject ) { - return function( expression ) { + var __export; + __export = function( expression ) { var refs = [], flattened; extractRefs( expression, refs ); @@ -2314,11 +2411,12 @@ case types.CONDITIONAL: return stringify( parser, node.o[ 0 ], refs ) + '?' + stringify( parser, node.o[ 1 ], refs ) + ':' + stringify( parser, node.o[ 2 ], refs ); case types.REFERENCE: - return '${' + refs.indexOf( node.n ) + '}'; + return '_' + refs.indexOf( node.n ); default: parser.error( 'Expected legal JavaScript' ); } } + return __export; }( types, isObject ); /* parse/Parser/_Parser.js */ @@ -2336,10 +2434,17 @@ }; ParseError.prototype = Error.prototype; Parser = function( str, options ) { - var items, item; + var items, item, lineStart = 0; this.str = str; this.options = options || {}; this.pos = 0; + this.lines = this.str.split( '\n' ); + this.lineEnds = this.lines.map( function( line ) { + var lineEnd = lineStart + line.length + 1; + // +1 for the newline + lineStart = lineEnd; + return lineEnd; + }, 0 ); // Custom init logic if ( this.init ) this.init( str, options ); @@ -2376,38 +2481,33 @@ return getConditional( this ); }, flattenExpression: flattenExpression, - getLinePos: function() { - var lines, currentLine, currentLineEnd, nextLineEnd, lineNum, columnNum; - lines = this.str.split( '\n' ); - lineNum = -1; - nextLineEnd = 0; - do { - currentLineEnd = nextLineEnd; - lineNum++; - currentLine = lines[ lineNum ]; - nextLineEnd += currentLine.length + 1; - } while ( nextLineEnd <= this.pos ); - columnNum = this.pos - currentLineEnd; - return { - line: lineNum + 1, - ch: columnNum + 1, - text: currentLine, - toJSON: function() { - return [ - this.line, - this.ch - ]; - }, - toString: function() { - return 'line ' + this.line + ' character ' + this.ch + ':\n' + this.text + '\n' + this.text.substr( 0, this.ch - 1 ).replace( /[\S]/g, ' ' ) + '^----'; - } - }; + getLinePos: function( char ) { + var lineNum = 0, + lineStart = 0, + columnNum; + while ( char >= this.lineEnds[ lineNum ] ) { + lineStart = this.lineEnds[ lineNum ]; + lineNum += 1; + } + columnNum = char - lineStart; + return [ + lineNum + 1, + columnNum + 1, + char + ]; }, - error: function( err ) { - var pos, message; - pos = this.getLinePos(); - message = err + ' at ' + pos; - throw new ParseError( message ); + error: function( message ) { + var pos, lineNum, columnNum, line, annotation, error; + pos = this.getLinePos( this.pos ); + lineNum = pos[ 0 ]; + columnNum = pos[ 1 ]; + line = this.lines[ pos[ 0 ] - 1 ]; + annotation = line + '\n' + new Array( pos[ 1 ] ).join( ' ' ) + '^----'; + error = new ParseError( message + ' at line ' + lineNum + ' character ' + columnNum + ':\n' + annotation ); + error.line = pos[ 0 ]; + error.character = pos[ 1 ]; + error.shortMessage = message; + throw error; }, matchString: function( string ) { if ( this.str.substr( this.pos, string.length ) === string ) { @@ -2535,10 +2635,11 @@ var handlebarsBlockCodes = function( types ) { return { + 'each': types.SECTION_EACH, 'if': types.SECTION_IF, - 'unless': types.SECTION_UNLESS, + 'if-with': types.SECTION_IF_WITH, 'with': types.SECTION_WITH, - 'each': types.SECTION_EACH + 'unless': types.SECTION_UNLESS }; }( types ); @@ -2548,13 +2649,14 @@ /* parse/converters/mustache/content.js */ var content = function( types, mustacheType, handlebarsBlockCodes ) { + var __export; var indexRefPattern = /^\s*:\s*([a-zA-Z_$][a-zA-Z_$0-9]*)/, arrayMemberPattern = /^[0-9][1-9]*$/, handlebarsBlockPattern = new RegExp( '^(' + Object.keys( handlebarsBlockCodes ).join( '|' ) + ')\\b' ), legalReference; legalReference = /^[a-zA-Z$_0-9]+(?:(\.[a-zA-Z$_0-9]+)|(\[[a-zA-Z$_0-9]+\]))*$/; - return function( parser, delimiterType ) { - var start, pos, mustache, type, block, expression, i, remaining, index, delimiters, referenceExpression; + __export = function( parser, delimiterType ) { + var start, pos, mustache, type, block, expression, i, remaining, index, delimiters; start = parser.pos; mustache = {}; delimiters = parser[ delimiterType.delimiters ]; @@ -2567,17 +2669,27 @@ } else { // We need to test for expressions before we test for mustache type, because // an expression that begins '!' looks a lot like a comment - if ( parser.remaining()[ 0 ] === '!' && ( expression = parser.readExpression() ) ) { - mustache.t = types.INTERPOLATOR; - // Was it actually an expression, or a comment block in disguise? - parser.allowWhitespace(); - if ( parser.matchString( delimiters[ 1 ] ) ) { - // expression - parser.pos -= delimiters[ 1 ].length; - } else { - // comment block - parser.pos = start; - expression = null; + if ( parser.remaining()[ 0 ] === '!' ) { + try { + expression = parser.readExpression(); + // Was it actually an expression, or a comment block in disguise? + parser.allowWhitespace(); + if ( parser.remaining().indexOf( delimiters[ 1 ] ) ) { + expression = null; + } else { + mustache.t = types.INTERPOLATOR; + } + } catch ( err ) {} + if ( !expression ) { + index = parser.remaining().indexOf( delimiters[ 1 ] ); + if ( ~index ) { + parser.pos += index; + } else { + parser.error( 'Expected closing delimiter (\'' + delimiters[ 1 ] + '\')' ); + } + return { + t: types.COMMENT + }; } } if ( !expression ) { @@ -2594,7 +2706,7 @@ remaining = parser.remaining(); index = remaining.indexOf( delimiters[ 1 ] ); if ( index !== -1 ) { - mustache.r = remaining.substr( 0, index ); + mustache.r = remaining.substr( 0, index ).split( ' ' )[ 0 ]; parser.pos += index; return mustache; } @@ -2606,6 +2718,18 @@ parser.allowWhitespace(); // get expression expression = parser.readExpression(); + // If this is a partial, it may have a context (e.g. `{{>item foo}}`). These + // cases involve a bit of a hack - we want to turn it into the equivalent of + // `{{#with foo}}{{>item}}{{/with}}`, but to get there we temporarily append + // a 'contextPartialExpression' to the mustache, and process the context instead of + // the reference + var temp; + if ( mustache.t === types.PARTIAL && expression && ( temp = parser.readExpression() ) ) { + mustache = { + contextPartialExpression: expression + }; + expression = temp; + } // With certain valid references that aren't valid expressions, // e.g. {{1.foo}}, we have a problem: it looks like we've got an // expression, but the expression didn't consume the entire @@ -2629,6 +2753,22 @@ parser.pos = pos; } } + refineExpression( parser, expression, mustache ); + // if there was context, process the expression now and save it for later + if ( mustache.contextPartialExpression ) { + mustache.contextPartialExpression = [ refineExpression( parser, mustache.contextPartialExpression, { + t: types.PARTIAL + } ) ]; + } + // optional index reference + if ( i = parser.matchPattern( indexRefPattern ) ) { + mustache.i = i; + } + return mustache; + }; + + function refineExpression( parser, expression, mustache ) { + var referenceExpression; if ( expression ) { while ( expression.t === types.BRACKETED && expression.x ) { expression = expression.x; @@ -2646,13 +2786,9 @@ mustache.x = parser.flattenExpression( expression ); } } + return mustache; } - // optional index reference - if ( i = parser.matchPattern( indexRefPattern ) ) { - mustache.i = i; - } - return mustache; - }; + } // TODO refactor this! it's bewildering function getReferenceExpression( parser, expression ) { var members = [], @@ -2678,20 +2814,27 @@ m: members }; } + return __export; }( types, type, handlebarsBlockCodes, legacy ); /* parse/converters/mustache.js */ var mustache = function( types, delimiterChange, delimiterTypes, mustacheContent, handlebarsBlockCodes ) { + var __export; var delimiterChangeToken = { t: types.DELIMCHANGE, exclude: true }, handlebarsIndexRefPattern = /^@(?:index|key)$/; - return getMustache; + __export = getMustache; function getMustache( parser ) { var types; + // If we're inside a