ractive
Version:
Next-generation DOM manipulation
117 lines (87 loc) • 2.6 kB
JavaScript
import runloop from 'global/runloop';
import isEqual from 'utils/isEqual';
import getPattern from 'Ractive/prototype/observe/getPattern';
var PatternObserver, wildcard = /\*/, slice = Array.prototype.slice;
PatternObserver = function ( ractive, keypath, callback, options ) {
this.root = ractive;
this.callback = callback;
this.defer = options.defer;
this.debug = options.debug;
this.keypath = keypath;
this.regex = new RegExp( '^' + keypath.replace( /\./g, '\\.' ).replace( /\*/g, '([^\\.]+)' ) + '$' );
this.values = {};
if ( this.defer ) {
this.proxies = [];
}
// Observers are notified before any DOM changes take place (though
// they can defer execution until afterwards)
this.priority = 'pattern';
// default to root as context, but allow it to be overridden
this.context = ( options && options.context ? options.context : ractive );
};
PatternObserver.prototype = {
init: function ( immediate ) {
var values, keypath;
values = getPattern( this.root, this.keypath );
if ( immediate !== false ) {
for ( keypath in values ) {
if ( values.hasOwnProperty( keypath ) ) {
this.update( keypath );
}
}
} else {
this.values = values;
}
},
update: function ( keypath ) {
var values;
if ( wildcard.test( keypath ) ) {
values = getPattern( this.root, keypath );
for ( keypath in values ) {
if ( values.hasOwnProperty( keypath ) ) {
this.update( keypath );
}
}
return;
}
// special case - array mutation should not trigger `array.*`
// pattern observer with `array.length`
if ( this.root.viewmodel.implicitChanges[ keypath ] ) {
return;
}
if ( this.defer && this.ready ) {
runloop.addObserver( this.getProxy( keypath ) );
return;
}
this.reallyUpdate( keypath );
},
reallyUpdate: function ( keypath ) {
var value, keys, args;
value = this.root.viewmodel.get( keypath );
// Prevent infinite loops
if ( this.updating ) {
this.values[ keypath ] = value;
return;
}
this.updating = true;
if ( !isEqual( value, this.values[ keypath ] ) || !this.ready ) {
keys = slice.call( this.regex.exec( keypath ), 1 );
args = [ value, this.values[ keypath ], keypath ].concat( keys );
this.callback.apply( this.context, args );
this.values[ keypath ] = value;
}
this.updating = false;
},
getProxy: function ( keypath ) {
var self = this;
if ( !this.proxies[ keypath ] ) {
this.proxies[ keypath ] = {
update: function () {
self.reallyUpdate( keypath );
}
};
}
return this.proxies[ keypath ];
}
};
export default PatternObserver;