From 04c26bb1e59c0f9329a7a9ab1157a08475bc4fb3 Mon Sep 17 00:00:00 2001 From: Christophe Krebser Date: Tue, 17 Jun 2014 14:49:53 +0200 Subject: [PATCH 1/5] fix(form input): input["email"] and ngRequired not working together ngRequired added to an email field wasn't working properly. ng-invalid-required stayed true unless a valid email was entered. correct behaviour is that it turns to ng-valid-required at first entered key. closes: #7849 --- src/ng/directive/input.js | 10 ++++------ test/ng/directive/inputSpec.js | 10 ++++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index fa6fe55d9f5e..a89a67dfdfd9 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -1128,22 +1128,20 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { textInputType(scope, element, attr, ctrl, $sniffer, $browser); var urlValidator = function(value) { - return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value); + return ctrl.$isEmpty(value) || URL_REGEXP.test(value); }; - ctrl.$formatters.push(urlValidator); - ctrl.$parsers.push(urlValidator); + ctrl.$validators.url = urlValidator; } function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { textInputType(scope, element, attr, ctrl, $sniffer, $browser); var emailValidator = function(value) { - return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value); + return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value); }; - ctrl.$formatters.push(emailValidator); - ctrl.$parsers.push(emailValidator); + ctrl.$validators.email = emailValidator; } function radioInputType(scope, element, attr, ctrl) { diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index e48a2a082672..444c91cf51b7 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -486,6 +486,16 @@ describe('ngModel', function() { expect(element).toHaveClass('ng-invalid-required'); })); + it('should remove required on bad input', inject(function($compile, $rootScope, $sniffer) { + var element = $compile('')($rootScope); + $rootScope.$digest(); + + element.val('bademail'); + browserTrigger(element, $sniffer.hasEvent('input') ? 'input' : 'change'); + expect(element).toBeInvalid(); + expect(element).toHaveClass('ng-valid-required'); + })); + it('should set the control touched state on "blur" event', inject(function($compile, $rootScope) { var element = $compile('
' + '' + From 11b9ce336dbada775528fea89509bf768970d9fb Mon Sep 17 00:00:00 2001 From: Neil Giarratana Date: Tue, 17 Jun 2014 17:40:48 -0400 Subject: [PATCH 2/5] docs(ngPluralize): spell Mary's name correctly Update ngPluralize.js Just a silly change to the name of one of the examples that appears to be a typo. Changing Marry to Mary as the first would be a verb and the latter would be an extremely common name. Closes #7884 --- src/ng/directive/ngPluralize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ng/directive/ngPluralize.js b/src/ng/directive/ngPluralize.js index 0585009a5128..e750265817dc 100644 --- a/src/ng/directive/ngPluralize.js +++ b/src/ng/directive/ngPluralize.js @@ -76,7 +76,7 @@ * When one person, perhaps John, views the document, "John is viewing" will be shown. * When three people view the document, no explicit number rule is found, so * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category. - * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing" + * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing" * is shown. * * Note that when you specify offsets, you must provide explicit number rules for From 92a27519f08683fb04cf63ba1ca3e68507b39b67 Mon Sep 17 00:00:00 2001 From: Alex Muntada Date: Fri, 20 Jun 2014 14:30:21 +0200 Subject: [PATCH 3/5] docs(tutorial/step-4): fix e2e test After a protractor update the test syntax had to be changed. Closes #7919 --- docs/content/tutorial/step_04.ngdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/tutorial/step_04.ngdoc b/docs/content/tutorial/step_04.ngdoc index 035fd6cc905c..a941dbd67af7 100644 --- a/docs/content/tutorial/step_04.ngdoc +++ b/docs/content/tutorial/step_04.ngdoc @@ -166,7 +166,7 @@ __`test/e2e/scenarios.js`:__ "MOTOROLA XOOM\u2122" ]); - element(by.model('orderProp')).findElement(by.css('option[value="name"]')).click(); + element(by.model('orderProp')).element(by.css('option[value="name"]')).click(); expect(getNames()).toEqual([ "MOTOROLA XOOM\u2122", From ff06ac9979d1e2d52126c8dc5f6cee9d8eebc635 Mon Sep 17 00:00:00 2001 From: Laurent Curau Date: Fri, 20 Jun 2014 12:17:36 +0100 Subject: [PATCH 4/5] docs(ngMessages): correct a typo Closes #7918 --- src/ngMessages/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ngMessages/messages.js b/src/ngMessages/messages.js index 57d2f719ec4c..ecf5bc21f435 100644 --- a/src/ngMessages/messages.js +++ b/src/ngMessages/messages.js @@ -164,7 +164,7 @@ angular.module('ngMessages', []) * * @description * `ngMessages` is a directive that is designed to show and hide messages based on the state - * of a key/value object that is listens on. The directive itself compliments error message + * of a key/value object that it listens on. The directive itself compliments error message * reporting with the `ngModel` $error object (which stores a key/value state of validation errors). * * `ngMessages` manages the state of internal messages within its container element. The internal From c2879734c269160172825daae4a0cd50b6d46e32 Mon Sep 17 00:00:00 2001 From: Yuri Sulyma Date: Tue, 12 Nov 2013 17:46:11 -0700 Subject: [PATCH 5/5] fix(Angular): nodeName should always be lowercase XHTML uses lowercase node names, while HTML often uses uppercase. The generally accepted convention is to always lowercase them. Fixes #3987 --- src/Angular.js | 16 +++++++++------- src/jqLite.js | 6 +++--- src/ng/anchorScroll.js | 2 +- src/ng/compile.js | 12 ++++++------ src/ng/location.js | 2 +- src/ngScenario/browserTrigger.js | 1 - src/ngScenario/dsl.js | 4 ++-- test/AngularSpec.js | 4 ++-- test/ng/compileSpec.js | 2 +- 9 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/Angular.js b/src/Angular.js index 905421e0fd03..711483a26fd3 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -615,12 +615,14 @@ function makeMap(str) { if (msie < 9) { nodeName_ = function(element) { element = element.nodeName ? element : element[0]; - return (element.scopeName && element.scopeName != 'HTML') - ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName; + return lowercase( + (element.scopeName && element.scopeName != 'HTML') + ? element.scopeName + ':' + element.nodeName : element.nodeName + ); }; } else { nodeName_ = function(element) { - return element.nodeName ? element.nodeName : element[0].nodeName; + return lowercase(element.nodeName ? element.nodeName : element[0].nodeName); }; } @@ -683,10 +685,10 @@ function arrayRemove(array, value) { function isLeafNode (node) { if (node) { - switch (node.nodeName) { - case "OPTION": - case "PRE": - case "TITLE": + switch (nodeName_(node)) { + case "option": + case "pre": + case "title": return true; } } diff --git a/src/jqLite.js b/src/jqLite.js index ea52b043edb0..3eee01f507d1 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -482,7 +482,7 @@ forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), }); var BOOLEAN_ELEMENTS = {}; forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { - BOOLEAN_ELEMENTS[uppercase(value)] = true; + BOOLEAN_ELEMENTS[value] = true; }); var ALIASED_ATTR = { 'ngMinlength' : 'minlength', @@ -495,7 +495,7 @@ function getBooleanAttrName(element, name) { var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; // booleanAttr is here twice to minimize DOM access - return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; + return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr; } function getAliasedAttrName(element, name) { @@ -605,7 +605,7 @@ forEach({ val: function(element, value) { if (isUndefined(value)) { - if (nodeName_(element) === 'SELECT' && element.multiple) { + if (element.multiple && nodeName_(element) === 'select') { var result = []; forEach(element.options, function (option) { if (option.selected) { diff --git a/src/ng/anchorScroll.js b/src/ng/anchorScroll.js index b5ef1810cf56..749d693dbd15 100644 --- a/src/ng/anchorScroll.js +++ b/src/ng/anchorScroll.js @@ -67,7 +67,7 @@ function $AnchorScrollProvider() { function getFirstAnchor(list) { var result = null; forEach(list, function(element) { - if (!result && lowercase(element.nodeName) === 'a') result = element; + if (!result && nodeName_(element) === 'a') result = element; }); return result; } diff --git a/src/ng/compile.js b/src/ng/compile.js index c919929aa4ec..5d3cfed694a6 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -759,8 +759,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { nodeName = nodeName_(this.$$element); // sanitize a[href] and img[src] values - if ((nodeName === 'A' && key === 'href') || - (nodeName === 'IMG' && key === 'src')) { + if ((nodeName === 'a' && key === 'href') || + (nodeName === 'img' && key === 'src')) { this[key] = value = $$sanitizeUri(value, key === 'src'); } @@ -1030,7 +1030,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { case 1: /* Element */ // use the node name: addDirective(directives, - directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective); + directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective); // iterate over the attributes for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, @@ -1897,8 +1897,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var tag = nodeName_(node); // maction[xlink:href] can source SVG. It's not limited to . if (attrNormalizedName == "xlinkHref" || - (tag == "FORM" && attrNormalizedName == "action") || - (tag != "IMG" && (attrNormalizedName == "src" || + (tag == "form" && attrNormalizedName == "action") || + (tag != "img" && (attrNormalizedName == "src" || attrNormalizedName == "ngSrc"))) { return $sce.RESOURCE_URL; } @@ -1912,7 +1912,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (!interpolateFn) return; - if (name === "multiple" && nodeName_(node) === "SELECT") { + if (name === "multiple" && nodeName_(node) === "select") { throw $compileMinErr("selmulti", "Binding to the 'multiple' attribute is not supported. Element: {0}", startingTag(node)); diff --git a/src/ng/location.js b/src/ng/location.js index 522154514ca1..6fa0a0bdfcf0 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -636,7 +636,7 @@ function $LocationProvider(){ var elm = jqLite(event.target); // traverse the DOM up to find first A tag - while (lowercase(elm[0].nodeName) !== 'a') { + while (nodeName_(elm[0]) !== 'a') { // ignore rewriting if no A tag (reached root element, or no parent - removed from document) if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; } diff --git a/src/ngScenario/browserTrigger.js b/src/ngScenario/browserTrigger.js index f74a04c15110..e3450a8fcf8f 100644 --- a/src/ngScenario/browserTrigger.js +++ b/src/ngScenario/browserTrigger.js @@ -34,7 +34,6 @@ var inputType = (element.type) ? element.type.toLowerCase() : null, nodeName = element.nodeName.toLowerCase(); - if (!eventType) { eventType = { 'text': 'change', diff --git a/src/ngScenario/dsl.js b/src/ngScenario/dsl.js index 50d20c9510c9..2c4a3b1336eb 100644 --- a/src/ngScenario/dsl.js +++ b/src/ngScenario/dsl.js @@ -377,7 +377,7 @@ angular.scenario.dsl('element', function() { var href = elements.attr('href'); var eventProcessDefault = elements.trigger('click')[0]; - if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) { + if (href && elements[0].nodeName.toLowerCase() === 'a' && eventProcessDefault) { this.application.navigateTo(href, function() { done(); }, done); @@ -394,7 +394,7 @@ angular.scenario.dsl('element', function() { var href = elements.attr('href'); var eventProcessDefault = elements.trigger('dblclick')[0]; - if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) { + if (href && elements[0].nodeName.toLowerCase() === 'a' && eventProcessDefault) { this.application.navigateTo(href, function() { done(); }, done); diff --git a/test/AngularSpec.js b/test/AngularSpec.js index 472ae6269913..5d75eb1dca41 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -951,7 +951,7 @@ describe('angular', function() { var div = jqLite('
' + '' + '
')[0]; - expect(nodeName_(div.childNodes[0])).toBe('NGTEST:FOO'); + expect(nodeName_(div.childNodes[0])).toBe('ngtest:foo'); expect(div.childNodes[0].getAttribute('ngtest:attr')).toBe('bar'); }); @@ -960,7 +960,7 @@ describe('angular', function() { var div = jqLite('
' + '' + '
')[0]; - expect(nodeName_(div.childNodes[0])).toBe('NGTEST:FOO'); + expect(nodeName_(div.childNodes[0])).toBe('ngtest:foo'); expect(div.childNodes[0].getAttribute('ngtest:attr')).toBe('bar'); }); } diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 415d9d403dbe..5de1728a262b 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -4625,7 +4625,7 @@ describe('$compile', function() { inject(function($compile, $rootScope) { var element = jqLite('
before
after
').contents(); expect(element.length).toEqual(3); - expect(nodeName_(element[1])).toBe('DIV'); + expect(nodeName_(element[1])).toBe('div'); $compile(element)($rootScope); expect(nodeName_(element[1])).toBe('#comment'); expect(nodeName_(comment)).toBe('#comment');