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