svgutils.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. /*globals $, svgedit, unescape, DOMParser, ActiveXObject, getStrokedBBox*/
  2. /*jslint vars: true, eqeq: true, bitwise: true, continue: true, forin: true*/
  3. /**
  4. * Package: svgedit.utilities
  5. *
  6. * Licensed under the MIT License
  7. *
  8. * Copyright(c) 2010 Alexis Deveria
  9. * Copyright(c) 2010 Jeff Schiller
  10. */
  11. // Dependencies:
  12. // 1) jQuery
  13. // 2) pathseg.js
  14. // 3) browser.js
  15. // 4) svgtransformlist.js
  16. // 5) units.js
  17. (function(undef) {'use strict';
  18. if (!svgedit.utilities) {
  19. svgedit.utilities = {};
  20. }
  21. // Constants
  22. // String used to encode base64.
  23. var KEYSTR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  24. var NS = svgedit.NS;
  25. // Much faster than running getBBox() every time
  26. var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use';
  27. var visElems_arr = visElems.split(',');
  28. //var hidElems = 'clipPath,defs,desc,feGaussianBlur,filter,linearGradient,marker,mask,metadata,pattern,radialGradient,stop,switch,symbol,title,textPath';
  29. var editorContext_ = null;
  30. var domdoc_ = null;
  31. var domcontainer_ = null;
  32. var svgroot_ = null;
  33. svgedit.utilities.init = function(editorContext) {
  34. editorContext_ = editorContext;
  35. domdoc_ = editorContext.getDOMDocument();
  36. domcontainer_ = editorContext.getDOMContainer();
  37. svgroot_ = editorContext.getSVGRoot();
  38. };
  39. // Function: svgedit.utilities.toXml
  40. // Converts characters in a string to XML-friendly entities.
  41. //
  42. // Example: '&' becomes '&'
  43. //
  44. // Parameters:
  45. // str - The string to be converted
  46. //
  47. // Returns:
  48. // The converted string
  49. svgedit.utilities.toXml = function(str) {
  50. // ' is ok in XML, but not HTML
  51. // > does not normally need escaping, though it can if within a CDATA expression (and preceded by "]]")
  52. return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/, '&#x27;');
  53. };
  54. // Function: svgedit.utilities.fromXml
  55. // Converts XML entities in a string to single characters.
  56. // Example: '&amp;' becomes '&'
  57. //
  58. // Parameters:
  59. // str - The string to be converted
  60. //
  61. // Returns:
  62. // The converted string
  63. svgedit.utilities.fromXml = function(str) {
  64. return $('<p/>').html(str).text();
  65. };
  66. // This code was written by Tyler Akins and has been placed in the
  67. // public domain. It would be nice if you left this header intact.
  68. // Base64 code from Tyler Akins -- http://rumkin.com
  69. // schiller: Removed string concatenation in favour of Array.join() optimization,
  70. // also precalculate the size of the array needed.
  71. // Function: svgedit.utilities.encode64
  72. // Converts a string to base64
  73. svgedit.utilities.encode64 = function(input) {
  74. // base64 strings are 4/3 larger than the original string
  75. input = svgedit.utilities.encodeUTF8(input); // convert non-ASCII characters
  76. // input = svgedit.utilities.convertToXMLReferences(input);
  77. if (window.btoa) {
  78. return window.btoa(input); // Use native if available
  79. }
  80. var output = [];
  81. output.length = Math.floor( (input.length + 2) / 3 ) * 4;
  82. var chr1, chr2, chr3;
  83. var enc1, enc2, enc3, enc4;
  84. var i = 0, p = 0;
  85. do {
  86. chr1 = input.charCodeAt(i++);
  87. chr2 = input.charCodeAt(i++);
  88. chr3 = input.charCodeAt(i++);
  89. enc1 = chr1 >> 2;
  90. enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
  91. enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
  92. enc4 = chr3 & 63;
  93. if (isNaN(chr2)) {
  94. enc3 = enc4 = 64;
  95. } else if (isNaN(chr3)) {
  96. enc4 = 64;
  97. }
  98. output[p++] = KEYSTR.charAt(enc1);
  99. output[p++] = KEYSTR.charAt(enc2);
  100. output[p++] = KEYSTR.charAt(enc3);
  101. output[p++] = KEYSTR.charAt(enc4);
  102. } while (i < input.length);
  103. return output.join('');
  104. };
  105. // Function: svgedit.utilities.decode64
  106. // Converts a string from base64
  107. svgedit.utilities.decode64 = function(input) {
  108. if(window.atob) {
  109. return svgedit.utilities.decodeUTF8(window.atob(input));
  110. }
  111. var output = '';
  112. var chr1, chr2, chr3 = '';
  113. var enc1, enc2, enc3, enc4 = '';
  114. var i = 0;
  115. // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
  116. input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
  117. do {
  118. enc1 = KEYSTR.indexOf(input.charAt(i++));
  119. enc2 = KEYSTR.indexOf(input.charAt(i++));
  120. enc3 = KEYSTR.indexOf(input.charAt(i++));
  121. enc4 = KEYSTR.indexOf(input.charAt(i++));
  122. chr1 = (enc1 << 2) | (enc2 >> 4);
  123. chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
  124. chr3 = ((enc3 & 3) << 6) | enc4;
  125. output = output + String.fromCharCode(chr1);
  126. if (enc3 != 64) {
  127. output = output + String.fromCharCode(chr2);
  128. }
  129. if (enc4 != 64) {
  130. output = output + String.fromCharCode(chr3);
  131. }
  132. chr1 = chr2 = chr3 = '';
  133. enc1 = enc2 = enc3 = enc4 = '';
  134. } while (i < input.length);
  135. return svgedit.utilities.decodeUTF8(output);
  136. };
  137. svgedit.utilities.decodeUTF8 = function (argString) {
  138. return decodeURIComponent(escape(argString));
  139. };
  140. // codedread:does not seem to work with webkit-based browsers on OSX // Brettz9: please test again as function upgraded
  141. svgedit.utilities.encodeUTF8 = function (argString) {
  142. return unescape(encodeURIComponent(argString));
  143. };
  144. // Function: svgedit.utilities.convertToXMLReferences
  145. // Converts a string to use XML references
  146. svgedit.utilities.convertToXMLReferences = function(input) {
  147. var n,
  148. output = '';
  149. for (n = 0; n < input.length; n++){
  150. var c = input.charCodeAt(n);
  151. if (c < 128) {
  152. output += input[n];
  153. } else if(c > 127) {
  154. output += ('&#' + c + ';');
  155. }
  156. }
  157. return output;
  158. };
  159. // Function: svgedit.utilities.text2xml
  160. // Cross-browser compatible method of converting a string to an XML tree
  161. // found this function here: http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f
  162. svgedit.utilities.text2xml = function(sXML) {
  163. if(sXML.indexOf('<svg:svg') >= 0) {
  164. sXML = sXML.replace(/<(\/?)svg:/g, '<$1').replace('xmlns:svg', 'xmlns');
  165. }
  166. var out, dXML;
  167. try{
  168. dXML = (window.DOMParser)?new DOMParser():new ActiveXObject('Microsoft.XMLDOM');
  169. dXML.async = false;
  170. } catch(e){
  171. throw new Error('XML Parser could not be instantiated');
  172. }
  173. try{
  174. if (dXML.loadXML) {
  175. out = (dXML.loadXML(sXML)) ? dXML : false;
  176. }
  177. else {
  178. out = dXML.parseFromString(sXML, 'text/xml');
  179. }
  180. }
  181. catch(e2){ throw new Error('Error parsing XML string'); }
  182. return out;
  183. };
  184. // Function: svgedit.utilities.bboxToObj
  185. // Converts a SVGRect into an object.
  186. //
  187. // Parameters:
  188. // bbox - a SVGRect
  189. //
  190. // Returns:
  191. // An object with properties names x, y, width, height.
  192. svgedit.utilities.bboxToObj = function(bbox) {
  193. return {
  194. x: bbox.x,
  195. y: bbox.y,
  196. width: bbox.width,
  197. height: bbox.height
  198. };
  199. };
  200. // Function: svgedit.utilities.walkTree
  201. // Walks the tree and executes the callback on each element in a top-down fashion
  202. //
  203. // Parameters:
  204. // elem - DOM element to traverse
  205. // cbFn - Callback function to run on each element
  206. svgedit.utilities.walkTree = function(elem, cbFn){
  207. if (elem && elem.nodeType == 1) {
  208. cbFn(elem);
  209. var i = elem.childNodes.length;
  210. while (i--) {
  211. svgedit.utilities.walkTree(elem.childNodes.item(i), cbFn);
  212. }
  213. }
  214. };
  215. // Function: svgedit.utilities.walkTreePost
  216. // Walks the tree and executes the callback on each element in a depth-first fashion
  217. // TODO: FIXME: Shouldn't this be calling walkTreePost?
  218. //
  219. // Parameters:
  220. // elem - DOM element to traverse
  221. // cbFn - Callback function to run on each element
  222. svgedit.utilities.walkTreePost = function(elem, cbFn) {
  223. if (elem && elem.nodeType == 1) {
  224. var i = elem.childNodes.length;
  225. while (i--) {
  226. svgedit.utilities.walkTree(elem.childNodes.item(i), cbFn);
  227. }
  228. cbFn(elem);
  229. }
  230. };
  231. // Function: svgedit.utilities.getUrlFromAttr
  232. // Extracts the URL from the url(...) syntax of some attributes.
  233. // Three variants:
  234. // * <circle fill="url(someFile.svg#foo)" />
  235. // * <circle fill="url('someFile.svg#foo')" />
  236. // * <circle fill='url("someFile.svg#foo")' />
  237. //
  238. // Parameters:
  239. // attrVal - The attribute value as a string
  240. //
  241. // Returns:
  242. // String with just the URL, like someFile.svg#foo
  243. svgedit.utilities.getUrlFromAttr = function(attrVal) {
  244. if (attrVal) {
  245. // url("#somegrad")
  246. if (attrVal.indexOf('url("') === 0) {
  247. return attrVal.substring(5, attrVal.indexOf('"',6));
  248. }
  249. // url('#somegrad')
  250. if (attrVal.indexOf("url('") === 0) {
  251. return attrVal.substring(5, attrVal.indexOf("'",6));
  252. }
  253. if (attrVal.indexOf("url(") === 0) {
  254. return attrVal.substring(4, attrVal.indexOf(')'));
  255. }
  256. }
  257. return null;
  258. };
  259. // Function: svgedit.utilities.getHref
  260. // Returns the given element's xlink:href value
  261. svgedit.utilities.getHref = function(elem) {
  262. return elem.getAttributeNS(NS.XLINK, 'href');
  263. };
  264. // Function: svgedit.utilities.setHref
  265. // Sets the given element's xlink:href value
  266. svgedit.utilities.setHref = function(elem, val) {
  267. elem.setAttributeNS(NS.XLINK, 'xlink:href', val);
  268. };
  269. // Function: findDefs
  270. //
  271. // Returns:
  272. // The document's <defs> element, create it first if necessary
  273. svgedit.utilities.findDefs = function() {
  274. var svgElement = editorContext_.getSVGContent();
  275. var defs = svgElement.getElementsByTagNameNS(NS.SVG, 'defs');
  276. if (defs.length > 0) {
  277. defs = defs[0];
  278. } else {
  279. defs = svgElement.ownerDocument.createElementNS(NS.SVG, 'defs');
  280. if (svgElement.firstChild) {
  281. // first child is a comment, so call nextSibling
  282. svgElement.insertBefore(defs, svgElement.firstChild.nextSibling);
  283. } else {
  284. svgElement.appendChild(defs);
  285. }
  286. }
  287. return defs;
  288. };
  289. // TODO(codedread): Consider moving the next to functions to bbox.js
  290. // Function: svgedit.utilities.getPathBBox
  291. // Get correct BBox for a path in Webkit
  292. // Converted from code found here:
  293. // http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
  294. //
  295. // Parameters:
  296. // path - The path DOM element to get the BBox for
  297. //
  298. // Returns:
  299. // A BBox-like object
  300. svgedit.utilities.getPathBBox = function(path) {
  301. var seglist = path.pathSegList;
  302. var tot = seglist.numberOfItems;
  303. var bounds = [[], []];
  304. var start = seglist.getItem(0);
  305. var P0 = [start.x, start.y];
  306. var i;
  307. for (i = 0; i < tot; i++) {
  308. var seg = seglist.getItem(i);
  309. if(seg.x === undef) {continue;}
  310. // Add actual points to limits
  311. bounds[0].push(P0[0]);
  312. bounds[1].push(P0[1]);
  313. if (seg.x1) {
  314. var P1 = [seg.x1, seg.y1],
  315. P2 = [seg.x2, seg.y2],
  316. P3 = [seg.x, seg.y];
  317. var j;
  318. for (j = 0; j < 2; j++) {
  319. var calc = function(t) {
  320. return Math.pow(1-t,3) * P0[j]
  321. + 3 * Math.pow(1-t,2) * t * P1[j]
  322. + 3 * (1-t) * Math.pow(t, 2) * P2[j]
  323. + Math.pow(t,3) * P3[j];
  324. };
  325. var b = 6 * P0[j] - 12 * P1[j] + 6 * P2[j];
  326. var a = -3 * P0[j] + 9 * P1[j] - 9 * P2[j] + 3 * P3[j];
  327. var c = 3 * P1[j] - 3 * P0[j];
  328. if (a == 0) {
  329. if (b == 0) {
  330. continue;
  331. }
  332. var t = -c / b;
  333. if (0 < t && t < 1) {
  334. bounds[j].push(calc(t));
  335. }
  336. continue;
  337. }
  338. var b2ac = Math.pow(b,2) - 4 * c * a;
  339. if (b2ac < 0) {continue;}
  340. var t1 = (-b + Math.sqrt(b2ac))/(2 * a);
  341. if (0 < t1 && t1 < 1) {bounds[j].push(calc(t1));}
  342. var t2 = (-b - Math.sqrt(b2ac))/(2 * a);
  343. if (0 < t2 && t2 < 1) {bounds[j].push(calc(t2));}
  344. }
  345. P0 = P3;
  346. } else {
  347. bounds[0].push(seg.x);
  348. bounds[1].push(seg.y);
  349. }
  350. }
  351. var x = Math.min.apply(null, bounds[0]);
  352. var w = Math.max.apply(null, bounds[0]) - x;
  353. var y = Math.min.apply(null, bounds[1]);
  354. var h = Math.max.apply(null, bounds[1]) - y;
  355. return {
  356. 'x': x,
  357. 'y': y,
  358. 'width': w,
  359. 'height': h
  360. };
  361. };
  362. // Function: groupBBFix
  363. // Get the given/selected element's bounding box object, checking for
  364. // horizontal/vertical lines (see issue 717)
  365. // Note that performance is currently terrible, so some way to improve would
  366. // be great.
  367. //
  368. // Parameters:
  369. // selected - Container or <use> DOM element
  370. function groupBBFix(selected) {
  371. if(svgedit.browser.supportsHVLineContainerBBox()) {
  372. try { return selected.getBBox();} catch(e){}
  373. }
  374. var ref = $.data(selected, 'ref');
  375. var matched = null;
  376. var ret, copy;
  377. if(ref) {
  378. copy = $(ref).children().clone().attr('visibility', 'hidden');
  379. $(svgroot_).append(copy);
  380. matched = copy.filter('line, path');
  381. } else {
  382. matched = $(selected).find('line, path');
  383. }
  384. var issue = false;
  385. if(matched.length) {
  386. matched.each(function() {
  387. var bb = this.getBBox();
  388. if(!bb.width || !bb.height) {
  389. issue = true;
  390. }
  391. });
  392. if(issue) {
  393. var elems = ref ? copy : $(selected).children();
  394. ret = getStrokedBBox(elems); // getStrokedBBox defined in svgcanvas
  395. } else {
  396. ret = selected.getBBox();
  397. }
  398. } else {
  399. ret = selected.getBBox();
  400. }
  401. if(ref) {
  402. copy.remove();
  403. }
  404. return ret;
  405. }
  406. // Function: svgedit.utilities.getBBox
  407. // Get the given/selected element's bounding box object, convert it to be more
  408. // usable when necessary
  409. //
  410. // Parameters:
  411. // elem - Optional DOM element to get the BBox for
  412. svgedit.utilities.getBBox = function(elem) {
  413. var selected = elem || editorContext_.geSelectedElements()[0];
  414. if (elem.nodeType != 1) {return null;}
  415. var ret = null;
  416. var elname = selected.nodeName;
  417. switch ( elname ) {
  418. case 'text':
  419. if(selected.textContent === '') {
  420. selected.textContent = 'a'; // Some character needed for the selector to use.
  421. ret = selected.getBBox();
  422. selected.textContent = '';
  423. } else {
  424. if (selected.getBBox) { ret = selected.getBBox(); }
  425. }
  426. break;
  427. case 'path':
  428. if(!svgedit.browser.supportsPathBBox()) {
  429. ret = svgedit.utilities.getPathBBox(selected);
  430. } else {
  431. if (selected.getBBox) { ret = selected.getBBox(); }
  432. }
  433. break;
  434. case 'g':
  435. case 'a':
  436. ret = groupBBFix(selected);
  437. break;
  438. default:
  439. if(elname === 'use') {
  440. ret = groupBBFix(selected, true);
  441. }
  442. if(elname === 'use' || ( elname === 'foreignObject' && svgedit.browser.isWebkit() ) ) {
  443. if(!ret) {ret = selected.getBBox();}
  444. // This is resolved in later versions of webkit, perhaps we should
  445. // have a featured detection for correct 'use' behavior?
  446. // ——————————
  447. //if(!svgedit.browser.isWebkit()) {
  448. var bb = {};
  449. bb.width = ret.width;
  450. bb.height = ret.height;
  451. bb.x = ret.x + parseFloat(selected.getAttribute('x')||0);
  452. bb.y = ret.y + parseFloat(selected.getAttribute('y')||0);
  453. ret = bb;
  454. //}
  455. } else if(~visElems_arr.indexOf(elname)) {
  456. if (selected) { ret = selected.getBBox(); }
  457. else {
  458. // Check if element is child of a foreignObject
  459. var fo = $(selected).closest('foreignObject');
  460. if (fo.length) {
  461. if (fo[0].getBBox) {
  462. ret = fo[0].getBBox();
  463. }
  464. }
  465. }
  466. }
  467. }
  468. if(ret) {
  469. ret = svgedit.utilities.bboxToObj(ret);
  470. }
  471. // get the bounding box from the DOM (which is in that element's coordinate system)
  472. return ret;
  473. };
  474. // Function: svgedit.utilities.getRotationAngle
  475. // Get the rotation angle of the given/selected DOM element
  476. //
  477. // Parameters:
  478. // elem - Optional DOM element to get the angle for
  479. // to_rad - Boolean that when true returns the value in radians rather than degrees
  480. //
  481. // Returns:
  482. // Float with the angle in degrees or radians
  483. svgedit.utilities.getRotationAngle = function(elem, to_rad) {
  484. var selected = elem || editorContext_.getSelectedElements()[0];
  485. // find the rotation transform (if any) and set it
  486. var tlist = svgedit.transformlist.getTransformList(selected);
  487. if(!tlist) {return 0;} // <svg> elements have no tlist
  488. var N = tlist.numberOfItems;
  489. var i;
  490. for (i = 0; i < N; ++i) {
  491. var xform = tlist.getItem(i);
  492. if (xform.type == 4) {
  493. return to_rad ? xform.angle * Math.PI / 180.0 : xform.angle;
  494. }
  495. }
  496. return 0.0;
  497. };
  498. // Function getRefElem
  499. // Get the reference element associated with the given attribute value
  500. //
  501. // Parameters:
  502. // attrVal - The attribute value as a string
  503. svgedit.utilities.getRefElem = function(attrVal) {
  504. return svgedit.utilities.getElem(svgedit.utilities.getUrlFromAttr(attrVal).substr(1));
  505. };
  506. // Function: getElem
  507. // Get a DOM element by ID within the SVG root element.
  508. //
  509. // Parameters:
  510. // id - String with the element's new ID
  511. if (svgedit.browser.supportsSelectors()) {
  512. svgedit.utilities.getElem = function(id) {
  513. // querySelector lookup
  514. return svgroot_.querySelector('#'+id);
  515. };
  516. } else if (svgedit.browser.supportsXpath()) {
  517. svgedit.utilities.getElem = function(id) {
  518. // xpath lookup
  519. return domdoc_.evaluate(
  520. 'svg:svg[@id="svgroot"]//svg:*[@id="'+id+'"]',
  521. domcontainer_,
  522. function() { return svgedit.NS.SVG; },
  523. 9,
  524. null).singleNodeValue;
  525. };
  526. } else {
  527. svgedit.utilities.getElem = function(id) {
  528. // jQuery lookup: twice as slow as xpath in FF
  529. return $(svgroot_).find('[id=' + id + ']')[0];
  530. };
  531. }
  532. // Function: assignAttributes
  533. // Assigns multiple attributes to an element.
  534. //
  535. // Parameters:
  536. // node - DOM element to apply new attribute values to
  537. // attrs - Object with attribute keys/values
  538. // suspendLength - Optional integer of milliseconds to suspend redraw
  539. // unitCheck - Boolean to indicate the need to use svgedit.units.setUnitAttr
  540. svgedit.utilities.assignAttributes = function(node, attrs, suspendLength, unitCheck) {
  541. var i;
  542. for (i in attrs) {
  543. var ns = (i.substr(0,4) === 'xml:' ? NS.XML :
  544. i.substr(0,6) === 'xlink:' ? NS.XLINK : null);
  545. if(ns) {
  546. node.setAttributeNS(ns, i, attrs[i]);
  547. } else if(!unitCheck) {
  548. node.setAttribute(i, attrs[i]);
  549. } else {
  550. svgedit.units.setUnitAttr(node, i, attrs[i]);
  551. }
  552. }
  553. };
  554. // Function: cleanupElement
  555. // Remove unneeded (default) attributes, makes resulting SVG smaller
  556. //
  557. // Parameters:
  558. // element - DOM element to clean up
  559. svgedit.utilities.cleanupElement = function(element) {
  560. var defaults = {
  561. 'fill-opacity':1,
  562. 'stop-opacity':1,
  563. 'opacity':1,
  564. 'stroke':'none',
  565. 'stroke-dasharray':'none',
  566. 'stroke-linejoin':'miter',
  567. 'stroke-linecap':'butt',
  568. 'stroke-opacity':1,
  569. 'stroke-width':1,
  570. 'rx':0,
  571. 'ry':0
  572. };
  573. var attr;
  574. for (attr in defaults) {
  575. var val = defaults[attr];
  576. if(element.getAttribute(attr) == val) {
  577. element.removeAttribute(attr);
  578. }
  579. }
  580. };
  581. // Function: snapToGrid
  582. // round value to for snapping
  583. // NOTE: This function did not move to svgutils.js since it depends on curConfig.
  584. svgedit.utilities.snapToGrid = function(value) {
  585. var stepSize = editorContext_.getSnappingStep();
  586. var unit = editorContext_.getBaseUnit();
  587. if (unit !== "px") {
  588. stepSize *= svgedit.units.getTypeMap()[unit];
  589. }
  590. value = Math.round(value/stepSize)*stepSize;
  591. return value;
  592. };
  593. svgedit.utilities.preg_quote = function (str, delimiter) {
  594. // From: http://phpjs.org/functions
  595. return String(str).replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
  596. };
  597. /**
  598. * @param {string} globalCheck A global which can be used to determine if the script is already loaded
  599. * @param {array} scripts An array of scripts to preload (in order)
  600. * @param {function} cb The callback to execute upon load.
  601. */
  602. svgedit.utilities.executeAfterLoads = function (globalCheck, scripts, cb) {
  603. return function () {
  604. var args = arguments;
  605. function endCallback () {
  606. cb.apply(null, args);
  607. }
  608. if (window[globalCheck]) {
  609. endCallback();
  610. }
  611. else {
  612. scripts.reduceRight(function (oldFunc, script) {
  613. return function () {
  614. $.getScript(script, oldFunc);
  615. };
  616. }, endCallback)();
  617. }
  618. };
  619. };
  620. svgedit.utilities.buildCanvgCallback = function (callCanvg) {
  621. return svgedit.utilities.executeAfterLoads('canvg', ['canvg/rgbcolor.js', 'canvg/canvg.js'], callCanvg);
  622. };
  623. svgedit.utilities.buildJSPDFCallback = function (callJSPDF) {
  624. return svgedit.utilities.executeAfterLoads('RGBColor', ['canvg/rgbcolor.js'], function () {
  625. var arr = [];
  626. if (!RGBColor || RGBColor.ok === undef) { // It's not our RGBColor, so we'll need to load it
  627. arr.push('canvg/rgbcolor.js');
  628. }
  629. svgedit.utilities.executeAfterLoads('jsPDF', arr.concat('jspdf/underscore-min.js', 'jspdf/jspdf.min.js', 'jspdf/jspdf.plugin.svgToPdf.js'), callJSPDF)();
  630. });
  631. };
  632. }());