jquerysvg 1.4.5

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