aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMisko Hevery2010-04-03 17:04:36 -0700
committerMisko Hevery2010-04-03 17:04:36 -0700
commita80a61839a66d244c8bb14bbe2975746e02516c8 (patch)
tree5a7b4d9d3e2a7a15ebf55e068782fbf2aa4ac6bf /src
parent35ca4fcb9c49e505e28669e951e01ddedb01d7db (diff)
downloadangular.js-a80a61839a66d244c8bb14bbe2975746e02516c8.tar.bz2
injection is now working
Diffstat (limited to 'src')
-rw-r--r--src/Angular.js88
-rw-r--r--src/Browser.js46
-rw-r--r--src/Compiler.js11
-rw-r--r--src/Formatters.js2
-rw-r--r--src/JSON.js8
-rw-r--r--src/Parser.js4
-rw-r--r--src/Resource.js6
-rw-r--r--src/Scope.js53
-rw-r--r--src/Widgets.js23
-rw-r--r--src/angular-bootstrap.js1
-rw-r--r--src/directives.js11
-rw-r--r--src/jqLite.js4
-rw-r--r--src/markups.js6
-rw-r--r--src/services.js27
14 files changed, 148 insertions, 142 deletions
diff --git a/src/Angular.js b/src/Angular.js
index d97c2282..86fb5291 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -1,50 +1,3 @@
-
-//////////////////////////////
-//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;
- }
-};
-
////////////////////////////////////
if (typeof document.getAttribute == 'undefined')
@@ -53,9 +6,9 @@ if (typeof document.getAttribute == 'undefined')
if (!window['console']) window['console']={'log':noop, 'error':noop};
var consoleNode,
- PRIORITY_FIRST = -99999;
- PRIORITY_WATCH = -1000;
- PRIORITY_LAST = 99999;
+ PRIORITY_FIRST = -99999,
+ PRIORITY_WATCH = -1000,
+ PRIORITY_LAST = 99999,
NOOP = 'noop',
NG_ERROR = 'ng-error',
NG_EXCEPTION = 'ng-exception',
@@ -74,16 +27,14 @@ var consoleNode,
angularFilter = extensionMap(angular, 'filter'),
angularFormatter = extensionMap(angular, 'formatter'),
angularService = extensionMap(angular, 'service'),
- angularCallbacks = extensionMap(angular, 'callbacks'),
- urlWatcher = new UrlWatcher(window.location);
+ angularCallbacks = extensionMap(angular, 'callbacks');
function angularAlert(){
log(arguments); window.alert.apply(window, arguments);
-};
+}
extend(angular, {
'compile': compile,
- 'startUrlWatch': bind(urlWatcher, urlWatcher.start),
'copy': copy,
'extend': extend,
'foreach': foreach,
@@ -166,7 +117,7 @@ function isFunction(value){ return typeof value == 'function';}
function isTextNode(node) { return nodeName(node) == '#text'; }
function lowercase(value){ return isString(value) ? value.toLowerCase() : value; }
function uppercase(value){ return isString(value) ? value.toUpperCase() : value; }
-function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; };
+function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; }
function nodeName(element) { return (element[0] || element || {}).nodeName; }
function map(obj, iterator, context) {
var results = [];
@@ -174,7 +125,7 @@ function map(obj, iterator, context) {
results.push(iterator.call(context, value, index, list));
});
return results;
-};
+}
function size(obj) {
var size = 0;
if (obj) {
@@ -289,7 +240,7 @@ function copy(source, destination){
});
return destination;
}
-};
+}
function setHtml(node, html) {
if (isLeafNode(node)) {
@@ -367,22 +318,10 @@ function merge(src, dst) {
}
}
-function compile(element, config) {
+function compile(element, parentScope, overrides) {
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
- $element = jqLite(element),
- rootScope = createScope({
- $element: $element,
- $config: extend({
- 'onUpdateView': noop,
- 'server': "",
- 'location': {
- 'get':bind(urlWatcher, urlWatcher.get),
- 'set':bind(urlWatcher, urlWatcher.set),
- 'watch':bind(urlWatcher, urlWatcher.watch)
- }
- }, config || {})
- }, serviceAdapter(angularService));
- return compiler.compile($element)($element, rootScope);
+ $element = jqLite(element);
+ return compiler.compile($element)($element, parentScope, overrides);
}
/////////////////////////////////////////////////
@@ -404,11 +343,10 @@ function toKeyValue(obj) {
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
});
return parts.length ? parts.join('&') : '';
-};
+}
function angularInit(config){
if (config.autobind) {
- compile(window.document, config).$init();
+ compile(window.document, null, {'$config':config}).$init();
}
}
-
diff --git a/src/Browser.js b/src/Browser.js
new file mode 100644
index 00000000..bdf57386
--- /dev/null
+++ b/src/Browser.js
@@ -0,0 +1,46 @@
+
+//////////////////////////////
+// Browser
+//////////////////////////////
+
+function Browser(location) {
+ this.location = location;
+ this.delay = 25;
+ this.setTimeout = function(fn, delay) {
+ window.setTimeout(fn, delay);
+ };
+ this.expectedUrl = location.href;
+ this.listeners = [];
+}
+
+Browser.prototype = {
+ watchUrl: function(fn){
+ this.listeners.push(fn);
+ },
+
+ startUrlWatcher: 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);
+ })();
+ },
+
+ setUrl: function(url) {
+ var existingURL = this.location.href;
+ if (!existingURL.match(/#/))
+ existingURL += '#';
+ if (existingURL != url)
+ this.location.href = url;
+ this.existingURL = url;
+ },
+
+ getUrl: function() {
+ return this.location.href;
+ }
+};
diff --git a/src/Compiler.js b/src/Compiler.js
index c9039928..dac4931f 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -1,5 +1,5 @@
/**
- * Template provides directions an how to bind to a given element.
+= * Template provides directions an how to bind to a given element.
* It contains a list of init functions which need to be called to
* bind to a new instance of elements. It also provides a list
* of child paths which contain child templates
@@ -43,7 +43,7 @@ Template.prototype = {
},
empty: function() {
- return this.inits.length == 0 && this.paths.length == 0;
+ return this.inits.length === 0 && this.paths.length === 0;
}
};
@@ -63,8 +63,9 @@ Compiler.prototype = {
var template = this.templatize(rawElement) || new Template();
return function(element, parentScope){
element = jqLite(element);
- parentScope = parentScope || {};
- var scope = createScope(parentScope);
+ var scope = parentScope && parentScope.$eval ?
+ parentScope :
+ createScope(parentScope || {}, angularService);
return extend(scope, {
$element:element,
$init: function() {
@@ -161,7 +162,7 @@ function eachNode(element, fn){
function eachAttribute(element, fn){
var i, attrs = element[0].attributes || [], size = attrs.length, chld, attr, attrValue = {};
for (i = 0; i < size; i++) {
- var attr = attrs[i];
+ attr = attrs[i];
attrValue[attr.name] = attr.value;
}
foreach(attrValue, fn);
diff --git a/src/Formatters.js b/src/Formatters.js
index c1ff82c6..ee63c1a5 100644
--- a/src/Formatters.js
+++ b/src/Formatters.js
@@ -1,5 +1,5 @@
function formater(format, parse) {return {'format':format, 'parse':parse || format};}
-function toString(obj) {return isDefined(obj) ? "" + obj : obj;};
+function toString(obj) {return isDefined(obj) ? "" + obj : obj;}
extend(angularFormatter, {
'noop':formater(identity, identity),
'boolean':formater(toString, toBoolean),
diff --git a/src/JSON.js b/src/JSON.js
index 69e1b4c0..5c3e1043 100644
--- a/src/JSON.js
+++ b/src/JSON.js
@@ -4,11 +4,11 @@ function toJson(obj, pretty){
var buf = [];
toJsonArray(buf, obj, pretty ? "\n " : null, []);
return buf.join('');
-};
+}
function toPrettyJson(obj) {
return toJson(obj, true);
-};
+}
function fromJson(json) {
if (!json) return json;
@@ -21,7 +21,7 @@ function fromJson(json) {
error("fromJson error: ", json, e);
throw e;
}
-};
+}
angular['toJson'] = toJson;
angular['fromJson'] = fromJson;
@@ -102,4 +102,4 @@ function toJsonArray(buf, obj, pretty, stack){
if (typeof obj == "object") {
stack.pop();
}
-};
+}
diff --git a/src/Parser.js b/src/Parser.js
index ec58295a..8e42f18a 100644
--- a/src/Parser.js
+++ b/src/Parser.js
@@ -4,7 +4,7 @@ function Lexer(text, parsStrings){
this.dateParseLength = parsStrings ? 20 : -1;
this.tokens = [];
this.index = 0;
-};
+}
Lexer.OPERATORS = {
'null':function(self){return null;},
@@ -244,7 +244,7 @@ function Parser(text, parseStrings){
this.text = text;
this.tokens = new Lexer(text, parseStrings).parse();
this.index = 0;
-};
+}
Parser.ZERO = function(){
return 0;
diff --git a/src/Resource.js b/src/Resource.js
index 27ce8aa9..9fe60788 100644
--- a/src/Resource.js
+++ b/src/Resource.js
@@ -15,7 +15,7 @@ Route.prototype = {
var self = this;
var url = this.template;
params = params || {};
- foreach(this.urlParams, function(value, urlParam){
+ foreach(this.urlParams, function(_, urlParam){
var value = params[urlParam] || self.defaults[urlParam] || "";
url = url.replace(new RegExp(":" + urlParam + "(\\W)"), value + "$1");
});
@@ -57,7 +57,7 @@ ResourceFactory.prototype = {
function Resource(value){
copy(value || {}, this);
- };
+ }
foreach(actions, function(action, name){
var isGet = action.method == 'GET';
@@ -105,7 +105,7 @@ ResourceFactory.prototype = {
var params = {};
var callback = noop;
switch(arguments.length) {
- case 2: params = a1, callback = a2;
+ case 2: params = a1; callback = a2;
case 1: if (typeof a1 == 'function') callback = a1; else params = a1;
case 0: break;
default:
diff --git a/src/Scope.js b/src/Scope.js
index 26a3f85b..52ab3ed7 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -26,7 +26,7 @@ function getter(instance, path) {
return bind(lastInstance, instance);
}
return instance;
-};
+}
function setter(instance, path, value){
var element = path.split('.');
@@ -41,7 +41,7 @@ function setter(instance, path, value){
}
instance[element.shift()] = value;
return value;
-};
+}
var compileCache = {};
function expressionCompile(exp){
@@ -54,7 +54,7 @@ function expressionCompile(exp){
compileCache[exp] = expFn;
}
return parserNewScopeAdapter(expFn);
-};
+}
// return expFn
// TODO(remove this hack)
@@ -85,21 +85,16 @@ function errorHandlerFor(element, error) {
}
var scopeId = 0;
-function createScope(parent, Class) {
+function createScope(parent, services, existing) {
function Parent(){}
function API(){}
function Behavior(){}
- var instance, behavior, api, evalLists = {};
- if (isFunction(parent)) {
- Class = parent;
- parent = {};
- }
+ var instance, behavior, api, evalLists = {}, servicesCache = extend({}, existing);
- Class = Class || noop;
- parent = Parent.prototype = parent || {};
+ parent = Parent.prototype = (parent || {});
api = API.prototype = new Parent();
- behavior = Behavior.prototype = extend(new API(), Class.prototype);
+ behavior = Behavior.prototype = new API();
instance = new Behavior();
extend(api, {
@@ -161,22 +156,28 @@ function createScope(parent, Class) {
}
});
- if (isUndefined(instance.$root)) {
- behavior.$root = instance;
- behavior.$parent = instance;
+ if (!parent.$root) {
+ api.$root = instance;
+ api.$parent = instance;
}
- (parent.$onEval || noop)(instance.$eval);
- Class.apply(instance, slice.call(arguments, 2, arguments.length));
+ function inject(name){
+ var service = getter(servicesCache, name), factory, args = [];
+ if (isUndefined(service)) {
+ factory = services[name];
+ if (!isFunction(factory))
+ throw "Don't know how to inject '" + name + "'.";
+ foreach(factory.inject, function(dependency){
+ args.push(inject(dependency));
+ });
+ setter(servicesCache, name, service = factory.apply(instance, args));
+ }
+ return service;
+ }
+
+ foreach(services, function(_, name){
+ instance[name] = inject(name);
+ });
return instance;
}
-
-function serviceAdapter(services) {
- return function(){
- var self = this;
- foreach(services, function(service, name){
- self[name] = service.call(self);
- });
- };
-};
diff --git a/src/Widgets.js b/src/Widgets.js
index a05ea63c..1e703a56 100644
--- a/src/Widgets.js
+++ b/src/Widgets.js
@@ -23,7 +23,7 @@ function valueAccessor(scope, element) {
validator = compileValidator(validatorName),
required = element.attr('ng-required'),
lastError;
- required = required || required == '';
+ required = required || required === '';
if (!validator) throw "Validator named '" + validatorName + "' not found.";
function validate(value) {
var error = required && !trim(value) ? "Required" : validator({self:scope, scope:{get:scope.$get, set:scope.$set}}, value);
@@ -115,7 +115,7 @@ function radioInit(model, view, element) {
var modelValue = model.get(), viewValue = view.get(), input = element[0];
input.name = this.$id + '@' + input.name;
if (isUndefined(modelValue)) model.set(null);
- if (viewValue != null) model.set(viewValue);
+ if (viewValue !== null) model.set(viewValue);
}
function inputWidget(events, modelAccessor, viewAccessor, initFn) {
@@ -126,14 +126,17 @@ function inputWidget(events, modelAccessor, viewAccessor, initFn) {
action = element.attr('ng-change') || '';
initFn.call(scope, model, view, element);
this.$eval(element.attr('ng-init')||'');
- element.bind(events, function(){
- model.set(view.get());
- scope.$tryEval(action, element);
- scope.$root.$eval();
- // if we have noop initFn than we are just a button,
- // therefore we want to prevent default action
- return initFn != noop;
- });
+ // Don't register a handler if we are a button (noopAccessor) and there is no action
+ if (action || modelAccessor !== noopAccessor) {
+ element.bind(events, function(){
+ model.set(view.get());
+ scope.$tryEval(action, element);
+ scope.$root.$eval();
+ // if we have noop initFn than we are just a button,
+ // therefore we want to prevent default action
+ return initFn != noop;
+ });
+ }
view.set(model.get());
scope.$watch(model.get, view.set);
};
diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js
index b0a3aa4f..7484b0c3 100644
--- a/src/angular-bootstrap.js
+++ b/src/angular-bootstrap.js
@@ -46,6 +46,7 @@
addScript("/jqlite.js");
addScript("/Parser.js");
addScript("/Resource.js");
+ addScript("/Browser.js");
// Extension points
addScript("/apis.js");
diff --git a/src/directives.js b/src/directives.js
index de68360e..4f2916da 100644
--- a/src/directives.js
+++ b/src/directives.js
@@ -52,7 +52,8 @@ function compileBindTemplate(template){
};
}
return fn;
-};
+}
+
angularDirective("ng-bind-template", function(expression){
var templateFn = compileBindTemplate(expression);
return function(element) {
@@ -120,7 +121,7 @@ angularWidget("@ng-repeat", function(expression, element){
assign(childScope = children[index]);
} else {
// grow children
- assign(childScope = template(element.clone(), currentScope));
+ assign(childScope = template(element.clone(), createScope(currentScope)));
lastElement.after(childScope.$element);
childScope.$index = index;
childScope.$element.attr('ng-repeat-index', index);
@@ -144,7 +145,7 @@ angularDirective("ng-click", function(expression, element){
var self = this;
element.click(function(){
self.$tryEval(expression, element);
- self.$eval();
+ self.$root.$eval();
return false;
});
};
@@ -180,8 +181,8 @@ function ngClass(selector) {
}
angularDirective("ng-class", ngClass(function(){return true;}));
-angularDirective("ng-class-odd", ngClass(function(i){return i % 2 == 0;}));
-angularDirective("ng-class-even", ngClass(function(i){return i % 2 == 1;}));
+angularDirective("ng-class-odd", ngClass(function(i){return i % 2 === 0;}));
+angularDirective("ng-class-even", ngClass(function(i){return i % 2 === 1;}));
angularDirective("ng-show", function(expression, element){
return function(element){
diff --git a/src/jqLite.js b/src/jqLite.js
index 3baafd51..ec77a6fb 100644
--- a/src/jqLite.js
+++ b/src/jqLite.js
@@ -5,7 +5,7 @@
var jqCache = {};
var jqName = 'ng-' + new Date().getTime();
var jqId = 1;
-function jqNextId() { return jqId++; }
+function jqNextId() { return (jqId++); }
var addEventListener = window.document.attachEvent ?
function(element, type, fn) {
@@ -31,7 +31,7 @@ function jqClearData(element) {
delete jqCache[cacheId];
delete element[jqName];
}
-};
+}
function JQLite(element) {
this[0] = element;
diff --git a/src/markups.js b/src/markups.js
index 3ae713fb..5c069f49 100644
--- a/src/markups.js
+++ b/src/markups.js
@@ -16,16 +16,16 @@ function parseBindings(string) {
if (lastIndex != string.length)
results.push(string.substr(lastIndex, string.length - lastIndex));
return results.length === 0 ? [ string ] : results;
-};
+}
function binding(string) {
var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/);
return binding ? binding[1] : null;
-};
+}
function hasBindings(bindings) {
return bindings.length > 1 || binding(bindings[0]) !== null;
-};
+}
angularTextMarkup('{{}}', function(text, textNode, parentElement) {
var bindings = parseBindings(text),
diff --git a/src/services.js b/src/services.js
index fc12b22b..59c21d36 100644
--- a/src/services.js
+++ b/src/services.js
@@ -1,8 +1,11 @@
angularService("$window", bind(window, identity, window));
+angularService("$document", function(window){
+ return jqLite(window.document);
+}, {inject:['$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(){
+angularService("$location", function(browser){
var scope = this;
function location(url){
if (isDefined(url)) {
@@ -24,17 +27,29 @@ angularService("$location", function(){
return location.href +
(location.hashPath ? location.hashPath : '') +
(hashKeyValue ? '?' + hashKeyValue : '');
- };
- this.$config.location.watch(function(url){
+ }
+ browser.watchUrl(function(url){
location(url);
});
- location(this.$config.location.get());
+ location(browser.getUrl());
this.$onEval(PRIORITY_LAST, function(){
var href = location();
if (href != location.href) {
- scope.$config.location.set(location());
+ browser.setUrl(href);
location.href = href;
}
});
return location;
-});
+}, {inject: ['$browser']});
+
+if (!angularService['$browser']) {
+ var browserSingleton;
+ angularService('$browser', function browserFactory(){
+ if (!browserSingleton) {
+ browserSingleton = new Browser(window.location);
+ browserSingleton.startUrlWatcher();
+ }
+ return browserSingleton;
+ });
+}
+