ractive
Version:
Next-generation DOM manipulation
116 lines (89 loc) • 3.27 kB
JavaScript
var lastKey = /[^\.]+$/,
starMaps = {};
// TODO split into two functions? i.e. one for the top-level call, one for the cascade
export default function notifyPatternObservers ( ractive, registeredKeypath, actualKeypath, isParentOfChangedKeypath, isTopLevelCall ) {
var i, patternObserver, children, child, key, childActualKeypath, potentialWildcardMatches, cascade;
// First, observers that match patterns at the same level
// or higher in the tree
i = ractive.viewmodel.patternObservers.length;
while ( i-- ) {
patternObserver = ractive.viewmodel.patternObservers[i];
if ( patternObserver.regex.test( actualKeypath ) ) {
patternObserver.update( actualKeypath );
}
}
if ( isParentOfChangedKeypath ) {
return;
}
// If the changed keypath is 'foo.bar', we need to see if there are
// any pattern observer dependants of keypaths below any of
// 'foo.bar', 'foo.*', '*.bar' or '*.*' (e.g. 'foo.bar.*' or 'foo.*.baz' )
cascade = function ( keypath ) {
if ( children = ractive.viewmodel.depsMap[ keypath ] ) {
i = children.length;
while ( i-- ) {
child = children[i]; // foo.*.baz
key = lastKey.exec( child )[0]; // 'baz'
childActualKeypath = actualKeypath ? actualKeypath + '.' + key : key; // 'foo.bar.baz'
notifyPatternObservers( ractive, child, childActualKeypath ); // ractive, 'foo.*.baz', 'foo.bar.baz'
}
}
};
if ( isTopLevelCall ) {
potentialWildcardMatches = getPotentialWildcardMatches( actualKeypath );
potentialWildcardMatches.forEach( cascade );
}
else {
cascade( registeredKeypath );
}
}
// This function takes a keypath such as 'foo.bar.baz', and returns
// all the variants of that keypath that include a wildcard in place
// of a key, such as 'foo.bar.*', 'foo.*.baz', 'foo.*.*' and so on.
// These are then checked against the dependants map (ractive.viewmodel.depsMap)
// to see if any pattern observers are downstream of one or more of
// these wildcard keypaths (e.g. 'foo.bar.*.status')
function getPotentialWildcardMatches ( keypath ) {
var keys, starMap, mapper, i, result, wildcardKeypath;
keys = keypath.split( '.' );
starMap = getStarMap( keys.length );
result = [];
mapper = function ( star, i ) {
return star ? '*' : keys[i];
};
i = starMap.length;
while ( i-- ) {
wildcardKeypath = starMap[i].map( mapper ).join( '.' );
if ( !result[ wildcardKeypath ] ) {
result.push( wildcardKeypath );
result[ wildcardKeypath ] = true;
}
}
return result;
}
// This function returns all the possible true/false combinations for
// a given number - e.g. for two, the possible combinations are
// [ true, true ], [ true, false ], [ false, true ], [ false, false ].
// It does so by getting all the binary values between 0 and e.g. 11
function getStarMap ( num ) {
var ones = '', max, binary, starMap, mapper, i;
if ( !starMaps[ num ] ) {
starMap = [];
while ( ones.length < num ) {
ones += 1;
}
max = parseInt( ones, 2 );
mapper = function ( digit ) {
return digit === '1';
};
for ( i = 0; i <= max; i += 1 ) {
binary = i.toString( 2 );
while ( binary.length < num ) {
binary = '0' + binary;
}
starMap[i] = Array.prototype.map.call( binary, mapper );
}
starMaps[ num ] = starMap;
}
return starMaps[ num ];
}