angular.js/src/Scope.js

199 lines
5.4 KiB
JavaScript
Raw Normal View History

2010-01-06 00:36:58 +00:00
// Copyright (C) 2009 BRAT Tech LLC
nglr.Scope = function(initialState, name) {
this.widgets = [];
this.watchListeners = {};
this.name = name;
initialState = initialState || {};
var State = function(){};
State.prototype = initialState;
this.state = new State();
this.state.$parent = initialState;
if (name == "ROOT") {
this.state.$root = this.state;
}
};
nglr.Scope.expressionCache = {};
nglr.Scope.prototype.updateView = function() {
var self = this;
this.fireWatchers();
_.each(this.widgets, function(widget){
self.evalWidget(widget, "", {}, function(){
this.updateView(self);
});
});
};
nglr.Scope.prototype.addWidget = function(controller) {
if (controller) this.widgets.push(controller);
};
nglr.Scope.prototype.isProperty = function(exp) {
for ( var i = 0; i < exp.length; i++) {
var ch = exp.charAt(i);
if (ch!='.' && !nglr.Lexer.prototype.isIdent(ch)) {
return false;
}
}
return true;
};
nglr.Scope.getter = function(instance, path) {
if (!path) return instance;
var element = path.split('.');
var key;
var lastInstance = instance;
var len = element.length;
for ( var i = 0; i < len; i++) {
key = element[i];
if (!key.match(/^[\$\w][\$\w\d]*$/))
throw "Expression '" + path + "' is not a valid expression for accesing variables.";
if (instance) {
lastInstance = instance;
instance = instance[key];
}
if (_.isUndefined(instance) && key.charAt(0) == '$') {
var type = angular.Global.typeOf(lastInstance);
type = angular[type.charAt(0).toUpperCase()+type.substring(1)];
var fn = type ? type[[key.substring(1)]] : undefined;
if (fn) {
instance = _.bind(fn, lastInstance, lastInstance);
return instance;
}
}
}
if (typeof instance === 'function' && !instance.$$factory) {
return nglr.bind(lastInstance, instance);
}
return instance;
};
nglr.Scope.prototype.get = function(path) {
return nglr.Scope.getter(this.state, path);
};
nglr.Scope.prototype.set = function(path, value) {
var element = path.split('.');
var instance = this.state;
for ( var i = 0; element.length > 1; i++) {
var key = element.shift();
var newInstance = instance[key];
if (!newInstance) {
newInstance = {};
instance[key] = newInstance;
}
instance = newInstance;
}
instance[element.shift()] = value;
return value;
};
nglr.Scope.prototype.setEval = function(expressionText, value) {
this.eval(expressionText + "=" + nglr.toJson(value));
};
nglr.Scope.prototype.eval = function(expressionText, context) {
var expression = nglr.Scope.expressionCache[expressionText];
if (!expression) {
var parser = new nglr.Parser(expressionText);
expression = parser.statements();
parser.assertAllConsumed();
nglr.Scope.expressionCache[expressionText] = expression;
}
context = context || {};
context.scope = this;
return expression(context);
};
//TODO: Refactor. This function needs to be an execution closure for widgets
// move to widgets
// remove expression, just have inner closure.
nglr.Scope.prototype.evalWidget = function(widget, expression, context, onSuccess, onFailure) {
try {
var value = this.eval(expression, context);
if (widget.hasError) {
widget.hasError = false;
jQuery(widget.view).
removeClass('ng-exception').
removeAttr('ng-error');
}
if (onSuccess) {
value = onSuccess.apply(widget, [value]);
}
return true;
} catch (e){
console.error('Eval Widget Error:', e);
var jsonError = nglr.toJson(e, true);
widget.hasError = true;
jQuery(widget.view).
addClass('ng-exception').
attr('ng-error', jsonError);
if (onFailure) {
onFailure.apply(widget, [e, jsonError]);
}
return false;
}
};
nglr.Scope.prototype.validate = function(expressionText, value) {
var expression = nglr.Scope.expressionCache[expressionText];
if (!expression) {
expression = new nglr.Parser(expressionText).validator();
nglr.Scope.expressionCache[expressionText] = expression;
}
var self = {scope:this};
return expression(self)(self, value);
};
nglr.Scope.prototype.entity = function(entityDeclaration) {
var expression = new nglr.Parser(entityDeclaration).entityDeclaration();
return expression({scope:this});
};
nglr.Scope.prototype.markInvalid = function(widget) {
this.state.$invalidWidgets.push(widget);
};
nglr.Scope.prototype.watch = function(declaration) {
var self = this;
new nglr.Parser(declaration).watch()({
scope:this,
addListener:function(watch, exp){
self.addWatchListener(watch, function(n,o){
try {
return exp({scope:self}, n, o);
} catch(e) {
nglr.alert(e);
}
});
}
});
};
nglr.Scope.prototype.addWatchListener = function(watchExpression, listener) {
var watcher = this.watchListeners[watchExpression];
if (!watcher) {
watcher = {listeners:[], expression:watchExpression};
this.watchListeners[watchExpression] = watcher;
}
watcher.listeners.push(listener);
};
nglr.Scope.prototype.fireWatchers = function() {
var self = this;
var fired = false;
jQuery.each(this.watchListeners, function(name, watcher) {
var value = self.eval(watcher.expression);
if (value !== watcher.lastValue) {
jQuery.each(watcher.listeners, function(i, listener){
listener(value, watcher.lastValue);
fired = true;
});
watcher.lastValue = value;
}
});
return fired;
};