diff --git a/src/jqLite.js b/src/jqLite.js index 1d92f2ab0d55..fd9621eba9ae 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -369,6 +369,23 @@ function getBooleanAttrName(element, name) { return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; } +var IE_SPECIFIC_SET_ATTRIBUTE_CONTENT_PATTERN = /.*\S.*@.+/; +var IE_SPECIFIC_SET_ATTRIBUTE_VALUE_PATTERN = /^((ftp|https?):\/\/|mailto:|.*@.+)/; +function useIESpecificSetAttribute(element, name, value){ + //when setting href value of a link with a text that contains a word with + //the pattern above will replace the content with the href value + //if the href value is URL like (pattern above) + var linkElement; + + return msie <= 8 + && element.nodeName === "A" + && name === "href" + && IE_SPECIFIC_SET_ATTRIBUTE_VALUE_PATTERN.test(value) + && (linkElement = angular.element(element)) + && linkElement.children().length === 0 + && IE_SPECIFIC_SET_ATTRIBUTE_CONTENT_PATTERN.test(linkElement.text()); +} + forEach({ data: JQLiteData, inheritedData: JQLiteInheritedData, @@ -415,7 +432,9 @@ forEach({ }, attr: function(element, name, value){ - var lowercasedName = lowercase(name); + var textNodes, + lowercasedName = lowercase(name); + if (BOOLEAN_ATTR[lowercasedName]) { if (isDefined(value)) { if (!!value) { @@ -432,7 +451,21 @@ forEach({ : undefined; } } else if (isDefined(value)) { - element.setAttribute(name, value); + // IE7 and IE8 in some conditions replaces links text content with href + // value. To prevent this, content must be detached and attached after setting the new + // href value + if (useIESpecificSetAttribute(element, lowercasedName, value)) { + textNodes = []; + while(element.firstChild){ + textNodes.push(element.removeChild(element.firstChild)); + } + element.setAttribute(name, value); + while (textNodes.length) { + element.appendChild(textNodes.shift()); + } + } else { + element.setAttribute(name, value); + } } else if (element.getAttribute) { // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code // some elements (e.g. Document) don't have get attribute, so return undefined diff --git a/test/jqLiteSpec.js b/test/jqLiteSpec.js index b1fa6b058207..c56a05f796e3 100644 --- a/test/jqLiteSpec.js +++ b/test/jqLiteSpec.js @@ -406,6 +406,35 @@ describe('jqLite', function() { expect(elm.attr('readOnly')).toBeUndefined(); expect(elm.attr('disabled')).toBeUndefined(); }); + + it('should prevent IE for changing text content when setting attribute', function(){ + var unchanged = true, + values = ["http:/","http://","https://","ftp://","mailto:","mailto","@","a@b"], + contents = [ + "text with @arroba starting word", + "text with @ arroba starting word", + "text with @", + "text with (space at end) @ ", + "@ empty before", + " @ space before", + "- @ dash before", + "\n @ line feed before" + ]; + + function testValueWithTextContent(value, textContent){ + var link = angular.element("" + textContent + "") + linkText = link.text(); + link.attr("href", value); + unchanged = unchanged && (linkText == link.text()); + } + + forEach(values, function (value) { + forEach(contents, function (content) { + testValueWithTextContent(value, content); + }); + }); + expect(unchanged).toBe(true); + }); });