diff options
| -rwxr-xr-x | lib/jsl/jsl.default.conf | 4 | ||||
| -rw-r--r-- | scenario/widgets.html | 8 | ||||
| -rw-r--r-- | src/Angular.js | 88 | ||||
| -rw-r--r-- | src/Browser.js | 46 | ||||
| -rw-r--r-- | src/Compiler.js | 11 | ||||
| -rw-r--r-- | src/Formatters.js | 2 | ||||
| -rw-r--r-- | src/JSON.js | 8 | ||||
| -rw-r--r-- | src/Parser.js | 4 | ||||
| -rw-r--r-- | src/Resource.js | 6 | ||||
| -rw-r--r-- | src/Scope.js | 53 | ||||
| -rw-r--r-- | src/Widgets.js | 23 | ||||
| -rw-r--r-- | src/angular-bootstrap.js | 1 | ||||
| -rw-r--r-- | src/directives.js | 11 | ||||
| -rw-r--r-- | src/jqLite.js | 4 | ||||
| -rw-r--r-- | src/markups.js | 6 | ||||
| -rw-r--r-- | src/services.js | 27 | ||||
| -rw-r--r-- | test/BinderTest.js | 5 | ||||
| -rw-r--r-- | test/BrowserTest.js (renamed from test/UrlWatcherTest.js) | 10 | ||||
| -rw-r--r-- | test/CompilerSpec.js | 62 | ||||
| -rw-r--r-- | test/FormattersTest.js | 22 | ||||
| -rw-r--r-- | test/ParserTest.js | 2 | ||||
| -rw-r--r-- | test/ScopeSpec.js | 57 | ||||
| -rw-r--r-- | test/servicesSpec.js | 17 | ||||
| -rw-r--r-- | test/testabilityPatch.js | 30 |
24 files changed, 262 insertions, 245 deletions
diff --git a/lib/jsl/jsl.default.conf b/lib/jsl/jsl.default.conf index f494a35c..fe4d0ea4 100755 --- a/lib/jsl/jsl.default.conf +++ b/lib/jsl/jsl.default.conf @@ -23,7 +23,7 @@ -missing_break # missing break statement +missing_break_for_last_case # missing break statement for last case in switch +comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) -+inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement +-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement +useless_void # use of the void type may be unnecessary (void is always undefined) +multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs +use_of_label # use of label @@ -41,7 +41,7 @@ +useless_assign # useless assignment +ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity +ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent) -+missing_default_case # missing default case in switch statement +-missing_default_case # missing default case in switch statement +duplicate_case_in_switch # duplicate case in switch statements +default_not_at_end # the default case is not at the end of the switch statement +legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax diff --git a/scenario/widgets.html b/scenario/widgets.html index 64e48c54..73674be9 100644 --- a/scenario/widgets.html +++ b/scenario/widgets.html @@ -4,8 +4,8 @@ <link rel="stylesheet" type="text/css" href="style.css"></link> <script type="text/javascript" src="../src/angular-bootstrap.js#autobind"></script> </head> - <body ng-init="$window.$scope = $root"> - <table ng-repeat="i in [0, 1]"> + <body ng-init="$window.$scope = this"> + <table> <tr> <th>Description</th> <th>Test</th> @@ -14,7 +14,7 @@ <tr><th colspan="3">Input text field</th></tr> <tr> <td>basic</td> - <td><input type="text" name="text.basic" /></td> + <td><input type="text" name="text.basic" ng-required /></td> <td>text.basic={{text.basic}}</td> </tr> <tr> @@ -70,7 +70,7 @@ </tr> <tr><th colspan="3">Buttons</th></tr> <tr> - <td>ng-action</td> + <td>ng-change<br/>ng-click</td> <td> <form ng-init="button.count = 0"> <input type="button" value="button" ng-change="button.count = button.count + 1"/> <br/> 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; + }); +} + diff --git a/test/BinderTest.js b/test/BinderTest.js index 9c5c5dc6..fa3127d7 100644 --- a/test/BinderTest.js +++ b/test/BinderTest.js @@ -19,6 +19,7 @@ BinderTest.prototype.tearDown = function(){ if (this.element && this.element.dealoc) this.element.dealoc(); }; + BinderTest.prototype.testChangingTextfieldUpdatesModel = function(){ var state = this.compile('<input type="text" name="model.price" value="abc">', {model:{}}); state.scope.$eval(); @@ -707,7 +708,7 @@ BinderTest.prototype.testItShouldDisplayErrorWhenActionIsSyntacticlyIncorect = f var second = jqLite(c.node[0].childNodes[1]); first.click(); - assertEquals("ABC", c.scope.$get('greeting')); + assertEquals("ABC", c.scope.greeting); second.click(); assertTrue(second.hasClass("ng-exception")); @@ -821,5 +822,3 @@ BinderTest.prototype.XtestWriteAnchorAsPartOfTheUpdateView = function(){ binder.updateView(); assertEquals(binder.location.get(), "a#a=b"); }; - - diff --git a/test/UrlWatcherTest.js b/test/BrowserTest.js index 6080ca62..2e630172 100644 --- a/test/UrlWatcherTest.js +++ b/test/BrowserTest.js @@ -1,11 +1,11 @@ -UrlWatcherTest = TestCase('UrlWatcherTest'); +BrowserTest = TestCase('BrowserTest'); -UrlWatcherTest.prototype.testUrlWatcher = function () { +BrowserTest.prototype.testUrlWatcher = function () { expectAsserts(2); var location = {href:"http://server", hash:""}; - var watcher = new UrlWatcher(location); + var watcher = new Browser(location); watcher.delay = 1; - watcher.watch(function(url){ + watcher.watchUrl(function(url){ assertEquals('http://getangular.test', url); }); watcher.setTimeout = function(fn, delay){ @@ -15,7 +15,7 @@ UrlWatcherTest.prototype.testUrlWatcher = function () { }; fn(); }; - watcher.start(); + watcher.startUrlWatcher(); }; FunctionTest = TestCase("FunctionTest"); diff --git a/test/CompilerSpec.js b/test/CompilerSpec.js index 59b7ab6b..9922070f 100644 --- a/test/CompilerSpec.js +++ b/test/CompilerSpec.js @@ -1,8 +1,4 @@ -xdescribe('compiler', function(){ - function element(html) { - return jQuery(html)[0]; - } - +describe('compiler', function(){ var compiler, textMarkup, directives, widgets, compile, log; beforeEach(function(){ @@ -29,25 +25,25 @@ xdescribe('compiler', function(){ widgets = {}; compiler = new Compiler(textMarkup, attrMarkup, directives, widgets); compile = function(html){ - var e = element("<div>" + html + "</div>"); - var view = compiler.compile(e)(e); - view.init(); - return view.scope; + var e = jqLite("<div>" + html + "</div>"); + var scope = compiler.compile(e)(e); + scope.$init(); + return scope; }; }); it('should recognize a directive', function(){ - var e = element('<div directive="expr" ignore="me"></div>'); + var e = jqLite('<div directive="expr" ignore="me"></div>'); directives.directive = function(expression, element){ log += "found"; expect(expression).toEqual("expr"); - expect(element[0]).toEqual(e); + expect(element).toEqual(e); return function initFn() { log += ":init"; }; }; var template = compiler.compile(e); - var init = template(e).init; + var init = template(e).$init; expect(log).toEqual("found"); init(); expect(log).toEqual("found:init"); @@ -61,13 +57,13 @@ xdescribe('compiler', function(){ it('should watch scope', function(){ var scope = compile('<span watch="name"/>'); expect(log).toEqual(""); - scope.updateView(); - scope.set('name', 'misko'); - scope.updateView(); - scope.updateView(); - scope.set('name', 'adam'); - scope.updateView(); - scope.updateView(); + scope.$eval(); + scope.$set('name', 'misko'); + scope.$eval(); + scope.$eval(); + scope.$set('name', 'adam'); + scope.$eval(); + scope.$eval(); expect(log).toEqual(":misko:adam"); }); @@ -83,29 +79,17 @@ xdescribe('compiler', function(){ element.removeAttr("duplicate"); var template = this.compile(element); return function(marker) { - this.$addEval(function() { + this.$onEval(function() { marker.after(template(element.clone()).element); }); }; }; var scope = compile('before<span duplicate="expr">x</span>after'); - expect($(scope.element).html()).toEqual('before<!--marker-->after'); - scope.updateView(); - expect($(scope.element).html()).toEqual('before<!--marker--><span>x</span>after'); - scope.updateView(); - expect($(scope.element).html()).toEqual('before<!--marker--><span>x</span><span>x</span>after'); - }); - - it('should allow for exculsive tags which suppress others', function(){ - directives.exclusive = function(){ - return function() { - log += ('exclusive'); - }; - }; - directives.exclusive.exclusive = true; - - compile('<span hello="misko", exclusive/>'); - expect(log).toEqual('exclusive'); + expect(sortedHtml(scope.$element)).toEqual('<div>before<#comment></#comment>after</div>'); + scope.$eval(); + expect(sortedHtml(scope.$element)).toEqual('<div>before<#comment></#comment>after</div>'); + scope.$eval(); + expect(sortedHtml(scope.$element)).toEqual('<div>before<#comment></#comment>after</div>'); }); it('should process markup before directives', function(){ @@ -117,7 +101,7 @@ xdescribe('compiler', function(){ } }); var scope = compile('before<span>middle</span>after'); - expect(scope.element.innerHTML).toEqual('before<span hello="middle">replaced</span>after'); + expect(scope.$element[0].innerHTML).toEqual('before<span hello="middle">replaced</span>after'); expect(log).toEqual("hello middle"); }); @@ -129,7 +113,7 @@ xdescribe('compiler', function(){ }; }; var scope = compile('<ng:button>push me</ng:button>'); - expect(scope.element.innerHTML).toEqual('<div>button</div>'); + expect(scope.$element[0].innerHTML).toEqual('<div>button</div>'); expect(log).toEqual('init'); }); diff --git a/test/FormattersTest.js b/test/FormattersTest.js index 59f12c1f..b520faf9 100644 --- a/test/FormattersTest.js +++ b/test/FormattersTest.js @@ -4,7 +4,7 @@ TestCase("formatterTest", { assertEquals("xyz", angular.formatter.noop.parse("xyz")); assertEquals(null, angular.formatter.noop.parse(null)); }, - + testList: function() { assertEquals('a, b', angular.formatter.list.format(['a', 'b'])); assertEquals('', angular.formatter.list.format([])); @@ -12,26 +12,26 @@ TestCase("formatterTest", { assertEquals([], angular.formatter.list.parse("")); assertEquals([], angular.formatter.list.parse(null)); }, - + testBoolean: function() { - assertEquals('true', angular.formatter.boolean.format(true)); - assertEquals('false', angular.formatter.boolean.format(false)); - assertEquals(true, angular.formatter.boolean.parse("true")); - assertEquals(false, angular.formatter.boolean.parse("")); - assertEquals(false, angular.formatter.boolean.parse("false")); - assertEquals(null, angular.formatter.boolean.parse(null)); + assertEquals('true', angular.formatter['boolean'].format(true)); + assertEquals('false', angular.formatter['boolean'].format(false)); + assertEquals(true, angular.formatter['boolean'].parse("true")); + assertEquals(false, angular.formatter['boolean'].parse("")); + assertEquals(false, angular.formatter['boolean'].parse("false")); + assertEquals(null, angular.formatter['boolean'].parse(null)); }, - + testNumber: function() { assertEquals('1', angular.formatter.number.format(1)); assertEquals(1, angular.formatter.number.format('1')); }, - + testTrim: function() { assertEquals('', angular.formatter.trim.format(null)); assertEquals('', angular.formatter.trim.format("")); assertEquals('a', angular.formatter.trim.format(" a ")); assertEquals('a', angular.formatter.trim.parse(' a ')); } - + }); diff --git a/test/ParserTest.js b/test/ParserTest.js index 639e919f..6170dd4a 100644 --- a/test/ParserTest.js +++ b/test/ParserTest.js @@ -171,7 +171,7 @@ ParserTest.prototype.testComparison = function(){ assertEquals(scope.$eval("1>2"), 1>2); assertEquals(scope.$eval("2>=1"), 2>=1); - assertEquals(true==2<3, scope.$eval("true==2<3")); + assertEquals(true === 2<3, scope.$eval("true==2<3")); }; diff --git a/test/ScopeSpec.js b/test/ScopeSpec.js index 8d2a0ed4..a7322cae 100644 --- a/test/ScopeSpec.js +++ b/test/ScopeSpec.js @@ -65,20 +65,6 @@ describe('scope/model', function(){ expect(model.$bind(function(){return this.name;})()).toEqual('misko'); }); - //$behavior - it('should behave as class', function(){ - function Printer(brand){ - this.brand = brand; - }; - Printer.prototype.print = function(){ - this.printed = true; - }; - var model = createScope({ name: 'parent' }, Printer, 'hp'); - expect(model.brand).toEqual('hp'); - model.print(); - expect(model.printed).toEqual(true); - }); - //$tryEval it('should report error on element', function(){ var scope = createScope(); @@ -108,16 +94,6 @@ describe('scope/model', function(){ expect(scope.log).toEqual('first;middle;last;'); }); - // Services are initialized - it("should inject services", function(){ - var scope = createScope(serviceAdapter({ - $window: function(){ - return window; - } - })); - expect(scope.$window).toEqual(window); - }); - it("should have $root and $parent", function(){ var parent = createScope(); var scope = createScope(parent); @@ -125,4 +101,37 @@ describe('scope/model', function(){ expect(scope.$parent).toEqual(parent); }); + // Service injection + it('should inject services', function(){ + var scope = createScope(null, { + service:function(){ + return "ABC"; + } + }); + expect(scope.service).toEqual("ABC"); + }); + + it('should inject arugments', function(){ + var scope = createScope(null, { + name:function(){ + return "misko"; + }, + greet: extend(function(name) { + return 'hello ' + name; + }, {inject:['name']}) + }); + expect(scope.greet).toEqual("hello misko"); + }); + + it('should throw error on missing dependency', function(){ + try { + createScope(null, { + greet: extend(function(name) { + }, {inject:['name']}) + }); + } catch(e) { + expect(e).toEqual("Don't know how to inject 'name'."); + } + }); + }); diff --git a/test/servicesSpec.js b/test/servicesSpec.js index b92975d0..49000af4 100644 --- a/test/servicesSpec.js +++ b/test/servicesSpec.js @@ -2,11 +2,7 @@ describe("services", function(){ var scope; beforeEach(function(){ - scope = createScope({ - $config: { - 'location': {'get':noop, 'set':noop, 'watch':noop} - } - }, serviceAdapter(angularService)); + scope = createScope(null, angularService, {}); }); it("should inject $window", function(){ @@ -46,4 +42,15 @@ describe("services", function(){ expect(scope.$location()).toEqual('file:///Users/Shared/misko/work/angular.js/scenario/widgets.html'); }); + xit('should add stylesheets', function(){ + scope.$document = { + getElementsByTagName: function(name){ + expect(name).toEqual('LINK'); + return []; + } + }; + scope.$document.addStyleSheet('css/angular.css'); + + }); + }); diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index 86d139cf..752f8ef2 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -3,7 +3,7 @@ dump = bind(jstd.console, jstd.console.log); function nakedExpect(obj) { return expect(angular.fromJson(angular.toJson(obj))); -}; +} swfobject = { createSwf:function() { @@ -27,15 +27,27 @@ function report(reportTest){ }); } -MockLocation = function() { +function MockBrowser() { this.url = "http://server"; + this.watches = []; +} +MockBrowser.prototype = { + getUrl: function(){ + return this.url; + }, + + setUrl: function(url){ + this.url = url; + }, + + watchUrl: function(fn) { + this.watches.push(fn); + } }; -MockLocation.prototype.get = function(){ - return this.url; -}; -MockLocation.prototype.set = function(url){ - this.url = url; -}; + +angularService('$browser', function(){ + return new MockBrowser(); +}); function childNode(element, index) { return jqLite(element[0].childNodes[index]); @@ -80,7 +92,7 @@ function sortedHtml(element) { } })(element[0]); return html; -}; +} function isVisible(node) { var display = node.css('display'); |
