From b814c79b58deeeeaa12b03261399ef80c0d6cc9f Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Thu, 25 Mar 2010 13:01:08 -0700 Subject: checkbox and radio now working --- src/Parser.js | 1 + src/Scope.js | 6 +- src/jqLite.js | 13 +++-- src/widgets2.js | 176 +++++++++++++++++--------------------------------------- 4 files changed, 66 insertions(+), 130 deletions(-) (limited to 'src') diff --git a/src/Parser.js b/src/Parser.js index 941d37f7..81a2afdc 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -10,6 +10,7 @@ Lexer.OPERATORS = { 'null':function(self){return null;}, 'true':function(self){return true;}, 'false':function(self){return false;}, + 'undefined':noop, '+':function(self, a,b){return (a||0)+(b||0);}, '-':function(self, a,b){return (a||0)-(b||0);}, '*':function(self, a,b){return a*b;}, diff --git a/src/Scope.js b/src/Scope.js index daafabb0..5f1cfdda 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -12,8 +12,10 @@ function Scope(initialState, name) { '$parent': initialState, '$watch': bind(self, self.addWatchListener), '$eval': bind(self, self.eval), - // change name to onEval? - '$addEval': bind(self, self.addEval) + '$bind': bind(self, bind, self), + // change name to autoEval? + '$addEval': bind(self, self.addEval), + '$updateView': bind(self, self.updateView) }); if (name == "ROOT") { self.state['$root'] = self.state; diff --git a/src/jqLite.js b/src/jqLite.js index 2132bfc1..7646bf98 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -71,7 +71,7 @@ JQLite.prototype = { (function dealoc(element){ jqClearData(element); for ( var i = 0, children = element.childNodes; i < children.length; i++) { - dealoc(children[0]); + dealoc(children[i]); } })(this[0]); }, @@ -86,9 +86,11 @@ JQLite.prototype = { eventHandler = bind[type]; if (!eventHandler) { bind[type] = eventHandler = function() { + var value = false; foreach(eventHandler.fns, function(fn){ - fn.apply(self, arguments); + value = value || fn.apply(self, arguments); }); + return value; }; eventHandler.fns = []; addEventListener(element, type, eventHandler); @@ -98,10 +100,9 @@ JQLite.prototype = { }, trigger: function(type) { - var cache = this.data('bind'); - if (cache) { - (cache[type] || noop)(); - } + var evnt = document.createEvent('MouseEvent'); + evnt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); + this[0].dispatchEvent(evnt); }, click: function(fn) { diff --git a/src/widgets2.js b/src/widgets2.js index 5425b5a4..b67694b1 100644 --- a/src/widgets2.js +++ b/src/widgets2.js @@ -1,4 +1,4 @@ -function scopeAccessor(scope, element) { +function modelAccessor(scope, element) { var expr = element.attr('name'), farmatterName = element.attr('ng-format') || NOOP, formatter = angularFormatter(farmatterName); @@ -14,7 +14,7 @@ function scopeAccessor(scope, element) { }; } -function domAccessor(element) { +function valueAccessor(element) { var validatorName = element.attr('ng-validate') || NOOP, validator = angularValidator(validatorName), required = element.attr('ng-required'), @@ -41,135 +41,67 @@ function domAccessor(element) { }; } +function checkedAccessor(element) { + var domElement = element[0]; + return { + get: function(){ return !!domElement.checked; }, + set: function(value){ domElement.checked = !!value; } + }; +} + +function radioAccessor(element) { + var domElement = element[0]; + return { + get: function(){ return domElement.checked ? domElement.value : null; }, + set: function(value){ domElement.checked = value == domElement.value; } + }; +} + +function noopAccessor() { return { get: noop, set: noop }; } + var NG_ERROR = 'ng-error', NG_VALIDATION_ERROR = 'ng-validation-error', - TEXT_META = ['', 'keyup change'], - INPUT_META = { - 'text': TEXT_META, - 'textarea': TEXT_META, - 'hidden': TEXT_META, - 'password': TEXT_META + textWidget = inputWidget('keyup change', modelAccessor, valueAccessor, ''), + buttonWidget = inputWidget('click', noopAccessor, noopAccessor, undefined), + INPUT_TYPE = { + 'text': textWidget, + 'textarea': textWidget, + 'hidden': textWidget, + 'password': textWidget, + 'button': buttonWidget, + 'submit': buttonWidget, + 'reset': buttonWidget, + 'image': buttonWidget, + 'checkbox': inputWidget('click', modelAccessor, checkedAccessor, false), + 'radio': inputWidget('click', modelAccessor, radioAccessor, undefined) +// 'select-one': [null, 'change'], +// 'select-multiple': [[], 'change'], +// 'file': [{}, 'click'] }; -function inputWidget(meta) { - return meta ? function(element) { - var scope = scopeAccessor(this, element), - dom = domAccessor(element); - scope.set(dom.get() || meta[0]); - element.bind(meta[1], function(){ - scope.set(dom.get()); +function inputWidget(events, modelAccessor, viewAccessor, initValue) { + return function(element) { + var scope = this, + model = modelAccessor(scope, element), + view = viewAccessor(element), + action = element.attr('ng-action') || ''; + var value = view.get() || initValue; + if (isDefined(value)) model.set(value); + element.bind(events, function(){ + model.set(view.get()); + scope.$eval(action); }); - this.$watch(scope.get, dom.set); - } : 0; + scope.$watch(model.get, view.set); + }; } angularWidget('INPUT', function input(element){ - return inputWidget(INPUT_META[lowercase(element[0].type)]); -}); - -angularWidget('TEXTAREA', function(){ - return inputWidget(INPUT_META['text']); -}); - - - - -///////////////////////////////////////// -///////////////////////////////////////// -///////////////////////////////////////// -///////////////////////////////////////// -///////////////////////////////////////// - - - -//widget related -//ng-validate, ng-required, ng-formatter -//ng-error - -//ng-scope ng-controller???? - -// -> -angular.widget("inputtext", function(element) { - var expression = element.attr('name'); - var formatter = this.formatter(element.attr('formatter')); - var validator = this.validator(element.attr('validator')); - - function validate(value) { - var error = validator(element); - if (error) { - element.addClass("ng-error"); - scope.markInvalid(this); //move out of scope - } else { - scope.clearInvalid(this); - } - } - - - element.keyup(this.withScope(function(){ - this.$evalSet(expression, formatter.parse(element.val())); - validate(element.val()); - })); - - return {watch: expression, apply: function(newValue){ - element.val(formatter.format(newValue)); - validate(element.val()); - }}; - -}); - -angular.widget("inputfile", function(element) { - -}); - -angular.widget("inputradio", function(element) { - -}); - - -// -angular.widget("colorpicker", function(element) { - var name = element.attr('datasource'); - var formatter = this.formatter(element.attr('ng-formatter')); - - element.colorPicker(this.withScope(function(selectedColor){ - this.$evalSet(name, formatter.parse(selectedColor)); - })); - - return function(){ - this.$watch(expression, function(cmyk){ - element.setColor(formatter.format(cmyk)); - }); + return function(element) { + this.$eval(element.attr('ng-init')||''); + (INPUT_TYPE[lowercase(element[0].type)] || noop).call(this, element); }; }); -angular.widget("template", function(element) { - var srcExpression = element.attr('src'); - var self = this; - return {watch:srcExpression, apply:function(src){ - $.load(src, function(html){ - self.destroy(element); - element.html(html); - self.compile(element); - }); - }}; +angularWidget('TEXTAREA', function(){ + return textWidget; }); - - -/** - * - * { - * withScope: //safely executes, with a try/catch. applies scope - * compile: - * widget: - * directive: - * validator: - * formatter: - * - * - * config: - * loadCSS: - * loadScript: - * loadTemplate: - * } - * - **/ -- cgit v1.2.3