Merge remote-tracking branch 'upstream/main'

This commit is contained in:
2026-08-26 09:31:34 +08:00
parent 9b8c4bfab6
commit 394eb0285d
34137 changed files with 3589424 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
<a name="1.4.7"></a>
## [1.4.7](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/compare/svg-baker-runtime@1.4.6...svg-baker-runtime@1.4.7) (2020-04-28)
### Bug Fixes
* move `mask` & `clipPath` elements outside symbol ([2847f07](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/2847f07))
<a name="1.4.6"></a>
## [1.4.6](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/compare/svg-baker-runtime@1.4.5...svg-baker-runtime@1.4.6) (2020-04-12)
### Bug Fixes
* refer to transpiled code for webpack ([b139b21](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/b139b21)), closes [#385](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/issues/385)
<a name="1.4.5"></a>
## [1.4.5](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/compare/svg-baker-runtime@1.4.0...svg-baker-runtime@1.4.5) (2020-04-08)
### Bug Fixes
* add aria-hidden attribute to SVG sprites ([d9a2a11](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/d9a2a11)), closes [JetBrains/svg-sprite-loader#315](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/issues/315)
* dont reference browser globals on init ([aabcd04](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/aabcd04))
* replace all instances of urls in attribute ([39f8aaf](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/39f8aaf))
* **typecheck:** fixed wrong check for angular on window ([670a06b](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/670a06b))
<a name="1.4.4"></a>
## [1.4.4](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/compare/svg-baker-runtime@1.4.0...svg-baker-runtime@1.4.4) (2020-04-01)
### Bug Fixes
* add aria-hidden attribute to SVG sprites ([d9a2a11](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/d9a2a11)), closes [JetBrains/svg-sprite-loader#315](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/issues/315)
* dont reference browser globals on init ([aabcd04](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/aabcd04))
* replace all instances of urls in attribute ([39f8aaf](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/39f8aaf))
<a name="1.4.3"></a>
## [1.4.3](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/compare/svg-baker-runtime@1.4.0...svg-baker-runtime@1.4.3) (2020-01-24)
### Bug Fixes
* dont reference browser globals on init ([aabcd04](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/aabcd04))
* replace all instances of urls in attribute ([39f8aaf](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/39f8aaf))
<a name="1.4.2"></a>
## [1.4.2](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/compare/svg-baker-runtime@1.4.1...svg-baker-runtime@1.4.2) (2019-04-27)
**Note:** Version bump only for package svg-baker-runtime
<a name="1.4.1"></a>
## [1.4.1](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/compare/svg-baker-runtime@1.4.0...svg-baker-runtime@1.4.1) (2019-04-27)
### Bug Fixes
* dont reference browser globals on init ([aabcd04](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/aabcd04))
* replace all instances of urls in attribute ([39f8aaf](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/39f8aaf))
<a name="1.4.0"></a>
# [1.4.0](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/compare/svg-baker-runtime@1.2.99...svg-baker-runtime@1.4.0) (2018-10-29)
### Bug Fixes
* **runtime:** apply styles in dynamically appended symbols in Edge ([52a5033](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/52a5033))
* **runtime:** fix attaching to existing DOM node ([631cc2a](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/631cc2a))
* update package info ([7cc1b95](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/7cc1b95))
* upgrade merge-options due to severity vulnerabilities ([0538c60](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/0538c60))
### Features
* **runtime:** add ability to attach sprite to existing DOM element ([6f92906](https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime/commit/6f92906))
<a name="1.2.95"></a>
## 1.2.95 (2017-05-13)
### Bug Fixes
* **browser-sprite:** refactor & don't throw an error when Angular location update event fires ([537dd81](https://github.com/kisenka/svg-baker/packages/svg-baker-runtime/commit/537dd81))
+937
View File
@@ -0,0 +1,937 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.BrowserSprite = factory());
}(this, (function () { 'use strict';
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var deepmerge = createCommonjsModule(function (module, exports) {
(function (root, factory) {
if (typeof undefined === 'function' && undefined.amd) {
undefined(factory);
} else {
module.exports = factory();
}
}(commonjsGlobal, function () {
function isMergeableObject(val) {
var nonNullObject = val && typeof val === 'object';
return nonNullObject
&& Object.prototype.toString.call(val) !== '[object RegExp]'
&& Object.prototype.toString.call(val) !== '[object Date]'
}
function emptyTarget(val) {
return Array.isArray(val) ? [] : {}
}
function cloneIfNecessary(value, optionsArgument) {
var clone = optionsArgument && optionsArgument.clone === true;
return (clone && isMergeableObject(value)) ? deepmerge(emptyTarget(value), value, optionsArgument) : value
}
function defaultArrayMerge(target, source, optionsArgument) {
var destination = target.slice();
source.forEach(function(e, i) {
if (typeof destination[i] === 'undefined') {
destination[i] = cloneIfNecessary(e, optionsArgument);
} else if (isMergeableObject(e)) {
destination[i] = deepmerge(target[i], e, optionsArgument);
} else if (target.indexOf(e) === -1) {
destination.push(cloneIfNecessary(e, optionsArgument));
}
});
return destination
}
function mergeObject(target, source, optionsArgument) {
var destination = {};
if (isMergeableObject(target)) {
Object.keys(target).forEach(function (key) {
destination[key] = cloneIfNecessary(target[key], optionsArgument);
});
}
Object.keys(source).forEach(function (key) {
if (!isMergeableObject(source[key]) || !target[key]) {
destination[key] = cloneIfNecessary(source[key], optionsArgument);
} else {
destination[key] = deepmerge(target[key], source[key], optionsArgument);
}
});
return destination
}
function deepmerge(target, source, optionsArgument) {
var array = Array.isArray(source);
var options = optionsArgument || { arrayMerge: defaultArrayMerge };
var arrayMerge = options.arrayMerge || defaultArrayMerge;
if (array) {
return Array.isArray(target) ? arrayMerge(target, source, optionsArgument) : cloneIfNecessary(source, optionsArgument)
} else {
return mergeObject(target, source, optionsArgument)
}
}
deepmerge.all = function deepmergeAll(array, optionsArgument) {
if (!Array.isArray(array) || array.length < 2) {
throw new Error('first argument should be an array with at least two elements')
}
// we are sure there are at least 2 values, so it is safe to have no initial value
return array.reduce(function(prev, next) {
return deepmerge(prev, next, optionsArgument)
})
};
return deepmerge
}));
});
//
// An event handler can take an optional event argument
// and should not return a value
// An array of all currently registered event handlers for a type
// A map of event types and their corresponding event handlers.
/** Mitt: Tiny (~200b) functional event emitter / pubsub.
* @name mitt
* @returns {Mitt}
*/
function mitt(all ) {
all = all || Object.create(null);
return {
/**
* Register an event handler for the given type.
*
* @param {String} type Type of event to listen for, or `"*"` for all events
* @param {Function} handler Function to call in response to given event
* @memberOf mitt
*/
on: function on(type , handler ) {
(all[type] || (all[type] = [])).push(handler);
},
/**
* Remove an event handler for the given type.
*
* @param {String} type Type of event to unregister `handler` from, or `"*"`
* @param {Function} handler Handler function to remove
* @memberOf mitt
*/
off: function off(type , handler ) {
if (all[type]) {
all[type].splice(all[type].indexOf(handler) >>> 0, 1);
}
},
/**
* Invoke all handlers for the given type.
* If present, `"*"` handlers are invoked after type-matched handlers.
*
* @param {String} type The event type to invoke
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
* @memberof mitt
*/
emit: function emit(type , evt ) {
(all[type] || []).map(function (handler) { handler(evt); });
(all['*'] || []).map(function (handler) { handler(type, evt); });
}
};
}
var namespaces_1 = createCommonjsModule(function (module, exports) {
var namespaces = {
svg: {
name: 'xmlns',
uri: 'http://www.w3.org/2000/svg'
},
xlink: {
name: 'xmlns:xlink',
uri: 'http://www.w3.org/1999/xlink'
}
};
exports.default = namespaces;
module.exports = exports.default;
});
/**
* @param {Object} attrs
* @return {string}
*/
var objectToAttrsString = function (attrs) {
return Object.keys(attrs).map(function (attr) {
var value = attrs[attr].toString().replace(/"/g, '&quot;');
return (attr + "=\"" + value + "\"");
}).join(' ');
};
var svg = namespaces_1.svg;
var xlink = namespaces_1.xlink;
var defaultAttrs = {};
defaultAttrs[svg.name] = svg.uri;
defaultAttrs[xlink.name] = xlink.uri;
/**
* @param {string} [content]
* @param {Object} [attributes]
* @return {string}
*/
var wrapInSvgString = function (content, attributes) {
if ( content === void 0 ) content = '';
var attrs = deepmerge(defaultAttrs, attributes || {});
var attrsRendered = objectToAttrsString(attrs);
return ("<svg " + attrsRendered + ">" + content + "</svg>");
};
var svg$1 = namespaces_1.svg;
var xlink$1 = namespaces_1.xlink;
var defaultConfig = {
attrs: ( obj = {
style: ['position: absolute', 'width: 0', 'height: 0'].join('; '),
'aria-hidden': 'true'
}, obj[svg$1.name] = svg$1.uri, obj[xlink$1.name] = xlink$1.uri, obj )
};
var obj;
var Sprite = function Sprite(config) {
this.config = deepmerge(defaultConfig, config || {});
this.symbols = [];
};
/**
* Add new symbol. If symbol with the same id exists it will be replaced.
* @param {SpriteSymbol} symbol
* @return {boolean} `true` - symbol was added, `false` - replaced
*/
Sprite.prototype.add = function add (symbol) {
var ref = this;
var symbols = ref.symbols;
var existing = this.find(symbol.id);
if (existing) {
symbols[symbols.indexOf(existing)] = symbol;
return false;
}
symbols.push(symbol);
return true;
};
/**
* Remove symbol & destroy it
* @param {string} id
* @return {boolean} `true` - symbol was found & successfully destroyed, `false` - otherwise
*/
Sprite.prototype.remove = function remove (id) {
var ref = this;
var symbols = ref.symbols;
var symbol = this.find(id);
if (symbol) {
symbols.splice(symbols.indexOf(symbol), 1);
symbol.destroy();
return true;
}
return false;
};
/**
* @param {string} id
* @return {SpriteSymbol|null}
*/
Sprite.prototype.find = function find (id) {
return this.symbols.filter(function (s) { return s.id === id; })[0] || null;
};
/**
* @param {string} id
* @return {boolean}
*/
Sprite.prototype.has = function has (id) {
return this.find(id) !== null;
};
/**
* @return {string}
*/
Sprite.prototype.stringify = function stringify () {
var ref = this.config;
var attrs = ref.attrs;
var stringifiedSymbols = this.symbols.map(function (s) { return s.stringify(); }).join('');
return wrapInSvgString(stringifiedSymbols, attrs);
};
/**
* @return {string}
*/
Sprite.prototype.toString = function toString () {
return this.stringify();
};
Sprite.prototype.destroy = function destroy () {
this.symbols.forEach(function (s) { return s.destroy(); });
};
var SpriteSymbol = function SpriteSymbol(ref) {
var id = ref.id;
var viewBox = ref.viewBox;
var content = ref.content;
this.id = id;
this.viewBox = viewBox;
this.content = content;
};
/**
* @return {string}
*/
SpriteSymbol.prototype.stringify = function stringify () {
return this.content;
};
/**
* @return {string}
*/
SpriteSymbol.prototype.toString = function toString () {
return this.stringify();
};
SpriteSymbol.prototype.destroy = function destroy () {
var this$1 = this;
['id', 'viewBox', 'content'].forEach(function (prop) { return delete this$1[prop]; });
};
/**
* @param {string} content
* @return {Element}
*/
var parse = function (content) {
var hasImportNode = !!document.importNode;
var doc = new DOMParser().parseFromString(content, 'image/svg+xml').documentElement;
/**
* Fix for browser which are throwing WrongDocumentError
* if you insert an element which is not part of the document
* @see http://stackoverflow.com/a/7986519/4624403
*/
if (hasImportNode) {
return document.importNode(doc, true);
}
return doc;
};
var BrowserSpriteSymbol = (function (SpriteSymbol$$1) {
function BrowserSpriteSymbol () {
SpriteSymbol$$1.apply(this, arguments);
}
if ( SpriteSymbol$$1 ) BrowserSpriteSymbol.__proto__ = SpriteSymbol$$1;
BrowserSpriteSymbol.prototype = Object.create( SpriteSymbol$$1 && SpriteSymbol$$1.prototype );
BrowserSpriteSymbol.prototype.constructor = BrowserSpriteSymbol;
var prototypeAccessors = { isMounted: {} };
prototypeAccessors.isMounted.get = function () {
return !!this.node;
};
/**
* @param {Element} node
* @return {BrowserSpriteSymbol}
*/
BrowserSpriteSymbol.createFromExistingNode = function createFromExistingNode (node) {
return new BrowserSpriteSymbol({
id: node.getAttribute('id'),
viewBox: node.getAttribute('viewBox'),
content: node.outerHTML
});
};
BrowserSpriteSymbol.prototype.destroy = function destroy () {
if (this.isMounted) {
this.unmount();
}
SpriteSymbol$$1.prototype.destroy.call(this);
};
/**
* @param {Element|string} target
* @return {Element}
*/
BrowserSpriteSymbol.prototype.mount = function mount (target) {
if (this.isMounted) {
return this.node;
}
var mountTarget = typeof target === 'string' ? document.querySelector(target) : target;
var node = this.render();
this.node = node;
mountTarget.appendChild(node);
return node;
};
/**
* @return {Element}
*/
BrowserSpriteSymbol.prototype.render = function render () {
var content = this.stringify();
return parse(wrapInSvgString(content)).childNodes[0];
};
BrowserSpriteSymbol.prototype.unmount = function unmount () {
this.node.parentNode.removeChild(this.node);
};
Object.defineProperties( BrowserSpriteSymbol.prototype, prototypeAccessors );
return BrowserSpriteSymbol;
}(SpriteSymbol));
var defaultConfig$1 = {
/**
* Should following options be automatically configured:
* - `syncUrlsWithBaseTag`
* - `locationChangeAngularEmitter`
* - `moveGradientsOutsideSymbol`
* @type {boolean}
*/
autoConfigure: true,
/**
* Default mounting selector
* @type {string}
*/
mountTo: 'body',
/**
* Fix disappearing SVG elements when <base href> exists.
* Executes when sprite mounted.
* @see http://stackoverflow.com/a/18265336/796152
* @see https://github.com/everdimension/angular-svg-base-fix
* @see https://github.com/angular/angular.js/issues/8934#issuecomment-56568466
* @type {boolean}
*/
syncUrlsWithBaseTag: false,
/**
* Should sprite listen custom location change event
* @type {boolean}
*/
listenLocationChangeEvent: true,
/**
* Custom window event name which should be emitted to update sprite urls
* @type {string}
*/
locationChangeEvent: 'locationChange',
/**
* Emit location change event in Angular automatically
* @type {boolean}
*/
locationChangeAngularEmitter: false,
/**
* Selector to find symbols usages when updating sprite urls
* @type {string}
*/
usagesToUpdate: 'use[*|href]',
/**
* Fix Firefox bug when gradients and patterns don't work if they are within a symbol.
* Executes when sprite is rendered, but not mounted.
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=306674
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=353575
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=1235364
* @type {boolean}
*/
moveGradientsOutsideSymbol: false
};
/**
* @param {*} arrayLike
* @return {Array}
*/
var arrayFrom = function (arrayLike) {
return Array.prototype.slice.call(arrayLike, 0);
};
var browser = {
isChrome: function () { return /chrome/i.test(navigator.userAgent); },
isFirefox: function () { return /firefox/i.test(navigator.userAgent); },
// https://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
isIE: function () { return /msie/i.test(navigator.userAgent) || /trident/i.test(navigator.userAgent); },
isEdge: function () { return /edge/i.test(navigator.userAgent); }
};
/**
* @param {string} name
* @param {*} data
*/
var dispatchEvent = function (name, data) {
var event = document.createEvent('CustomEvent');
event.initCustomEvent(name, false, false, data);
window.dispatchEvent(event);
};
/**
* IE doesn't evaluate <style> tags in SVGs that are dynamically added to the page.
* This trick will trigger IE to read and use any existing SVG <style> tags.
* @see https://github.com/iconic/SVGInjector/issues/23
* @see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10898469/
*
* @param {Element} node DOM Element to search <style> tags in
* @return {Array<HTMLStyleElement>}
*/
var evalStylesIEWorkaround = function (node) {
var updatedNodes = [];
arrayFrom(node.querySelectorAll('style'))
.forEach(function (style) {
style.textContent += '';
updatedNodes.push(style);
});
return updatedNodes;
};
/**
* @param {string} [url] If not provided - current URL will be used
* @return {string}
*/
var getUrlWithoutFragment = function (url) {
return (url || window.location.href).split('#')[0];
};
/* global angular */
/**
* @param {string} eventName
*/
var locationChangeAngularEmitter = function (eventName) {
angular.module('ng').run(['$rootScope', function ($rootScope) {
$rootScope.$on('$locationChangeSuccess', function (e, newUrl, oldUrl) {
dispatchEvent(eventName, { oldUrl: oldUrl, newUrl: newUrl });
});
}]);
};
var defaultSelector = 'linearGradient, radialGradient, pattern, mask, clipPath';
/**
* @param {Element} svg
* @param {string} [selector]
* @return {Element}
*/
var moveGradientsOutsideSymbol = function (svg, selector) {
if ( selector === void 0 ) selector = defaultSelector;
arrayFrom(svg.querySelectorAll('symbol')).forEach(function (symbol) {
arrayFrom(symbol.querySelectorAll(selector)).forEach(function (node) {
symbol.parentNode.insertBefore(node, symbol);
});
});
return svg;
};
/**
* @param {NodeList} nodes
* @param {Function} [matcher]
* @return {Attr[]}
*/
function selectAttributes(nodes, matcher) {
var attrs = arrayFrom(nodes).reduce(function (acc, node) {
if (!node.attributes) {
return acc;
}
var arrayfied = arrayFrom(node.attributes);
var matched = matcher ? arrayfied.filter(matcher) : arrayfied;
return acc.concat(matched);
}, []);
return attrs;
}
/**
* @param {NodeList|Node} nodes
* @param {boolean} [clone=true]
* @return {string}
*/
var xLinkNS = namespaces_1.xlink.uri;
var xLinkAttrName = 'xlink:href';
// eslint-disable-next-line no-useless-escape
var specialUrlCharsPattern = /[{}|\\\^\[\]`"<>]/g;
function encoder(url) {
return url.replace(specialUrlCharsPattern, function (match) {
return ("%" + (match[0].charCodeAt(0).toString(16).toUpperCase()));
});
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}
/**
* @param {NodeList} nodes
* @param {string} startsWith
* @param {string} replaceWith
* @return {NodeList}
*/
function updateReferences(nodes, startsWith, replaceWith) {
arrayFrom(nodes).forEach(function (node) {
var href = node.getAttribute(xLinkAttrName);
if (href && href.indexOf(startsWith) === 0) {
var newUrl = href.replace(startsWith, replaceWith);
node.setAttributeNS(xLinkNS, xLinkAttrName, newUrl);
}
});
return nodes;
}
/**
* List of SVG attributes to update url() target in them
*/
var attList = [
'clipPath',
'colorProfile',
'src',
'cursor',
'fill',
'filter',
'marker',
'markerStart',
'markerMid',
'markerEnd',
'mask',
'stroke',
'style'
];
var attSelector = attList.map(function (attr) { return ("[" + attr + "]"); }).join(',');
/**
* Update URLs in svg image (like `fill="url(...)"`) and update referencing elements
* @param {Element} svg
* @param {NodeList} references
* @param {string|RegExp} startsWith
* @param {string} replaceWith
* @return {void}
*
* @example
* const sprite = document.querySelector('svg.sprite');
* const usages = document.querySelectorAll('use');
* updateUrls(sprite, usages, '#', 'prefix#');
*/
var updateUrls = function (svg, references, startsWith, replaceWith) {
var startsWithEncoded = encoder(startsWith);
var replaceWithEncoded = encoder(replaceWith);
var nodes = svg.querySelectorAll(attSelector);
var attrs = selectAttributes(nodes, function (ref) {
var localName = ref.localName;
var value = ref.value;
return attList.indexOf(localName) !== -1 && value.indexOf(("url(" + startsWithEncoded)) !== -1;
});
attrs.forEach(function (attr) { return attr.value = attr.value.replace(new RegExp(escapeRegExp(startsWithEncoded), 'g'), replaceWithEncoded); });
updateReferences(references, startsWithEncoded, replaceWithEncoded);
};
/**
* Internal emitter events
* @enum
* @private
*/
var Events = {
MOUNT: 'mount',
SYMBOL_MOUNT: 'symbol_mount'
};
var BrowserSprite = (function (Sprite$$1) {
function BrowserSprite(cfg) {
var this$1 = this;
if ( cfg === void 0 ) cfg = {};
Sprite$$1.call(this, deepmerge(defaultConfig$1, cfg));
var emitter = mitt();
this._emitter = emitter;
this.node = null;
var ref = this;
var config = ref.config;
if (config.autoConfigure) {
this._autoConfigure(cfg);
}
if (config.syncUrlsWithBaseTag) {
var baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
emitter.on(Events.MOUNT, function () { return this$1.updateUrls('#', baseUrl); });
}
var handleLocationChange = this._handleLocationChange.bind(this);
this._handleLocationChange = handleLocationChange;
// Provide way to update sprite urls externally via dispatching custom window event
if (config.listenLocationChangeEvent) {
window.addEventListener(config.locationChangeEvent, handleLocationChange);
}
// Emit location change event in Angular automatically
if (config.locationChangeAngularEmitter) {
locationChangeAngularEmitter(config.locationChangeEvent);
}
// After sprite mounted
emitter.on(Events.MOUNT, function (spriteNode) {
if (config.moveGradientsOutsideSymbol) {
moveGradientsOutsideSymbol(spriteNode);
}
});
// After symbol mounted into sprite
emitter.on(Events.SYMBOL_MOUNT, function (symbolNode) {
if (config.moveGradientsOutsideSymbol) {
moveGradientsOutsideSymbol(symbolNode.parentNode);
}
if (browser.isIE() || browser.isEdge()) {
evalStylesIEWorkaround(symbolNode);
}
});
}
if ( Sprite$$1 ) BrowserSprite.__proto__ = Sprite$$1;
BrowserSprite.prototype = Object.create( Sprite$$1 && Sprite$$1.prototype );
BrowserSprite.prototype.constructor = BrowserSprite;
var prototypeAccessors = { isMounted: {} };
/**
* @return {boolean}
*/
prototypeAccessors.isMounted.get = function () {
return !!this.node;
};
/**
* Automatically configure following options
* - `syncUrlsWithBaseTag`
* - `locationChangeAngularEmitter`
* - `moveGradientsOutsideSymbol`
* @param {Object} cfg
* @private
*/
BrowserSprite.prototype._autoConfigure = function _autoConfigure (cfg) {
var ref = this;
var config = ref.config;
if (typeof cfg.syncUrlsWithBaseTag === 'undefined') {
config.syncUrlsWithBaseTag = typeof document.getElementsByTagName('base')[0] !== 'undefined';
}
if (typeof cfg.locationChangeAngularEmitter === 'undefined') {
config.locationChangeAngularEmitter = typeof window.angular !== 'undefined';
}
if (typeof cfg.moveGradientsOutsideSymbol === 'undefined') {
config.moveGradientsOutsideSymbol = browser.isFirefox();
}
};
/**
* @param {Event} event
* @param {Object} event.detail
* @param {string} event.detail.oldUrl
* @param {string} event.detail.newUrl
* @private
*/
BrowserSprite.prototype._handleLocationChange = function _handleLocationChange (event) {
var ref = event.detail;
var oldUrl = ref.oldUrl;
var newUrl = ref.newUrl;
this.updateUrls(oldUrl, newUrl);
};
/**
* Add new symbol. If symbol with the same id exists it will be replaced.
* If sprite already mounted - `symbol.mount(sprite.node)` will be called.
* @fires Events#SYMBOL_MOUNT
* @param {BrowserSpriteSymbol} symbol
* @return {boolean} `true` - symbol was added, `false` - replaced
*/
BrowserSprite.prototype.add = function add (symbol) {
var sprite = this;
var isNewSymbol = Sprite$$1.prototype.add.call(this, symbol);
if (this.isMounted && isNewSymbol) {
symbol.mount(sprite.node);
this._emitter.emit(Events.SYMBOL_MOUNT, symbol.node);
}
return isNewSymbol;
};
/**
* Attach to existing DOM node
* @param {string|Element} target
* @return {Element|null} attached DOM Element. null if node to attach not found.
*/
BrowserSprite.prototype.attach = function attach (target) {
var this$1 = this;
var sprite = this;
if (sprite.isMounted) {
return sprite.node;
}
/** @type Element */
var node = typeof target === 'string' ? document.querySelector(target) : target;
sprite.node = node;
// Already added symbols needs to be mounted
this.symbols.forEach(function (symbol) {
symbol.mount(sprite.node);
this$1._emitter.emit(Events.SYMBOL_MOUNT, symbol.node);
});
// Create symbols from existing DOM nodes, add and mount them
arrayFrom(node.querySelectorAll('symbol'))
.forEach(function (symbolNode) {
var symbol = BrowserSpriteSymbol.createFromExistingNode(symbolNode);
symbol.node = symbolNode; // hack to prevent symbol mounting to sprite when adding
sprite.add(symbol);
});
this._emitter.emit(Events.MOUNT, node);
return node;
};
BrowserSprite.prototype.destroy = function destroy () {
var ref = this;
var config = ref.config;
var symbols = ref.symbols;
var _emitter = ref._emitter;
symbols.forEach(function (s) { return s.destroy(); });
_emitter.off('*');
window.removeEventListener(config.locationChangeEvent, this._handleLocationChange);
if (this.isMounted) {
this.unmount();
}
};
/**
* @fires Events#MOUNT
* @param {string|Element} [target]
* @param {boolean} [prepend=false]
* @return {Element|null} rendered sprite node. null if mount node not found.
*/
BrowserSprite.prototype.mount = function mount (target, prepend) {
if ( target === void 0 ) target = this.config.mountTo;
if ( prepend === void 0 ) prepend = false;
var sprite = this;
if (sprite.isMounted) {
return sprite.node;
}
var mountNode = typeof target === 'string' ? document.querySelector(target) : target;
var node = sprite.render();
this.node = node;
if (prepend && mountNode.childNodes[0]) {
mountNode.insertBefore(node, mountNode.childNodes[0]);
} else {
mountNode.appendChild(node);
}
this._emitter.emit(Events.MOUNT, node);
return node;
};
/**
* @return {Element}
*/
BrowserSprite.prototype.render = function render () {
return parse(this.stringify());
};
/**
* Detach sprite from the DOM
*/
BrowserSprite.prototype.unmount = function unmount () {
this.node.parentNode.removeChild(this.node);
};
/**
* Update URLs in sprite and usage elements
* @param {string} oldUrl
* @param {string} newUrl
* @return {boolean} `true` - URLs was updated, `false` - sprite is not mounted
*/
BrowserSprite.prototype.updateUrls = function updateUrls$1 (oldUrl, newUrl) {
if (!this.isMounted) {
return false;
}
var usages = document.querySelectorAll(this.config.usagesToUpdate);
updateUrls(
this.node,
usages,
((getUrlWithoutFragment(oldUrl)) + "#"),
((getUrlWithoutFragment(newUrl)) + "#")
);
return true;
};
Object.defineProperties( BrowserSprite.prototype, prototypeAccessors );
return BrowserSprite;
}(Sprite));
return BrowserSprite;
})));
+270
View File
@@ -0,0 +1,270 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.BrowserSpriteSymbol = factory());
}(this, (function () { 'use strict';
var SpriteSymbol = function SpriteSymbol(ref) {
var id = ref.id;
var viewBox = ref.viewBox;
var content = ref.content;
this.id = id;
this.viewBox = viewBox;
this.content = content;
};
/**
* @return {string}
*/
SpriteSymbol.prototype.stringify = function stringify () {
return this.content;
};
/**
* @return {string}
*/
SpriteSymbol.prototype.toString = function toString () {
return this.stringify();
};
SpriteSymbol.prototype.destroy = function destroy () {
var this$1 = this;
['id', 'viewBox', 'content'].forEach(function (prop) { return delete this$1[prop]; });
};
/**
* @param {string} content
* @return {Element}
*/
var parse = function (content) {
var hasImportNode = !!document.importNode;
var doc = new DOMParser().parseFromString(content, 'image/svg+xml').documentElement;
/**
* Fix for browser which are throwing WrongDocumentError
* if you insert an element which is not part of the document
* @see http://stackoverflow.com/a/7986519/4624403
*/
if (hasImportNode) {
return document.importNode(doc, true);
}
return doc;
};
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var deepmerge = createCommonjsModule(function (module, exports) {
(function (root, factory) {
if (typeof undefined === 'function' && undefined.amd) {
undefined(factory);
} else {
module.exports = factory();
}
}(commonjsGlobal, function () {
function isMergeableObject(val) {
var nonNullObject = val && typeof val === 'object';
return nonNullObject
&& Object.prototype.toString.call(val) !== '[object RegExp]'
&& Object.prototype.toString.call(val) !== '[object Date]'
}
function emptyTarget(val) {
return Array.isArray(val) ? [] : {}
}
function cloneIfNecessary(value, optionsArgument) {
var clone = optionsArgument && optionsArgument.clone === true;
return (clone && isMergeableObject(value)) ? deepmerge(emptyTarget(value), value, optionsArgument) : value
}
function defaultArrayMerge(target, source, optionsArgument) {
var destination = target.slice();
source.forEach(function(e, i) {
if (typeof destination[i] === 'undefined') {
destination[i] = cloneIfNecessary(e, optionsArgument);
} else if (isMergeableObject(e)) {
destination[i] = deepmerge(target[i], e, optionsArgument);
} else if (target.indexOf(e) === -1) {
destination.push(cloneIfNecessary(e, optionsArgument));
}
});
return destination
}
function mergeObject(target, source, optionsArgument) {
var destination = {};
if (isMergeableObject(target)) {
Object.keys(target).forEach(function (key) {
destination[key] = cloneIfNecessary(target[key], optionsArgument);
});
}
Object.keys(source).forEach(function (key) {
if (!isMergeableObject(source[key]) || !target[key]) {
destination[key] = cloneIfNecessary(source[key], optionsArgument);
} else {
destination[key] = deepmerge(target[key], source[key], optionsArgument);
}
});
return destination
}
function deepmerge(target, source, optionsArgument) {
var array = Array.isArray(source);
var options = optionsArgument || { arrayMerge: defaultArrayMerge };
var arrayMerge = options.arrayMerge || defaultArrayMerge;
if (array) {
return Array.isArray(target) ? arrayMerge(target, source, optionsArgument) : cloneIfNecessary(source, optionsArgument)
} else {
return mergeObject(target, source, optionsArgument)
}
}
deepmerge.all = function deepmergeAll(array, optionsArgument) {
if (!Array.isArray(array) || array.length < 2) {
throw new Error('first argument should be an array with at least two elements')
}
// we are sure there are at least 2 values, so it is safe to have no initial value
return array.reduce(function(prev, next) {
return deepmerge(prev, next, optionsArgument)
})
};
return deepmerge
}));
});
var namespaces_1 = createCommonjsModule(function (module, exports) {
var namespaces = {
svg: {
name: 'xmlns',
uri: 'http://www.w3.org/2000/svg'
},
xlink: {
name: 'xmlns:xlink',
uri: 'http://www.w3.org/1999/xlink'
}
};
exports.default = namespaces;
module.exports = exports.default;
});
/**
* @param {Object} attrs
* @return {string}
*/
var objectToAttrsString = function (attrs) {
return Object.keys(attrs).map(function (attr) {
var value = attrs[attr].toString().replace(/"/g, '&quot;');
return (attr + "=\"" + value + "\"");
}).join(' ');
};
var svg = namespaces_1.svg;
var xlink = namespaces_1.xlink;
var defaultAttrs = {};
defaultAttrs[svg.name] = svg.uri;
defaultAttrs[xlink.name] = xlink.uri;
/**
* @param {string} [content]
* @param {Object} [attributes]
* @return {string}
*/
var wrapInSvgString = function (content, attributes) {
if ( content === void 0 ) content = '';
var attrs = deepmerge(defaultAttrs, attributes || {});
var attrsRendered = objectToAttrsString(attrs);
return ("<svg " + attrsRendered + ">" + content + "</svg>");
};
var BrowserSpriteSymbol = (function (SpriteSymbol$$1) {
function BrowserSpriteSymbol () {
SpriteSymbol$$1.apply(this, arguments);
}
if ( SpriteSymbol$$1 ) BrowserSpriteSymbol.__proto__ = SpriteSymbol$$1;
BrowserSpriteSymbol.prototype = Object.create( SpriteSymbol$$1 && SpriteSymbol$$1.prototype );
BrowserSpriteSymbol.prototype.constructor = BrowserSpriteSymbol;
var prototypeAccessors = { isMounted: {} };
prototypeAccessors.isMounted.get = function () {
return !!this.node;
};
/**
* @param {Element} node
* @return {BrowserSpriteSymbol}
*/
BrowserSpriteSymbol.createFromExistingNode = function createFromExistingNode (node) {
return new BrowserSpriteSymbol({
id: node.getAttribute('id'),
viewBox: node.getAttribute('viewBox'),
content: node.outerHTML
});
};
BrowserSpriteSymbol.prototype.destroy = function destroy () {
if (this.isMounted) {
this.unmount();
}
SpriteSymbol$$1.prototype.destroy.call(this);
};
/**
* @param {Element|string} target
* @return {Element}
*/
BrowserSpriteSymbol.prototype.mount = function mount (target) {
if (this.isMounted) {
return this.node;
}
var mountTarget = typeof target === 'string' ? document.querySelector(target) : target;
var node = this.render();
this.node = node;
mountTarget.appendChild(node);
return node;
};
/**
* @return {Element}
*/
BrowserSpriteSymbol.prototype.render = function render () {
var content = this.stringify();
return parse(wrapInSvgString(content)).childNodes[0];
};
BrowserSpriteSymbol.prototype.unmount = function unmount () {
this.node.parentNode.removeChild(this.node);
};
Object.defineProperties( BrowserSpriteSymbol.prototype, prototypeAccessors );
return BrowserSpriteSymbol;
}(SpriteSymbol));
return BrowserSpriteSymbol;
})));
@@ -0,0 +1,3 @@
test/
.gitignore
.travis.yml
@@ -0,0 +1,126 @@
deepmerge
=========
> ~550B gzipped, ~1.0kB minified
Merge the enumerable attributes of two objects deeply.
example
=======
<!--js
var merge = require('./')
-->
```js
var x = {
foo: { bar: 3 },
array: [{
does: 'work',
too: [ 1, 2, 3 ]
}]
}
var y = {
foo: { baz: 4 },
quux: 5,
array: [{
does: 'work',
too: [ 4, 5, 6 ]
}, {
really: 'yes'
}]
}
var expected = {
foo: {
bar: 3,
baz: 4
},
array: [{
does: 'work',
too: [ 1, 2, 3, 4, 5, 6 ]
}, {
really: 'yes'
}],
quux: 5
}
merge(x, y) // => expected
```
methods
=======
```
var merge = require('deepmerge')
```
merge(x, y, [options])
-----------
Merge two objects `x` and `y` deeply, returning a new merged object with the
elements from both `x` and `y`.
If an element at the same key is present for both `x` and `y`, the value from
`y` will appear in the result.
Merging creates a new object, so that neither `x` or `y` are be modified. However, child objects on `x` or `y` are copied over - if you to copy all values, you must pass `true` to the clone option.
merge.all(arrayOfObjects, [options])
-----------
Merges two or more objects into a single result object.
```js
var x = { foo: { bar: 3 } }
var y = { foo: { baz: 4 } }
var z = { bar: 'yay!' }
var expected = { foo: { bar: 3, baz: 4 }, bar: 'yay!' }
merge.all([x, y, z]) // => expected
```
### options
#### arrayMerge
The merge will also merge arrays and array values by default. However, there are nigh-infinite valid ways to merge arrays, and you may want to supply your own. You can do this by passing an `arrayMerge` function as an option.
```js
function concatMerge(destinationArray, sourceArray, options) {
destinationArray // => [1, 2, 3]
sourceArray // => [3, 2, 1]
options // => { arrayMerge: concatMerge }
return destinationArray.concat(sourceArray)
}
merge([1, 2, 3], [3, 2, 1], { arrayMerge: concatMerge }) // => [1, 2, 3, 3, 2, 1]
```
#### clone
Defaults to `false`. If `clone` is `true` then both `x` and `y` are recursively cloned as part of the merge.
install
=======
With [npm](http://npmjs.org) do:
```sh
npm install deepmerge
```
test
====
With [npm](http://npmjs.org) do:
```sh
npm test
```
license
=======
MIT
@@ -0,0 +1,16 @@
{
"name": "deepmerge",
"main": "index.js",
"homepage": "https://github.com/KyleAMathews/deepmerge",
"description": "Merge the enumerable attributes of two objects.",
"keywords": ["merge", "deep", "extend", "copy", "clone", "recursive"],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"app/bower_components",
"test",
"tests"
]
}
@@ -0,0 +1,45 @@
# [1.3.2](https://github.com/KyleAMathews/deepmerge/releases/tag/v1.3.2)
- documentation: note the minified/gzipped file sizes [56](https://github.com/KyleAMathews/deepmerge/pull/56)
- documentation: make data structures more readable in merge example: pull request [57](https://github.com/KyleAMathews/deepmerge/pull/57)
# [1.3.1](https://github.com/KyleAMathews/deepmerge/releases/tag/v1.3.1)
- documentation: clarify and test some array merging documentation: pull request [51](https://github.com/KyleAMathews/deepmerge/pull/51)
# [1.3.0](https://github.com/KyleAMathews/deepmerge/releases/tag/v1.3.0)
- feature: `merge.all`, a merge function that merges any number of objects: pull request [50](https://github.com/KyleAMathews/deepmerge/pull/50)
# [1.2.0](https://github.com/KyleAMathews/deepmerge/releases/tag/v1.2.0)
- fix: an error that would be thrown when an array would be merged onto a truthy non-array value: pull request [46](https://github.com/KyleAMathews/deepmerge/pull/46)
- feature: the ability to clone: Issue [28](https://github.com/KyleAMathews/deepmerge/issues/28), pull requests [44](https://github.com/KyleAMathews/deepmerge/pull/44) and [48](https://github.com/KyleAMathews/deepmerge/pull/48)
- maintenance: added tests + travis to `.npmignore`: pull request [47](https://github.com/KyleAMathews/deepmerge/pull/47)
# [1.1.1](https://github.com/KyleAMathews/deepmerge/releases/tag/v1.1.1)
- fix an issue where an error was thrown when merging an array onto a non-array: [Pull request 46](https://github.com/KyleAMathews/deepmerge/pull/46)
# [1.1.0](https://github.com/KyleAMathews/deepmerge/releases/tag/v1.1.0)
- allow consumers to specify their own array merging algorithm: [Pull request 37](https://github.com/KyleAMathews/deepmerge/pull/37)
# [1.0.3](https://github.com/KyleAMathews/deepmerge/releases/tag/v1.0.3)
- adding bower.json back: [Issue 38](https://github.com/KyleAMathews/deepmerge/pull/38)
- updating keywords and Github links in package.json [bc3898e](https://github.com/KyleAMathews/deepmerge/commit/bc3898e587a56f74591328f40f656b0152c1d5eb)
# [1.0.2](https://github.com/KyleAMathews/deepmerge/releases/tag/v1.0.2)
- Updating the readme: dropping bower, testing that the example works: [7102fc](https://github.com/KyleAMathews/deepmerge/commit/7102fcc4ddec11e2d33205866f9f18df14e5aeb5)
# [1.0.1](https://github.com/KyleAMathews/deepmerge/releases/tag/v1.0.1)
- `null`, dates, and regular expressions are now properly merged in arrays: [Issue 18](https://github.com/KyleAMathews/deepmerge/pull/18), plus commit: [ef1c6b](https://github.com/KyleAMathews/deepmerge/commit/ef1c6bac8350ba12a24966f0bc7da02560827586)
# 1.0.0
- Should only be a patch change, because this module is READY. [Issue 15](https://github.com/KyleAMathews/deepmerge/issues/15)
- Regular expressions are now treated like primitive values when merging: [Issue 30](https://github.com/KyleAMathews/deepmerge/pull/30)
- Dates are now treated like primitives when merging: [Issue 31](https://github.com/KyleAMathews/deepmerge/issues/31)
@@ -0,0 +1,84 @@
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.deepmerge = factory();
}
}(this, function () {
function isMergeableObject(val) {
var nonNullObject = val && typeof val === 'object'
return nonNullObject
&& Object.prototype.toString.call(val) !== '[object RegExp]'
&& Object.prototype.toString.call(val) !== '[object Date]'
}
function emptyTarget(val) {
return Array.isArray(val) ? [] : {}
}
function cloneIfNecessary(value, optionsArgument) {
var clone = optionsArgument && optionsArgument.clone === true
return (clone && isMergeableObject(value)) ? deepmerge(emptyTarget(value), value, optionsArgument) : value
}
function defaultArrayMerge(target, source, optionsArgument) {
var destination = target.slice()
source.forEach(function(e, i) {
if (typeof destination[i] === 'undefined') {
destination[i] = cloneIfNecessary(e, optionsArgument)
} else if (isMergeableObject(e)) {
destination[i] = deepmerge(target[i], e, optionsArgument)
} else if (target.indexOf(e) === -1) {
destination.push(cloneIfNecessary(e, optionsArgument))
}
})
return destination
}
function mergeObject(target, source, optionsArgument) {
var destination = {}
if (isMergeableObject(target)) {
Object.keys(target).forEach(function (key) {
destination[key] = cloneIfNecessary(target[key], optionsArgument)
})
}
Object.keys(source).forEach(function (key) {
if (!isMergeableObject(source[key]) || !target[key]) {
destination[key] = cloneIfNecessary(source[key], optionsArgument)
} else {
destination[key] = deepmerge(target[key], source[key], optionsArgument)
}
})
return destination
}
function deepmerge(target, source, optionsArgument) {
var array = Array.isArray(source);
var options = optionsArgument || { arrayMerge: defaultArrayMerge }
var arrayMerge = options.arrayMerge || defaultArrayMerge
if (array) {
return Array.isArray(target) ? arrayMerge(target, source, optionsArgument) : cloneIfNecessary(source, optionsArgument)
} else {
return mergeObject(target, source, optionsArgument)
}
}
deepmerge.all = function deepmergeAll(array, optionsArgument) {
if (!Array.isArray(array) || array.length < 2) {
throw new Error('first argument should be an array with at least two elements')
}
// we are sure there are at least 2 values, so it is safe to have no initial value
return array.reduce(function(prev, next) {
return deepmerge(prev, next, optionsArgument)
})
}
return deepmerge
}));
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2012 Nicholas Fisher
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,32 @@
{
"author": "Nick Fisher",
"name": "deepmerge",
"description": "A library for deep (recursive) merging of Javascript objects",
"keywords": [
"merge",
"deep",
"extend",
"copy",
"clone",
"recursive"
],
"version": "1.3.2",
"homepage": "https://github.com/KyleAMathews/deepmerge",
"repository": {
"type": "git",
"url": "git://github.com/KyleAMathews/deepmerge.git"
},
"main": "index",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "tap test/*.js && jsmd README.markdown"
},
"dependencies": {},
"devDependencies": {
"jsmd": "0.3.1",
"tap": "~7.1.2"
},
"license": "MIT"
}
+57
View File
@@ -0,0 +1,57 @@
{
"name": "svg-baker-runtime",
"version": "1.4.7",
"description": "",
"author": "JetBrains",
"license": "MIT",
"repository": "https://github.com/JetBrains/svg-mixer/tree/v1/packages/svg-baker-runtime",
"main": "browser-sprite.js",
"browser": "browser-sprite.js",
"module": "src/browser-sprite.js",
"files": [
"src/",
"browser-sprite.js",
"browser-symbol.js",
"sprite.js",
"symbol.js",
"README.md"
],
"dependencies": {
"deepmerge": "1.3.2",
"mitt": "1.1.2",
"svg-baker": "^1.7.0"
},
"devDependencies": {
"babel-core": "^6.24.1",
"babel-loader": "6.4.1",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-plugin-transform-runtime": "6.23.0",
"babel-preset-es2015": "6.24.0",
"bluebird": "^3.5.0",
"chai": "3.5.0",
"electron": "^1.6.6",
"glob": "^7.1.1",
"json-loader": "^0.5.4",
"karma": "1.6.0",
"karma-chai-plugins": "0.9.0",
"karma-chrome-launcher": "^2.0.0",
"karma-mocha": "^1.3.0",
"karma-mocha-reporter": "^2.2.3",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.3",
"query-string": "4.3.4",
"rollup": "^0.41.6",
"rollup-plugin-buble": "^0.15.0",
"rollup-plugin-commonjs": "^8.0.2",
"rollup-plugin-node-resolve": "^3.0.0",
"string-loader": "^0.0.1",
"wallaby-webpack": "0.0.38",
"webpack": "2.4.1"
},
"scripts": {
"build": "node scripts/build-runtime",
"lint": "eslint src test",
"generate-test-data": "node scripts/generate-test-data",
"test": "yarn build && karma start"
}
}
+242
View File
@@ -0,0 +1,242 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Sprite = factory());
}(this, (function () { 'use strict';
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var deepmerge = createCommonjsModule(function (module, exports) {
(function (root, factory) {
if (typeof undefined === 'function' && undefined.amd) {
undefined(factory);
} else {
module.exports = factory();
}
}(commonjsGlobal, function () {
function isMergeableObject(val) {
var nonNullObject = val && typeof val === 'object';
return nonNullObject
&& Object.prototype.toString.call(val) !== '[object RegExp]'
&& Object.prototype.toString.call(val) !== '[object Date]'
}
function emptyTarget(val) {
return Array.isArray(val) ? [] : {}
}
function cloneIfNecessary(value, optionsArgument) {
var clone = optionsArgument && optionsArgument.clone === true;
return (clone && isMergeableObject(value)) ? deepmerge(emptyTarget(value), value, optionsArgument) : value
}
function defaultArrayMerge(target, source, optionsArgument) {
var destination = target.slice();
source.forEach(function(e, i) {
if (typeof destination[i] === 'undefined') {
destination[i] = cloneIfNecessary(e, optionsArgument);
} else if (isMergeableObject(e)) {
destination[i] = deepmerge(target[i], e, optionsArgument);
} else if (target.indexOf(e) === -1) {
destination.push(cloneIfNecessary(e, optionsArgument));
}
});
return destination
}
function mergeObject(target, source, optionsArgument) {
var destination = {};
if (isMergeableObject(target)) {
Object.keys(target).forEach(function (key) {
destination[key] = cloneIfNecessary(target[key], optionsArgument);
});
}
Object.keys(source).forEach(function (key) {
if (!isMergeableObject(source[key]) || !target[key]) {
destination[key] = cloneIfNecessary(source[key], optionsArgument);
} else {
destination[key] = deepmerge(target[key], source[key], optionsArgument);
}
});
return destination
}
function deepmerge(target, source, optionsArgument) {
var array = Array.isArray(source);
var options = optionsArgument || { arrayMerge: defaultArrayMerge };
var arrayMerge = options.arrayMerge || defaultArrayMerge;
if (array) {
return Array.isArray(target) ? arrayMerge(target, source, optionsArgument) : cloneIfNecessary(source, optionsArgument)
} else {
return mergeObject(target, source, optionsArgument)
}
}
deepmerge.all = function deepmergeAll(array, optionsArgument) {
if (!Array.isArray(array) || array.length < 2) {
throw new Error('first argument should be an array with at least two elements')
}
// we are sure there are at least 2 values, so it is safe to have no initial value
return array.reduce(function(prev, next) {
return deepmerge(prev, next, optionsArgument)
})
};
return deepmerge
}));
});
var namespaces_1 = createCommonjsModule(function (module, exports) {
var namespaces = {
svg: {
name: 'xmlns',
uri: 'http://www.w3.org/2000/svg'
},
xlink: {
name: 'xmlns:xlink',
uri: 'http://www.w3.org/1999/xlink'
}
};
exports.default = namespaces;
module.exports = exports.default;
});
/**
* @param {Object} attrs
* @return {string}
*/
var objectToAttrsString = function (attrs) {
return Object.keys(attrs).map(function (attr) {
var value = attrs[attr].toString().replace(/"/g, '&quot;');
return (attr + "=\"" + value + "\"");
}).join(' ');
};
var svg = namespaces_1.svg;
var xlink = namespaces_1.xlink;
var defaultAttrs = {};
defaultAttrs[svg.name] = svg.uri;
defaultAttrs[xlink.name] = xlink.uri;
/**
* @param {string} [content]
* @param {Object} [attributes]
* @return {string}
*/
var wrapInSvgString = function (content, attributes) {
if ( content === void 0 ) content = '';
var attrs = deepmerge(defaultAttrs, attributes || {});
var attrsRendered = objectToAttrsString(attrs);
return ("<svg " + attrsRendered + ">" + content + "</svg>");
};
var svg$1 = namespaces_1.svg;
var xlink$1 = namespaces_1.xlink;
var defaultConfig = {
attrs: ( obj = {
style: ['position: absolute', 'width: 0', 'height: 0'].join('; '),
'aria-hidden': 'true'
}, obj[svg$1.name] = svg$1.uri, obj[xlink$1.name] = xlink$1.uri, obj )
};
var obj;
var Sprite = function Sprite(config) {
this.config = deepmerge(defaultConfig, config || {});
this.symbols = [];
};
/**
* Add new symbol. If symbol with the same id exists it will be replaced.
* @param {SpriteSymbol} symbol
* @return {boolean} `true` - symbol was added, `false` - replaced
*/
Sprite.prototype.add = function add (symbol) {
var ref = this;
var symbols = ref.symbols;
var existing = this.find(symbol.id);
if (existing) {
symbols[symbols.indexOf(existing)] = symbol;
return false;
}
symbols.push(symbol);
return true;
};
/**
* Remove symbol & destroy it
* @param {string} id
* @return {boolean} `true` - symbol was found & successfully destroyed, `false` - otherwise
*/
Sprite.prototype.remove = function remove (id) {
var ref = this;
var symbols = ref.symbols;
var symbol = this.find(id);
if (symbol) {
symbols.splice(symbols.indexOf(symbol), 1);
symbol.destroy();
return true;
}
return false;
};
/**
* @param {string} id
* @return {SpriteSymbol|null}
*/
Sprite.prototype.find = function find (id) {
return this.symbols.filter(function (s) { return s.id === id; })[0] || null;
};
/**
* @param {string} id
* @return {boolean}
*/
Sprite.prototype.has = function has (id) {
return this.find(id) !== null;
};
/**
* @return {string}
*/
Sprite.prototype.stringify = function stringify () {
var ref = this.config;
var attrs = ref.attrs;
var stringifiedSymbols = this.symbols.map(function (s) { return s.stringify(); }).join('');
return wrapInSvgString(stringifiedSymbols, attrs);
};
/**
* @return {string}
*/
Sprite.prototype.toString = function toString () {
return this.stringify();
};
Sprite.prototype.destroy = function destroy () {
this.symbols.forEach(function (s) { return s.destroy(); });
};
return Sprite;
})));
+60
View File
@@ -0,0 +1,60 @@
export default {
/**
* Should following options be automatically configured:
* - `syncUrlsWithBaseTag`
* - `locationChangeAngularEmitter`
* - `moveGradientsOutsideSymbol`
* @type {boolean}
*/
autoConfigure: true,
/**
* Default mounting selector
* @type {string}
*/
mountTo: 'body',
/**
* Fix disappearing SVG elements when <base href> exists.
* Executes when sprite mounted.
* @see http://stackoverflow.com/a/18265336/796152
* @see https://github.com/everdimension/angular-svg-base-fix
* @see https://github.com/angular/angular.js/issues/8934#issuecomment-56568466
* @type {boolean}
*/
syncUrlsWithBaseTag: false,
/**
* Should sprite listen custom location change event
* @type {boolean}
*/
listenLocationChangeEvent: true,
/**
* Custom window event name which should be emitted to update sprite urls
* @type {string}
*/
locationChangeEvent: 'locationChange',
/**
* Emit location change event in Angular automatically
* @type {boolean}
*/
locationChangeAngularEmitter: false,
/**
* Selector to find symbols usages when updating sprite urls
* @type {string}
*/
usagesToUpdate: 'use[*|href]',
/**
* Fix Firefox bug when gradients and patterns don't work if they are within a symbol.
* Executes when sprite is rendered, but not mounted.
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=306674
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=353575
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=1235364
* @type {boolean}
*/
moveGradientsOutsideSymbol: false
};
+253
View File
@@ -0,0 +1,253 @@
import merge from 'deepmerge';
import Emitter from 'mitt';
import Sprite from './sprite';
import BrowserSymbol from './browser-symbol';
import defaultConfig from './browser-sprite.config';
import {
arrayFrom,
parse,
moveGradientsOutsideSymbol,
browserDetector as browser,
getUrlWithoutFragment,
updateUrls,
locationChangeAngularEmitter,
evalStylesIEWorkaround
} from './utils';
/**
* Internal emitter events
* @enum
* @private
*/
const Events = {
MOUNT: 'mount',
SYMBOL_MOUNT: 'symbol_mount'
};
export default class BrowserSprite extends Sprite {
constructor(cfg = {}) {
super(merge(defaultConfig, cfg));
const emitter = Emitter();
this._emitter = emitter;
this.node = null;
const { config } = this;
if (config.autoConfigure) {
this._autoConfigure(cfg);
}
if (config.syncUrlsWithBaseTag) {
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
emitter.on(Events.MOUNT, () => this.updateUrls('#', baseUrl));
}
const handleLocationChange = this._handleLocationChange.bind(this);
this._handleLocationChange = handleLocationChange;
// Provide way to update sprite urls externally via dispatching custom window event
if (config.listenLocationChangeEvent) {
window.addEventListener(config.locationChangeEvent, handleLocationChange);
}
// Emit location change event in Angular automatically
if (config.locationChangeAngularEmitter) {
locationChangeAngularEmitter(config.locationChangeEvent);
}
// After sprite mounted
emitter.on(Events.MOUNT, (spriteNode) => {
if (config.moveGradientsOutsideSymbol) {
moveGradientsOutsideSymbol(spriteNode);
}
});
// After symbol mounted into sprite
emitter.on(Events.SYMBOL_MOUNT, (symbolNode) => {
if (config.moveGradientsOutsideSymbol) {
moveGradientsOutsideSymbol(symbolNode.parentNode);
}
if (browser.isIE() || browser.isEdge()) {
evalStylesIEWorkaround(symbolNode);
}
});
}
/**
* @return {boolean}
*/
get isMounted() {
return !!this.node;
}
/**
* Automatically configure following options
* - `syncUrlsWithBaseTag`
* - `locationChangeAngularEmitter`
* - `moveGradientsOutsideSymbol`
* @param {Object} cfg
* @private
*/
_autoConfigure(cfg) {
const { config } = this;
if (typeof cfg.syncUrlsWithBaseTag === 'undefined') {
config.syncUrlsWithBaseTag = typeof document.getElementsByTagName('base')[0] !== 'undefined';
}
if (typeof cfg.locationChangeAngularEmitter === 'undefined') {
config.locationChangeAngularEmitter = typeof window.angular !== 'undefined';
}
if (typeof cfg.moveGradientsOutsideSymbol === 'undefined') {
config.moveGradientsOutsideSymbol = browser.isFirefox();
}
}
/**
* @param {Event} event
* @param {Object} event.detail
* @param {string} event.detail.oldUrl
* @param {string} event.detail.newUrl
* @private
*/
_handleLocationChange(event) {
const { oldUrl, newUrl } = event.detail;
this.updateUrls(oldUrl, newUrl);
}
/**
* Add new symbol. If symbol with the same id exists it will be replaced.
* If sprite already mounted - `symbol.mount(sprite.node)` will be called.
* @fires Events#SYMBOL_MOUNT
* @param {BrowserSpriteSymbol} symbol
* @return {boolean} `true` - symbol was added, `false` - replaced
*/
add(symbol) {
const sprite = this;
const isNewSymbol = super.add(symbol);
if (this.isMounted && isNewSymbol) {
symbol.mount(sprite.node);
this._emitter.emit(Events.SYMBOL_MOUNT, symbol.node);
}
return isNewSymbol;
}
/**
* Attach to existing DOM node
* @param {string|Element} target
* @return {Element|null} attached DOM Element. null if node to attach not found.
*/
attach(target) {
const sprite = this;
if (sprite.isMounted) {
return sprite.node;
}
/** @type Element */
const node = typeof target === 'string' ? document.querySelector(target) : target;
sprite.node = node;
// Already added symbols needs to be mounted
this.symbols.forEach((symbol) => {
symbol.mount(sprite.node);
this._emitter.emit(Events.SYMBOL_MOUNT, symbol.node);
});
// Create symbols from existing DOM nodes, add and mount them
arrayFrom(node.querySelectorAll('symbol'))
.forEach((symbolNode) => {
const symbol = BrowserSymbol.createFromExistingNode(symbolNode);
symbol.node = symbolNode; // hack to prevent symbol mounting to sprite when adding
sprite.add(symbol);
});
this._emitter.emit(Events.MOUNT, node);
return node;
}
destroy() {
const { config, symbols, _emitter } = this;
symbols.forEach(s => s.destroy());
_emitter.off('*');
window.removeEventListener(config.locationChangeEvent, this._handleLocationChange);
if (this.isMounted) {
this.unmount();
}
}
/**
* @fires Events#MOUNT
* @param {string|Element} [target]
* @param {boolean} [prepend=false]
* @return {Element|null} rendered sprite node. null if mount node not found.
*/
mount(target = this.config.mountTo, prepend = false) {
const sprite = this;
if (sprite.isMounted) {
return sprite.node;
}
const mountNode = typeof target === 'string' ? document.querySelector(target) : target;
const node = sprite.render();
this.node = node;
if (prepend && mountNode.childNodes[0]) {
mountNode.insertBefore(node, mountNode.childNodes[0]);
} else {
mountNode.appendChild(node);
}
this._emitter.emit(Events.MOUNT, node);
return node;
}
/**
* @return {Element}
*/
render() {
return parse(this.stringify());
}
/**
* Detach sprite from the DOM
*/
unmount() {
this.node.parentNode.removeChild(this.node);
}
/**
* Update URLs in sprite and usage elements
* @param {string} oldUrl
* @param {string} newUrl
* @return {boolean} `true` - URLs was updated, `false` - sprite is not mounted
*/
updateUrls(oldUrl, newUrl) {
if (!this.isMounted) {
return false;
}
const usages = document.querySelectorAll(this.config.usagesToUpdate);
updateUrls(
this.node,
usages,
`${getUrlWithoutFragment(oldUrl)}#`,
`${getUrlWithoutFragment(newUrl)}#`
);
return true;
}
}
+58
View File
@@ -0,0 +1,58 @@
import SpriteSymbol from './symbol';
import parse from './utils/parse';
import wrapInSvgString from './utils/wrap-in-svg-string';
export default class BrowserSpriteSymbol extends SpriteSymbol {
get isMounted() {
return !!this.node;
}
/**
* @param {Element} node
* @return {BrowserSpriteSymbol}
*/
static createFromExistingNode(node) {
return new BrowserSpriteSymbol({
id: node.getAttribute('id'),
viewBox: node.getAttribute('viewBox'),
content: node.outerHTML
});
}
destroy() {
if (this.isMounted) {
this.unmount();
}
super.destroy();
}
/**
* @param {Element|string} target
* @return {Element}
*/
mount(target) {
if (this.isMounted) {
return this.node;
}
const mountTarget = typeof target === 'string' ? document.querySelector(target) : target;
const node = this.render();
this.node = node;
mountTarget.appendChild(node);
return node;
}
/**
* @return {Element}
*/
render() {
const content = this.stringify();
return parse(wrapInSvgString(content)).childNodes[0];
}
unmount() {
this.node.parentNode.removeChild(this.node);
}
}
+12
View File
@@ -0,0 +1,12 @@
import namespaces from 'svg-baker/namespaces';
const { svg, xlink } = namespaces;
export default {
attrs: {
[svg.name]: svg.uri,
[xlink.name]: xlink.uri,
style: ['position: absolute', 'width: 0', 'height: 0'].join('; '),
'aria-hidden': 'true'
}
};
+85
View File
@@ -0,0 +1,85 @@
import merge from 'deepmerge';
import wrapInSvgString from './utils/wrap-in-svg-string';
import defaultConfig from './sprite.config';
export default class Sprite {
/**
* @param {Object} [config]
*/
constructor(config) {
this.config = merge(defaultConfig, config || {});
this.symbols = [];
}
/**
* Add new symbol. If symbol with the same id exists it will be replaced.
* @param {SpriteSymbol} symbol
* @return {boolean} `true` - symbol was added, `false` - replaced
*/
add(symbol) {
const { symbols } = this;
const existing = this.find(symbol.id);
if (existing) {
symbols[symbols.indexOf(existing)] = symbol;
return false;
}
symbols.push(symbol);
return true;
}
/**
* Remove symbol & destroy it
* @param {string} id
* @return {boolean} `true` - symbol was found & successfully destroyed, `false` - otherwise
*/
remove(id) {
const { symbols } = this;
const symbol = this.find(id);
if (symbol) {
symbols.splice(symbols.indexOf(symbol), 1);
symbol.destroy();
return true;
}
return false;
}
/**
* @param {string} id
* @return {SpriteSymbol|null}
*/
find(id) {
return this.symbols.filter(s => s.id === id)[0] || null;
}
/**
* @param {string} id
* @return {boolean}
*/
has(id) {
return this.find(id) !== null;
}
/**
* @return {string}
*/
stringify() {
const { attrs } = this.config;
const stringifiedSymbols = this.symbols.map(s => s.stringify()).join('');
return wrapInSvgString(stringifiedSymbols, attrs);
}
/**
* @return {string}
*/
toString() {
return this.stringify();
}
destroy() {
this.symbols.forEach(s => s.destroy());
}
}
+25
View File
@@ -0,0 +1,25 @@
export default class SpriteSymbol {
constructor({ id, viewBox, content }) {
this.id = id;
this.viewBox = viewBox;
this.content = content;
}
/**
* @return {string}
*/
stringify() {
return this.content;
}
/**
* @return {string}
*/
toString() {
return this.stringify();
}
destroy() {
['id', 'viewBox', 'content'].forEach(prop => delete this[prop]);
}
}
+7
View File
@@ -0,0 +1,7 @@
/**
* @param {*} arrayLike
* @return {Array}
*/
export default function (arrayLike) {
return Array.prototype.slice.call(arrayLike, 0);
}
@@ -0,0 +1,8 @@
export default {
isChrome: () => /chrome/i.test(navigator.userAgent),
isFirefox: () => /firefox/i.test(navigator.userAgent),
// https://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
isIE: () => /msie/i.test(navigator.userAgent) || /trident/i.test(navigator.userAgent),
isEdge: () => /edge/i.test(navigator.userAgent)
};
@@ -0,0 +1,9 @@
/**
* @param {string} name
* @param {*} data
*/
export default function (name, data) {
const event = document.createEvent('CustomEvent');
event.initCustomEvent(name, false, false, data);
window.dispatchEvent(event);
}
@@ -0,0 +1,22 @@
import arrayFrom from './array-from';
/**
* IE doesn't evaluate <style> tags in SVGs that are dynamically added to the page.
* This trick will trigger IE to read and use any existing SVG <style> tags.
* @see https://github.com/iconic/SVGInjector/issues/23
* @see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10898469/
*
* @param {Element} node DOM Element to search <style> tags in
* @return {Array<HTMLStyleElement>}
*/
export default function (node) {
const updatedNodes = [];
arrayFrom(node.querySelectorAll('style'))
.forEach((style) => {
style.textContent += '';
updatedNodes.push(style);
});
return updatedNodes;
}
@@ -0,0 +1,7 @@
/**
* @param {string} [url] If not provided - current URL will be used
* @return {string}
*/
export default function (url) {
return (url || window.location.href).split('#')[0];
}
+13
View File
@@ -0,0 +1,13 @@
export { default as arrayFrom } from './array-from';
export { default as browserDetector } from './browser-detector';
export { default as dispatchCustomEvent } from './dispatch-custom-event';
export { default as evalStylesIEWorkaround } from './eval-styles-ie-workaround';
export { default as getUrlWithoutFragment } from './get-url-without-fragment';
export { default as locationChangeAngularEmitter } from './location-change-angular-emitter';
export { default as moveGradientsOutsideSymbol } from './move-gradients-outside-symbol';
export { default as objectToAttrsString } from './object-to-attrs-string';
export { default as parse } from './parse';
export { default as selectAttributes } from './select-attributes';
export { default as stringify } from './stringify';
export { default as updateUrls } from './update-urls';
export { default as wrapInSvgString } from './wrap-in-svg-string';
@@ -0,0 +1,13 @@
/* global angular */
import dispatchEvent from './dispatch-custom-event';
/**
* @param {string} eventName
*/
export default function (eventName) {
angular.module('ng').run(['$rootScope', ($rootScope) => {
$rootScope.$on('$locationChangeSuccess', (e, newUrl, oldUrl) => {
dispatchEvent(eventName, { oldUrl, newUrl });
});
}]);
}
@@ -0,0 +1,17 @@
import arrayFrom from './array-from';
const defaultSelector = 'linearGradient, radialGradient, pattern, mask, clipPath';
/**
* @param {Element} svg
* @param {string} [selector]
* @return {Element}
*/
export default function (svg, selector = defaultSelector) {
arrayFrom(svg.querySelectorAll('symbol')).forEach((symbol) => {
arrayFrom(symbol.querySelectorAll(selector)).forEach((node) => {
symbol.parentNode.insertBefore(node, symbol);
});
});
return svg;
}
@@ -0,0 +1,10 @@
/**
* @param {Object} attrs
* @return {string}
*/
export default function (attrs) {
return Object.keys(attrs).map((attr) => {
const value = attrs[attr].toString().replace(/"/g, '&quot;');
return `${attr}="${value}"`;
}).join(' ');
}
+19
View File
@@ -0,0 +1,19 @@
/**
* @param {string} content
* @return {Element}
*/
export default function (content) {
const hasImportNode = !!document.importNode;
const doc = new DOMParser().parseFromString(content, 'image/svg+xml').documentElement;
/**
* Fix for browser which are throwing WrongDocumentError
* if you insert an element which is not part of the document
* @see http://stackoverflow.com/a/7986519/4624403
*/
if (hasImportNode) {
return document.importNode(doc, true);
}
return doc;
}
+20
View File
@@ -0,0 +1,20 @@
import arrayFrom from './array-from';
/**
* @param {NodeList} nodes
* @param {Function} [matcher]
* @return {Attr[]}
*/
export default function selectAttributes(nodes, matcher) {
const attrs = arrayFrom(nodes).reduce((acc, node) => {
if (!node.attributes) {
return acc;
}
const arrayfied = arrayFrom(node.attributes);
const matched = matcher ? arrayfied.filter(matcher) : arrayfied;
return acc.concat(matched);
}, []);
return attrs;
}
+20
View File
@@ -0,0 +1,20 @@
import arrayFrom from './array-from';
/**
* @param {NodeList|Node} nodes
* @param {boolean} [clone=true]
* @return {string}
*/
export default function (nodes, clone = true) {
const wrapper = document.createElement('div');
if (nodes instanceof NodeList) {
arrayFrom(nodes).forEach((node) => {
wrapper.appendChild(clone === true ? node.cloneNode(true) : node);
});
} else if (nodes instanceof Node) {
wrapper.appendChild(clone === true ? nodes.cloneNode(true) : nodes);
}
return wrapper.innerHTML;
}
+84
View File
@@ -0,0 +1,84 @@
import namespaces from 'svg-baker/namespaces';
import selectAttributes from './select-attributes';
import arrayFrom from './array-from';
const xLinkNS = namespaces.xlink.uri;
const xLinkAttrName = 'xlink:href';
// eslint-disable-next-line no-useless-escape
const specialUrlCharsPattern = /[{}|\\\^\[\]`"<>]/g;
function encoder(url) {
return url.replace(specialUrlCharsPattern, (match) => {
return `%${match[0].charCodeAt(0).toString(16).toUpperCase()}`;
});
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}
/**
* @param {NodeList} nodes
* @param {string} startsWith
* @param {string} replaceWith
* @return {NodeList}
*/
function updateReferences(nodes, startsWith, replaceWith) {
arrayFrom(nodes).forEach((node) => {
const href = node.getAttribute(xLinkAttrName);
if (href && href.indexOf(startsWith) === 0) {
const newUrl = href.replace(startsWith, replaceWith);
node.setAttributeNS(xLinkNS, xLinkAttrName, newUrl);
}
});
return nodes;
}
/**
* List of SVG attributes to update url() target in them
*/
const attList = [
'clipPath',
'colorProfile',
'src',
'cursor',
'fill',
'filter',
'marker',
'markerStart',
'markerMid',
'markerEnd',
'mask',
'stroke',
'style'
];
const attSelector = attList.map(attr => `[${attr}]`).join(',');
/**
* Update URLs in svg image (like `fill="url(...)"`) and update referencing elements
* @param {Element} svg
* @param {NodeList} references
* @param {string|RegExp} startsWith
* @param {string} replaceWith
* @return {void}
*
* @example
* const sprite = document.querySelector('svg.sprite');
* const usages = document.querySelectorAll('use');
* updateUrls(sprite, usages, '#', 'prefix#');
*/
export default function (svg, references, startsWith, replaceWith) {
const startsWithEncoded = encoder(startsWith);
const replaceWithEncoded = encoder(replaceWith);
const nodes = svg.querySelectorAll(attSelector);
const attrs = selectAttributes(nodes, ({ localName, value }) => {
return attList.indexOf(localName) !== -1 && value.indexOf(`url(${startsWithEncoded}`) !== -1;
});
attrs.forEach(attr => attr.value = attr.value.replace(new RegExp(escapeRegExp(startsWithEncoded), 'g'), replaceWithEncoded));
updateReferences(references, startsWithEncoded, replaceWithEncoded);
}
@@ -0,0 +1,21 @@
import merge from 'deepmerge';
import namespaces from 'svg-baker/namespaces';
import objectToAttrsString from './object-to-attrs-string';
const { svg, xlink } = namespaces;
const defaultAttrs = {
[svg.name]: svg.uri,
[xlink.name]: xlink.uri
};
/**
* @param {string} [content]
* @param {Object} [attributes]
* @return {string}
*/
export default function (content = '', attributes) {
const attrs = merge(defaultAttrs, attributes || {});
const attrsRendered = objectToAttrsString(attrs);
return `<svg ${attrsRendered}>${content}</svg>`;
}
+39
View File
@@ -0,0 +1,39 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.SpriteSymbol = factory());
}(this, (function () { 'use strict';
var SpriteSymbol = function SpriteSymbol(ref) {
var id = ref.id;
var viewBox = ref.viewBox;
var content = ref.content;
this.id = id;
this.viewBox = viewBox;
this.content = content;
};
/**
* @return {string}
*/
SpriteSymbol.prototype.stringify = function stringify () {
return this.content;
};
/**
* @return {string}
*/
SpriteSymbol.prototype.toString = function toString () {
return this.stringify();
};
SpriteSymbol.prototype.destroy = function destroy () {
var this$1 = this;
['id', 'viewBox', 'content'].forEach(function (prop) { return delete this$1[prop]; });
};
return SpriteSymbol;
})));