From 843bd355d25ebf2369aec79f98cb6704d38497e9 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 9 Apr 2010 16:20:15 -0700 Subject: [PATCH] various bug fixes --- angular-debug.js | 86 ++++++---- example/temp.html | 9 ++ jsTestDriver.conf | 4 +- lib/jasmine-jstd-adapter/JasmineAdapter.js | 149 +++++++++--------- .../{jasmine-0.10.1.js => jasmine-0.10.3.js} | 99 +++++++++--- lib/jstestdriver/JsTestDriver.jar | Bin 3133666 -> 3133701 bytes src/Angular.js | 2 +- src/Compiler.js | 14 +- src/Scope.js | 5 +- src/angular-bootstrap.js | 2 +- src/apis.js | 4 +- src/services.js | 26 +-- src/widgets.js | 35 ++-- test/ApiTest.js | 4 + test/ValidatorsTest.js | 2 +- test/markupSpec.js | 5 + test/servicesSpec.js | 14 ++ test/widgetsSpec.js | 9 +- 18 files changed, 304 insertions(+), 165 deletions(-) create mode 100644 example/temp.html rename lib/jasmine/{jasmine-0.10.1.js => jasmine-0.10.3.js} (96%) diff --git a/angular-debug.js b/angular-debug.js index c3f419f7..dd74428a 100644 --- a/angular-debug.js +++ b/angular-debug.js @@ -343,7 +343,7 @@ function merge(src, dst) { } function compile(element, parentScope, overrides) { - var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget); + var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget), $element = jqLite(element), parent = extend({}, parentScope); parent.$element = $element; @@ -641,8 +641,8 @@ Compiler.prototype = { }; function eachTextNode(element, fn){ - var i, chldNodes = element[0].childNodes || [], size = chldNodes.length, chld; - for (i = 0; i < size; i++) { + var i, chldNodes = element[0].childNodes || [], chld; + for (i = 0; i < chldNodes.length; i++) { if(isTextNode(chld = chldNodes[i])) { fn(jqLite(chld), i); } @@ -650,8 +650,8 @@ function eachTextNode(element, fn){ } function eachNode(element, fn){ - var i, chldNodes = element[0].childNodes || [], size = chldNodes.length, chld; - for (i = 0; i < size; i++) { + var i, chldNodes = element[0].childNodes || [], chld; + for (i = 0; i < chldNodes.length; i++) { if(!isTextNode(chld = chldNodes[i])) { fn(jqLite(chld), i); } @@ -659,12 +659,12 @@ 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 i, attrs = element[0].attributes || [], chld, attr, attrValue = {}; + for (i = 0; i < attrs.length; i++) { attr = attrs[i]; attrValue[attr.name] = attr.value; } - foreach(attrValue, fn); + foreachSorted(attrValue, fn); } function getter(instance, path, unboundFn) { @@ -848,7 +848,10 @@ function createScope(parent, services, existing) { } foreach(services, function(_, name){ - instance[name] = inject(name); + var service = inject(name); + if (service) { + instance[name] = service; + } }); return instance; @@ -2026,7 +2029,9 @@ var angularGlobal = { var angularCollection = { 'size': size }; -var angularObject = {}; +var angularObject = { + 'extend': extend +}; var angularArray = { 'indexOf': indexOf, 'include': includes, @@ -3135,9 +3140,11 @@ function valueAccessor(scope, element) { required = required || required === ''; if (!validator) throw "Validator named '" + validatorName + "' not found."; function validate(value) { - var error = required && !trim(value) ? - "Required" : - validator({state:scope, scope:{get:scope.$get, set:scope.$set}}, value); + var error, + validateScope = extend(new (extend(function(){}, {prototype:scope}))(), {$element:element}); + error = required && !trim(value) ? + "Required" : + validator({state:validateScope, scope:{get:validateScope.$get, set:validateScope.$set}}, value); if (error !== lastError) { elementError(element, NG_VALIDATION_ERROR, error); lastError = error; @@ -3298,7 +3305,8 @@ angularWidget('NG:INCLUDE', function(element){ angularWidget('NG:SWITCH', function ngSwitch(element){ var compiler = this, watchExpr = element.attr("on"), - whenFn = ngSwitch[element.attr("using") || 'equals']; + whenExpr = (element.attr("using") || 'equals').split(":"); + whenFn = ngSwitch[whenExpr.shift()]; changeExpr = element.attr('change') || '', cases = []; if (!whenFn) throw "Using expression '" + usingExpr + "' unknown."; @@ -3307,7 +3315,11 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ if (when) { cases.push({ when: function(scope, value){ - return whenFn.call(scope, value, when); + var args = [value, when]; + foreach(whenExpr, function(arg){ + args.push(arg); + }); + return whenFn.apply(scope, args); }, change: changeExpr, element: caseElement, @@ -3320,13 +3332,10 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ var scope = this, childScope; this.$watch(watchExpr, function(value){ element.html(''); - childScope = null; - var params = {}; + childScope = createScope(scope); foreach(cases, function(switchCase){ - if (switchCase.when(params, value)) { + if (switchCase.when(childScope, value)) { element.append(switchCase.element); - childScope = createScope(scope); - extend(childScope, params); childScope.$tryEval(switchCase.change, element); switchCase.template(switchCase.element, childScope); childScope.$init(); @@ -3341,13 +3350,15 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ equals: function(on, when) { return on == when; }, - route: function(on, when) { - var regex = '^' + when.replace(/[\.\\\(\)\^\$]/g, "\$1") + '$', params = [], self = this; + route: function(on, when, dstName) { + var regex = '^' + when.replace(/[\.\\\(\)\^\$]/g, "\$1") + '$', + params = [], + dst = {}; foreach(when.split(/\W/), function(param){ if (param) { var paramRegExp = new RegExp(":" + param + "([\\W])"); if (regex.match(paramRegExp)) { - regex = regex.replace(paramRegExp, "(.*)$1"); + regex = regex.replace(paramRegExp, "([^\/]*)$1"); params.push(param); } } @@ -3355,8 +3366,9 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ var match = on.match(new RegExp(regex)); if (match) { foreach(params, function(name, index){ - self[name] = match[index + 1]; + dst[name] = match[index + 1]; }); + if (dstName) this.$set(dstName, dst); } return match; } @@ -3370,6 +3382,7 @@ 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, location = {parse:parse, toString:toString}; + var lastHash; function parse(url){ if (isDefined(url)) { var match = URL_MATCH.exec(url); @@ -3381,16 +3394,25 @@ angularService("$location", function(browser){ location.path = match[6]; location.search = parseKeyValue(match[8]); location.hash = match[9]; - if (location.hash) location.hash = location.hash.substr(1); + if (location.hash) + location.hash = location.hash.substr(1); + lastHash = location.hash; location.hashPath = match[11] || ''; location.hashSearch = parseKeyValue(match[13]); } } } function toString() { - var hashKeyValue = toKeyValue(location.hashSearch), - hash = (location.hashPath ? location.hashPath : '') + (hashKeyValue ? '?' + hashKeyValue : ''); - return location.href.split('#')[0] + '#' + (hash ? hash : ''); + if (lastHash === location.hash) { + var hashKeyValue = toKeyValue(location.hashSearch), + hash = (location.hashPath ? location.hashPath : '') + (hashKeyValue ? '?' + hashKeyValue : ''), + url = location.href.split('#')[0] + '#' + (hash ? hash : ''); + if (url !== location.href) parse(url); + return url; + } else { + parse(location.href.split('#')[0] + '#' + location.hash); + return toString(); + } } browser.watchUrl(function(url){ parse(url); @@ -3398,11 +3420,7 @@ angularService("$location", function(browser){ }); parse(browser.getUrl()); this.$onEval(PRIORITY_LAST, function(){ - var href = toString(); - if (href != location.href) { - browser.setUrl(href); - location.href = href; - } + browser.setUrl(toString()); }); return location; }, {inject: ['$browser']}); @@ -3432,6 +3450,7 @@ angularService("$hover", function(browser) { tooltip.arrow.addClass('ng-arrow-right'); tooltip.arrow.css({left: (width + 1)+'px'}); tooltip.callout.css({ + position: 'fixed', left: (elementRect.left - arrowWidth - width - 4) + "px", top: (elementRect.top - 3) + "px", width: width + "px" @@ -3439,6 +3458,7 @@ angularService("$hover", function(browser) { } else { tooltip.arrow.addClass('ng-arrow-left'); tooltip.callout.css({ + position: 'fixed', left: (elementRect.right + arrowWidth) + "px", top: (elementRect.top - 3) + "px", width: width + "px" diff --git a/example/temp.html b/example/temp.html new file mode 100644 index 00000000..3580249d --- /dev/null +++ b/example/temp.html @@ -0,0 +1,9 @@ + + + + + + + {{'first'}}
{{'second'}}
+ + diff --git a/jsTestDriver.conf b/jsTestDriver.conf index a0cae9a4..9c2ef63f 100644 --- a/jsTestDriver.conf +++ b/jsTestDriver.conf @@ -1,9 +1,9 @@ server: http://localhost:9876 load: - - lib/jasmine/jasmine-0.10.1.js + - lib/jasmine/jasmine-0.10.3.js - lib/jasmine-jstd-adapter/JasmineAdapter.js - - lib/webtoolkit/webtoolkit.base64.js +# - lib/webtoolkit/webtoolkit.base64.js # - lib/jquery/jquery-1.4.2.js # - lib/underscore/underscore.js - src/Angular.js diff --git a/lib/jasmine-jstd-adapter/JasmineAdapter.js b/lib/jasmine-jstd-adapter/JasmineAdapter.js index 83a1deed..ba54251a 100644 --- a/lib/jasmine-jstd-adapter/JasmineAdapter.js +++ b/lib/jasmine-jstd-adapter/JasmineAdapter.js @@ -1,96 +1,103 @@ /** * @fileoverview Jasmine JsTestDriver Adapter. * @author ibolmo@gmail.com (Olmo Maldonado) + * @author misko@hevery.com (Misko Hevery) */ (function() { -// Suite/TestCase before and after function stacks. -var before = []; -var after = []; + function bind(_this, _function){ + return function(){ + return _function.call(_this); + } + } -jasmine.Env.prototype.describe = (function(describe){ + var currentFrame = frame(null, null); - // TODO(ibolmo): Support nested describes. - return function(description, specDefinitions){ - this.currentTestCase = TestCase(description); - return describe.call(this, description, specDefinitions); - }; + function frame(parent, name){ + var caseName = (parent && parent.caseName ? parent.caseName + " " : '') + (name ? name : ''); + var frame = { + name: name, + caseName: caseName, + parent: parent, + testCase: TestCase(caseName), + before: [], + after: [], + runBefore: function(){ + if (parent) parent.runBefore.apply(this); + for ( var i = 0; i < frame.before.length; i++) { + frame.before[i].apply(this); + } + }, + runAfter: function(){ + for ( var i = 0; i < frame.after.length; i++) { + frame.after[i].apply(this); + } + if (parent) parent.runAfter.apply(this); + } + }; + return frame; + }; -})(jasmine.Env.prototype.describe); + jasmine.Env.prototype.describe = (function(describe){ + return function(description){ + currentFrame = frame(currentFrame, description); + var val = describe.apply(this, arguments); + currentFrame = currentFrame.parent; + return val; + }; + + })(jasmine.Env.prototype.describe); -jasmine.Env.prototype.it = (function(it){ + jasmine.Env.prototype.it = (function(it){ + return function(desc, itFn){ + var self = this; + var spec = it.apply(this, arguments); + var currentSpec = this.currentSpec; + var frame = this.jstdFrame = currentFrame; + this.jstdFrame.testCase.prototype['test that it ' + desc] = function(){ + frame.runBefore.apply(currentSpec); + try { + itFn.apply(currentSpec); + } finally { + frame.runAfter.apply(currentSpec); + } + }; + return spec; + }; - return function(desc, func){ - var spec = it.call(this, desc, func); - this.currentTestCase.prototype['test that it ' + desc] = func; - return spec; - }; - -})(jasmine.Env.prototype.it); + })(jasmine.Env.prototype.it); -jasmine.Env.prototype.beforeEach = (function(beforeEach){ + jasmine.Env.prototype.beforeEach = (function(beforeEach){ + return function(beforeEachFunction) { + beforeEach.apply(this, arguments); + currentFrame.before.push(beforeEachFunction); + }; - // TODO(ibolmo): Support beforeEach TestCase. - return function(beforeEachFunction) { - beforeEach.call(this, beforeEachFunction); - if (this.currentTestCase) { - this.currentTestCase.prototype.setUp = beforeEachFunction; - } else { - before.push(beforeEachFunction); - } - }; - -})(jasmine.Env.prototype.beforeEach); + })(jasmine.Env.prototype.beforeEach); -jasmine.Env.prototype.afterEach = (function(afterEach){ + jasmine.Env.prototype.afterEach = (function(afterEach){ + return function(afterEachFunction) { + afterEach.apply(this, arguments); + currentFrame.after.push(afterEachFunction); + }; - // TODO(ibolmo): Support afterEach TestCase. - return function(afterEachFunction) { - afterEach.call(this, afterEachFunction); - if (this.currentTestCase) { - this.currentTestCase.prototype.tearDown = afterEachFunction; - } else { - after.push(afterEachFunction); - } - }; - -})(jasmine.Env.prototype.afterEach); + })(jasmine.Env.prototype.afterEach); -jasmine.NestedResults.prototype.addResult = (function(addResult){ + jasmine.NestedResults.prototype.addResult = (function(addResult){ + return function(result) { + addResult.call(this, result); + if (result.type != 'MessageResult' && !result.passed()) fail(result.message); + }; - return function(result) { - addResult.call(this, result); - if (result.type != 'MessageResult' && !result.passed()) fail(result.message); - }; + })(jasmine.NestedResults.prototype.addResult); -})(jasmine.NestedResults.prototype.addResult); - - -jstestdriver.plugins.TestRunnerPlugin.prototype.runTestConfiguration = (function(runTestConfiguration){ - - return function(testRunConfiguration, onTestDone, onTestRunConfigurationComplete){ - for (var i = 0, l = before.length; i < l; i++) before[i](); - onTestRunConfigurationComplete = (function(configurationComplete){ - - return function() { - for (var i = 0, l = after.length; i < l; i++) after[i](); - configurationComplete(); - }; - - })(onTestRunConfigurationComplete); - runTestConfiguration.call(this, testRunConfiguration, onTestDone, onTestRunConfigurationComplete); - }; - -})(jstestdriver.plugins.TestRunnerPlugin.prototype.runTestConfiguration); - - -// Reset environment with overriden methods. -jasmine.currentEnv_ = null; -jasmine.getEnv(); + // Reset environment with overriden methods. + jasmine.currentEnv_ = null; + jasmine.getEnv(); })(); diff --git a/lib/jasmine/jasmine-0.10.1.js b/lib/jasmine/jasmine-0.10.3.js similarity index 96% rename from lib/jasmine/jasmine-0.10.1.js rename to lib/jasmine/jasmine-0.10.3.js index f9bd7d6f..f309493f 100644 --- a/lib/jasmine/jasmine-0.10.1.js +++ b/lib/jasmine/jasmine-0.10.3.js @@ -13,7 +13,7 @@ jasmine.unimplementedMethod_ = function() { }; /** - * Use jasmine.undefined instead of undefined, since undefinedjasmine.undefined instead of undefined, since undefined is just * a plain old variable and may be redefined by somebody else. * * @private @@ -89,7 +89,38 @@ jasmine.getEnv = function() { * @returns {Boolean} */ jasmine.isArray_ = function(value) { - return Object.prototype.toString.apply(value) === '[object Array]'; + return jasmine.isA_("Array", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isString_ = function(value) { + return jasmine.isA_("String", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isNumber_ = function(value) { + return jasmine.isA_("Number", value); +}; + +/** + * @ignore + * @private + * @param {String} typeName + * @param value + * @returns {Boolean} + */ +jasmine.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; }; /** @@ -527,6 +558,7 @@ jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { * * @param {String} url path to the file to include * @param {Boolean} opt_global + * @deprecated We suggest you use a different method of including JS source files. jasmine.include will be removed soon. */ jasmine.include = function(url, opt_global) { if (opt_global) { @@ -659,6 +691,18 @@ jasmine.Env.prototype.version = function () { } }; +/** + * @returns string containing jasmine version build info, if set. + */ +jasmine.Env.prototype.versionString = function() { + if (jasmine.version_) { + var version = this.version(); + return version.major + "." + version.minor + "." + version.build + " revision " + version.revision; + } else { + return "version unknown"; + } +}; + /** * @returns a sequential integer starting at 0 */ @@ -794,6 +838,12 @@ jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { mismatchKeys = mismatchKeys || []; mismatchValues = mismatchValues || []; + for (var i = 0; i < this.equalityTesters_.length; i++) { + var equalityTester = this.equalityTesters_[i]; + var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); + if (result !== jasmine.undefined) return result; + } + if (a === b) return true; if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { @@ -816,14 +866,16 @@ jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { return b.matches(a); } - if (typeof a === "object" && typeof b === "object") { - return this.compareObjects_(a, b, mismatchKeys, mismatchValues); + if (jasmine.isString_(a) && jasmine.isString_(b)) { + return (a == b); } - for (var i = 0; i < this.equalityTesters_.length; i++) { - var equalityTester = this.equalityTesters_[i]; - var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); - if (result !== jasmine.undefined) return result; + if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { + return (a == b); + } + + if (typeof a === "object" && typeof b === "object") { + return this.compareObjects_(a, b, mismatchKeys, mismatchValues); } //Straight check @@ -1009,11 +1061,13 @@ jasmine.Matchers = function(env, actual, spec, opt_isNot) { this.reportWasCalled_ = false; }; +// todo: @deprecated as of Jasmine 0.11, remove soon [xw] jasmine.Matchers.pp = function(str) { - return jasmine.util.htmlEscape(jasmine.pp(str)); + throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); + this.report(); }; -/** @deprecated */ +/** @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. */ jasmine.Matchers.prototype.report = function(result, failing_message, details) { // todo: report a deprecation warning [xw] @@ -1180,7 +1234,7 @@ jasmine.Matchers.prototype.wasCalled = function() { } if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.'); + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { @@ -1199,7 +1253,7 @@ jasmine.Matchers.prototype.wasNotCalled = function() { } if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.'); + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { @@ -1218,7 +1272,7 @@ jasmine.Matchers.prototype.wasNotCalled = function() { jasmine.Matchers.prototype.wasCalledWith = function() { var expectedArgs = jasmine.util.argsToArray(arguments); if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.'); + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { if (this.actual.callCount == 0) { @@ -1234,7 +1288,7 @@ jasmine.Matchers.prototype.wasCalledWith = function() { jasmine.Matchers.prototype.wasNotCalledWith = function() { var expectedArgs = jasmine.util.argsToArray(arguments); if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.'); + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { @@ -1882,8 +1936,7 @@ jasmine.Spec.prototype.finish = function(onComplete) { } }; -jasmine.Spec.prototype.after = function(doAfter, test) { - +jasmine.Spec.prototype.after = function(doAfter) { if (this.queue.isRunning()) { this.queue.add(new jasmine.Block(this.env, doAfter, this)); } else { @@ -1911,23 +1964,25 @@ jasmine.Spec.prototype.execute = function(onComplete) { jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { var runner = this.env.currentRunner(); + var i; + for (var suite = this.suite; suite; suite = suite.parentSuite) { - for (var i = 0; i < suite.before_.length; i++) { + for (i = 0; i < suite.before_.length; i++) { this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); } } - for (var i = 0; i < runner.before_.length; i++) { + for (i = 0; i < runner.before_.length; i++) { this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); } for (i = 0; i < this.afterCallbacks.length; i++) { this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); } for (suite = this.suite; suite; suite = suite.parentSuite) { - for (var i = 0; i < suite.after_.length; i++) { + for (i = 0; i < suite.after_.length; i++) { this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); } } - for (var i = 0; i < runner.after_.length; i++) { + for (i = 0; i < runner.after_.length; i++) { this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); } }; @@ -2271,6 +2326,6 @@ window.clearInterval = function(timeoutKey) { jasmine.version_= { "major": 0, "minor": 10, - "build": 1, - "revision": 1267503060 + "build": 3, + "revision": 1270162784 }; diff --git a/lib/jstestdriver/JsTestDriver.jar b/lib/jstestdriver/JsTestDriver.jar index ead315934ae9e6571eca4ef67b1bdc3534232fd3..00482edadd9454b37ecdf2eb3552929a5975d4eb 100644 GIT binary patch delta 17196 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 delta 17139 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 Xj!K{Vzuy4Vj7po4Z5_dZUse2nfQ{nT diff --git a/src/Angular.js b/src/Angular.js index 7fb59f86..11ebc4bc 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -319,7 +319,7 @@ function merge(src, dst) { } function compile(element, parentScope, overrides) { - var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget); + var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget), $element = jqLite(element), parent = extend({}, parentScope); parent.$element = $element; diff --git a/src/Compiler.js b/src/Compiler.js index ae2bcdb6..8c95ee8e 100644 --- a/src/Compiler.js +++ b/src/Compiler.js @@ -143,8 +143,8 @@ Compiler.prototype = { }; function eachTextNode(element, fn){ - var i, chldNodes = element[0].childNodes || [], size = chldNodes.length, chld; - for (i = 0; i < size; i++) { + var i, chldNodes = element[0].childNodes || [], chld; + for (i = 0; i < chldNodes.length; i++) { if(isTextNode(chld = chldNodes[i])) { fn(jqLite(chld), i); } @@ -152,8 +152,8 @@ function eachTextNode(element, fn){ } function eachNode(element, fn){ - var i, chldNodes = element[0].childNodes || [], size = chldNodes.length, chld; - for (i = 0; i < size; i++) { + var i, chldNodes = element[0].childNodes || [], chld; + for (i = 0; i < chldNodes.length; i++) { if(!isTextNode(chld = chldNodes[i])) { fn(jqLite(chld), i); } @@ -161,11 +161,11 @@ 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 i, attrs = element[0].attributes || [], chld, attr, attrValue = {}; + for (i = 0; i < attrs.length; i++) { attr = attrs[i]; attrValue[attr.name] = attr.value; } - foreach(attrValue, fn); + foreachSorted(attrValue, fn); } diff --git a/src/Scope.js b/src/Scope.js index 0bc551c4..7529d726 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -179,7 +179,10 @@ function createScope(parent, services, existing) { } foreach(services, function(_, name){ - instance[name] = inject(name); + var service = inject(name); + if (service) { + instance[name] = service; + } }); return instance; diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js index d9633854..704c50e2 100644 --- a/src/angular-bootstrap.js +++ b/src/angular-bootstrap.js @@ -43,7 +43,7 @@ addScript("/JSON.js"); addScript("/Compiler.js"); addScript("/Scope.js"); - addScript("/jqlite.js"); + addScript("/jqLite.js"); addScript("/Parser.js"); addScript("/Resource.js"); addScript("/Browser.js"); diff --git a/src/apis.js b/src/apis.js index 3d0c5db3..5864ad60 100644 --- a/src/apis.js +++ b/src/apis.js @@ -14,7 +14,9 @@ var angularGlobal = { var angularCollection = { 'size': size }; -var angularObject = {}; +var angularObject = { + 'extend': extend +}; var angularArray = { 'indexOf': indexOf, 'include': includes, diff --git a/src/services.js b/src/services.js index 6e1a1945..e2132f1c 100644 --- a/src/services.js +++ b/src/services.js @@ -7,6 +7,7 @@ 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, location = {parse:parse, toString:toString}; + var lastHash; function parse(url){ if (isDefined(url)) { var match = URL_MATCH.exec(url); @@ -18,16 +19,25 @@ angularService("$location", function(browser){ location.path = match[6]; location.search = parseKeyValue(match[8]); location.hash = match[9]; - if (location.hash) location.hash = location.hash.substr(1); + if (location.hash) + location.hash = location.hash.substr(1); + lastHash = location.hash; location.hashPath = match[11] || ''; location.hashSearch = parseKeyValue(match[13]); } } } function toString() { - var hashKeyValue = toKeyValue(location.hashSearch), - hash = (location.hashPath ? location.hashPath : '') + (hashKeyValue ? '?' + hashKeyValue : ''); - return location.href.split('#')[0] + '#' + (hash ? hash : ''); + if (lastHash === location.hash) { + var hashKeyValue = toKeyValue(location.hashSearch), + hash = (location.hashPath ? location.hashPath : '') + (hashKeyValue ? '?' + hashKeyValue : ''), + url = location.href.split('#')[0] + '#' + (hash ? hash : ''); + if (url !== location.href) parse(url); + return url; + } else { + parse(location.href.split('#')[0] + '#' + location.hash); + return toString(); + } } browser.watchUrl(function(url){ parse(url); @@ -35,11 +45,7 @@ angularService("$location", function(browser){ }); parse(browser.getUrl()); this.$onEval(PRIORITY_LAST, function(){ - var href = toString(); - if (href != location.href) { - browser.setUrl(href); - location.href = href; - } + browser.setUrl(toString()); }); return location; }, {inject: ['$browser']}); @@ -69,6 +75,7 @@ angularService("$hover", function(browser) { tooltip.arrow.addClass('ng-arrow-right'); tooltip.arrow.css({left: (width + 1)+'px'}); tooltip.callout.css({ + position: 'fixed', left: (elementRect.left - arrowWidth - width - 4) + "px", top: (elementRect.top - 3) + "px", width: width + "px" @@ -76,6 +83,7 @@ angularService("$hover", function(browser) { } else { tooltip.arrow.addClass('ng-arrow-left'); tooltip.callout.css({ + position: 'fixed', left: (elementRect.right + arrowWidth) + "px", top: (elementRect.top - 3) + "px", width: width + "px" diff --git a/src/widgets.js b/src/widgets.js index f87c1d02..f5f02813 100644 --- a/src/widgets.js +++ b/src/widgets.js @@ -27,9 +27,11 @@ function valueAccessor(scope, element) { required = required || required === ''; if (!validator) throw "Validator named '" + validatorName + "' not found."; function validate(value) { - var error = required && !trim(value) ? - "Required" : - validator({state:scope, scope:{get:scope.$get, set:scope.$set}}, value); + var error, + validateScope = extend(new (extend(function(){}, {prototype:scope}))(), {$element:element}); + error = required && !trim(value) ? + "Required" : + validator({state:validateScope, scope:{get:validateScope.$get, set:validateScope.$set}}, value); if (error !== lastError) { elementError(element, NG_VALIDATION_ERROR, error); lastError = error; @@ -190,7 +192,8 @@ angularWidget('NG:INCLUDE', function(element){ angularWidget('NG:SWITCH', function ngSwitch(element){ var compiler = this, watchExpr = element.attr("on"), - whenFn = ngSwitch[element.attr("using") || 'equals']; + whenExpr = (element.attr("using") || 'equals').split(":"); + whenFn = ngSwitch[whenExpr.shift()]; changeExpr = element.attr('change') || '', cases = []; if (!whenFn) throw "Using expression '" + usingExpr + "' unknown."; @@ -199,7 +202,11 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ if (when) { cases.push({ when: function(scope, value){ - return whenFn.call(scope, value, when); + var args = [value, when]; + foreach(whenExpr, function(arg){ + args.push(arg); + }); + return whenFn.apply(scope, args); }, change: changeExpr, element: caseElement, @@ -212,13 +219,10 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ var scope = this, childScope; this.$watch(watchExpr, function(value){ element.html(''); - childScope = null; - var params = {}; + childScope = createScope(scope); foreach(cases, function(switchCase){ - if (switchCase.when(params, value)) { + if (switchCase.when(childScope, value)) { element.append(switchCase.element); - childScope = createScope(scope); - extend(childScope, params); childScope.$tryEval(switchCase.change, element); switchCase.template(switchCase.element, childScope); childScope.$init(); @@ -233,13 +237,15 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ equals: function(on, when) { return on == when; }, - route: function(on, when) { - var regex = '^' + when.replace(/[\.\\\(\)\^\$]/g, "\$1") + '$', params = [], self = this; + route: function(on, when, dstName) { + var regex = '^' + when.replace(/[\.\\\(\)\^\$]/g, "\$1") + '$', + params = [], + dst = {}; foreach(when.split(/\W/), function(param){ if (param) { var paramRegExp = new RegExp(":" + param + "([\\W])"); if (regex.match(paramRegExp)) { - regex = regex.replace(paramRegExp, "(.*)$1"); + regex = regex.replace(paramRegExp, "([^\/]*)$1"); params.push(param); } } @@ -247,8 +253,9 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ var match = on.match(new RegExp(regex)); if (match) { foreach(params, function(name, index){ - self[name] = match[index + 1]; + dst[name] = match[index + 1]; }); + if (dstName) this.$set(dstName, dst); } return match; } diff --git a/test/ApiTest.js b/test/ApiTest.js index 19860822..5d85987b 100644 --- a/test/ApiTest.js +++ b/test/ApiTest.js @@ -250,3 +250,7 @@ ApiTest.prototype.testStringFromUTC = function(){ assertEquals("2003-09-10T13:02:03Z", angular.Date.toString(date)); assertEquals("str", angular.String.toDate("str")); }; + +ApiTest.prototype.testObjectShouldHaveExtend = function(){ + assertEquals(angular.Object.extend, extend); +}; diff --git a/test/ValidatorsTest.js b/test/ValidatorsTest.js index 2b2f6753..17c67d38 100644 --- a/test/ValidatorsTest.js +++ b/test/ValidatorsTest.js @@ -12,7 +12,7 @@ ValidatorTest.prototype.testItShouldHaveThisSet = function() { scope.$init(); assertEquals('misko', validator.first); assertEquals('hevery', validator.last); - assertSame(scope, validator._this); + assertSame(scope, validator._this.__proto__); delete angular.validator.myValidator; scope.$element.remove(); }; diff --git a/test/markupSpec.js b/test/markupSpec.js index e416b8ea..a1112490 100644 --- a/test/markupSpec.js +++ b/test/markupSpec.js @@ -47,6 +47,11 @@ describe("markups", function(){ expect(element.html()).toEqual(''); }); + it('should process all bindings when we have leading space', function(){ + compile(' {{a}}
{{b}}
'); + expect(sortedHtml(scope.$element)).toEqual('

'); + }); + }); diff --git a/test/servicesSpec.js b/test/servicesSpec.js index b7dfe4c8..91cc1f0e 100644 --- a/test/servicesSpec.js +++ b/test/servicesSpec.js @@ -42,6 +42,20 @@ describe("services", function(){ expect(scope.$location.toString()).toEqual('file:///Users/Shared/misko/work/angular.js/scenario/widgets.html#'); }); + it('should update url on hash change', function(){ + scope.$location.parse('http://server/#path?a=b'); + scope.$location.hash = ''; + expect(scope.$location.toString()).toEqual('http://server/#'); + expect(scope.$location.hashPath).toEqual(''); + }); + + it('should update url on hashPath change', function(){ + scope.$location.parse('http://server/#path?a=b'); + scope.$location.hashPath = ''; + expect(scope.$location.toString()).toEqual('http://server/#?a=b'); + expect(scope.$location.hash).toEqual('?a=b'); + }); + xit('should add stylesheets', function(){ scope.$document = { getElementsByTagName: function(name){ diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index b48656f9..c6158c37 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -207,13 +207,18 @@ describe("input widget", function(){ describe('ng:switch', function(){ it("should match urls", function(){ - var scope = compile('
{{name}}
'); + var scope = compile('
{{params.name}}
'); scope.url = '/Book/Moby'; scope.$init(); -// jstestdriver.console.log('text'); expect(scope.$element.text()).toEqual('Moby'); }); + it("should match sandwich ids", function(){ + var scope = {}; + var match = angular.widget['NG:SWITCH'].route.call(scope, '/a/123/b', '/a/:id'); + expect(match).toBeFalsy(); + }); + it('should call init on switch', function(){ var scope = compile('
{{name}}
'); scope.url = 'a';