2018-05-13 10:47:00 +00:00
|
|
|
/*
|
2009-11-16 15:19:51 +00:00
|
|
|
* SVG Icon Loader 2.0
|
2009-11-09 18:59:53 +00:00
|
|
|
*
|
|
|
|
* jQuery Plugin for loading SVG icons from a single file
|
|
|
|
*
|
|
|
|
* Copyright (c) 2009 Alexis Deveria
|
|
|
|
* http://a.deveria.com
|
|
|
|
*
|
2012-09-16 18:53:27 +00:00
|
|
|
* MIT License
|
2009-11-09 18:59:53 +00:00
|
|
|
|
|
|
|
How to use:
|
|
|
|
|
|
|
|
1. Create the SVG master file that includes all icons:
|
|
|
|
|
2018-05-13 10:47:00 +00:00
|
|
|
The master SVG icon-containing file is an SVG file that contains
|
2009-11-16 15:19:51 +00:00
|
|
|
<g> elements. Each <g> element should contain the markup of an SVG
|
2018-05-13 10:47:00 +00:00
|
|
|
icon. The <g> element has an ID that should
|
|
|
|
correspond with the ID of the HTML element used on the page that should contain
|
2009-11-09 18:59:53 +00:00
|
|
|
or optionally be replaced by the icon. Additionally, one empty element should be
|
|
|
|
added at the end with id "svg_eof".
|
|
|
|
|
|
|
|
2. Optionally create fallback raster images for each SVG icon.
|
|
|
|
|
|
|
|
3. Include the jQuery and the SVG Icon Loader scripts on your page.
|
|
|
|
|
|
|
|
4. Run $.svgIcons() when the document is ready:
|
|
|
|
|
|
|
|
$.svgIcons( file [string], options [object literal]);
|
|
|
|
|
|
|
|
File is the location of a local SVG or SVGz file.
|
|
|
|
|
|
|
|
All options are optional and can include:
|
|
|
|
|
|
|
|
- 'w (number)': The icon widths
|
|
|
|
|
|
|
|
- 'h (number)': The icon heights
|
|
|
|
|
|
|
|
- 'fallback (object literal)': List of raster images with each
|
2018-05-18 06:23:36 +00:00
|
|
|
key being the SVG icon ID to replace, and the value the image file name.
|
2018-05-13 10:47:00 +00:00
|
|
|
|
2009-11-09 18:59:53 +00:00
|
|
|
- 'fallback_path (string)': The path to use for all images
|
2018-05-18 06:23:36 +00:00
|
|
|
listed under "fallback"
|
2018-05-13 10:47:00 +00:00
|
|
|
|
2009-11-09 18:59:53 +00:00
|
|
|
- 'replace (boolean)': If set to true, HTML elements will be replaced by,
|
2018-05-18 06:23:36 +00:00
|
|
|
rather than include the SVG icon.
|
2009-11-09 18:59:53 +00:00
|
|
|
|
|
|
|
- 'placement (object literal)': List with selectors for keys and SVG icon ids
|
2018-05-18 06:23:36 +00:00
|
|
|
as values. This provides a custom method of adding icons.
|
2009-11-09 18:59:53 +00:00
|
|
|
|
|
|
|
- 'resize (object literal)': List with selectors for keys and numbers
|
2018-05-18 06:23:36 +00:00
|
|
|
as values. This allows an easy way to resize specific icons.
|
2018-05-13 10:47:00 +00:00
|
|
|
|
|
|
|
- 'callback (function)': A function to call when all icons have been loaded.
|
2018-05-18 06:23:36 +00:00
|
|
|
Includes an object literal as its argument with as keys all icon IDs and the
|
|
|
|
icon as a jQuery object as its value.
|
2009-11-09 18:59:53 +00:00
|
|
|
|
|
|
|
- 'id_match (boolean)': Automatically attempt to match SVG icon ids with
|
2018-05-18 06:23:36 +00:00
|
|
|
corresponding HTML id (default: true)
|
2018-05-13 10:47:00 +00:00
|
|
|
|
2009-11-16 15:19:51 +00:00
|
|
|
- 'no_img (boolean)': Prevent attempting to convert the icon into an <img>
|
2018-05-18 06:23:36 +00:00
|
|
|
element (may be faster, help for browser consistency)
|
2009-11-16 15:19:51 +00:00
|
|
|
|
|
|
|
- 'svgz (boolean)': Indicate that the file is an SVGZ file, and thus not to
|
2018-05-18 06:23:36 +00:00
|
|
|
parse as XML. SVGZ files add compression benefits, but getting data from
|
|
|
|
them fails in Firefox 2 and older.
|
2009-11-09 18:59:53 +00:00
|
|
|
|
|
|
|
5. To access an icon at a later point without using the callback, use this:
|
2018-05-18 06:23:36 +00:00
|
|
|
$.getSvgIcon(id (string));
|
2009-11-09 18:59:53 +00:00
|
|
|
|
|
|
|
This will return the icon (as jQuery object) with a given ID.
|
2018-05-13 10:47:00 +00:00
|
|
|
|
2009-11-09 18:59:53 +00:00
|
|
|
6. To resize icons at a later point without using the callback, use this:
|
2018-05-18 06:23:36 +00:00
|
|
|
$.resizeSvgIcons(resizeOptions) (use the same way as the "resize" parameter)
|
2009-11-09 18:59:53 +00:00
|
|
|
|
|
|
|
Example usage #1:
|
|
|
|
|
|
|
|
$(function() {
|
2018-05-18 06:23:36 +00:00
|
|
|
$.svgIcons('my_icon_set.svg'); // The SVG file that contains all icons
|
|
|
|
// No options have been set, so all icons will automatically be inserted
|
|
|
|
// into HTML elements that match the same IDs.
|
2009-11-09 18:59:53 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Example usage #2:
|
|
|
|
|
|
|
|
$(function() {
|
2018-05-18 06:23:36 +00:00
|
|
|
$.svgIcons('my_icon_set.svg', { // The SVG file that contains all icons
|
2018-05-18 03:25:45 +00:00
|
|
|
callback (icons) { // Custom callback function that sets click
|
2018-05-18 06:23:36 +00:00
|
|
|
// events for each icon
|
|
|
|
$.each(icons, function(id, icon) {
|
|
|
|
icon.click(function() {
|
|
|
|
alert('You clicked on the icon with id ' + id);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}); //The SVG file that contains all icons
|
2009-11-09 18:59:53 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Example usage #3:
|
|
|
|
|
|
|
|
$(function() {
|
2018-05-18 06:23:36 +00:00
|
|
|
$.svgIcons('my_icon_set.svgz', { // The SVGZ file that contains all icons
|
|
|
|
w: 32, // All icons will be 32px wide
|
|
|
|
h: 32, // All icons will be 32px high
|
|
|
|
fallback_path: 'icons/', // All fallback files can be found here
|
|
|
|
fallback: {
|
|
|
|
'#open_icon': 'open.png', // The "open.png" will be appended to the
|
|
|
|
// HTML element with ID "open_icon"
|
|
|
|
'#close_icon': 'close.png',
|
|
|
|
'#save_icon': 'save.png'
|
|
|
|
},
|
|
|
|
placement: {'.open_icon','open'}, // The "open" icon will be added
|
|
|
|
// to all elements with class "open_icon"
|
2018-05-18 03:25:45 +00:00
|
|
|
resize () {
|
2018-05-18 06:23:36 +00:00
|
|
|
'#save_icon .svg_icon': 64 // The "save" icon will be resized to 64 x 64px
|
|
|
|
},
|
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
callback (icons) { // Sets background color for "close" icon
|
2018-05-18 06:23:36 +00:00
|
|
|
icons['close'].css('background','red');
|
|
|
|
},
|
|
|
|
|
|
|
|
svgz: true // Indicates that an SVGZ file is being used
|
|
|
|
|
|
|
|
})
|
2009-11-09 18:59:53 +00:00
|
|
|
});
|
|
|
|
*/
|
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
export default function ($) {
|
|
|
|
const svgIcons = {};
|
|
|
|
|
|
|
|
let fixIDs;
|
|
|
|
$.svgIcons = function (file, opts) {
|
|
|
|
const svgns = 'http://www.w3.org/2000/svg',
|
|
|
|
xlinkns = 'http://www.w3.org/1999/xlink',
|
|
|
|
iconW = opts.w || 24,
|
|
|
|
iconH = opts.h || 24;
|
|
|
|
let elems, svgdoc, testImg,
|
|
|
|
iconsMade = false, dataLoaded = false, loadAttempts = 0;
|
|
|
|
const isOpera = !!window.opera,
|
|
|
|
// ua = navigator.userAgent,
|
|
|
|
// isSafari = (ua.includes('Safari/') && !ua.includes('Chrome/')),
|
|
|
|
dataPre = 'data:image/svg+xml;charset=utf-8;base64,';
|
|
|
|
|
|
|
|
let dataEl;
|
|
|
|
if (opts.svgz) {
|
|
|
|
dataEl = $('<object data="' + file + '" type=image/svg+xml>').appendTo('body').hide();
|
|
|
|
try {
|
|
|
|
svgdoc = dataEl[0].contentDocument;
|
|
|
|
dataEl.load(getIcons);
|
|
|
|
getIcons(0, true); // Opera will not run "load" event if file is already cached
|
|
|
|
} catch (err1) {
|
|
|
|
useFallback();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const parser = new DOMParser();
|
|
|
|
$.ajax({
|
|
|
|
url: file,
|
|
|
|
dataType: 'string',
|
|
|
|
success (data) {
|
|
|
|
if (!data) {
|
|
|
|
$(useFallback);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
svgdoc = parser.parseFromString(data, 'text/xml');
|
2018-05-18 06:23:36 +00:00
|
|
|
$(function () {
|
2018-05-18 03:25:45 +00:00
|
|
|
getIcons('ajax');
|
2018-05-18 06:23:36 +00:00
|
|
|
});
|
2018-05-18 03:25:45 +00:00
|
|
|
},
|
|
|
|
error (err) {
|
|
|
|
// TODO: Fix Opera widget icon bug
|
|
|
|
if (window.opera) {
|
2018-05-18 06:23:36 +00:00
|
|
|
$(function () {
|
2018-05-18 03:25:45 +00:00
|
|
|
useFallback();
|
2018-05-18 06:23:36 +00:00
|
|
|
});
|
|
|
|
} else {
|
2018-05-18 03:25:45 +00:00
|
|
|
if (err.responseText) {
|
|
|
|
svgdoc = parser.parseFromString(err.responseText, 'text/xml');
|
|
|
|
|
|
|
|
if (!svgdoc.childNodes.length) {
|
|
|
|
$(useFallback);
|
|
|
|
}
|
|
|
|
$(function () {
|
|
|
|
getIcons('ajax');
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
$(useFallback);
|
|
|
|
}
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-18 03:25:45 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function getIcons (evt, noWait) {
|
|
|
|
if (evt !== 'ajax') {
|
|
|
|
if (dataLoaded) return;
|
|
|
|
// Webkit sometimes says svgdoc is undefined, other times
|
|
|
|
// it fails to load all nodes. Thus we must make sure the "eof"
|
|
|
|
// element is loaded.
|
|
|
|
svgdoc = dataEl[0].contentDocument; // Needed again for Webkit
|
|
|
|
const isReady = (svgdoc && svgdoc.getElementById('svg_eof'));
|
|
|
|
if (!isReady && !(noWait && isReady)) {
|
|
|
|
loadAttempts++;
|
|
|
|
if (loadAttempts < 50) {
|
|
|
|
setTimeout(getIcons, 20);
|
|
|
|
} else {
|
|
|
|
useFallback();
|
|
|
|
dataLoaded = true;
|
|
|
|
}
|
|
|
|
return;
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
2018-05-18 03:25:45 +00:00
|
|
|
dataLoaded = true;
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
elems = $(svgdoc.firstChild).children(); // .getElementsByTagName('foreignContent');
|
|
|
|
|
|
|
|
if (!opts.no_img) {
|
|
|
|
const testSrc = dataPre + 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNzUiIGhlaWdodD0iMjc1Ij48L3N2Zz4%3D';
|
|
|
|
|
|
|
|
testImg = $(new Image()).attr({
|
|
|
|
src: testSrc,
|
|
|
|
width: 0,
|
|
|
|
height: 0
|
|
|
|
}).appendTo('body')
|
|
|
|
.load(function () {
|
|
|
|
// Safari 4 crashes, Opera and Chrome don't
|
|
|
|
makeIcons(true);
|
|
|
|
}).error(function () {
|
|
|
|
makeIcons();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
setTimeout(function () {
|
|
|
|
if (!iconsMade) makeIcons();
|
|
|
|
}, 500);
|
|
|
|
}
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
const setIcon = function (target, icon, id, setID) {
|
|
|
|
if (isOpera) icon.css('visibility', 'hidden');
|
|
|
|
if (opts.replace) {
|
|
|
|
if (setID) icon.attr('id', id);
|
|
|
|
const cl = target.attr('class');
|
|
|
|
if (cl) icon.attr('class', 'svg_icon ' + cl);
|
|
|
|
target.replaceWith(icon);
|
|
|
|
} else {
|
|
|
|
target.append(icon);
|
|
|
|
}
|
|
|
|
if (isOpera) {
|
|
|
|
setTimeout(function () {
|
|
|
|
icon.removeAttr('style');
|
|
|
|
}, 1);
|
|
|
|
}
|
|
|
|
};
|
2018-05-18 06:23:36 +00:00
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
let holder;
|
|
|
|
const addIcon = function (icon, id) {
|
|
|
|
if (opts.id_match === undefined || opts.id_match !== false) {
|
|
|
|
setIcon(holder, icon, id, true);
|
|
|
|
}
|
|
|
|
svgIcons[id] = icon;
|
|
|
|
};
|
2018-05-18 06:23:36 +00:00
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
function makeIcons (toImage, fallback) {
|
|
|
|
if (iconsMade) return;
|
|
|
|
if (opts.no_img) toImage = false;
|
2018-05-18 06:23:36 +00:00
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
let tempHolder;
|
|
|
|
if (toImage) {
|
|
|
|
tempHolder = $(document.createElement('div'));
|
|
|
|
tempHolder.hide().appendTo('body');
|
|
|
|
}
|
|
|
|
if (fallback) {
|
|
|
|
const path = opts.fallback_path || '';
|
|
|
|
$.each(fallback, function (id, imgsrc) {
|
|
|
|
holder = $('#' + id);
|
|
|
|
const icon = $(new Image())
|
|
|
|
.attr({
|
- Breaking change: Rename config file to `svgedit-config-iife.js` (or for the module version, `svgedit-config-es.js`);
also expect one directory higher; incorporates #207 (@iuyiuy)
- Breaking change: Separate `extIconsPath` from `extPath` (not copying over icons)
- Breaking change: Don't reference `custom.css` in HTML; can instead be referenced in JavaScript through
the config file (provided in `svgedit-config-sample-iife.js`/`svgedit-config-sample-es.js` as `svgedit-custom.css` for
better namespacing); incorporates #207 (@iuyiuy)
- Breaking change: Remove minified jgraduate/spinbtn files (minified within Rollup routine)
- Fix: Zoom when scrolled; incorporates #169 (@AndrolGenhald), adapting for conventions; also allow avoidance when shift key pressed
- Fix: Update Atom feed reference in HTML
- Fixes related to recent commits: Some path and method name fixes needed, function order, missing methods, variable scope declaration, no need for DOMContentLoaded listeners in modules, switch back to non-default export, avoid trimming nullish, deal with mock tests, fix `math.matrixMultiply`, use jquery-svg where needed for array/SVG attributes; add babel-polyfill and defer script to imagelib; other misc. fixes
- Enhancement: Move config-sample.js out of `editor` directory
- Enhancement: For `callback`-style extensions, also provide config object; add following
to that object: buildCanvgCallback, canvg, decode64, encode64, executeAfterLoads, getTypeMap, isChrome, ieIE, NS, text2xml
- Enhancement: Complete ES6 modules work (extensions, locales, tests), along with Babel;
make Node build routine for converting modular source to non-modular,
use `loadStylesheets` for modular stylehsheet defining (but parallel loading);
- Enhancement: Add `stylesheets` config for modular but parallel stylesheet loading with `@default` option for simple inclusion/exclusion of defaults (if not going with default).
- Refactoring: Clean up `svg-editor.html`: consistent indents; avoid extra lbs, avoid long lines
- Refactoring: Avoid embedded API adding inline JavaScript listener
- Refactoring: Move layers and context code to `draw.js`
- Refactoring: Move `pathActions` from `svgcanvas.js` (though preserve aliases to these methods on `canvas`) and `convertPath` from `svgutils.js` to `path.js`
- Refactoring: Move `getStrokedBBox` from `svgcanvas.js` (while keeping an alias) to `svgutils.js` (as `getStrokedBBoxDefaultVisible` to avoid conflict with existing)
- Docs: Remove "dependencies" comments in code except where summarizing role of jQuery or a non-obvious dependency
- Refactoring/Linting: Enfore `no-extra-semi` and `quote-props` rules
- Refactoring: Further avoidance of quotes on properties (as possible)
- Refactoring: Use `class` in place of functions where intended as classes
- Refactoring: Consistency and granularity in extensions imports
- Testing: Update QUnit to 2.6.1 (node_modules) and Sinon to 5.0.8 (and add sinon-test at 2.1.3) and enforce eslint-plugin-qunit linting rules; update custom extensions
- Testing: Add node-static for automating (and accessing out-of-directory contents)
- Testing: Avoid HTML attributes for styling
- Testing: Add npm `test` script
- Testing: Comment out unused jQuery SVG test
- Testing: Add test1 and svgutils_performance_test to all tests page
- Testing: Due apparently to Path having not been a formal class, the test was calling it without `new`; refactored now with sufficient mock data to take into account it is a class
- npm: Update devDeps
- npm: Add html modules and config build to test script
2018-05-22 10:03:16 +00:00
|
|
|
class: 'svg_icon',
|
2018-05-18 03:25:45 +00:00
|
|
|
src: path + imgsrc,
|
- Breaking change: Rename config file to `svgedit-config-iife.js` (or for the module version, `svgedit-config-es.js`);
also expect one directory higher; incorporates #207 (@iuyiuy)
- Breaking change: Separate `extIconsPath` from `extPath` (not copying over icons)
- Breaking change: Don't reference `custom.css` in HTML; can instead be referenced in JavaScript through
the config file (provided in `svgedit-config-sample-iife.js`/`svgedit-config-sample-es.js` as `svgedit-custom.css` for
better namespacing); incorporates #207 (@iuyiuy)
- Breaking change: Remove minified jgraduate/spinbtn files (minified within Rollup routine)
- Fix: Zoom when scrolled; incorporates #169 (@AndrolGenhald), adapting for conventions; also allow avoidance when shift key pressed
- Fix: Update Atom feed reference in HTML
- Fixes related to recent commits: Some path and method name fixes needed, function order, missing methods, variable scope declaration, no need for DOMContentLoaded listeners in modules, switch back to non-default export, avoid trimming nullish, deal with mock tests, fix `math.matrixMultiply`, use jquery-svg where needed for array/SVG attributes; add babel-polyfill and defer script to imagelib; other misc. fixes
- Enhancement: Move config-sample.js out of `editor` directory
- Enhancement: For `callback`-style extensions, also provide config object; add following
to that object: buildCanvgCallback, canvg, decode64, encode64, executeAfterLoads, getTypeMap, isChrome, ieIE, NS, text2xml
- Enhancement: Complete ES6 modules work (extensions, locales, tests), along with Babel;
make Node build routine for converting modular source to non-modular,
use `loadStylesheets` for modular stylehsheet defining (but parallel loading);
- Enhancement: Add `stylesheets` config for modular but parallel stylesheet loading with `@default` option for simple inclusion/exclusion of defaults (if not going with default).
- Refactoring: Clean up `svg-editor.html`: consistent indents; avoid extra lbs, avoid long lines
- Refactoring: Avoid embedded API adding inline JavaScript listener
- Refactoring: Move layers and context code to `draw.js`
- Refactoring: Move `pathActions` from `svgcanvas.js` (though preserve aliases to these methods on `canvas`) and `convertPath` from `svgutils.js` to `path.js`
- Refactoring: Move `getStrokedBBox` from `svgcanvas.js` (while keeping an alias) to `svgutils.js` (as `getStrokedBBoxDefaultVisible` to avoid conflict with existing)
- Docs: Remove "dependencies" comments in code except where summarizing role of jQuery or a non-obvious dependency
- Refactoring/Linting: Enfore `no-extra-semi` and `quote-props` rules
- Refactoring: Further avoidance of quotes on properties (as possible)
- Refactoring: Use `class` in place of functions where intended as classes
- Refactoring: Consistency and granularity in extensions imports
- Testing: Update QUnit to 2.6.1 (node_modules) and Sinon to 5.0.8 (and add sinon-test at 2.1.3) and enforce eslint-plugin-qunit linting rules; update custom extensions
- Testing: Add node-static for automating (and accessing out-of-directory contents)
- Testing: Avoid HTML attributes for styling
- Testing: Add npm `test` script
- Testing: Comment out unused jQuery SVG test
- Testing: Add test1 and svgutils_performance_test to all tests page
- Testing: Due apparently to Path having not been a formal class, the test was calling it without `new`; refactored now with sufficient mock data to take into account it is a class
- npm: Update devDeps
- npm: Add html modules and config build to test script
2018-05-22 10:03:16 +00:00
|
|
|
width: iconW,
|
|
|
|
height: iconH,
|
|
|
|
alt: 'icon'
|
2018-05-18 03:25:45 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
addIcon(icon, id);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
const len = elems.length;
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
const elem = elems[i];
|
|
|
|
const {id} = elem;
|
|
|
|
if (id === 'svg_eof') break;
|
|
|
|
holder = $('#' + id);
|
|
|
|
const svgroot = document.createElementNS(svgns, 'svg');
|
|
|
|
// Per https://www.w3.org/TR/xml-names11/#defaulting, the namespace for
|
|
|
|
// attributes should have no value.
|
|
|
|
svgroot.setAttributeNS(null, 'viewBox', [0, 0, iconW, iconH].join(' '));
|
|
|
|
|
|
|
|
let svg = elem.getElementsByTagNameNS(svgns, 'svg')[0];
|
|
|
|
|
|
|
|
// Make flexible by converting width/height to viewBox
|
|
|
|
const w = svg.getAttribute('width');
|
|
|
|
const h = svg.getAttribute('height');
|
|
|
|
svg.removeAttribute('width');
|
|
|
|
svg.removeAttribute('height');
|
|
|
|
|
|
|
|
const vb = svg.getAttribute('viewBox');
|
|
|
|
if (!vb) {
|
|
|
|
svg.setAttribute('viewBox', [0, 0, w, h].join(' '));
|
|
|
|
}
|
2018-05-18 06:23:36 +00:00
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
// Not using jQuery to be a bit faster
|
|
|
|
svgroot.setAttribute('xmlns', svgns);
|
|
|
|
svgroot.setAttribute('width', iconW);
|
|
|
|
svgroot.setAttribute('height', iconH);
|
|
|
|
svgroot.setAttribute('xmlns:xlink', xlinkns);
|
|
|
|
svgroot.setAttribute('class', 'svg_icon');
|
|
|
|
|
|
|
|
// Without cloning, Firefox will make another GET request.
|
|
|
|
// With cloning, causes issue in Opera/Win/Non-EN
|
|
|
|
if (!isOpera) svg = svg.cloneNode(true);
|
|
|
|
|
|
|
|
svgroot.appendChild(svg);
|
|
|
|
|
|
|
|
let icon;
|
|
|
|
if (toImage) {
|
|
|
|
tempHolder.empty().append(svgroot);
|
|
|
|
const str = dataPre + encode64(unescape(encodeURIComponent(new XMLSerializer().serializeToString(svgroot))));
|
|
|
|
icon = $(new Image())
|
- Breaking change: Rename config file to `svgedit-config-iife.js` (or for the module version, `svgedit-config-es.js`);
also expect one directory higher; incorporates #207 (@iuyiuy)
- Breaking change: Separate `extIconsPath` from `extPath` (not copying over icons)
- Breaking change: Don't reference `custom.css` in HTML; can instead be referenced in JavaScript through
the config file (provided in `svgedit-config-sample-iife.js`/`svgedit-config-sample-es.js` as `svgedit-custom.css` for
better namespacing); incorporates #207 (@iuyiuy)
- Breaking change: Remove minified jgraduate/spinbtn files (minified within Rollup routine)
- Fix: Zoom when scrolled; incorporates #169 (@AndrolGenhald), adapting for conventions; also allow avoidance when shift key pressed
- Fix: Update Atom feed reference in HTML
- Fixes related to recent commits: Some path and method name fixes needed, function order, missing methods, variable scope declaration, no need for DOMContentLoaded listeners in modules, switch back to non-default export, avoid trimming nullish, deal with mock tests, fix `math.matrixMultiply`, use jquery-svg where needed for array/SVG attributes; add babel-polyfill and defer script to imagelib; other misc. fixes
- Enhancement: Move config-sample.js out of `editor` directory
- Enhancement: For `callback`-style extensions, also provide config object; add following
to that object: buildCanvgCallback, canvg, decode64, encode64, executeAfterLoads, getTypeMap, isChrome, ieIE, NS, text2xml
- Enhancement: Complete ES6 modules work (extensions, locales, tests), along with Babel;
make Node build routine for converting modular source to non-modular,
use `loadStylesheets` for modular stylehsheet defining (but parallel loading);
- Enhancement: Add `stylesheets` config for modular but parallel stylesheet loading with `@default` option for simple inclusion/exclusion of defaults (if not going with default).
- Refactoring: Clean up `svg-editor.html`: consistent indents; avoid extra lbs, avoid long lines
- Refactoring: Avoid embedded API adding inline JavaScript listener
- Refactoring: Move layers and context code to `draw.js`
- Refactoring: Move `pathActions` from `svgcanvas.js` (though preserve aliases to these methods on `canvas`) and `convertPath` from `svgutils.js` to `path.js`
- Refactoring: Move `getStrokedBBox` from `svgcanvas.js` (while keeping an alias) to `svgutils.js` (as `getStrokedBBoxDefaultVisible` to avoid conflict with existing)
- Docs: Remove "dependencies" comments in code except where summarizing role of jQuery or a non-obvious dependency
- Refactoring/Linting: Enfore `no-extra-semi` and `quote-props` rules
- Refactoring: Further avoidance of quotes on properties (as possible)
- Refactoring: Use `class` in place of functions where intended as classes
- Refactoring: Consistency and granularity in extensions imports
- Testing: Update QUnit to 2.6.1 (node_modules) and Sinon to 5.0.8 (and add sinon-test at 2.1.3) and enforce eslint-plugin-qunit linting rules; update custom extensions
- Testing: Add node-static for automating (and accessing out-of-directory contents)
- Testing: Avoid HTML attributes for styling
- Testing: Add npm `test` script
- Testing: Comment out unused jQuery SVG test
- Testing: Add test1 and svgutils_performance_test to all tests page
- Testing: Due apparently to Path having not been a formal class, the test was calling it without `new`; refactored now with sufficient mock data to take into account it is a class
- npm: Update devDeps
- npm: Add html modules and config build to test script
2018-05-22 10:03:16 +00:00
|
|
|
.attr({class: 'svg_icon', src: str});
|
2018-05-18 03:25:45 +00:00
|
|
|
} else {
|
|
|
|
icon = fixIDs($(svgroot), i);
|
|
|
|
}
|
|
|
|
addIcon(icon, id);
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
if (opts.placement) {
|
|
|
|
$.each(opts.placement, function (sel, id) {
|
|
|
|
if (!svgIcons[id]) return;
|
|
|
|
$(sel).each(function (i) {
|
|
|
|
let copy = svgIcons[id].clone();
|
|
|
|
if (i > 0 && !toImage) copy = fixIDs(copy, i, true);
|
|
|
|
setIcon($(this), copy, id);
|
|
|
|
});
|
2018-05-18 06:23:36 +00:00
|
|
|
});
|
2018-05-18 03:25:45 +00:00
|
|
|
}
|
|
|
|
if (!fallback) {
|
|
|
|
if (toImage) tempHolder.remove();
|
|
|
|
if (dataEl) dataEl.remove();
|
|
|
|
if (testImg) testImg.remove();
|
|
|
|
}
|
|
|
|
if (opts.resize) $.resizeSvgIcons(opts.resize);
|
|
|
|
iconsMade = true;
|
|
|
|
|
|
|
|
if (opts.callback) opts.callback(svgIcons);
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
fixIDs = function (svgEl, svgNum, force) {
|
|
|
|
const defs = svgEl.find('defs');
|
|
|
|
if (!defs.length) return svgEl;
|
|
|
|
|
|
|
|
let idElems;
|
|
|
|
if (isOpera) {
|
|
|
|
idElems = defs.find('*').filter(function () {
|
|
|
|
return !!this.id;
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
idElems = defs.find('[id]');
|
|
|
|
}
|
|
|
|
|
|
|
|
const allElems = svgEl[0].getElementsByTagName('*'), len = allElems.length;
|
|
|
|
|
|
|
|
idElems.each(function (i) {
|
|
|
|
const {id} = this;
|
|
|
|
/*
|
|
|
|
const noDupes = ($(svgdoc).find('#' + id).length <= 1);
|
|
|
|
if (isOpera) noDupes = false; // Opera didn't clone svgEl, so not reliable
|
|
|
|
if(!force && noDupes) return;
|
|
|
|
*/
|
|
|
|
const newId = 'x' + id + svgNum + i;
|
|
|
|
this.id = newId;
|
|
|
|
|
|
|
|
const oldVal = 'url(#' + id + ')';
|
|
|
|
const newVal = 'url(#' + newId + ')';
|
|
|
|
|
|
|
|
// Selector method, possibly faster but fails in Opera / jQuery 1.4.3
|
|
|
|
// svgEl.find('[fill="url(#' + id + ')"]').each(function() {
|
|
|
|
// this.setAttribute('fill', 'url(#' + newId + ')');
|
|
|
|
// }).end().find('[stroke="url(#' + id + ')"]').each(function() {
|
|
|
|
// this.setAttribute('stroke', 'url(#' + newId + ')');
|
|
|
|
// }).end().find('use').each(function() {
|
|
|
|
// if(this.getAttribute('xlink:href') == '#' + id) {
|
|
|
|
// this.setAttributeNS(xlinkns,'href','#' + newId);
|
|
|
|
// }
|
|
|
|
// }).end().find('[filter="url(#' + id + ')"]').each(function() {
|
|
|
|
// this.setAttribute('filter', 'url(#' + newId + ')');
|
|
|
|
// });
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
const elem = allElems[i];
|
|
|
|
if (elem.getAttribute('fill') === oldVal) {
|
|
|
|
elem.setAttribute('fill', newVal);
|
|
|
|
}
|
|
|
|
if (elem.getAttribute('stroke') === oldVal) {
|
|
|
|
elem.setAttribute('stroke', newVal);
|
|
|
|
}
|
|
|
|
if (elem.getAttribute('filter') === oldVal) {
|
|
|
|
elem.setAttribute('filter', newVal);
|
|
|
|
}
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
2018-05-18 03:25:45 +00:00
|
|
|
});
|
|
|
|
return svgEl;
|
|
|
|
};
|
|
|
|
|
|
|
|
function useFallback () {
|
|
|
|
if (file.includes('.svgz')) {
|
|
|
|
const regFile = file.replace('.svgz', '.svg');
|
|
|
|
if (window.console) {
|
|
|
|
console.log('.svgz failed, trying with .svg');
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
2018-05-18 03:25:45 +00:00
|
|
|
$.svgIcons(regFile, opts);
|
|
|
|
} else if (opts.fallback) {
|
|
|
|
makeIcons(false, opts.fallback);
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
2018-05-18 03:25:45 +00:00
|
|
|
}
|
2018-05-18 06:23:36 +00:00
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
function encode64 (input) {
|
|
|
|
// base64 strings are 4/3 larger than the original string
|
|
|
|
if (window.btoa) return window.btoa(input);
|
|
|
|
const _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
|
|
const output = new Array(Math.floor((input.length + 2) / 3) * 4);
|
|
|
|
|
|
|
|
let i = 0, p = 0;
|
|
|
|
do {
|
|
|
|
const chr1 = input.charCodeAt(i++);
|
|
|
|
const chr2 = input.charCodeAt(i++);
|
|
|
|
const chr3 = input.charCodeAt(i++);
|
|
|
|
|
|
|
|
const enc1 = chr1 >> 2;
|
|
|
|
const enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
|
|
|
|
|
|
|
let enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
|
|
|
let enc4 = chr3 & 63;
|
|
|
|
if (isNaN(chr2)) {
|
|
|
|
enc3 = enc4 = 64;
|
|
|
|
} else if (isNaN(chr3)) {
|
|
|
|
enc4 = 64;
|
|
|
|
}
|
|
|
|
|
|
|
|
output[p++] = _keyStr.charAt(enc1);
|
|
|
|
output[p++] = _keyStr.charAt(enc2);
|
|
|
|
output[p++] = _keyStr.charAt(enc3);
|
|
|
|
output[p++] = _keyStr.charAt(enc4);
|
|
|
|
} while (i < input.length);
|
|
|
|
|
|
|
|
return output.join('');
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
2018-05-18 03:25:45 +00:00
|
|
|
};
|
2018-05-18 06:23:36 +00:00
|
|
|
|
2018-05-18 03:25:45 +00:00
|
|
|
$.getSvgIcon = function (id, uniqueClone) {
|
|
|
|
let icon = svgIcons[id];
|
|
|
|
if (uniqueClone && icon) {
|
|
|
|
icon = fixIDs(icon, 0, true).clone(true);
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
2018-05-18 03:25:45 +00:00
|
|
|
return icon;
|
|
|
|
};
|
|
|
|
|
|
|
|
$.resizeSvgIcons = function (obj) {
|
|
|
|
// FF2 and older don't detect .svg_icon, so we change it detect svg elems instead
|
|
|
|
const changeSel = !$('.svg_icon:first').length;
|
|
|
|
$.each(obj, function (sel, size) {
|
|
|
|
const arr = Array.isArray(size);
|
|
|
|
const w = arr ? size[0] : size,
|
|
|
|
h = arr ? size[1] : size;
|
|
|
|
if (changeSel) {
|
|
|
|
sel = sel.replace(/\.svg_icon/g, 'svg');
|
2018-05-18 06:23:36 +00:00
|
|
|
}
|
2018-05-18 03:25:45 +00:00
|
|
|
$(sel).each(function () {
|
|
|
|
this.setAttribute('width', w);
|
|
|
|
this.setAttribute('height', h);
|
|
|
|
if (window.opera && window.widget) {
|
|
|
|
this.parentNode.style.width = w + 'px';
|
|
|
|
this.parentNode.style.height = h + 'px';
|
|
|
|
}
|
|
|
|
});
|
2018-05-18 06:23:36 +00:00
|
|
|
});
|
2018-05-18 03:25:45 +00:00
|
|
|
};
|
|
|
|
return $;
|
|
|
|
}
|