aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Compiler.js94
-rw-r--r--src/Scope.js19
-rw-r--r--src/directives.js2
-rw-r--r--test/directivesSpec.js7
4 files changed, 111 insertions, 11 deletions
diff --git a/src/Compiler.js b/src/Compiler.js
index ece44805..115ed094 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -50,7 +50,39 @@ Template.prototype = {
//JQLite
//////////////////////////////////
+var jqCache = {};
+var jqName = 'ng-' + new Date().getTime();
+var jqId = 1;
+function jqNextId() { return jqId++; }
+
+var addEventListener = window.document.attachEvent ?
+ function(element, type, fn) {
+ element.attachEvent('on' + type, fn);
+ } : function(element, type, fn) {
+ element.addEventListener(type, fn, false);
+ };
+
+var removeEventListener = window.document.detachEvent ?
+ function(element, type, fn) {
+ element.detachEvent('on' + type, fn);
+ } : function(element, type, fn) {
+ element.removeEventListener(type, fn, false);
+ };
+
+function jqClearData(element) {
+ var cacheId = element[jqName],
+ cache = jqCache[cacheId];
+ if (cache) {
+ foreach(cache.bind || {}, function(fn, type){
+ removeEventListener(element, type, fn);
+ });
+ delete jqCache[cacheId];
+ delete element[jqName];
+ }
+};
+
function JQLite(element) {
+ //todo: change to this[0];
this.element = element;
}
@@ -64,6 +96,67 @@ function jqLite(element) {
}
JQLite.prototype = {
+ data: function(key, value) {
+ var element = this.element,
+ cacheId = element[jqName],
+ cache = jqCache[cacheId || -1];
+ if (isDefined(value)) {
+ if (!cache) {
+ element[jqName] = cacheId = jqNextId();
+ cache = jqCache[cacheId] = {};
+ }
+ cache[key] = value;
+ } else {
+ return cache ? cache[key] : null;
+ }
+ },
+
+ removeData: function(){
+ jqClearData(this.element);
+ },
+
+ dealoc: function(){
+ (function dealoc(element){
+ jqClearData(element);
+ for ( var i = 0, children = element.childNodes; i < children.length; i++) {
+ dealoc(children[0]);
+ }
+ })(this.element);
+ },
+
+ bind: function(type, fn){
+ var element = this.element,
+ bind = this.data('bind'),
+ eventHandler;
+ if (!bind) this.data('bind', bind = {});
+ eventHandler = bind[type];
+ if (!eventHandler) {
+ bind[type] = eventHandler = function() {
+ var self = this;
+ foreach(eventHandler.fns, function(fn){
+ fn.apply(self, arguments);
+ });
+ };
+ eventHandler.fns = [];
+ addEventListener(element, type, eventHandler);
+ }
+ eventHandler.fns.push(fn);
+ },
+
+ trigger: function(type) {
+ var cache = this.data('bind');
+ if (cache) {
+ (cache[type] || noop)();
+ }
+ },
+
+ click: function(fn) {
+ if (fn)
+ this.bind('click', fn);
+ else
+ this.trigger('click');
+ },
+
eachTextNode: function(fn){
var i, chldNodes = this.element.childNodes || [], size = chldNodes.length, chld;
for (i = 0; i < size; i++) {
@@ -96,6 +189,7 @@ JQLite.prototype = {
},
remove: function() {
+ this.dealoc();
this.element.parentNode.removeChild(this.element);
},
diff --git a/src/Scope.js b/src/Scope.js
index 7b1d2673..a3e128b6 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -73,7 +73,7 @@ Scope.prototype = {
// todo: this is a hack, which will need to be cleaned up.
var self = this,
listenFn = listener || noop,
- expr = bind(self, self.compile(fn), {scope: self, self: self.state});
+ expr = self.compile(fn);
this.evals.push(function(){
self.apply(listenFn, expr());
});
@@ -117,23 +117,24 @@ Scope.prototype = {
compile: function(exp) {
if (isFunction(exp)) return exp;
- var expFn = Scope.expressionCache[exp];
+ var expFn = Scope.expressionCache[exp], self = this;
if (!expFn) {
var parser = new Parser(exp);
expFn = parser.statements();
parser.assertAllConsumed();
Scope.expressionCache[exp] = expFn;
}
- return expFn;
+ return function(context){
+ context = context || {};
+ context.self = self.state;
+ context.scope = self;
+ return expFn.call(self, context);
+ };
},
eval: function(expressionText, context) {
// log('Scope.eval', expressionText);
- var expression = this.compile(expressionText);
- context = context || {};
- context.scope = this;
- context.self = this.state;
- return expression(context);
+ return this.compile(expressionText)(context);
},
//TODO: Refactor. This function needs to be an execution closure for widgets
@@ -209,7 +210,7 @@ Scope.prototype = {
addWatchListener: function(watchExpression, listener) {
// TODO: clean me up!
if (!isFunction(listener)) {
- listener = bind(this, this.compile(listener), {scope: this, self: this.state});
+ listener = this.compile(listener);
}
var watcher = this.watchListeners[watchExpression];
if (!watcher) {
diff --git a/src/directives.js b/src/directives.js
index 9f98badb..adcfa508 100644
--- a/src/directives.js
+++ b/src/directives.js
@@ -85,7 +85,7 @@ angularDirective("ng-repeat", function(expression, element){
angularDirective("ng-action", function(expression, element){
return function(){
var self = this;
- jQuery(element.element).click(function(){
+ element.click(function(){
self.$eval(expression);
});
};
diff --git a/test/directivesSpec.js b/test/directivesSpec.js
index 447698a3..7e0446ef 100644
--- a/test/directivesSpec.js
+++ b/test/directivesSpec.js
@@ -12,6 +12,11 @@ describe("directives", function(){
};
});
+ afterEach(function(){
+ element.remove();
+ expect(_(jqCache).size()).toEqual(0);
+ });
+
it("should ng-init", function() {
var scope = compile('<div ng-init="a=123"></div>');
expect(scope.get('a')).toEqual(123);
@@ -100,7 +105,7 @@ describe("directives", function(){
scope.updateView();
expect(scope.get('clicked')).toBeFalsy();
- jQuery(element.element).click();
+ element.click();
expect(scope.get('clicked')).toEqual(true);
});
});