2014-01-31 02:13:37 +00:00
|
|
|
/*globals $, svgedit, unescape, DOMParser, ActiveXObject, getStrokedBBox*/
|
|
|
|
/*jslint vars: true, eqeq: true, bitwise: true, continue: true, forin: true*/
|
2010-10-26 02:48:36 +00:00
|
|
|
/**
|
2010-11-05 17:00:32 +00:00
|
|
|
* Package: svgedit.utilities
|
2010-10-26 02:48:36 +00:00
|
|
|
*
|
2012-09-16 18:53:27 +00:00
|
|
|
* Licensed under the MIT License
|
2010-10-26 02:48:36 +00:00
|
|
|
*
|
|
|
|
* Copyright(c) 2010 Alexis Deveria
|
|
|
|
* Copyright(c) 2010 Jeff Schiller
|
|
|
|
*/
|
|
|
|
|
2010-10-26 16:33:44 +00:00
|
|
|
// Dependencies:
|
|
|
|
// 1) jQuery
|
2015-11-05 03:25:30 +00:00
|
|
|
// 2) pathseg.js
|
|
|
|
// 3) browser.js
|
|
|
|
// 4) svgtransformlist.js
|
|
|
|
// 5) units.js
|
2010-10-26 02:48:36 +00:00
|
|
|
|
2014-05-22 10:21:29 +00:00
|
|
|
(function(undef) {'use strict';
|
2010-10-26 16:33:44 +00:00
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
if (!svgedit.utilities) {
|
|
|
|
svgedit.utilities = {};
|
2010-10-28 06:08:45 +00:00
|
|
|
}
|
2010-10-26 02:48:36 +00:00
|
|
|
|
2010-10-29 05:09:39 +00:00
|
|
|
// Constants
|
|
|
|
|
2010-10-26 02:48:36 +00:00
|
|
|
// String used to encode base64.
|
2010-12-02 17:14:24 +00:00
|
|
|
var KEYSTR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
2013-02-16 15:02:26 +00:00
|
|
|
var NS = svgedit.NS;
|
2010-10-26 02:48:36 +00:00
|
|
|
|
2010-11-05 15:29:30 +00:00
|
|
|
// Much faster than running getBBox() every time
|
|
|
|
var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use';
|
|
|
|
var visElems_arr = visElems.split(',');
|
|
|
|
//var hidElems = 'clipPath,defs,desc,feGaussianBlur,filter,linearGradient,marker,mask,metadata,pattern,radialGradient,stop,switch,symbol,title,textPath';
|
|
|
|
|
2010-12-02 17:14:24 +00:00
|
|
|
var editorContext_ = null;
|
2011-02-04 08:02:46 +00:00
|
|
|
var domdoc_ = null;
|
|
|
|
var domcontainer_ = null;
|
|
|
|
var svgroot_ = null;
|
2010-12-02 17:14:24 +00:00
|
|
|
|
|
|
|
svgedit.utilities.init = function(editorContext) {
|
|
|
|
editorContext_ = editorContext;
|
2011-02-04 08:02:46 +00:00
|
|
|
domdoc_ = editorContext.getDOMDocument();
|
|
|
|
domcontainer_ = editorContext.getDOMContainer();
|
|
|
|
svgroot_ = editorContext.getSVGRoot();
|
2010-12-02 17:14:24 +00:00
|
|
|
};
|
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
// Function: svgedit.utilities.toXml
|
2010-10-26 02:48:36 +00:00
|
|
|
// Converts characters in a string to XML-friendly entities.
|
|
|
|
//
|
2013-02-15 17:02:24 +00:00
|
|
|
// Example: '&' becomes '&'
|
2010-10-26 02:48:36 +00:00
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// str - The string to be converted
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// The converted string
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.toXml = function(str) {
|
2014-02-12 10:10:56 +00:00
|
|
|
// ' is ok in XML, but not HTML
|
|
|
|
// > does not normally need escaping, though it can if within a CDATA expression (and preceded by "]]")
|
|
|
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/, ''');
|
2010-10-26 02:48:36 +00:00
|
|
|
};
|
2013-02-15 23:05:23 +00:00
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
// Function: svgedit.utilities.fromXml
|
2013-02-15 23:05:23 +00:00
|
|
|
// Converts XML entities in a string to single characters.
|
2013-02-15 17:02:24 +00:00
|
|
|
// Example: '&' becomes '&'
|
2010-10-26 02:48:36 +00:00
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// str - The string to be converted
|
|
|
|
//
|
2013-02-15 23:05:23 +00:00
|
|
|
// Returns:
|
2010-10-26 02:48:36 +00:00
|
|
|
// The converted string
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.fromXml = function(str) {
|
2010-10-26 02:48:36 +00:00
|
|
|
return $('<p/>').html(str).text();
|
|
|
|
};
|
|
|
|
|
|
|
|
// This code was written by Tyler Akins and has been placed in the
|
|
|
|
// public domain. It would be nice if you left this header intact.
|
|
|
|
// Base64 code from Tyler Akins -- http://rumkin.com
|
|
|
|
|
|
|
|
// schiller: Removed string concatenation in favour of Array.join() optimization,
|
2014-01-31 02:13:37 +00:00
|
|
|
// also precalculate the size of the array needed.
|
2010-10-26 02:48:36 +00:00
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
// Function: svgedit.utilities.encode64
|
2010-10-26 02:48:36 +00:00
|
|
|
// Converts a string to base64
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.encode64 = function(input) {
|
2010-10-26 02:48:36 +00:00
|
|
|
// base64 strings are 4/3 larger than the original string
|
2014-02-01 16:13:51 +00:00
|
|
|
input = svgedit.utilities.encodeUTF8(input); // convert non-ASCII characters
|
|
|
|
// input = svgedit.utilities.convertToXMLReferences(input);
|
|
|
|
if (window.btoa) {
|
|
|
|
return window.btoa(input); // Use native if available
|
2014-05-22 10:21:29 +00:00
|
|
|
}
|
|
|
|
var output = [];
|
|
|
|
output.length = Math.floor( (input.length + 2) / 3 ) * 4;
|
2010-10-26 02:48:36 +00:00
|
|
|
var chr1, chr2, chr3;
|
|
|
|
var enc1, enc2, enc3, enc4;
|
|
|
|
var i = 0, p = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
chr1 = input.charCodeAt(i++);
|
|
|
|
chr2 = input.charCodeAt(i++);
|
|
|
|
chr3 = input.charCodeAt(i++);
|
|
|
|
|
|
|
|
enc1 = chr1 >> 2;
|
|
|
|
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
|
|
|
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
|
|
|
enc4 = chr3 & 63;
|
|
|
|
|
|
|
|
if (isNaN(chr2)) {
|
|
|
|
enc3 = enc4 = 64;
|
|
|
|
} else if (isNaN(chr3)) {
|
|
|
|
enc4 = 64;
|
|
|
|
}
|
|
|
|
|
2010-10-29 05:09:39 +00:00
|
|
|
output[p++] = KEYSTR.charAt(enc1);
|
|
|
|
output[p++] = KEYSTR.charAt(enc2);
|
|
|
|
output[p++] = KEYSTR.charAt(enc3);
|
|
|
|
output[p++] = KEYSTR.charAt(enc4);
|
2010-10-26 02:48:36 +00:00
|
|
|
} while (i < input.length);
|
|
|
|
|
|
|
|
return output.join('');
|
|
|
|
};
|
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
// Function: svgedit.utilities.decode64
|
2010-10-26 02:48:36 +00:00
|
|
|
// Converts a string from base64
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.decode64 = function(input) {
|
2014-01-31 00:06:50 +00:00
|
|
|
if(window.atob) {
|
2014-06-12 23:42:27 +00:00
|
|
|
return svgedit.utilities.decodeUTF8(window.atob(input));
|
2014-01-31 00:06:50 +00:00
|
|
|
}
|
2013-02-15 17:02:24 +00:00
|
|
|
var output = '';
|
|
|
|
var chr1, chr2, chr3 = '';
|
|
|
|
var enc1, enc2, enc3, enc4 = '';
|
2010-10-26 02:48:36 +00:00
|
|
|
var i = 0;
|
|
|
|
|
2013-02-19 11:18:04 +00:00
|
|
|
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
|
|
|
|
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
|
2010-10-26 02:48:36 +00:00
|
|
|
|
2013-02-19 11:18:04 +00:00
|
|
|
do {
|
2010-10-29 05:09:39 +00:00
|
|
|
enc1 = KEYSTR.indexOf(input.charAt(i++));
|
|
|
|
enc2 = KEYSTR.indexOf(input.charAt(i++));
|
|
|
|
enc3 = KEYSTR.indexOf(input.charAt(i++));
|
|
|
|
enc4 = KEYSTR.indexOf(input.charAt(i++));
|
2010-10-26 02:48:36 +00:00
|
|
|
|
|
|
|
chr1 = (enc1 << 2) | (enc2 >> 4);
|
|
|
|
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
|
|
|
chr3 = ((enc3 & 3) << 6) | enc4;
|
|
|
|
|
|
|
|
output = output + String.fromCharCode(chr1);
|
|
|
|
|
|
|
|
if (enc3 != 64) {
|
2013-02-19 11:18:04 +00:00
|
|
|
output = output + String.fromCharCode(chr2);
|
2010-10-26 02:48:36 +00:00
|
|
|
}
|
|
|
|
if (enc4 != 64) {
|
2013-02-19 11:18:04 +00:00
|
|
|
output = output + String.fromCharCode(chr3);
|
2010-10-26 02:48:36 +00:00
|
|
|
}
|
|
|
|
|
2013-02-15 17:02:24 +00:00
|
|
|
chr1 = chr2 = chr3 = '';
|
|
|
|
enc1 = enc2 = enc3 = enc4 = '';
|
2010-10-26 02:48:36 +00:00
|
|
|
|
2013-02-19 11:18:04 +00:00
|
|
|
} while (i < input.length);
|
2014-06-12 23:42:27 +00:00
|
|
|
return svgedit.utilities.decodeUTF8(output);
|
|
|
|
};
|
|
|
|
|
|
|
|
svgedit.utilities.decodeUTF8 = function (argString) {
|
|
|
|
return decodeURIComponent(escape(argString));
|
2010-10-26 02:48:36 +00:00
|
|
|
};
|
|
|
|
|
2014-02-01 16:13:51 +00:00
|
|
|
// codedread:does not seem to work with webkit-based browsers on OSX // Brettz9: please test again as function upgraded
|
|
|
|
svgedit.utilities.encodeUTF8 = function (argString) {
|
2014-06-12 23:42:27 +00:00
|
|
|
return unescape(encodeURIComponent(argString));
|
2014-02-01 16:13:51 +00:00
|
|
|
};
|
2010-10-26 02:48:36 +00:00
|
|
|
|
2013-02-15 23:05:23 +00:00
|
|
|
// Function: svgedit.utilities.convertToXMLReferences
|
2010-10-26 02:48:36 +00:00
|
|
|
// Converts a string to use XML references
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.convertToXMLReferences = function(input) {
|
2014-01-31 02:13:37 +00:00
|
|
|
var n,
|
|
|
|
output = '';
|
|
|
|
for (n = 0; n < input.length; n++){
|
2010-10-26 02:48:36 +00:00
|
|
|
var c = input.charCodeAt(n);
|
|
|
|
if (c < 128) {
|
|
|
|
output += input[n];
|
2010-11-12 19:08:29 +00:00
|
|
|
} else if(c > 127) {
|
2013-02-15 17:02:24 +00:00
|
|
|
output += ('&#' + c + ';');
|
2010-10-26 02:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
};
|
|
|
|
|
2011-02-02 17:28:43 +00:00
|
|
|
// Function: svgedit.utilities.text2xml
|
2010-10-26 02:48:36 +00:00
|
|
|
// Cross-browser compatible method of converting a string to an XML tree
|
|
|
|
// found this function here: http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.text2xml = function(sXML) {
|
2010-10-26 02:48:36 +00:00
|
|
|
if(sXML.indexOf('<svg:svg') >= 0) {
|
|
|
|
sXML = sXML.replace(/<(\/?)svg:/g, '<$1').replace('xmlns:svg', 'xmlns');
|
|
|
|
}
|
|
|
|
|
2014-01-31 02:13:37 +00:00
|
|
|
var out, dXML;
|
2010-10-26 02:48:36 +00:00
|
|
|
try{
|
2014-01-31 02:13:37 +00:00
|
|
|
dXML = (window.DOMParser)?new DOMParser():new ActiveXObject('Microsoft.XMLDOM');
|
2010-10-26 02:48:36 +00:00
|
|
|
dXML.async = false;
|
2013-02-15 23:05:23 +00:00
|
|
|
} catch(e){
|
|
|
|
throw new Error('XML Parser could not be instantiated');
|
2013-02-19 11:18:04 +00:00
|
|
|
}
|
2010-10-26 02:48:36 +00:00
|
|
|
try{
|
2014-01-31 02:13:37 +00:00
|
|
|
if (dXML.loadXML) {
|
|
|
|
out = (dXML.loadXML(sXML)) ? dXML : false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
out = dXML.parseFromString(sXML, 'text/xml');
|
|
|
|
}
|
2010-10-26 02:48:36 +00:00
|
|
|
}
|
2014-01-31 02:13:37 +00:00
|
|
|
catch(e2){ throw new Error('Error parsing XML string'); }
|
2010-10-26 02:48:36 +00:00
|
|
|
return out;
|
|
|
|
};
|
|
|
|
|
2011-02-02 17:28:43 +00:00
|
|
|
// Function: svgedit.utilities.bboxToObj
|
2010-10-26 02:48:36 +00:00
|
|
|
// Converts a SVGRect into an object.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// bbox - a SVGRect
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// An object with properties names x, y, width, height.
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.bboxToObj = function(bbox) {
|
2010-10-26 02:48:36 +00:00
|
|
|
return {
|
|
|
|
x: bbox.x,
|
|
|
|
y: bbox.y,
|
|
|
|
width: bbox.width,
|
|
|
|
height: bbox.height
|
2013-02-19 11:18:04 +00:00
|
|
|
};
|
2010-10-26 02:48:36 +00:00
|
|
|
};
|
2010-10-26 16:33:44 +00:00
|
|
|
|
2011-02-02 17:28:43 +00:00
|
|
|
// Function: svgedit.utilities.walkTree
|
2010-10-26 17:11:23 +00:00
|
|
|
// Walks the tree and executes the callback on each element in a top-down fashion
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// elem - DOM element to traverse
|
|
|
|
// cbFn - Callback function to run on each element
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.walkTree = function(elem, cbFn){
|
2010-10-26 17:11:23 +00:00
|
|
|
if (elem && elem.nodeType == 1) {
|
|
|
|
cbFn(elem);
|
|
|
|
var i = elem.childNodes.length;
|
|
|
|
while (i--) {
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.walkTree(elem.childNodes.item(i), cbFn);
|
2010-10-26 17:11:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-02-02 17:28:43 +00:00
|
|
|
// Function: svgedit.utilities.walkTreePost
|
2010-10-26 17:11:23 +00:00
|
|
|
// Walks the tree and executes the callback on each element in a depth-first fashion
|
|
|
|
// TODO: FIXME: Shouldn't this be calling walkTreePost?
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// elem - DOM element to traverse
|
|
|
|
// cbFn - Callback function to run on each element
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.walkTreePost = function(elem, cbFn) {
|
2010-10-26 17:11:23 +00:00
|
|
|
if (elem && elem.nodeType == 1) {
|
|
|
|
var i = elem.childNodes.length;
|
|
|
|
while (i--) {
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.walkTree(elem.childNodes.item(i), cbFn);
|
2010-10-26 17:11:23 +00:00
|
|
|
}
|
|
|
|
cbFn(elem);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
// Function: svgedit.utilities.getUrlFromAttr
|
2013-02-15 23:05:23 +00:00
|
|
|
// Extracts the URL from the url(...) syntax of some attributes.
|
2010-10-26 17:11:23 +00:00
|
|
|
// Three variants:
|
2013-02-19 11:18:04 +00:00
|
|
|
// * <circle fill="url(someFile.svg#foo)" />
|
2010-10-26 17:11:23 +00:00
|
|
|
// * <circle fill="url('someFile.svg#foo')" />
|
|
|
|
// * <circle fill='url("someFile.svg#foo")' />
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// attrVal - The attribute value as a string
|
2013-02-15 23:05:23 +00:00
|
|
|
//
|
2010-10-26 17:11:23 +00:00
|
|
|
// Returns:
|
|
|
|
// String with just the URL, like someFile.svg#foo
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.getUrlFromAttr = function(attrVal) {
|
2013-02-15 23:05:23 +00:00
|
|
|
if (attrVal) {
|
2010-10-26 17:11:23 +00:00
|
|
|
// url("#somegrad")
|
|
|
|
if (attrVal.indexOf('url("') === 0) {
|
2013-02-15 16:51:48 +00:00
|
|
|
return attrVal.substring(5, attrVal.indexOf('"',6));
|
2010-10-26 17:11:23 +00:00
|
|
|
}
|
|
|
|
// url('#somegrad')
|
2014-01-31 02:13:37 +00:00
|
|
|
if (attrVal.indexOf("url('") === 0) {
|
2013-02-15 16:51:48 +00:00
|
|
|
return attrVal.substring(5, attrVal.indexOf("'",6));
|
2010-10-26 17:11:23 +00:00
|
|
|
}
|
2014-01-31 02:13:37 +00:00
|
|
|
if (attrVal.indexOf("url(") === 0) {
|
2013-02-15 16:51:48 +00:00
|
|
|
return attrVal.substring(4, attrVal.indexOf(')'));
|
2010-10-26 17:11:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
// Function: svgedit.utilities.getHref
|
2010-10-28 05:50:39 +00:00
|
|
|
// Returns the given element's xlink:href value
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.getHref = function(elem) {
|
2013-02-16 15:02:26 +00:00
|
|
|
return elem.getAttributeNS(NS.XLINK, 'href');
|
2013-02-19 11:18:04 +00:00
|
|
|
};
|
2010-10-28 05:50:39 +00:00
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
// Function: svgedit.utilities.setHref
|
2010-10-28 05:50:39 +00:00
|
|
|
// Sets the given element's xlink:href value
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.setHref = function(elem, val) {
|
2013-02-16 15:02:26 +00:00
|
|
|
elem.setAttributeNS(NS.XLINK, 'xlink:href', val);
|
2013-02-19 11:18:04 +00:00
|
|
|
};
|
2010-10-28 05:50:39 +00:00
|
|
|
|
|
|
|
// Function: findDefs
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// The document's <defs> element, create it first if necessary
|
2013-02-14 15:19:46 +00:00
|
|
|
svgedit.utilities.findDefs = function() {
|
2013-02-14 19:11:30 +00:00
|
|
|
var svgElement = editorContext_.getSVGContent();
|
2013-02-16 15:02:26 +00:00
|
|
|
var defs = svgElement.getElementsByTagNameNS(NS.SVG, 'defs');
|
2010-10-28 05:50:39 +00:00
|
|
|
if (defs.length > 0) {
|
|
|
|
defs = defs[0];
|
2013-02-15 23:05:23 +00:00
|
|
|
} else {
|
2013-02-16 15:02:26 +00:00
|
|
|
defs = svgElement.ownerDocument.createElementNS(NS.SVG, 'defs');
|
2013-02-14 15:19:46 +00:00
|
|
|
if (svgElement.firstChild) {
|
|
|
|
// first child is a comment, so call nextSibling
|
|
|
|
svgElement.insertBefore(defs, svgElement.firstChild.nextSibling);
|
|
|
|
} else {
|
|
|
|
svgElement.appendChild(defs);
|
|
|
|
}
|
2010-10-28 05:50:39 +00:00
|
|
|
}
|
|
|
|
return defs;
|
|
|
|
};
|
|
|
|
|
2010-11-08 08:42:46 +00:00
|
|
|
// TODO(codedread): Consider moving the next to functions to bbox.js
|
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
// Function: svgedit.utilities.getPathBBox
|
2010-11-05 15:29:30 +00:00
|
|
|
// Get correct BBox for a path in Webkit
|
|
|
|
// Converted from code found here:
|
|
|
|
// http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
|
2013-02-15 23:05:23 +00:00
|
|
|
//
|
2010-11-05 15:29:30 +00:00
|
|
|
// Parameters:
|
|
|
|
// path - The path DOM element to get the BBox for
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// A BBox-like object
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.getPathBBox = function(path) {
|
2010-11-05 15:29:30 +00:00
|
|
|
var seglist = path.pathSegList;
|
|
|
|
var tot = seglist.numberOfItems;
|
2013-02-15 23:05:23 +00:00
|
|
|
|
2010-11-05 15:29:30 +00:00
|
|
|
var bounds = [[], []];
|
|
|
|
var start = seglist.getItem(0);
|
|
|
|
var P0 = [start.x, start.y];
|
2012-03-30 03:50:54 +00:00
|
|
|
|
2014-01-31 02:13:37 +00:00
|
|
|
var i;
|
|
|
|
for (i = 0; i < tot; i++) {
|
2010-11-05 15:29:30 +00:00
|
|
|
var seg = seglist.getItem(i);
|
2012-03-30 03:50:54 +00:00
|
|
|
|
2014-05-22 10:21:29 +00:00
|
|
|
if(seg.x === undef) {continue;}
|
2012-03-30 03:50:54 +00:00
|
|
|
|
2010-11-05 15:29:30 +00:00
|
|
|
// Add actual points to limits
|
|
|
|
bounds[0].push(P0[0]);
|
|
|
|
bounds[1].push(P0[1]);
|
2013-02-15 23:05:23 +00:00
|
|
|
|
2014-05-22 10:21:29 +00:00
|
|
|
if (seg.x1) {
|
2010-11-05 15:29:30 +00:00
|
|
|
var P1 = [seg.x1, seg.y1],
|
|
|
|
P2 = [seg.x2, seg.y2],
|
|
|
|
P3 = [seg.x, seg.y];
|
|
|
|
|
2014-01-31 02:13:37 +00:00
|
|
|
var j;
|
|
|
|
for (j = 0; j < 2; j++) {
|
2010-11-05 15:29:30 +00:00
|
|
|
|
|
|
|
var calc = function(t) {
|
2013-02-15 23:05:23 +00:00
|
|
|
return Math.pow(1-t,3) * P0[j]
|
2010-11-05 15:29:30 +00:00
|
|
|
+ 3 * Math.pow(1-t,2) * t * P1[j]
|
2013-02-15 16:51:48 +00:00
|
|
|
+ 3 * (1-t) * Math.pow(t, 2) * P2[j]
|
2010-11-05 15:29:30 +00:00
|
|
|
+ Math.pow(t,3) * P3[j];
|
|
|
|
};
|
|
|
|
|
|
|
|
var b = 6 * P0[j] - 12 * P1[j] + 6 * P2[j];
|
|
|
|
var a = -3 * P0[j] + 9 * P1[j] - 9 * P2[j] + 3 * P3[j];
|
|
|
|
var c = 3 * P1[j] - 3 * P0[j];
|
2013-02-15 23:05:23 +00:00
|
|
|
|
2014-01-31 02:13:37 +00:00
|
|
|
if (a == 0) {
|
|
|
|
if (b == 0) {
|
2010-11-05 15:29:30 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var t = -c / b;
|
2014-01-31 02:13:37 +00:00
|
|
|
if (0 < t && t < 1) {
|
2010-11-05 15:29:30 +00:00
|
|
|
bounds[j].push(calc(t));
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var b2ac = Math.pow(b,2) - 4 * c * a;
|
2014-01-31 02:13:37 +00:00
|
|
|
if (b2ac < 0) {continue;}
|
2010-11-05 15:29:30 +00:00
|
|
|
var t1 = (-b + Math.sqrt(b2ac))/(2 * a);
|
2014-01-31 02:13:37 +00:00
|
|
|
if (0 < t1 && t1 < 1) {bounds[j].push(calc(t1));}
|
2010-11-05 15:29:30 +00:00
|
|
|
var t2 = (-b - Math.sqrt(b2ac))/(2 * a);
|
2014-01-31 02:13:37 +00:00
|
|
|
if (0 < t2 && t2 < 1) {bounds[j].push(calc(t2));}
|
2010-11-05 15:29:30 +00:00
|
|
|
}
|
|
|
|
P0 = P3;
|
|
|
|
} else {
|
|
|
|
bounds[0].push(seg.x);
|
|
|
|
bounds[1].push(seg.y);
|
|
|
|
}
|
|
|
|
}
|
2013-02-15 23:05:23 +00:00
|
|
|
|
2010-11-05 15:29:30 +00:00
|
|
|
var x = Math.min.apply(null, bounds[0]);
|
|
|
|
var w = Math.max.apply(null, bounds[0]) - x;
|
|
|
|
var y = Math.min.apply(null, bounds[1]);
|
|
|
|
var h = Math.max.apply(null, bounds[1]) - y;
|
|
|
|
return {
|
|
|
|
'x': x,
|
|
|
|
'y': y,
|
|
|
|
'width': w,
|
|
|
|
'height': h
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2011-02-09 21:32:53 +00:00
|
|
|
// Function: groupBBFix
|
|
|
|
// Get the given/selected element's bounding box object, checking for
|
|
|
|
// horizontal/vertical lines (see issue 717)
|
|
|
|
// Note that performance is currently terrible, so some way to improve would
|
|
|
|
// be great.
|
|
|
|
//
|
2013-02-15 23:05:23 +00:00
|
|
|
// Parameters:
|
2011-02-09 21:32:53 +00:00
|
|
|
// selected - Container or <use> DOM element
|
|
|
|
function groupBBFix(selected) {
|
|
|
|
if(svgedit.browser.supportsHVLineContainerBBox()) {
|
2013-02-15 23:05:23 +00:00
|
|
|
try { return selected.getBBox();} catch(e){}
|
2011-02-09 21:32:53 +00:00
|
|
|
}
|
|
|
|
var ref = $.data(selected, 'ref');
|
|
|
|
var matched = null;
|
2014-01-31 02:13:37 +00:00
|
|
|
var ret, copy;
|
2013-02-15 23:05:23 +00:00
|
|
|
|
2011-02-09 21:32:53 +00:00
|
|
|
if(ref) {
|
2014-01-31 02:13:37 +00:00
|
|
|
copy = $(ref).children().clone().attr('visibility', 'hidden');
|
2011-02-09 21:32:53 +00:00
|
|
|
$(svgroot_).append(copy);
|
|
|
|
matched = copy.filter('line, path');
|
|
|
|
} else {
|
|
|
|
matched = $(selected).find('line, path');
|
|
|
|
}
|
2013-02-15 23:05:23 +00:00
|
|
|
|
2011-02-09 21:32:53 +00:00
|
|
|
var issue = false;
|
|
|
|
if(matched.length) {
|
|
|
|
matched.each(function() {
|
|
|
|
var bb = this.getBBox();
|
|
|
|
if(!bb.width || !bb.height) {
|
|
|
|
issue = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if(issue) {
|
|
|
|
var elems = ref ? copy : $(selected).children();
|
2014-01-31 02:13:37 +00:00
|
|
|
ret = getStrokedBBox(elems); // getStrokedBBox defined in svgcanvas
|
2011-02-09 21:32:53 +00:00
|
|
|
} else {
|
|
|
|
ret = selected.getBBox();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret = selected.getBBox();
|
|
|
|
}
|
|
|
|
if(ref) {
|
|
|
|
copy.remove();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
// Function: svgedit.utilities.getBBox
|
2010-11-05 15:29:30 +00:00
|
|
|
// Get the given/selected element's bounding box object, convert it to be more
|
|
|
|
// usable when necessary
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// elem - Optional DOM element to get the BBox for
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.getBBox = function(elem) {
|
2010-12-02 17:14:24 +00:00
|
|
|
var selected = elem || editorContext_.geSelectedElements()[0];
|
2014-01-31 02:13:37 +00:00
|
|
|
if (elem.nodeType != 1) {return null;}
|
2010-11-05 15:29:30 +00:00
|
|
|
var ret = null;
|
|
|
|
var elname = selected.nodeName;
|
2013-02-15 23:05:23 +00:00
|
|
|
|
2011-02-09 16:30:11 +00:00
|
|
|
switch ( elname ) {
|
|
|
|
case 'text':
|
|
|
|
if(selected.textContent === '') {
|
|
|
|
selected.textContent = 'a'; // Some character needed for the selector to use.
|
|
|
|
ret = selected.getBBox();
|
|
|
|
selected.textContent = '';
|
|
|
|
} else {
|
2015-12-01 03:08:13 +00:00
|
|
|
if (selected.getBBox) { ret = selected.getBBox(); }
|
2011-02-09 16:30:11 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'path':
|
|
|
|
if(!svgedit.browser.supportsPathBBox()) {
|
|
|
|
ret = svgedit.utilities.getPathBBox(selected);
|
|
|
|
} else {
|
2015-12-01 03:08:13 +00:00
|
|
|
if (selected.getBBox) { ret = selected.getBBox(); }
|
2011-02-09 16:30:11 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'g':
|
|
|
|
case 'a':
|
2011-02-09 21:32:53 +00:00
|
|
|
ret = groupBBFix(selected);
|
2011-02-09 16:30:11 +00:00
|
|
|
break;
|
|
|
|
default:
|
2011-02-09 21:32:53 +00:00
|
|
|
|
|
|
|
if(elname === 'use') {
|
|
|
|
ret = groupBBFix(selected, true);
|
|
|
|
}
|
2013-02-15 17:02:24 +00:00
|
|
|
if(elname === 'use' || ( elname === 'foreignObject' && svgedit.browser.isWebkit() ) ) {
|
2014-01-31 02:13:37 +00:00
|
|
|
if(!ret) {ret = selected.getBBox();}
|
2012-11-28 05:39:48 +00:00
|
|
|
// This is resolved in later versions of webkit, perhaps we should
|
|
|
|
// have a featured detection for correct 'use' behavior?
|
|
|
|
// ——————————
|
|
|
|
//if(!svgedit.browser.isWebkit()) {
|
2011-02-23 15:56:14 +00:00
|
|
|
var bb = {};
|
|
|
|
bb.width = ret.width;
|
|
|
|
bb.height = ret.height;
|
|
|
|
bb.x = ret.x + parseFloat(selected.getAttribute('x')||0);
|
|
|
|
bb.y = ret.y + parseFloat(selected.getAttribute('y')||0);
|
|
|
|
ret = bb;
|
2012-11-28 05:39:48 +00:00
|
|
|
//}
|
2011-02-09 16:30:11 +00:00
|
|
|
} else if(~visElems_arr.indexOf(elname)) {
|
2015-12-01 03:08:13 +00:00
|
|
|
if (selected) { ret = selected.getBBox(); }
|
|
|
|
else {
|
2011-02-09 16:30:11 +00:00
|
|
|
// Check if element is child of a foreignObject
|
2013-02-15 17:02:24 +00:00
|
|
|
var fo = $(selected).closest('foreignObject');
|
2015-12-01 03:08:13 +00:00
|
|
|
if (fo.length) {
|
|
|
|
if (fo[0].getBBox) {
|
2011-02-09 16:30:11 +00:00
|
|
|
ret = fo[0].getBBox();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(ret) {
|
|
|
|
ret = svgedit.utilities.bboxToObj(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the bounding box from the DOM (which is in that element's coordinate system)
|
|
|
|
return ret;
|
2010-11-05 15:29:30 +00:00
|
|
|
};
|
|
|
|
|
2010-11-05 17:00:32 +00:00
|
|
|
// Function: svgedit.utilities.getRotationAngle
|
2010-11-05 15:59:30 +00:00
|
|
|
// Get the rotation angle of the given/selected DOM element
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// elem - Optional DOM element to get the angle for
|
|
|
|
// to_rad - Boolean that when true returns the value in radians rather than degrees
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// Float with the angle in degrees or radians
|
2010-11-05 17:00:32 +00:00
|
|
|
svgedit.utilities.getRotationAngle = function(elem, to_rad) {
|
2010-12-02 17:14:24 +00:00
|
|
|
var selected = elem || editorContext_.getSelectedElements()[0];
|
2010-11-05 15:59:30 +00:00
|
|
|
// find the rotation transform (if any) and set it
|
|
|
|
var tlist = svgedit.transformlist.getTransformList(selected);
|
2014-01-31 02:13:37 +00:00
|
|
|
if(!tlist) {return 0;} // <svg> elements have no tlist
|
2010-11-05 15:59:30 +00:00
|
|
|
var N = tlist.numberOfItems;
|
2014-01-31 02:13:37 +00:00
|
|
|
var i;
|
|
|
|
for (i = 0; i < N; ++i) {
|
2010-11-05 15:59:30 +00:00
|
|
|
var xform = tlist.getItem(i);
|
|
|
|
if (xform.type == 4) {
|
|
|
|
return to_rad ? xform.angle * Math.PI / 180.0 : xform.angle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0.0;
|
|
|
|
};
|
2010-11-05 15:29:30 +00:00
|
|
|
|
2013-02-14 15:19:46 +00:00
|
|
|
// Function getRefElem
|
|
|
|
// Get the reference element associated with the given attribute value
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// attrVal - The attribute value as a string
|
2013-02-15 23:05:23 +00:00
|
|
|
svgedit.utilities.getRefElem = function(attrVal) {
|
2013-02-14 15:19:46 +00:00
|
|
|
return svgedit.utilities.getElem(svgedit.utilities.getUrlFromAttr(attrVal).substr(1));
|
|
|
|
};
|
|
|
|
|
2011-02-04 08:02:46 +00:00
|
|
|
// Function: getElem
|
|
|
|
// Get a DOM element by ID within the SVG root element.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// id - String with the element's new ID
|
|
|
|
if (svgedit.browser.supportsSelectors()) {
|
|
|
|
svgedit.utilities.getElem = function(id) {
|
|
|
|
// querySelector lookup
|
|
|
|
return svgroot_.querySelector('#'+id);
|
|
|
|
};
|
|
|
|
} else if (svgedit.browser.supportsXpath()) {
|
|
|
|
svgedit.utilities.getElem = function(id) {
|
|
|
|
// xpath lookup
|
|
|
|
return domdoc_.evaluate(
|
|
|
|
'svg:svg[@id="svgroot"]//svg:*[@id="'+id+'"]',
|
2013-02-15 23:05:23 +00:00
|
|
|
domcontainer_,
|
2013-02-16 15:02:26 +00:00
|
|
|
function() { return svgedit.NS.SVG; },
|
2011-02-04 08:02:46 +00:00
|
|
|
9,
|
|
|
|
null).singleNodeValue;
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
svgedit.utilities.getElem = function(id) {
|
|
|
|
// jQuery lookup: twice as slow as xpath in FF
|
|
|
|
return $(svgroot_).find('[id=' + id + ']')[0];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: assignAttributes
|
|
|
|
// Assigns multiple attributes to an element.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// node - DOM element to apply new attribute values to
|
|
|
|
// attrs - Object with attribute keys/values
|
|
|
|
// suspendLength - Optional integer of milliseconds to suspend redraw
|
|
|
|
// unitCheck - Boolean to indicate the need to use svgedit.units.setUnitAttr
|
|
|
|
svgedit.utilities.assignAttributes = function(node, attrs, suspendLength, unitCheck) {
|
2014-01-31 02:13:37 +00:00
|
|
|
var i;
|
|
|
|
for (i in attrs) {
|
2013-02-16 15:02:26 +00:00
|
|
|
var ns = (i.substr(0,4) === 'xml:' ? NS.XML :
|
|
|
|
i.substr(0,6) === 'xlink:' ? NS.XLINK : null);
|
2013-02-15 23:05:23 +00:00
|
|
|
|
2011-02-04 08:02:46 +00:00
|
|
|
if(ns) {
|
|
|
|
node.setAttributeNS(ns, i, attrs[i]);
|
|
|
|
} else if(!unitCheck) {
|
|
|
|
node.setAttribute(i, attrs[i]);
|
|
|
|
} else {
|
|
|
|
svgedit.units.setUnitAttr(node, i, attrs[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-02-10 04:10:03 +00:00
|
|
|
// Function: cleanupElement
|
|
|
|
// Remove unneeded (default) attributes, makes resulting SVG smaller
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// element - DOM element to clean up
|
|
|
|
svgedit.utilities.cleanupElement = function(element) {
|
|
|
|
var defaults = {
|
|
|
|
'fill-opacity':1,
|
|
|
|
'stop-opacity':1,
|
|
|
|
'opacity':1,
|
|
|
|
'stroke':'none',
|
|
|
|
'stroke-dasharray':'none',
|
|
|
|
'stroke-linejoin':'miter',
|
|
|
|
'stroke-linecap':'butt',
|
|
|
|
'stroke-opacity':1,
|
|
|
|
'stroke-width':1,
|
|
|
|
'rx':0,
|
|
|
|
'ry':0
|
2013-02-19 11:18:04 +00:00
|
|
|
};
|
2013-02-15 23:05:23 +00:00
|
|
|
|
2014-01-31 02:13:37 +00:00
|
|
|
var attr;
|
|
|
|
for (attr in defaults) {
|
2011-02-10 04:10:03 +00:00
|
|
|
var val = defaults[attr];
|
|
|
|
if(element.getAttribute(attr) == val) {
|
|
|
|
element.removeAttribute(attr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-02-17 04:58:04 +00:00
|
|
|
// Function: snapToGrid
|
|
|
|
// round value to for snapping
|
|
|
|
// NOTE: This function did not move to svgutils.js since it depends on curConfig.
|
|
|
|
svgedit.utilities.snapToGrid = function(value) {
|
|
|
|
var stepSize = editorContext_.getSnappingStep();
|
|
|
|
var unit = editorContext_.getBaseUnit();
|
|
|
|
if (unit !== "px") {
|
|
|
|
stepSize *= svgedit.units.getTypeMap()[unit];
|
|
|
|
}
|
|
|
|
value = Math.round(value/stepSize)*stepSize;
|
|
|
|
return value;
|
|
|
|
};
|
|
|
|
|
2014-02-19 01:37:21 +00:00
|
|
|
svgedit.utilities.preg_quote = function (str, delimiter) {
|
|
|
|
// From: http://phpjs.org/functions
|
|
|
|
return String(str).replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
|
|
|
|
};
|
|
|
|
|
2014-05-22 10:21:29 +00:00
|
|
|
/**
|
|
|
|
* @param {string} globalCheck A global which can be used to determine if the script is already loaded
|
|
|
|
* @param {array} scripts An array of scripts to preload (in order)
|
|
|
|
* @param {function} cb The callback to execute upon load.
|
|
|
|
*/
|
|
|
|
svgedit.utilities.executeAfterLoads = function (globalCheck, scripts, cb) {
|
|
|
|
return function () {
|
|
|
|
var args = arguments;
|
|
|
|
function endCallback () {
|
|
|
|
cb.apply(null, args);
|
|
|
|
}
|
|
|
|
if (window[globalCheck]) {
|
|
|
|
endCallback();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
scripts.reduceRight(function (oldFunc, script) {
|
|
|
|
return function () {
|
|
|
|
$.getScript(script, oldFunc);
|
|
|
|
};
|
|
|
|
}, endCallback)();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
svgedit.utilities.buildCanvgCallback = function (callCanvg) {
|
|
|
|
return svgedit.utilities.executeAfterLoads('canvg', ['canvg/rgbcolor.js', 'canvg/canvg.js'], callCanvg);
|
|
|
|
};
|
|
|
|
|
|
|
|
svgedit.utilities.buildJSPDFCallback = function (callJSPDF) {
|
|
|
|
return svgedit.utilities.executeAfterLoads('RGBColor', ['canvg/rgbcolor.js'], function () {
|
|
|
|
var arr = [];
|
|
|
|
if (!RGBColor || RGBColor.ok === undef) { // It's not our RGBColor, so we'll need to load it
|
|
|
|
arr.push('canvg/rgbcolor.js');
|
|
|
|
}
|
|
|
|
svgedit.utilities.executeAfterLoads('jsPDF', arr.concat('jspdf/underscore-min.js', 'jspdf/jspdf.min.js', 'jspdf/jspdf.plugin.svgToPdf.js'), callJSPDF)();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-01-31 02:13:37 +00:00
|
|
|
}());
|