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