canvgjs 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +6 -0
- data/LICENSE +13 -0
- data/README.md +45 -0
- data/Rakefile +2 -0
- data/canvgjs.gemspec +26 -0
- data/lib/canvgjs.rb +6 -0
- data/lib/canvgjs/version.rb +3 -0
- data/vendor/MIT-LICENSE.txt +22 -0
- data/vendor/assets/javascripts/canvg.js +3189 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8814655125fa46ebe710a6b5cfd812eb90817a02
|
4
|
+
data.tar.gz: b6b093655a87ac8a1139b2366c8d46f0a21f9a52
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 538e653591fe136039adabba89824171e180323f8b6e75b9655daa2f45a9611d889a9e1e0ea4041862a960cc41cdbdfe97eb19598d94120e1a83703007465d73
|
7
|
+
data.tar.gz: e3d4ded3154b3670db15727fa1b21bfa07784b40f4027bf0d74c361e561a318ab6554808a46d5edbdb8e1e0e87e063d54e5ad7485f28caa109ad914ca877c8b2
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2018, koparo.com <info@koparo.com>
|
2
|
+
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
purpose with or without fee is hereby granted, provided that the above
|
5
|
+
copyright notice and this permission notice appear in all copies.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Canvgjs
|
2
|
+
|
3
|
+
Javascript SVG parser and renderer on Canvas
|
4
|
+
|
5
|
+
This gem bundles the upstream distribution for use with the Ruby on Rails framework. The version number of
|
6
|
+
the gem always tracks the upstream javascript release and the gem itself doesn't provide any additional
|
7
|
+
methods or helpers. If a need for helpers arises in the future they will be developed as a separate gem
|
8
|
+
with this one as its dependency. Should a gem bug be discovered an additional version identifier will be
|
9
|
+
appended and incremented after the upstream version number.
|
10
|
+
|
11
|
+
The gem is developed and tested against Rails 5
|
12
|
+
|
13
|
+
## License
|
14
|
+
canvgjs and changes made to canvg required for rails are licensed under ISC.
|
15
|
+
|
16
|
+
The original canvg code distributed with this gem is licensed under [MIT](https://tldrlegal.com/license/mit-license)
|
17
|
+
You can find the canvg license file in the vendor directory, changes made to the original code base are as follows:
|
18
|
+
|
19
|
+
none so far.
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'canvgjs'
|
27
|
+
```
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
$ bundle
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install canvgjs
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
Add the following directive to your JavaScript manifest file (application.js):
|
40
|
+
|
41
|
+
//= require canvg
|
42
|
+
|
43
|
+
## Contributing
|
44
|
+
|
45
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/koparo/canvgjs.
|
data/Rakefile
ADDED
data/canvgjs.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "canvgjs/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "canvgjs"
|
8
|
+
spec.version = Canvgjs::VERSION
|
9
|
+
spec.authors = ["Adam Wolk"]
|
10
|
+
spec.email = ["adam.wolk@koparo.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{canvg javascript bundle}
|
13
|
+
spec.description = %q{Javascript SVG parser and renderer on Canvas}
|
14
|
+
spec.homepage = "https://github.com/canvg/canvg"
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
end
|
data/lib/canvgjs.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2010-2011 Gabe Lerner (gabelerner@gmail.com) - http://code.google.com/p/canvg/
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,3189 @@
|
|
1
|
+
/*
|
2
|
+
* canvg.js - Javascript SVG parser and renderer on Canvas
|
3
|
+
* MIT Licensed
|
4
|
+
* Gabe Lerner (gabelerner@gmail.com)
|
5
|
+
* http://code.google.com/p/canvg/
|
6
|
+
*
|
7
|
+
* Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
|
8
|
+
*/
|
9
|
+
(function ( global, factory ) {
|
10
|
+
|
11
|
+
'use strict';
|
12
|
+
// export as AMD...
|
13
|
+
if ( typeof define !== 'undefined' && define.amd ) {
|
14
|
+
define('canvgModule', [ 'rgbcolor', 'stackblur' ], factory );
|
15
|
+
}
|
16
|
+
|
17
|
+
// ...or as browserify
|
18
|
+
else if ( typeof module !== 'undefined' && module.exports ) {
|
19
|
+
module.exports = factory( require( 'rgbcolor' ), require( 'stackblur' ) );
|
20
|
+
}
|
21
|
+
else {
|
22
|
+
global.canvg = factory( global.RGBColor, global.stackBlur );
|
23
|
+
}
|
24
|
+
|
25
|
+
}( typeof window !== 'undefined' ? window : this, function ( RGBColor, stackBlur ) {
|
26
|
+
var nodeEnv = (typeof module !== 'undefined' && module.exports);
|
27
|
+
var windowEnv, ImageClass, CanvasClass,
|
28
|
+
defaultClientWidth = 800, defaultClientHeight = 600;
|
29
|
+
if (nodeEnv && (typeof window === 'undefined')) {
|
30
|
+
var jsdom = require('jsdom').jsdom;
|
31
|
+
windowEnv = jsdom().defaultView;
|
32
|
+
} else {
|
33
|
+
windowEnv = window;
|
34
|
+
}
|
35
|
+
if (!windowEnv.DOMParser) {
|
36
|
+
windowEnv.DOMParser = require('xmldom').DOMParser;
|
37
|
+
}
|
38
|
+
|
39
|
+
function createCanvas() {
|
40
|
+
var c;
|
41
|
+
if (nodeEnv) {
|
42
|
+
c = new CanvasClass();
|
43
|
+
} else {
|
44
|
+
c = document.createElement('canvas');
|
45
|
+
}
|
46
|
+
return c;
|
47
|
+
}
|
48
|
+
|
49
|
+
// canvg(target, s)
|
50
|
+
// empty parameters: replace all 'svg' elements on page with 'canvas' elements
|
51
|
+
// target: canvas element or the id of a canvas element
|
52
|
+
// s: svg string, url to svg file, or xml document
|
53
|
+
// opts: optional hash of options
|
54
|
+
// ignoreMouse: true => ignore mouse events
|
55
|
+
// ignoreAnimation: true => ignore animations
|
56
|
+
// ignoreDimensions: true => does not try to resize canvas
|
57
|
+
// ignoreClear: true => does not clear canvas
|
58
|
+
// offsetX: int => draws at a x offset
|
59
|
+
// offsetY: int => draws at a y offset
|
60
|
+
// scaleWidth: int => scales horizontally to width
|
61
|
+
// scaleHeight: int => scales vertically to height
|
62
|
+
// renderCallback: function => will call the function after the first render is completed
|
63
|
+
// enableRedraw: function => whether enable the redraw interval in node environment
|
64
|
+
// forceRedraw: function => will call the function on every frame, if it returns true, will redraw
|
65
|
+
var canvg = function (target, s, opts) {
|
66
|
+
// no parameters
|
67
|
+
if (target == null && s == null && opts == null) {
|
68
|
+
var svgTags = document.querySelectorAll('svg');
|
69
|
+
for (var i=0; i<svgTags.length; i++) {
|
70
|
+
var svgTag = svgTags[i];
|
71
|
+
var c = document.createElement('canvas');
|
72
|
+
c.width = svgTag.clientWidth;
|
73
|
+
c.height = svgTag.clientHeight;
|
74
|
+
svgTag.parentNode.insertBefore(c, svgTag);
|
75
|
+
svgTag.parentNode.removeChild(svgTag);
|
76
|
+
var div = document.createElement('div');
|
77
|
+
div.appendChild(svgTag);
|
78
|
+
canvg(c, div.innerHTML);
|
79
|
+
}
|
80
|
+
return;
|
81
|
+
}
|
82
|
+
|
83
|
+
var svg = build(opts || {});
|
84
|
+
if (nodeEnv) {
|
85
|
+
if (!s || s === '') {
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
ImageClass = opts['ImageClass'];
|
89
|
+
CanvasClass = target.constructor;
|
90
|
+
//only support svg string in node env.
|
91
|
+
svg.loadXml(target.getContext('2d'), s);
|
92
|
+
return;
|
93
|
+
}
|
94
|
+
|
95
|
+
if (typeof target == 'string') {
|
96
|
+
target = document.getElementById(target);
|
97
|
+
}
|
98
|
+
|
99
|
+
// store class on canvas
|
100
|
+
if (target.svg != null) target.svg.stop();
|
101
|
+
|
102
|
+
// on i.e. 8 for flash canvas, we can't assign the property so check for it
|
103
|
+
if (!(target.childNodes && target.childNodes.length == 1 && target.childNodes[0].nodeName == 'OBJECT')) target.svg = svg;
|
104
|
+
|
105
|
+
var ctx = target.getContext('2d');
|
106
|
+
|
107
|
+
if (typeof s.documentElement != 'undefined') {
|
108
|
+
// load from xml doc
|
109
|
+
svg.loadXmlDoc(ctx, s);
|
110
|
+
}
|
111
|
+
else if (s.substr(0,1) == '<') {
|
112
|
+
// load from xml string
|
113
|
+
svg.loadXml(ctx, s);
|
114
|
+
}
|
115
|
+
else {
|
116
|
+
// load from url
|
117
|
+
svg.load(ctx, s);
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
var matchesSelector;
|
122
|
+
if (nodeEnv) {
|
123
|
+
//Now only used to decide whether the node has the class names specified by selector,
|
124
|
+
//Add this implementation to avoid compatibility issues in node env.
|
125
|
+
matchesSelector = function(node, selector) {
|
126
|
+
var styleClasses = node.getAttribute('class');
|
127
|
+
if (!styleClasses || styleClasses === '') {
|
128
|
+
return false;
|
129
|
+
}
|
130
|
+
styleClasses = styleClasses.split(' ');
|
131
|
+
for (var i = 0; i < styleClasses.length; i++) {
|
132
|
+
if ('.'+styleClasses[i] === selector) {
|
133
|
+
return true;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
return false;
|
137
|
+
};
|
138
|
+
} else {
|
139
|
+
// see https://developer.mozilla.org/en-US/docs/Web/API/Element.matches
|
140
|
+
if (typeof Element == 'undefined') {
|
141
|
+
// We include this test, as otherwise IE7 will throw an error simply by
|
142
|
+
// including this file, as it does not support the Element object.
|
143
|
+
} else if (typeof Element.prototype.matches != 'undefined') {
|
144
|
+
matchesSelector = function(node, selector) {
|
145
|
+
return node.matches(selector);
|
146
|
+
};
|
147
|
+
} else if (typeof Element.prototype.webkitMatchesSelector != 'undefined') {
|
148
|
+
matchesSelector = function(node, selector) {
|
149
|
+
return node.webkitMatchesSelector(selector);
|
150
|
+
};
|
151
|
+
} else if (typeof Element.prototype.mozMatchesSelector != 'undefined') {
|
152
|
+
matchesSelector = function(node, selector) {
|
153
|
+
return node.mozMatchesSelector(selector);
|
154
|
+
};
|
155
|
+
} else if (typeof Element.prototype.msMatchesSelector != 'undefined') {
|
156
|
+
matchesSelector = function(node, selector) {
|
157
|
+
return node.msMatchesSelector(selector);
|
158
|
+
};
|
159
|
+
} else if (typeof Element.prototype.oMatchesSelector != 'undefined') {
|
160
|
+
matchesSelector = function(node, selector) {
|
161
|
+
return node.oMatchesSelector(selector);
|
162
|
+
};
|
163
|
+
} else {
|
164
|
+
// requires Sizzle: https://github.com/jquery/sizzle/wiki/Sizzle-Documentation
|
165
|
+
// or jQuery: http://jquery.com/download/
|
166
|
+
// or Zepto: http://zeptojs.com/#
|
167
|
+
// without it, this is a ReferenceError
|
168
|
+
|
169
|
+
if (typeof jQuery === 'function' || typeof Zepto === 'function') {
|
170
|
+
matchesSelector = function (node, selector) {
|
171
|
+
return $(node).is(selector);
|
172
|
+
};
|
173
|
+
}
|
174
|
+
|
175
|
+
if (typeof matchesSelector === 'undefined' && typeof Sizzle !== 'undefined') {
|
176
|
+
matchesSelector = Sizzle.matchesSelector;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
// slightly modified version of https://github.com/keeganstreet/specificity/blob/master/specificity.js
|
184
|
+
var attributeRegex = /(\[[^\]]+\])/g;
|
185
|
+
var idRegex = /(#[^\s\+>~\.\[:]+)/g;
|
186
|
+
var classRegex = /(\.[^\s\+>~\.\[:]+)/g;
|
187
|
+
var pseudoElementRegex = /(::[^\s\+>~\.\[:]+|:first-line|:first-letter|:before|:after)/gi;
|
188
|
+
var pseudoClassWithBracketsRegex = /(:[\w-]+\([^\)]*\))/gi;
|
189
|
+
var pseudoClassRegex = /(:[^\s\+>~\.\[:]+)/g;
|
190
|
+
var elementRegex = /([^\s\+>~\.\[:]+)/g;
|
191
|
+
function getSelectorSpecificity(selector) {
|
192
|
+
var typeCount = [0, 0, 0];
|
193
|
+
var findMatch = function(regex, type) {
|
194
|
+
var matches = selector.match(regex);
|
195
|
+
if (matches == null) {
|
196
|
+
return;
|
197
|
+
}
|
198
|
+
typeCount[type] += matches.length;
|
199
|
+
selector = selector.replace(regex, ' ');
|
200
|
+
};
|
201
|
+
|
202
|
+
selector = selector.replace(/:not\(([^\)]*)\)/g, ' $1 ');
|
203
|
+
selector = selector.replace(/{[\s\S]*/gm, ' ');
|
204
|
+
findMatch(attributeRegex, 1);
|
205
|
+
findMatch(idRegex, 0);
|
206
|
+
findMatch(classRegex, 1);
|
207
|
+
findMatch(pseudoElementRegex, 2);
|
208
|
+
findMatch(pseudoClassWithBracketsRegex, 1);
|
209
|
+
findMatch(pseudoClassRegex, 1);
|
210
|
+
selector = selector.replace(/[\*\s\+>~]/g, ' ');
|
211
|
+
selector = selector.replace(/[#\.]/g, ' ');
|
212
|
+
findMatch(elementRegex, 2);
|
213
|
+
return typeCount.join('');
|
214
|
+
}
|
215
|
+
|
216
|
+
function build(opts) {
|
217
|
+
var svg = { opts: opts };
|
218
|
+
|
219
|
+
svg.FRAMERATE = 30;
|
220
|
+
svg.MAX_VIRTUAL_PIXELS = 30000;
|
221
|
+
|
222
|
+
svg.log = function(msg) {};
|
223
|
+
if (svg.opts['log'] == true && typeof console != 'undefined') {
|
224
|
+
svg.log = function(msg) { console.log(msg); };
|
225
|
+
};
|
226
|
+
|
227
|
+
// globals
|
228
|
+
svg.init = function(ctx) {
|
229
|
+
var uniqueId = 0;
|
230
|
+
svg.UniqueId = function () { uniqueId++; return 'canvg' + uniqueId; };
|
231
|
+
svg.Definitions = {};
|
232
|
+
svg.Styles = {};
|
233
|
+
svg.StylesSpecificity = {};
|
234
|
+
svg.Animations = [];
|
235
|
+
svg.Images = [];
|
236
|
+
svg.ctx = ctx;
|
237
|
+
svg.ViewPort = new (function () {
|
238
|
+
|
239
|
+
this.viewPorts = [];
|
240
|
+
this.Clear = function() { this.viewPorts = []; }
|
241
|
+
this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); }
|
242
|
+
this.RemoveCurrent = function() { this.viewPorts.pop(); }
|
243
|
+
this.Current = function() { return this.viewPorts[this.viewPorts.length - 1]; }
|
244
|
+
this.width = function() { return this.Current().width; }
|
245
|
+
this.height = function() { return this.Current().height; }
|
246
|
+
this.ComputeSize = function(d) {
|
247
|
+
if (d != null && typeof d == 'number') return d;
|
248
|
+
if (d == 'x') return this.width();
|
249
|
+
if (d == 'y') return this.height();
|
250
|
+
return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2);
|
251
|
+
}
|
252
|
+
});
|
253
|
+
}
|
254
|
+
svg.init();
|
255
|
+
|
256
|
+
// images loaded
|
257
|
+
svg.ImagesLoaded = function() {
|
258
|
+
for (var i=0; i<svg.Images.length; i++) {
|
259
|
+
if (!svg.Images[i].loaded) return false;
|
260
|
+
}
|
261
|
+
return true;
|
262
|
+
}
|
263
|
+
|
264
|
+
// trim
|
265
|
+
svg.trim = function(s) { return s.replace(/^\s+|\s+$/g, ''); }
|
266
|
+
|
267
|
+
// compress spaces
|
268
|
+
svg.compressSpaces = function(s) { return s.replace(/[\s\r\t\n]+/gm,' '); }
|
269
|
+
|
270
|
+
// ajax
|
271
|
+
svg.ajax = function(url) {
|
272
|
+
if (nodeEnv) {return null;}
|
273
|
+
var AJAX;
|
274
|
+
if(windowEnv.XMLHttpRequest){AJAX=new windowEnv.XMLHttpRequest();}
|
275
|
+
else{AJAX=new ActiveXObject('Microsoft.XMLHTTP');}
|
276
|
+
if(AJAX){
|
277
|
+
AJAX.open('GET',url,false);
|
278
|
+
AJAX.send(null);
|
279
|
+
return AJAX.responseText;
|
280
|
+
}
|
281
|
+
return null;
|
282
|
+
}
|
283
|
+
|
284
|
+
// parse xml
|
285
|
+
svg.parseXml = function(xml) {
|
286
|
+
if (typeof Windows != 'undefined' && typeof Windows.Data != 'undefined' && typeof Windows.Data.Xml != 'undefined') {
|
287
|
+
var xmlDoc = new Windows.Data.Xml.Dom.XmlDocument();
|
288
|
+
var settings = new Windows.Data.Xml.Dom.XmlLoadSettings();
|
289
|
+
settings.prohibitDtd = false;
|
290
|
+
xmlDoc.loadXml(xml, settings);
|
291
|
+
return xmlDoc;
|
292
|
+
}
|
293
|
+
else if (windowEnv.DOMParser)
|
294
|
+
{
|
295
|
+
try {
|
296
|
+
var parser = new windowEnv.DOMParser();
|
297
|
+
return parser.parseFromString(xml, 'image/svg+xml');
|
298
|
+
}
|
299
|
+
catch(e){
|
300
|
+
parser = new windowEnv.DOMParser();
|
301
|
+
return parser.parseFromString(xml, 'text/xml');
|
302
|
+
}
|
303
|
+
}
|
304
|
+
else
|
305
|
+
{
|
306
|
+
xml = xml.replace(/<!DOCTYPE svg[^>]*>/, '');
|
307
|
+
var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
|
308
|
+
xmlDoc.async = 'false';
|
309
|
+
xmlDoc.loadXML(xml);
|
310
|
+
return xmlDoc;
|
311
|
+
}
|
312
|
+
}
|
313
|
+
|
314
|
+
svg.Property = function(name, value) {
|
315
|
+
this.name = name;
|
316
|
+
this.value = value;
|
317
|
+
}
|
318
|
+
svg.Property.prototype.getValue = function() {
|
319
|
+
return this.value;
|
320
|
+
}
|
321
|
+
|
322
|
+
svg.Property.prototype.hasValue = function() {
|
323
|
+
return (this.value != null && this.value !== '');
|
324
|
+
}
|
325
|
+
|
326
|
+
// return the numerical value of the property
|
327
|
+
svg.Property.prototype.numValue = function() {
|
328
|
+
if (!this.hasValue()) return 0;
|
329
|
+
|
330
|
+
var n = parseFloat(this.value);
|
331
|
+
if ((this.value + '').match(/%$/)) {
|
332
|
+
n = n / 100.0;
|
333
|
+
}
|
334
|
+
return n;
|
335
|
+
}
|
336
|
+
|
337
|
+
svg.Property.prototype.valueOrDefault = function(def) {
|
338
|
+
if (this.hasValue()) return this.value;
|
339
|
+
return def;
|
340
|
+
}
|
341
|
+
|
342
|
+
svg.Property.prototype.numValueOrDefault = function(def) {
|
343
|
+
if (this.hasValue()) return this.numValue();
|
344
|
+
return def;
|
345
|
+
}
|
346
|
+
|
347
|
+
// color extensions
|
348
|
+
// augment the current color value with the opacity
|
349
|
+
svg.Property.prototype.addOpacity = function(opacityProp) {
|
350
|
+
var newValue = this.value;
|
351
|
+
if (opacityProp.value != null && opacityProp.value != '' && typeof this.value == 'string') { // can only add opacity to colors, not patterns
|
352
|
+
var color = new RGBColor(this.value);
|
353
|
+
if (color.ok) {
|
354
|
+
newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacityProp.numValue() + ')';
|
355
|
+
}
|
356
|
+
}
|
357
|
+
return new svg.Property(this.name, newValue);
|
358
|
+
}
|
359
|
+
|
360
|
+
// definition extensions
|
361
|
+
// get the definition from the definitions table
|
362
|
+
svg.Property.prototype.getDefinition = function() {
|
363
|
+
var name = this.value.match(/#([^\)'"]+)/);
|
364
|
+
if (name) { name = name[1]; }
|
365
|
+
if (!name) { name = this.value; }
|
366
|
+
return svg.Definitions[name];
|
367
|
+
}
|
368
|
+
|
369
|
+
svg.Property.prototype.isUrlDefinition = function() {
|
370
|
+
return this.value.indexOf('url(') == 0
|
371
|
+
}
|
372
|
+
|
373
|
+
svg.Property.prototype.getFillStyleDefinition = function(e, opacityProp) {
|
374
|
+
var def = this.getDefinition();
|
375
|
+
|
376
|
+
// gradient
|
377
|
+
if (def != null && def.createGradient) {
|
378
|
+
return def.createGradient(svg.ctx, e, opacityProp);
|
379
|
+
}
|
380
|
+
|
381
|
+
// pattern
|
382
|
+
if (def != null && def.createPattern) {
|
383
|
+
if (def.getHrefAttribute().hasValue()) {
|
384
|
+
var pt = def.attribute('patternTransform');
|
385
|
+
def = def.getHrefAttribute().getDefinition();
|
386
|
+
if (pt.hasValue()) { def.attribute('patternTransform', true).value = pt.value; }
|
387
|
+
}
|
388
|
+
return def.createPattern(svg.ctx, e);
|
389
|
+
}
|
390
|
+
|
391
|
+
return null;
|
392
|
+
}
|
393
|
+
|
394
|
+
// length extensions
|
395
|
+
svg.Property.prototype.getDPI = function(viewPort) {
|
396
|
+
return 96.0; // TODO: compute?
|
397
|
+
}
|
398
|
+
|
399
|
+
svg.Property.prototype.getEM = function(viewPort) {
|
400
|
+
var em = 12;
|
401
|
+
|
402
|
+
var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
|
403
|
+
if (fontSize.hasValue()) em = fontSize.toPixels(viewPort);
|
404
|
+
|
405
|
+
return em;
|
406
|
+
}
|
407
|
+
|
408
|
+
svg.Property.prototype.getUnits = function() {
|
409
|
+
var s = this.value+'';
|
410
|
+
return s.replace(/[0-9\.\-]/g,'');
|
411
|
+
}
|
412
|
+
|
413
|
+
// get the length as pixels
|
414
|
+
svg.Property.prototype.toPixels = function(viewPort, processPercent) {
|
415
|
+
if (!this.hasValue()) return 0;
|
416
|
+
var s = this.value+'';
|
417
|
+
if (s.match(/em$/)) return this.numValue() * this.getEM(viewPort);
|
418
|
+
if (s.match(/ex$/)) return this.numValue() * this.getEM(viewPort) / 2.0;
|
419
|
+
if (s.match(/px$/)) return this.numValue();
|
420
|
+
if (s.match(/pt$/)) return this.numValue() * this.getDPI(viewPort) * (1.0 / 72.0);
|
421
|
+
if (s.match(/pc$/)) return this.numValue() * 15;
|
422
|
+
if (s.match(/cm$/)) return this.numValue() * this.getDPI(viewPort) / 2.54;
|
423
|
+
if (s.match(/mm$/)) return this.numValue() * this.getDPI(viewPort) / 25.4;
|
424
|
+
if (s.match(/in$/)) return this.numValue() * this.getDPI(viewPort);
|
425
|
+
if (s.match(/%$/)) return this.numValue() * svg.ViewPort.ComputeSize(viewPort);
|
426
|
+
var n = this.numValue();
|
427
|
+
if (processPercent && n < 1.0) return n * svg.ViewPort.ComputeSize(viewPort);
|
428
|
+
return n;
|
429
|
+
}
|
430
|
+
|
431
|
+
// time extensions
|
432
|
+
// get the time as milliseconds
|
433
|
+
svg.Property.prototype.toMilliseconds = function() {
|
434
|
+
if (!this.hasValue()) return 0;
|
435
|
+
var s = this.value+'';
|
436
|
+
if (s.match(/s$/)) return this.numValue() * 1000;
|
437
|
+
if (s.match(/ms$/)) return this.numValue();
|
438
|
+
return this.numValue();
|
439
|
+
}
|
440
|
+
|
441
|
+
// angle extensions
|
442
|
+
// get the angle as radians
|
443
|
+
svg.Property.prototype.toRadians = function() {
|
444
|
+
if (!this.hasValue()) return 0;
|
445
|
+
var s = this.value+'';
|
446
|
+
if (s.match(/deg$/)) return this.numValue() * (Math.PI / 180.0);
|
447
|
+
if (s.match(/grad$/)) return this.numValue() * (Math.PI / 200.0);
|
448
|
+
if (s.match(/rad$/)) return this.numValue();
|
449
|
+
return this.numValue() * (Math.PI / 180.0);
|
450
|
+
}
|
451
|
+
|
452
|
+
// text extensions
|
453
|
+
// get the text baseline
|
454
|
+
var textBaselineMapping = {
|
455
|
+
'baseline': 'alphabetic',
|
456
|
+
'before-edge': 'top',
|
457
|
+
'text-before-edge': 'top',
|
458
|
+
'middle': 'middle',
|
459
|
+
'central': 'middle',
|
460
|
+
'after-edge': 'bottom',
|
461
|
+
'text-after-edge': 'bottom',
|
462
|
+
'ideographic': 'ideographic',
|
463
|
+
'alphabetic': 'alphabetic',
|
464
|
+
'hanging': 'hanging',
|
465
|
+
'mathematical': 'alphabetic'
|
466
|
+
};
|
467
|
+
svg.Property.prototype.toTextBaseline = function () {
|
468
|
+
if (!this.hasValue()) return null;
|
469
|
+
return textBaselineMapping[this.value];
|
470
|
+
}
|
471
|
+
|
472
|
+
// fonts
|
473
|
+
svg.Font = new (function() {
|
474
|
+
this.Styles = 'normal|italic|oblique|inherit';
|
475
|
+
this.Variants = 'normal|small-caps|inherit';
|
476
|
+
this.Weights = 'normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900|inherit';
|
477
|
+
|
478
|
+
this.CreateFont = function(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) {
|
479
|
+
var f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font);
|
480
|
+
var fontFamily = fontFamily || f.fontFamily;
|
481
|
+
if (fontFamily) {
|
482
|
+
var trimed = fontFamily.trim();
|
483
|
+
if (trimed[0] !== '"' && trimed.indexOf(' ') > 0) {
|
484
|
+
fontFamily = '"' + trimed + '"';
|
485
|
+
}
|
486
|
+
}
|
487
|
+
return {
|
488
|
+
fontFamily: fontFamily,
|
489
|
+
fontSize: fontSize || f.fontSize,
|
490
|
+
fontStyle: fontStyle || f.fontStyle,
|
491
|
+
fontWeight: fontWeight || f.fontWeight,
|
492
|
+
fontVariant: fontVariant || f.fontVariant,
|
493
|
+
toString: function () { return [this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily].join(' ') }
|
494
|
+
}
|
495
|
+
}
|
496
|
+
|
497
|
+
var that = this;
|
498
|
+
this.Parse = function(s) {
|
499
|
+
var f = {};
|
500
|
+
var d = svg.trim(svg.compressSpaces(s || '')).split(' ');
|
501
|
+
var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false }
|
502
|
+
var ff = '';
|
503
|
+
for (var i=0; i<d.length; i++) {
|
504
|
+
if (!set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontStyle = d[i]; set.fontStyle = true; }
|
505
|
+
else if (!set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontVariant = d[i]; set.fontStyle = set.fontVariant = true; }
|
506
|
+
else if (!set.fontWeight && that.Weights.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontWeight = d[i]; set.fontStyle = set.fontVariant = set.fontWeight = true; }
|
507
|
+
else if (!set.fontSize) { if (d[i] != 'inherit') f.fontSize = d[i].split('/')[0]; set.fontStyle = set.fontVariant = set.fontWeight = set.fontSize = true; }
|
508
|
+
else { if (d[i] != 'inherit') ff += d[i]; }
|
509
|
+
} if (ff != '') f.fontFamily = ff;
|
510
|
+
return f;
|
511
|
+
}
|
512
|
+
});
|
513
|
+
|
514
|
+
// points and paths
|
515
|
+
svg.ToNumberArray = function(s) {
|
516
|
+
var a = svg.trim(svg.compressSpaces((s || '').replace(/,/g, ' '))).split(' ');
|
517
|
+
for (var i=0; i<a.length; i++) {
|
518
|
+
a[i] = parseFloat(a[i]);
|
519
|
+
}
|
520
|
+
return a;
|
521
|
+
}
|
522
|
+
svg.Point = function(x, y) {
|
523
|
+
this.x = x;
|
524
|
+
this.y = y;
|
525
|
+
}
|
526
|
+
svg.Point.prototype.angleTo = function(p) {
|
527
|
+
return Math.atan2(p.y - this.y, p.x - this.x);
|
528
|
+
}
|
529
|
+
|
530
|
+
svg.Point.prototype.applyTransform = function(v) {
|
531
|
+
var xp = this.x * v[0] + this.y * v[2] + v[4];
|
532
|
+
var yp = this.x * v[1] + this.y * v[3] + v[5];
|
533
|
+
this.x = xp;
|
534
|
+
this.y = yp;
|
535
|
+
}
|
536
|
+
|
537
|
+
svg.CreatePoint = function(s) {
|
538
|
+
var a = svg.ToNumberArray(s);
|
539
|
+
return new svg.Point(a[0], a[1]);
|
540
|
+
}
|
541
|
+
svg.CreatePath = function(s) {
|
542
|
+
var a = svg.ToNumberArray(s);
|
543
|
+
var path = [];
|
544
|
+
for (var i=0; i<a.length; i+=2) {
|
545
|
+
path.push(new svg.Point(a[i], a[i+1]));
|
546
|
+
}
|
547
|
+
return path;
|
548
|
+
}
|
549
|
+
|
550
|
+
// bounding box
|
551
|
+
svg.BoundingBox = function(x1, y1, x2, y2) { // pass in initial points if you want
|
552
|
+
this.x1 = Number.NaN;
|
553
|
+
this.y1 = Number.NaN;
|
554
|
+
this.x2 = Number.NaN;
|
555
|
+
this.y2 = Number.NaN;
|
556
|
+
|
557
|
+
this.x = function() { return this.x1; }
|
558
|
+
this.y = function() { return this.y1; }
|
559
|
+
this.width = function() { return this.x2 - this.x1; }
|
560
|
+
this.height = function() { return this.y2 - this.y1; }
|
561
|
+
|
562
|
+
this.addPoint = function(x, y) {
|
563
|
+
if (x != null) {
|
564
|
+
if (isNaN(this.x1) || isNaN(this.x2)) {
|
565
|
+
this.x1 = x;
|
566
|
+
this.x2 = x;
|
567
|
+
}
|
568
|
+
if (x < this.x1) this.x1 = x;
|
569
|
+
if (x > this.x2) this.x2 = x;
|
570
|
+
}
|
571
|
+
|
572
|
+
if (y != null) {
|
573
|
+
if (isNaN(this.y1) || isNaN(this.y2)) {
|
574
|
+
this.y1 = y;
|
575
|
+
this.y2 = y;
|
576
|
+
}
|
577
|
+
if (y < this.y1) this.y1 = y;
|
578
|
+
if (y > this.y2) this.y2 = y;
|
579
|
+
}
|
580
|
+
}
|
581
|
+
this.addX = function(x) { this.addPoint(x, null); }
|
582
|
+
this.addY = function(y) { this.addPoint(null, y); }
|
583
|
+
|
584
|
+
this.addBoundingBox = function(bb) {
|
585
|
+
this.addPoint(bb.x1, bb.y1);
|
586
|
+
this.addPoint(bb.x2, bb.y2);
|
587
|
+
}
|
588
|
+
|
589
|
+
this.addQuadraticCurve = function(p0x, p0y, p1x, p1y, p2x, p2y) {
|
590
|
+
var cp1x = p0x + 2/3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
|
591
|
+
var cp1y = p0y + 2/3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
|
592
|
+
var cp2x = cp1x + 1/3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
|
593
|
+
var cp2y = cp1y + 1/3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
|
594
|
+
this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y);
|
595
|
+
}
|
596
|
+
|
597
|
+
this.addBezierCurve = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {
|
598
|
+
// from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
|
599
|
+
var p0 = [p0x, p0y], p1 = [p1x, p1y], p2 = [p2x, p2y], p3 = [p3x, p3y];
|
600
|
+
this.addPoint(p0[0], p0[1]);
|
601
|
+
this.addPoint(p3[0], p3[1]);
|
602
|
+
|
603
|
+
for (var i=0; i<=1; i++) {
|
604
|
+
var f = function(t) {
|
605
|
+
return Math.pow(1-t, 3) * p0[i]
|
606
|
+
+ 3 * Math.pow(1-t, 2) * t * p1[i]
|
607
|
+
+ 3 * (1-t) * Math.pow(t, 2) * p2[i]
|
608
|
+
+ Math.pow(t, 3) * p3[i];
|
609
|
+
}
|
610
|
+
|
611
|
+
var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
|
612
|
+
var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
|
613
|
+
var c = 3 * p1[i] - 3 * p0[i];
|
614
|
+
|
615
|
+
if (a == 0) {
|
616
|
+
if (b == 0) continue;
|
617
|
+
var t = -c / b;
|
618
|
+
if (0 < t && t < 1) {
|
619
|
+
if (i == 0) this.addX(f(t));
|
620
|
+
if (i == 1) this.addY(f(t));
|
621
|
+
}
|
622
|
+
continue;
|
623
|
+
}
|
624
|
+
|
625
|
+
var b2ac = Math.pow(b, 2) - 4 * c * a;
|
626
|
+
if (b2ac < 0) continue;
|
627
|
+
var t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
|
628
|
+
if (0 < t1 && t1 < 1) {
|
629
|
+
if (i == 0) this.addX(f(t1));
|
630
|
+
if (i == 1) this.addY(f(t1));
|
631
|
+
}
|
632
|
+
var t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
|
633
|
+
if (0 < t2 && t2 < 1) {
|
634
|
+
if (i == 0) this.addX(f(t2));
|
635
|
+
if (i == 1) this.addY(f(t2));
|
636
|
+
}
|
637
|
+
}
|
638
|
+
}
|
639
|
+
|
640
|
+
this.isPointInBox = function(x, y) {
|
641
|
+
return (this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2);
|
642
|
+
}
|
643
|
+
|
644
|
+
this.addPoint(x1, y1);
|
645
|
+
this.addPoint(x2, y2);
|
646
|
+
}
|
647
|
+
|
648
|
+
// transforms
|
649
|
+
svg.Transform = function(v) {
|
650
|
+
var that = this;
|
651
|
+
this.Type = {}
|
652
|
+
|
653
|
+
// translate
|
654
|
+
this.Type.translate = function(s) {
|
655
|
+
this.p = svg.CreatePoint(s);
|
656
|
+
this.apply = function(ctx) {
|
657
|
+
ctx.translate(this.p.x || 0.0, this.p.y || 0.0);
|
658
|
+
}
|
659
|
+
this.unapply = function(ctx) {
|
660
|
+
ctx.translate(-1.0 * this.p.x || 0.0, -1.0 * this.p.y || 0.0);
|
661
|
+
}
|
662
|
+
this.applyToPoint = function(p) {
|
663
|
+
p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
|
664
|
+
}
|
665
|
+
}
|
666
|
+
|
667
|
+
// rotate
|
668
|
+
this.Type.rotate = function(s) {
|
669
|
+
var a = svg.ToNumberArray(s);
|
670
|
+
this.angle = new svg.Property('angle', a[0]);
|
671
|
+
this.cx = a[1] || 0;
|
672
|
+
this.cy = a[2] || 0;
|
673
|
+
this.apply = function(ctx) {
|
674
|
+
ctx.translate(this.cx, this.cy);
|
675
|
+
ctx.rotate(this.angle.toRadians());
|
676
|
+
ctx.translate(-this.cx, -this.cy);
|
677
|
+
}
|
678
|
+
this.unapply = function(ctx) {
|
679
|
+
ctx.translate(this.cx, this.cy);
|
680
|
+
ctx.rotate(-1.0 * this.angle.toRadians());
|
681
|
+
ctx.translate(-this.cx, -this.cy);
|
682
|
+
}
|
683
|
+
this.applyToPoint = function(p) {
|
684
|
+
var a = this.angle.toRadians();
|
685
|
+
p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
|
686
|
+
p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
|
687
|
+
p.applyTransform([1, 0, 0, 1, -this.p.x || 0.0, -this.p.y || 0.0]);
|
688
|
+
}
|
689
|
+
}
|
690
|
+
|
691
|
+
this.Type.scale = function(s) {
|
692
|
+
this.p = svg.CreatePoint(s);
|
693
|
+
this.apply = function(ctx) {
|
694
|
+
ctx.scale(this.p.x || 1.0, this.p.y || this.p.x || 1.0);
|
695
|
+
}
|
696
|
+
this.unapply = function(ctx) {
|
697
|
+
ctx.scale(1.0 / this.p.x || 1.0, 1.0 / this.p.y || this.p.x || 1.0);
|
698
|
+
}
|
699
|
+
this.applyToPoint = function(p) {
|
700
|
+
p.applyTransform([this.p.x || 0.0, 0, 0, this.p.y || 0.0, 0, 0]);
|
701
|
+
}
|
702
|
+
}
|
703
|
+
|
704
|
+
this.Type.matrix = function(s) {
|
705
|
+
this.m = svg.ToNumberArray(s);
|
706
|
+
this.apply = function(ctx) {
|
707
|
+
ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
|
708
|
+
}
|
709
|
+
this.unapply = function(ctx) {
|
710
|
+
var a = this.m[0];
|
711
|
+
var b = this.m[2];
|
712
|
+
var c = this.m[4];
|
713
|
+
var d = this.m[1];
|
714
|
+
var e = this.m[3];
|
715
|
+
var f = this.m[5];
|
716
|
+
var g = 0.0;
|
717
|
+
var h = 0.0;
|
718
|
+
var i = 1.0;
|
719
|
+
var det = 1 / (a*(e*i-f*h)-b*(d*i-f*g)+c*(d*h-e*g));
|
720
|
+
ctx.transform(
|
721
|
+
det*(e*i-f*h),
|
722
|
+
det*(f*g-d*i),
|
723
|
+
det*(c*h-b*i),
|
724
|
+
det*(a*i-c*g),
|
725
|
+
det*(b*f-c*e),
|
726
|
+
det*(c*d-a*f)
|
727
|
+
);
|
728
|
+
}
|
729
|
+
this.applyToPoint = function(p) {
|
730
|
+
p.applyTransform(this.m);
|
731
|
+
}
|
732
|
+
}
|
733
|
+
|
734
|
+
this.Type.SkewBase = function(s) {
|
735
|
+
this.base = that.Type.matrix;
|
736
|
+
this.base(s);
|
737
|
+
this.angle = new svg.Property('angle', s);
|
738
|
+
}
|
739
|
+
this.Type.SkewBase.prototype = new this.Type.matrix;
|
740
|
+
|
741
|
+
this.Type.skewX = function(s) {
|
742
|
+
this.base = that.Type.SkewBase;
|
743
|
+
this.base(s);
|
744
|
+
this.m = [1, 0, Math.tan(this.angle.toRadians()), 1, 0, 0];
|
745
|
+
}
|
746
|
+
this.Type.skewX.prototype = new this.Type.SkewBase;
|
747
|
+
|
748
|
+
this.Type.skewY = function(s) {
|
749
|
+
this.base = that.Type.SkewBase;
|
750
|
+
this.base(s);
|
751
|
+
this.m = [1, Math.tan(this.angle.toRadians()), 0, 1, 0, 0];
|
752
|
+
}
|
753
|
+
this.Type.skewY.prototype = new this.Type.SkewBase;
|
754
|
+
|
755
|
+
this.transforms = [];
|
756
|
+
|
757
|
+
this.apply = function(ctx) {
|
758
|
+
for (var i=0; i<this.transforms.length; i++) {
|
759
|
+
this.transforms[i].apply(ctx);
|
760
|
+
}
|
761
|
+
}
|
762
|
+
|
763
|
+
this.unapply = function(ctx) {
|
764
|
+
for (var i=this.transforms.length-1; i>=0; i--) {
|
765
|
+
this.transforms[i].unapply(ctx);
|
766
|
+
}
|
767
|
+
}
|
768
|
+
|
769
|
+
this.applyToPoint = function(p) {
|
770
|
+
for (var i=0; i<this.transforms.length; i++) {
|
771
|
+
this.transforms[i].applyToPoint(p);
|
772
|
+
}
|
773
|
+
}
|
774
|
+
|
775
|
+
var data = svg.trim(svg.compressSpaces(v)).replace(/\)([a-zA-Z])/g, ') $1').replace(/\)(\s?,\s?)/g,') ').split(/\s(?=[a-z])/);
|
776
|
+
for (var i=0; i<data.length; i++) {
|
777
|
+
var type = svg.trim(data[i].split('(')[0]);
|
778
|
+
var s = data[i].split('(')[1].replace(')','');
|
779
|
+
var transformType = this.Type[type];
|
780
|
+
if (typeof transformType != 'undefined') {
|
781
|
+
var transform = new transformType(s);
|
782
|
+
transform.type = type;
|
783
|
+
this.transforms.push(transform);
|
784
|
+
}
|
785
|
+
}
|
786
|
+
}
|
787
|
+
|
788
|
+
// aspect ratio
|
789
|
+
svg.AspectRatio = function(ctx, aspectRatio, width, desiredWidth, height, desiredHeight, minX, minY, refX, refY) {
|
790
|
+
// aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
|
791
|
+
aspectRatio = svg.compressSpaces(aspectRatio);
|
792
|
+
aspectRatio = aspectRatio.replace(/^defer\s/,''); // ignore defer
|
793
|
+
var align = aspectRatio.split(' ')[0] || 'xMidYMid';
|
794
|
+
var meetOrSlice = aspectRatio.split(' ')[1] || 'meet';
|
795
|
+
|
796
|
+
// calculate scale
|
797
|
+
var scaleX = width / desiredWidth;
|
798
|
+
var scaleY = height / desiredHeight;
|
799
|
+
var scaleMin = Math.min(scaleX, scaleY);
|
800
|
+
var scaleMax = Math.max(scaleX, scaleY);
|
801
|
+
if (meetOrSlice == 'meet') { desiredWidth *= scaleMin; desiredHeight *= scaleMin; }
|
802
|
+
if (meetOrSlice == 'slice') { desiredWidth *= scaleMax; desiredHeight *= scaleMax; }
|
803
|
+
|
804
|
+
refX = new svg.Property('refX', refX);
|
805
|
+
refY = new svg.Property('refY', refY);
|
806
|
+
if (refX.hasValue() && refY.hasValue()) {
|
807
|
+
ctx.translate(-scaleMin * refX.toPixels('x'), -scaleMin * refY.toPixels('y'));
|
808
|
+
}
|
809
|
+
else {
|
810
|
+
// align
|
811
|
+
if (align.match(/^xMid/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width / 2.0 - desiredWidth / 2.0, 0);
|
812
|
+
if (align.match(/YMid$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height / 2.0 - desiredHeight / 2.0);
|
813
|
+
if (align.match(/^xMax/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width - desiredWidth, 0);
|
814
|
+
if (align.match(/YMax$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height - desiredHeight);
|
815
|
+
}
|
816
|
+
|
817
|
+
// scale
|
818
|
+
if (align == 'none') ctx.scale(scaleX, scaleY);
|
819
|
+
else if (meetOrSlice == 'meet') ctx.scale(scaleMin, scaleMin);
|
820
|
+
else if (meetOrSlice == 'slice') ctx.scale(scaleMax, scaleMax);
|
821
|
+
|
822
|
+
// translate
|
823
|
+
ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY);
|
824
|
+
}
|
825
|
+
|
826
|
+
// elements
|
827
|
+
svg.Element = {}
|
828
|
+
|
829
|
+
svg.EmptyProperty = new svg.Property('EMPTY', '');
|
830
|
+
|
831
|
+
svg.Element.ElementBase = function(node) {
|
832
|
+
this.attributes = {};
|
833
|
+
this.styles = {};
|
834
|
+
this.stylesSpecificity = {};
|
835
|
+
this.children = [];
|
836
|
+
|
837
|
+
// get or create attribute
|
838
|
+
this.attribute = function(name, createIfNotExists) {
|
839
|
+
var a = this.attributes[name];
|
840
|
+
if (a != null) return a;
|
841
|
+
|
842
|
+
if (createIfNotExists == true) { a = new svg.Property(name, ''); this.attributes[name] = a; }
|
843
|
+
return a || svg.EmptyProperty;
|
844
|
+
}
|
845
|
+
|
846
|
+
this.getHrefAttribute = function() {
|
847
|
+
for (var a in this.attributes) {
|
848
|
+
if (a == 'href' || a.match(/:href$/)) {
|
849
|
+
return this.attributes[a];
|
850
|
+
}
|
851
|
+
}
|
852
|
+
return svg.EmptyProperty;
|
853
|
+
}
|
854
|
+
|
855
|
+
// get or create style, crawls up node tree
|
856
|
+
this.style = function(name, createIfNotExists, skipAncestors) {
|
857
|
+
var s = this.styles[name];
|
858
|
+
if (s != null) return s;
|
859
|
+
|
860
|
+
var a = this.attribute(name);
|
861
|
+
if (a != null && a.hasValue()) {
|
862
|
+
this.styles[name] = a; // move up to me to cache
|
863
|
+
return a;
|
864
|
+
}
|
865
|
+
|
866
|
+
if (skipAncestors != true) {
|
867
|
+
var p = this.parent;
|
868
|
+
if (p != null) {
|
869
|
+
var ps = p.style(name);
|
870
|
+
if (ps != null && ps.hasValue()) {
|
871
|
+
return ps;
|
872
|
+
}
|
873
|
+
}
|
874
|
+
}
|
875
|
+
|
876
|
+
if (createIfNotExists == true) { s = new svg.Property(name, ''); this.styles[name] = s; }
|
877
|
+
return s || svg.EmptyProperty;
|
878
|
+
}
|
879
|
+
|
880
|
+
// base render
|
881
|
+
this.render = function(ctx) {
|
882
|
+
// don't render display=none
|
883
|
+
if (this.style('display').value == 'none') return;
|
884
|
+
|
885
|
+
// don't render visibility=hidden
|
886
|
+
if (this.style('visibility').value == 'hidden') return;
|
887
|
+
|
888
|
+
ctx.save();
|
889
|
+
if (this.style('mask').hasValue()) { // mask
|
890
|
+
var mask = this.style('mask').getDefinition();
|
891
|
+
if (mask != null) mask.apply(ctx, this);
|
892
|
+
}
|
893
|
+
else if (this.style('filter').hasValue()) { // filter
|
894
|
+
var filter = this.style('filter').getDefinition();
|
895
|
+
if (filter != null) filter.apply(ctx, this);
|
896
|
+
}
|
897
|
+
else {
|
898
|
+
this.setContext(ctx);
|
899
|
+
this.renderChildren(ctx);
|
900
|
+
this.clearContext(ctx);
|
901
|
+
}
|
902
|
+
ctx.restore();
|
903
|
+
}
|
904
|
+
|
905
|
+
// base set context
|
906
|
+
this.setContext = function(ctx) {
|
907
|
+
// OVERRIDE ME!
|
908
|
+
}
|
909
|
+
|
910
|
+
// base clear context
|
911
|
+
this.clearContext = function(ctx) {
|
912
|
+
// OVERRIDE ME!
|
913
|
+
}
|
914
|
+
|
915
|
+
// base render children
|
916
|
+
this.renderChildren = function(ctx) {
|
917
|
+
for (var i=0; i<this.children.length; i++) {
|
918
|
+
this.children[i].render(ctx);
|
919
|
+
}
|
920
|
+
}
|
921
|
+
|
922
|
+
this.addChild = function(childNode, create) {
|
923
|
+
var child = childNode;
|
924
|
+
if (create) child = svg.CreateElement(childNode);
|
925
|
+
child.parent = this;
|
926
|
+
if (child.type != 'title') { this.children.push(child); }
|
927
|
+
}
|
928
|
+
|
929
|
+
this.addStylesFromStyleDefinition = function () {
|
930
|
+
// add styles
|
931
|
+
for (var selector in svg.Styles) {
|
932
|
+
if (selector[0] != '@' && matchesSelector(node, selector)) {
|
933
|
+
var styles = svg.Styles[selector];
|
934
|
+
var specificity = svg.StylesSpecificity[selector];
|
935
|
+
if (styles != null) {
|
936
|
+
for (var name in styles) {
|
937
|
+
var existingSpecificity = this.stylesSpecificity[name];
|
938
|
+
if (typeof existingSpecificity == 'undefined') {
|
939
|
+
existingSpecificity = '000';
|
940
|
+
}
|
941
|
+
if (specificity > existingSpecificity) {
|
942
|
+
this.styles[name] = styles[name];
|
943
|
+
this.stylesSpecificity[name] = specificity;
|
944
|
+
}
|
945
|
+
}
|
946
|
+
}
|
947
|
+
}
|
948
|
+
}
|
949
|
+
};
|
950
|
+
|
951
|
+
// Microsoft Edge fix
|
952
|
+
var allUppercase = new RegExp("^[A-Z\-]+$");
|
953
|
+
var normalizeAttributeName = function (name) {
|
954
|
+
if (allUppercase.test(name)) {
|
955
|
+
return name.toLowerCase();
|
956
|
+
}
|
957
|
+
return name;
|
958
|
+
};
|
959
|
+
|
960
|
+
if (node != null && node.nodeType == 1) { //ELEMENT_NODE
|
961
|
+
// add attributes
|
962
|
+
for (var i=0; i<node.attributes.length; i++) {
|
963
|
+
var attribute = node.attributes[i];
|
964
|
+
var nodeName = normalizeAttributeName(attribute.nodeName);
|
965
|
+
this.attributes[nodeName] = new svg.Property(nodeName, attribute.value);
|
966
|
+
}
|
967
|
+
|
968
|
+
this.addStylesFromStyleDefinition();
|
969
|
+
|
970
|
+
// add inline styles
|
971
|
+
if (this.attribute('style').hasValue()) {
|
972
|
+
var styles = this.attribute('style').value.split(';');
|
973
|
+
for (var i=0; i<styles.length; i++) {
|
974
|
+
if (svg.trim(styles[i]) != '') {
|
975
|
+
var style = styles[i].split(':');
|
976
|
+
var name = svg.trim(style[0]);
|
977
|
+
var value = svg.trim(style[1]);
|
978
|
+
this.styles[name] = new svg.Property(name, value);
|
979
|
+
}
|
980
|
+
}
|
981
|
+
}
|
982
|
+
|
983
|
+
// add id
|
984
|
+
if (this.attribute('id').hasValue()) {
|
985
|
+
if (svg.Definitions[this.attribute('id').value] == null) {
|
986
|
+
svg.Definitions[this.attribute('id').value] = this;
|
987
|
+
}
|
988
|
+
}
|
989
|
+
|
990
|
+
// add children
|
991
|
+
for (var i=0; i<node.childNodes.length; i++) {
|
992
|
+
var childNode = node.childNodes[i];
|
993
|
+
if (childNode.nodeType == 1) this.addChild(childNode, true); //ELEMENT_NODE
|
994
|
+
if (this.captureTextNodes && (childNode.nodeType == 3 || childNode.nodeType == 4)) {
|
995
|
+
var text = childNode.value || childNode.text || childNode.textContent || '';
|
996
|
+
if (svg.compressSpaces(text) != '') {
|
997
|
+
this.addChild(new svg.Element.tspan(childNode), false); // TEXT_NODE
|
998
|
+
}
|
999
|
+
}
|
1000
|
+
}
|
1001
|
+
}
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
svg.Element.RenderedElementBase = function(node) {
|
1005
|
+
this.base = svg.Element.ElementBase;
|
1006
|
+
this.base(node);
|
1007
|
+
|
1008
|
+
this.setContext = function(ctx) {
|
1009
|
+
// fill
|
1010
|
+
if (this.style('fill').isUrlDefinition()) {
|
1011
|
+
var fs = this.style('fill').getFillStyleDefinition(this, this.style('fill-opacity'));
|
1012
|
+
if (fs != null) ctx.fillStyle = fs;
|
1013
|
+
}
|
1014
|
+
else if (this.style('fill').hasValue()) {
|
1015
|
+
var fillStyle = this.style('fill');
|
1016
|
+
if (fillStyle.value == 'currentColor') fillStyle.value = this.style('color').value;
|
1017
|
+
if (fillStyle.value != 'inherit') ctx.fillStyle = (fillStyle.value == 'none' ? 'rgba(0,0,0,0)' : fillStyle.value);
|
1018
|
+
}
|
1019
|
+
if (this.style('fill-opacity').hasValue()) {
|
1020
|
+
var fillStyle = new svg.Property('fill', ctx.fillStyle);
|
1021
|
+
fillStyle = fillStyle.addOpacity(this.style('fill-opacity'));
|
1022
|
+
ctx.fillStyle = fillStyle.value;
|
1023
|
+
}
|
1024
|
+
|
1025
|
+
// stroke
|
1026
|
+
if (this.style('stroke').isUrlDefinition()) {
|
1027
|
+
var fs = this.style('stroke').getFillStyleDefinition(this, this.style('stroke-opacity'));
|
1028
|
+
if (fs != null) ctx.strokeStyle = fs;
|
1029
|
+
}
|
1030
|
+
else if (this.style('stroke').hasValue()) {
|
1031
|
+
var strokeStyle = this.style('stroke');
|
1032
|
+
if (strokeStyle.value == 'currentColor') strokeStyle.value = this.style('color').value;
|
1033
|
+
if (strokeStyle.value != 'inherit') ctx.strokeStyle = (strokeStyle.value == 'none' ? 'rgba(0,0,0,0)' : strokeStyle.value);
|
1034
|
+
}
|
1035
|
+
if (this.style('stroke-opacity').hasValue()) {
|
1036
|
+
var strokeStyle = new svg.Property('stroke', ctx.strokeStyle);
|
1037
|
+
strokeStyle = strokeStyle.addOpacity(this.style('stroke-opacity'));
|
1038
|
+
ctx.strokeStyle = strokeStyle.value;
|
1039
|
+
}
|
1040
|
+
if (this.style('stroke-width').hasValue()) {
|
1041
|
+
var newLineWidth = this.style('stroke-width').toPixels();
|
1042
|
+
ctx.lineWidth = newLineWidth == 0 ? 0.001 : newLineWidth; // browsers don't respect 0
|
1043
|
+
}
|
1044
|
+
if (this.style('stroke-linecap').hasValue()) ctx.lineCap = this.style('stroke-linecap').value;
|
1045
|
+
if (this.style('stroke-linejoin').hasValue()) ctx.lineJoin = this.style('stroke-linejoin').value;
|
1046
|
+
if (this.style('stroke-miterlimit').hasValue()) ctx.miterLimit = this.style('stroke-miterlimit').value;
|
1047
|
+
if (this.style('paint-order').hasValue()) ctx.paintOrder = this.style('paint-order').value;
|
1048
|
+
if (this.style('stroke-dasharray').hasValue() && this.style('stroke-dasharray').value != 'none') {
|
1049
|
+
var gaps = svg.ToNumberArray(this.style('stroke-dasharray').value);
|
1050
|
+
if (typeof ctx.setLineDash != 'undefined') { ctx.setLineDash(gaps); }
|
1051
|
+
else if (typeof ctx.webkitLineDash != 'undefined') { ctx.webkitLineDash = gaps; }
|
1052
|
+
else if (typeof ctx.mozDash != 'undefined' && !(gaps.length==1 && gaps[0]==0)) { ctx.mozDash = gaps; }
|
1053
|
+
|
1054
|
+
var offset = this.style('stroke-dashoffset').numValueOrDefault(1);
|
1055
|
+
if (typeof ctx.lineDashOffset != 'undefined') { ctx.lineDashOffset = offset; }
|
1056
|
+
else if (typeof ctx.webkitLineDashOffset != 'undefined') { ctx.webkitLineDashOffset = offset; }
|
1057
|
+
else if (typeof ctx.mozDashOffset != 'undefined') { ctx.mozDashOffset = offset; }
|
1058
|
+
}
|
1059
|
+
|
1060
|
+
// font
|
1061
|
+
if (typeof ctx.font != 'undefined') {
|
1062
|
+
ctx.font = svg.Font.CreateFont(
|
1063
|
+
this.style('font-style').value,
|
1064
|
+
this.style('font-variant').value,
|
1065
|
+
this.style('font-weight').value,
|
1066
|
+
this.style('font-size').hasValue() ? this.style('font-size').toPixels() + 'px' : '',
|
1067
|
+
this.style('font-family').value).toString();
|
1068
|
+
}
|
1069
|
+
|
1070
|
+
// transform
|
1071
|
+
if (this.style('transform', false, true).hasValue()) {
|
1072
|
+
var transform = new svg.Transform(this.style('transform', false, true).value);
|
1073
|
+
transform.apply(ctx);
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
// clip
|
1077
|
+
if (this.style('clip-path', false, true).hasValue()) {
|
1078
|
+
var clip = this.style('clip-path', false, true).getDefinition();
|
1079
|
+
if (clip != null) clip.apply(ctx);
|
1080
|
+
}
|
1081
|
+
|
1082
|
+
// opacity
|
1083
|
+
if (this.style('opacity').hasValue()) {
|
1084
|
+
ctx.globalAlpha = this.style('opacity').numValue();
|
1085
|
+
}
|
1086
|
+
}
|
1087
|
+
}
|
1088
|
+
svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase;
|
1089
|
+
|
1090
|
+
svg.Element.PathElementBase = function(node) {
|
1091
|
+
this.base = svg.Element.RenderedElementBase;
|
1092
|
+
this.base(node);
|
1093
|
+
|
1094
|
+
this.path = function(ctx) {
|
1095
|
+
if (ctx != null) ctx.beginPath();
|
1096
|
+
return new svg.BoundingBox();
|
1097
|
+
}
|
1098
|
+
|
1099
|
+
this.renderChildren = function(ctx) {
|
1100
|
+
this.path(ctx);
|
1101
|
+
svg.Mouse.checkPath(this, ctx);
|
1102
|
+
if (ctx.fillStyle != '') {
|
1103
|
+
if (this.style('fill-rule').valueOrDefault('inherit') != 'inherit') { ctx.fill(this.style('fill-rule').value); }
|
1104
|
+
else { ctx.fill(); }
|
1105
|
+
}
|
1106
|
+
if (ctx.strokeStyle != '') ctx.stroke();
|
1107
|
+
|
1108
|
+
var markers = this.getMarkers();
|
1109
|
+
if (markers != null) {
|
1110
|
+
if (this.style('marker-start').isUrlDefinition()) {
|
1111
|
+
var marker = this.style('marker-start').getDefinition();
|
1112
|
+
marker.render(ctx, markers[0][0], markers[0][1]);
|
1113
|
+
}
|
1114
|
+
if (this.style('marker-mid').isUrlDefinition()) {
|
1115
|
+
var marker = this.style('marker-mid').getDefinition();
|
1116
|
+
for (var i=1;i<markers.length-1;i++) {
|
1117
|
+
marker.render(ctx, markers[i][0], markers[i][1]);
|
1118
|
+
}
|
1119
|
+
}
|
1120
|
+
if (this.style('marker-end').isUrlDefinition()) {
|
1121
|
+
var marker = this.style('marker-end').getDefinition();
|
1122
|
+
marker.render(ctx, markers[markers.length-1][0], markers[markers.length-1][1]);
|
1123
|
+
}
|
1124
|
+
}
|
1125
|
+
}
|
1126
|
+
|
1127
|
+
this.getBoundingBox = function() {
|
1128
|
+
return this.path();
|
1129
|
+
}
|
1130
|
+
|
1131
|
+
this.getMarkers = function() {
|
1132
|
+
return null;
|
1133
|
+
}
|
1134
|
+
}
|
1135
|
+
svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase;
|
1136
|
+
|
1137
|
+
// svg element
|
1138
|
+
svg.Element.svg = function(node) {
|
1139
|
+
this.base = svg.Element.RenderedElementBase;
|
1140
|
+
this.base(node);
|
1141
|
+
|
1142
|
+
this.baseClearContext = this.clearContext;
|
1143
|
+
this.clearContext = function(ctx) {
|
1144
|
+
this.baseClearContext(ctx);
|
1145
|
+
svg.ViewPort.RemoveCurrent();
|
1146
|
+
}
|
1147
|
+
|
1148
|
+
this.baseSetContext = this.setContext;
|
1149
|
+
this.setContext = function(ctx) {
|
1150
|
+
// initial values and defaults
|
1151
|
+
ctx.strokeStyle = 'rgba(0,0,0,0)';
|
1152
|
+
ctx.lineCap = 'butt';
|
1153
|
+
ctx.lineJoin = 'miter';
|
1154
|
+
ctx.miterLimit = 4;
|
1155
|
+
if (ctx.canvas.style && typeof ctx.font != 'undefined' && typeof windowEnv.getComputedStyle != 'undefined') {
|
1156
|
+
ctx.font = windowEnv.getComputedStyle(ctx.canvas).getPropertyValue('font');
|
1157
|
+
}
|
1158
|
+
|
1159
|
+
this.baseSetContext(ctx);
|
1160
|
+
|
1161
|
+
// create new view port
|
1162
|
+
if (!this.attribute('x').hasValue()) this.attribute('x', true).value = 0;
|
1163
|
+
if (!this.attribute('y').hasValue()) this.attribute('y', true).value = 0;
|
1164
|
+
ctx.translate(this.attribute('x').toPixels('x'), this.attribute('y').toPixels('y'));
|
1165
|
+
|
1166
|
+
var width = svg.ViewPort.width();
|
1167
|
+
var height = svg.ViewPort.height();
|
1168
|
+
|
1169
|
+
if (!this.attribute('width').hasValue()) this.attribute('width', true).value = '100%';
|
1170
|
+
if (!this.attribute('height').hasValue()) this.attribute('height', true).value = '100%';
|
1171
|
+
if (typeof this.root == 'undefined') {
|
1172
|
+
width = this.attribute('width').toPixels('x');
|
1173
|
+
height = this.attribute('height').toPixels('y');
|
1174
|
+
|
1175
|
+
var x = 0;
|
1176
|
+
var y = 0;
|
1177
|
+
if (this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) {
|
1178
|
+
x = -this.attribute('refX').toPixels('x');
|
1179
|
+
y = -this.attribute('refY').toPixels('y');
|
1180
|
+
}
|
1181
|
+
|
1182
|
+
if (this.attribute('overflow').valueOrDefault('hidden') != 'visible') {
|
1183
|
+
ctx.beginPath();
|
1184
|
+
ctx.moveTo(x, y);
|
1185
|
+
ctx.lineTo(width, y);
|
1186
|
+
ctx.lineTo(width, height);
|
1187
|
+
ctx.lineTo(x, height);
|
1188
|
+
ctx.closePath();
|
1189
|
+
ctx.clip();
|
1190
|
+
}
|
1191
|
+
}
|
1192
|
+
svg.ViewPort.SetCurrent(width, height);
|
1193
|
+
|
1194
|
+
// viewbox
|
1195
|
+
if (this.attribute('viewBox').hasValue()) {
|
1196
|
+
var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
|
1197
|
+
var minX = viewBox[0];
|
1198
|
+
var minY = viewBox[1];
|
1199
|
+
width = viewBox[2];
|
1200
|
+
height = viewBox[3];
|
1201
|
+
|
1202
|
+
svg.AspectRatio(ctx,
|
1203
|
+
this.attribute('preserveAspectRatio').value,
|
1204
|
+
svg.ViewPort.width(),
|
1205
|
+
width,
|
1206
|
+
svg.ViewPort.height(),
|
1207
|
+
height,
|
1208
|
+
minX,
|
1209
|
+
minY,
|
1210
|
+
this.attribute('refX').value,
|
1211
|
+
this.attribute('refY').value);
|
1212
|
+
|
1213
|
+
svg.ViewPort.RemoveCurrent();
|
1214
|
+
svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
|
1215
|
+
}
|
1216
|
+
}
|
1217
|
+
}
|
1218
|
+
svg.Element.svg.prototype = new svg.Element.RenderedElementBase;
|
1219
|
+
|
1220
|
+
// rect element
|
1221
|
+
svg.Element.rect = function(node) {
|
1222
|
+
this.base = svg.Element.PathElementBase;
|
1223
|
+
this.base(node);
|
1224
|
+
|
1225
|
+
this.path = function(ctx) {
|
1226
|
+
var x = this.attribute('x').toPixels('x');
|
1227
|
+
var y = this.attribute('y').toPixels('y');
|
1228
|
+
var width = this.attribute('width').toPixels('x');
|
1229
|
+
var height = this.attribute('height').toPixels('y');
|
1230
|
+
var rx = this.attribute('rx').toPixels('x');
|
1231
|
+
var ry = this.attribute('ry').toPixels('y');
|
1232
|
+
if (this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry = rx;
|
1233
|
+
if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx = ry;
|
1234
|
+
rx = Math.min(rx, width / 2.0);
|
1235
|
+
ry = Math.min(ry, height / 2.0);
|
1236
|
+
if (ctx != null) {
|
1237
|
+
ctx.beginPath();
|
1238
|
+
ctx.moveTo(x + rx, y);
|
1239
|
+
ctx.lineTo(x + width - rx, y);
|
1240
|
+
ctx.quadraticCurveTo(x + width, y, x + width, y + ry)
|
1241
|
+
ctx.lineTo(x + width, y + height - ry);
|
1242
|
+
ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height)
|
1243
|
+
ctx.lineTo(x + rx, y + height);
|
1244
|
+
ctx.quadraticCurveTo(x, y + height, x, y + height - ry)
|
1245
|
+
ctx.lineTo(x, y + ry);
|
1246
|
+
ctx.quadraticCurveTo(x, y, x + rx, y)
|
1247
|
+
ctx.closePath();
|
1248
|
+
}
|
1249
|
+
|
1250
|
+
return new svg.BoundingBox(x, y, x + width, y + height);
|
1251
|
+
}
|
1252
|
+
}
|
1253
|
+
svg.Element.rect.prototype = new svg.Element.PathElementBase;
|
1254
|
+
|
1255
|
+
// circle element
|
1256
|
+
svg.Element.circle = function(node) {
|
1257
|
+
this.base = svg.Element.PathElementBase;
|
1258
|
+
this.base(node);
|
1259
|
+
|
1260
|
+
this.path = function(ctx) {
|
1261
|
+
var cx = this.attribute('cx').toPixels('x');
|
1262
|
+
var cy = this.attribute('cy').toPixels('y');
|
1263
|
+
var r = this.attribute('r').toPixels();
|
1264
|
+
|
1265
|
+
if (ctx != null) {
|
1266
|
+
ctx.beginPath();
|
1267
|
+
ctx.arc(cx, cy, r, 0, Math.PI * 2, true);
|
1268
|
+
ctx.closePath();
|
1269
|
+
}
|
1270
|
+
|
1271
|
+
return new svg.BoundingBox(cx - r, cy - r, cx + r, cy + r);
|
1272
|
+
}
|
1273
|
+
}
|
1274
|
+
svg.Element.circle.prototype = new svg.Element.PathElementBase;
|
1275
|
+
|
1276
|
+
// ellipse element
|
1277
|
+
svg.Element.ellipse = function(node) {
|
1278
|
+
this.base = svg.Element.PathElementBase;
|
1279
|
+
this.base(node);
|
1280
|
+
|
1281
|
+
this.path = function(ctx) {
|
1282
|
+
var KAPPA = 4 * ((Math.sqrt(2) - 1) / 3);
|
1283
|
+
var rx = this.attribute('rx').toPixels('x');
|
1284
|
+
var ry = this.attribute('ry').toPixels('y');
|
1285
|
+
var cx = this.attribute('cx').toPixels('x');
|
1286
|
+
var cy = this.attribute('cy').toPixels('y');
|
1287
|
+
|
1288
|
+
if (ctx != null) {
|
1289
|
+
ctx.beginPath();
|
1290
|
+
ctx.moveTo(cx, cy - ry);
|
1291
|
+
ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry, cx + rx, cy - (KAPPA * ry), cx + rx, cy);
|
1292
|
+
ctx.bezierCurveTo(cx + rx, cy + (KAPPA * ry), cx + (KAPPA * rx), cy + ry, cx, cy + ry);
|
1293
|
+
ctx.bezierCurveTo(cx - (KAPPA * rx), cy + ry, cx - rx, cy + (KAPPA * ry), cx - rx, cy);
|
1294
|
+
ctx.bezierCurveTo(cx - rx, cy - (KAPPA * ry), cx - (KAPPA * rx), cy - ry, cx, cy - ry);
|
1295
|
+
ctx.closePath();
|
1296
|
+
}
|
1297
|
+
|
1298
|
+
return new svg.BoundingBox(cx - rx, cy - ry, cx + rx, cy + ry);
|
1299
|
+
}
|
1300
|
+
}
|
1301
|
+
svg.Element.ellipse.prototype = new svg.Element.PathElementBase;
|
1302
|
+
|
1303
|
+
// line element
|
1304
|
+
svg.Element.line = function(node) {
|
1305
|
+
this.base = svg.Element.PathElementBase;
|
1306
|
+
this.base(node);
|
1307
|
+
|
1308
|
+
this.getPoints = function() {
|
1309
|
+
return [
|
1310
|
+
new svg.Point(this.attribute('x1').toPixels('x'), this.attribute('y1').toPixels('y')),
|
1311
|
+
new svg.Point(this.attribute('x2').toPixels('x'), this.attribute('y2').toPixels('y'))];
|
1312
|
+
}
|
1313
|
+
|
1314
|
+
this.path = function(ctx) {
|
1315
|
+
var points = this.getPoints();
|
1316
|
+
|
1317
|
+
if (ctx != null) {
|
1318
|
+
ctx.beginPath();
|
1319
|
+
ctx.moveTo(points[0].x, points[0].y);
|
1320
|
+
ctx.lineTo(points[1].x, points[1].y);
|
1321
|
+
}
|
1322
|
+
|
1323
|
+
return new svg.BoundingBox(points[0].x, points[0].y, points[1].x, points[1].y);
|
1324
|
+
}
|
1325
|
+
|
1326
|
+
this.getMarkers = function() {
|
1327
|
+
var points = this.getPoints();
|
1328
|
+
var a = points[0].angleTo(points[1]);
|
1329
|
+
return [[points[0], a], [points[1], a]];
|
1330
|
+
}
|
1331
|
+
}
|
1332
|
+
svg.Element.line.prototype = new svg.Element.PathElementBase;
|
1333
|
+
|
1334
|
+
// polyline element
|
1335
|
+
svg.Element.polyline = function(node) {
|
1336
|
+
this.base = svg.Element.PathElementBase;
|
1337
|
+
this.base(node);
|
1338
|
+
|
1339
|
+
this.points = svg.CreatePath(this.attribute('points').value);
|
1340
|
+
this.path = function(ctx) {
|
1341
|
+
var bb = new svg.BoundingBox(this.points[0].x, this.points[0].y);
|
1342
|
+
if (ctx != null) {
|
1343
|
+
ctx.beginPath();
|
1344
|
+
ctx.moveTo(this.points[0].x, this.points[0].y);
|
1345
|
+
}
|
1346
|
+
for (var i=1; i<this.points.length; i++) {
|
1347
|
+
bb.addPoint(this.points[i].x, this.points[i].y);
|
1348
|
+
if (ctx != null) ctx.lineTo(this.points[i].x, this.points[i].y);
|
1349
|
+
}
|
1350
|
+
return bb;
|
1351
|
+
}
|
1352
|
+
|
1353
|
+
this.getMarkers = function() {
|
1354
|
+
var markers = [];
|
1355
|
+
for (var i=0; i<this.points.length - 1; i++) {
|
1356
|
+
markers.push([this.points[i], this.points[i].angleTo(this.points[i+1])]);
|
1357
|
+
}
|
1358
|
+
if (markers.length > 0) {
|
1359
|
+
markers.push([this.points[this.points.length-1], markers[markers.length-1][1]]);
|
1360
|
+
}
|
1361
|
+
return markers;
|
1362
|
+
}
|
1363
|
+
}
|
1364
|
+
svg.Element.polyline.prototype = new svg.Element.PathElementBase;
|
1365
|
+
|
1366
|
+
// polygon element
|
1367
|
+
svg.Element.polygon = function(node) {
|
1368
|
+
this.base = svg.Element.polyline;
|
1369
|
+
this.base(node);
|
1370
|
+
|
1371
|
+
this.basePath = this.path;
|
1372
|
+
this.path = function(ctx) {
|
1373
|
+
var bb = this.basePath(ctx);
|
1374
|
+
if (ctx != null) {
|
1375
|
+
ctx.lineTo(this.points[0].x, this.points[0].y);
|
1376
|
+
ctx.closePath();
|
1377
|
+
}
|
1378
|
+
return bb;
|
1379
|
+
}
|
1380
|
+
}
|
1381
|
+
svg.Element.polygon.prototype = new svg.Element.polyline;
|
1382
|
+
|
1383
|
+
// path element
|
1384
|
+
svg.Element.path = function(node) {
|
1385
|
+
this.base = svg.Element.PathElementBase;
|
1386
|
+
this.base(node);
|
1387
|
+
|
1388
|
+
var d = this.attribute('d').value;
|
1389
|
+
// TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF
|
1390
|
+
d = d.replace(/,/gm,' '); // get rid of all commas
|
1391
|
+
// As the end of a match can also be the start of the next match, we need to run this replace twice.
|
1392
|
+
for(var i=0; i<2; i++)
|
1393
|
+
d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,'$1 $2'); // suffix commands with spaces
|
1394
|
+
d = d.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // prefix commands with spaces
|
1395
|
+
d = d.replace(/([0-9])([+\-])/gm,'$1 $2'); // separate digits on +- signs
|
1396
|
+
// Again, we need to run this twice to find all occurances
|
1397
|
+
for(var i=0; i<2; i++)
|
1398
|
+
d = d.replace(/(\.[0-9]*)(\.)/gm,'$1 $2'); // separate digits when they start with a comma
|
1399
|
+
d = d.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,'$1 $3 $4 '); // shorthand elliptical arc path syntax
|
1400
|
+
d = svg.compressSpaces(d); // compress multiple spaces
|
1401
|
+
d = svg.trim(d);
|
1402
|
+
this.PathParser = new (function(d) {
|
1403
|
+
this.tokens = d.split(' ');
|
1404
|
+
|
1405
|
+
this.reset = function() {
|
1406
|
+
this.i = -1;
|
1407
|
+
this.command = '';
|
1408
|
+
this.previousCommand = '';
|
1409
|
+
this.start = new svg.Point(0, 0);
|
1410
|
+
this.control = new svg.Point(0, 0);
|
1411
|
+
this.current = new svg.Point(0, 0);
|
1412
|
+
this.points = [];
|
1413
|
+
this.angles = [];
|
1414
|
+
}
|
1415
|
+
|
1416
|
+
this.isEnd = function() {
|
1417
|
+
return this.i >= this.tokens.length - 1;
|
1418
|
+
}
|
1419
|
+
|
1420
|
+
this.isCommandOrEnd = function() {
|
1421
|
+
if (this.isEnd()) return true;
|
1422
|
+
return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null;
|
1423
|
+
}
|
1424
|
+
|
1425
|
+
this.isRelativeCommand = function() {
|
1426
|
+
switch(this.command)
|
1427
|
+
{
|
1428
|
+
case 'm':
|
1429
|
+
case 'l':
|
1430
|
+
case 'h':
|
1431
|
+
case 'v':
|
1432
|
+
case 'c':
|
1433
|
+
case 's':
|
1434
|
+
case 'q':
|
1435
|
+
case 't':
|
1436
|
+
case 'a':
|
1437
|
+
case 'z':
|
1438
|
+
return true;
|
1439
|
+
break;
|
1440
|
+
}
|
1441
|
+
return false;
|
1442
|
+
}
|
1443
|
+
|
1444
|
+
this.getToken = function() {
|
1445
|
+
this.i++;
|
1446
|
+
return this.tokens[this.i];
|
1447
|
+
}
|
1448
|
+
|
1449
|
+
this.getScalar = function() {
|
1450
|
+
return parseFloat(this.getToken());
|
1451
|
+
}
|
1452
|
+
|
1453
|
+
this.nextCommand = function() {
|
1454
|
+
this.previousCommand = this.command;
|
1455
|
+
this.command = this.getToken();
|
1456
|
+
}
|
1457
|
+
|
1458
|
+
this.getPoint = function() {
|
1459
|
+
var p = new svg.Point(this.getScalar(), this.getScalar());
|
1460
|
+
return this.makeAbsolute(p);
|
1461
|
+
}
|
1462
|
+
|
1463
|
+
this.getAsControlPoint = function() {
|
1464
|
+
var p = this.getPoint();
|
1465
|
+
this.control = p;
|
1466
|
+
return p;
|
1467
|
+
}
|
1468
|
+
|
1469
|
+
this.getAsCurrentPoint = function() {
|
1470
|
+
var p = this.getPoint();
|
1471
|
+
this.current = p;
|
1472
|
+
return p;
|
1473
|
+
}
|
1474
|
+
|
1475
|
+
this.getReflectedControlPoint = function() {
|
1476
|
+
if (this.previousCommand.toLowerCase() != 'c' &&
|
1477
|
+
this.previousCommand.toLowerCase() != 's' &&
|
1478
|
+
this.previousCommand.toLowerCase() != 'q' &&
|
1479
|
+
this.previousCommand.toLowerCase() != 't' ){
|
1480
|
+
return this.current;
|
1481
|
+
}
|
1482
|
+
|
1483
|
+
// reflect point
|
1484
|
+
var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y);
|
1485
|
+
return p;
|
1486
|
+
}
|
1487
|
+
|
1488
|
+
this.makeAbsolute = function(p) {
|
1489
|
+
if (this.isRelativeCommand()) {
|
1490
|
+
p.x += this.current.x;
|
1491
|
+
p.y += this.current.y;
|
1492
|
+
}
|
1493
|
+
return p;
|
1494
|
+
}
|
1495
|
+
|
1496
|
+
this.addMarker = function(p, from, priorTo) {
|
1497
|
+
// if the last angle isn't filled in because we didn't have this point yet ...
|
1498
|
+
if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length-1] == null) {
|
1499
|
+
this.angles[this.angles.length-1] = this.points[this.points.length-1].angleTo(priorTo);
|
1500
|
+
}
|
1501
|
+
this.addMarkerAngle(p, from == null ? null : from.angleTo(p));
|
1502
|
+
}
|
1503
|
+
|
1504
|
+
this.addMarkerAngle = function(p, a) {
|
1505
|
+
this.points.push(p);
|
1506
|
+
this.angles.push(a);
|
1507
|
+
}
|
1508
|
+
|
1509
|
+
this.getMarkerPoints = function() { return this.points; }
|
1510
|
+
this.getMarkerAngles = function() {
|
1511
|
+
for (var i=0; i<this.angles.length; i++) {
|
1512
|
+
if (this.angles[i] == null) {
|
1513
|
+
for (var j=i+1; j<this.angles.length; j++) {
|
1514
|
+
if (this.angles[j] != null) {
|
1515
|
+
this.angles[i] = this.angles[j];
|
1516
|
+
break;
|
1517
|
+
}
|
1518
|
+
}
|
1519
|
+
}
|
1520
|
+
}
|
1521
|
+
return this.angles;
|
1522
|
+
}
|
1523
|
+
})(d);
|
1524
|
+
|
1525
|
+
this.path = function(ctx) {
|
1526
|
+
var pp = this.PathParser;
|
1527
|
+
pp.reset();
|
1528
|
+
|
1529
|
+
var bb = new svg.BoundingBox();
|
1530
|
+
if (ctx != null) ctx.beginPath();
|
1531
|
+
while (!pp.isEnd()) {
|
1532
|
+
pp.nextCommand();
|
1533
|
+
switch (pp.command) {
|
1534
|
+
case 'M':
|
1535
|
+
case 'm':
|
1536
|
+
var p = pp.getAsCurrentPoint();
|
1537
|
+
pp.addMarker(p);
|
1538
|
+
bb.addPoint(p.x, p.y);
|
1539
|
+
if (ctx != null) ctx.moveTo(p.x, p.y);
|
1540
|
+
pp.start = pp.current;
|
1541
|
+
while (!pp.isCommandOrEnd()) {
|
1542
|
+
var p = pp.getAsCurrentPoint();
|
1543
|
+
pp.addMarker(p, pp.start);
|
1544
|
+
bb.addPoint(p.x, p.y);
|
1545
|
+
if (ctx != null) ctx.lineTo(p.x, p.y);
|
1546
|
+
}
|
1547
|
+
break;
|
1548
|
+
case 'L':
|
1549
|
+
case 'l':
|
1550
|
+
while (!pp.isCommandOrEnd()) {
|
1551
|
+
var c = pp.current;
|
1552
|
+
var p = pp.getAsCurrentPoint();
|
1553
|
+
pp.addMarker(p, c);
|
1554
|
+
bb.addPoint(p.x, p.y);
|
1555
|
+
if (ctx != null) ctx.lineTo(p.x, p.y);
|
1556
|
+
}
|
1557
|
+
break;
|
1558
|
+
case 'H':
|
1559
|
+
case 'h':
|
1560
|
+
while (!pp.isCommandOrEnd()) {
|
1561
|
+
var newP = new svg.Point((pp.isRelativeCommand() ? pp.current.x : 0) + pp.getScalar(), pp.current.y);
|
1562
|
+
pp.addMarker(newP, pp.current);
|
1563
|
+
pp.current = newP;
|
1564
|
+
bb.addPoint(pp.current.x, pp.current.y);
|
1565
|
+
if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
|
1566
|
+
}
|
1567
|
+
break;
|
1568
|
+
case 'V':
|
1569
|
+
case 'v':
|
1570
|
+
while (!pp.isCommandOrEnd()) {
|
1571
|
+
var newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.y : 0) + pp.getScalar());
|
1572
|
+
pp.addMarker(newP, pp.current);
|
1573
|
+
pp.current = newP;
|
1574
|
+
bb.addPoint(pp.current.x, pp.current.y);
|
1575
|
+
if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
|
1576
|
+
}
|
1577
|
+
break;
|
1578
|
+
case 'C':
|
1579
|
+
case 'c':
|
1580
|
+
while (!pp.isCommandOrEnd()) {
|
1581
|
+
var curr = pp.current;
|
1582
|
+
var p1 = pp.getPoint();
|
1583
|
+
var cntrl = pp.getAsControlPoint();
|
1584
|
+
var cp = pp.getAsCurrentPoint();
|
1585
|
+
pp.addMarker(cp, cntrl, p1);
|
1586
|
+
bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1587
|
+
if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1588
|
+
}
|
1589
|
+
break;
|
1590
|
+
case 'S':
|
1591
|
+
case 's':
|
1592
|
+
while (!pp.isCommandOrEnd()) {
|
1593
|
+
var curr = pp.current;
|
1594
|
+
var p1 = pp.getReflectedControlPoint();
|
1595
|
+
var cntrl = pp.getAsControlPoint();
|
1596
|
+
var cp = pp.getAsCurrentPoint();
|
1597
|
+
pp.addMarker(cp, cntrl, p1);
|
1598
|
+
bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1599
|
+
if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1600
|
+
}
|
1601
|
+
break;
|
1602
|
+
case 'Q':
|
1603
|
+
case 'q':
|
1604
|
+
while (!pp.isCommandOrEnd()) {
|
1605
|
+
var curr = pp.current;
|
1606
|
+
var cntrl = pp.getAsControlPoint();
|
1607
|
+
var cp = pp.getAsCurrentPoint();
|
1608
|
+
pp.addMarker(cp, cntrl, cntrl);
|
1609
|
+
bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1610
|
+
if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
|
1611
|
+
}
|
1612
|
+
break;
|
1613
|
+
case 'T':
|
1614
|
+
case 't':
|
1615
|
+
while (!pp.isCommandOrEnd()) {
|
1616
|
+
var curr = pp.current;
|
1617
|
+
var cntrl = pp.getReflectedControlPoint();
|
1618
|
+
pp.control = cntrl;
|
1619
|
+
var cp = pp.getAsCurrentPoint();
|
1620
|
+
pp.addMarker(cp, cntrl, cntrl);
|
1621
|
+
bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1622
|
+
if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
|
1623
|
+
}
|
1624
|
+
break;
|
1625
|
+
case 'A':
|
1626
|
+
case 'a':
|
1627
|
+
while (!pp.isCommandOrEnd()) {
|
1628
|
+
var curr = pp.current;
|
1629
|
+
var rx = pp.getScalar();
|
1630
|
+
var ry = pp.getScalar();
|
1631
|
+
var xAxisRotation = pp.getScalar() * (Math.PI / 180.0);
|
1632
|
+
var largeArcFlag = pp.getScalar();
|
1633
|
+
var sweepFlag = pp.getScalar();
|
1634
|
+
var cp = pp.getAsCurrentPoint();
|
1635
|
+
|
1636
|
+
// Conversion from endpoint to center parameterization
|
1637
|
+
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
1638
|
+
// x1', y1'
|
1639
|
+
var currp = new svg.Point(
|
1640
|
+
Math.cos(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.sin(xAxisRotation) * (curr.y - cp.y) / 2.0,
|
1641
|
+
-Math.sin(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.cos(xAxisRotation) * (curr.y - cp.y) / 2.0
|
1642
|
+
);
|
1643
|
+
// adjust radii
|
1644
|
+
var l = Math.pow(currp.x,2)/Math.pow(rx,2)+Math.pow(currp.y,2)/Math.pow(ry,2);
|
1645
|
+
if (l > 1) {
|
1646
|
+
rx *= Math.sqrt(l);
|
1647
|
+
ry *= Math.sqrt(l);
|
1648
|
+
}
|
1649
|
+
// cx', cy'
|
1650
|
+
var s = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt(
|
1651
|
+
((Math.pow(rx,2)*Math.pow(ry,2))-(Math.pow(rx,2)*Math.pow(currp.y,2))-(Math.pow(ry,2)*Math.pow(currp.x,2))) /
|
1652
|
+
(Math.pow(rx,2)*Math.pow(currp.y,2)+Math.pow(ry,2)*Math.pow(currp.x,2))
|
1653
|
+
);
|
1654
|
+
if (isNaN(s)) s = 0;
|
1655
|
+
var cpp = new svg.Point(s * rx * currp.y / ry, s * -ry * currp.x / rx);
|
1656
|
+
// cx, cy
|
1657
|
+
var centp = new svg.Point(
|
1658
|
+
(curr.x + cp.x) / 2.0 + Math.cos(xAxisRotation) * cpp.x - Math.sin(xAxisRotation) * cpp.y,
|
1659
|
+
(curr.y + cp.y) / 2.0 + Math.sin(xAxisRotation) * cpp.x + Math.cos(xAxisRotation) * cpp.y
|
1660
|
+
);
|
1661
|
+
// vector magnitude
|
1662
|
+
var m = function(v) { return Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2)); }
|
1663
|
+
// ratio between two vectors
|
1664
|
+
var r = function(u, v) { return (u[0]*v[0]+u[1]*v[1]) / (m(u)*m(v)) }
|
1665
|
+
// angle between two vectors
|
1666
|
+
var a = function(u, v) { return (u[0]*v[1] < u[1]*v[0] ? -1 : 1) * Math.acos(r(u,v)); }
|
1667
|
+
// initial angle
|
1668
|
+
var a1 = a([1,0], [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry]);
|
1669
|
+
// angle delta
|
1670
|
+
var u = [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry];
|
1671
|
+
var v = [(-currp.x-cpp.x)/rx,(-currp.y-cpp.y)/ry];
|
1672
|
+
var ad = a(u, v);
|
1673
|
+
if (r(u,v) <= -1) ad = Math.PI;
|
1674
|
+
if (r(u,v) >= 1) ad = 0;
|
1675
|
+
|
1676
|
+
// for markers
|
1677
|
+
var dir = 1 - sweepFlag ? 1.0 : -1.0;
|
1678
|
+
var ah = a1 + dir * (ad / 2.0);
|
1679
|
+
var halfWay = new svg.Point(
|
1680
|
+
centp.x + rx * Math.cos(ah),
|
1681
|
+
centp.y + ry * Math.sin(ah)
|
1682
|
+
);
|
1683
|
+
pp.addMarkerAngle(halfWay, ah - dir * Math.PI / 2);
|
1684
|
+
pp.addMarkerAngle(cp, ah - dir * Math.PI);
|
1685
|
+
|
1686
|
+
bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better
|
1687
|
+
if (ctx != null) {
|
1688
|
+
var r = rx > ry ? rx : ry;
|
1689
|
+
var sx = rx > ry ? 1 : rx / ry;
|
1690
|
+
var sy = rx > ry ? ry / rx : 1;
|
1691
|
+
|
1692
|
+
ctx.translate(centp.x, centp.y);
|
1693
|
+
ctx.rotate(xAxisRotation);
|
1694
|
+
ctx.scale(sx, sy);
|
1695
|
+
ctx.arc(0, 0, r, a1, a1 + ad, 1 - sweepFlag);
|
1696
|
+
ctx.scale(1/sx, 1/sy);
|
1697
|
+
ctx.rotate(-xAxisRotation);
|
1698
|
+
ctx.translate(-centp.x, -centp.y);
|
1699
|
+
}
|
1700
|
+
}
|
1701
|
+
break;
|
1702
|
+
case 'Z':
|
1703
|
+
case 'z':
|
1704
|
+
if (ctx != null) ctx.closePath();
|
1705
|
+
pp.current = pp.start;
|
1706
|
+
}
|
1707
|
+
}
|
1708
|
+
|
1709
|
+
return bb;
|
1710
|
+
}
|
1711
|
+
|
1712
|
+
this.getMarkers = function() {
|
1713
|
+
var points = this.PathParser.getMarkerPoints();
|
1714
|
+
var angles = this.PathParser.getMarkerAngles();
|
1715
|
+
|
1716
|
+
var markers = [];
|
1717
|
+
for (var i=0; i<points.length; i++) {
|
1718
|
+
markers.push([points[i], angles[i]]);
|
1719
|
+
}
|
1720
|
+
return markers;
|
1721
|
+
}
|
1722
|
+
}
|
1723
|
+
svg.Element.path.prototype = new svg.Element.PathElementBase;
|
1724
|
+
|
1725
|
+
// pattern element
|
1726
|
+
svg.Element.pattern = function(node) {
|
1727
|
+
this.base = svg.Element.ElementBase;
|
1728
|
+
this.base(node);
|
1729
|
+
|
1730
|
+
this.createPattern = function(ctx, element) {
|
1731
|
+
var width = this.attribute('width').toPixels('x', true);
|
1732
|
+
var height = this.attribute('height').toPixels('y', true);
|
1733
|
+
|
1734
|
+
// render me using a temporary svg element
|
1735
|
+
var tempSvg = new svg.Element.svg();
|
1736
|
+
tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
|
1737
|
+
tempSvg.attributes['width'] = new svg.Property('width', width + 'px');
|
1738
|
+
tempSvg.attributes['height'] = new svg.Property('height', height + 'px');
|
1739
|
+
tempSvg.attributes['transform'] = new svg.Property('transform', this.attribute('patternTransform').value);
|
1740
|
+
tempSvg.children = this.children;
|
1741
|
+
|
1742
|
+
var c = createCanvas();
|
1743
|
+
c.width = width;
|
1744
|
+
c.height = height;
|
1745
|
+
var cctx = c.getContext('2d');
|
1746
|
+
if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) {
|
1747
|
+
cctx.translate(this.attribute('x').toPixels('x', true), this.attribute('y').toPixels('y', true));
|
1748
|
+
}
|
1749
|
+
// render 3x3 grid so when we transform there's no white space on edges
|
1750
|
+
for (var x=-1; x<=1; x++) {
|
1751
|
+
for (var y=-1; y<=1; y++) {
|
1752
|
+
cctx.save();
|
1753
|
+
tempSvg.attributes['x'] = new svg.Property('x', x * c.width);
|
1754
|
+
tempSvg.attributes['y'] = new svg.Property('y', y * c.height);
|
1755
|
+
tempSvg.render(cctx);
|
1756
|
+
cctx.restore();
|
1757
|
+
}
|
1758
|
+
}
|
1759
|
+
var pattern = ctx.createPattern(c, 'repeat');
|
1760
|
+
return pattern;
|
1761
|
+
}
|
1762
|
+
}
|
1763
|
+
svg.Element.pattern.prototype = new svg.Element.ElementBase;
|
1764
|
+
|
1765
|
+
// marker element
|
1766
|
+
svg.Element.marker = function(node) {
|
1767
|
+
this.base = svg.Element.ElementBase;
|
1768
|
+
this.base(node);
|
1769
|
+
|
1770
|
+
this.baseRender = this.render;
|
1771
|
+
this.render = function(ctx, point, angle) {
|
1772
|
+
ctx.translate(point.x, point.y);
|
1773
|
+
if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(angle);
|
1774
|
+
if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(ctx.lineWidth, ctx.lineWidth);
|
1775
|
+
ctx.save();
|
1776
|
+
|
1777
|
+
// render me using a temporary svg element
|
1778
|
+
var tempSvg = new svg.Element.svg();
|
1779
|
+
tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
|
1780
|
+
tempSvg.attributes['refX'] = new svg.Property('refX', this.attribute('refX').value);
|
1781
|
+
tempSvg.attributes['refY'] = new svg.Property('refY', this.attribute('refY').value);
|
1782
|
+
tempSvg.attributes['width'] = new svg.Property('width', this.attribute('markerWidth').value);
|
1783
|
+
tempSvg.attributes['height'] = new svg.Property('height', this.attribute('markerHeight').value);
|
1784
|
+
tempSvg.attributes['fill'] = new svg.Property('fill', this.attribute('fill').valueOrDefault('black'));
|
1785
|
+
tempSvg.attributes['stroke'] = new svg.Property('stroke', this.attribute('stroke').valueOrDefault('none'));
|
1786
|
+
tempSvg.children = this.children;
|
1787
|
+
tempSvg.render(ctx);
|
1788
|
+
|
1789
|
+
ctx.restore();
|
1790
|
+
if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(1/ctx.lineWidth, 1/ctx.lineWidth);
|
1791
|
+
if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(-angle);
|
1792
|
+
ctx.translate(-point.x, -point.y);
|
1793
|
+
}
|
1794
|
+
}
|
1795
|
+
svg.Element.marker.prototype = new svg.Element.ElementBase;
|
1796
|
+
|
1797
|
+
// definitions element
|
1798
|
+
svg.Element.defs = function(node) {
|
1799
|
+
this.base = svg.Element.ElementBase;
|
1800
|
+
this.base(node);
|
1801
|
+
|
1802
|
+
this.render = function(ctx) {
|
1803
|
+
// NOOP
|
1804
|
+
}
|
1805
|
+
}
|
1806
|
+
svg.Element.defs.prototype = new svg.Element.ElementBase;
|
1807
|
+
|
1808
|
+
// base for gradients
|
1809
|
+
svg.Element.GradientBase = function(node) {
|
1810
|
+
this.base = svg.Element.ElementBase;
|
1811
|
+
this.base(node);
|
1812
|
+
|
1813
|
+
this.stops = [];
|
1814
|
+
for (var i=0; i<this.children.length; i++) {
|
1815
|
+
var child = this.children[i];
|
1816
|
+
if (child.type == 'stop') this.stops.push(child);
|
1817
|
+
}
|
1818
|
+
|
1819
|
+
this.getGradient = function() {
|
1820
|
+
// OVERRIDE ME!
|
1821
|
+
}
|
1822
|
+
|
1823
|
+
this.gradientUnits = function () {
|
1824
|
+
return this.attribute('gradientUnits').valueOrDefault('objectBoundingBox');
|
1825
|
+
}
|
1826
|
+
|
1827
|
+
this.attributesToInherit = ['gradientUnits'];
|
1828
|
+
|
1829
|
+
this.inheritStopContainer = function (stopsContainer) {
|
1830
|
+
for (var i=0; i<this.attributesToInherit.length; i++) {
|
1831
|
+
var attributeToInherit = this.attributesToInherit[i];
|
1832
|
+
if (!this.attribute(attributeToInherit).hasValue() && stopsContainer.attribute(attributeToInherit).hasValue()) {
|
1833
|
+
this.attribute(attributeToInherit, true).value = stopsContainer.attribute(attributeToInherit).value;
|
1834
|
+
}
|
1835
|
+
}
|
1836
|
+
}
|
1837
|
+
|
1838
|
+
this.createGradient = function(ctx, element, parentOpacityProp) {
|
1839
|
+
var stopsContainer = this;
|
1840
|
+
if (this.getHrefAttribute().hasValue()) {
|
1841
|
+
stopsContainer = this.getHrefAttribute().getDefinition();
|
1842
|
+
this.inheritStopContainer(stopsContainer);
|
1843
|
+
}
|
1844
|
+
|
1845
|
+
var addParentOpacity = function (color) {
|
1846
|
+
if (parentOpacityProp.hasValue()) {
|
1847
|
+
var p = new svg.Property('color', color);
|
1848
|
+
return p.addOpacity(parentOpacityProp).value;
|
1849
|
+
}
|
1850
|
+
return color;
|
1851
|
+
};
|
1852
|
+
|
1853
|
+
var g = this.getGradient(ctx, element);
|
1854
|
+
if (g == null) return addParentOpacity(stopsContainer.stops[stopsContainer.stops.length - 1].color);
|
1855
|
+
for (var i=0; i<stopsContainer.stops.length; i++) {
|
1856
|
+
g.addColorStop(stopsContainer.stops[i].offset, addParentOpacity(stopsContainer.stops[i].color));
|
1857
|
+
}
|
1858
|
+
|
1859
|
+
if (this.attribute('gradientTransform').hasValue()) {
|
1860
|
+
// render as transformed pattern on temporary canvas
|
1861
|
+
var rootView = svg.ViewPort.viewPorts[0];
|
1862
|
+
|
1863
|
+
var rect = new svg.Element.rect();
|
1864
|
+
rect.attributes['x'] = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS/3.0);
|
1865
|
+
rect.attributes['y'] = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS/3.0);
|
1866
|
+
rect.attributes['width'] = new svg.Property('width', svg.MAX_VIRTUAL_PIXELS);
|
1867
|
+
rect.attributes['height'] = new svg.Property('height', svg.MAX_VIRTUAL_PIXELS);
|
1868
|
+
|
1869
|
+
var group = new svg.Element.g();
|
1870
|
+
group.attributes['transform'] = new svg.Property('transform', this.attribute('gradientTransform').value);
|
1871
|
+
group.children = [ rect ];
|
1872
|
+
|
1873
|
+
var tempSvg = new svg.Element.svg();
|
1874
|
+
tempSvg.attributes['x'] = new svg.Property('x', 0);
|
1875
|
+
tempSvg.attributes['y'] = new svg.Property('y', 0);
|
1876
|
+
tempSvg.attributes['width'] = new svg.Property('width', rootView.width);
|
1877
|
+
tempSvg.attributes['height'] = new svg.Property('height', rootView.height);
|
1878
|
+
tempSvg.children = [ group ];
|
1879
|
+
var c = createCanvas();
|
1880
|
+
c.width = rootView.width;
|
1881
|
+
c.height = rootView.height;
|
1882
|
+
var tempCtx = c.getContext('2d');
|
1883
|
+
tempCtx.fillStyle = g;
|
1884
|
+
tempSvg.render(tempCtx);
|
1885
|
+
return tempCtx.createPattern(c, 'no-repeat');
|
1886
|
+
}
|
1887
|
+
|
1888
|
+
return g;
|
1889
|
+
}
|
1890
|
+
}
|
1891
|
+
svg.Element.GradientBase.prototype = new svg.Element.ElementBase;
|
1892
|
+
|
1893
|
+
// linear gradient element
|
1894
|
+
svg.Element.linearGradient = function(node) {
|
1895
|
+
this.base = svg.Element.GradientBase;
|
1896
|
+
this.base(node);
|
1897
|
+
|
1898
|
+
this.attributesToInherit.push('x1');
|
1899
|
+
this.attributesToInherit.push('y1');
|
1900
|
+
this.attributesToInherit.push('x2');
|
1901
|
+
this.attributesToInherit.push('y2');
|
1902
|
+
|
1903
|
+
this.getGradient = function(ctx, element) {
|
1904
|
+
var bb = this.gradientUnits() == 'objectBoundingBox' ? element.getBoundingBox() : null;
|
1905
|
+
|
1906
|
+
if (!this.attribute('x1').hasValue()
|
1907
|
+
&& !this.attribute('y1').hasValue()
|
1908
|
+
&& !this.attribute('x2').hasValue()
|
1909
|
+
&& !this.attribute('y2').hasValue()) {
|
1910
|
+
this.attribute('x1', true).value = 0;
|
1911
|
+
this.attribute('y1', true).value = 0;
|
1912
|
+
this.attribute('x2', true).value = 1;
|
1913
|
+
this.attribute('y2', true).value = 0;
|
1914
|
+
}
|
1915
|
+
|
1916
|
+
var x1 = (this.gradientUnits() == 'objectBoundingBox'
|
1917
|
+
? bb.x() + bb.width() * this.attribute('x1').numValue()
|
1918
|
+
: this.attribute('x1').toPixels('x'));
|
1919
|
+
var y1 = (this.gradientUnits() == 'objectBoundingBox'
|
1920
|
+
? bb.y() + bb.height() * this.attribute('y1').numValue()
|
1921
|
+
: this.attribute('y1').toPixels('y'));
|
1922
|
+
var x2 = (this.gradientUnits() == 'objectBoundingBox'
|
1923
|
+
? bb.x() + bb.width() * this.attribute('x2').numValue()
|
1924
|
+
: this.attribute('x2').toPixels('x'));
|
1925
|
+
var y2 = (this.gradientUnits() == 'objectBoundingBox'
|
1926
|
+
? bb.y() + bb.height() * this.attribute('y2').numValue()
|
1927
|
+
: this.attribute('y2').toPixels('y'));
|
1928
|
+
|
1929
|
+
if (x1 == x2 && y1 == y2) return null;
|
1930
|
+
return ctx.createLinearGradient(x1, y1, x2, y2);
|
1931
|
+
}
|
1932
|
+
}
|
1933
|
+
svg.Element.linearGradient.prototype = new svg.Element.GradientBase;
|
1934
|
+
|
1935
|
+
// radial gradient element
|
1936
|
+
svg.Element.radialGradient = function(node) {
|
1937
|
+
this.base = svg.Element.GradientBase;
|
1938
|
+
this.base(node);
|
1939
|
+
|
1940
|
+
this.attributesToInherit.push('cx');
|
1941
|
+
this.attributesToInherit.push('cy');
|
1942
|
+
this.attributesToInherit.push('r');
|
1943
|
+
this.attributesToInherit.push('fx');
|
1944
|
+
this.attributesToInherit.push('fy');
|
1945
|
+
|
1946
|
+
this.getGradient = function(ctx, element) {
|
1947
|
+
var bb = element.getBoundingBox();
|
1948
|
+
|
1949
|
+
if (!this.attribute('cx').hasValue()) this.attribute('cx', true).value = '50%';
|
1950
|
+
if (!this.attribute('cy').hasValue()) this.attribute('cy', true).value = '50%';
|
1951
|
+
if (!this.attribute('r').hasValue()) this.attribute('r', true).value = '50%';
|
1952
|
+
|
1953
|
+
var cx = (this.gradientUnits() == 'objectBoundingBox'
|
1954
|
+
? bb.x() + bb.width() * this.attribute('cx').numValue()
|
1955
|
+
: this.attribute('cx').toPixels('x'));
|
1956
|
+
var cy = (this.gradientUnits() == 'objectBoundingBox'
|
1957
|
+
? bb.y() + bb.height() * this.attribute('cy').numValue()
|
1958
|
+
: this.attribute('cy').toPixels('y'));
|
1959
|
+
|
1960
|
+
var fx = cx;
|
1961
|
+
var fy = cy;
|
1962
|
+
if (this.attribute('fx').hasValue()) {
|
1963
|
+
fx = (this.gradientUnits() == 'objectBoundingBox'
|
1964
|
+
? bb.x() + bb.width() * this.attribute('fx').numValue()
|
1965
|
+
: this.attribute('fx').toPixels('x'));
|
1966
|
+
}
|
1967
|
+
if (this.attribute('fy').hasValue()) {
|
1968
|
+
fy = (this.gradientUnits() == 'objectBoundingBox'
|
1969
|
+
? bb.y() + bb.height() * this.attribute('fy').numValue()
|
1970
|
+
: this.attribute('fy').toPixels('y'));
|
1971
|
+
}
|
1972
|
+
|
1973
|
+
var r = (this.gradientUnits() == 'objectBoundingBox'
|
1974
|
+
? (bb.width() + bb.height()) / 2.0 * this.attribute('r').numValue()
|
1975
|
+
: this.attribute('r').toPixels());
|
1976
|
+
|
1977
|
+
return ctx.createRadialGradient(fx, fy, 0, cx, cy, r);
|
1978
|
+
}
|
1979
|
+
}
|
1980
|
+
svg.Element.radialGradient.prototype = new svg.Element.GradientBase;
|
1981
|
+
|
1982
|
+
// gradient stop element
|
1983
|
+
svg.Element.stop = function(node) {
|
1984
|
+
this.base = svg.Element.ElementBase;
|
1985
|
+
this.base(node);
|
1986
|
+
|
1987
|
+
this.offset = this.attribute('offset').numValue();
|
1988
|
+
if (this.offset < 0) this.offset = 0;
|
1989
|
+
if (this.offset > 1) this.offset = 1;
|
1990
|
+
|
1991
|
+
var stopColor = this.style('stop-color', true);
|
1992
|
+
if (stopColor.value === '') stopColor.value = '#000';
|
1993
|
+
if (this.style('stop-opacity').hasValue()) stopColor = stopColor.addOpacity(this.style('stop-opacity'));
|
1994
|
+
this.color = stopColor.value;
|
1995
|
+
}
|
1996
|
+
svg.Element.stop.prototype = new svg.Element.ElementBase;
|
1997
|
+
|
1998
|
+
// animation base element
|
1999
|
+
svg.Element.AnimateBase = function(node) {
|
2000
|
+
this.base = svg.Element.ElementBase;
|
2001
|
+
this.base(node);
|
2002
|
+
|
2003
|
+
svg.Animations.push(this);
|
2004
|
+
|
2005
|
+
this.duration = 0.0;
|
2006
|
+
this.begin = this.attribute('begin').toMilliseconds();
|
2007
|
+
this.maxDuration = this.begin + this.attribute('dur').toMilliseconds();
|
2008
|
+
|
2009
|
+
this.getProperty = function() {
|
2010
|
+
var attributeType = this.attribute('attributeType').value;
|
2011
|
+
var attributeName = this.attribute('attributeName').value;
|
2012
|
+
|
2013
|
+
if (attributeType == 'CSS') {
|
2014
|
+
return this.parent.style(attributeName, true);
|
2015
|
+
}
|
2016
|
+
return this.parent.attribute(attributeName, true);
|
2017
|
+
};
|
2018
|
+
|
2019
|
+
this.initialValue = null;
|
2020
|
+
this.initialUnits = '';
|
2021
|
+
this.removed = false;
|
2022
|
+
|
2023
|
+
this.calcValue = function() {
|
2024
|
+
// OVERRIDE ME!
|
2025
|
+
return '';
|
2026
|
+
}
|
2027
|
+
|
2028
|
+
this.update = function(delta) {
|
2029
|
+
// set initial value
|
2030
|
+
if (this.initialValue == null) {
|
2031
|
+
this.initialValue = this.getProperty().value;
|
2032
|
+
this.initialUnits = this.getProperty().getUnits();
|
2033
|
+
}
|
2034
|
+
|
2035
|
+
// if we're past the end time
|
2036
|
+
if (this.duration > this.maxDuration) {
|
2037
|
+
// loop for indefinitely repeating animations
|
2038
|
+
if (this.attribute('repeatCount').value == 'indefinite'
|
2039
|
+
|| this.attribute('repeatDur').value == 'indefinite') {
|
2040
|
+
this.duration = 0.0
|
2041
|
+
}
|
2042
|
+
else if (this.attribute('fill').valueOrDefault('remove') == 'freeze' && !this.frozen) {
|
2043
|
+
this.frozen = true;
|
2044
|
+
this.parent.animationFrozen = true;
|
2045
|
+
this.parent.animationFrozenValue = this.getProperty().value;
|
2046
|
+
}
|
2047
|
+
else if (this.attribute('fill').valueOrDefault('remove') == 'remove' && !this.removed) {
|
2048
|
+
this.removed = true;
|
2049
|
+
this.getProperty().value = this.parent.animationFrozen ? this.parent.animationFrozenValue : this.initialValue;
|
2050
|
+
return true;
|
2051
|
+
}
|
2052
|
+
return false;
|
2053
|
+
}
|
2054
|
+
this.duration = this.duration + delta;
|
2055
|
+
|
2056
|
+
// if we're past the begin time
|
2057
|
+
var updated = false;
|
2058
|
+
if (this.begin < this.duration) {
|
2059
|
+
var newValue = this.calcValue(); // tween
|
2060
|
+
|
2061
|
+
if (this.attribute('type').hasValue()) {
|
2062
|
+
// for transform, etc.
|
2063
|
+
var type = this.attribute('type').value;
|
2064
|
+
newValue = type + '(' + newValue + ')';
|
2065
|
+
}
|
2066
|
+
|
2067
|
+
this.getProperty().value = newValue;
|
2068
|
+
updated = true;
|
2069
|
+
}
|
2070
|
+
|
2071
|
+
return updated;
|
2072
|
+
}
|
2073
|
+
|
2074
|
+
this.from = this.attribute('from');
|
2075
|
+
this.to = this.attribute('to');
|
2076
|
+
this.values = this.attribute('values');
|
2077
|
+
if (this.values.hasValue()) this.values.value = this.values.value.split(';');
|
2078
|
+
|
2079
|
+
// fraction of duration we've covered
|
2080
|
+
this.progress = function() {
|
2081
|
+
var ret = { progress: (this.duration - this.begin) / (this.maxDuration - this.begin) };
|
2082
|
+
if (this.values.hasValue()) {
|
2083
|
+
var p = ret.progress * (this.values.value.length - 1);
|
2084
|
+
var lb = Math.floor(p), ub = Math.ceil(p);
|
2085
|
+
ret.from = new svg.Property('from', parseFloat(this.values.value[lb]));
|
2086
|
+
ret.to = new svg.Property('to', parseFloat(this.values.value[ub]));
|
2087
|
+
ret.progress = (p - lb) / (ub - lb);
|
2088
|
+
}
|
2089
|
+
else {
|
2090
|
+
ret.from = this.from;
|
2091
|
+
ret.to = this.to;
|
2092
|
+
}
|
2093
|
+
return ret;
|
2094
|
+
}
|
2095
|
+
}
|
2096
|
+
svg.Element.AnimateBase.prototype = new svg.Element.ElementBase;
|
2097
|
+
|
2098
|
+
// animate element
|
2099
|
+
svg.Element.animate = function(node) {
|
2100
|
+
this.base = svg.Element.AnimateBase;
|
2101
|
+
this.base(node);
|
2102
|
+
|
2103
|
+
this.calcValue = function() {
|
2104
|
+
var p = this.progress();
|
2105
|
+
|
2106
|
+
// tween value linearly
|
2107
|
+
var newValue = p.from.numValue() + (p.to.numValue() - p.from.numValue()) * p.progress;
|
2108
|
+
return newValue + this.initialUnits;
|
2109
|
+
};
|
2110
|
+
}
|
2111
|
+
svg.Element.animate.prototype = new svg.Element.AnimateBase;
|
2112
|
+
|
2113
|
+
// animate color element
|
2114
|
+
svg.Element.animateColor = function(node) {
|
2115
|
+
this.base = svg.Element.AnimateBase;
|
2116
|
+
this.base(node);
|
2117
|
+
|
2118
|
+
this.calcValue = function() {
|
2119
|
+
var p = this.progress();
|
2120
|
+
var from = new RGBColor(p.from.value);
|
2121
|
+
var to = new RGBColor(p.to.value);
|
2122
|
+
|
2123
|
+
if (from.ok && to.ok) {
|
2124
|
+
// tween color linearly
|
2125
|
+
var r = from.r + (to.r - from.r) * p.progress;
|
2126
|
+
var g = from.g + (to.g - from.g) * p.progress;
|
2127
|
+
var b = from.b + (to.b - from.b) * p.progress;
|
2128
|
+
return 'rgb('+parseInt(r,10)+','+parseInt(g,10)+','+parseInt(b,10)+')';
|
2129
|
+
}
|
2130
|
+
return this.attribute('from').value;
|
2131
|
+
};
|
2132
|
+
}
|
2133
|
+
svg.Element.animateColor.prototype = new svg.Element.AnimateBase;
|
2134
|
+
|
2135
|
+
// animate transform element
|
2136
|
+
svg.Element.animateTransform = function(node) {
|
2137
|
+
this.base = svg.Element.AnimateBase;
|
2138
|
+
this.base(node);
|
2139
|
+
|
2140
|
+
this.calcValue = function() {
|
2141
|
+
var p = this.progress();
|
2142
|
+
|
2143
|
+
// tween value linearly
|
2144
|
+
var from = svg.ToNumberArray(p.from.value);
|
2145
|
+
var to = svg.ToNumberArray(p.to.value);
|
2146
|
+
var newValue = '';
|
2147
|
+
for (var i=0; i<from.length; i++) {
|
2148
|
+
newValue += from[i] + (to[i] - from[i]) * p.progress + ' ';
|
2149
|
+
}
|
2150
|
+
return newValue;
|
2151
|
+
};
|
2152
|
+
}
|
2153
|
+
svg.Element.animateTransform.prototype = new svg.Element.animate;
|
2154
|
+
|
2155
|
+
// font element
|
2156
|
+
svg.Element.font = function(node) {
|
2157
|
+
this.base = svg.Element.ElementBase;
|
2158
|
+
this.base(node);
|
2159
|
+
|
2160
|
+
this.horizAdvX = this.attribute('horiz-adv-x').numValue();
|
2161
|
+
|
2162
|
+
this.isRTL = false;
|
2163
|
+
this.isArabic = false;
|
2164
|
+
this.fontFace = null;
|
2165
|
+
this.missingGlyph = null;
|
2166
|
+
this.glyphs = [];
|
2167
|
+
for (var i=0; i<this.children.length; i++) {
|
2168
|
+
var child = this.children[i];
|
2169
|
+
if (child.type == 'font-face') {
|
2170
|
+
this.fontFace = child;
|
2171
|
+
if (child.style('font-family').hasValue()) {
|
2172
|
+
svg.Definitions[child.style('font-family').value] = this;
|
2173
|
+
}
|
2174
|
+
}
|
2175
|
+
else if (child.type == 'missing-glyph') this.missingGlyph = child;
|
2176
|
+
else if (child.type == 'glyph') {
|
2177
|
+
if (child.arabicForm != '') {
|
2178
|
+
this.isRTL = true;
|
2179
|
+
this.isArabic = true;
|
2180
|
+
if (typeof this.glyphs[child.unicode] == 'undefined') this.glyphs[child.unicode] = [];
|
2181
|
+
this.glyphs[child.unicode][child.arabicForm] = child;
|
2182
|
+
}
|
2183
|
+
else {
|
2184
|
+
this.glyphs[child.unicode] = child;
|
2185
|
+
}
|
2186
|
+
}
|
2187
|
+
}
|
2188
|
+
}
|
2189
|
+
svg.Element.font.prototype = new svg.Element.ElementBase;
|
2190
|
+
|
2191
|
+
// font-face element
|
2192
|
+
svg.Element.fontface = function(node) {
|
2193
|
+
this.base = svg.Element.ElementBase;
|
2194
|
+
this.base(node);
|
2195
|
+
|
2196
|
+
this.ascent = this.attribute('ascent').value;
|
2197
|
+
this.descent = this.attribute('descent').value;
|
2198
|
+
this.unitsPerEm = this.attribute('units-per-em').numValue();
|
2199
|
+
}
|
2200
|
+
svg.Element.fontface.prototype = new svg.Element.ElementBase;
|
2201
|
+
|
2202
|
+
// missing-glyph element
|
2203
|
+
svg.Element.missingglyph = function(node) {
|
2204
|
+
this.base = svg.Element.path;
|
2205
|
+
this.base(node);
|
2206
|
+
|
2207
|
+
this.horizAdvX = 0;
|
2208
|
+
}
|
2209
|
+
svg.Element.missingglyph.prototype = new svg.Element.path;
|
2210
|
+
|
2211
|
+
// glyph element
|
2212
|
+
svg.Element.glyph = function(node) {
|
2213
|
+
this.base = svg.Element.path;
|
2214
|
+
this.base(node);
|
2215
|
+
|
2216
|
+
this.horizAdvX = this.attribute('horiz-adv-x').numValue();
|
2217
|
+
this.unicode = this.attribute('unicode').value;
|
2218
|
+
this.arabicForm = this.attribute('arabic-form').value;
|
2219
|
+
}
|
2220
|
+
svg.Element.glyph.prototype = new svg.Element.path;
|
2221
|
+
|
2222
|
+
// text element
|
2223
|
+
svg.Element.text = function(node) {
|
2224
|
+
this.captureTextNodes = true;
|
2225
|
+
this.base = svg.Element.RenderedElementBase;
|
2226
|
+
this.base(node);
|
2227
|
+
|
2228
|
+
this.baseSetContext = this.setContext;
|
2229
|
+
this.setContext = function(ctx) {
|
2230
|
+
this.baseSetContext(ctx);
|
2231
|
+
|
2232
|
+
var textBaseline = this.style('dominant-baseline').toTextBaseline();
|
2233
|
+
if (textBaseline == null) textBaseline = this.style('alignment-baseline').toTextBaseline();
|
2234
|
+
if (textBaseline != null) ctx.textBaseline = textBaseline;
|
2235
|
+
}
|
2236
|
+
|
2237
|
+
this.getBoundingBox = function () {
|
2238
|
+
var x = this.attribute('x').toPixels('x');
|
2239
|
+
var y = this.attribute('y').toPixels('y');
|
2240
|
+
var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
|
2241
|
+
return new svg.BoundingBox(x, y - fontSize, x + Math.floor(fontSize * 2.0 / 3.0) * this.children[0].getText().length, y);
|
2242
|
+
}
|
2243
|
+
|
2244
|
+
this.renderChildren = function(ctx) {
|
2245
|
+
this.x = this.attribute('x').toPixels('x');
|
2246
|
+
this.y = this.attribute('y').toPixels('y');
|
2247
|
+
if (this.attribute('dx').hasValue()) this.x += this.attribute('dx').toPixels('x');
|
2248
|
+
if (this.attribute('dy').hasValue()) this.y += this.attribute('dy').toPixels('y');
|
2249
|
+
this.x += this.getAnchorDelta(ctx, this, 0);
|
2250
|
+
for (var i=0; i<this.children.length; i++) {
|
2251
|
+
this.renderChild(ctx, this, this, i);
|
2252
|
+
}
|
2253
|
+
}
|
2254
|
+
|
2255
|
+
this.getAnchorDelta = function (ctx, parent, startI) {
|
2256
|
+
var textAnchor = this.style('text-anchor').valueOrDefault('start');
|
2257
|
+
if (textAnchor != 'start') {
|
2258
|
+
var width = 0;
|
2259
|
+
for (var i=startI; i<parent.children.length; i++) {
|
2260
|
+
var child = parent.children[i];
|
2261
|
+
if (i > startI && child.attribute('x').hasValue()) break; // new group
|
2262
|
+
width += child.measureTextRecursive(ctx);
|
2263
|
+
}
|
2264
|
+
return -1 * (textAnchor == 'end' ? width : width / 2.0);
|
2265
|
+
}
|
2266
|
+
return 0;
|
2267
|
+
}
|
2268
|
+
|
2269
|
+
this.renderChild = function(ctx, textParent, parent, i) {
|
2270
|
+
var child = parent.children[i];
|
2271
|
+
if (child.attribute('x').hasValue()) {
|
2272
|
+
child.x = child.attribute('x').toPixels('x') + textParent.getAnchorDelta(ctx, parent, i);
|
2273
|
+
if (child.attribute('dx').hasValue()) child.x += child.attribute('dx').toPixels('x');
|
2274
|
+
}
|
2275
|
+
else {
|
2276
|
+
if (child.attribute('dx').hasValue()) textParent.x += child.attribute('dx').toPixels('x');
|
2277
|
+
child.x = textParent.x;
|
2278
|
+
}
|
2279
|
+
textParent.x = child.x + child.measureText(ctx);
|
2280
|
+
|
2281
|
+
if (child.attribute('y').hasValue()) {
|
2282
|
+
child.y = child.attribute('y').toPixels('y');
|
2283
|
+
if (child.attribute('dy').hasValue()) child.y += child.attribute('dy').toPixels('y');
|
2284
|
+
}
|
2285
|
+
else {
|
2286
|
+
if (child.attribute('dy').hasValue()) textParent.y += child.attribute('dy').toPixels('y');
|
2287
|
+
child.y = textParent.y;
|
2288
|
+
}
|
2289
|
+
textParent.y = child.y;
|
2290
|
+
|
2291
|
+
child.render(ctx);
|
2292
|
+
|
2293
|
+
for (var i=0; i<child.children.length; i++) {
|
2294
|
+
textParent.renderChild(ctx, textParent, child, i);
|
2295
|
+
}
|
2296
|
+
}
|
2297
|
+
}
|
2298
|
+
svg.Element.text.prototype = new svg.Element.RenderedElementBase;
|
2299
|
+
|
2300
|
+
// text base
|
2301
|
+
svg.Element.TextElementBase = function(node) {
|
2302
|
+
this.base = svg.Element.RenderedElementBase;
|
2303
|
+
this.base(node);
|
2304
|
+
|
2305
|
+
this.getGlyph = function(font, text, i) {
|
2306
|
+
var c = text[i];
|
2307
|
+
var glyph = null;
|
2308
|
+
if (font.isArabic) {
|
2309
|
+
var arabicForm = 'isolated';
|
2310
|
+
if ((i==0 || text[i-1]==' ') && i<text.length-2 && text[i+1]!=' ') arabicForm = 'terminal';
|
2311
|
+
if (i>0 && text[i-1]!=' ' && i<text.length-2 && text[i+1]!=' ') arabicForm = 'medial';
|
2312
|
+
if (i>0 && text[i-1]!=' ' && (i == text.length-1 || text[i+1]==' ')) arabicForm = 'initial';
|
2313
|
+
if (typeof font.glyphs[c] != 'undefined') {
|
2314
|
+
glyph = font.glyphs[c][arabicForm];
|
2315
|
+
if (glyph == null && font.glyphs[c].type == 'glyph') glyph = font.glyphs[c];
|
2316
|
+
}
|
2317
|
+
}
|
2318
|
+
else {
|
2319
|
+
glyph = font.glyphs[c];
|
2320
|
+
}
|
2321
|
+
if (glyph == null) glyph = font.missingGlyph;
|
2322
|
+
return glyph;
|
2323
|
+
}
|
2324
|
+
|
2325
|
+
this.renderChildren = function(ctx) {
|
2326
|
+
var customFont = this.parent.style('font-family').getDefinition();
|
2327
|
+
if (customFont != null) {
|
2328
|
+
var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
|
2329
|
+
var fontStyle = this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle);
|
2330
|
+
var text = this.getText();
|
2331
|
+
if (customFont.isRTL) text = text.split("").reverse().join("");
|
2332
|
+
|
2333
|
+
var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
|
2334
|
+
for (var i=0; i<text.length; i++) {
|
2335
|
+
var glyph = this.getGlyph(customFont, text, i);
|
2336
|
+
var scale = fontSize / customFont.fontFace.unitsPerEm;
|
2337
|
+
ctx.translate(this.x, this.y);
|
2338
|
+
ctx.scale(scale, -scale);
|
2339
|
+
var lw = ctx.lineWidth;
|
2340
|
+
ctx.lineWidth = ctx.lineWidth * customFont.fontFace.unitsPerEm / fontSize;
|
2341
|
+
if (fontStyle == 'italic') ctx.transform(1, 0, .4, 1, 0, 0);
|
2342
|
+
glyph.render(ctx);
|
2343
|
+
if (fontStyle == 'italic') ctx.transform(1, 0, -.4, 1, 0, 0);
|
2344
|
+
ctx.lineWidth = lw;
|
2345
|
+
ctx.scale(1/scale, -1/scale);
|
2346
|
+
ctx.translate(-this.x, -this.y);
|
2347
|
+
|
2348
|
+
this.x += fontSize * (glyph.horizAdvX || customFont.horizAdvX) / customFont.fontFace.unitsPerEm;
|
2349
|
+
if (typeof dx[i] != 'undefined' && !isNaN(dx[i])) {
|
2350
|
+
this.x += dx[i];
|
2351
|
+
}
|
2352
|
+
}
|
2353
|
+
return;
|
2354
|
+
}
|
2355
|
+
if(ctx.paintOrder == "stroke") {
|
2356
|
+
if (ctx.strokeStyle != '') ctx.strokeText(svg.compressSpaces(this.getText()), this.x, this.y);
|
2357
|
+
if (ctx.fillStyle != '') ctx.fillText(svg.compressSpaces(this.getText()), this.x, this.y);
|
2358
|
+
} else {
|
2359
|
+
if (ctx.fillStyle != '') ctx.fillText(svg.compressSpaces(this.getText()), this.x, this.y);
|
2360
|
+
if (ctx.strokeStyle != '') ctx.strokeText(svg.compressSpaces(this.getText()), this.x, this.y);
|
2361
|
+
}
|
2362
|
+
}
|
2363
|
+
|
2364
|
+
this.getText = function() {
|
2365
|
+
// OVERRIDE ME
|
2366
|
+
}
|
2367
|
+
|
2368
|
+
this.measureTextRecursive = function(ctx) {
|
2369
|
+
var width = this.measureText(ctx);
|
2370
|
+
for (var i=0; i<this.children.length; i++) {
|
2371
|
+
width += this.children[i].measureTextRecursive(ctx);
|
2372
|
+
}
|
2373
|
+
return width;
|
2374
|
+
}
|
2375
|
+
|
2376
|
+
this.measureText = function(ctx) {
|
2377
|
+
var customFont = this.parent.style('font-family').getDefinition();
|
2378
|
+
if (customFont != null) {
|
2379
|
+
var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
|
2380
|
+
var measure = 0;
|
2381
|
+
var text = this.getText();
|
2382
|
+
if (customFont.isRTL) text = text.split("").reverse().join("");
|
2383
|
+
var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
|
2384
|
+
for (var i=0; i<text.length; i++) {
|
2385
|
+
var glyph = this.getGlyph(customFont, text, i);
|
2386
|
+
measure += (glyph.horizAdvX || customFont.horizAdvX) * fontSize / customFont.fontFace.unitsPerEm;
|
2387
|
+
if (typeof dx[i] != 'undefined' && !isNaN(dx[i])) {
|
2388
|
+
measure += dx[i];
|
2389
|
+
}
|
2390
|
+
}
|
2391
|
+
return measure;
|
2392
|
+
}
|
2393
|
+
|
2394
|
+
var textToMeasure = svg.compressSpaces(this.getText());
|
2395
|
+
if (!ctx.measureText) return textToMeasure.length * 10;
|
2396
|
+
|
2397
|
+
ctx.save();
|
2398
|
+
this.setContext(ctx);
|
2399
|
+
var width = ctx.measureText(textToMeasure).width;
|
2400
|
+
ctx.restore();
|
2401
|
+
return width;
|
2402
|
+
}
|
2403
|
+
}
|
2404
|
+
svg.Element.TextElementBase.prototype = new svg.Element.RenderedElementBase;
|
2405
|
+
|
2406
|
+
// tspan
|
2407
|
+
svg.Element.tspan = function(node) {
|
2408
|
+
this.captureTextNodes = true;
|
2409
|
+
this.base = svg.Element.TextElementBase;
|
2410
|
+
this.base(node);
|
2411
|
+
|
2412
|
+
this.text = svg.compressSpaces(node.value || node.text || node.textContent || '');
|
2413
|
+
this.getText = function() {
|
2414
|
+
// if this node has children, then they own the text
|
2415
|
+
if (this.children.length > 0) { return ''; }
|
2416
|
+
return this.text;
|
2417
|
+
}
|
2418
|
+
}
|
2419
|
+
svg.Element.tspan.prototype = new svg.Element.TextElementBase;
|
2420
|
+
|
2421
|
+
// tref
|
2422
|
+
svg.Element.tref = function(node) {
|
2423
|
+
this.base = svg.Element.TextElementBase;
|
2424
|
+
this.base(node);
|
2425
|
+
|
2426
|
+
this.getText = function() {
|
2427
|
+
var element = this.getHrefAttribute().getDefinition();
|
2428
|
+
if (element != null) return element.children[0].getText();
|
2429
|
+
}
|
2430
|
+
}
|
2431
|
+
svg.Element.tref.prototype = new svg.Element.TextElementBase;
|
2432
|
+
|
2433
|
+
// a element
|
2434
|
+
svg.Element.a = function(node) {
|
2435
|
+
this.base = svg.Element.TextElementBase;
|
2436
|
+
this.base(node);
|
2437
|
+
|
2438
|
+
this.hasText = node.childNodes.length > 0;
|
2439
|
+
for (var i=0; i<node.childNodes.length; i++) {
|
2440
|
+
if (node.childNodes[i].nodeType != 3) this.hasText = false;
|
2441
|
+
}
|
2442
|
+
|
2443
|
+
// this might contain text
|
2444
|
+
this.text = this.hasText ? node.childNodes[0].value || node.childNodes[0].data : '';
|
2445
|
+
this.getText = function() {
|
2446
|
+
return this.text;
|
2447
|
+
}
|
2448
|
+
|
2449
|
+
this.baseRenderChildren = this.renderChildren;
|
2450
|
+
this.renderChildren = function(ctx) {
|
2451
|
+
if (this.hasText) {
|
2452
|
+
// render as text element
|
2453
|
+
this.baseRenderChildren(ctx);
|
2454
|
+
var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
|
2455
|
+
svg.Mouse.checkBoundingBox(this, new svg.BoundingBox(this.x, this.y - fontSize.toPixels('y'), this.x + this.measureText(ctx), this.y));
|
2456
|
+
}
|
2457
|
+
else if (this.children.length > 0) {
|
2458
|
+
// render as temporary group
|
2459
|
+
var g = new svg.Element.g();
|
2460
|
+
g.children = this.children;
|
2461
|
+
g.parent = this;
|
2462
|
+
g.render(ctx);
|
2463
|
+
}
|
2464
|
+
}
|
2465
|
+
|
2466
|
+
this.onclick = function() {
|
2467
|
+
windowEnv.open(this.getHrefAttribute().value);
|
2468
|
+
}
|
2469
|
+
|
2470
|
+
this.onmousemove = function() {
|
2471
|
+
svg.ctx.canvas.style.cursor = 'pointer';
|
2472
|
+
}
|
2473
|
+
}
|
2474
|
+
svg.Element.a.prototype = new svg.Element.TextElementBase;
|
2475
|
+
|
2476
|
+
// image element
|
2477
|
+
svg.Element.image = function(node) {
|
2478
|
+
this.base = svg.Element.RenderedElementBase;
|
2479
|
+
this.base(node);
|
2480
|
+
|
2481
|
+
var href = this.getHrefAttribute().value;
|
2482
|
+
if (href == '') { return; }
|
2483
|
+
var isSvg = href.match(/\.svg$/)
|
2484
|
+
|
2485
|
+
svg.Images.push(this);
|
2486
|
+
this.loaded = false;
|
2487
|
+
if (!isSvg) {
|
2488
|
+
this.img = nodeEnv ? new ImageClass() : document.createElement('img');
|
2489
|
+
if (svg.opts['useCORS'] == true) { this.img.crossOrigin = 'Anonymous'; }
|
2490
|
+
var self = this;
|
2491
|
+
this.img.onload = function() { self.loaded = true; }
|
2492
|
+
this.img.onerror = function() { svg.log('ERROR: image "' + href + '" not found'); self.loaded = true; }
|
2493
|
+
this.img.src = href;
|
2494
|
+
}
|
2495
|
+
else {
|
2496
|
+
this.img = svg.ajax(href);
|
2497
|
+
this.loaded = true;
|
2498
|
+
}
|
2499
|
+
|
2500
|
+
this.renderChildren = function(ctx) {
|
2501
|
+
var x = this.attribute('x').toPixels('x');
|
2502
|
+
var y = this.attribute('y').toPixels('y');
|
2503
|
+
|
2504
|
+
var width = this.attribute('width').toPixels('x');
|
2505
|
+
var height = this.attribute('height').toPixels('y');
|
2506
|
+
if (width == 0 || height == 0) return;
|
2507
|
+
|
2508
|
+
ctx.save();
|
2509
|
+
if (isSvg) {
|
2510
|
+
ctx.drawSvg(this.img, x, y, width, height);
|
2511
|
+
}
|
2512
|
+
else {
|
2513
|
+
ctx.translate(x, y);
|
2514
|
+
svg.AspectRatio(ctx,
|
2515
|
+
this.attribute('preserveAspectRatio').value,
|
2516
|
+
width,
|
2517
|
+
this.img.width,
|
2518
|
+
height,
|
2519
|
+
this.img.height,
|
2520
|
+
0,
|
2521
|
+
0);
|
2522
|
+
if (self.loaded) {
|
2523
|
+
if (this.img.complete === undefined || this.img.complete) {
|
2524
|
+
ctx.drawImage(this.img, 0, 0);
|
2525
|
+
}
|
2526
|
+
}
|
2527
|
+
}
|
2528
|
+
ctx.restore();
|
2529
|
+
}
|
2530
|
+
|
2531
|
+
this.getBoundingBox = function() {
|
2532
|
+
var x = this.attribute('x').toPixels('x');
|
2533
|
+
var y = this.attribute('y').toPixels('y');
|
2534
|
+
var width = this.attribute('width').toPixels('x');
|
2535
|
+
var height = this.attribute('height').toPixels('y');
|
2536
|
+
return new svg.BoundingBox(x, y, x + width, y + height);
|
2537
|
+
}
|
2538
|
+
}
|
2539
|
+
svg.Element.image.prototype = new svg.Element.RenderedElementBase;
|
2540
|
+
|
2541
|
+
// group element
|
2542
|
+
svg.Element.g = function(node) {
|
2543
|
+
this.base = svg.Element.RenderedElementBase;
|
2544
|
+
this.base(node);
|
2545
|
+
|
2546
|
+
this.getBoundingBox = function() {
|
2547
|
+
var bb = new svg.BoundingBox();
|
2548
|
+
for (var i=0; i<this.children.length; i++) {
|
2549
|
+
bb.addBoundingBox(this.children[i].getBoundingBox());
|
2550
|
+
}
|
2551
|
+
return bb;
|
2552
|
+
};
|
2553
|
+
}
|
2554
|
+
svg.Element.g.prototype = new svg.Element.RenderedElementBase;
|
2555
|
+
|
2556
|
+
// symbol element
|
2557
|
+
svg.Element.symbol = function(node) {
|
2558
|
+
this.base = svg.Element.RenderedElementBase;
|
2559
|
+
this.base(node);
|
2560
|
+
|
2561
|
+
this.render = function(ctx) {
|
2562
|
+
// NO RENDER
|
2563
|
+
};
|
2564
|
+
}
|
2565
|
+
svg.Element.symbol.prototype = new svg.Element.RenderedElementBase;
|
2566
|
+
|
2567
|
+
// style element
|
2568
|
+
svg.Element.style = function(node) {
|
2569
|
+
this.base = svg.Element.ElementBase;
|
2570
|
+
this.base(node);
|
2571
|
+
|
2572
|
+
// text, or spaces then CDATA
|
2573
|
+
var css = ''
|
2574
|
+
for (var i=0; i<node.childNodes.length; i++) {
|
2575
|
+
css += node.childNodes[i].data;
|
2576
|
+
}
|
2577
|
+
css = css.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm, ''); // remove comments
|
2578
|
+
css = svg.compressSpaces(css); // replace whitespace
|
2579
|
+
var cssDefs = css.split('}');
|
2580
|
+
for (var i=0; i<cssDefs.length; i++) {
|
2581
|
+
if (svg.trim(cssDefs[i]) != '') {
|
2582
|
+
var cssDef = cssDefs[i].split('{');
|
2583
|
+
var cssClasses = cssDef[0].split(',');
|
2584
|
+
var cssProps = cssDef[1].split(';');
|
2585
|
+
for (var j=0; j<cssClasses.length; j++) {
|
2586
|
+
var cssClass = svg.trim(cssClasses[j]);
|
2587
|
+
if (cssClass != '') {
|
2588
|
+
var props = svg.Styles[cssClass] || {};
|
2589
|
+
for (var k=0; k<cssProps.length; k++) {
|
2590
|
+
var prop = cssProps[k].indexOf(':');
|
2591
|
+
var name = cssProps[k].substr(0, prop);
|
2592
|
+
var value = cssProps[k].substr(prop + 1, cssProps[k].length - prop);
|
2593
|
+
if (name != null && value != null) {
|
2594
|
+
props[svg.trim(name)] = new svg.Property(svg.trim(name), svg.trim(value));
|
2595
|
+
}
|
2596
|
+
}
|
2597
|
+
svg.Styles[cssClass] = props;
|
2598
|
+
svg.StylesSpecificity[cssClass] = getSelectorSpecificity(cssClass);
|
2599
|
+
if (cssClass == '@font-face' && !nodeEnv) {
|
2600
|
+
var fontFamily = props['font-family'].value.replace(/"/g,'');
|
2601
|
+
var srcs = props['src'].value.split(',');
|
2602
|
+
for (var s=0; s<srcs.length; s++) {
|
2603
|
+
if (srcs[s].indexOf('format("svg")') > 0) {
|
2604
|
+
var urlStart = srcs[s].indexOf('url');
|
2605
|
+
var urlEnd = srcs[s].indexOf(')', urlStart);
|
2606
|
+
var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6);
|
2607
|
+
var doc = svg.parseXml(svg.ajax(url));
|
2608
|
+
var fonts = doc.getElementsByTagName('font');
|
2609
|
+
for (var f=0; f<fonts.length; f++) {
|
2610
|
+
var font = svg.CreateElement(fonts[f]);
|
2611
|
+
svg.Definitions[fontFamily] = font;
|
2612
|
+
}
|
2613
|
+
}
|
2614
|
+
}
|
2615
|
+
}
|
2616
|
+
}
|
2617
|
+
}
|
2618
|
+
}
|
2619
|
+
}
|
2620
|
+
}
|
2621
|
+
svg.Element.style.prototype = new svg.Element.ElementBase;
|
2622
|
+
|
2623
|
+
// use element
|
2624
|
+
svg.Element.use = function(node) {
|
2625
|
+
this.base = svg.Element.RenderedElementBase;
|
2626
|
+
this.base(node);
|
2627
|
+
|
2628
|
+
this.baseSetContext = this.setContext;
|
2629
|
+
this.setContext = function(ctx) {
|
2630
|
+
this.baseSetContext(ctx);
|
2631
|
+
if (this.attribute('x').hasValue()) ctx.translate(this.attribute('x').toPixels('x'), 0);
|
2632
|
+
if (this.attribute('y').hasValue()) ctx.translate(0, this.attribute('y').toPixels('y'));
|
2633
|
+
}
|
2634
|
+
|
2635
|
+
var element = this.getHrefAttribute().getDefinition();
|
2636
|
+
|
2637
|
+
this.path = function(ctx) {
|
2638
|
+
if (element != null) element.path(ctx);
|
2639
|
+
}
|
2640
|
+
|
2641
|
+
this.getBoundingBox = function() {
|
2642
|
+
if (element != null) return element.getBoundingBox();
|
2643
|
+
}
|
2644
|
+
|
2645
|
+
this.renderChildren = function(ctx) {
|
2646
|
+
if (element != null) {
|
2647
|
+
var tempSvg = element;
|
2648
|
+
if (element.type == 'symbol') {
|
2649
|
+
// render me using a temporary svg element in symbol cases (http://www.w3.org/TR/SVG/struct.html#UseElement)
|
2650
|
+
tempSvg = new svg.Element.svg();
|
2651
|
+
tempSvg.type = 'svg';
|
2652
|
+
tempSvg.attributes['viewBox'] = new svg.Property('viewBox', element.attribute('viewBox').value);
|
2653
|
+
tempSvg.attributes['preserveAspectRatio'] = new svg.Property('preserveAspectRatio', element.attribute('preserveAspectRatio').value);
|
2654
|
+
tempSvg.attributes['overflow'] = new svg.Property('overflow', element.attribute('overflow').value);
|
2655
|
+
tempSvg.children = element.children;
|
2656
|
+
}
|
2657
|
+
if (tempSvg.type == 'svg') {
|
2658
|
+
// if symbol or svg, inherit width/height from me
|
2659
|
+
if (this.attribute('width').hasValue()) tempSvg.attributes['width'] = new svg.Property('width', this.attribute('width').value);
|
2660
|
+
if (this.attribute('height').hasValue()) tempSvg.attributes['height'] = new svg.Property('height', this.attribute('height').value);
|
2661
|
+
}
|
2662
|
+
var oldParent = tempSvg.parent;
|
2663
|
+
tempSvg.parent = null;
|
2664
|
+
tempSvg.render(ctx);
|
2665
|
+
tempSvg.parent = oldParent;
|
2666
|
+
}
|
2667
|
+
}
|
2668
|
+
}
|
2669
|
+
svg.Element.use.prototype = new svg.Element.RenderedElementBase;
|
2670
|
+
|
2671
|
+
// mask element
|
2672
|
+
svg.Element.mask = function(node) {
|
2673
|
+
this.base = svg.Element.ElementBase;
|
2674
|
+
this.base(node);
|
2675
|
+
|
2676
|
+
this.apply = function(ctx, element) {
|
2677
|
+
// render as temp svg
|
2678
|
+
var x = this.attribute('x').toPixels('x');
|
2679
|
+
var y = this.attribute('y').toPixels('y');
|
2680
|
+
var width = this.attribute('width').toPixels('x');
|
2681
|
+
var height = this.attribute('height').toPixels('y');
|
2682
|
+
|
2683
|
+
if (width == 0 && height == 0) {
|
2684
|
+
var bb = new svg.BoundingBox();
|
2685
|
+
for (var i=0; i<this.children.length; i++) {
|
2686
|
+
bb.addBoundingBox(this.children[i].getBoundingBox());
|
2687
|
+
}
|
2688
|
+
var x = Math.floor(bb.x1);
|
2689
|
+
var y = Math.floor(bb.y1);
|
2690
|
+
var width = Math.floor(bb.width());
|
2691
|
+
var height = Math.floor(bb.height());
|
2692
|
+
}
|
2693
|
+
|
2694
|
+
// temporarily remove mask to avoid recursion
|
2695
|
+
var mask = element.attribute('mask').value;
|
2696
|
+
element.attribute('mask').value = '';
|
2697
|
+
|
2698
|
+
var cMask = createCanvas();
|
2699
|
+
cMask.width = x + width;
|
2700
|
+
cMask.height = y + height;
|
2701
|
+
var maskCtx = cMask.getContext('2d');
|
2702
|
+
this.renderChildren(maskCtx);
|
2703
|
+
|
2704
|
+
var c = createCanvas();
|
2705
|
+
c.width = x + width;
|
2706
|
+
c.height = y + height;
|
2707
|
+
var tempCtx = c.getContext('2d');
|
2708
|
+
element.render(tempCtx);
|
2709
|
+
tempCtx.globalCompositeOperation = 'destination-in';
|
2710
|
+
tempCtx.fillStyle = maskCtx.createPattern(cMask, 'no-repeat');
|
2711
|
+
tempCtx.fillRect(0, 0, x + width, y + height);
|
2712
|
+
|
2713
|
+
ctx.fillStyle = tempCtx.createPattern(c, 'no-repeat');
|
2714
|
+
ctx.fillRect(0, 0, x + width, y + height);
|
2715
|
+
|
2716
|
+
// reassign mask
|
2717
|
+
element.attribute('mask').value = mask;
|
2718
|
+
}
|
2719
|
+
|
2720
|
+
this.render = function(ctx) {
|
2721
|
+
// NO RENDER
|
2722
|
+
}
|
2723
|
+
}
|
2724
|
+
svg.Element.mask.prototype = new svg.Element.ElementBase;
|
2725
|
+
|
2726
|
+
// clip element
|
2727
|
+
svg.Element.clipPath = function(node) {
|
2728
|
+
this.base = svg.Element.ElementBase;
|
2729
|
+
this.base(node);
|
2730
|
+
|
2731
|
+
this.apply = function(ctx) {
|
2732
|
+
var hasContext2D = (typeof CanvasRenderingContext2D !== 'undefined');
|
2733
|
+
var oldBeginPath = ctx.beginPath;
|
2734
|
+
var oldClosePath = ctx.closePath;
|
2735
|
+
if (hasContext2D) {
|
2736
|
+
CanvasRenderingContext2D.prototype.beginPath = function () { };
|
2737
|
+
CanvasRenderingContext2D.prototype.closePath = function () { };
|
2738
|
+
}
|
2739
|
+
|
2740
|
+
oldBeginPath.call(ctx);
|
2741
|
+
for (var i=0; i<this.children.length; i++) {
|
2742
|
+
var child = this.children[i];
|
2743
|
+
if (typeof child.path != 'undefined') {
|
2744
|
+
var transform = null;
|
2745
|
+
if (child.style('transform', false, true).hasValue()) {
|
2746
|
+
transform = new svg.Transform(child.style('transform', false, true).value);
|
2747
|
+
transform.apply(ctx);
|
2748
|
+
}
|
2749
|
+
child.path(ctx);
|
2750
|
+
if (hasContext2D) {
|
2751
|
+
CanvasRenderingContext2D.prototype.closePath = oldClosePath;
|
2752
|
+
}
|
2753
|
+
if (transform) { transform.unapply(ctx); }
|
2754
|
+
}
|
2755
|
+
}
|
2756
|
+
oldClosePath.call(ctx);
|
2757
|
+
ctx.clip();
|
2758
|
+
if (hasContext2D) {
|
2759
|
+
CanvasRenderingContext2D.prototype.beginPath = oldBeginPath;
|
2760
|
+
CanvasRenderingContext2D.prototype.closePath = oldClosePath;
|
2761
|
+
}
|
2762
|
+
}
|
2763
|
+
|
2764
|
+
this.render = function(ctx) {
|
2765
|
+
// NO RENDER
|
2766
|
+
}
|
2767
|
+
}
|
2768
|
+
svg.Element.clipPath.prototype = new svg.Element.ElementBase;
|
2769
|
+
|
2770
|
+
// filters
|
2771
|
+
svg.Element.filter = function(node) {
|
2772
|
+
this.base = svg.Element.ElementBase;
|
2773
|
+
this.base(node);
|
2774
|
+
|
2775
|
+
this.apply = function(ctx, element) {
|
2776
|
+
// render as temp svg
|
2777
|
+
var bb = element.getBoundingBox();
|
2778
|
+
var x = Math.floor(bb.x1);
|
2779
|
+
var y = Math.floor(bb.y1);
|
2780
|
+
var width = Math.floor(bb.width());
|
2781
|
+
var height = Math.floor(bb.height());
|
2782
|
+
|
2783
|
+
// temporarily remove filter to avoid recursion
|
2784
|
+
var filter = element.style('filter').value;
|
2785
|
+
element.style('filter').value = '';
|
2786
|
+
|
2787
|
+
var px = 0, py = 0;
|
2788
|
+
for (var i=0; i<this.children.length; i++) {
|
2789
|
+
var efd = this.children[i].extraFilterDistance || 0;
|
2790
|
+
px = Math.max(px, efd);
|
2791
|
+
py = Math.max(py, efd);
|
2792
|
+
}
|
2793
|
+
|
2794
|
+
var c = createCanvas();
|
2795
|
+
c.width = width + 2*px;
|
2796
|
+
c.height = height + 2*py;
|
2797
|
+
var tempCtx = c.getContext('2d');
|
2798
|
+
tempCtx.translate(-x + px, -y + py);
|
2799
|
+
element.render(tempCtx);
|
2800
|
+
|
2801
|
+
// apply filters
|
2802
|
+
for (var i=0; i<this.children.length; i++) {
|
2803
|
+
if (typeof this.children[i].apply == 'function') {
|
2804
|
+
this.children[i].apply(tempCtx, 0, 0, width + 2*px, height + 2*py);
|
2805
|
+
}
|
2806
|
+
}
|
2807
|
+
|
2808
|
+
// render on me
|
2809
|
+
ctx.drawImage(c, 0, 0, width + 2*px, height + 2*py, x - px, y - py, width + 2*px, height + 2*py);
|
2810
|
+
|
2811
|
+
// reassign filter
|
2812
|
+
element.style('filter', true).value = filter;
|
2813
|
+
}
|
2814
|
+
|
2815
|
+
this.render = function(ctx) {
|
2816
|
+
// NO RENDER
|
2817
|
+
}
|
2818
|
+
}
|
2819
|
+
svg.Element.filter.prototype = new svg.Element.ElementBase;
|
2820
|
+
|
2821
|
+
svg.Element.feMorphology = function(node) {
|
2822
|
+
this.base = svg.Element.ElementBase;
|
2823
|
+
this.base(node);
|
2824
|
+
|
2825
|
+
this.apply = function(ctx, x, y, width, height) {
|
2826
|
+
// TODO: implement
|
2827
|
+
}
|
2828
|
+
}
|
2829
|
+
svg.Element.feMorphology.prototype = new svg.Element.ElementBase;
|
2830
|
+
|
2831
|
+
svg.Element.feComposite = function(node) {
|
2832
|
+
this.base = svg.Element.ElementBase;
|
2833
|
+
this.base(node);
|
2834
|
+
|
2835
|
+
this.apply = function(ctx, x, y, width, height) {
|
2836
|
+
// TODO: implement
|
2837
|
+
}
|
2838
|
+
}
|
2839
|
+
svg.Element.feComposite.prototype = new svg.Element.ElementBase;
|
2840
|
+
|
2841
|
+
svg.Element.feColorMatrix = function(node) {
|
2842
|
+
this.base = svg.Element.ElementBase;
|
2843
|
+
this.base(node);
|
2844
|
+
|
2845
|
+
var matrix = svg.ToNumberArray(this.attribute('values').value);
|
2846
|
+
switch (this.attribute('type').valueOrDefault('matrix')) { // http://www.w3.org/TR/SVG/filters.html#feColorMatrixElement
|
2847
|
+
case 'saturate':
|
2848
|
+
var s = matrix[0];
|
2849
|
+
matrix = [0.213+0.787*s,0.715-0.715*s,0.072-0.072*s,0,0,
|
2850
|
+
0.213-0.213*s,0.715+0.285*s,0.072-0.072*s,0,0,
|
2851
|
+
0.213-0.213*s,0.715-0.715*s,0.072+0.928*s,0,0,
|
2852
|
+
0,0,0,1,0,
|
2853
|
+
0,0,0,0,1];
|
2854
|
+
break;
|
2855
|
+
case 'hueRotate':
|
2856
|
+
var a = matrix[0] * Math.PI / 180.0;
|
2857
|
+
var c = function (m1,m2,m3) { return m1 + Math.cos(a)*m2 + Math.sin(a)*m3; };
|
2858
|
+
matrix = [c(0.213,0.787,-0.213),c(0.715,-0.715,-0.715),c(0.072,-0.072,0.928),0,0,
|
2859
|
+
c(0.213,-0.213,0.143),c(0.715,0.285,0.140),c(0.072,-0.072,-0.283),0,0,
|
2860
|
+
c(0.213,-0.213,-0.787),c(0.715,-0.715,0.715),c(0.072,0.928,0.072),0,0,
|
2861
|
+
0,0,0,1,0,
|
2862
|
+
0,0,0,0,1];
|
2863
|
+
break;
|
2864
|
+
case 'luminanceToAlpha':
|
2865
|
+
matrix = [0,0,0,0,0,
|
2866
|
+
0,0,0,0,0,
|
2867
|
+
0,0,0,0,0,
|
2868
|
+
0.2125,0.7154,0.0721,0,0,
|
2869
|
+
0,0,0,0,1];
|
2870
|
+
break;
|
2871
|
+
}
|
2872
|
+
|
2873
|
+
function imGet(img, x, y, width, height, rgba) {
|
2874
|
+
return img[y*width*4 + x*4 + rgba];
|
2875
|
+
}
|
2876
|
+
|
2877
|
+
function imSet(img, x, y, width, height, rgba, val) {
|
2878
|
+
img[y*width*4 + x*4 + rgba] = val;
|
2879
|
+
}
|
2880
|
+
|
2881
|
+
function m(i, v) {
|
2882
|
+
var mi = matrix[i];
|
2883
|
+
return mi * (mi < 0 ? v - 255 : v);
|
2884
|
+
}
|
2885
|
+
|
2886
|
+
this.apply = function(ctx, x, y, width, height) {
|
2887
|
+
// assuming x==0 && y==0 for now
|
2888
|
+
var srcData = ctx.getImageData(0, 0, width, height);
|
2889
|
+
for (var y = 0; y < height; y++) {
|
2890
|
+
for (var x = 0; x < width; x++) {
|
2891
|
+
var r = imGet(srcData.data, x, y, width, height, 0);
|
2892
|
+
var g = imGet(srcData.data, x, y, width, height, 1);
|
2893
|
+
var b = imGet(srcData.data, x, y, width, height, 2);
|
2894
|
+
var a = imGet(srcData.data, x, y, width, height, 3);
|
2895
|
+
imSet(srcData.data, x, y, width, height, 0, m(0,r)+m(1,g)+m(2,b)+m(3,a)+m(4,1));
|
2896
|
+
imSet(srcData.data, x, y, width, height, 1, m(5,r)+m(6,g)+m(7,b)+m(8,a)+m(9,1));
|
2897
|
+
imSet(srcData.data, x, y, width, height, 2, m(10,r)+m(11,g)+m(12,b)+m(13,a)+m(14,1));
|
2898
|
+
imSet(srcData.data, x, y, width, height, 3, m(15,r)+m(16,g)+m(17,b)+m(18,a)+m(19,1));
|
2899
|
+
}
|
2900
|
+
}
|
2901
|
+
ctx.clearRect(0, 0, width, height);
|
2902
|
+
ctx.putImageData(srcData, 0, 0);
|
2903
|
+
}
|
2904
|
+
}
|
2905
|
+
svg.Element.feColorMatrix.prototype = new svg.Element.ElementBase;
|
2906
|
+
|
2907
|
+
svg.Element.feGaussianBlur = function(node) {
|
2908
|
+
this.base = svg.Element.ElementBase;
|
2909
|
+
this.base(node);
|
2910
|
+
|
2911
|
+
this.blurRadius = Math.floor(this.attribute('stdDeviation').numValue());
|
2912
|
+
this.extraFilterDistance = this.blurRadius;
|
2913
|
+
|
2914
|
+
this.apply = function(ctx, x, y, width, height) {
|
2915
|
+
if (typeof stackBlur.canvasRGBA == 'undefined') {
|
2916
|
+
svg.log('ERROR: StackBlur.js must be included for blur to work');
|
2917
|
+
return;
|
2918
|
+
}
|
2919
|
+
|
2920
|
+
// StackBlur requires canvas be on document
|
2921
|
+
ctx.canvas.id = svg.UniqueId();
|
2922
|
+
ctx.canvas.style.display = 'none';
|
2923
|
+
document.body.appendChild(ctx.canvas);
|
2924
|
+
stackBlur.canvasRGBA(ctx.canvas.id, x, y, width, height, this.blurRadius);
|
2925
|
+
document.body.removeChild(ctx.canvas);
|
2926
|
+
}
|
2927
|
+
}
|
2928
|
+
svg.Element.feGaussianBlur.prototype = new svg.Element.ElementBase;
|
2929
|
+
|
2930
|
+
// title element, do nothing
|
2931
|
+
svg.Element.title = function(node) {
|
2932
|
+
}
|
2933
|
+
svg.Element.title.prototype = new svg.Element.ElementBase;
|
2934
|
+
|
2935
|
+
// desc element, do nothing
|
2936
|
+
svg.Element.desc = function(node) {
|
2937
|
+
}
|
2938
|
+
svg.Element.desc.prototype = new svg.Element.ElementBase;
|
2939
|
+
|
2940
|
+
svg.Element.MISSING = function(node) {
|
2941
|
+
svg.log('ERROR: Element \'' + node.nodeName + '\' not yet implemented.');
|
2942
|
+
}
|
2943
|
+
svg.Element.MISSING.prototype = new svg.Element.ElementBase;
|
2944
|
+
|
2945
|
+
// element factory
|
2946
|
+
svg.CreateElement = function(node) {
|
2947
|
+
var className = node.nodeName.replace(/^[^:]+:/,''); // remove namespace
|
2948
|
+
className = className.replace(/\-/g,''); // remove dashes
|
2949
|
+
var e = null;
|
2950
|
+
if (typeof svg.Element[className] != 'undefined') {
|
2951
|
+
e = new svg.Element[className](node);
|
2952
|
+
}
|
2953
|
+
else {
|
2954
|
+
e = new svg.Element.MISSING(node);
|
2955
|
+
}
|
2956
|
+
|
2957
|
+
e.type = node.nodeName;
|
2958
|
+
return e;
|
2959
|
+
}
|
2960
|
+
|
2961
|
+
// load from url
|
2962
|
+
svg.load = function(ctx, url) {
|
2963
|
+
svg.loadXml(ctx, svg.ajax(url));
|
2964
|
+
}
|
2965
|
+
|
2966
|
+
// load from xml
|
2967
|
+
svg.loadXml = function(ctx, xml) {
|
2968
|
+
svg.loadXmlDoc(ctx, svg.parseXml(xml));
|
2969
|
+
}
|
2970
|
+
|
2971
|
+
svg.loadXmlDoc = function(ctx, dom) {
|
2972
|
+
svg.init(ctx);
|
2973
|
+
|
2974
|
+
var mapXY = function(p) {
|
2975
|
+
var e = ctx.canvas;
|
2976
|
+
while (e) {
|
2977
|
+
p.x -= e.offsetLeft;
|
2978
|
+
p.y -= e.offsetTop;
|
2979
|
+
e = e.offsetParent;
|
2980
|
+
}
|
2981
|
+
if (windowEnv.scrollX) p.x += windowEnv.scrollX;
|
2982
|
+
if (windowEnv.scrollY) p.y += windowEnv.scrollY;
|
2983
|
+
return p;
|
2984
|
+
}
|
2985
|
+
|
2986
|
+
// bind mouse
|
2987
|
+
if (svg.opts['ignoreMouse'] != true) {
|
2988
|
+
ctx.canvas.onclick = function(e) {
|
2989
|
+
var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
|
2990
|
+
svg.Mouse.onclick(p.x, p.y);
|
2991
|
+
};
|
2992
|
+
ctx.canvas.onmousemove = function(e) {
|
2993
|
+
var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
|
2994
|
+
svg.Mouse.onmousemove(p.x, p.y);
|
2995
|
+
};
|
2996
|
+
}
|
2997
|
+
|
2998
|
+
var e = svg.CreateElement(dom.documentElement);
|
2999
|
+
e.root = true;
|
3000
|
+
e.addStylesFromStyleDefinition();
|
3001
|
+
|
3002
|
+
// render loop
|
3003
|
+
var isFirstRender = true;
|
3004
|
+
var draw = function() {
|
3005
|
+
svg.ViewPort.Clear();
|
3006
|
+
if (ctx.canvas.parentNode) {
|
3007
|
+
svg.ViewPort.SetCurrent(ctx.canvas.parentNode.clientWidth, ctx.canvas.parentNode.clientHeight);
|
3008
|
+
} else {
|
3009
|
+
svg.ViewPort.SetCurrent(defaultClientWidth, defaultClientHeight);
|
3010
|
+
}
|
3011
|
+
|
3012
|
+
if (svg.opts['ignoreDimensions'] != true) {
|
3013
|
+
// set canvas size
|
3014
|
+
if (e.style('width').hasValue()) {
|
3015
|
+
ctx.canvas.width = e.style('width').toPixels('x');
|
3016
|
+
if (ctx.canvas.style) {ctx.canvas.style.width = ctx.canvas.width + 'px';}
|
3017
|
+
}
|
3018
|
+
if (e.style('height').hasValue()) {
|
3019
|
+
ctx.canvas.height = e.style('height').toPixels('y');
|
3020
|
+
if (ctx.canvas.style) {ctx.canvas.style.height = ctx.canvas.height + 'px';}
|
3021
|
+
}
|
3022
|
+
}
|
3023
|
+
var cWidth = ctx.canvas.clientWidth || ctx.canvas.width;
|
3024
|
+
var cHeight = ctx.canvas.clientHeight || ctx.canvas.height;
|
3025
|
+
if (svg.opts['ignoreDimensions'] == true && e.style('width').hasValue() && e.style('height').hasValue()) {
|
3026
|
+
cWidth = e.style('width').toPixels('x');
|
3027
|
+
cHeight = e.style('height').toPixels('y');
|
3028
|
+
}
|
3029
|
+
svg.ViewPort.SetCurrent(cWidth, cHeight);
|
3030
|
+
|
3031
|
+
if (svg.opts['offsetX'] != null) e.attribute('x', true).value = svg.opts['offsetX'];
|
3032
|
+
if (svg.opts['offsetY'] != null) e.attribute('y', true).value = svg.opts['offsetY'];
|
3033
|
+
if (svg.opts['scaleWidth'] != null || svg.opts['scaleHeight'] != null) {
|
3034
|
+
var xRatio = null, yRatio = null, viewBox = svg.ToNumberArray(e.attribute('viewBox').value);
|
3035
|
+
|
3036
|
+
if (svg.opts['scaleWidth'] != null) {
|
3037
|
+
if (e.attribute('width').hasValue()) xRatio = e.attribute('width').toPixels('x') / svg.opts['scaleWidth'];
|
3038
|
+
else if (!isNaN(viewBox[2])) xRatio = viewBox[2] / svg.opts['scaleWidth'];
|
3039
|
+
}
|
3040
|
+
|
3041
|
+
if (svg.opts['scaleHeight'] != null) {
|
3042
|
+
if (e.attribute('height').hasValue()) yRatio = e.attribute('height').toPixels('y') / svg.opts['scaleHeight'];
|
3043
|
+
else if (!isNaN(viewBox[3])) yRatio = viewBox[3] / svg.opts['scaleHeight'];
|
3044
|
+
}
|
3045
|
+
|
3046
|
+
if (xRatio == null) { xRatio = yRatio; }
|
3047
|
+
if (yRatio == null) { yRatio = xRatio; }
|
3048
|
+
|
3049
|
+
e.attribute('width', true).value = svg.opts['scaleWidth'];
|
3050
|
+
e.attribute('height', true).value = svg.opts['scaleHeight'];
|
3051
|
+
e.style('transform', true, true).value += ' scale('+(1.0/xRatio)+','+(1.0/yRatio)+')';
|
3052
|
+
}
|
3053
|
+
|
3054
|
+
// clear and render
|
3055
|
+
if (svg.opts['ignoreClear'] != true) {
|
3056
|
+
ctx.clearRect(0, 0, cWidth, cHeight);
|
3057
|
+
}
|
3058
|
+
e.render(ctx);
|
3059
|
+
if (isFirstRender) {
|
3060
|
+
isFirstRender = false;
|
3061
|
+
if (typeof svg.opts['renderCallback'] == 'function') svg.opts['renderCallback'](dom);
|
3062
|
+
}
|
3063
|
+
}
|
3064
|
+
|
3065
|
+
var waitingForImages = true;
|
3066
|
+
if (svg.ImagesLoaded()) {
|
3067
|
+
waitingForImages = false;
|
3068
|
+
draw();
|
3069
|
+
}
|
3070
|
+
if (!nodeEnv || opts['enableRedraw']) {
|
3071
|
+
//In node, in the most cases, we don't need the animation listener.
|
3072
|
+
svg.intervalID = setInterval(function() {
|
3073
|
+
var needUpdate = false;
|
3074
|
+
|
3075
|
+
if (waitingForImages && svg.ImagesLoaded()) {
|
3076
|
+
waitingForImages = false;
|
3077
|
+
needUpdate = true;
|
3078
|
+
}
|
3079
|
+
|
3080
|
+
// need update from mouse events?
|
3081
|
+
if (svg.opts['ignoreMouse'] != true) {
|
3082
|
+
needUpdate = needUpdate | svg.Mouse.hasEvents();
|
3083
|
+
}
|
3084
|
+
|
3085
|
+
// need update from animations?
|
3086
|
+
if (svg.opts['ignoreAnimation'] != true) {
|
3087
|
+
for (var i=0; i<svg.Animations.length; i++) {
|
3088
|
+
needUpdate = needUpdate | svg.Animations[i].update(1000 / svg.FRAMERATE);
|
3089
|
+
}
|
3090
|
+
}
|
3091
|
+
|
3092
|
+
// need update from redraw?
|
3093
|
+
if (typeof svg.opts['forceRedraw'] == 'function') {
|
3094
|
+
if (svg.opts['forceRedraw']() == true) needUpdate = true;
|
3095
|
+
}
|
3096
|
+
|
3097
|
+
// render if needed
|
3098
|
+
if (needUpdate) {
|
3099
|
+
draw();
|
3100
|
+
svg.Mouse.runEvents(); // run and clear our events
|
3101
|
+
}
|
3102
|
+
}, 1000 / svg.FRAMERATE);
|
3103
|
+
}
|
3104
|
+
}
|
3105
|
+
|
3106
|
+
svg.stop = function() {
|
3107
|
+
if (svg.intervalID) {
|
3108
|
+
clearInterval(svg.intervalID);
|
3109
|
+
}
|
3110
|
+
}
|
3111
|
+
|
3112
|
+
svg.Mouse = new (function() {
|
3113
|
+
this.events = [];
|
3114
|
+
this.hasEvents = function() { return this.events.length != 0; }
|
3115
|
+
|
3116
|
+
this.onclick = function(x, y) {
|
3117
|
+
this.events.push({ type: 'onclick', x: x, y: y,
|
3118
|
+
run: function(e) { if (e.onclick) e.onclick(); }
|
3119
|
+
});
|
3120
|
+
}
|
3121
|
+
|
3122
|
+
this.onmousemove = function(x, y) {
|
3123
|
+
this.events.push({ type: 'onmousemove', x: x, y: y,
|
3124
|
+
run: function(e) { if (e.onmousemove) e.onmousemove(); }
|
3125
|
+
});
|
3126
|
+
}
|
3127
|
+
|
3128
|
+
this.eventElements = [];
|
3129
|
+
|
3130
|
+
this.checkPath = function(element, ctx) {
|
3131
|
+
for (var i=0; i<this.events.length; i++) {
|
3132
|
+
var e = this.events[i];
|
3133
|
+
if (ctx.isPointInPath && ctx.isPointInPath(e.x, e.y)) this.eventElements[i] = element;
|
3134
|
+
}
|
3135
|
+
}
|
3136
|
+
|
3137
|
+
this.checkBoundingBox = function(element, bb) {
|
3138
|
+
for (var i=0; i<this.events.length; i++) {
|
3139
|
+
var e = this.events[i];
|
3140
|
+
if (bb.isPointInBox(e.x, e.y)) this.eventElements[i] = element;
|
3141
|
+
}
|
3142
|
+
}
|
3143
|
+
|
3144
|
+
this.runEvents = function() {
|
3145
|
+
svg.ctx.canvas.style.cursor = '';
|
3146
|
+
|
3147
|
+
for (var i=0; i<this.events.length; i++) {
|
3148
|
+
var e = this.events[i];
|
3149
|
+
var element = this.eventElements[i];
|
3150
|
+
while (element) {
|
3151
|
+
e.run(element);
|
3152
|
+
element = element.parent;
|
3153
|
+
}
|
3154
|
+
}
|
3155
|
+
|
3156
|
+
// done running, clear
|
3157
|
+
this.events = [];
|
3158
|
+
this.eventElements = [];
|
3159
|
+
}
|
3160
|
+
});
|
3161
|
+
|
3162
|
+
return svg;
|
3163
|
+
};
|
3164
|
+
|
3165
|
+
if (typeof CanvasRenderingContext2D != 'undefined') {
|
3166
|
+
CanvasRenderingContext2D.prototype.drawSvg = function(s, dx, dy, dw, dh, opts) {
|
3167
|
+
var cOpts = {
|
3168
|
+
ignoreMouse: true,
|
3169
|
+
ignoreAnimation: true,
|
3170
|
+
ignoreDimensions: true,
|
3171
|
+
ignoreClear: true,
|
3172
|
+
offsetX: dx,
|
3173
|
+
offsetY: dy,
|
3174
|
+
scaleWidth: dw,
|
3175
|
+
scaleHeight: dh
|
3176
|
+
}
|
3177
|
+
|
3178
|
+
for(var prop in opts) {
|
3179
|
+
if(opts.hasOwnProperty(prop)){
|
3180
|
+
cOpts[prop] = opts[prop];
|
3181
|
+
}
|
3182
|
+
}
|
3183
|
+
canvg(this.canvas, s, cOpts);
|
3184
|
+
}
|
3185
|
+
}
|
3186
|
+
|
3187
|
+
return canvg;
|
3188
|
+
|
3189
|
+
}));
|