diff options
Diffstat (limited to 'src/Scope.js')
| -rw-r--r-- | src/Scope.js | 198 | 
1 files changed, 198 insertions, 0 deletions
| diff --git a/src/Scope.js b/src/Scope.js new file mode 100644 index 00000000..45dd15a4 --- /dev/null +++ b/src/Scope.js @@ -0,0 +1,198 @@ +// 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; +}; | 
