UNPKG

ractive

Version:

Next-generation DOM manipulation

114 lines (86 loc) 3.31 kB
import normaliseRef from 'utils/normaliseRef'; import getInnerContext from 'shared/getInnerContext'; import createComponentBinding from 'shared/createComponentBinding'; var ancestorErrorMessage, getOptions; ancestorErrorMessage = 'Could not resolve reference - too many "../" prefixes'; getOptions = { evaluateWrapped: true }; export default 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( /^\.\//, '.' ); }