Comparing version 1.0.1 to 1.1.0
69
index.js
@@ -5,5 +5,12 @@ /* eslint-env browser */ | ||
var regexDataSvg64 = /^(data:image\/svg\+xml)(;\w+)?(,)(.*)$/; | ||
var regexUrlDataSvg64 = /^(url\("data:image\/svg\+xml)(;\w+)?(,)(.*)("\))$/; | ||
var ENCODING_MATCH_INDEX = 2, DATA_MATCH_INDEX = 4; | ||
var PATTERN = '((data:image/svg\\+xml)(;\\w+)?(,)(.*)|(.*\\.svg))'; | ||
var regexDataSvg64 = new RegExp('^()' + PATTERN + '()$'); | ||
var regexUrlDataSvg64 = new RegExp('^(url\\(")' + PATTERN + '("\\))$'); | ||
var WHOLE_MATCH_INDEX = 0, | ||
URL_CONSTRUCT_INDEX = 1, | ||
URI_MATCH_INDEX = 2, | ||
SCHEME_INDEX = 3, | ||
ENCODING_MATCH_INDEX = 4, | ||
DATA_MATCH_INDEX = 6, | ||
SVG_FILE_URL_INDEX = 7; | ||
var propsUsingImageUrl = ['background-image', 'list-style-image', 'border-image', 'content']; | ||
@@ -29,3 +36,5 @@ | ||
if (match) { | ||
this.src = applyThemeToSvg64.call(this, match, theme, setSvgProps); | ||
applyThemeToSvgData.call(this, match, theme, setSvgProps, function(imageData) { | ||
this.src = imageData; | ||
}); | ||
} | ||
@@ -52,7 +61,16 @@ } | ||
} else { | ||
propName = propsUsingImageUrl.find(matchProp); | ||
// propName = propsUsingImageUrl.find(matchProp); | ||
// avoiding .find() for IE11's sake | ||
for (var i = 0; i < propsUsingImageUrl.length; ++i) { | ||
propName = propsUsingImageUrl[i]; | ||
if (matchProp(propName)) { | ||
break; | ||
} | ||
} | ||
} | ||
if (match) { | ||
ruleProps[propName] = applyThemeToSvg64.call(this, match, theme, setSvgProps); | ||
applyThemeToSvgData.call(this, match, theme, setSvgProps, function(urlImageData) { | ||
ruleProps[propName] = urlImageData; | ||
}); | ||
} | ||
@@ -63,3 +81,32 @@ | ||
function applyThemeToSvg64(match, theme, setSvgProps) { | ||
function applyThemeToSvgData(match, theme, setSvgProps, callback) { | ||
var self = this; | ||
if (match[SVG_FILE_URL_INDEX]) { | ||
get(match[SVG_FILE_URL_INDEX], function(svgMarkup) { | ||
match[SCHEME_INDEX] = 'data:image/svg+xml,'; | ||
match[DATA_MATCH_INDEX] = svgMarkup; | ||
match[SVG_FILE_URL_INDEX] = ''; | ||
applyNow.call(self, match, theme, setSvgProps, callback); | ||
}); | ||
} else { | ||
applyNow.call(this, match, theme, setSvgProps, callback); | ||
} | ||
} | ||
var HTTP_STATE_DONE = 4, HTTP_STATUS_OK = 200; | ||
function get(url, callback) { | ||
var httpRequest = new XMLHttpRequest(); | ||
httpRequest.open('GET', url, true); | ||
httpRequest.onreadystatechange = function() { | ||
if ( | ||
httpRequest.readyState === HTTP_STATE_DONE && | ||
httpRequest.status === HTTP_STATUS_OK | ||
) { | ||
callback(httpRequest.responseText); | ||
} | ||
}; | ||
httpRequest.send(null); | ||
} | ||
function applyNow(match, theme, setSvgProps, callback) { | ||
var encoding = match[ENCODING_MATCH_INDEX]; | ||
@@ -73,5 +120,6 @@ var data = match[DATA_MATCH_INDEX]; | ||
case undefined: | ||
div.innerHTML = data; | ||
div.innerHTML = data.indexOf('%3C') >= 0 ? decodeURIComponent(data) : data; | ||
setSvgProps.call(div.firstElementChild, theme); | ||
match[DATA_MATCH_INDEX] = div.firstElementChild.outerHTML; | ||
var svgMarkup = (new XMLSerializer()).serializeToString(div.firstElementChild); | ||
match[DATA_MATCH_INDEX] = encodeURIComponent(svgMarkup); // always encode result for IE11's sake | ||
break; | ||
@@ -88,3 +136,4 @@ case ';base64': | ||
return match.slice(1).join(''); | ||
match[WHOLE_MATCH_INDEX] = match[URI_MATCH_INDEX] = undefined; // omit from join | ||
callback.call(this, match.join('')); | ||
} | ||
@@ -91,0 +140,0 @@ |
{ | ||
"name": "svg-themer", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "Apply a theme to SVG elements as well as image data and css rules constructed from SVG markup", | ||
@@ -8,4 +8,5 @@ "main": "index.js", | ||
"lint": "eslint -c ../fin-hypergrid/.eslintrc index.js", | ||
"build": "mkdir umd; cp index.js umd/svg-themer.js; uglifyjs index.js > umd/svg-themer.min.js", | ||
"test": "open test.html" | ||
"build": "mkdir umd 2>/dev/null; cp index.js umd/svg-themer.js; uglifyjs index.js > umd/svg-themer.min.js; ls -lah umd/*.*", | ||
"test": "http-server -p 49867 -o index.html # -o is not working for me", | ||
"prepublishOnly": "npm run build" | ||
}, | ||
@@ -12,0 +13,0 @@ "repository": { |
@@ -15,2 +15,4 @@ # svg-themer | ||
> **TL;DR** Skip to [API](#api) with examples. | ||
SVG data is displayed on a web page by the following DOM `Element`s: | ||
@@ -23,9 +25,18 @@ * `SVGSVGElement` — Represented in XML as `<svg>...</svg>` (with descendant elements) | ||
The situation is quite different, however, for `<img/>` elements with underlying SVG data and for any element referencing SVG data via a CSS `url()` function. In these cases applying styles has _no effect_ on the image because it has already been rasterized. | ||
The situation is quite different, however, for `<img/>` elements with underlying SVG data and for any element referencing SVG data via a CSS `url()` function. In these cases applying styles has _no effect_ on the image because it has already been rasterized. **With this module, you can apply a set of thematic styles directly to such SVG data.** | ||
**With this module, you can apply a set of thematic styles directly to such SVG data.** | ||
## Data encoding | ||
This module properly handles all flavors of SVG data: | ||
Entity | SVG Data Source | Example | ||
------ | --------------- | ------- | ||
`HTMLImageElement` object | GET request | `<img src="yourfile.svg">` | ||
`HTMLImageElement` object | in-line, raw (unencoded) | `<img src="data:image/svg+xml,...">` | ||
`HTMLImageElement` object | in-line, base64-encoded | `<img src="data:image/svg+xml;base64,...">` | ||
`CSSStyleRule` property | GET request | `background-image: url(yourfile.svg)` | ||
`CSSStyleRule` property | in-line, URI-encoded | `background-image: url(data:image/svg+xml,...)` | ||
`CSSStyleRule` property | in-line, base64-encoded | `background-image: url(data:image/svg+xml;base64,...)` | ||
> **IMPORTANT NOTE:** For this to work, the SVG data must be "baked-in" to the object using `data:image/svg+xml`; this version does not handle file URLs. | ||
> **Note:** Data in CSS `url()` constructs must always be encoded. (FYI, base64 encoding produces shorter results.) | ||
> **Data encoding:** This module properly handles both unencoded data (`data:image/svg+xml,` + raw SVG markup) as well as base64-encoded data (`data:image/svg+xml;base64,` + encoded string). If you experience any issues trying to use unencoded SVG markup directly (such as nested quotes), you can always encode it. | ||
> **Note:** Some browsers (looking at you, IE11) require `HTMLImageElement#src` to be encoded as well, depending on the content. For this reason, formerly unencoded data is always URI-encoded on write-back to `src` after styling as the content is now indeterminate. | ||
@@ -58,4 +69,4 @@ ## `theme` objects | ||
```html | ||
<script src="https://unpkg.com/svg-themer@1.0/umd/svg-themer.js"></script> | ||
<script src="https://unpkg.com/svg-themer@1.0/umd/svg-themer.min.js"></script> | ||
<script src="https://unpkg.com/svg-themer/umd/svg-themer.js"></script> | ||
<script src="https://unpkg.com/svg-themer/umd/svg-themer.min.js"></script> | ||
``` | ||
@@ -143,3 +154,3 @@ | ||
```js | ||
var rule = document.querySelector('style').sheet.rules[0]; // first rule in first stylesheet | ||
var rule = document.querySelector('style').sheet.cssRules[0]; // first rule in first stylesheet | ||
svgThemer.setRuleSvgProps.call(rule, theme.stopSignage); | ||
@@ -171,3 +182,3 @@ ``` | ||
var imgEl = document.querySelector('img'); | ||
var rule = document.querySelector('style').sheet.rules[0]; // first rule in first stylesheet | ||
var rule = document.querySelector('style').sheet.cssRules[0]; // first rule in first stylesheet | ||
@@ -196,1 +207,17 @@ svgThemer.mixin(svgEl, propSetter); | ||
## UAT | ||
Tested in the following browsers: | ||
* macOS 10.13.6 (17G65) | ||
* Chrome 70.0.3538.77 | ||
* Safari 12.0 (13606.2.11) | ||
* Opera 56.0.3051.102 | ||
* Firefox 63.0.1 | ||
* Windows 10 | ||
* Chrome 70.0.3538.77 | ||
* Opera 56.0.3051.70 | ||
* Firefox 63.0.0.6865 | ||
* Edge 42.17134.1.0 | ||
* Internet Explorer 11.345.17134.0 (Update 11.0.90) | ||
## Version History | ||
See [releases](https://github.com/joneit/svg-themer/releases). |
@@ -5,5 +5,12 @@ /* eslint-env browser */ | ||
var regexDataSvg64 = /^(data:image\/svg\+xml)(;\w+)?(,)(.*)$/; | ||
var regexUrlDataSvg64 = /^(url\("data:image\/svg\+xml)(;\w+)?(,)(.*)("\))$/; | ||
var ENCODING_MATCH_INDEX = 2, DATA_MATCH_INDEX = 4; | ||
var PATTERN = '((data:image/svg\\+xml)(;\\w+)?(,)(.*)|(.*\\.svg))'; | ||
var regexDataSvg64 = new RegExp('^()' + PATTERN + '()$'); | ||
var regexUrlDataSvg64 = new RegExp('^(url\\(")' + PATTERN + '("\\))$'); | ||
var WHOLE_MATCH_INDEX = 0, | ||
URL_CONSTRUCT_INDEX = 1, | ||
URI_MATCH_INDEX = 2, | ||
SCHEME_INDEX = 3, | ||
ENCODING_MATCH_INDEX = 4, | ||
DATA_MATCH_INDEX = 6, | ||
SVG_FILE_URL_INDEX = 7; | ||
var propsUsingImageUrl = ['background-image', 'list-style-image', 'border-image', 'content']; | ||
@@ -29,3 +36,5 @@ | ||
if (match) { | ||
this.src = applyThemeToSvg64.call(this, match, theme, setSvgProps); | ||
applyThemeToSvgData.call(this, match, theme, setSvgProps, function(imageData) { | ||
this.src = imageData; | ||
}); | ||
} | ||
@@ -52,7 +61,16 @@ } | ||
} else { | ||
propName = propsUsingImageUrl.find(matchProp); | ||
// propName = propsUsingImageUrl.find(matchProp); | ||
// avoiding .find() for IE11's sake | ||
for (var i = 0; i < propsUsingImageUrl.length; ++i) { | ||
propName = propsUsingImageUrl[i]; | ||
if (matchProp(propName)) { | ||
break; | ||
} | ||
} | ||
} | ||
if (match) { | ||
ruleProps[propName] = applyThemeToSvg64.call(this, match, theme, setSvgProps); | ||
applyThemeToSvgData.call(this, match, theme, setSvgProps, function(urlImageData) { | ||
ruleProps[propName] = urlImageData; | ||
}); | ||
} | ||
@@ -63,3 +81,32 @@ | ||
function applyThemeToSvg64(match, theme, setSvgProps) { | ||
function applyThemeToSvgData(match, theme, setSvgProps, callback) { | ||
var self = this; | ||
if (match[SVG_FILE_URL_INDEX]) { | ||
get(match[SVG_FILE_URL_INDEX], function(svgMarkup) { | ||
match[SCHEME_INDEX] = 'data:image/svg+xml,'; | ||
match[DATA_MATCH_INDEX] = svgMarkup; | ||
match[SVG_FILE_URL_INDEX] = ''; | ||
applyNow.call(self, match, theme, setSvgProps, callback); | ||
}); | ||
} else { | ||
applyNow.call(this, match, theme, setSvgProps, callback); | ||
} | ||
} | ||
var HTTP_STATE_DONE = 4, HTTP_STATUS_OK = 200; | ||
function get(url, callback) { | ||
var httpRequest = new XMLHttpRequest(); | ||
httpRequest.open('GET', url, true); | ||
httpRequest.onreadystatechange = function() { | ||
if ( | ||
httpRequest.readyState === HTTP_STATE_DONE && | ||
httpRequest.status === HTTP_STATUS_OK | ||
) { | ||
callback(httpRequest.responseText); | ||
} | ||
}; | ||
httpRequest.send(null); | ||
} | ||
function applyNow(match, theme, setSvgProps, callback) { | ||
var encoding = match[ENCODING_MATCH_INDEX]; | ||
@@ -73,5 +120,6 @@ var data = match[DATA_MATCH_INDEX]; | ||
case undefined: | ||
div.innerHTML = data; | ||
div.innerHTML = data.indexOf('%3C') >= 0 ? decodeURIComponent(data) : data; | ||
setSvgProps.call(div.firstElementChild, theme); | ||
match[DATA_MATCH_INDEX] = div.firstElementChild.outerHTML; | ||
var svgMarkup = (new XMLSerializer()).serializeToString(div.firstElementChild); | ||
match[DATA_MATCH_INDEX] = encodeURIComponent(svgMarkup); // always encode result for IE11's sake | ||
break; | ||
@@ -88,3 +136,4 @@ case ';base64': | ||
return match.slice(1).join(''); | ||
match[WHOLE_MATCH_INDEX] = match[URI_MATCH_INDEX] = undefined; // omit from join | ||
callback.call(this, match.join('')); | ||
} | ||
@@ -91,0 +140,0 @@ |
@@ -1,1 +0,1 @@ | ||
"use strict";var regexDataSvg64=/^(data:image\/svg\+xml)(;\w+)?(,)(.*)$/;var regexUrlDataSvg64=/^(url\("data:image\/svg\+xml)(;\w+)?(,)(.*)("\))$/;var ENCODING_MATCH_INDEX=2,DATA_MATCH_INDEX=4;var propsUsingImageUrl=["background-image","list-style-image","border-image","content"];var api;if(typeof exports==="undefined"){window.svgThemer=api={}}else{api=exports}api.setSvgProps=function(theme){if(theme.color){this.style.stroke=this.style.color=theme.color}if(theme.backgroundColor){this.style.fill=theme.backgroundColor}};api.setImgSvgProps=function(theme,setSvgProps){if(this.tagName==="IMG"){var match=this.src.match(regexDataSvg64);if(match){this.src=applyThemeToSvg64.call(this,match,theme,setSvgProps)}}return this};api.setRuleSvgProps=function(theme,setSvgProps,propName){var match;var ruleProps=this.style;if(typeof setSvgProps==="string"){propName=setSvgProps;setSvgProps=undefined}function matchProp(propName){return match=ruleProps[propName].match(regexUrlDataSvg64)}if(propName){matchProp(propName)}else{propName=propsUsingImageUrl.find(matchProp)}if(match){ruleProps[propName]=applyThemeToSvg64.call(this,match,theme,setSvgProps)}return this};function applyThemeToSvg64(match,theme,setSvgProps){var encoding=match[ENCODING_MATCH_INDEX];var data=match[DATA_MATCH_INDEX];var div=document.createElement("div");setSvgProps=setSvgProps||this.setSvgProps||api.setSvgProps;switch(encoding){case undefined:div.innerHTML=data;setSvgProps.call(div.firstElementChild,theme);match[DATA_MATCH_INDEX]=div.firstElementChild.outerHTML;break;case";base64":div.innerHTML=atob(data);setSvgProps.call(div.firstElementChild,theme);var svgMarkup=(new XMLSerializer).serializeToString(div.firstElementChild);match[DATA_MATCH_INDEX]=btoa(svgMarkup);break;default:throw new TypeError('Unexpected encoding "'+encoding+'"')}return match.slice(1).join("")}api.mixin=function(svgOrImgOrRule,setSvgProps){if(svgOrImgOrRule.tagName==="svg"){if(setSvgProps){svgOrImgOrRule.setTheme=setSvgProps}else{Object.defineProperty(svgOrImgOrRule,"setTheme",{get:function(){return api.setSvgProps}})}}else if(svgOrImgOrRule.tagName==="IMG"){if(setSvgProps){svgOrImgOrRule.setSvgProps=setSvgProps}svgOrImgOrRule.setTheme=api.setImgSvgProps}else if(svgOrImgOrRule=="[object CSSStyleRule]"){if(setSvgProps){svgOrImgOrRule.setSvgProps=setSvgProps}svgOrImgOrRule.setTheme=api.setRuleSvgProps}return svgOrImgOrRule}; | ||
"use strict";var PATTERN="((data:image/svg\\+xml)(;\\w+)?(,)(.*)|(.*\\.svg))";var regexDataSvg64=new RegExp("^()"+PATTERN+"()$");var regexUrlDataSvg64=new RegExp('^(url\\(")'+PATTERN+'("\\))$');var WHOLE_MATCH_INDEX=0,URL_CONSTRUCT_INDEX=1,URI_MATCH_INDEX=2,SCHEME_INDEX=3,ENCODING_MATCH_INDEX=4,DATA_MATCH_INDEX=6,SVG_FILE_URL_INDEX=7;var propsUsingImageUrl=["background-image","list-style-image","border-image","content"];var api;if(typeof exports==="undefined"){window.svgThemer=api={}}else{api=exports}api.setSvgProps=function(theme){if(theme.color){this.style.stroke=this.style.color=theme.color}if(theme.backgroundColor){this.style.fill=theme.backgroundColor}};api.setImgSvgProps=function(theme,setSvgProps){if(this.tagName==="IMG"){var match=this.src.match(regexDataSvg64);if(match){applyThemeToSvgData.call(this,match,theme,setSvgProps,function(imageData){this.src=imageData})}}return this};api.setRuleSvgProps=function(theme,setSvgProps,propName){var match;var ruleProps=this.style;if(typeof setSvgProps==="string"){propName=setSvgProps;setSvgProps=undefined}function matchProp(propName){return match=ruleProps[propName].match(regexUrlDataSvg64)}if(propName){matchProp(propName)}else{for(var i=0;i<propsUsingImageUrl.length;++i){propName=propsUsingImageUrl[i];if(matchProp(propName)){break}}}if(match){applyThemeToSvgData.call(this,match,theme,setSvgProps,function(urlImageData){ruleProps[propName]=urlImageData})}return this};function applyThemeToSvgData(match,theme,setSvgProps,callback){var self=this;if(match[SVG_FILE_URL_INDEX]){get(match[SVG_FILE_URL_INDEX],function(svgMarkup){match[SCHEME_INDEX]="data:image/svg+xml,";match[DATA_MATCH_INDEX]=svgMarkup;match[SVG_FILE_URL_INDEX]="";applyNow.call(self,match,theme,setSvgProps,callback)})}else{applyNow.call(this,match,theme,setSvgProps,callback)}}var HTTP_STATE_DONE=4,HTTP_STATUS_OK=200;function get(url,callback){var httpRequest=new XMLHttpRequest;httpRequest.open("GET",url,true);httpRequest.onreadystatechange=function(){if(httpRequest.readyState===HTTP_STATE_DONE&&httpRequest.status===HTTP_STATUS_OK){callback(httpRequest.responseText)}};httpRequest.send(null)}function applyNow(match,theme,setSvgProps,callback){var encoding=match[ENCODING_MATCH_INDEX];var data=match[DATA_MATCH_INDEX];var div=document.createElement("div");setSvgProps=setSvgProps||this.setSvgProps||api.setSvgProps;switch(encoding){case undefined:div.innerHTML=data.indexOf("%3C")>=0?decodeURIComponent(data):data;setSvgProps.call(div.firstElementChild,theme);var svgMarkup=(new XMLSerializer).serializeToString(div.firstElementChild);match[DATA_MATCH_INDEX]=encodeURIComponent(svgMarkup);break;case";base64":div.innerHTML=atob(data);setSvgProps.call(div.firstElementChild,theme);var svgMarkup=(new XMLSerializer).serializeToString(div.firstElementChild);match[DATA_MATCH_INDEX]=btoa(svgMarkup);break;default:throw new TypeError('Unexpected encoding "'+encoding+'"')}match[WHOLE_MATCH_INDEX]=match[URI_MATCH_INDEX]=undefined;callback.call(this,match.join(""))}api.mixin=function(svgOrImgOrRule,setSvgProps){if(svgOrImgOrRule.tagName==="svg"){if(setSvgProps){svgOrImgOrRule.setTheme=setSvgProps}else{Object.defineProperty(svgOrImgOrRule,"setTheme",{get:function(){return api.setSvgProps}})}}else if(svgOrImgOrRule.tagName==="IMG"){if(setSvgProps){svgOrImgOrRule.setSvgProps=setSvgProps}svgOrImgOrRule.setTheme=api.setImgSvgProps}else if(svgOrImgOrRule=="[object CSSStyleRule]"){if(setSvgProps){svgOrImgOrRule.setSvgProps=setSvgProps}svgOrImgOrRule.setTheme=api.setRuleSvgProps}return svgOrImgOrRule}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
28712
288
218