diff options
| author | Misko Hevery | 2011-02-03 16:35:51 -0800 |
|---|---|---|
| committer | Misko Hevery | 2011-02-04 13:44:22 -0800 |
| commit | 46d690ff0188836688811dda9af1b99c44750c48 (patch) | |
| tree | f2253031ba3c8574ec93f03a18bb9fb2667aef51 | |
| parent | 882f412d578e4f01394847fa5fde21b6b4096de2 (diff) | |
| download | angular.js-46d690ff0188836688811dda9af1b99c44750c48.tar.bz2 | |
smarter normalization of value on option, and htmlParser fixes
| -rw-r--r-- | src/Angular.js | 2 | ||||
| -rw-r--r-- | src/markups.js | 33 | ||||
| -rw-r--r-- | src/sanitizer.js | 35 | ||||
| -rw-r--r-- | test/markupSpec.js | 14 | ||||
| -rw-r--r-- | test/sanitizerSpec.js | 15 | ||||
| -rw-r--r-- | test/testabilityPatch.js | 4 | ||||
| -rw-r--r-- | test/widgetsSpec.js | 2 |
7 files changed, 63 insertions, 42 deletions
diff --git a/src/Angular.js b/src/Angular.js index de2de30d..aea1dd3f 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -719,7 +719,7 @@ function elementError(element, type, error) { element[0]['$NG_ERROR'] = error; if (error) { element.addClass(type); - element.attr(type, error); + element.attr(type, error.message || error); } else { element.removeClass(type); element.removeAttr(type); diff --git a/src/markups.js b/src/markups.js index 0aa7170e..89cc0d05 100644 --- a/src/markups.js +++ b/src/markups.js @@ -57,21 +57,26 @@ angularTextMarkup('{{}}', function(text, textNode, parentElement) { } }); -// TODO: this should be widget not a markup -angularTextMarkup('OPTION', function(text, textNode, parentElement){ - if (nodeName_(parentElement) == "OPTION") { - var select = jqLite('<select>'); - select.append(parentElement.clone()); - htmlParser(select.html(), { - start: function(tag, attrs) { - if (isUndefined(attrs.value)) { - parentElement.attr('value', text); +/** + * This tries to normalize the behavior of value attribute across browsers. If value attribute is + * not specified, then specify it to be that of the text. + */ +angularTextMarkup('option', function(text, textNode, parentElement){ + if (lowercase(nodeName_(parentElement)) == 'option') { + if (msie <= 7) { + // In IE7 The issue is that there is no way to see if the value was specified hence + // we have to resort to parsing HTML; + htmlParser(parentElement[0].outerHTML, { + start: function(tag, attrs) { + if (isUndefined(attrs.value)) { + parentElement.attr('value', text); + } } - }, - chars: noop, - end: noop, - comment: noop - }); + }); + } else if (parentElement[0].getAttribute('value') == null) { + // jQuery does normalization on 'value' so we have to bypass it. + parentElement.attr('value', text); + } } }); diff --git a/src/sanitizer.js b/src/sanitizer.js index 8f4b87a6..7bd26455 100644 --- a/src/sanitizer.js +++ b/src/sanitizer.js @@ -42,15 +42,12 @@ var closeSelfElements = makeMap("colgroup,dd,dt,li,p,td,tfoot,th,thead,tr"); var specialElements = makeMap("script,style"); var validElements = extend({}, emptyElements, blockElements, inlineElements, closeSelfElements); -//see: http://www.w3.org/TR/html4/index/attributes.html -//Attributes that have their values filled in disabled="disabled" -var fillAttrs = makeMap("compact,ismap,nohref,nowrap"); //Attributes that have href and hence need to be sanitized var uriAttrs = makeMap("background,href,longdesc,src,usemap"); -var validAttrs = extend({}, fillAttrs, uriAttrs, makeMap( +var validAttrs = extend({}, uriAttrs, makeMap( 'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ - 'color,cols,colspan,coords,dir,face,headers,height,hreflang,hspace,'+ - 'lang,language,rel,rev,rows,rowspan,rules,'+ + 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+ + 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+ 'scope,scrolling,shape,span,start,summary,target,title,type,'+ 'valign,value,vspace,width')); @@ -81,8 +78,7 @@ function htmlParser( html, handler ) { index = html.indexOf("-->"); if ( index >= 0 ) { - if ( handler.comment ) - handler.comment( html.substring( 4, index ) ); + if (handler.comment) handler.comment( html.substring( 4, index ) ); html = html.substring( index + 3 ); chars = false; } @@ -114,7 +110,7 @@ function htmlParser( html, handler ) { var text = index < 0 ? html : html.substring( 0, index ); html = index < 0 ? "" : html.substring( index ); - handler.chars( decodeEntities(text) ); + if (handler.chars) handler.chars( decodeEntities(text) ); } } else { @@ -123,7 +119,7 @@ function htmlParser( html, handler ) { replace(COMMENT_REGEXP, "$1"). replace(CDATA_REGEXP, "$1"); - handler.chars( decodeEntities(text) ); + if (handler.chars) handler.chars( decodeEntities(text) ); return ""; }); @@ -159,16 +155,15 @@ function htmlParser( html, handler ) { var attrs = {}; - rest.replace(ATTR_REGEXP, function(match, name) { - var value = arguments[2] ? arguments[2] : - arguments[3] ? arguments[3] : - arguments[4] ? arguments[4] : - fillAttrs[name] ? name : ""; + rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) { + var value = doubleQuotedValue + || singleQoutedValue + || unqoutedValue + || ''; - attrs[name] = decodeEntities(value); //value.replace(/(^|[^\\])"/g, '$1\\\"') //" + attrs[name] = decodeEntities(value); }); - - handler.start( tagName, attrs, unary ); + if (handler.start) handler.start( tagName, attrs, unary ); } function parseEndTag( tag, tagName ) { @@ -183,7 +178,7 @@ function htmlParser( html, handler ) { if ( pos >= 0 ) { // Close all the open elements, up the stack for ( i = stack.length - 1; i >= pos; i-- ) - handler.end( stack[ i ] ); + if (handler.end) handler.end( stack[ i ] ); // Remove the open elements from the stack stack.length = pos; @@ -210,7 +205,7 @@ function makeMap(str){ var hiddenPre=document.createElement("pre"); function decodeEntities(value) { hiddenPre.innerHTML=value.replace(/</g,"<"); - return hiddenPre.innerText || hiddenPre.textContent; + return hiddenPre.innerText || hiddenPre.textContent || ''; } /** diff --git a/test/markupSpec.js b/test/markupSpec.js index f629378d..16efba55 100644 --- a/test/markupSpec.js +++ b/test/markupSpec.js @@ -46,10 +46,18 @@ describe("markups", function(){ this.addMatchers({ toHaveValue: function(expected){ this.message = function(){ - return 'Expected "' + sortedHtml(this.actual) + '" to have value="' + expected + '".'; + return 'Expected "' + this.actual.html() + '" to have value="' + expected + '".'; }; - return this.actual.html().indexOf('value="' + expected + '"') != -1; + var value; + htmlParser(this.actual.html(), { + start:function(tag, attrs){ + value = attrs.value; + }, + end:noop, + chars:noop + }); + return trim(value) == trim(expected); } }); }); @@ -70,6 +78,8 @@ describe("markups", function(){ }); it('should set value even if self closing HTML', function(){ + // IE removes the \n from option, which makes this test pointless + if (msie) return; compile('<select name="x"><option>\n</option></select>'); expect(element).toHaveValue('\n'); }); diff --git a/test/sanitizerSpec.js b/test/sanitizerSpec.js index 7158fbee..787ce84c 100644 --- a/test/sanitizerSpec.js +++ b/test/sanitizerSpec.js @@ -14,6 +14,11 @@ describe('HTML', function(){ attrs: attrs, unary: unary }; + // Since different browsers handle newlines differenttly we trim + // so that it is easier to write tests. + forEach(attrs, function(value, key){ + attrs[key] = trim(value); + }); }, chars: function(text_){ text = text_; @@ -38,16 +43,22 @@ describe('HTML', function(){ it('should parse newlines in attributes', function(){ htmlParser('<tag attr="\nvalue\n">text</tag>', handler); - expect(start).toEqual({tag:'tag', attrs:{attr:'\nvalue\n'}, unary:false}); + expect(start).toEqual({tag:'tag', attrs:{attr:'value'}, unary:false}); expect(text).toEqual('text'); }); it('should parse namespace', function(){ htmlParser('<ns:t-a-g ns:a-t-t-r="\nvalue\n">text</ns:t-a-g>', handler); - expect(start).toEqual({tag:'ns:t-a-g', attrs:{'ns:a-t-t-r':'\nvalue\n'}, unary:false}); + expect(start).toEqual({tag:'ns:t-a-g', attrs:{'ns:a-t-t-r':'value'}, unary:false}); expect(text).toEqual('text'); }); + it('should parse empty value attribute of node', function(){ + htmlParser('<OPTION selected value="">abc</OPTION>', handler); + expect(start).toEqual({tag:'option', attrs:{selected:'', value:''}, unary:false}); + expect(text).toEqual('abc'); + }); + }); it('should echo html', function(){ diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index 76033ab2..e0086b8a 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -70,7 +70,7 @@ beforeEach(function(){ expected = toJson(this.actual); } return "Expected " + expected + " to be an Error with message " + toJson(message); - } + }; return this.actual.name == 'Error' && this.actual.message == message; }, @@ -83,7 +83,7 @@ beforeEach(function(){ expected = toJson(this.actual); } return "Expected " + expected + " to match an Error with message " + toJson(messageRegexp); - } + }; return this.actual.name == 'Error' && messageRegexp.test(this.actual.message); } }); diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index ee339e89..6bff5a36 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -475,7 +475,7 @@ describe("widget", function(){ scope.$eval(); expect(scope.$element.text()).toBe('foobarC'); - }) + }); }); |
