Some styling
[scpubgit/stemmaweb.git] / root / js / jquery.svg.js
CommitLineData
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
13function 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
29function 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
38var 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
206function 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, '&amp;').\r
862 replace(/</g, '&lt;').replace(/>/g, '&gt;'));\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, '&amp;');\r
1053 text = text.replace(/</g, '&lt;');\r
1054 text = text.replace(/>/g, '&gt;');\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
1066function 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
1246SVGPath.prototype.moveTo = SVGPath.prototype.move;\r
1247SVGPath.prototype.lineTo = SVGPath.prototype.line;\r
1248SVGPath.prototype.horizTo = SVGPath.prototype.horiz;\r
1249SVGPath.prototype.vertTo = SVGPath.prototype.vert;\r
1250SVGPath.prototype.curveCTo = SVGPath.prototype.curveC;\r
1251SVGPath.prototype.smoothCTo = SVGPath.prototype.smoothC;\r
1252SVGPath.prototype.curveQTo = SVGPath.prototype.curveQ;\r
1253SVGPath.prototype.smoothQTo = SVGPath.prototype.smoothQ;\r
1254SVGPath.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
1262function 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
1332function 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