From a8227086748e37c31c1bb71dec50c96d63c45eef Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 22 Mar 2010 20:20:05 -0700 Subject: rudementary event bind and trigger for jqlite --- src/Compiler.js | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/Scope.js | 19 +++++------ src/directives.js | 2 +- 3 files changed, 105 insertions(+), 10 deletions(-) (limited to 'src') 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); }); }; -- cgit v1.2.3