diff --git a/src/components/icon/icon.spec.js b/src/components/icon/icon.spec.js index 9a2934acf37..a1a16ba7786 100644 --- a/src/components/icon/icon.spec.js +++ b/src/components/icon/icon.spec.js @@ -478,6 +478,41 @@ describe('mdIcon service', function() { }); }); + describe('icon is cached', function() { + + it('should prevent duplicate ids', function() { + var firstId; + + $mdIcon('android.svg').then(function(el) { + // First child is in our case always the node with an id. + firstId = el.firstChild.id; + }); + + $scope.$digest(); + + $mdIcon('android.svg').then(function(el) { + expect(el.firstChild.id).not.toBe(firstId); + }); + + $scope.$digest(); + + }); + + it('should suffix duplicated ids', function() { + // Just request the icon to be stored in the cache. + $mdIcon('android.svg'); + + $scope.$digest(); + + $mdIcon('android.svg').then(function(el) { + expect(el.firstChild.id).toMatch(/.+_cache[0-9]+/g); + }); + + $scope.$digest(); + }); + + }); + describe('icon group is not found', function() { it('should log Error', function() { var msg; @@ -509,11 +544,6 @@ describe('mdIcon service', function() { function updateDefaults(svg) { svg = angular.element(svg)[0]; - svg.removeAttribute('id'); - angular.forEach(svg.querySelectorAll('[id]'), function(item) { - item.removeAttribute('id'); - }); - angular.forEach({ 'xmlns' : 'http://www.w3.org/2000/svg', 'fit' : '', diff --git a/src/components/icon/js/iconService.js b/src/components/icon/js/iconService.js index 35050ef11a5..f2f2fe91c52 100644 --- a/src/components/icon/js/iconService.js +++ b/src/components/icon/js/iconService.js @@ -316,9 +316,9 @@ }, - $get : ['$http', '$q', '$log', '$templateCache', function($http, $q, $log, $templateCache) { + $get : ['$http', '$q', '$log', '$templateCache', '$mdUtil', function($http, $q, $log, $templateCache, $mdUtil) { this.preloadIcons($templateCache); - return MdIconService(config, $http, $q, $log, $templateCache); + return MdIconService(config, $http, $q, $log, $templateCache, $mdUtil); }] }; @@ -373,7 +373,7 @@ */ /* @ngInject */ - function MdIconService(config, $http, $q, $log, $templateCache) { + function MdIconService(config, $http, $q, $log, $templateCache, $mdUtil) { var iconCache = {}; var urlRegex = /[-\w@:%\+.~#?&//=]{2,}\.[a-z]{2,4}\b(\/[-\w@:%\+.~#?&//=]*)?/i; var dataUrlRegex = /^data:image\/svg\+xml[\s*;\w\-\=]*?(base64)?,(.*)$/i; @@ -393,7 +393,7 @@ // If already loaded and cached, use a clone of the cached icon. // Otherwise either load by URL, or lookup in the registry and then load by URL, and cache. - if ( iconCache[id] ) return $q.when( iconCache[id].clone() ); + if ( iconCache[id] ) return $q.when( transformClone(iconCache[id]) ); if ( urlRegex.test(id) || dataUrlRegex.test(id) ) return loadByURL(id).then( cacheIcon(id) ); if ( id.indexOf(':') == -1 ) id = '$default:' + id; @@ -418,24 +418,28 @@ return result; } + function transformClone(cacheElement) { + var clone = cacheElement.clone(); + var cacheSuffix = '_cache' + $mdUtil.nextUid(); + + // We need to modify for each cached icon the id attributes. + // This is needed because SVG id's are treated as normal DOM ids + // and should not have a duplicated id. + if (clone.id) clone.id += cacheSuffix; + angular.forEach(clone.querySelectorAll('[id]'), function (item) { + item.id += cacheSuffix; + }); + + return clone; + } + /** * Prepare and cache the loaded icon for the specified `id` */ function cacheIcon( id ) { - return function updateCache( _icon ) { - var icon = isIcon(_icon) ? _icon : new Icon(_icon, config[id]); - - //clear id attributes to prevent aria issues - var elem = icon.element; - elem.removeAttribute('id'); - - angular.forEach(elem.querySelectorAll('[id]'), function(item) { - item.removeAttribute('id'); - }); - - iconCache[id] = icon; - + return function updateCache( icon ) { + iconCache[id] = isIcon(icon) ? icon : new Icon(icon, config[id]); return iconCache[id].clone(); };