Commit | Line | Data |
5ba6c2b4 |
1 | \feff/* http://keith-wood.name/svg.html\r |
2 | SVG for jQuery v1.4.3.\r |
3 | Written by Keith Wood (kbwood{at}iinet.com.au) August 2007.\r |
4 | Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and \r |
5 | MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. \r |
6 | Please attribute the author if you use it. */\r |
7 | \r |
8 | (function($) { // Hide scope, no $ conflict\r |
9 | \r |
10 | /* SVG manager.\r |
11 | Use the singleton instance of this class, $.svg, \r |
12 | to interact with the SVG functionality. */\r |
13 | function SVGManager() {\r |
14 | this._settings = []; // Settings to be remembered per SVG object\r |
15 | this._extensions = []; // List of SVG extensions added to SVGWrapper\r |
16 | // for each entry [0] is extension name, [1] is extension class (function)\r |
17 | // the function takes one parameter - the SVGWrapper instance\r |
18 | this.regional = []; // Localisations, indexed by language, '' for default (English)\r |
19 | this.regional[''] = {errorLoadingText: 'Error loading',\r |
20 | notSupportedText: 'This browser does not support SVG'};\r |
21 | this.local = this.regional['']; // Current localisation\r |
22 | this._uuid = new Date().getTime();\r |
23 | this._renesis = detectActiveX('RenesisX.RenesisCtrl');\r |
24 | }\r |
25 | \r |
26 | /* Determine whether a given ActiveX control is available.\r |
27 | @param classId (string) the ID for the ActiveX control\r |
28 | @return (boolean) true if found, false if not */\r |
29 | function detectActiveX(classId) {\r |
30 | try {\r |
31 | return !!(window.ActiveXObject && new ActiveXObject(classId));\r |
32 | }\r |
33 | catch (e) {\r |
34 | return false;\r |
35 | }\r |
36 | }\r |
37 | \r |
38 | var PROP_NAME = 'svgwrapper';\r |
39 | \r |
40 | $.extend(SVGManager.prototype, {\r |
41 | /* Class name added to elements to indicate already configured with SVG. */\r |
42 | markerClassName: 'hasSVG',\r |
43 | \r |
44 | /* SVG namespace. */\r |
45 | svgNS: 'http://www.w3.org/2000/svg',\r |
46 | /* XLink namespace. */\r |
47 | xlinkNS: 'http://www.w3.org/1999/xlink',\r |
48 | \r |
49 | /* SVG wrapper class. */\r |
50 | _wrapperClass: SVGWrapper,\r |
51 | \r |
52 | /* Camel-case versions of attribute names containing dashes or are reserved words. */\r |
53 | _attrNames: {class_: 'class', in_: 'in',\r |
54 | alignmentBaseline: 'alignment-baseline', baselineShift: 'baseline-shift',\r |
55 | clipPath: 'clip-path', clipRule: 'clip-rule',\r |
56 | colorInterpolation: 'color-interpolation',\r |
57 | colorInterpolationFilters: 'color-interpolation-filters',\r |
58 | colorRendering: 'color-rendering', dominantBaseline: 'dominant-baseline',\r |
59 | enableBackground: 'enable-background', fillOpacity: 'fill-opacity',\r |
60 | fillRule: 'fill-rule', floodColor: 'flood-color',\r |
61 | floodOpacity: 'flood-opacity', fontFamily: 'font-family',\r |
62 | fontSize: 'font-size', fontSizeAdjust: 'font-size-adjust',\r |
63 | fontStretch: 'font-stretch', fontStyle: 'font-style',\r |
64 | fontVariant: 'font-variant', fontWeight: 'font-weight',\r |
65 | glyphOrientationHorizontal: 'glyph-orientation-horizontal',\r |
66 | glyphOrientationVertical: 'glyph-orientation-vertical',\r |
67 | horizAdvX: 'horiz-adv-x', horizOriginX: 'horiz-origin-x',\r |
68 | imageRendering: 'image-rendering', letterSpacing: 'letter-spacing',\r |
69 | lightingColor: 'lighting-color', markerEnd: 'marker-end',\r |
70 | markerMid: 'marker-mid', markerStart: 'marker-start',\r |
71 | stopColor: 'stop-color', stopOpacity: 'stop-opacity',\r |
72 | strikethroughPosition: 'strikethrough-position',\r |
73 | strikethroughThickness: 'strikethrough-thickness',\r |
74 | strokeDashArray: 'stroke-dasharray', strokeDashOffset: 'stroke-dashoffset',\r |
75 | strokeLineCap: 'stroke-linecap', strokeLineJoin: 'stroke-linejoin',\r |
76 | strokeMiterLimit: 'stroke-miterlimit', strokeOpacity: 'stroke-opacity',\r |
77 | strokeWidth: 'stroke-width', textAnchor: 'text-anchor',\r |
78 | textDecoration: 'text-decoration', textRendering: 'text-rendering',\r |
79 | underlinePosition: 'underline-position', underlineThickness: 'underline-thickness',\r |
80 | vertAdvY: 'vert-adv-y', vertOriginY: 'vert-origin-y',\r |
81 | wordSpacing: 'word-spacing', writingMode: 'writing-mode'},\r |
82 | \r |
83 | /* Add the SVG object to its container. */\r |
84 | _attachSVG: function(container, settings) {\r |
85 | var svg = (container.namespaceURI == this.svgNS ? container : null);\r |
86 | var container = (svg ? null : container);\r |
87 | if ($(container || svg).hasClass(this.markerClassName)) {\r |
88 | return;\r |
89 | }\r |
90 | if (typeof settings == 'string') {\r |
91 | settings = {loadURL: settings};\r |
92 | }\r |
93 | else if (typeof settings == 'function') {\r |
94 | settings = {onLoad: settings};\r |
95 | }\r |
96 | $(container || svg).addClass(this.markerClassName);\r |
97 | try {\r |
98 | if (!svg) {\r |
99 | svg = document.createElementNS(this.svgNS, 'svg');\r |
100 | svg.setAttribute('version', '1.1');\r |
101 | svg.setAttribute('width', container.clientWidth);\r |
102 | svg.setAttribute('height', container.clientHeight);\r |
103 | container.appendChild(svg);\r |
104 | }\r |
105 | this._afterLoad(container, svg, settings || {});\r |
106 | }\r |
107 | catch (e) {\r |
108 | if ($.browser.msie) {\r |
109 | if (!container.id) {\r |
110 | container.id = 'svg' + (this._uuid++);\r |
111 | }\r |
112 | this._settings[container.id] = settings;\r |
113 | container.innerHTML = '<embed type="image/svg+xml" width="100%" ' +\r |
114 | 'height="100%" src="' + (settings.initPath || '') + 'blank.svg"/>';\r |
115 | }\r |
116 | else {\r |
117 | container.innerHTML = '<p class="svg_error">' +\r |
118 | this.local.notSupportedText + '</p>';\r |
119 | }\r |
120 | }\r |
121 | },\r |
122 | \r |
123 | /* SVG callback after loading - register SVG root. */\r |
124 | _registerSVG: function() {\r |
125 | for (var i = 0; i < document.embeds.length; i++) { // Check all\r |
126 | var container = document.embeds[i].parentNode;\r |
127 | if (!$(container).hasClass($.svg.markerClassName) || // Not SVG\r |
128 | $.data(container, PROP_NAME)) { // Already done\r |
129 | continue;\r |
130 | }\r |
131 | var svg = null;\r |
132 | try {\r |
133 | svg = document.embeds[i].getSVGDocument();\r |
134 | }\r |
135 | catch(e) {\r |
136 | setTimeout($.svg._registerSVG, 250); // Renesis takes longer to load\r |
137 | return;\r |
138 | }\r |
139 | svg = (svg ? svg.documentElement : null);\r |
140 | if (svg) {\r |
141 | $.svg._afterLoad(container, svg);\r |
142 | }\r |
143 | }\r |
144 | },\r |
145 | \r |
146 | /* Post-processing once loaded. */\r |
147 | _afterLoad: function(container, svg, settings) {\r |
148 | var settings = settings || this._settings[container.id];\r |
149 | this._settings[container ? container.id : ''] = null;\r |
150 | var wrapper = new this._wrapperClass(svg, container);\r |
151 | $.data(container || svg, PROP_NAME, wrapper);\r |
152 | try {\r |
153 | if (settings.loadURL) { // Load URL\r |
154 | wrapper.load(settings.loadURL, settings);\r |
155 | }\r |
156 | if (settings.settings) { // Additional settings\r |
157 | wrapper.configure(settings.settings);\r |
158 | }\r |
159 | if (settings.onLoad && !settings.loadURL) { // Onload callback\r |
160 | settings.onLoad.apply(container || svg, [wrapper]);\r |
161 | }\r |
162 | }\r |
163 | catch (e) {\r |
164 | alert(e);\r |
165 | }\r |
166 | },\r |
167 | \r |
168 | /* Return the SVG wrapper created for a given container.\r |
169 | @param container (string) selector for the container or\r |
170 | (element) the container for the SVG object or\r |
171 | jQuery collection - first entry is the container\r |
172 | @return (SVGWrapper) the corresponding SVG wrapper element, or null if not attached */\r |
173 | _getSVG: function(container) {\r |
174 | container = (typeof container == 'string' ? $(container)[0] :\r |
175 | (container.jquery ? container[0] : container));\r |
176 | return $.data(container, PROP_NAME);\r |
177 | },\r |
178 | \r |
179 | /* Remove the SVG functionality from a div.\r |
180 | @param container (element) the container for the SVG object */\r |
181 | _destroySVG: function(container) {\r |
182 | var $container = $(container);\r |
183 | if (!$container.hasClass(this.markerClassName)) {\r |
184 | return;\r |
185 | }\r |
186 | $container.removeClass(this.markerClassName);\r |
187 | if (container.namespaceURI != this.svgNS) {\r |
188 | $container.empty();\r |
189 | }\r |
190 | $.removeData(container, PROP_NAME);\r |
191 | },\r |
192 | \r |
193 | /* Extend the SVGWrapper object with an embedded class.\r |
194 | The constructor function must take a single parameter that is\r |
195 | a reference to the owning SVG root object. This allows the \r |
196 | extension to access the basic SVG functionality.\r |
197 | @param name (string) the name of the SVGWrapper attribute to access the new class\r |
198 | @param extClass (function) the extension class constructor */\r |
199 | addExtension: function(name, extClass) {\r |
200 | this._extensions.push([name, extClass]);\r |
201 | }\r |
202 | });\r |
203 | \r |
204 | /* The main SVG interface, which encapsulates the SVG element.\r |
205 | Obtain a reference from $().svg('get') */\r |
206 | function SVGWrapper(svg, container) {\r |
207 | this._svg = svg; // The SVG root node\r |
208 | this._container = container; // The containing div\r |
209 | for (var i = 0; i < $.svg._extensions.length; i++) {\r |
210 | var extension = $.svg._extensions[i];\r |
211 | this[extension[0]] = new extension[1](this);\r |
212 | }\r |
213 | }\r |
214 | \r |
215 | $.extend(SVGWrapper.prototype, {\r |
216 | \r |
217 | /* Retrieve the width of the SVG object. */\r |
218 | _width: function() {\r |
219 | return (this._container ? this._container.clientWidth : this._svg.width);\r |
220 | },\r |
221 | \r |
222 | /* Retrieve the height of the SVG object. */\r |
223 | _height: function() {\r |
224 | return (this._container ? this._container.clientHeight : this._svg.height);\r |
225 | },\r |
226 | \r |
227 | /* Retrieve the root SVG element.\r |
228 | @return the top-level SVG element */\r |
229 | root: function() {\r |
230 | return this._svg;\r |
231 | },\r |
232 | \r |
233 | /* Configure the SVG root.\r |
234 | @param settings (object) additional settings for the root\r |
235 | @param clear (boolean) true to remove existing attributes first,\r |
236 | false to add to what is already there (optional)\r |
237 | @return (SVGWrapper) this root */\r |
238 | configure: function(settings, clear) {\r |
239 | if (clear) {\r |
240 | for (var i = this._svg.attributes.length - 1; i >= 0; i--) {\r |
241 | var attr = this._svg.attributes.item(i);\r |
242 | if (!(attr.nodeName == 'onload' || attr.nodeName == 'version' || \r |
243 | attr.nodeName.substring(0, 5) == 'xmlns')) {\r |
244 | this._svg.attributes.removeNamedItem(attr.nodeName);\r |
245 | }\r |
246 | }\r |
247 | }\r |
248 | for (var attrName in settings) {\r |
249 | this._svg.setAttribute(attrName, settings[attrName]);\r |
250 | }\r |
251 | return this;\r |
252 | },\r |
253 | \r |
254 | /* Locate a specific element in the SVG document.\r |
255 | @param id (string) the element's identifier\r |
256 | @return (element) the element reference, or null if not found */\r |
257 | getElementById: function(id) {\r |
258 | return this._svg.ownerDocument.getElementById(id);\r |
259 | },\r |
260 | \r |
261 | /* Change the attributes for a SVG node.\r |
262 | @param element (SVG element) the node to change\r |
263 | @param settings (object) the new settings\r |
264 | @return (SVGWrapper) this root */\r |
265 | change: function(element, settings) {\r |
266 | if (element) {\r |
267 | for (var name in settings) {\r |
268 | if (settings[name] == null) {\r |
269 | element.removeAttribute(name);\r |
270 | }\r |
271 | else {\r |
272 | element.setAttribute(name, settings[name]);\r |
273 | }\r |
274 | }\r |
275 | }\r |
276 | return this;\r |
277 | },\r |
278 | \r |
279 | /* Check for parent being absent and adjust arguments accordingly. */\r |
280 | _args: function(values, names, optSettings) {\r |
281 | names.splice(0, 0, 'parent');\r |
282 | names.splice(names.length, 0, 'settings');\r |
283 | var args = {};\r |
284 | var offset = 0;\r |
285 | if (values[0] != null && values[0].jquery) {\r |
286 | values[0] = values[0][0];\r |
287 | }\r |
288 | if (values[0] != null && !(typeof values[0] == 'object' && values[0].nodeName)) {\r |
289 | args['parent'] = null;\r |
290 | offset = 1;\r |
291 | }\r |
292 | for (var i = 0; i < values.length; i++) {\r |
293 | args[names[i + offset]] = values[i];\r |
294 | }\r |
295 | if (optSettings) {\r |
296 | $.each(optSettings, function(i, value) {\r |
297 | if (typeof args[value] == 'object') {\r |
298 | args.settings = args[value];\r |
299 | args[value] = null;\r |
300 | }\r |
301 | });\r |
302 | }\r |
303 | return args;\r |
304 | },\r |
305 | \r |
306 | /* Add a title.\r |
307 | @param parent (element or jQuery) the parent node for the new title (optional)\r |
308 | @param text (string) the text of the title\r |
309 | @param settings (object) additional settings for the title (optional)\r |
310 | @return (element) the new title node */\r |
311 | title: function(parent, text, settings) {\r |
312 | var args = this._args(arguments, ['text']);\r |
313 | var node = this._makeNode(args.parent, 'title', args.settings || {});\r |
314 | node.appendChild(this._svg.ownerDocument.createTextNode(args.text));\r |
315 | return node;\r |
316 | },\r |
317 | \r |
318 | /* Add a description.\r |
319 | @param parent (element or jQuery) the parent node for the new description (optional)\r |
320 | @param text (string) the text of the description\r |
321 | @param settings (object) additional settings for the description (optional)\r |
322 | @return (element) the new description node */\r |
323 | describe: function(parent, text, settings) {\r |
324 | var args = this._args(arguments, ['text']);\r |
325 | var node = this._makeNode(args.parent, 'desc', args.settings || {});\r |
326 | node.appendChild(this._svg.ownerDocument.createTextNode(args.text));\r |
327 | return node;\r |
328 | },\r |
329 | \r |
330 | /* Add a definitions node.\r |
331 | @param parent (element or jQuery) the parent node for the new definitions (optional)\r |
332 | @param id (string) the ID of this definitions (optional)\r |
333 | @param settings (object) additional settings for the definitions (optional)\r |
334 | @return (element) the new definitions node */\r |
335 | defs: function(parent, id, settings) {\r |
336 | var args = this._args(arguments, ['id'], ['id']);\r |
337 | return this._makeNode(args.parent, 'defs', $.extend(\r |
338 | (args.id ? {id: args.id} : {}), args.settings || {}));\r |
339 | },\r |
340 | \r |
341 | /* Add a symbol definition.\r |
342 | @param parent (element or jQuery) the parent node for the new symbol (optional)\r |
343 | @param id (string) the ID of this symbol\r |
344 | @param x1 (number) the left coordinate for this symbol\r |
345 | @param y1 (number) the top coordinate for this symbol\r |
346 | @param width (number) the width of this symbol\r |
347 | @param height (number) the height of this symbol\r |
348 | @param settings (object) additional settings for the symbol (optional)\r |
349 | @return (element) the new symbol node */\r |
350 | symbol: function(parent, id, x1, y1, width, height, settings) {\r |
351 | var args = this._args(arguments, ['id', 'x1', 'y1', 'width', 'height']);\r |
352 | return this._makeNode(args.parent, 'symbol', $.extend({id: args.id,\r |
353 | viewBox: args.x1 + ' ' + args.y1 + ' ' + args.width + ' ' + args.height},\r |
354 | args.settings || {}));\r |
355 | },\r |
356 | \r |
357 | /* Add a marker definition.\r |
358 | @param parent (element or jQuery) the parent node for the new marker (optional)\r |
359 | @param id (string) the ID of this marker\r |
360 | @param refX (number) the x-coordinate for the reference point\r |
361 | @param refY (number) the y-coordinate for the reference point\r |
362 | @param mWidth (number) the marker viewport width\r |
363 | @param mHeight (number) the marker viewport height\r |
364 | @param orient (string or int) 'auto' or angle (degrees) (optional)\r |
365 | @param settings (object) additional settings for the marker (optional)\r |
366 | @return (element) the new marker node */\r |
367 | marker: function(parent, id, refX, refY, mWidth, mHeight, orient, settings) {\r |
368 | var args = this._args(arguments, ['id', 'refX', 'refY',\r |
369 | 'mWidth', 'mHeight', 'orient'], ['orient']);\r |
370 | return this._makeNode(args.parent, 'marker', $.extend(\r |
371 | {id: args.id, refX: args.refX, refY: args.refY, markerWidth: args.mWidth, \r |
372 | markerHeight: args.mHeight, orient: args.orient || 'auto'}, args.settings || {}));\r |
373 | },\r |
374 | \r |
375 | /* Add a style node.\r |
376 | @param parent (element or jQuery) the parent node for the new node (optional)\r |
377 | @param styles (string) the CSS styles\r |
378 | @param settings (object) additional settings for the node (optional)\r |
379 | @return (element) the new style node */\r |
380 | style: function(parent, styles, settings) {\r |
381 | var args = this._args(arguments, ['styles']);\r |
382 | var node = this._makeNode(args.parent, 'style', $.extend(\r |
383 | {type: 'text/css'}, args.settings || {}));\r |
384 | node.appendChild(this._svg.ownerDocument.createTextNode(args.styles));\r |
385 | if ($.browser.opera) {\r |
386 | $('head').append('<style type="text/css">' + args.styles + '</style>');\r |
387 | }\r |
388 | return node;\r |
389 | },\r |
390 | \r |
391 | /* Add a script node.\r |
392 | @param parent (element or jQuery) the parent node for the new node (optional)\r |
393 | @param script (string) the JavaScript code\r |
394 | @param type (string) the MIME type for the code (optional, default 'text/javascript')\r |
395 | @param settings (object) additional settings for the node (optional)\r |
396 | @return (element) the new script node */\r |
397 | script: function(parent, script, type, settings) {\r |
398 | var args = this._args(arguments, ['script', 'type'], ['type']);\r |
399 | var node = this._makeNode(args.parent, 'script', $.extend(\r |
400 | {type: args.type || 'text/javascript'}, args.settings || {}));\r |
401 | node.appendChild(this._svg.ownerDocument.createTextNode(this._escapeXML(args.script)));\r |
402 | if (!$.browser.mozilla) {\r |
403 | $.globalEval(args.script);\r |
404 | }\r |
405 | return node;\r |
406 | },\r |
407 | \r |
408 | /* Add a linear gradient definition.\r |
409 | Specify all of x1, y1, x2, y2 or none of them.\r |
410 | @param parent (element or jQuery) the parent node for the new gradient (optional)\r |
411 | @param id (string) the ID for this gradient\r |
412 | @param stops (string[][]) the gradient stops, each entry is\r |
413 | [0] is offset (0.0-1.0 or 0%-100%), [1] is colour, \r |
414 | [2] is opacity (optional)\r |
415 | @param x1 (number) the x-coordinate of the gradient start (optional)\r |
416 | @param y1 (number) the y-coordinate of the gradient start (optional)\r |
417 | @param x2 (number) the x-coordinate of the gradient end (optional)\r |
418 | @param y2 (number) the y-coordinate of the gradient end (optional)\r |
419 | @param settings (object) additional settings for the gradient (optional)\r |
420 | @return (element) the new gradient node */\r |
421 | linearGradient: function(parent, id, stops, x1, y1, x2, y2, settings) {\r |
422 | var args = this._args(arguments,\r |
423 | ['id', 'stops', 'x1', 'y1', 'x2', 'y2'], ['x1']);\r |
424 | var sets = $.extend({id: args.id}, \r |
425 | (args.x1 != null ? {x1: args.x1, y1: args.y1, x2: args.x2, y2: args.y2} : {}));\r |
426 | return this._gradient(args.parent, 'linearGradient', \r |
427 | $.extend(sets, args.settings || {}), args.stops);\r |
428 | },\r |
429 | \r |
430 | /* Add a radial gradient definition.\r |
431 | Specify all of cx, cy, r, fx, fy or none of them.\r |
432 | @param parent (element or jQuery) the parent node for the new gradient (optional)\r |
433 | @param id (string) the ID for this gradient\r |
434 | @param stops (string[][]) the gradient stops, each entry\r |
435 | [0] is offset, [1] is colour, [2] is opacity (optional)\r |
436 | @param cx (number) the x-coordinate of the largest circle centre (optional)\r |
437 | @param cy (number) the y-coordinate of the largest circle centre (optional)\r |
438 | @param r (number) the radius of the largest circle (optional)\r |
439 | @param fx (number) the x-coordinate of the gradient focus (optional)\r |
440 | @param fy (number) the y-coordinate of the gradient focus (optional)\r |
441 | @param settings (object) additional settings for the gradient (optional)\r |
442 | @return (element) the new gradient node */\r |
443 | radialGradient: function(parent, id, stops, cx, cy, r, fx, fy, settings) {\r |
444 | var args = this._args(arguments,\r |
445 | ['id', 'stops', 'cx', 'cy', 'r', 'fx', 'fy'], ['cx']);\r |
446 | var sets = $.extend({id: args.id}, (args.cx != null ?\r |
447 | {cx: args.cx, cy: args.cy, r: args.r, fx: args.fx, fy: args.fy} : {}));\r |
448 | return this._gradient(args.parent, 'radialGradient', \r |
449 | $.extend(sets, args.settings || {}), args.stops);\r |
450 | },\r |
451 | \r |
452 | /* Add a gradient node. */\r |
453 | _gradient: function(parent, name, settings, stops) {\r |
454 | var node = this._makeNode(parent, name, settings);\r |
455 | for (var i = 0; i < stops.length; i++) {\r |
456 | var stop = stops[i];\r |
457 | this._makeNode(node, 'stop', $.extend(\r |
458 | {offset: stop[0], stopColor: stop[1]}, \r |
459 | (stop[2] != null ? {stopOpacity: stop[2]} : {})));\r |
460 | }\r |
461 | return node;\r |
462 | },\r |
463 | \r |
464 | /* Add a pattern definition.\r |
465 | Specify all of vx, vy, xwidth, vheight or none of them.\r |
466 | @param parent (element or jQuery) the parent node for the new pattern (optional)\r |
467 | @param id (string) the ID for this pattern\r |
468 | @param x (number) the x-coordinate for the left edge of the pattern\r |
469 | @param y (number) the y-coordinate for the top edge of the pattern\r |
470 | @param width (number) the width of the pattern\r |
471 | @param height (number) the height of the pattern\r |
472 | @param vx (number) the minimum x-coordinate for view box (optional)\r |
473 | @param vy (number) the minimum y-coordinate for the view box (optional)\r |
474 | @param vwidth (number) the width of the view box (optional)\r |
475 | @param vheight (number) the height of the view box (optional)\r |
476 | @param settings (object) additional settings for the pattern (optional)\r |
477 | @return (element) the new pattern node */\r |
478 | pattern: function(parent, id, x, y, width, height, vx, vy, vwidth, vheight, settings) {\r |
479 | var args = this._args(arguments, ['id', 'x', 'y', 'width', 'height',\r |
480 | 'vx', 'vy', 'vwidth', 'vheight'], ['vx']);\r |
481 | var sets = $.extend({id: args.id, x: args.x, y: args.y,\r |
482 | width: args.width, height: args.height}, (args.vx != null ?\r |
483 | {viewBox: args.vx + ' ' + args.vy + ' ' + args.vwidth + ' ' + args.vheight} : {}));\r |
484 | return this._makeNode(args.parent, 'pattern', $.extend(sets, args.settings || {}));\r |
485 | },\r |
486 | \r |
487 | /* Add a mask definition.\r |
488 | @param parent (element or jQuery) the parent node for the new mask (optional)\r |
489 | @param id (string) the ID for this mask\r |
490 | @param x (number) the x-coordinate for the left edge of the mask\r |
491 | @param y (number) the y-coordinate for the top edge of the mask\r |
492 | @param width (number) the width of the mask\r |
493 | @param height (number) the height of the mask\r |
494 | @param settings (object) additional settings for the mask (optional)\r |
495 | @return (element) the new mask node */\r |
496 | mask: function(parent, id, x, y, width, height, settings) {\r |
497 | var args = this._args(arguments, ['id', 'x', 'y', 'width', 'height']);\r |
498 | return this._makeNode(args.parent, 'mask', $.extend(\r |
499 | {id: args.id, x: args.x, y: args.y, width: args.width, height: args.height},\r |
500 | args.settings || {}));\r |
501 | },\r |
502 | \r |
503 | /* Create a new path object.\r |
504 | @return (SVGPath) a new path object */\r |
505 | createPath: function() {\r |
506 | return new SVGPath();\r |
507 | },\r |
508 | \r |
509 | /* Create a new text object.\r |
510 | @return (SVGText) a new text object */\r |
511 | createText: function() {\r |
512 | return new SVGText();\r |
513 | },\r |
514 | \r |
515 | /* Add an embedded SVG element.\r |
516 | Specify all of vx, vy, vwidth, vheight or none of them.\r |
517 | @param parent (element or jQuery) the parent node for the new node (optional)\r |
518 | @param x (number) the x-coordinate for the left edge of the node\r |
519 | @param y (number) the y-coordinate for the top edge of the node\r |
520 | @param width (number) the width of the node\r |
521 | @param height (number) the height of the node\r |
522 | @param vx (number) the minimum x-coordinate for view box (optional)\r |
523 | @param vy (number) the minimum y-coordinate for the view box (optional)\r |
524 | @param vwidth (number) the width of the view box (optional)\r |
525 | @param vheight (number) the height of the view box (optional)\r |
526 | @param settings (object) additional settings for the node (optional)\r |
527 | @return (element) the new node */\r |
528 | svg: function(parent, x, y, width, height, vx, vy, vwidth, vheight, settings) {\r |
529 | var args = this._args(arguments, ['x', 'y', 'width', 'height',\r |
530 | 'vx', 'vy', 'vwidth', 'vheight'], ['vx']);\r |
531 | var sets = $.extend({x: args.x, y: args.y, width: args.width, height: args.height}, \r |
532 | (args.vx != null ? {viewBox: args.vx + ' ' + args.vy + ' ' +\r |
533 | args.vwidth + ' ' + args.vheight} : {}));\r |
534 | return this._makeNode(args.parent, 'svg', $.extend(sets, args.settings || {}));\r |
535 | },\r |
536 | \r |
537 | /* Create a group.\r |
538 | @param parent (element or jQuery) the parent node for the new group (optional)\r |
539 | @param id (string) the ID of this group (optional)\r |
540 | @param settings (object) additional settings for the group (optional)\r |
541 | @return (element) the new group node */\r |
542 | group: function(parent, id, settings) {\r |
543 | var args = this._args(arguments, ['id'], ['id']);\r |
544 | return this._makeNode(args.parent, 'g', $.extend({id: args.id}, args.settings || {}));\r |
545 | },\r |
546 | \r |
547 | /* Add a usage reference.\r |
548 | Specify all of x, y, width, height or none of them.\r |
549 | @param parent (element or jQuery) the parent node for the new node (optional)\r |
550 | @param x (number) the x-coordinate for the left edge of the node (optional)\r |
551 | @param y (number) the y-coordinate for the top edge of the node (optional)\r |
552 | @param width (number) the width of the node (optional)\r |
553 | @param height (number) the height of the node (optional)\r |
554 | @param ref (string) the ID of the definition node\r |
555 | @param settings (object) additional settings for the node (optional)\r |
556 | @return (element) the new node */\r |
557 | use: function(parent, x, y, width, height, ref, settings) {\r |
558 | var args = this._args(arguments, ['x', 'y', 'width', 'height', 'ref']);\r |
559 | if (typeof args.x == 'string') {\r |
560 | args.ref = args.x;\r |
561 | args.settings = args.y;\r |
562 | args.x = args.y = args.width = args.height = null;\r |
563 | }\r |
564 | var node = this._makeNode(args.parent, 'use', $.extend(\r |
565 | {x: args.x, y: args.y, width: args.width, height: args.height},\r |
566 | args.settings || {}));\r |
567 | node.setAttributeNS($.svg.xlinkNS, 'href', args.ref);\r |
568 | return node;\r |
569 | },\r |
570 | \r |
571 | /* Add a link, which applies to all child elements.\r |
572 | @param parent (element or jQuery) the parent node for the new link (optional)\r |
573 | @param ref (string) the target URL\r |
574 | @param settings (object) additional settings for the link (optional)\r |
575 | @return (element) the new link node */\r |
576 | link: function(parent, ref, settings) {\r |
577 | var args = this._args(arguments, ['ref']);\r |
578 | var node = this._makeNode(args.parent, 'a', args.settings);\r |
579 | node.setAttributeNS($.svg.xlinkNS, 'href', args.ref);\r |
580 | return node;\r |
581 | },\r |
582 | \r |
583 | /* Add an image.\r |
584 | @param parent (element or jQuery) the parent node for the new image (optional)\r |
585 | @param x (number) the x-coordinate for the left edge of the image\r |
586 | @param y (number) the y-coordinate for the top edge of the image\r |
587 | @param width (number) the width of the image\r |
588 | @param height (number) the height of the image\r |
589 | @param ref (string) the path to the image\r |
590 | @param settings (object) additional settings for the image (optional)\r |
591 | @return (element) the new image node */\r |
592 | image: function(parent, x, y, width, height, ref, settings) {\r |
593 | var args = this._args(arguments, ['x', 'y', 'width', 'height', 'ref']);\r |
594 | var node = this._makeNode(args.parent, 'image', $.extend(\r |
595 | {x: args.x, y: args.y, width: args.width, height: args.height},\r |
596 | args.settings || {}));\r |
597 | node.setAttributeNS($.svg.xlinkNS, 'href', args.ref);\r |
598 | return node;\r |
599 | },\r |
600 | \r |
601 | /* Draw a path.\r |
602 | @param parent (element or jQuery) the parent node for the new shape (optional)\r |
603 | @param path (string or SVGPath) the path to draw\r |
604 | @param settings (object) additional settings for the shape (optional)\r |
605 | @return (element) the new shape node */\r |
606 | path: function(parent, path, settings) {\r |
607 | var args = this._args(arguments, ['path']);\r |
608 | return this._makeNode(args.parent, 'path', $.extend(\r |
609 | {d: (args.path.path ? args.path.path() : args.path)}, args.settings || {}));\r |
610 | },\r |
611 | \r |
612 | /* Draw a rectangle.\r |
613 | Specify both of rx and ry or neither.\r |
614 | @param parent (element or jQuery) the parent node for the new shape (optional)\r |
615 | @param x (number) the x-coordinate for the left edge of the rectangle\r |
616 | @param y (number) the y-coordinate for the top edge of the rectangle\r |
617 | @param width (number) the width of the rectangle\r |
618 | @param height (number) the height of the rectangle\r |
619 | @param rx (number) the x-radius of the ellipse for the rounded corners (optional)\r |
620 | @param ry (number) the y-radius of the ellipse for the rounded corners (optional)\r |
621 | @param settings (object) additional settings for the shape (optional)\r |
622 | @return (element) the new shape node */\r |
623 | rect: function(parent, x, y, width, height, rx, ry, settings) {\r |
624 | var args = this._args(arguments, ['x', 'y', 'width', 'height', 'rx', 'ry'], ['rx']);\r |
625 | return this._makeNode(args.parent, 'rect', $.extend(\r |
626 | {x: args.x, y: args.y, width: args.width, height: args.height},\r |
627 | (args.rx ? {rx: args.rx, ry: args.ry} : {}), args.settings || {}));\r |
628 | },\r |
629 | \r |
630 | /* Draw a circle.\r |
631 | @param parent (element or jQuery) the parent node for the new shape (optional)\r |
632 | @param cx (number) the x-coordinate for the centre of the circle\r |
633 | @param cy (number) the y-coordinate for the centre of the circle\r |
634 | @param r (number) the radius of the circle\r |
635 | @param settings (object) additional settings for the shape (optional)\r |
636 | @return (element) the new shape node */\r |
637 | circle: function(parent, cx, cy, r, settings) {\r |
638 | var args = this._args(arguments, ['cx', 'cy', 'r']);\r |
639 | return this._makeNode(args.parent, 'circle', $.extend(\r |
640 | {cx: args.cx, cy: args.cy, r: args.r}, args.settings || {}));\r |
641 | },\r |
642 | \r |
643 | /* Draw an ellipse.\r |
644 | @param parent (element or jQuery) the parent node for the new shape (optional)\r |
645 | @param cx (number) the x-coordinate for the centre of the ellipse\r |
646 | @param cy (number) the y-coordinate for the centre of the ellipse\r |
647 | @param rx (number) the x-radius of the ellipse\r |
648 | @param ry (number) the y-radius of the ellipse\r |
649 | @param settings (object) additional settings for the shape (optional)\r |
650 | @return (element) the new shape node */\r |
651 | ellipse: function(parent, cx, cy, rx, ry, settings) {\r |
652 | var args = this._args(arguments, ['cx', 'cy', 'rx', 'ry']);\r |
653 | return this._makeNode(args.parent, 'ellipse', $.extend(\r |
654 | {cx: args.cx, cy: args.cy, rx: args.rx, ry: args.ry}, args.settings || {}));\r |
655 | },\r |
656 | \r |
657 | /* Draw a line.\r |
658 | @param parent (element or jQuery) the parent node for the new shape (optional)\r |
659 | @param x1 (number) the x-coordinate for the start of the line\r |
660 | @param y1 (number) the y-coordinate for the start of the line\r |
661 | @param x2 (number) the x-coordinate for the end of the line\r |
662 | @param y2 (number) the y-coordinate for the end of the line\r |
663 | @param settings (object) additional settings for the shape (optional)\r |
664 | @return (element) the new shape node */\r |
665 | line: function(parent, x1, y1, x2, y2, settings) {\r |
666 | var args = this._args(arguments, ['x1', 'y1', 'x2', 'y2']);\r |
667 | return this._makeNode(args.parent, 'line', $.extend(\r |
668 | {x1: args.x1, y1: args.y1, x2: args.x2, y2: args.y2}, args.settings || {}));\r |
669 | },\r |
670 | \r |
671 | /* Draw a polygonal line.\r |
672 | @param parent (element or jQuery) the parent node for the new shape (optional)\r |
673 | @param points (number[][]) the x-/y-coordinates for the points on the line\r |
674 | @param settings (object) additional settings for the shape (optional)\r |
675 | @return (element) the new shape node */\r |
676 | polyline: function(parent, points, settings) {\r |
677 | var args = this._args(arguments, ['points']);\r |
678 | return this._poly(args.parent, 'polyline', args.points, args.settings);\r |
679 | },\r |
680 | \r |
681 | /* Draw a polygonal shape.\r |
682 | @param parent (element or jQuery) the parent node for the new shape (optional)\r |
683 | @param points (number[][]) the x-/y-coordinates for the points on the shape\r |
684 | @param settings (object) additional settings for the shape (optional)\r |
685 | @return (element) the new shape node */\r |
686 | polygon: function(parent, points, settings) {\r |
687 | var args = this._args(arguments, ['points']);\r |
688 | return this._poly(args.parent, 'polygon', args.points, args.settings);\r |
689 | },\r |
690 | \r |
691 | /* Draw a polygonal line or shape. */\r |
692 | _poly: function(parent, name, points, settings) {\r |
693 | var ps = '';\r |
694 | for (var i = 0; i < points.length; i++) {\r |
695 | ps += points[i].join() + ' ';\r |
696 | }\r |
697 | return this._makeNode(parent, name, $.extend(\r |
698 | {points: $.trim(ps)}, settings || {}));\r |
699 | },\r |
700 | \r |
701 | /* Draw text.\r |
702 | Specify both of x and y or neither of them.\r |
703 | @param parent (element or jQuery) the parent node for the text (optional)\r |
704 | @param x (number or number[]) the x-coordinate(s) for the text (optional)\r |
705 | @param y (number or number[]) the y-coordinate(s) for the text (optional)\r |
706 | @param value (string) the text content or\r |
707 | (SVGText) text with spans and references\r |
708 | @param settings (object) additional settings for the text (optional)\r |
709 | @return (element) the new text node */\r |
710 | text: function(parent, x, y, value, settings) {\r |
711 | var args = this._args(arguments, ['x', 'y', 'value']);\r |
712 | if (typeof args.x == 'string' && arguments.length < 4) {\r |
713 | args.value = args.x;\r |
714 | args.settings = args.y;\r |
715 | args.x = args.y = null;\r |
716 | }\r |
717 | return this._text(args.parent, 'text', args.value, $.extend(\r |
718 | {x: (args.x && isArray(args.x) ? args.x.join(' ') : args.x),\r |
719 | y: (args.y && isArray(args.y) ? args.y.join(' ') : args.y)}, \r |
720 | args.settings || {}));\r |
721 | },\r |
722 | \r |
723 | /* Draw text along a path.\r |
724 | @param parent (element or jQuery) the parent node for the text (optional)\r |
725 | @param path (string) the ID of the path\r |
726 | @param value (string) the text content or\r |
727 | (SVGText) text with spans and references\r |
728 | @param settings (object) additional settings for the text (optional)\r |
729 | @return (element) the new text node */\r |
730 | textpath: function(parent, path, value, settings) {\r |
731 | var args = this._args(arguments, ['path', 'value']);\r |
732 | var node = this._text(args.parent, 'textPath', args.value, args.settings || {});\r |
733 | node.setAttributeNS($.svg.xlinkNS, 'href', args.path);\r |
734 | return node;\r |
735 | },\r |
736 | \r |
737 | /* Draw text. */\r |
738 | _text: function(parent, name, value, settings) {\r |
739 | var node = this._makeNode(parent, name, settings);\r |
740 | if (typeof value == 'string') {\r |
741 | node.appendChild(node.ownerDocument.createTextNode(value));\r |
742 | }\r |
743 | else {\r |
744 | for (var i = 0; i < value._parts.length; i++) {\r |
745 | var part = value._parts[i];\r |
746 | if (part[0] == 'tspan') {\r |
747 | var child = this._makeNode(node, part[0], part[2]);\r |
748 | child.appendChild(node.ownerDocument.createTextNode(part[1]));\r |
749 | node.appendChild(child);\r |
750 | }\r |
751 | else if (part[0] == 'tref') {\r |
752 | var child = this._makeNode(node, part[0], part[2]);\r |
753 | child.setAttributeNS($.svg.xlinkNS, 'href', part[1]);\r |
754 | node.appendChild(child);\r |
755 | }\r |
756 | else if (part[0] == 'textpath') {\r |
757 | var set = $.extend({}, part[2]);\r |
758 | set.href = null;\r |
759 | var child = this._makeNode(node, part[0], set);\r |
760 | child.setAttributeNS($.svg.xlinkNS, 'href', part[2].href);\r |
761 | child.appendChild(node.ownerDocument.createTextNode(part[1]));\r |
762 | node.appendChild(child);\r |
763 | }\r |
764 | else { // straight text\r |
765 | node.appendChild(node.ownerDocument.createTextNode(part[1]));\r |
766 | }\r |
767 | }\r |
768 | }\r |
769 | return node;\r |
770 | },\r |
771 | \r |
772 | /* Add a custom SVG element.\r |
773 | @param parent (element or jQuery) the parent node for the new element (optional)\r |
774 | @param name (string) the name of the element\r |
775 | @param settings (object) additional settings for the element (optional)\r |
776 | @return (element) the new custom node */\r |
777 | other: function(parent, name, settings) {\r |
778 | var args = this._args(arguments, ['name']);\r |
779 | return this._makeNode(args.parent, args.name, args.settings || {});\r |
780 | },\r |
781 | \r |
782 | /* Create a shape node with the given settings. */\r |
783 | _makeNode: function(parent, name, settings) {\r |
784 | parent = parent || this._svg;\r |
785 | var node = this._svg.ownerDocument.createElementNS($.svg.svgNS, name);\r |
786 | for (var name in settings) {\r |
787 | var value = settings[name];\r |
788 | if (value != null && value != null && \r |
789 | (typeof value != 'string' || value != '')) {\r |
790 | node.setAttribute($.svg._attrNames[name] || name, value);\r |
791 | }\r |
792 | }\r |
793 | parent.appendChild(node);\r |
794 | return node;\r |
795 | },\r |
796 | \r |
797 | /* Add an existing SVG node to the diagram.\r |
798 | @param parent (element or jQuery) the parent node for the new node (optional)\r |
799 | @param node (element) the new node to add or\r |
800 | (string) the jQuery selector for the node or\r |
801 | (jQuery collection) set of nodes to add\r |
802 | @return (SVGWrapper) this wrapper */\r |
803 | add: function(parent, node) {\r |
804 | var args = this._args((arguments.length == 1 ? [null, parent] : arguments), ['node']);\r |
805 | var svg = this;\r |
806 | args.parent = args.parent || this._svg;\r |
807 | try {\r |
808 | if ($.svg._renesis) {\r |
809 | throw 'Force traversal';\r |
810 | }\r |
811 | args.parent.appendChild(args.node.cloneNode(true));\r |
812 | }\r |
813 | catch (e) {\r |
814 | args.node = (args.node.jquery ? args.node : $(args.node));\r |
815 | args.node.each(function() {\r |
816 | var child = svg._cloneAsSVG(this);\r |
817 | if (child) {\r |
818 | args.parent.appendChild(child);\r |
819 | }\r |
820 | });\r |
821 | }\r |
822 | return this;\r |
823 | },\r |
824 | \r |
825 | /* SVG nodes must belong to the SVG namespace, so clone and ensure this is so. */\r |
826 | _cloneAsSVG: function(node) {\r |
827 | var newNode = null;\r |
828 | if (node.nodeType == 1) { // element\r |
829 | newNode = this._svg.ownerDocument.createElementNS(\r |
830 | $.svg.svgNS, this._checkName(node.nodeName));\r |
831 | for (var i = 0; i < node.attributes.length; i++) {\r |
832 | var attr = node.attributes.item(i);\r |
833 | if (attr.nodeName != 'xmlns' && attr.nodeValue) {\r |
834 | if (attr.prefix == 'xlink') {\r |
835 | newNode.setAttributeNS($.svg.xlinkNS, attr.localName, attr.nodeValue);\r |
836 | }\r |
837 | else {\r |
838 | newNode.setAttribute(this._checkName(attr.nodeName), attr.nodeValue);\r |
839 | }\r |
840 | }\r |
841 | }\r |
842 | for (var i = 0; i < node.childNodes.length; i++) {\r |
843 | var child = this._cloneAsSVG(node.childNodes[i]);\r |
844 | if (child) {\r |
845 | newNode.appendChild(child);\r |
846 | }\r |
847 | }\r |
848 | }\r |
849 | else if (node.nodeType == 3) { // text\r |
850 | if ($.trim(node.nodeValue)) {\r |
851 | newNode = this._svg.ownerDocument.createTextNode(node.nodeValue);\r |
852 | }\r |
853 | }\r |
854 | else if (node.nodeType == 4) { // CDATA\r |
855 | if ($.trim(node.nodeValue)) {\r |
856 | try {\r |
857 | newNode = this._svg.ownerDocument.createCDATASection(node.nodeValue);\r |
858 | }\r |
859 | catch (e) {\r |
860 | newNode = this._svg.ownerDocument.createTextNode(\r |
861 | node.nodeValue.replace(/&/g, '&').\r |
862 | replace(/</g, '<').replace(/>/g, '>'));\r |
863 | }\r |
864 | }\r |
865 | }\r |
866 | return newNode;\r |
867 | },\r |
868 | \r |
869 | /* Node names must be lower case and without SVG namespace prefix. */\r |
870 | _checkName: function(name) {\r |
871 | name = (name.substring(0, 1) >= 'A' && name.substring(0, 1) <= 'Z' ?\r |
872 | name.toLowerCase() : name);\r |
873 | return (name.substring(0, 4) == 'svg:' ? name.substring(4) : name);\r |
874 | },\r |
875 | \r |
876 | /* Load an external SVG document.\r |
877 | @param url (string) the location of the SVG document or\r |
878 | the actual SVG content\r |
879 | @param settings (boolean) see addTo below or\r |
880 | (function) see onLoad below or\r |
881 | (object) additional settings for the load with attributes below:\r |
882 | addTo (boolean) true to add to what's already there,\r |
883 | or false to clear the canvas first\r |
884 | changeSize (boolean) true to allow the canvas size to change,\r |
885 | or false to retain the original\r |
886 | onLoad (function) callback after the document has loaded,\r |
887 | 'this' is the container, receives SVG object and\r |
888 | optional error message as a parameter\r |
889 | @return (SVGWrapper) this root */\r |
890 | load: function(url, settings) {\r |
891 | settings = (typeof settings == 'boolean'? {addTo: settings} :\r |
892 | (typeof settings == 'function'? {onLoad: settings} : settings || {}));\r |
893 | if (!settings.addTo) {\r |
894 | this.clear(false);\r |
895 | }\r |
896 | var size = [this._svg.getAttribute('width'), this._svg.getAttribute('height')];\r |
897 | var wrapper = this;\r |
898 | // Report a problem with the load\r |
899 | var reportError = function(message) {\r |
900 | message = $.svg.local.errorLoadingText + ': ' + message;\r |
901 | if (settings.onLoad) {\r |
902 | settings.onLoad.apply(wrapper._container || wrapper._svg, [wrapper, message]);\r |
903 | }\r |
904 | else {\r |
905 | wrapper.text(null, 10, 20, message);\r |
906 | }\r |
907 | };\r |
908 | // Create a DOM from SVG content\r |
909 | var loadXML4IE = function(data) {\r |
910 | var xml = new ActiveXObject('Microsoft.XMLDOM');\r |
911 | xml.validateOnParse = false;\r |
912 | xml.resolveExternals = false;\r |
913 | xml.async = false;\r |
914 | xml.loadXML(data);\r |
915 | if (xml.parseError.errorCode != 0) {\r |
916 | reportError(xml.parseError.reason);\r |
917 | return null;\r |
918 | }\r |
919 | return xml;\r |
920 | };\r |
921 | // Load the SVG DOM\r |
922 | var loadSVG = function(data) {\r |
923 | if (!data) {\r |
924 | return;\r |
925 | }\r |
926 | if (data.documentElement.nodeName != 'svg') {\r |
927 | var errors = data.getElementsByTagName('parsererror');\r |
928 | var messages = (errors.length ? errors[0].getElementsByTagName('div') : []); // Safari\r |
929 | reportError(!errors.length ? '???' :\r |
930 | (messages.length ? messages[0] : errors[0]).firstChild.nodeValue);\r |
931 | return;\r |
932 | }\r |
933 | var attrs = {};\r |
934 | for (var i = 0; i < data.documentElement.attributes.length; i++) {\r |
935 | var attr = data.documentElement.attributes.item(i);\r |
936 | if (!(attr.nodeName == 'version' || attr.nodeName.substring(0, 5) == 'xmlns')) {\r |
937 | attrs[attr.nodeName] = attr.nodeValue;\r |
938 | }\r |
939 | }\r |
940 | wrapper.configure(attrs, true);\r |
941 | var nodes = data.documentElement.childNodes;\r |
942 | for (var i = 0; i < nodes.length; i++) {\r |
943 | try {\r |
944 | if ($.svg._renesis) {\r |
945 | throw 'Force traversal';\r |
946 | }\r |
947 | wrapper._svg.appendChild(nodes[i].cloneNode(true));\r |
948 | if (nodes[i].nodeName == 'script') {\r |
949 | $.globalEval(nodes[i].textContent);\r |
950 | }\r |
951 | }\r |
952 | catch (e) {\r |
953 | wrapper.add(null, nodes[i]);\r |
954 | }\r |
955 | }\r |
956 | if (!settings.changeSize) {\r |
957 | wrapper.configure({width: size[0], height: size[1]});\r |
958 | }\r |
959 | if (settings.onLoad) {\r |
960 | settings.onLoad.apply(wrapper._container || wrapper._svg, [wrapper]);\r |
961 | }\r |
962 | };\r |
963 | if (url.match('<svg')) { // Inline SVG\r |
964 | loadSVG($.browser.msie ? loadXML4IE(url) :\r |
965 | new DOMParser().parseFromString(url, 'text/xml'));\r |
966 | }\r |
967 | else { // Remote SVG\r |
968 | $.ajax({url: url, dataType: ($.browser.msie ? 'text' : 'xml'),\r |
969 | success: function(xml) {\r |
970 | loadSVG($.browser.msie ? loadXML4IE(xml) : xml);\r |
971 | }, error: function(http, message, exc) {\r |
972 | reportError(message + (exc ? ' ' + exc.message : ''));\r |
973 | }});\r |
974 | }\r |
975 | return this;\r |
976 | },\r |
977 | \r |
978 | /* Delete a specified node.\r |
979 | @param node (element or jQuery) the drawing node to remove\r |
980 | @return (SVGWrapper) this root */\r |
981 | remove: function(node) {\r |
982 | node = (node.jquery ? node[0] : node);\r |
983 | node.parentNode.removeChild(node);\r |
984 | return this;\r |
985 | },\r |
986 | \r |
987 | /* Delete everything in the current document.\r |
988 | @param attrsToo (boolean) true to clear any root attributes as well,\r |
989 | false to leave them (optional)\r |
990 | @return (SVGWrapper) this root */\r |
991 | clear: function(attrsToo) {\r |
992 | if (attrsToo) {\r |
993 | this.configure({}, true);\r |
994 | }\r |
995 | while (this._svg.firstChild) {\r |
996 | this._svg.removeChild(this._svg.firstChild);\r |
997 | }\r |
998 | return this;\r |
999 | },\r |
1000 | \r |
1001 | /* Serialise the current diagram into an SVG text document.\r |
1002 | @param node (SVG element) the starting node (optional)\r |
1003 | @return (string) the SVG as text */\r |
1004 | toSVG: function(node) {\r |
1005 | node = node || this._svg;\r |
1006 | return (typeof XMLSerializer == 'undefined' ? this._toSVG(node) :\r |
1007 | new XMLSerializer().serializeToString(node));\r |
1008 | },\r |
1009 | \r |
1010 | /* Serialise one node in the SVG hierarchy. */\r |
1011 | _toSVG: function(node) {\r |
1012 | var svgDoc = '';\r |
1013 | if (!node) {\r |
1014 | return svgDoc;\r |
1015 | }\r |
1016 | if (node.nodeType == 3) { // Text\r |
1017 | svgDoc = node.nodeValue;\r |
1018 | }\r |
1019 | else if (node.nodeType == 4) { // CDATA\r |
1020 | svgDoc = '<![CDATA[' + node.nodeValue + ']]>';\r |
1021 | }\r |
1022 | else { // Element\r |
1023 | svgDoc = '<' + node.nodeName;\r |
1024 | if (node.attributes) {\r |
1025 | for (var i = 0; i < node.attributes.length; i++) {\r |
1026 | var attr = node.attributes.item(i);\r |
1027 | if (!($.trim(attr.nodeValue) == '' || attr.nodeValue.match(/^\[object/) ||\r |
1028 | attr.nodeValue.match(/^function/))) {\r |
1029 | svgDoc += ' ' + (attr.namespaceURI == $.svg.xlinkNS ? 'xlink:' : '') + \r |
1030 | attr.nodeName + '="' + attr.nodeValue + '"';\r |
1031 | }\r |
1032 | }\r |
1033 | } \r |
1034 | if (node.firstChild) {\r |
1035 | svgDoc += '>';\r |
1036 | var child = node.firstChild;\r |
1037 | while (child) {\r |
1038 | svgDoc += this._toSVG(child);\r |
1039 | child = child.nextSibling;\r |
1040 | }\r |
1041 | svgDoc += '</' + node.nodeName + '>';\r |
1042 | }\r |
1043 | else {\r |
1044 | svgDoc += '/>';\r |
1045 | }\r |
1046 | }\r |
1047 | return svgDoc;\r |
1048 | },\r |
1049 | \r |
1050 | /* Escape reserved characters in XML. */\r |
1051 | _escapeXML: function(text) {\r |
1052 | text = text.replace(/&/g, '&');\r |
1053 | text = text.replace(/</g, '<');\r |
1054 | text = text.replace(/>/g, '>');\r |
1055 | return text;\r |
1056 | }\r |
1057 | });\r |
1058 | \r |
1059 | /* Helper to generate an SVG path.\r |
1060 | Obtain an instance from the SVGWrapper object.\r |
1061 | String calls together to generate the path and use its value:\r |
1062 | var path = root.createPath();\r |
1063 | root.path(null, path.move(100, 100).line(300, 100).line(200, 300).close(), {fill: 'red'});\r |
1064 | or\r |
1065 | root.path(null, path.move(100, 100).line([[300, 100], [200, 300]]).close(), {fill: 'red'}); */\r |
1066 | function SVGPath() {\r |
1067 | this._path = '';\r |
1068 | }\r |
1069 | \r |
1070 | $.extend(SVGPath.prototype, {\r |
1071 | /* Prepare to create a new path.\r |
1072 | @return (SVGPath) this path */\r |
1073 | reset: function() {\r |
1074 | this._path = '';\r |
1075 | return this;\r |
1076 | },\r |
1077 | \r |
1078 | /* Move the pointer to a position.\r |
1079 | @param x (number) x-coordinate to move to or\r |
1080 | (number[][]) x-/y-coordinates to move to\r |
1081 | @param y (number) y-coordinate to move to (omitted if x is array)\r |
1082 | @param relative (boolean) true for coordinates relative to the current point,\r |
1083 | false for coordinates being absolute\r |
1084 | @return (SVGPath) this path */\r |
1085 | move: function(x, y, relative) {\r |
1086 | relative = (isArray(x) ? y : relative);\r |
1087 | return this._coords((relative ? 'm' : 'M'), x, y);\r |
1088 | },\r |
1089 | \r |
1090 | /* Draw a line to a position.\r |
1091 | @param x (number) x-coordinate to move to or\r |
1092 | (number[][]) x-/y-coordinates to move to\r |
1093 | @param y (number) y-coordinate to move to (omitted if x is array)\r |
1094 | @param relative (boolean) true for coordinates relative to the current point,\r |
1095 | false for coordinates being absolute\r |
1096 | @return (SVGPath) this path */\r |
1097 | line: function(x, y, relative) {\r |
1098 | relative = (isArray(x) ? y : relative);\r |
1099 | return this._coords((relative ? 'l' : 'L'), x, y);\r |
1100 | },\r |
1101 | \r |
1102 | /* Draw a horizontal line to a position.\r |
1103 | @param x (number) x-coordinate to draw to or\r |
1104 | (number[]) x-coordinates to draw to\r |
1105 | @param relative (boolean) true for coordinates relative to the current point,\r |
1106 | false for coordinates being absolute\r |
1107 | @return (SVGPath) this path */\r |
1108 | horiz: function(x, relative) {\r |
1109 | this._path += (relative ? 'h' : 'H') + (isArray(x) ? x.join(' ') : x);\r |
1110 | return this;\r |
1111 | },\r |
1112 | \r |
1113 | /* Draw a vertical line to a position.\r |
1114 | @param y (number) y-coordinate to draw to or\r |
1115 | (number[]) y-coordinates to draw to\r |
1116 | @param relative (boolean) true for coordinates relative to the current point,\r |
1117 | false for coordinates being absolute\r |
1118 | @return (SVGPath) this path */\r |
1119 | vert: function(y, relative) {\r |
1120 | this._path += (relative ? 'v' : 'V') + (isArray(y) ? y.join(' ') : y);\r |
1121 | return this;\r |
1122 | },\r |
1123 | \r |
1124 | /* Draw a cubic Bézier curve.\r |
1125 | @param x1 (number) x-coordinate of beginning control point or\r |
1126 | (number[][]) x-/y-coordinates of control and end points to draw to\r |
1127 | @param y1 (number) y-coordinate of beginning control point (omitted if x1 is array)\r |
1128 | @param x2 (number) x-coordinate of ending control point (omitted if x1 is array)\r |
1129 | @param y2 (number) y-coordinate of ending control point (omitted if x1 is array)\r |
1130 | @param x (number) x-coordinate of curve end (omitted if x1 is array)\r |
1131 | @param y (number) y-coordinate of curve end (omitted if x1 is array)\r |
1132 | @param relative (boolean) true for coordinates relative to the current point,\r |
1133 | false for coordinates being absolute\r |
1134 | @return (SVGPath) this path */\r |
1135 | curveC: function(x1, y1, x2, y2, x, y, relative) {\r |
1136 | relative = (isArray(x1) ? y1 : relative);\r |
1137 | return this._coords((relative ? 'c' : 'C'), x1, y1, x2, y2, x, y);\r |
1138 | },\r |
1139 | \r |
1140 | /* Continue a cubic Bézier curve.\r |
1141 | Starting control point is the reflection of the previous end control point.\r |
1142 | @param x2 (number) x-coordinate of ending control point or\r |
1143 | (number[][]) x-/y-coordinates of control and end points to draw to\r |
1144 | @param y2 (number) y-coordinate of ending control point (omitted if x2 is array)\r |
1145 | @param x (number) x-coordinate of curve end (omitted if x2 is array)\r |
1146 | @param y (number) y-coordinate of curve end (omitted if x2 is array)\r |
1147 | @param relative (boolean) true for coordinates relative to the current point,\r |
1148 | false for coordinates being absolute\r |
1149 | @return (SVGPath) this path */\r |
1150 | smoothC: function(x2, y2, x, y, relative) {\r |
1151 | relative = (isArray(x2) ? y2 : relative);\r |
1152 | return this._coords((relative ? 's' : 'S'), x2, y2, x, y);\r |
1153 | },\r |
1154 | \r |
1155 | /* Draw a quadratic Bézier curve.\r |
1156 | @param x1 (number) x-coordinate of control point or\r |
1157 | (number[][]) x-/y-coordinates of control and end points to draw to\r |
1158 | @param y1 (number) y-coordinate of control point (omitted if x1 is array)\r |
1159 | @param x (number) x-coordinate of curve end (omitted if x1 is array)\r |
1160 | @param y (number) y-coordinate of curve end (omitted if x1 is array)\r |
1161 | @param relative (boolean) true for coordinates relative to the current point,\r |
1162 | false for coordinates being absolute\r |
1163 | @return (SVGPath) this path */\r |
1164 | curveQ: function(x1, y1, x, y, relative) {\r |
1165 | relative = (isArray(x1) ? y1 : relative);\r |
1166 | return this._coords((relative ? 'q' : 'Q'), x1, y1, x, y);\r |
1167 | },\r |
1168 | \r |
1169 | /* Continue a quadratic Bézier curve.\r |
1170 | Control point is the reflection of the previous control point.\r |
1171 | @param x (number) x-coordinate of curve end or\r |
1172 | (number[][]) x-/y-coordinates of points to draw to\r |
1173 | @param y (number) y-coordinate of curve end (omitted if x is array)\r |
1174 | @param relative (boolean) true for coordinates relative to the current point,\r |
1175 | false for coordinates being absolute\r |
1176 | @return (SVGPath) this path */\r |
1177 | smoothQ: function(x, y, relative) {\r |
1178 | relative = (isArray(x) ? y : relative);\r |
1179 | return this._coords((relative ? 't' : 'T'), x, y);\r |
1180 | },\r |
1181 | \r |
1182 | /* Generate a path command with (a list of) coordinates. */\r |
1183 | _coords: function(cmd, x1, y1, x2, y2, x3, y3) {\r |
1184 | if (isArray(x1)) {\r |
1185 | for (var i = 0; i < x1.length; i++) {\r |
1186 | var cs = x1[i];\r |
1187 | this._path += (i == 0 ? cmd : ' ') + cs[0] + ',' + cs[1] +\r |
1188 | (cs.length < 4 ? '' : ' ' + cs[2] + ',' + cs[3] +\r |
1189 | (cs.length < 6 ? '': ' ' + cs[4] + ',' + cs[5]));\r |
1190 | }\r |
1191 | }\r |
1192 | else {\r |
1193 | this._path += cmd + x1 + ',' + y1 + \r |
1194 | (x2 == null ? '' : ' ' + x2 + ',' + y2 +\r |
1195 | (x3 == null ? '' : ' ' + x3 + ',' + y3));\r |
1196 | }\r |
1197 | return this;\r |
1198 | },\r |
1199 | \r |
1200 | /* Draw an arc to a position.\r |
1201 | @param rx (number) x-radius of arc or\r |
1202 | (number/boolean[][]) x-/y-coordinates and flags for points to draw to\r |
1203 | @param ry (number) y-radius of arc (omitted if rx is array)\r |
1204 | @param xRotate (number) x-axis rotation (degrees, clockwise) (omitted if rx is array)\r |
1205 | @param large (boolean) true to draw the large part of the arc,\r |
1206 | false to draw the small part (omitted if rx is array)\r |
1207 | @param clockwise (boolean) true to draw the clockwise arc,\r |
1208 | false to draw the anti-clockwise arc (omitted if rx is array)\r |
1209 | @param x (number) x-coordinate of arc end (omitted if rx is array)\r |
1210 | @param y (number) y-coordinate of arc end (omitted if rx is array)\r |
1211 | @param relative (boolean) true for coordinates relative to the current point,\r |
1212 | false for coordinates being absolute\r |
1213 | @return (SVGPath) this path */\r |
1214 | arc: function(rx, ry, xRotate, large, clockwise, x, y, relative) {\r |
1215 | relative = (isArray(rx) ? ry : relative);\r |
1216 | this._path += (relative ? 'a' : 'A');\r |
1217 | if (isArray(rx)) {\r |
1218 | for (var i = 0; i < rx.length; i++) {\r |
1219 | var cs = rx[i];\r |
1220 | this._path += (i == 0 ? '' : ' ') + cs[0] + ',' + cs[1] + ' ' +\r |
1221 | cs[2] + ' ' + (cs[3] ? '1' : '0') + ',' +\r |
1222 | (cs[4] ? '1' : '0') + ' ' + cs[5] + ',' + cs[6];\r |
1223 | }\r |
1224 | }\r |
1225 | else {\r |
1226 | this._path += rx + ',' + ry + ' ' + xRotate + ' ' +\r |
1227 | (large ? '1' : '0') + ',' + (clockwise ? '1' : '0') + ' ' + x + ',' + y;\r |
1228 | }\r |
1229 | return this;\r |
1230 | },\r |
1231 | \r |
1232 | /* Close the current path.\r |
1233 | @return (SVGPath) this path */\r |
1234 | close: function() {\r |
1235 | this._path += 'z';\r |
1236 | return this;\r |
1237 | },\r |
1238 | \r |
1239 | /* Return the string rendering of the specified path.\r |
1240 | @return (string) stringified path */\r |
1241 | path: function() {\r |
1242 | return this._path;\r |
1243 | }\r |
1244 | });\r |
1245 | \r |
1246 | SVGPath.prototype.moveTo = SVGPath.prototype.move;\r |
1247 | SVGPath.prototype.lineTo = SVGPath.prototype.line;\r |
1248 | SVGPath.prototype.horizTo = SVGPath.prototype.horiz;\r |
1249 | SVGPath.prototype.vertTo = SVGPath.prototype.vert;\r |
1250 | SVGPath.prototype.curveCTo = SVGPath.prototype.curveC;\r |
1251 | SVGPath.prototype.smoothCTo = SVGPath.prototype.smoothC;\r |
1252 | SVGPath.prototype.curveQTo = SVGPath.prototype.curveQ;\r |
1253 | SVGPath.prototype.smoothQTo = SVGPath.prototype.smoothQ;\r |
1254 | SVGPath.prototype.arcTo = SVGPath.prototype.arc;\r |
1255 | \r |
1256 | /* Helper to generate an SVG text object.\r |
1257 | Obtain an instance from the SVGWrapper object.\r |
1258 | String calls together to generate the text and use its value:\r |
1259 | var text = root.createText();\r |
1260 | root.text(null, x, y, text.string('This is ').\r |
1261 | span('red', {fill: 'red'}).string('!'), {fill: 'blue'}); */\r |
1262 | function SVGText() {\r |
1263 | this._parts = []; // The components of the text object\r |
1264 | }\r |
1265 | \r |
1266 | $.extend(SVGText.prototype, {\r |
1267 | /* Prepare to create a new text object.\r |
1268 | @return (SVGText) this text */\r |
1269 | reset: function() {\r |
1270 | this._parts = [];\r |
1271 | return this;\r |
1272 | },\r |
1273 | \r |
1274 | /* Add a straight string value.\r |
1275 | @param value (string) the actual text\r |
1276 | @return (SVGText) this text object */\r |
1277 | string: function(value) {\r |
1278 | this._parts[this._parts.length] = ['text', value];\r |
1279 | return this;\r |
1280 | },\r |
1281 | \r |
1282 | /* Add a separate text span that has its own settings.\r |
1283 | @param value (string) the actual text\r |
1284 | @param settings (object) the settings for this text\r |
1285 | @return (SVGText) this text object */\r |
1286 | span: function(value, settings) {\r |
1287 | this._parts[this._parts.length] = ['tspan', value, settings];\r |
1288 | return this;\r |
1289 | },\r |
1290 | \r |
1291 | /* Add a reference to a previously defined text string.\r |
1292 | @param id (string) the ID of the actual text\r |
1293 | @param settings (object) the settings for this text\r |
1294 | @return (SVGText) this text object */\r |
1295 | ref: function(id, settings) {\r |
1296 | this._parts[this._parts.length] = ['tref', id, settings];\r |
1297 | return this;\r |
1298 | },\r |
1299 | \r |
1300 | /* Add text drawn along a path.\r |
1301 | @param id (string) the ID of the path\r |
1302 | @param value (string) the actual text\r |
1303 | @param settings (object) the settings for this text\r |
1304 | @return (SVGText) this text object */\r |
1305 | path: function(id, value, settings) {\r |
1306 | this._parts[this._parts.length] = ['textpath', value, \r |
1307 | $.extend({href: id}, settings || {})];\r |
1308 | return this;\r |
1309 | }\r |
1310 | });\r |
1311 | \r |
1312 | /* Attach the SVG functionality to a jQuery selection.\r |
1313 | @param command (string) the command to run (optional, default 'attach')\r |
1314 | @param options (object) the new settings to use for these SVG instances\r |
1315 | @return jQuery (object) for chaining further calls */\r |
1316 | $.fn.svg = function(options) {\r |
1317 | var otherArgs = Array.prototype.slice.call(arguments, 1);\r |
1318 | if (typeof options == 'string' && options == 'get') {\r |
1319 | return $.svg['_' + options + 'SVG'].apply($.svg, [this[0]].concat(otherArgs));\r |
1320 | }\r |
1321 | return this.each(function() {\r |
1322 | if (typeof options == 'string') {\r |
1323 | $.svg['_' + options + 'SVG'].apply($.svg, [this].concat(otherArgs));\r |
1324 | }\r |
1325 | else {\r |
1326 | $.svg._attachSVG(this, options || {});\r |
1327 | } \r |
1328 | });\r |
1329 | };\r |
1330 | \r |
1331 | /* Determine whether an object is an array. */\r |
1332 | function isArray(a) {\r |
1333 | return (a && a.constructor == Array);\r |
1334 | }\r |
1335 | \r |
1336 | // Singleton primary SVG interface\r |
1337 | $.svg = new SVGManager();\r |
1338 | \r |
1339 | })(jQuery);\r |