Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

Commit

Permalink
fix(icon): Allow using data URLs
Browse files Browse the repository at this point in the history
Refs #6531. Fixes #4126. Closes #7547
  • Loading branch information
programmist authored and ThomasBurleson committed Apr 1, 2016
1 parent acb5a61 commit 63134ae
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 13 deletions.
22 changes: 22 additions & 0 deletions src/components/icon/demoLoadSvgIconsFromUrl/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,27 @@
<md-icon md-svg-src="{{ getAndroid() }}" class="s36" aria-label="Android "></md-icon>
<md-icon md-svg-src="img/icons/addShoppingCart.svg" class="s48" aria-label="Cart" ></md-icon>
</p>

<p>Use data URLs (base64 or un-encoded):</p>
<p>
<md-icon
md-svg-src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGcgaWQ9ImNha2UiPjxwYXRoIGQ9Ik0xMiA2YzEuMTEgMCAyLS45IDItMiAwLS4zOC0uMS0uNzMtLjI5LTEuMDNMMTIgMGwtMS43MSAyLjk3Yy0uMTkuMy0uMjkuNjUtLjI5IDEuMDMgMCAxLjEuOSAyIDIgMnptNC42IDkuOTlsLTEuMDctMS4wNy0xLjA4IDEuMDdjLTEuMyAxLjMtMy41OCAxLjMxLTQuODkgMGwtMS4wNy0xLjA3LTEuMDkgMS4wN0M2Ljc1IDE2LjY0IDUuODggMTcgNC45NiAxN2MtLjczIDAtMS40LS4yMy0xLjk2LS42MVYyMWMwIC41NS40NSAxIDEgMWgxNmMuNTUgMCAxLS40NSAxLTF2LTQuNjFjLS41Ni4zOC0xLjIzLjYxLTEuOTYuNjEtLjkyIDAtMS43OS0uMzYtMi40NC0xLjAxek0xOCA5aC01VjdoLTJ2Mkg2Yy0xLjY2IDAtMyAxLjM0LTMgM3YxLjU0YzAgMS4wOC44OCAxLjk2IDEuOTYgMS45Ni41MiAwIDEuMDItLjIgMS4zOC0uNTdsMi4xNC0yLjEzIDIuMTMgMi4xM2MuNzQuNzQgMi4wMy43NCAyLjc3IDBsMi4xNC0yLjEzIDIuMTMgMi4xM2MuMzcuMzcuODYuNTcgMS4zOC41NyAxLjA4IDAgMS45Ni0uODggMS45Ni0xLjk2VjEyQzIxIDEwLjM0IDE5LjY2IDkgMTggOXoiLz48L2c+PC9zdmc+"
class="s24"
aria-label="Cake">
</md-icon>

<md-icon
md-svg-src="data:image/svg+xml;base64,{{ getAndroidEncoded() }}"
class="s36"
aria-label="Android">
</md-icon>

<!-- un-encoded -->
<md-icon
md-svg-src='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g id="add-shopping-cart"><path d="M11 9h2V6h3V4h-3V1h-2v3H8v2h3v3zm-4 9c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zm10 0c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2zm-9.83-3.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.86-7.01L19.42 4h-.01l-1.1 2-2.76 5H8.53l-.13-.27L6.16 6l-.95-2-.94-2H1v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.13 0-.25-.11-.25-.25z"/></g></svg>'
class="s48"
aria-label="Cart">
</md-icon>
</p>
</div>

4 changes: 4 additions & 0 deletions src/components/icon/demoLoadSvgIconsFromUrl/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ angular.module('appDemoSvgIcons', ['ngMaterial'])
$scope.getAndroid = function() {
return 'img/icons/android.svg';
}
/* Returns base64 encoded SVG. */
$scope.getAndroidEncoded = function() {
return 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGcgaWQ9ImFuZHJvaWQiPjxwYXRoIGQ9Ik02IDE4YzAgLjU1LjQ1IDEgMSAxaDF2My41YzAgLjgzLjY3IDEuNSAxLjUgMS41czEuNS0uNjcgMS41LTEuNVYxOWgydjMuNWMwIC44My42NyAxLjUgMS41IDEuNXMxLjUtLjY3IDEuNS0xLjVWMTloMWMuNTUgMCAxLS40NSAxLTFWOEg2djEwek0zLjUgOEMyLjY3IDggMiA4LjY3IDIgOS41djdjMCAuODMuNjcgMS41IDEuNSAxLjVTNSAxNy4zMyA1IDE2LjV2LTdDNSA4LjY3IDQuMzMgOCAzLjUgOHptMTcgMGMtLjgzIDAtMS41LjY3LTEuNSAxLjV2N2MwIC44My42NyAxLjUgMS41IDEuNXMxLjUtLjY3IDEuNS0xLjV2LTdjMC0uODMtLjY3LTEuNS0xLjUtMS41em0tNC45Ny01Ljg0bDEuMy0xLjNjLjItLjIuMi0uNTEgMC0uNzEtLjItLjItLjUxLS4yLS43MSAwbC0xLjQ4IDEuNDhDMTMuODUgMS4yMyAxMi45NSAxIDEyIDFjLS45NiAwLTEuODYuMjMtMi42Ni42M0w3Ljg1LjE1Yy0uMi0uMi0uNTEtLjItLjcxIDAtLjIuMi0uMi41MSAwIC43MWwxLjMxIDEuMzFDNi45NyAzLjI2IDYgNS4wMSA2IDdoMTJjMC0xLjk5LS45Ny0zLjc1LTIuNDctNC44NHpNMTAgNUg5VjRoMXYxem01IDBoLTFWNGgxdjF6Ii8+PC9nPjwvc3ZnPg==';
}
});
53 changes: 48 additions & 5 deletions src/components/icon/icon.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,20 @@ describe('mdIcon directive', function() {
return {
then: function(fn) {
switch(id) {
case 'android' : fn('<svg><g id="android"></g></svg>');
case 'cake' : fn('<svg><g id="cake"></g></svg>');
case 'android.svg' : fn('<svg><g id="android"></g></svg>');
case 'cake.svg' : fn('<svg><g id="cake"></g></svg>');
case 'image:android': fn('');
case 'android' : fn('<svg><g id="android"></g></svg>');
break;
case 'cake' : fn('<svg><g id="cake"></g></svg>');
break;
case 'android.svg' : fn('<svg><g id="android"></g></svg>');
break;
case 'cake.svg' : fn('<svg><g id="cake"></g></svg>');
break;
case 'image:android' : fn('');
break;
default :
if (/^data:/.test(id)) {
fn(window.atob(id.split(',')[1]));
}
}
}
}
Expand Down Expand Up @@ -240,6 +249,17 @@ describe('mdIcon directive', function() {
expect(el.html()).toEqual('');
}));

describe('with a data URL', function() {
it('should set mdSvgSrc from a function expression', inject(function() {
var svgData = '<svg><g><circle r="50" cx="100" cy="100"></circle></g></svg>';
$scope.getData = function() {
return 'data:image/svg+xml;base64,' + window.btoa(svgData);
}
el = make('<md-icon md-svg-src="{{ getData() }}"></md-icon>');
$scope.$digest();
expect(el[0].innerHTML).toEqual(svgData);
}));
})
});

describe('with ARIA support', function() {
Expand Down Expand Up @@ -419,6 +439,29 @@ describe('mdIcon service', function() {
$scope.$digest();
});

describe('and the URL is a data URL', function() {
var svgData = '<svg><g><circle r="50" cx="100" cy="100"></circle></g></svg>';

describe('and the data is base64 encoded', function() {
it('should return correct SVG markup', function() {
var data = 'data:image/svg+xml;base64,' + btoa(svgData);
$mdIcon(data).then(function(el) {
expect(el.outerHTML).toEqual( updateDefaults(svgData) );
})
$scope.$digest();
});
});

describe('and the data is un-encoded', function() {
it('should return correct SVG markup', function() {
var data = 'data:image/svg+xml,' + svgData;
$mdIcon(data).then(function(el) {
expect(el.outerHTML).toEqual( updateDefaults(svgData) );
})
$scope.$digest();
});
});
});
});

describe('icon set URL is not found', function() {
Expand Down
32 changes: 24 additions & 8 deletions src/components/icon/js/iconService.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@
/* @ngInject */
function MdIconService(config, $http, $q, $log, $templateCache) {
var iconCache = {};
var urlRegex = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/i;
var urlRegex = /[-\w@:%\+.~#?&//=]{2,}\.[a-z]{2,4}\b(\/[-\w@:%\+.~#?&//=]*)?/i;
var dataUrlRegex = /^data:image\/svg\+xml[\s*;\w\-\=]*?(base64)?,(.*)$/i;

Icon.prototype = { clone : cloneSVG, prepare: prepareAndStyle };
getIcon.fontSet = findRegisteredFontSet;
Expand All @@ -392,8 +393,8 @@
// 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 ( urlRegex.test(id) ) return loadByURL(id).then( cacheIcon(id) );
if ( iconCache[id] ) return $q.when( iconCache[id].clone() );
if ( urlRegex.test(id) || dataUrlRegex.test(id) ) return loadByURL(id).then( cacheIcon(id) );
if ( id.indexOf(':') == -1 ) id = '$default:' + id;

var load = config[id] ? loadByID : loadFromIconSet;
Expand Down Expand Up @@ -481,11 +482,26 @@
* Extract the data for later conversion to Icon
*/
function loadByURL(url) {
return $http
.get(url, { cache: $templateCache })
.then(function(response) {
return angular.element('<div>').append(response.data).find('svg')[0];
}).catch(announceNotFound);
/* Load the icon from embedded data URL. */
function loadByDataUrl(url) {
var results = dataUrlRegex.exec(url);
var isBase64 = /base64/i.test(url);
var data = isBase64 ? window.atob(results[2]) : results[2];
return $q.when(angular.element(data)[0]);
}

/* Load the icon by URL using HTTP. */
function loadByHttpUrl(url) {
return $http
.get(url, { cache: $templateCache })
.then(function(response) {
return angular.element('<div>').append(response.data).find('svg')[0];
}).catch(announceNotFound);
}

return dataUrlRegex.test(url)
? loadByDataUrl(url)
: loadByHttpUrl(url);
}

/**
Expand Down

0 comments on commit 63134ae

Please sign in to comment.