aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMisko Hevery2010-04-05 11:46:53 -0700
committerMisko Hevery2010-04-05 11:46:53 -0700
commit7a4b48020688060debe9cb0f9c17615d7585cbe7 (patch)
tree48a5b1d8cf92bb272028a106ab9ea3ec16f477a2 /src
parent4bfa4e230d5ebdd582068effe7f4f1b60c43093a (diff)
downloadangular.js-7a4b48020688060debe9cb0f9c17615d7585cbe7.tar.bz2
added ng:switch widget
Diffstat (limited to 'src')
-rw-r--r--src/Angular.js9
-rw-r--r--src/AngularPublic.js35
-rw-r--r--src/Browser.js20
-rw-r--r--src/Compiler.js1
-rw-r--r--src/Scope.js17
-rw-r--r--src/UrlWatcher.js46
-rw-r--r--src/Widgets.js59
-rw-r--r--src/angular-bootstrap.js2
-rw-r--r--src/jqLite.js7
-rw-r--r--src/services.js29
-rw-r--r--src/~AngularPublic.js17
11 files changed, 119 insertions, 123 deletions
diff --git a/src/Angular.js b/src/Angular.js
index d00a9bf6..0952a352 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -247,7 +247,16 @@ function escapeHtml(html) {
replace(/>/g, '>');
}
+
+function isRenderableElement(element) {
+ var name = element && element[0] && element[0].nodeName;
+ return name && name.charAt(0) != '#' &&
+ !includes(['TR', 'COL', 'COLGROUP', 'TBODY', 'THEAD', 'TFOOT'], name);
+}
function elementError(element, type, error) {
+ while (!isRenderableElement(element)) {
+ element = element.parent() || jqLite(document.body);
+ }
if (error) {
element.addClass(type);
element.attr(NG_ERROR, error);
diff --git a/src/AngularPublic.js b/src/AngularPublic.js
new file mode 100644
index 00000000..470eb258
--- /dev/null
+++ b/src/AngularPublic.js
@@ -0,0 +1,35 @@
+var browserSingleton;
+angularService('$browser', function browserFactory(){
+ if (!browserSingleton) {
+ var XHR = XMLHttpRequest;
+ if (isUndefined(XHR)) {
+ XHR = function () {
+ try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
+ try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
+ try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
+ throw new Error("This browser does not support XMLHttpRequest.");
+ };
+ }
+ browserSingleton = new Browser(window.location, XHR);
+ browserSingleton.startUrlWatcher();
+ }
+ return browserSingleton;
+});
+
+extend(angular, {
+ 'element': jqLite,
+ 'compile': compile,
+ 'scope': createScope,
+ 'copy': copy,
+ 'extend': extend,
+ 'foreach': foreach,
+ 'noop':noop,
+ 'identity':identity,
+ 'isUndefined': isUndefined,
+ 'isDefined': isDefined,
+ 'isString': isString,
+ 'isFunction': isFunction,
+ 'isNumber': isNumber,
+ 'isArray': isArray
+});
+
diff --git a/src/Browser.js b/src/Browser.js
index 893459ae..6036884f 100644
--- a/src/Browser.js
+++ b/src/Browser.js
@@ -3,9 +3,10 @@
// Browser
//////////////////////////////
-function Browser(location) {
+function Browser(location, XHR) {
this.location = location;
this.delay = 25;
+ this.XHR = XHR;
this.setTimeout = function(fn, delay) {
window.setTimeout(fn, delay);
};
@@ -14,6 +15,17 @@ function Browser(location) {
}
Browser.prototype = {
+ xhr: function(method, url, callback){
+ var xhr = new this.XHR();
+ xhr.open(method, url, true);
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ callback(xhr.status, xhr.responseText);
+ }
+ };
+ xhr.send('');
+ },
+
watchUrl: function(fn){
this.listeners.push(fn);
},
@@ -23,7 +35,11 @@ Browser.prototype = {
(function pull () {
if (self.expectedUrl !== self.location.href) {
foreach(self.listeners, function(listener){
- listener(self.location.href);
+ try {
+ listener(self.location.href);
+ } catch (e) {
+ error(e);
+ }
});
self.expectedUrl = self.location.href;
}
diff --git a/src/Compiler.js b/src/Compiler.js
index dac4931f..67c22461 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -71,6 +71,7 @@ Compiler.prototype = {
$init: function() {
template.init(element, scope);
scope.$eval();
+ delete scope.$init;
return scope;
}
});
diff --git a/src/Scope.js b/src/Scope.js
index 52ab3ed7..562dfbd8 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -70,17 +70,8 @@ function parserNewScopeAdapter(fn) {
};
}
-function isRenderableElement(element) {
- var name = element && element[0] && element[0].nodeName;
- return name && name.charAt(0) != '#' &&
- !includes(['TR', 'COL', 'COLGROUP', 'TBODY', 'THEAD', 'TFOOT'], name);
-}
-
function rethrow(e) { throw e; }
function errorHandlerFor(element, error) {
- while (!isRenderableElement(element)) {
- element = element.parent() || jqLite(document.body);
- }
elementError(element, NG_EXCEPTION, isDefined(error) ? toJson(error) : error);
}
@@ -132,14 +123,16 @@ function createScope(parent, services, existing) {
$watch: function(watchExp, listener, exceptionHandler) {
var watch = expressionCompile(watchExp),
- last = watch.call(instance);
- instance.$onEval(PRIORITY_WATCH, function(){
+ last;
+ function watcher(){
var value = watch.call(instance);
if (last !== value) {
instance.$tryEval(listener, exceptionHandler, value, last);
last = value;
}
- });
+ }
+ instance.$onEval(PRIORITY_WATCH, watcher);
+ watcher();
},
$onEval: function(priority, expr, exceptionHandler){
diff --git a/src/UrlWatcher.js b/src/UrlWatcher.js
deleted file mode 100644
index 1b2a9cf0..00000000
--- a/src/UrlWatcher.js
+++ /dev/null
@@ -1,46 +0,0 @@
-
-// ////////////////////////////
-// UrlWatcher
-// ////////////////////////////
-
-function UrlWatcher(location) {
- this.location = location;
- this.delay = 25;
- this.setTimeout = function(fn, delay) {
- window.setTimeout(fn, delay);
- };
- this.expectedUrl = location.href;
- this.listeners = [];
-}
-
-UrlWatcher.prototype = {
- watch: function(fn){
- this.listeners.push(fn);
- },
-
- start: function() {
- var self = this;
- (function pull () {
- if (self.expectedUrl !== self.location.href) {
- foreach(self.listeners, function(listener){
- listener(self.location.href);
- });
- self.expectedUrl = self.location.href;
- }
- self.setTimeout(pull, self.delay);
- })();
- },
-
- set: function(url) {
- var existingURL = this.location.href;
- if (!existingURL.match(/#/))
- existingURL += '#';
- if (existingURL != url)
- this.location.href = url;
- this.existingURL = url;
- },
-
- get: function() {
- return this.location.href;
- }
-};
diff --git a/src/Widgets.js b/src/Widgets.js
index 1e703a56..8e668c8f 100644
--- a/src/Widgets.js
+++ b/src/Widgets.js
@@ -156,40 +156,47 @@ angularWidget('SELECT', function(element){
});
-angularWidget('INLINE', function(element){
- element.replaceWith(this.element("div"));
+angularWidget('NG:INCLUDE', function(element){
var compiler = this,
- behavior = element.attr("behavior"),
- template = element.attr("template"),
- initExpr = element.attr("init");
- return function(boundElement){
- var scope = this;
- boundElement.load(template, function(){
- var templateScope = compiler.compile(boundElement)(boundElement, scope);
- templateScope.$tryEval(initExpr, boundElement);
- templateScope.$init();
+ src = element.attr("src");
+ return element.attr('switch-instance') ? null : function(element){
+ var scope = this, childScope;
+ element.attr('switch-instance', 'compiled');
+ scope.$browser.xhr('GET', src, function(code, response){
+ element.html(response);
+ childScope = createScope(scope);
+ compiler.compile(element)(element, childScope);
+ childScope.$init();
});
+ scope.$onEval(function(){ if (childScope) childScope.$eval(); });
};
});
-angularWidget('INCLUDE', function(element){
- element.replaceWith(this.element("div"));
- var matches = [];
- element.find("INLINE").each(function(){
- matches.push({match: jQuery(this).attr("match"), element: jQuery(this)});
- });
+angularWidget('NG:SWITCH', function(element){
var compiler = this,
- watchExpr = element.attr("watch");
- return function(boundElement){
+ watchExpr = element.attr("on"),
+ cases = [];
+ eachNode(element, function(caseElement){
+ var when = caseElement.attr('ng-switch-when');
+ if (when) {
+ cases.push({
+ when: function(value){ return value == when; },
+ element: caseElement,
+ template: compiler.compile(caseElement)
+ });
+ }
+ });
+ element.html('');
+ return function(element){
var scope = this;
this.$watch(watchExpr, function(value){
- foreach(matches, function(inline){
- if(inline.match == value) {
- var template = inline.element.attr("template");
- boundElement.load(template, function(){
- var templateScope = compiler.compile(boundElement)(boundElement, scope);
- templateScope.$init();
- });
+ element.html('');
+ foreach(cases, function(switchCase){
+ if (switchCase.when(value)) {
+ element.append(switchCase.element);
+ var childScope = createScope(scope);
+ switchCase.template(switchCase.element, childScope);
+ childScope.$init();
}
});
});
diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js
index d9633854..ce7849d8 100644
--- a/src/angular-bootstrap.js
+++ b/src/angular-bootstrap.js
@@ -47,7 +47,7 @@
addScript("/Parser.js");
addScript("/Resource.js");
addScript("/Browser.js");
- addScript("/AngularPublic.js");
+ addScript("/~AngularPublic.js");
// Extension points
addScript("/apis.js");
diff --git a/src/jqLite.js b/src/jqLite.js
index ec77a6fb..f8ed4d7d 100644
--- a/src/jqLite.js
+++ b/src/jqLite.js
@@ -109,6 +109,10 @@ JQLite.prototype = {
this[0].parentNode.replaceChild(jqLite(replaceNode)[0], this[0]);
},
+ append: function(node) {
+ this[0].appendChild(jqLite(node)[0]);
+ },
+
remove: function() {
this.dealoc();
this[0].parentNode.removeChild(this[0]);
@@ -182,6 +186,9 @@ JQLite.prototype = {
html: function(value) {
if (isDefined(value)) {
+ for ( var i = 0, children = this[0].childNodes; i < children.length; i++) {
+ jqLite(children[i]).dealoc();
+ }
this[0].innerHTML = value;
}
return this[0].innerHTML;
diff --git a/src/services.js b/src/services.js
index 16b48031..2532d3d3 100644
--- a/src/services.js
+++ b/src/services.js
@@ -6,8 +6,8 @@ angularService("$document", function(window){
var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.]*)(:([0-9]+))?([^\?#]+)(\?([^#]*))?((#([^\?]*))?(\?([^\?]*))?)$/;
var DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21};
angularService("$location", function(browser){
- var scope = this;
- function location(url){
+ var scope = this, location = {parse:parse, toString:toString};
+ function parse(url){
if (isDefined(url)) {
var match = URL_MATCH.exec(url);
if (match) {
@@ -23,17 +23,19 @@ angularService("$location", function(browser){
location.hashSearch = parseKeyValue(match[13]);
}
}
- var hashKeyValue = toKeyValue(location.hashSearch);
- var hash = (location.hashPath ? location.hashPath : '') +
- (hashKeyValue ? '?' + hashKeyValue : '');
+ }
+ function toString() {
+ var hashKeyValue = toKeyValue(location.hashSearch),
+ hash = (location.hashPath ? location.hashPath : '') + (hashKeyValue ? '?' + hashKeyValue : '');
return location.href.split('#')[0] + '#' + (hash ? hash : '');
}
browser.watchUrl(function(url){
- location(url);
+ parse(url);
+ scope.$root.$eval();
});
- location(browser.getUrl());
+ parse(browser.getUrl());
this.$onEval(PRIORITY_LAST, function(){
- var href = location();
+ var href = toString();
if (href != location.href) {
browser.setUrl(href);
location.href = href;
@@ -42,14 +44,3 @@ angularService("$location", function(browser){
return location;
}, {inject: ['$browser']});
-if (!angularService['$browser']) {
- var browserSingleton;
- angularService('$browser', function browserFactory(){
- if (!browserSingleton) {
- browserSingleton = new Browser(window.location);
- browserSingleton.startUrlWatcher();
- }
- return browserSingleton;
- });
-}
-
diff --git a/src/~AngularPublic.js b/src/~AngularPublic.js
deleted file mode 100644
index b9d0f9d7..00000000
--- a/src/~AngularPublic.js
+++ /dev/null
@@ -1,17 +0,0 @@
-extend(angular, {
- 'element': jqLite,
- 'compile': compile,
- 'scope': createScope,
- 'copy': copy,
- 'extend': extend,
- 'foreach': foreach,
- 'noop':noop,
- 'identity':identity,
- 'isUndefined': isUndefined,
- 'isDefined': isDefined,
- 'isString': isString,
- 'isFunction': isFunction,
- 'isNumber': isNumber,
- 'isArray': isArray
-});
-