highcharts-rails 2.1.9 → 2.2.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.
- data/lib/highcharts/version.rb +1 -1
- data/vendor/assets/javascripts/highcharts.js +3872 -2035
- data/vendor/assets/javascripts/highcharts/adapters/mootools.js +37 -3
- data/vendor/assets/javascripts/highcharts/adapters/prototype.js +39 -12
- data/vendor/assets/javascripts/highcharts/modules/canvas-tools.js +3114 -0
- data/vendor/assets/javascripts/highcharts/modules/exporting.js +72 -96
- data/vendor/assets/javascripts/highcharts/themes/dark-blue.js +1 -1
- data/vendor/assets/javascripts/highcharts/themes/dark-green.js +1 -1
- data/vendor/assets/javascripts/highcharts/themes/gray.js +0 -0
- data/vendor/assets/javascripts/highcharts/themes/grid.js +0 -1
- metadata +9 -8
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license Highcharts JS v2.
|
2
|
+
* @license Highcharts JS v2.2.0 (2012-02-16)
|
3
3
|
* MooTools adapter
|
4
4
|
*
|
5
5
|
* (c) 2010-2011 Torstein Hønsi
|
@@ -13,6 +13,7 @@
|
|
13
13
|
(function () {
|
14
14
|
|
15
15
|
var win = window,
|
16
|
+
doc = document,
|
16
17
|
mooVersion = win.MooTools.version.substring(0, 3), // Get the first three characters of the version number
|
17
18
|
legacy = mooVersion === '1.2' || mooVersion === '1.1', // 1.1 && 1.2 considered legacy, 1.3 is not.
|
18
19
|
legacyEvent = legacy || mooVersion === '1.3', // In versions 1.1 - 1.3 the event class is named Event, in newer versions it is named DOMEvent.
|
@@ -68,6 +69,23 @@ win.HighchartsAdapter = {
|
|
68
69
|
/*jslint unparam: false*/
|
69
70
|
},
|
70
71
|
|
72
|
+
/**
|
73
|
+
* Downloads a script and executes a callback when done.
|
74
|
+
* @param {String} scriptLocation
|
75
|
+
* @param {Function} callback
|
76
|
+
*/
|
77
|
+
getScript: function (scriptLocation, callback) {
|
78
|
+
// We cannot assume that Assets class from mootools-more is available so instead insert a script tag to download script.
|
79
|
+
var head = doc.getElementsByTagName('head')[0];
|
80
|
+
var script = doc.createElement('script');
|
81
|
+
|
82
|
+
script.type = 'text/javascript';
|
83
|
+
script.src = scriptLocation;
|
84
|
+
script.onload = callback;
|
85
|
+
|
86
|
+
head.appendChild(script);
|
87
|
+
},
|
88
|
+
|
71
89
|
/**
|
72
90
|
* Animate a HTML element or SVG element wrapper
|
73
91
|
* @param {Object} el
|
@@ -87,7 +105,7 @@ win.HighchartsAdapter = {
|
|
87
105
|
el.attr.call(el, args[0], args[1][0]);
|
88
106
|
};
|
89
107
|
// dirty hack to trick Moo into handling el as an element wrapper
|
90
|
-
el.$family =
|
108
|
+
el.$family = function () { return true; };
|
91
109
|
}
|
92
110
|
|
93
111
|
// stop running animations
|
@@ -101,6 +119,11 @@ win.HighchartsAdapter = {
|
|
101
119
|
}, options)
|
102
120
|
);
|
103
121
|
|
122
|
+
// Make sure that the element reference is set when animating svg elements
|
123
|
+
if (isSVGElement) {
|
124
|
+
effect.element = el;
|
125
|
+
}
|
126
|
+
|
104
127
|
// special treatment for paths
|
105
128
|
if (params.d) {
|
106
129
|
effect.toD = params.d;
|
@@ -125,7 +148,7 @@ win.HighchartsAdapter = {
|
|
125
148
|
each: function (arr, fn) {
|
126
149
|
return legacy ?
|
127
150
|
$each(arr, fn) :
|
128
|
-
|
151
|
+
Array.each(arr, fn);
|
129
152
|
},
|
130
153
|
|
131
154
|
/**
|
@@ -171,6 +194,17 @@ win.HighchartsAdapter = {
|
|
171
194
|
return ret;
|
172
195
|
},
|
173
196
|
|
197
|
+
/**
|
198
|
+
* Get the offset of an element relative to the top left corner of the web page
|
199
|
+
*/
|
200
|
+
offset: function (el) {
|
201
|
+
var offsets = $(el).getOffsets();
|
202
|
+
return {
|
203
|
+
left: offsets.x,
|
204
|
+
top: offsets.y
|
205
|
+
};
|
206
|
+
},
|
207
|
+
|
174
208
|
/**
|
175
209
|
* Extends an object with Events, if its not done
|
176
210
|
*/
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license Highcharts JS v2.
|
2
|
+
* @license Highcharts JS v2.2.0 (2012-02-16)
|
3
3
|
* Prototype adapter
|
4
4
|
*
|
5
5
|
* @author Michael Nelson, Torstein Hønsi.
|
@@ -8,13 +8,8 @@
|
|
8
8
|
* Highcharts license: www.highcharts.com/license.
|
9
9
|
*/
|
10
10
|
|
11
|
-
/*
|
12
|
-
* Known issues:
|
13
|
-
* - Some grid lines land in wrong position - http://jsfiddle.net/highcharts/jaRhY/28
|
14
|
-
*/
|
15
|
-
|
16
11
|
// JSLint options:
|
17
|
-
/*global Effect, Class, Event, $, $A */
|
12
|
+
/*global Effect, Class, Event, Element, $, $$, $A */
|
18
13
|
|
19
14
|
// Adapter interface between prototype and the Highcharts charting library
|
20
15
|
var HighchartsAdapter = (function () {
|
@@ -43,7 +38,7 @@ return {
|
|
43
38
|
|
44
39
|
this.element = element;
|
45
40
|
this.key = attr;
|
46
|
-
from = element.attr(attr);
|
41
|
+
from = element.attr ? element.attr(attr) : $(element).getStyle(attr);
|
47
42
|
|
48
43
|
// special treatment for paths
|
49
44
|
if (attr === 'd') {
|
@@ -79,13 +74,23 @@ return {
|
|
79
74
|
this.element._highchart_animation[this.key] = this;
|
80
75
|
},
|
81
76
|
update: function (position) {
|
82
|
-
var paths = this.paths
|
77
|
+
var paths = this.paths,
|
78
|
+
element = this.element,
|
79
|
+
obj;
|
83
80
|
|
84
81
|
if (paths) {
|
85
82
|
position = pathAnim.step(paths[0], paths[1], position, this.toD);
|
86
83
|
}
|
87
84
|
|
88
|
-
|
85
|
+
if (element.attr) { // SVGElement
|
86
|
+
element.attr(this.options.attribute, position);
|
87
|
+
|
88
|
+
} else { // HTML, #409
|
89
|
+
obj = {};
|
90
|
+
obj[this.options.attribute] = position;
|
91
|
+
$(element).setStyle(obj);
|
92
|
+
}
|
93
|
+
|
89
94
|
},
|
90
95
|
finish: function () {
|
91
96
|
// Delete the property that holds this animation now that it is finished.
|
@@ -96,6 +101,19 @@ return {
|
|
96
101
|
}
|
97
102
|
},
|
98
103
|
|
104
|
+
/**
|
105
|
+
* Downloads a script and executes a callback when done.
|
106
|
+
* @param {String} scriptLocation
|
107
|
+
* @param {Function} callback
|
108
|
+
*/
|
109
|
+
getScript: function (scriptLocation, callback) {
|
110
|
+
var head = $$('head')[0]; // Returns an array, so pick the first element.
|
111
|
+
if (head) {
|
112
|
+
// Append a new 'script' element, set its type and src attributes, add a 'load' handler that calls the callback
|
113
|
+
head.appendChild(new Element('script', { type: 'text/javascript', src: scriptLocation}).observe('load', callback));
|
114
|
+
}
|
115
|
+
},
|
116
|
+
|
99
117
|
/**
|
100
118
|
* Custom events in prototype needs to be namespaced. This method adds a namespace 'h:' in front of
|
101
119
|
* events that are not recognized as native.
|
@@ -142,8 +160,8 @@ return {
|
|
142
160
|
}
|
143
161
|
}
|
144
162
|
|
145
|
-
if (!el.attr) {
|
146
|
-
|
163
|
+
if (!el.attr) { // HTML element, #409
|
164
|
+
$(el).setStyle(params);
|
147
165
|
}
|
148
166
|
},
|
149
167
|
|
@@ -164,6 +182,15 @@ return {
|
|
164
182
|
$A(arr).each(fn);
|
165
183
|
},
|
166
184
|
|
185
|
+
/**
|
186
|
+
* Get the cumulative offset relative to the top left of the page. This method, unlike its
|
187
|
+
* jQuery and MooTools counterpart, still suffers from issue #208 regarding the position
|
188
|
+
* of a chart within a fixed container.
|
189
|
+
*/
|
190
|
+
offset: function (el) {
|
191
|
+
return $(el).cumulativeOffset();
|
192
|
+
},
|
193
|
+
|
167
194
|
// fire an event based on an event name (event) and an object (el).
|
168
195
|
// again, el may not be a dom element
|
169
196
|
fireEvent: function (el, event, eventArguments, defaultFunction) {
|
@@ -0,0 +1,3114 @@
|
|
1
|
+
/**
|
2
|
+
* @license A class to parse color values
|
3
|
+
* @author Stoyan Stefanov <sstoo@gmail.com>
|
4
|
+
* @link http://www.phpied.com/rgb-color-parser-in-javascript/
|
5
|
+
* Use it if you like it
|
6
|
+
*
|
7
|
+
*/
|
8
|
+
function RGBColor(color_string)
|
9
|
+
{
|
10
|
+
this.ok = false;
|
11
|
+
|
12
|
+
// strip any leading #
|
13
|
+
if (color_string.charAt(0) == '#') { // remove # if any
|
14
|
+
color_string = color_string.substr(1,6);
|
15
|
+
}
|
16
|
+
|
17
|
+
color_string = color_string.replace(/ /g,'');
|
18
|
+
color_string = color_string.toLowerCase();
|
19
|
+
|
20
|
+
// before getting into regexps, try simple matches
|
21
|
+
// and overwrite the input
|
22
|
+
var simple_colors = {
|
23
|
+
aliceblue: 'f0f8ff',
|
24
|
+
antiquewhite: 'faebd7',
|
25
|
+
aqua: '00ffff',
|
26
|
+
aquamarine: '7fffd4',
|
27
|
+
azure: 'f0ffff',
|
28
|
+
beige: 'f5f5dc',
|
29
|
+
bisque: 'ffe4c4',
|
30
|
+
black: '000000',
|
31
|
+
blanchedalmond: 'ffebcd',
|
32
|
+
blue: '0000ff',
|
33
|
+
blueviolet: '8a2be2',
|
34
|
+
brown: 'a52a2a',
|
35
|
+
burlywood: 'deb887',
|
36
|
+
cadetblue: '5f9ea0',
|
37
|
+
chartreuse: '7fff00',
|
38
|
+
chocolate: 'd2691e',
|
39
|
+
coral: 'ff7f50',
|
40
|
+
cornflowerblue: '6495ed',
|
41
|
+
cornsilk: 'fff8dc',
|
42
|
+
crimson: 'dc143c',
|
43
|
+
cyan: '00ffff',
|
44
|
+
darkblue: '00008b',
|
45
|
+
darkcyan: '008b8b',
|
46
|
+
darkgoldenrod: 'b8860b',
|
47
|
+
darkgray: 'a9a9a9',
|
48
|
+
darkgreen: '006400',
|
49
|
+
darkkhaki: 'bdb76b',
|
50
|
+
darkmagenta: '8b008b',
|
51
|
+
darkolivegreen: '556b2f',
|
52
|
+
darkorange: 'ff8c00',
|
53
|
+
darkorchid: '9932cc',
|
54
|
+
darkred: '8b0000',
|
55
|
+
darksalmon: 'e9967a',
|
56
|
+
darkseagreen: '8fbc8f',
|
57
|
+
darkslateblue: '483d8b',
|
58
|
+
darkslategray: '2f4f4f',
|
59
|
+
darkturquoise: '00ced1',
|
60
|
+
darkviolet: '9400d3',
|
61
|
+
deeppink: 'ff1493',
|
62
|
+
deepskyblue: '00bfff',
|
63
|
+
dimgray: '696969',
|
64
|
+
dodgerblue: '1e90ff',
|
65
|
+
feldspar: 'd19275',
|
66
|
+
firebrick: 'b22222',
|
67
|
+
floralwhite: 'fffaf0',
|
68
|
+
forestgreen: '228b22',
|
69
|
+
fuchsia: 'ff00ff',
|
70
|
+
gainsboro: 'dcdcdc',
|
71
|
+
ghostwhite: 'f8f8ff',
|
72
|
+
gold: 'ffd700',
|
73
|
+
goldenrod: 'daa520',
|
74
|
+
gray: '808080',
|
75
|
+
green: '008000',
|
76
|
+
greenyellow: 'adff2f',
|
77
|
+
honeydew: 'f0fff0',
|
78
|
+
hotpink: 'ff69b4',
|
79
|
+
indianred : 'cd5c5c',
|
80
|
+
indigo : '4b0082',
|
81
|
+
ivory: 'fffff0',
|
82
|
+
khaki: 'f0e68c',
|
83
|
+
lavender: 'e6e6fa',
|
84
|
+
lavenderblush: 'fff0f5',
|
85
|
+
lawngreen: '7cfc00',
|
86
|
+
lemonchiffon: 'fffacd',
|
87
|
+
lightblue: 'add8e6',
|
88
|
+
lightcoral: 'f08080',
|
89
|
+
lightcyan: 'e0ffff',
|
90
|
+
lightgoldenrodyellow: 'fafad2',
|
91
|
+
lightgrey: 'd3d3d3',
|
92
|
+
lightgreen: '90ee90',
|
93
|
+
lightpink: 'ffb6c1',
|
94
|
+
lightsalmon: 'ffa07a',
|
95
|
+
lightseagreen: '20b2aa',
|
96
|
+
lightskyblue: '87cefa',
|
97
|
+
lightslateblue: '8470ff',
|
98
|
+
lightslategray: '778899',
|
99
|
+
lightsteelblue: 'b0c4de',
|
100
|
+
lightyellow: 'ffffe0',
|
101
|
+
lime: '00ff00',
|
102
|
+
limegreen: '32cd32',
|
103
|
+
linen: 'faf0e6',
|
104
|
+
magenta: 'ff00ff',
|
105
|
+
maroon: '800000',
|
106
|
+
mediumaquamarine: '66cdaa',
|
107
|
+
mediumblue: '0000cd',
|
108
|
+
mediumorchid: 'ba55d3',
|
109
|
+
mediumpurple: '9370d8',
|
110
|
+
mediumseagreen: '3cb371',
|
111
|
+
mediumslateblue: '7b68ee',
|
112
|
+
mediumspringgreen: '00fa9a',
|
113
|
+
mediumturquoise: '48d1cc',
|
114
|
+
mediumvioletred: 'c71585',
|
115
|
+
midnightblue: '191970',
|
116
|
+
mintcream: 'f5fffa',
|
117
|
+
mistyrose: 'ffe4e1',
|
118
|
+
moccasin: 'ffe4b5',
|
119
|
+
navajowhite: 'ffdead',
|
120
|
+
navy: '000080',
|
121
|
+
oldlace: 'fdf5e6',
|
122
|
+
olive: '808000',
|
123
|
+
olivedrab: '6b8e23',
|
124
|
+
orange: 'ffa500',
|
125
|
+
orangered: 'ff4500',
|
126
|
+
orchid: 'da70d6',
|
127
|
+
palegoldenrod: 'eee8aa',
|
128
|
+
palegreen: '98fb98',
|
129
|
+
paleturquoise: 'afeeee',
|
130
|
+
palevioletred: 'd87093',
|
131
|
+
papayawhip: 'ffefd5',
|
132
|
+
peachpuff: 'ffdab9',
|
133
|
+
peru: 'cd853f',
|
134
|
+
pink: 'ffc0cb',
|
135
|
+
plum: 'dda0dd',
|
136
|
+
powderblue: 'b0e0e6',
|
137
|
+
purple: '800080',
|
138
|
+
red: 'ff0000',
|
139
|
+
rosybrown: 'bc8f8f',
|
140
|
+
royalblue: '4169e1',
|
141
|
+
saddlebrown: '8b4513',
|
142
|
+
salmon: 'fa8072',
|
143
|
+
sandybrown: 'f4a460',
|
144
|
+
seagreen: '2e8b57',
|
145
|
+
seashell: 'fff5ee',
|
146
|
+
sienna: 'a0522d',
|
147
|
+
silver: 'c0c0c0',
|
148
|
+
skyblue: '87ceeb',
|
149
|
+
slateblue: '6a5acd',
|
150
|
+
slategray: '708090',
|
151
|
+
snow: 'fffafa',
|
152
|
+
springgreen: '00ff7f',
|
153
|
+
steelblue: '4682b4',
|
154
|
+
tan: 'd2b48c',
|
155
|
+
teal: '008080',
|
156
|
+
thistle: 'd8bfd8',
|
157
|
+
tomato: 'ff6347',
|
158
|
+
turquoise: '40e0d0',
|
159
|
+
violet: 'ee82ee',
|
160
|
+
violetred: 'd02090',
|
161
|
+
wheat: 'f5deb3',
|
162
|
+
white: 'ffffff',
|
163
|
+
whitesmoke: 'f5f5f5',
|
164
|
+
yellow: 'ffff00',
|
165
|
+
yellowgreen: '9acd32'
|
166
|
+
};
|
167
|
+
for (var key in simple_colors) {
|
168
|
+
if (color_string == key) {
|
169
|
+
color_string = simple_colors[key];
|
170
|
+
}
|
171
|
+
}
|
172
|
+
// emd of simple type-in colors
|
173
|
+
|
174
|
+
// array of color definition objects
|
175
|
+
var color_defs = [
|
176
|
+
{
|
177
|
+
re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
|
178
|
+
example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
|
179
|
+
process: function (bits){
|
180
|
+
return [
|
181
|
+
parseInt(bits[1]),
|
182
|
+
parseInt(bits[2]),
|
183
|
+
parseInt(bits[3])
|
184
|
+
];
|
185
|
+
}
|
186
|
+
},
|
187
|
+
{
|
188
|
+
re: /^(\w{2})(\w{2})(\w{2})$/,
|
189
|
+
example: ['#00ff00', '336699'],
|
190
|
+
process: function (bits){
|
191
|
+
return [
|
192
|
+
parseInt(bits[1], 16),
|
193
|
+
parseInt(bits[2], 16),
|
194
|
+
parseInt(bits[3], 16)
|
195
|
+
];
|
196
|
+
}
|
197
|
+
},
|
198
|
+
{
|
199
|
+
re: /^(\w{1})(\w{1})(\w{1})$/,
|
200
|
+
example: ['#fb0', 'f0f'],
|
201
|
+
process: function (bits){
|
202
|
+
return [
|
203
|
+
parseInt(bits[1] + bits[1], 16),
|
204
|
+
parseInt(bits[2] + bits[2], 16),
|
205
|
+
parseInt(bits[3] + bits[3], 16)
|
206
|
+
];
|
207
|
+
}
|
208
|
+
}
|
209
|
+
];
|
210
|
+
|
211
|
+
// search through the definitions to find a match
|
212
|
+
for (var i = 0; i < color_defs.length; i++) {
|
213
|
+
var re = color_defs[i].re;
|
214
|
+
var processor = color_defs[i].process;
|
215
|
+
var bits = re.exec(color_string);
|
216
|
+
if (bits) {
|
217
|
+
channels = processor(bits);
|
218
|
+
this.r = channels[0];
|
219
|
+
this.g = channels[1];
|
220
|
+
this.b = channels[2];
|
221
|
+
this.ok = true;
|
222
|
+
}
|
223
|
+
|
224
|
+
}
|
225
|
+
|
226
|
+
// validate/cleanup values
|
227
|
+
this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
|
228
|
+
this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
|
229
|
+
this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
|
230
|
+
|
231
|
+
// some getters
|
232
|
+
this.toRGB = function () {
|
233
|
+
return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
|
234
|
+
}
|
235
|
+
this.toHex = function () {
|
236
|
+
var r = this.r.toString(16);
|
237
|
+
var g = this.g.toString(16);
|
238
|
+
var b = this.b.toString(16);
|
239
|
+
if (r.length == 1) r = '0' + r;
|
240
|
+
if (g.length == 1) g = '0' + g;
|
241
|
+
if (b.length == 1) b = '0' + b;
|
242
|
+
return '#' + r + g + b;
|
243
|
+
}
|
244
|
+
|
245
|
+
// help
|
246
|
+
this.getHelpXML = function () {
|
247
|
+
|
248
|
+
var examples = new Array();
|
249
|
+
// add regexps
|
250
|
+
for (var i = 0; i < color_defs.length; i++) {
|
251
|
+
var example = color_defs[i].example;
|
252
|
+
for (var j = 0; j < example.length; j++) {
|
253
|
+
examples[examples.length] = example[j];
|
254
|
+
}
|
255
|
+
}
|
256
|
+
// add type-in colors
|
257
|
+
for (var sc in simple_colors) {
|
258
|
+
examples[examples.length] = sc;
|
259
|
+
}
|
260
|
+
|
261
|
+
var xml = document.createElement('ul');
|
262
|
+
xml.setAttribute('id', 'rgbcolor-examples');
|
263
|
+
for (var i = 0; i < examples.length; i++) {
|
264
|
+
try {
|
265
|
+
var list_item = document.createElement('li');
|
266
|
+
var list_color = new RGBColor(examples[i]);
|
267
|
+
var example_div = document.createElement('div');
|
268
|
+
example_div.style.cssText =
|
269
|
+
'margin: 3px; '
|
270
|
+
+ 'border: 1px solid black; '
|
271
|
+
+ 'background:' + list_color.toHex() + '; '
|
272
|
+
+ 'color:' + list_color.toHex()
|
273
|
+
;
|
274
|
+
example_div.appendChild(document.createTextNode('test'));
|
275
|
+
var list_item_value = document.createTextNode(
|
276
|
+
' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex()
|
277
|
+
);
|
278
|
+
list_item.appendChild(example_div);
|
279
|
+
list_item.appendChild(list_item_value);
|
280
|
+
xml.appendChild(list_item);
|
281
|
+
|
282
|
+
} catch(e){}
|
283
|
+
}
|
284
|
+
return xml;
|
285
|
+
|
286
|
+
}
|
287
|
+
|
288
|
+
}
|
289
|
+
|
290
|
+
/**
|
291
|
+
* @license canvg.js - Javascript SVG parser and renderer on Canvas
|
292
|
+
* MIT Licensed
|
293
|
+
* Gabe Lerner (gabelerner@gmail.com)
|
294
|
+
* http://code.google.com/p/canvg/
|
295
|
+
*
|
296
|
+
* Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
|
297
|
+
*
|
298
|
+
*/
|
299
|
+
if(!window.console) {
|
300
|
+
window.console = {};
|
301
|
+
window.console.log = function(str) {};
|
302
|
+
window.console.dir = function(str) {};
|
303
|
+
}
|
304
|
+
|
305
|
+
if(!Array.prototype.indexOf){
|
306
|
+
Array.prototype.indexOf = function(obj){
|
307
|
+
for(var i=0; i<this.length; i++){
|
308
|
+
if(this[i]==obj){
|
309
|
+
return i;
|
310
|
+
}
|
311
|
+
}
|
312
|
+
return -1;
|
313
|
+
}
|
314
|
+
}
|
315
|
+
|
316
|
+
(function(){
|
317
|
+
// canvg(target, s)
|
318
|
+
// empty parameters: replace all 'svg' elements on page with 'canvas' elements
|
319
|
+
// target: canvas element or the id of a canvas element
|
320
|
+
// s: svg string, url to svg file, or xml document
|
321
|
+
// opts: optional hash of options
|
322
|
+
// ignoreMouse: true => ignore mouse events
|
323
|
+
// ignoreAnimation: true => ignore animations
|
324
|
+
// ignoreDimensions: true => does not try to resize canvas
|
325
|
+
// ignoreClear: true => does not clear canvas
|
326
|
+
// offsetX: int => draws at a x offset
|
327
|
+
// offsetY: int => draws at a y offset
|
328
|
+
// scaleWidth: int => scales horizontally to width
|
329
|
+
// scaleHeight: int => scales vertically to height
|
330
|
+
// renderCallback: function => will call the function after the first render is completed
|
331
|
+
// forceRedraw: function => will call the function on every frame, if it returns true, will redraw
|
332
|
+
this.canvg = function (target, s, opts) {
|
333
|
+
// no parameters
|
334
|
+
if (target == null && s == null && opts == null) {
|
335
|
+
var svgTags = document.getElementsByTagName('svg');
|
336
|
+
for (var i=0; i<svgTags.length; i++) {
|
337
|
+
var svgTag = svgTags[i];
|
338
|
+
var c = document.createElement('canvas');
|
339
|
+
c.width = svgTag.clientWidth;
|
340
|
+
c.height = svgTag.clientHeight;
|
341
|
+
svgTag.parentNode.insertBefore(c, svgTag);
|
342
|
+
svgTag.parentNode.removeChild(svgTag);
|
343
|
+
var div = document.createElement('div');
|
344
|
+
div.appendChild(svgTag);
|
345
|
+
canvg(c, div.innerHTML);
|
346
|
+
}
|
347
|
+
return;
|
348
|
+
}
|
349
|
+
opts = opts || {};
|
350
|
+
|
351
|
+
if (typeof target == 'string') {
|
352
|
+
target = document.getElementById(target);
|
353
|
+
}
|
354
|
+
|
355
|
+
// reuse class per canvas
|
356
|
+
var svg;
|
357
|
+
if (target.svg == null) {
|
358
|
+
svg = build();
|
359
|
+
target.svg = svg;
|
360
|
+
}
|
361
|
+
else {
|
362
|
+
svg = target.svg;
|
363
|
+
svg.stop();
|
364
|
+
}
|
365
|
+
svg.opts = opts;
|
366
|
+
|
367
|
+
var ctx = target.getContext('2d');
|
368
|
+
if (typeof(s.documentElement) != 'undefined') {
|
369
|
+
// load from xml doc
|
370
|
+
svg.loadXmlDoc(ctx, s);
|
371
|
+
}
|
372
|
+
else if (s.substr(0,1) == '<') {
|
373
|
+
// load from xml string
|
374
|
+
svg.loadXml(ctx, s);
|
375
|
+
}
|
376
|
+
else {
|
377
|
+
// load from url
|
378
|
+
svg.load(ctx, s);
|
379
|
+
}
|
380
|
+
}
|
381
|
+
|
382
|
+
function build() {
|
383
|
+
var svg = { };
|
384
|
+
|
385
|
+
svg.FRAMERATE = 30;
|
386
|
+
svg.MAX_VIRTUAL_PIXELS = 30000;
|
387
|
+
|
388
|
+
// globals
|
389
|
+
svg.init = function(ctx) {
|
390
|
+
svg.Definitions = {};
|
391
|
+
svg.Styles = {};
|
392
|
+
svg.Animations = [];
|
393
|
+
svg.Images = [];
|
394
|
+
svg.ctx = ctx;
|
395
|
+
svg.ViewPort = new (function () {
|
396
|
+
this.viewPorts = [];
|
397
|
+
this.Clear = function() { this.viewPorts = []; }
|
398
|
+
this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); }
|
399
|
+
this.RemoveCurrent = function() { this.viewPorts.pop(); }
|
400
|
+
this.Current = function() { return this.viewPorts[this.viewPorts.length - 1]; }
|
401
|
+
this.width = function() { return this.Current().width; }
|
402
|
+
this.height = function() { return this.Current().height; }
|
403
|
+
this.ComputeSize = function(d) {
|
404
|
+
if (d != null && typeof(d) == 'number') return d;
|
405
|
+
if (d == 'x') return this.width();
|
406
|
+
if (d == 'y') return this.height();
|
407
|
+
return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2);
|
408
|
+
}
|
409
|
+
});
|
410
|
+
}
|
411
|
+
svg.init();
|
412
|
+
|
413
|
+
// images loaded
|
414
|
+
svg.ImagesLoaded = function() {
|
415
|
+
for (var i=0; i<svg.Images.length; i++) {
|
416
|
+
if (!svg.Images[i].loaded) return false;
|
417
|
+
}
|
418
|
+
return true;
|
419
|
+
}
|
420
|
+
|
421
|
+
// trim
|
422
|
+
svg.trim = function(s) { return s.replace(/^\s+|\s+$/g, ''); }
|
423
|
+
|
424
|
+
// compress spaces
|
425
|
+
svg.compressSpaces = function(s) { return s.replace(/[\s\r\t\n]+/gm,' '); }
|
426
|
+
|
427
|
+
// ajax
|
428
|
+
svg.ajax = function(url) {
|
429
|
+
var AJAX;
|
430
|
+
if(window.XMLHttpRequest){AJAX=new XMLHttpRequest();}
|
431
|
+
else{AJAX=new ActiveXObject('Microsoft.XMLHTTP');}
|
432
|
+
if(AJAX){
|
433
|
+
AJAX.open('GET',url,false);
|
434
|
+
AJAX.send(null);
|
435
|
+
return AJAX.responseText;
|
436
|
+
}
|
437
|
+
return null;
|
438
|
+
}
|
439
|
+
|
440
|
+
// parse xml
|
441
|
+
svg.parseXml = function(xml) {
|
442
|
+
if (window.DOMParser)
|
443
|
+
{
|
444
|
+
var parser = new DOMParser();
|
445
|
+
return parser.parseFromString(xml, 'text/xml');
|
446
|
+
}
|
447
|
+
else
|
448
|
+
{
|
449
|
+
xml = xml.replace(/<!DOCTYPE svg[^>]*>/, '');
|
450
|
+
var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
|
451
|
+
xmlDoc.async = 'false';
|
452
|
+
xmlDoc.loadXML(xml);
|
453
|
+
return xmlDoc;
|
454
|
+
}
|
455
|
+
}
|
456
|
+
|
457
|
+
svg.Property = function(name, value) {
|
458
|
+
this.name = name;
|
459
|
+
this.value = value;
|
460
|
+
|
461
|
+
this.hasValue = function() {
|
462
|
+
return (this.value != null && this.value !== '');
|
463
|
+
}
|
464
|
+
|
465
|
+
// return the numerical value of the property
|
466
|
+
this.numValue = function() {
|
467
|
+
if (!this.hasValue()) return 0;
|
468
|
+
|
469
|
+
var n = parseFloat(this.value);
|
470
|
+
if ((this.value + '').match(/%$/)) {
|
471
|
+
n = n / 100.0;
|
472
|
+
}
|
473
|
+
return n;
|
474
|
+
}
|
475
|
+
|
476
|
+
this.valueOrDefault = function(def) {
|
477
|
+
if (this.hasValue()) return this.value;
|
478
|
+
return def;
|
479
|
+
}
|
480
|
+
|
481
|
+
this.numValueOrDefault = function(def) {
|
482
|
+
if (this.hasValue()) return this.numValue();
|
483
|
+
return def;
|
484
|
+
}
|
485
|
+
|
486
|
+
/* EXTENSIONS */
|
487
|
+
var that = this;
|
488
|
+
|
489
|
+
// color extensions
|
490
|
+
this.Color = {
|
491
|
+
// augment the current color value with the opacity
|
492
|
+
addOpacity: function(opacity) {
|
493
|
+
var newValue = that.value;
|
494
|
+
if (opacity != null && opacity != '') {
|
495
|
+
var color = new RGBColor(that.value);
|
496
|
+
if (color.ok) {
|
497
|
+
newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacity + ')';
|
498
|
+
}
|
499
|
+
}
|
500
|
+
return new svg.Property(that.name, newValue);
|
501
|
+
}
|
502
|
+
}
|
503
|
+
|
504
|
+
// definition extensions
|
505
|
+
this.Definition = {
|
506
|
+
// get the definition from the definitions table
|
507
|
+
getDefinition: function() {
|
508
|
+
var name = that.value.replace(/^(url\()?#([^\)]+)\)?$/, '$2');
|
509
|
+
return svg.Definitions[name];
|
510
|
+
},
|
511
|
+
|
512
|
+
isUrl: function() {
|
513
|
+
return that.value.indexOf('url(') == 0
|
514
|
+
},
|
515
|
+
|
516
|
+
getFillStyle: function(e) {
|
517
|
+
var def = this.getDefinition();
|
518
|
+
|
519
|
+
// gradient
|
520
|
+
if (def != null && def.createGradient) {
|
521
|
+
return def.createGradient(svg.ctx, e);
|
522
|
+
}
|
523
|
+
|
524
|
+
// pattern
|
525
|
+
if (def != null && def.createPattern) {
|
526
|
+
return def.createPattern(svg.ctx, e);
|
527
|
+
}
|
528
|
+
|
529
|
+
return null;
|
530
|
+
}
|
531
|
+
}
|
532
|
+
|
533
|
+
// length extensions
|
534
|
+
this.Length = {
|
535
|
+
DPI: function(viewPort) {
|
536
|
+
return 96.0; // TODO: compute?
|
537
|
+
},
|
538
|
+
|
539
|
+
EM: function(viewPort) {
|
540
|
+
var em = 12;
|
541
|
+
|
542
|
+
var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
|
543
|
+
if (fontSize.hasValue()) em = fontSize.Length.toPixels(viewPort);
|
544
|
+
|
545
|
+
return em;
|
546
|
+
},
|
547
|
+
|
548
|
+
// get the length as pixels
|
549
|
+
toPixels: function(viewPort) {
|
550
|
+
if (!that.hasValue()) return 0;
|
551
|
+
var s = that.value+'';
|
552
|
+
if (s.match(/em$/)) return that.numValue() * this.EM(viewPort);
|
553
|
+
if (s.match(/ex$/)) return that.numValue() * this.EM(viewPort) / 2.0;
|
554
|
+
if (s.match(/px$/)) return that.numValue();
|
555
|
+
if (s.match(/pt$/)) return that.numValue() * 1.25;
|
556
|
+
if (s.match(/pc$/)) return that.numValue() * 15;
|
557
|
+
if (s.match(/cm$/)) return that.numValue() * this.DPI(viewPort) / 2.54;
|
558
|
+
if (s.match(/mm$/)) return that.numValue() * this.DPI(viewPort) / 25.4;
|
559
|
+
if (s.match(/in$/)) return that.numValue() * this.DPI(viewPort);
|
560
|
+
if (s.match(/%$/)) return that.numValue() * svg.ViewPort.ComputeSize(viewPort);
|
561
|
+
return that.numValue();
|
562
|
+
}
|
563
|
+
}
|
564
|
+
|
565
|
+
// time extensions
|
566
|
+
this.Time = {
|
567
|
+
// get the time as milliseconds
|
568
|
+
toMilliseconds: function() {
|
569
|
+
if (!that.hasValue()) return 0;
|
570
|
+
var s = that.value+'';
|
571
|
+
if (s.match(/s$/)) return that.numValue() * 1000;
|
572
|
+
if (s.match(/ms$/)) return that.numValue();
|
573
|
+
return that.numValue();
|
574
|
+
}
|
575
|
+
}
|
576
|
+
|
577
|
+
// angle extensions
|
578
|
+
this.Angle = {
|
579
|
+
// get the angle as radians
|
580
|
+
toRadians: function() {
|
581
|
+
if (!that.hasValue()) return 0;
|
582
|
+
var s = that.value+'';
|
583
|
+
if (s.match(/deg$/)) return that.numValue() * (Math.PI / 180.0);
|
584
|
+
if (s.match(/grad$/)) return that.numValue() * (Math.PI / 200.0);
|
585
|
+
if (s.match(/rad$/)) return that.numValue();
|
586
|
+
return that.numValue() * (Math.PI / 180.0);
|
587
|
+
}
|
588
|
+
}
|
589
|
+
}
|
590
|
+
|
591
|
+
// fonts
|
592
|
+
svg.Font = new (function() {
|
593
|
+
this.Styles = ['normal','italic','oblique','inherit'];
|
594
|
+
this.Variants = ['normal','small-caps','inherit'];
|
595
|
+
this.Weights = ['normal','bold','bolder','lighter','100','200','300','400','500','600','700','800','900','inherit'];
|
596
|
+
|
597
|
+
this.CreateFont = function(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) {
|
598
|
+
var f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font);
|
599
|
+
return {
|
600
|
+
fontFamily: fontFamily || f.fontFamily,
|
601
|
+
fontSize: fontSize || f.fontSize,
|
602
|
+
fontStyle: fontStyle || f.fontStyle,
|
603
|
+
fontWeight: fontWeight || f.fontWeight,
|
604
|
+
fontVariant: fontVariant || f.fontVariant,
|
605
|
+
toString: function () { return [this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily].join(' ') }
|
606
|
+
}
|
607
|
+
}
|
608
|
+
|
609
|
+
var that = this;
|
610
|
+
this.Parse = function(s) {
|
611
|
+
var f = {};
|
612
|
+
var d = svg.trim(svg.compressSpaces(s || '')).split(' ');
|
613
|
+
var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false }
|
614
|
+
var ff = '';
|
615
|
+
for (var i=0; i<d.length; i++) {
|
616
|
+
if (!set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontStyle = d[i]; set.fontStyle = true; }
|
617
|
+
else if (!set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontVariant = d[i]; set.fontStyle = set.fontVariant = true; }
|
618
|
+
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; }
|
619
|
+
else if (!set.fontSize) { if (d[i] != 'inherit') f.fontSize = d[i].split('/')[0]; set.fontStyle = set.fontVariant = set.fontWeight = set.fontSize = true; }
|
620
|
+
else { if (d[i] != 'inherit') ff += d[i]; }
|
621
|
+
} if (ff != '') f.fontFamily = ff;
|
622
|
+
return f;
|
623
|
+
}
|
624
|
+
});
|
625
|
+
|
626
|
+
// points and paths
|
627
|
+
svg.ToNumberArray = function(s) {
|
628
|
+
var a = svg.trim(svg.compressSpaces((s || '').replace(/,/g, ' '))).split(' ');
|
629
|
+
for (var i=0; i<a.length; i++) {
|
630
|
+
a[i] = parseFloat(a[i]);
|
631
|
+
}
|
632
|
+
return a;
|
633
|
+
}
|
634
|
+
svg.Point = function(x, y) {
|
635
|
+
this.x = x;
|
636
|
+
this.y = y;
|
637
|
+
|
638
|
+
this.angleTo = function(p) {
|
639
|
+
return Math.atan2(p.y - this.y, p.x - this.x);
|
640
|
+
}
|
641
|
+
|
642
|
+
this.applyTransform = function(v) {
|
643
|
+
var xp = this.x * v[0] + this.y * v[2] + v[4];
|
644
|
+
var yp = this.x * v[1] + this.y * v[3] + v[5];
|
645
|
+
this.x = xp;
|
646
|
+
this.y = yp;
|
647
|
+
}
|
648
|
+
}
|
649
|
+
svg.CreatePoint = function(s) {
|
650
|
+
var a = svg.ToNumberArray(s);
|
651
|
+
return new svg.Point(a[0], a[1]);
|
652
|
+
}
|
653
|
+
svg.CreatePath = function(s) {
|
654
|
+
var a = svg.ToNumberArray(s);
|
655
|
+
var path = [];
|
656
|
+
for (var i=0; i<a.length; i+=2) {
|
657
|
+
path.push(new svg.Point(a[i], a[i+1]));
|
658
|
+
}
|
659
|
+
return path;
|
660
|
+
}
|
661
|
+
|
662
|
+
// bounding box
|
663
|
+
svg.BoundingBox = function(x1, y1, x2, y2) { // pass in initial points if you want
|
664
|
+
this.x1 = Number.NaN;
|
665
|
+
this.y1 = Number.NaN;
|
666
|
+
this.x2 = Number.NaN;
|
667
|
+
this.y2 = Number.NaN;
|
668
|
+
|
669
|
+
this.x = function() { return this.x1; }
|
670
|
+
this.y = function() { return this.y1; }
|
671
|
+
this.width = function() { return this.x2 - this.x1; }
|
672
|
+
this.height = function() { return this.y2 - this.y1; }
|
673
|
+
|
674
|
+
this.addPoint = function(x, y) {
|
675
|
+
if (x != null) {
|
676
|
+
if (isNaN(this.x1) || isNaN(this.x2)) {
|
677
|
+
this.x1 = x;
|
678
|
+
this.x2 = x;
|
679
|
+
}
|
680
|
+
if (x < this.x1) this.x1 = x;
|
681
|
+
if (x > this.x2) this.x2 = x;
|
682
|
+
}
|
683
|
+
|
684
|
+
if (y != null) {
|
685
|
+
if (isNaN(this.y1) || isNaN(this.y2)) {
|
686
|
+
this.y1 = y;
|
687
|
+
this.y2 = y;
|
688
|
+
}
|
689
|
+
if (y < this.y1) this.y1 = y;
|
690
|
+
if (y > this.y2) this.y2 = y;
|
691
|
+
}
|
692
|
+
}
|
693
|
+
this.addX = function(x) { this.addPoint(x, null); }
|
694
|
+
this.addY = function(y) { this.addPoint(null, y); }
|
695
|
+
|
696
|
+
this.addBoundingBox = function(bb) {
|
697
|
+
this.addPoint(bb.x1, bb.y1);
|
698
|
+
this.addPoint(bb.x2, bb.y2);
|
699
|
+
}
|
700
|
+
|
701
|
+
this.addQuadraticCurve = function(p0x, p0y, p1x, p1y, p2x, p2y) {
|
702
|
+
var cp1x = p0x + 2/3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
|
703
|
+
var cp1y = p0y + 2/3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
|
704
|
+
var cp2x = cp1x + 1/3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
|
705
|
+
var cp2y = cp1y + 1/3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
|
706
|
+
this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y);
|
707
|
+
}
|
708
|
+
|
709
|
+
this.addBezierCurve = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {
|
710
|
+
// from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
|
711
|
+
var p0 = [p0x, p0y], p1 = [p1x, p1y], p2 = [p2x, p2y], p3 = [p3x, p3y];
|
712
|
+
this.addPoint(p0[0], p0[1]);
|
713
|
+
this.addPoint(p3[0], p3[1]);
|
714
|
+
|
715
|
+
for (i=0; i<=1; i++) {
|
716
|
+
var f = function(t) {
|
717
|
+
return Math.pow(1-t, 3) * p0[i]
|
718
|
+
+ 3 * Math.pow(1-t, 2) * t * p1[i]
|
719
|
+
+ 3 * (1-t) * Math.pow(t, 2) * p2[i]
|
720
|
+
+ Math.pow(t, 3) * p3[i];
|
721
|
+
}
|
722
|
+
|
723
|
+
var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
|
724
|
+
var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
|
725
|
+
var c = 3 * p1[i] - 3 * p0[i];
|
726
|
+
|
727
|
+
if (a == 0) {
|
728
|
+
if (b == 0) continue;
|
729
|
+
var t = -c / b;
|
730
|
+
if (0 < t && t < 1) {
|
731
|
+
if (i == 0) this.addX(f(t));
|
732
|
+
if (i == 1) this.addY(f(t));
|
733
|
+
}
|
734
|
+
continue;
|
735
|
+
}
|
736
|
+
|
737
|
+
var b2ac = Math.pow(b, 2) - 4 * c * a;
|
738
|
+
if (b2ac < 0) continue;
|
739
|
+
var t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
|
740
|
+
if (0 < t1 && t1 < 1) {
|
741
|
+
if (i == 0) this.addX(f(t1));
|
742
|
+
if (i == 1) this.addY(f(t1));
|
743
|
+
}
|
744
|
+
var t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
|
745
|
+
if (0 < t2 && t2 < 1) {
|
746
|
+
if (i == 0) this.addX(f(t2));
|
747
|
+
if (i == 1) this.addY(f(t2));
|
748
|
+
}
|
749
|
+
}
|
750
|
+
}
|
751
|
+
|
752
|
+
this.isPointInBox = function(x, y) {
|
753
|
+
return (this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2);
|
754
|
+
}
|
755
|
+
|
756
|
+
this.addPoint(x1, y1);
|
757
|
+
this.addPoint(x2, y2);
|
758
|
+
}
|
759
|
+
|
760
|
+
// transforms
|
761
|
+
svg.Transform = function(v) {
|
762
|
+
var that = this;
|
763
|
+
this.Type = {}
|
764
|
+
|
765
|
+
// translate
|
766
|
+
this.Type.translate = function(s) {
|
767
|
+
this.p = svg.CreatePoint(s);
|
768
|
+
this.apply = function(ctx) {
|
769
|
+
ctx.translate(this.p.x || 0.0, this.p.y || 0.0);
|
770
|
+
}
|
771
|
+
this.applyToPoint = function(p) {
|
772
|
+
p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
|
773
|
+
}
|
774
|
+
}
|
775
|
+
|
776
|
+
// rotate
|
777
|
+
this.Type.rotate = function(s) {
|
778
|
+
var a = svg.ToNumberArray(s);
|
779
|
+
this.angle = new svg.Property('angle', a[0]);
|
780
|
+
this.cx = a[1] || 0;
|
781
|
+
this.cy = a[2] || 0;
|
782
|
+
this.apply = function(ctx) {
|
783
|
+
ctx.translate(this.cx, this.cy);
|
784
|
+
ctx.rotate(this.angle.Angle.toRadians());
|
785
|
+
ctx.translate(-this.cx, -this.cy);
|
786
|
+
}
|
787
|
+
this.applyToPoint = function(p) {
|
788
|
+
var a = this.angle.Angle.toRadians();
|
789
|
+
p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
|
790
|
+
p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
|
791
|
+
p.applyTransform([1, 0, 0, 1, -this.p.x || 0.0, -this.p.y || 0.0]);
|
792
|
+
}
|
793
|
+
}
|
794
|
+
|
795
|
+
this.Type.scale = function(s) {
|
796
|
+
this.p = svg.CreatePoint(s);
|
797
|
+
this.apply = function(ctx) {
|
798
|
+
ctx.scale(this.p.x || 1.0, this.p.y || this.p.x || 1.0);
|
799
|
+
}
|
800
|
+
this.applyToPoint = function(p) {
|
801
|
+
p.applyTransform([this.p.x || 0.0, 0, 0, this.p.y || 0.0, 0, 0]);
|
802
|
+
}
|
803
|
+
}
|
804
|
+
|
805
|
+
this.Type.matrix = function(s) {
|
806
|
+
this.m = svg.ToNumberArray(s);
|
807
|
+
this.apply = function(ctx) {
|
808
|
+
ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
|
809
|
+
}
|
810
|
+
this.applyToPoint = function(p) {
|
811
|
+
p.applyTransform(this.m);
|
812
|
+
}
|
813
|
+
}
|
814
|
+
|
815
|
+
this.Type.SkewBase = function(s) {
|
816
|
+
this.base = that.Type.matrix;
|
817
|
+
this.base(s);
|
818
|
+
this.angle = new svg.Property('angle', s);
|
819
|
+
}
|
820
|
+
this.Type.SkewBase.prototype = new this.Type.matrix;
|
821
|
+
|
822
|
+
this.Type.skewX = function(s) {
|
823
|
+
this.base = that.Type.SkewBase;
|
824
|
+
this.base(s);
|
825
|
+
this.m = [1, 0, Math.tan(this.angle.Angle.toRadians()), 1, 0, 0];
|
826
|
+
}
|
827
|
+
this.Type.skewX.prototype = new this.Type.SkewBase;
|
828
|
+
|
829
|
+
this.Type.skewY = function(s) {
|
830
|
+
this.base = that.Type.SkewBase;
|
831
|
+
this.base(s);
|
832
|
+
this.m = [1, Math.tan(this.angle.Angle.toRadians()), 0, 1, 0, 0];
|
833
|
+
}
|
834
|
+
this.Type.skewY.prototype = new this.Type.SkewBase;
|
835
|
+
|
836
|
+
this.transforms = [];
|
837
|
+
|
838
|
+
this.apply = function(ctx) {
|
839
|
+
for (var i=0; i<this.transforms.length; i++) {
|
840
|
+
this.transforms[i].apply(ctx);
|
841
|
+
}
|
842
|
+
}
|
843
|
+
|
844
|
+
this.applyToPoint = function(p) {
|
845
|
+
for (var i=0; i<this.transforms.length; i++) {
|
846
|
+
this.transforms[i].applyToPoint(p);
|
847
|
+
}
|
848
|
+
}
|
849
|
+
|
850
|
+
var data = svg.trim(svg.compressSpaces(v)).split(/\s(?=[a-z])/);
|
851
|
+
for (var i=0; i<data.length; i++) {
|
852
|
+
var type = data[i].split('(')[0];
|
853
|
+
var s = data[i].split('(')[1].replace(')','');
|
854
|
+
var transform = new this.Type[type](s);
|
855
|
+
this.transforms.push(transform);
|
856
|
+
}
|
857
|
+
}
|
858
|
+
|
859
|
+
// aspect ratio
|
860
|
+
svg.AspectRatio = function(ctx, aspectRatio, width, desiredWidth, height, desiredHeight, minX, minY, refX, refY) {
|
861
|
+
// aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
|
862
|
+
aspectRatio = svg.compressSpaces(aspectRatio);
|
863
|
+
aspectRatio = aspectRatio.replace(/^defer\s/,''); // ignore defer
|
864
|
+
var align = aspectRatio.split(' ')[0] || 'xMidYMid';
|
865
|
+
var meetOrSlice = aspectRatio.split(' ')[1] || 'meet';
|
866
|
+
|
867
|
+
// calculate scale
|
868
|
+
var scaleX = width / desiredWidth;
|
869
|
+
var scaleY = height / desiredHeight;
|
870
|
+
var scaleMin = Math.min(scaleX, scaleY);
|
871
|
+
var scaleMax = Math.max(scaleX, scaleY);
|
872
|
+
if (meetOrSlice == 'meet') { desiredWidth *= scaleMin; desiredHeight *= scaleMin; }
|
873
|
+
if (meetOrSlice == 'slice') { desiredWidth *= scaleMax; desiredHeight *= scaleMax; }
|
874
|
+
|
875
|
+
refX = new svg.Property('refX', refX);
|
876
|
+
refY = new svg.Property('refY', refY);
|
877
|
+
if (refX.hasValue() && refY.hasValue()) {
|
878
|
+
ctx.translate(-scaleMin * refX.Length.toPixels('x'), -scaleMin * refY.Length.toPixels('y'));
|
879
|
+
}
|
880
|
+
else {
|
881
|
+
// align
|
882
|
+
if (align.match(/^xMid/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width / 2.0 - desiredWidth / 2.0, 0);
|
883
|
+
if (align.match(/YMid$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height / 2.0 - desiredHeight / 2.0);
|
884
|
+
if (align.match(/^xMax/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width - desiredWidth, 0);
|
885
|
+
if (align.match(/YMax$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height - desiredHeight);
|
886
|
+
}
|
887
|
+
|
888
|
+
// scale
|
889
|
+
if (align == 'none') ctx.scale(scaleX, scaleY);
|
890
|
+
else if (meetOrSlice == 'meet') ctx.scale(scaleMin, scaleMin);
|
891
|
+
else if (meetOrSlice == 'slice') ctx.scale(scaleMax, scaleMax);
|
892
|
+
|
893
|
+
// translate
|
894
|
+
ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY);
|
895
|
+
}
|
896
|
+
|
897
|
+
// elements
|
898
|
+
svg.Element = {}
|
899
|
+
|
900
|
+
svg.Element.ElementBase = function(node) {
|
901
|
+
this.attributes = {};
|
902
|
+
this.styles = {};
|
903
|
+
this.children = [];
|
904
|
+
|
905
|
+
// get or create attribute
|
906
|
+
this.attribute = function(name, createIfNotExists) {
|
907
|
+
var a = this.attributes[name];
|
908
|
+
if (a != null) return a;
|
909
|
+
|
910
|
+
a = new svg.Property(name, '');
|
911
|
+
if (createIfNotExists == true) this.attributes[name] = a;
|
912
|
+
return a;
|
913
|
+
}
|
914
|
+
|
915
|
+
// get or create style, crawls up node tree
|
916
|
+
this.style = function(name, createIfNotExists) {
|
917
|
+
var s = this.styles[name];
|
918
|
+
if (s != null) return s;
|
919
|
+
|
920
|
+
var a = this.attribute(name);
|
921
|
+
if (a != null && a.hasValue()) {
|
922
|
+
return a;
|
923
|
+
}
|
924
|
+
|
925
|
+
var p = this.parent;
|
926
|
+
if (p != null) {
|
927
|
+
var ps = p.style(name);
|
928
|
+
if (ps != null && ps.hasValue()) {
|
929
|
+
return ps;
|
930
|
+
}
|
931
|
+
}
|
932
|
+
|
933
|
+
s = new svg.Property(name, '');
|
934
|
+
if (createIfNotExists == true) this.styles[name] = s;
|
935
|
+
return s;
|
936
|
+
}
|
937
|
+
|
938
|
+
// base render
|
939
|
+
this.render = function(ctx) {
|
940
|
+
// don't render display=none
|
941
|
+
if (this.style('display').value == 'none') return;
|
942
|
+
|
943
|
+
// don't render visibility=hidden
|
944
|
+
if (this.attribute('visibility').value == 'hidden') return;
|
945
|
+
|
946
|
+
ctx.save();
|
947
|
+
this.setContext(ctx);
|
948
|
+
// mask
|
949
|
+
if (this.attribute('mask').hasValue()) {
|
950
|
+
var mask = this.attribute('mask').Definition.getDefinition();
|
951
|
+
if (mask != null) mask.apply(ctx, this);
|
952
|
+
}
|
953
|
+
else if (this.style('filter').hasValue()) {
|
954
|
+
var filter = this.style('filter').Definition.getDefinition();
|
955
|
+
if (filter != null) filter.apply(ctx, this);
|
956
|
+
}
|
957
|
+
else this.renderChildren(ctx);
|
958
|
+
this.clearContext(ctx);
|
959
|
+
ctx.restore();
|
960
|
+
}
|
961
|
+
|
962
|
+
// base set context
|
963
|
+
this.setContext = function(ctx) {
|
964
|
+
// OVERRIDE ME!
|
965
|
+
}
|
966
|
+
|
967
|
+
// base clear context
|
968
|
+
this.clearContext = function(ctx) {
|
969
|
+
// OVERRIDE ME!
|
970
|
+
}
|
971
|
+
|
972
|
+
// base render children
|
973
|
+
this.renderChildren = function(ctx) {
|
974
|
+
for (var i=0; i<this.children.length; i++) {
|
975
|
+
this.children[i].render(ctx);
|
976
|
+
}
|
977
|
+
}
|
978
|
+
|
979
|
+
this.addChild = function(childNode, create) {
|
980
|
+
var child = childNode;
|
981
|
+
if (create) child = svg.CreateElement(childNode);
|
982
|
+
child.parent = this;
|
983
|
+
this.children.push(child);
|
984
|
+
}
|
985
|
+
|
986
|
+
if (node != null && node.nodeType == 1) { //ELEMENT_NODE
|
987
|
+
// add children
|
988
|
+
for (var i=0; i<node.childNodes.length; i++) {
|
989
|
+
var childNode = node.childNodes[i];
|
990
|
+
if (childNode.nodeType == 1) this.addChild(childNode, true); //ELEMENT_NODE
|
991
|
+
}
|
992
|
+
|
993
|
+
// add attributes
|
994
|
+
for (var i=0; i<node.attributes.length; i++) {
|
995
|
+
var attribute = node.attributes[i];
|
996
|
+
this.attributes[attribute.nodeName] = new svg.Property(attribute.nodeName, attribute.nodeValue);
|
997
|
+
}
|
998
|
+
|
999
|
+
// add tag styles
|
1000
|
+
var styles = svg.Styles[node.nodeName];
|
1001
|
+
if (styles != null) {
|
1002
|
+
for (var name in styles) {
|
1003
|
+
this.styles[name] = styles[name];
|
1004
|
+
}
|
1005
|
+
}
|
1006
|
+
|
1007
|
+
// add class styles
|
1008
|
+
if (this.attribute('class').hasValue()) {
|
1009
|
+
var classes = svg.compressSpaces(this.attribute('class').value).split(' ');
|
1010
|
+
for (var j=0; j<classes.length; j++) {
|
1011
|
+
styles = svg.Styles['.'+classes[j]];
|
1012
|
+
if (styles != null) {
|
1013
|
+
for (var name in styles) {
|
1014
|
+
this.styles[name] = styles[name];
|
1015
|
+
}
|
1016
|
+
}
|
1017
|
+
styles = svg.Styles[node.nodeName+'.'+classes[j]];
|
1018
|
+
if (styles != null) {
|
1019
|
+
for (var name in styles) {
|
1020
|
+
this.styles[name] = styles[name];
|
1021
|
+
}
|
1022
|
+
}
|
1023
|
+
}
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
// add inline styles
|
1027
|
+
if (this.attribute('style').hasValue()) {
|
1028
|
+
var styles = this.attribute('style').value.split(';');
|
1029
|
+
for (var i=0; i<styles.length; i++) {
|
1030
|
+
if (svg.trim(styles[i]) != '') {
|
1031
|
+
var style = styles[i].split(':');
|
1032
|
+
var name = svg.trim(style[0]);
|
1033
|
+
var value = svg.trim(style[1]);
|
1034
|
+
this.styles[name] = new svg.Property(name, value);
|
1035
|
+
}
|
1036
|
+
}
|
1037
|
+
}
|
1038
|
+
|
1039
|
+
// add id
|
1040
|
+
if (this.attribute('id').hasValue()) {
|
1041
|
+
if (svg.Definitions[this.attribute('id').value] == null) {
|
1042
|
+
svg.Definitions[this.attribute('id').value] = this;
|
1043
|
+
}
|
1044
|
+
}
|
1045
|
+
}
|
1046
|
+
}
|
1047
|
+
|
1048
|
+
svg.Element.RenderedElementBase = function(node) {
|
1049
|
+
this.base = svg.Element.ElementBase;
|
1050
|
+
this.base(node);
|
1051
|
+
|
1052
|
+
this.setContext = function(ctx) {
|
1053
|
+
// fill
|
1054
|
+
if (this.style('fill').Definition.isUrl()) {
|
1055
|
+
var fs = this.style('fill').Definition.getFillStyle(this);
|
1056
|
+
if (fs != null) ctx.fillStyle = fs;
|
1057
|
+
}
|
1058
|
+
else if (this.style('fill').hasValue()) {
|
1059
|
+
var fillStyle = this.style('fill');
|
1060
|
+
if (this.style('fill-opacity').hasValue()) fillStyle = fillStyle.Color.addOpacity(this.style('fill-opacity').value);
|
1061
|
+
ctx.fillStyle = (fillStyle.value == 'none' ? 'rgba(0,0,0,0)' : fillStyle.value);
|
1062
|
+
}
|
1063
|
+
|
1064
|
+
// stroke
|
1065
|
+
if (this.style('stroke').Definition.isUrl()) {
|
1066
|
+
var fs = this.style('stroke').Definition.getFillStyle(this);
|
1067
|
+
if (fs != null) ctx.strokeStyle = fs;
|
1068
|
+
}
|
1069
|
+
else if (this.style('stroke').hasValue()) {
|
1070
|
+
var strokeStyle = this.style('stroke');
|
1071
|
+
if (this.style('stroke-opacity').hasValue()) strokeStyle = strokeStyle.Color.addOpacity(this.style('stroke-opacity').value);
|
1072
|
+
ctx.strokeStyle = (strokeStyle.value == 'none' ? 'rgba(0,0,0,0)' : strokeStyle.value);
|
1073
|
+
}
|
1074
|
+
if (this.style('stroke-width').hasValue()) ctx.lineWidth = this.style('stroke-width').Length.toPixels();
|
1075
|
+
if (this.style('stroke-linecap').hasValue()) ctx.lineCap = this.style('stroke-linecap').value;
|
1076
|
+
if (this.style('stroke-linejoin').hasValue()) ctx.lineJoin = this.style('stroke-linejoin').value;
|
1077
|
+
if (this.style('stroke-miterlimit').hasValue()) ctx.miterLimit = this.style('stroke-miterlimit').value;
|
1078
|
+
|
1079
|
+
// font
|
1080
|
+
if (typeof(ctx.font) != 'undefined') {
|
1081
|
+
ctx.font = svg.Font.CreateFont(
|
1082
|
+
this.style('font-style').value,
|
1083
|
+
this.style('font-variant').value,
|
1084
|
+
this.style('font-weight').value,
|
1085
|
+
this.style('font-size').hasValue() ? this.style('font-size').Length.toPixels() + 'px' : '',
|
1086
|
+
this.style('font-family').value).toString();
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
// transform
|
1090
|
+
if (this.attribute('transform').hasValue()) {
|
1091
|
+
var transform = new svg.Transform(this.attribute('transform').value);
|
1092
|
+
transform.apply(ctx);
|
1093
|
+
}
|
1094
|
+
|
1095
|
+
// clip
|
1096
|
+
if (this.attribute('clip-path').hasValue()) {
|
1097
|
+
var clip = this.attribute('clip-path').Definition.getDefinition();
|
1098
|
+
if (clip != null) clip.apply(ctx);
|
1099
|
+
}
|
1100
|
+
|
1101
|
+
// opacity
|
1102
|
+
if (this.style('opacity').hasValue()) {
|
1103
|
+
ctx.globalAlpha = this.style('opacity').numValue();
|
1104
|
+
}
|
1105
|
+
}
|
1106
|
+
}
|
1107
|
+
svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase;
|
1108
|
+
|
1109
|
+
svg.Element.PathElementBase = function(node) {
|
1110
|
+
this.base = svg.Element.RenderedElementBase;
|
1111
|
+
this.base(node);
|
1112
|
+
|
1113
|
+
this.path = function(ctx) {
|
1114
|
+
if (ctx != null) ctx.beginPath();
|
1115
|
+
return new svg.BoundingBox();
|
1116
|
+
}
|
1117
|
+
|
1118
|
+
this.renderChildren = function(ctx) {
|
1119
|
+
this.path(ctx);
|
1120
|
+
svg.Mouse.checkPath(this, ctx);
|
1121
|
+
if (ctx.fillStyle != '') ctx.fill();
|
1122
|
+
if (ctx.strokeStyle != '') ctx.stroke();
|
1123
|
+
|
1124
|
+
var markers = this.getMarkers();
|
1125
|
+
if (markers != null) {
|
1126
|
+
if (this.style('marker-start').Definition.isUrl()) {
|
1127
|
+
var marker = this.style('marker-start').Definition.getDefinition();
|
1128
|
+
marker.render(ctx, markers[0][0], markers[0][1]);
|
1129
|
+
}
|
1130
|
+
if (this.style('marker-mid').Definition.isUrl()) {
|
1131
|
+
var marker = this.style('marker-mid').Definition.getDefinition();
|
1132
|
+
for (var i=1;i<markers.length-1;i++) {
|
1133
|
+
marker.render(ctx, markers[i][0], markers[i][1]);
|
1134
|
+
}
|
1135
|
+
}
|
1136
|
+
if (this.style('marker-end').Definition.isUrl()) {
|
1137
|
+
var marker = this.style('marker-end').Definition.getDefinition();
|
1138
|
+
marker.render(ctx, markers[markers.length-1][0], markers[markers.length-1][1]);
|
1139
|
+
}
|
1140
|
+
}
|
1141
|
+
}
|
1142
|
+
|
1143
|
+
this.getBoundingBox = function() {
|
1144
|
+
return this.path();
|
1145
|
+
}
|
1146
|
+
|
1147
|
+
this.getMarkers = function() {
|
1148
|
+
return null;
|
1149
|
+
}
|
1150
|
+
}
|
1151
|
+
svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase;
|
1152
|
+
|
1153
|
+
// svg element
|
1154
|
+
svg.Element.svg = function(node) {
|
1155
|
+
this.base = svg.Element.RenderedElementBase;
|
1156
|
+
this.base(node);
|
1157
|
+
|
1158
|
+
this.baseClearContext = this.clearContext;
|
1159
|
+
this.clearContext = function(ctx) {
|
1160
|
+
this.baseClearContext(ctx);
|
1161
|
+
svg.ViewPort.RemoveCurrent();
|
1162
|
+
}
|
1163
|
+
|
1164
|
+
this.baseSetContext = this.setContext;
|
1165
|
+
this.setContext = function(ctx) {
|
1166
|
+
// initial values
|
1167
|
+
ctx.strokeStyle = 'rgba(0,0,0,0)';
|
1168
|
+
ctx.lineCap = 'butt';
|
1169
|
+
ctx.lineJoin = 'miter';
|
1170
|
+
ctx.miterLimit = 4;
|
1171
|
+
|
1172
|
+
this.baseSetContext(ctx);
|
1173
|
+
|
1174
|
+
// create new view port
|
1175
|
+
if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) {
|
1176
|
+
ctx.translate(this.attribute('x').Length.toPixels('x'), this.attribute('y').Length.toPixels('y'));
|
1177
|
+
}
|
1178
|
+
|
1179
|
+
var width = svg.ViewPort.width();
|
1180
|
+
var height = svg.ViewPort.height();
|
1181
|
+
if (typeof(this.root) == 'undefined' && this.attribute('width').hasValue() && this.attribute('height').hasValue()) {
|
1182
|
+
width = this.attribute('width').Length.toPixels('x');
|
1183
|
+
height = this.attribute('height').Length.toPixels('y');
|
1184
|
+
|
1185
|
+
var x = 0;
|
1186
|
+
var y = 0;
|
1187
|
+
if (this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) {
|
1188
|
+
x = -this.attribute('refX').Length.toPixels('x');
|
1189
|
+
y = -this.attribute('refY').Length.toPixels('y');
|
1190
|
+
}
|
1191
|
+
|
1192
|
+
ctx.beginPath();
|
1193
|
+
ctx.moveTo(x, y);
|
1194
|
+
ctx.lineTo(width, y);
|
1195
|
+
ctx.lineTo(width, height);
|
1196
|
+
ctx.lineTo(x, height);
|
1197
|
+
ctx.closePath();
|
1198
|
+
ctx.clip();
|
1199
|
+
}
|
1200
|
+
svg.ViewPort.SetCurrent(width, height);
|
1201
|
+
|
1202
|
+
// viewbox
|
1203
|
+
if (this.attribute('viewBox').hasValue()) {
|
1204
|
+
var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
|
1205
|
+
var minX = viewBox[0];
|
1206
|
+
var minY = viewBox[1];
|
1207
|
+
width = viewBox[2];
|
1208
|
+
height = viewBox[3];
|
1209
|
+
|
1210
|
+
svg.AspectRatio(ctx,
|
1211
|
+
this.attribute('preserveAspectRatio').value,
|
1212
|
+
svg.ViewPort.width(),
|
1213
|
+
width,
|
1214
|
+
svg.ViewPort.height(),
|
1215
|
+
height,
|
1216
|
+
minX,
|
1217
|
+
minY,
|
1218
|
+
this.attribute('refX').value,
|
1219
|
+
this.attribute('refY').value);
|
1220
|
+
|
1221
|
+
svg.ViewPort.RemoveCurrent();
|
1222
|
+
svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
|
1223
|
+
}
|
1224
|
+
}
|
1225
|
+
}
|
1226
|
+
svg.Element.svg.prototype = new svg.Element.RenderedElementBase;
|
1227
|
+
|
1228
|
+
// rect element
|
1229
|
+
svg.Element.rect = function(node) {
|
1230
|
+
this.base = svg.Element.PathElementBase;
|
1231
|
+
this.base(node);
|
1232
|
+
|
1233
|
+
this.path = function(ctx) {
|
1234
|
+
var x = this.attribute('x').Length.toPixels('x');
|
1235
|
+
var y = this.attribute('y').Length.toPixels('y');
|
1236
|
+
var width = this.attribute('width').Length.toPixels('x');
|
1237
|
+
var height = this.attribute('height').Length.toPixels('y');
|
1238
|
+
var rx = this.attribute('rx').Length.toPixels('x');
|
1239
|
+
var ry = this.attribute('ry').Length.toPixels('y');
|
1240
|
+
if (this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry = rx;
|
1241
|
+
if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx = ry;
|
1242
|
+
|
1243
|
+
if (ctx != null) {
|
1244
|
+
ctx.beginPath();
|
1245
|
+
ctx.moveTo(x + rx, y);
|
1246
|
+
ctx.lineTo(x + width - rx, y);
|
1247
|
+
ctx.quadraticCurveTo(x + width, y, x + width, y + ry)
|
1248
|
+
ctx.lineTo(x + width, y + height - ry);
|
1249
|
+
ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height)
|
1250
|
+
ctx.lineTo(x + rx, y + height);
|
1251
|
+
ctx.quadraticCurveTo(x, y + height, x, y + height - ry)
|
1252
|
+
ctx.lineTo(x, y + ry);
|
1253
|
+
ctx.quadraticCurveTo(x, y, x + rx, y)
|
1254
|
+
ctx.closePath();
|
1255
|
+
}
|
1256
|
+
|
1257
|
+
return new svg.BoundingBox(x, y, x + width, y + height);
|
1258
|
+
}
|
1259
|
+
}
|
1260
|
+
svg.Element.rect.prototype = new svg.Element.PathElementBase;
|
1261
|
+
|
1262
|
+
// circle element
|
1263
|
+
svg.Element.circle = function(node) {
|
1264
|
+
this.base = svg.Element.PathElementBase;
|
1265
|
+
this.base(node);
|
1266
|
+
|
1267
|
+
this.path = function(ctx) {
|
1268
|
+
var cx = this.attribute('cx').Length.toPixels('x');
|
1269
|
+
var cy = this.attribute('cy').Length.toPixels('y');
|
1270
|
+
var r = this.attribute('r').Length.toPixels();
|
1271
|
+
|
1272
|
+
if (ctx != null) {
|
1273
|
+
ctx.beginPath();
|
1274
|
+
ctx.arc(cx, cy, r, 0, Math.PI * 2, true);
|
1275
|
+
ctx.closePath();
|
1276
|
+
}
|
1277
|
+
|
1278
|
+
return new svg.BoundingBox(cx - r, cy - r, cx + r, cy + r);
|
1279
|
+
}
|
1280
|
+
}
|
1281
|
+
svg.Element.circle.prototype = new svg.Element.PathElementBase;
|
1282
|
+
|
1283
|
+
// ellipse element
|
1284
|
+
svg.Element.ellipse = function(node) {
|
1285
|
+
this.base = svg.Element.PathElementBase;
|
1286
|
+
this.base(node);
|
1287
|
+
|
1288
|
+
this.path = function(ctx) {
|
1289
|
+
var KAPPA = 4 * ((Math.sqrt(2) - 1) / 3);
|
1290
|
+
var rx = this.attribute('rx').Length.toPixels('x');
|
1291
|
+
var ry = this.attribute('ry').Length.toPixels('y');
|
1292
|
+
var cx = this.attribute('cx').Length.toPixels('x');
|
1293
|
+
var cy = this.attribute('cy').Length.toPixels('y');
|
1294
|
+
|
1295
|
+
if (ctx != null) {
|
1296
|
+
ctx.beginPath();
|
1297
|
+
ctx.moveTo(cx, cy - ry);
|
1298
|
+
ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry, cx + rx, cy - (KAPPA * ry), cx + rx, cy);
|
1299
|
+
ctx.bezierCurveTo(cx + rx, cy + (KAPPA * ry), cx + (KAPPA * rx), cy + ry, cx, cy + ry);
|
1300
|
+
ctx.bezierCurveTo(cx - (KAPPA * rx), cy + ry, cx - rx, cy + (KAPPA * ry), cx - rx, cy);
|
1301
|
+
ctx.bezierCurveTo(cx - rx, cy - (KAPPA * ry), cx - (KAPPA * rx), cy - ry, cx, cy - ry);
|
1302
|
+
ctx.closePath();
|
1303
|
+
}
|
1304
|
+
|
1305
|
+
return new svg.BoundingBox(cx - rx, cy - ry, cx + rx, cy + ry);
|
1306
|
+
}
|
1307
|
+
}
|
1308
|
+
svg.Element.ellipse.prototype = new svg.Element.PathElementBase;
|
1309
|
+
|
1310
|
+
// line element
|
1311
|
+
svg.Element.line = function(node) {
|
1312
|
+
this.base = svg.Element.PathElementBase;
|
1313
|
+
this.base(node);
|
1314
|
+
|
1315
|
+
this.getPoints = function() {
|
1316
|
+
return [
|
1317
|
+
new svg.Point(this.attribute('x1').Length.toPixels('x'), this.attribute('y1').Length.toPixels('y')),
|
1318
|
+
new svg.Point(this.attribute('x2').Length.toPixels('x'), this.attribute('y2').Length.toPixels('y'))];
|
1319
|
+
}
|
1320
|
+
|
1321
|
+
this.path = function(ctx) {
|
1322
|
+
var points = this.getPoints();
|
1323
|
+
|
1324
|
+
if (ctx != null) {
|
1325
|
+
ctx.beginPath();
|
1326
|
+
ctx.moveTo(points[0].x, points[0].y);
|
1327
|
+
ctx.lineTo(points[1].x, points[1].y);
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
return new svg.BoundingBox(points[0].x, points[0].y, points[1].x, points[1].y);
|
1331
|
+
}
|
1332
|
+
|
1333
|
+
this.getMarkers = function() {
|
1334
|
+
var points = this.getPoints();
|
1335
|
+
var a = points[0].angleTo(points[1]);
|
1336
|
+
return [[points[0], a], [points[1], a]];
|
1337
|
+
}
|
1338
|
+
}
|
1339
|
+
svg.Element.line.prototype = new svg.Element.PathElementBase;
|
1340
|
+
|
1341
|
+
// polyline element
|
1342
|
+
svg.Element.polyline = function(node) {
|
1343
|
+
this.base = svg.Element.PathElementBase;
|
1344
|
+
this.base(node);
|
1345
|
+
|
1346
|
+
this.points = svg.CreatePath(this.attribute('points').value);
|
1347
|
+
this.path = function(ctx) {
|
1348
|
+
var bb = new svg.BoundingBox(this.points[0].x, this.points[0].y);
|
1349
|
+
if (ctx != null) {
|
1350
|
+
ctx.beginPath();
|
1351
|
+
ctx.moveTo(this.points[0].x, this.points[0].y);
|
1352
|
+
}
|
1353
|
+
for (var i=1; i<this.points.length; i++) {
|
1354
|
+
bb.addPoint(this.points[i].x, this.points[i].y);
|
1355
|
+
if (ctx != null) ctx.lineTo(this.points[i].x, this.points[i].y);
|
1356
|
+
}
|
1357
|
+
return bb;
|
1358
|
+
}
|
1359
|
+
|
1360
|
+
this.getMarkers = function() {
|
1361
|
+
var markers = [];
|
1362
|
+
for (var i=0; i<this.points.length - 1; i++) {
|
1363
|
+
markers.push([this.points[i], this.points[i].angleTo(this.points[i+1])]);
|
1364
|
+
}
|
1365
|
+
markers.push([this.points[this.points.length-1], markers[markers.length-1][1]]);
|
1366
|
+
return markers;
|
1367
|
+
}
|
1368
|
+
}
|
1369
|
+
svg.Element.polyline.prototype = new svg.Element.PathElementBase;
|
1370
|
+
|
1371
|
+
// polygon element
|
1372
|
+
svg.Element.polygon = function(node) {
|
1373
|
+
this.base = svg.Element.polyline;
|
1374
|
+
this.base(node);
|
1375
|
+
|
1376
|
+
this.basePath = this.path;
|
1377
|
+
this.path = function(ctx) {
|
1378
|
+
var bb = this.basePath(ctx);
|
1379
|
+
if (ctx != null) {
|
1380
|
+
ctx.lineTo(this.points[0].x, this.points[0].y);
|
1381
|
+
ctx.closePath();
|
1382
|
+
}
|
1383
|
+
return bb;
|
1384
|
+
}
|
1385
|
+
}
|
1386
|
+
svg.Element.polygon.prototype = new svg.Element.polyline;
|
1387
|
+
|
1388
|
+
// path element
|
1389
|
+
svg.Element.path = function(node) {
|
1390
|
+
this.base = svg.Element.PathElementBase;
|
1391
|
+
this.base(node);
|
1392
|
+
|
1393
|
+
var d = this.attribute('d').value;
|
1394
|
+
// TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF
|
1395
|
+
d = d.replace(/,/gm,' '); // get rid of all commas
|
1396
|
+
d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
|
1397
|
+
d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
|
1398
|
+
d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,'$1 $2'); // separate commands from points
|
1399
|
+
d = d.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from points
|
1400
|
+
d = d.replace(/([0-9])([+\-])/gm,'$1 $2'); // separate digits when no comma
|
1401
|
+
d = d.replace(/(\.[0-9]*)(\.)/gm,'$1 $2'); // separate digits when no comma
|
1402
|
+
d = d.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,'$1 $3 $4 '); // shorthand elliptical arc path syntax
|
1403
|
+
d = svg.compressSpaces(d); // compress multiple spaces
|
1404
|
+
d = svg.trim(d);
|
1405
|
+
this.PathParser = new (function(d) {
|
1406
|
+
this.tokens = d.split(' ');
|
1407
|
+
|
1408
|
+
this.reset = function() {
|
1409
|
+
this.i = -1;
|
1410
|
+
this.command = '';
|
1411
|
+
this.previousCommand = '';
|
1412
|
+
this.start = new svg.Point(0, 0);
|
1413
|
+
this.control = new svg.Point(0, 0);
|
1414
|
+
this.current = new svg.Point(0, 0);
|
1415
|
+
this.points = [];
|
1416
|
+
this.angles = [];
|
1417
|
+
}
|
1418
|
+
|
1419
|
+
this.isEnd = function() {
|
1420
|
+
return this.i >= this.tokens.length - 1;
|
1421
|
+
}
|
1422
|
+
|
1423
|
+
this.isCommandOrEnd = function() {
|
1424
|
+
if (this.isEnd()) return true;
|
1425
|
+
return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null;
|
1426
|
+
}
|
1427
|
+
|
1428
|
+
this.isRelativeCommand = function() {
|
1429
|
+
return this.command == this.command.toLowerCase();
|
1430
|
+
}
|
1431
|
+
|
1432
|
+
this.getToken = function() {
|
1433
|
+
this.i = this.i + 1;
|
1434
|
+
return this.tokens[this.i];
|
1435
|
+
}
|
1436
|
+
|
1437
|
+
this.getScalar = function() {
|
1438
|
+
return parseFloat(this.getToken());
|
1439
|
+
}
|
1440
|
+
|
1441
|
+
this.nextCommand = function() {
|
1442
|
+
this.previousCommand = this.command;
|
1443
|
+
this.command = this.getToken();
|
1444
|
+
}
|
1445
|
+
|
1446
|
+
this.getPoint = function() {
|
1447
|
+
var p = new svg.Point(this.getScalar(), this.getScalar());
|
1448
|
+
return this.makeAbsolute(p);
|
1449
|
+
}
|
1450
|
+
|
1451
|
+
this.getAsControlPoint = function() {
|
1452
|
+
var p = this.getPoint();
|
1453
|
+
this.control = p;
|
1454
|
+
return p;
|
1455
|
+
}
|
1456
|
+
|
1457
|
+
this.getAsCurrentPoint = function() {
|
1458
|
+
var p = this.getPoint();
|
1459
|
+
this.current = p;
|
1460
|
+
return p;
|
1461
|
+
}
|
1462
|
+
|
1463
|
+
this.getReflectedControlPoint = function() {
|
1464
|
+
if (this.previousCommand.toLowerCase() != 'c' && this.previousCommand.toLowerCase() != 's') {
|
1465
|
+
return this.current;
|
1466
|
+
}
|
1467
|
+
|
1468
|
+
// reflect point
|
1469
|
+
var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y);
|
1470
|
+
return p;
|
1471
|
+
}
|
1472
|
+
|
1473
|
+
this.makeAbsolute = function(p) {
|
1474
|
+
if (this.isRelativeCommand()) {
|
1475
|
+
p.x = this.current.x + p.x;
|
1476
|
+
p.y = this.current.y + p.y;
|
1477
|
+
}
|
1478
|
+
return p;
|
1479
|
+
}
|
1480
|
+
|
1481
|
+
this.addMarker = function(p, from, priorTo) {
|
1482
|
+
// if the last angle isn't filled in because we didn't have this point yet ...
|
1483
|
+
if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length-1] == null) {
|
1484
|
+
this.angles[this.angles.length-1] = this.points[this.points.length-1].angleTo(priorTo);
|
1485
|
+
}
|
1486
|
+
this.addMarkerAngle(p, from == null ? null : from.angleTo(p));
|
1487
|
+
}
|
1488
|
+
|
1489
|
+
this.addMarkerAngle = function(p, a) {
|
1490
|
+
this.points.push(p);
|
1491
|
+
this.angles.push(a);
|
1492
|
+
}
|
1493
|
+
|
1494
|
+
this.getMarkerPoints = function() { return this.points; }
|
1495
|
+
this.getMarkerAngles = function() {
|
1496
|
+
for (var i=0; i<this.angles.length; i++) {
|
1497
|
+
if (this.angles[i] == null) {
|
1498
|
+
for (var j=i+1; j<this.angles.length; j++) {
|
1499
|
+
if (this.angles[j] != null) {
|
1500
|
+
this.angles[i] = this.angles[j];
|
1501
|
+
break;
|
1502
|
+
}
|
1503
|
+
}
|
1504
|
+
}
|
1505
|
+
}
|
1506
|
+
return this.angles;
|
1507
|
+
}
|
1508
|
+
})(d);
|
1509
|
+
|
1510
|
+
this.path = function(ctx) {
|
1511
|
+
var pp = this.PathParser;
|
1512
|
+
pp.reset();
|
1513
|
+
|
1514
|
+
var bb = new svg.BoundingBox();
|
1515
|
+
if (ctx != null) ctx.beginPath();
|
1516
|
+
while (!pp.isEnd()) {
|
1517
|
+
pp.nextCommand();
|
1518
|
+
switch (pp.command.toUpperCase()) {
|
1519
|
+
case 'M':
|
1520
|
+
var p = pp.getAsCurrentPoint();
|
1521
|
+
pp.addMarker(p);
|
1522
|
+
bb.addPoint(p.x, p.y);
|
1523
|
+
if (ctx != null) ctx.moveTo(p.x, p.y);
|
1524
|
+
pp.start = pp.current;
|
1525
|
+
while (!pp.isCommandOrEnd()) {
|
1526
|
+
var p = pp.getAsCurrentPoint();
|
1527
|
+
pp.addMarker(p, pp.start);
|
1528
|
+
bb.addPoint(p.x, p.y);
|
1529
|
+
if (ctx != null) ctx.lineTo(p.x, p.y);
|
1530
|
+
}
|
1531
|
+
break;
|
1532
|
+
case 'L':
|
1533
|
+
while (!pp.isCommandOrEnd()) {
|
1534
|
+
var c = pp.current;
|
1535
|
+
var p = pp.getAsCurrentPoint();
|
1536
|
+
pp.addMarker(p, c);
|
1537
|
+
bb.addPoint(p.x, p.y);
|
1538
|
+
if (ctx != null) ctx.lineTo(p.x, p.y);
|
1539
|
+
}
|
1540
|
+
break;
|
1541
|
+
case 'H':
|
1542
|
+
while (!pp.isCommandOrEnd()) {
|
1543
|
+
var newP = new svg.Point((pp.isRelativeCommand() ? pp.current.x : 0) + pp.getScalar(), pp.current.y);
|
1544
|
+
pp.addMarker(newP, pp.current);
|
1545
|
+
pp.current = newP;
|
1546
|
+
bb.addPoint(pp.current.x, pp.current.y);
|
1547
|
+
if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
|
1548
|
+
}
|
1549
|
+
break;
|
1550
|
+
case 'V':
|
1551
|
+
while (!pp.isCommandOrEnd()) {
|
1552
|
+
var newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.y : 0) + pp.getScalar());
|
1553
|
+
pp.addMarker(newP, pp.current);
|
1554
|
+
pp.current = newP;
|
1555
|
+
bb.addPoint(pp.current.x, pp.current.y);
|
1556
|
+
if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
|
1557
|
+
}
|
1558
|
+
break;
|
1559
|
+
case 'C':
|
1560
|
+
while (!pp.isCommandOrEnd()) {
|
1561
|
+
var curr = pp.current;
|
1562
|
+
var p1 = pp.getPoint();
|
1563
|
+
var cntrl = pp.getAsControlPoint();
|
1564
|
+
var cp = pp.getAsCurrentPoint();
|
1565
|
+
pp.addMarker(cp, cntrl, p1);
|
1566
|
+
bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1567
|
+
if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1568
|
+
}
|
1569
|
+
break;
|
1570
|
+
case 'S':
|
1571
|
+
while (!pp.isCommandOrEnd()) {
|
1572
|
+
var curr = pp.current;
|
1573
|
+
var p1 = pp.getReflectedControlPoint();
|
1574
|
+
var cntrl = pp.getAsControlPoint();
|
1575
|
+
var cp = pp.getAsCurrentPoint();
|
1576
|
+
pp.addMarker(cp, cntrl, p1);
|
1577
|
+
bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1578
|
+
if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1579
|
+
}
|
1580
|
+
break;
|
1581
|
+
case 'Q':
|
1582
|
+
while (!pp.isCommandOrEnd()) {
|
1583
|
+
var curr = pp.current;
|
1584
|
+
var cntrl = pp.getAsControlPoint();
|
1585
|
+
var cp = pp.getAsCurrentPoint();
|
1586
|
+
pp.addMarker(cp, cntrl, cntrl);
|
1587
|
+
bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1588
|
+
if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
|
1589
|
+
}
|
1590
|
+
break;
|
1591
|
+
case 'T':
|
1592
|
+
while (!pp.isCommandOrEnd()) {
|
1593
|
+
var curr = pp.current;
|
1594
|
+
var cntrl = pp.getReflectedControlPoint();
|
1595
|
+
pp.control = cntrl;
|
1596
|
+
var cp = pp.getAsCurrentPoint();
|
1597
|
+
pp.addMarker(cp, cntrl, cntrl);
|
1598
|
+
bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
|
1599
|
+
if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
|
1600
|
+
}
|
1601
|
+
break;
|
1602
|
+
case 'A':
|
1603
|
+
while (!pp.isCommandOrEnd()) {
|
1604
|
+
var curr = pp.current;
|
1605
|
+
var rx = pp.getScalar();
|
1606
|
+
var ry = pp.getScalar();
|
1607
|
+
var xAxisRotation = pp.getScalar() * (Math.PI / 180.0);
|
1608
|
+
var largeArcFlag = pp.getScalar();
|
1609
|
+
var sweepFlag = pp.getScalar();
|
1610
|
+
var cp = pp.getAsCurrentPoint();
|
1611
|
+
|
1612
|
+
// Conversion from endpoint to center parameterization
|
1613
|
+
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
1614
|
+
// x1', y1'
|
1615
|
+
var currp = new svg.Point(
|
1616
|
+
Math.cos(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.sin(xAxisRotation) * (curr.y - cp.y) / 2.0,
|
1617
|
+
-Math.sin(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.cos(xAxisRotation) * (curr.y - cp.y) / 2.0
|
1618
|
+
);
|
1619
|
+
// adjust radii
|
1620
|
+
var l = Math.pow(currp.x,2)/Math.pow(rx,2)+Math.pow(currp.y,2)/Math.pow(ry,2);
|
1621
|
+
if (l > 1) {
|
1622
|
+
rx *= Math.sqrt(l);
|
1623
|
+
ry *= Math.sqrt(l);
|
1624
|
+
}
|
1625
|
+
// cx', cy'
|
1626
|
+
var s = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt(
|
1627
|
+
((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))) /
|
1628
|
+
(Math.pow(rx,2)*Math.pow(currp.y,2)+Math.pow(ry,2)*Math.pow(currp.x,2))
|
1629
|
+
);
|
1630
|
+
if (isNaN(s)) s = 0;
|
1631
|
+
var cpp = new svg.Point(s * rx * currp.y / ry, s * -ry * currp.x / rx);
|
1632
|
+
// cx, cy
|
1633
|
+
var centp = new svg.Point(
|
1634
|
+
(curr.x + cp.x) / 2.0 + Math.cos(xAxisRotation) * cpp.x - Math.sin(xAxisRotation) * cpp.y,
|
1635
|
+
(curr.y + cp.y) / 2.0 + Math.sin(xAxisRotation) * cpp.x + Math.cos(xAxisRotation) * cpp.y
|
1636
|
+
);
|
1637
|
+
// vector magnitude
|
1638
|
+
var m = function(v) { return Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2)); }
|
1639
|
+
// ratio between two vectors
|
1640
|
+
var r = function(u, v) { return (u[0]*v[0]+u[1]*v[1]) / (m(u)*m(v)) }
|
1641
|
+
// angle between two vectors
|
1642
|
+
var a = function(u, v) { return (u[0]*v[1] < u[1]*v[0] ? -1 : 1) * Math.acos(r(u,v)); }
|
1643
|
+
// initial angle
|
1644
|
+
var a1 = a([1,0], [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry]);
|
1645
|
+
// angle delta
|
1646
|
+
var u = [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry];
|
1647
|
+
var v = [(-currp.x-cpp.x)/rx,(-currp.y-cpp.y)/ry];
|
1648
|
+
var ad = a(u, v);
|
1649
|
+
if (r(u,v) <= -1) ad = Math.PI;
|
1650
|
+
if (r(u,v) >= 1) ad = 0;
|
1651
|
+
|
1652
|
+
if (sweepFlag == 0 && ad > 0) ad = ad - 2 * Math.PI;
|
1653
|
+
if (sweepFlag == 1 && ad < 0) ad = ad + 2 * Math.PI;
|
1654
|
+
|
1655
|
+
// for markers
|
1656
|
+
var halfWay = new svg.Point(
|
1657
|
+
centp.x - rx * Math.cos((a1 + ad) / 2),
|
1658
|
+
centp.y - ry * Math.sin((a1 + ad) / 2)
|
1659
|
+
);
|
1660
|
+
pp.addMarkerAngle(halfWay, (a1 + ad) / 2 + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2);
|
1661
|
+
pp.addMarkerAngle(cp, ad + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2);
|
1662
|
+
|
1663
|
+
bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better
|
1664
|
+
if (ctx != null) {
|
1665
|
+
var r = rx > ry ? rx : ry;
|
1666
|
+
var sx = rx > ry ? 1 : rx / ry;
|
1667
|
+
var sy = rx > ry ? ry / rx : 1;
|
1668
|
+
|
1669
|
+
ctx.translate(centp.x, centp.y);
|
1670
|
+
ctx.rotate(xAxisRotation);
|
1671
|
+
ctx.scale(sx, sy);
|
1672
|
+
ctx.arc(0, 0, r, a1, a1 + ad, 1 - sweepFlag);
|
1673
|
+
ctx.scale(1/sx, 1/sy);
|
1674
|
+
ctx.rotate(-xAxisRotation);
|
1675
|
+
ctx.translate(-centp.x, -centp.y);
|
1676
|
+
}
|
1677
|
+
}
|
1678
|
+
break;
|
1679
|
+
case 'Z':
|
1680
|
+
if (ctx != null) ctx.closePath();
|
1681
|
+
pp.current = pp.start;
|
1682
|
+
}
|
1683
|
+
}
|
1684
|
+
|
1685
|
+
return bb;
|
1686
|
+
}
|
1687
|
+
|
1688
|
+
this.getMarkers = function() {
|
1689
|
+
var points = this.PathParser.getMarkerPoints();
|
1690
|
+
var angles = this.PathParser.getMarkerAngles();
|
1691
|
+
|
1692
|
+
var markers = [];
|
1693
|
+
for (var i=0; i<points.length; i++) {
|
1694
|
+
markers.push([points[i], angles[i]]);
|
1695
|
+
}
|
1696
|
+
return markers;
|
1697
|
+
}
|
1698
|
+
}
|
1699
|
+
svg.Element.path.prototype = new svg.Element.PathElementBase;
|
1700
|
+
|
1701
|
+
// pattern element
|
1702
|
+
svg.Element.pattern = function(node) {
|
1703
|
+
this.base = svg.Element.ElementBase;
|
1704
|
+
this.base(node);
|
1705
|
+
|
1706
|
+
this.createPattern = function(ctx, element) {
|
1707
|
+
// render me using a temporary svg element
|
1708
|
+
var tempSvg = new svg.Element.svg();
|
1709
|
+
tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
|
1710
|
+
tempSvg.attributes['x'] = new svg.Property('x', this.attribute('x').value);
|
1711
|
+
tempSvg.attributes['y'] = new svg.Property('y', this.attribute('y').value);
|
1712
|
+
tempSvg.attributes['width'] = new svg.Property('width', this.attribute('width').value);
|
1713
|
+
tempSvg.attributes['height'] = new svg.Property('height', this.attribute('height').value);
|
1714
|
+
tempSvg.children = this.children;
|
1715
|
+
|
1716
|
+
var c = document.createElement('canvas');
|
1717
|
+
c.width = this.attribute('width').Length.toPixels('x');
|
1718
|
+
c.height = this.attribute('height').Length.toPixels('y');
|
1719
|
+
tempSvg.render(c.getContext('2d'));
|
1720
|
+
return ctx.createPattern(c, 'repeat');
|
1721
|
+
}
|
1722
|
+
}
|
1723
|
+
svg.Element.pattern.prototype = new svg.Element.ElementBase;
|
1724
|
+
|
1725
|
+
// marker element
|
1726
|
+
svg.Element.marker = function(node) {
|
1727
|
+
this.base = svg.Element.ElementBase;
|
1728
|
+
this.base(node);
|
1729
|
+
|
1730
|
+
this.baseRender = this.render;
|
1731
|
+
this.render = function(ctx, point, angle) {
|
1732
|
+
ctx.translate(point.x, point.y);
|
1733
|
+
if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(angle);
|
1734
|
+
if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(ctx.lineWidth, ctx.lineWidth);
|
1735
|
+
ctx.save();
|
1736
|
+
|
1737
|
+
// render me using a temporary svg element
|
1738
|
+
var tempSvg = new svg.Element.svg();
|
1739
|
+
tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
|
1740
|
+
tempSvg.attributes['refX'] = new svg.Property('refX', this.attribute('refX').value);
|
1741
|
+
tempSvg.attributes['refY'] = new svg.Property('refY', this.attribute('refY').value);
|
1742
|
+
tempSvg.attributes['width'] = new svg.Property('width', this.attribute('markerWidth').value);
|
1743
|
+
tempSvg.attributes['height'] = new svg.Property('height', this.attribute('markerHeight').value);
|
1744
|
+
tempSvg.attributes['fill'] = new svg.Property('fill', this.attribute('fill').valueOrDefault('black'));
|
1745
|
+
tempSvg.attributes['stroke'] = new svg.Property('stroke', this.attribute('stroke').valueOrDefault('none'));
|
1746
|
+
tempSvg.children = this.children;
|
1747
|
+
tempSvg.render(ctx);
|
1748
|
+
|
1749
|
+
ctx.restore();
|
1750
|
+
if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(1/ctx.lineWidth, 1/ctx.lineWidth);
|
1751
|
+
if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(-angle);
|
1752
|
+
ctx.translate(-point.x, -point.y);
|
1753
|
+
}
|
1754
|
+
}
|
1755
|
+
svg.Element.marker.prototype = new svg.Element.ElementBase;
|
1756
|
+
|
1757
|
+
// definitions element
|
1758
|
+
svg.Element.defs = function(node) {
|
1759
|
+
this.base = svg.Element.ElementBase;
|
1760
|
+
this.base(node);
|
1761
|
+
|
1762
|
+
this.render = function(ctx) {
|
1763
|
+
// NOOP
|
1764
|
+
}
|
1765
|
+
}
|
1766
|
+
svg.Element.defs.prototype = new svg.Element.ElementBase;
|
1767
|
+
|
1768
|
+
// base for gradients
|
1769
|
+
svg.Element.GradientBase = function(node) {
|
1770
|
+
this.base = svg.Element.ElementBase;
|
1771
|
+
this.base(node);
|
1772
|
+
|
1773
|
+
this.gradientUnits = this.attribute('gradientUnits').valueOrDefault('objectBoundingBox');
|
1774
|
+
|
1775
|
+
this.stops = [];
|
1776
|
+
for (var i=0; i<this.children.length; i++) {
|
1777
|
+
var child = this.children[i];
|
1778
|
+
this.stops.push(child);
|
1779
|
+
}
|
1780
|
+
|
1781
|
+
this.getGradient = function() {
|
1782
|
+
// OVERRIDE ME!
|
1783
|
+
}
|
1784
|
+
|
1785
|
+
this.createGradient = function(ctx, element) {
|
1786
|
+
var stopsContainer = this;
|
1787
|
+
if (this.attribute('xlink:href').hasValue()) {
|
1788
|
+
stopsContainer = this.attribute('xlink:href').Definition.getDefinition();
|
1789
|
+
}
|
1790
|
+
|
1791
|
+
var g = this.getGradient(ctx, element);
|
1792
|
+
for (var i=0; i<stopsContainer.stops.length; i++) {
|
1793
|
+
g.addColorStop(stopsContainer.stops[i].offset, stopsContainer.stops[i].color);
|
1794
|
+
}
|
1795
|
+
|
1796
|
+
if (this.attribute('gradientTransform').hasValue()) {
|
1797
|
+
// render as transformed pattern on temporary canvas
|
1798
|
+
var rootView = svg.ViewPort.viewPorts[0];
|
1799
|
+
|
1800
|
+
var rect = new svg.Element.rect();
|
1801
|
+
rect.attributes['x'] = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS/3.0);
|
1802
|
+
rect.attributes['y'] = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS/3.0);
|
1803
|
+
rect.attributes['width'] = new svg.Property('width', svg.MAX_VIRTUAL_PIXELS);
|
1804
|
+
rect.attributes['height'] = new svg.Property('height', svg.MAX_VIRTUAL_PIXELS);
|
1805
|
+
|
1806
|
+
var group = new svg.Element.g();
|
1807
|
+
group.attributes['transform'] = new svg.Property('transform', this.attribute('gradientTransform').value);
|
1808
|
+
group.children = [ rect ];
|
1809
|
+
|
1810
|
+
var tempSvg = new svg.Element.svg();
|
1811
|
+
tempSvg.attributes['x'] = new svg.Property('x', 0);
|
1812
|
+
tempSvg.attributes['y'] = new svg.Property('y', 0);
|
1813
|
+
tempSvg.attributes['width'] = new svg.Property('width', rootView.width);
|
1814
|
+
tempSvg.attributes['height'] = new svg.Property('height', rootView.height);
|
1815
|
+
tempSvg.children = [ group ];
|
1816
|
+
|
1817
|
+
var c = document.createElement('canvas');
|
1818
|
+
c.width = rootView.width;
|
1819
|
+
c.height = rootView.height;
|
1820
|
+
var tempCtx = c.getContext('2d');
|
1821
|
+
tempCtx.fillStyle = g;
|
1822
|
+
tempSvg.render(tempCtx);
|
1823
|
+
return tempCtx.createPattern(c, 'no-repeat');
|
1824
|
+
}
|
1825
|
+
|
1826
|
+
return g;
|
1827
|
+
}
|
1828
|
+
}
|
1829
|
+
svg.Element.GradientBase.prototype = new svg.Element.ElementBase;
|
1830
|
+
|
1831
|
+
// linear gradient element
|
1832
|
+
svg.Element.linearGradient = function(node) {
|
1833
|
+
this.base = svg.Element.GradientBase;
|
1834
|
+
this.base(node);
|
1835
|
+
|
1836
|
+
this.getGradient = function(ctx, element) {
|
1837
|
+
var bb = element.getBoundingBox();
|
1838
|
+
|
1839
|
+
var x1 = (this.gradientUnits == 'objectBoundingBox'
|
1840
|
+
? bb.x() + bb.width() * this.attribute('x1').numValue()
|
1841
|
+
: this.attribute('x1').Length.toPixels('x'));
|
1842
|
+
var y1 = (this.gradientUnits == 'objectBoundingBox'
|
1843
|
+
? bb.y() + bb.height() * this.attribute('y1').numValue()
|
1844
|
+
: this.attribute('y1').Length.toPixels('y'));
|
1845
|
+
var x2 = (this.gradientUnits == 'objectBoundingBox'
|
1846
|
+
? bb.x() + bb.width() * this.attribute('x2').numValue()
|
1847
|
+
: this.attribute('x2').Length.toPixels('x'));
|
1848
|
+
var y2 = (this.gradientUnits == 'objectBoundingBox'
|
1849
|
+
? bb.y() + bb.height() * this.attribute('y2').numValue()
|
1850
|
+
: this.attribute('y2').Length.toPixels('y'));
|
1851
|
+
|
1852
|
+
return ctx.createLinearGradient(x1, y1, x2, y2);
|
1853
|
+
}
|
1854
|
+
}
|
1855
|
+
svg.Element.linearGradient.prototype = new svg.Element.GradientBase;
|
1856
|
+
|
1857
|
+
// radial gradient element
|
1858
|
+
svg.Element.radialGradient = function(node) {
|
1859
|
+
this.base = svg.Element.GradientBase;
|
1860
|
+
this.base(node);
|
1861
|
+
|
1862
|
+
this.getGradient = function(ctx, element) {
|
1863
|
+
var bb = element.getBoundingBox();
|
1864
|
+
|
1865
|
+
var cx = (this.gradientUnits == 'objectBoundingBox'
|
1866
|
+
? bb.x() + bb.width() * this.attribute('cx').numValue()
|
1867
|
+
: this.attribute('cx').Length.toPixels('x'));
|
1868
|
+
var cy = (this.gradientUnits == 'objectBoundingBox'
|
1869
|
+
? bb.y() + bb.height() * this.attribute('cy').numValue()
|
1870
|
+
: this.attribute('cy').Length.toPixels('y'));
|
1871
|
+
|
1872
|
+
var fx = cx;
|
1873
|
+
var fy = cy;
|
1874
|
+
if (this.attribute('fx').hasValue()) {
|
1875
|
+
fx = (this.gradientUnits == 'objectBoundingBox'
|
1876
|
+
? bb.x() + bb.width() * this.attribute('fx').numValue()
|
1877
|
+
: this.attribute('fx').Length.toPixels('x'));
|
1878
|
+
}
|
1879
|
+
if (this.attribute('fy').hasValue()) {
|
1880
|
+
fy = (this.gradientUnits == 'objectBoundingBox'
|
1881
|
+
? bb.y() + bb.height() * this.attribute('fy').numValue()
|
1882
|
+
: this.attribute('fy').Length.toPixels('y'));
|
1883
|
+
}
|
1884
|
+
|
1885
|
+
var r = (this.gradientUnits == 'objectBoundingBox'
|
1886
|
+
? (bb.width() + bb.height()) / 2.0 * this.attribute('r').numValue()
|
1887
|
+
: this.attribute('r').Length.toPixels());
|
1888
|
+
|
1889
|
+
return ctx.createRadialGradient(fx, fy, 0, cx, cy, r);
|
1890
|
+
}
|
1891
|
+
}
|
1892
|
+
svg.Element.radialGradient.prototype = new svg.Element.GradientBase;
|
1893
|
+
|
1894
|
+
// gradient stop element
|
1895
|
+
svg.Element.stop = function(node) {
|
1896
|
+
this.base = svg.Element.ElementBase;
|
1897
|
+
this.base(node);
|
1898
|
+
|
1899
|
+
this.offset = this.attribute('offset').numValue();
|
1900
|
+
|
1901
|
+
var stopColor = this.style('stop-color');
|
1902
|
+
if (this.style('stop-opacity').hasValue()) stopColor = stopColor.Color.addOpacity(this.style('stop-opacity').value);
|
1903
|
+
this.color = stopColor.value;
|
1904
|
+
}
|
1905
|
+
svg.Element.stop.prototype = new svg.Element.ElementBase;
|
1906
|
+
|
1907
|
+
// animation base element
|
1908
|
+
svg.Element.AnimateBase = function(node) {
|
1909
|
+
this.base = svg.Element.ElementBase;
|
1910
|
+
this.base(node);
|
1911
|
+
|
1912
|
+
svg.Animations.push(this);
|
1913
|
+
|
1914
|
+
this.duration = 0.0;
|
1915
|
+
this.begin = this.attribute('begin').Time.toMilliseconds();
|
1916
|
+
this.maxDuration = this.begin + this.attribute('dur').Time.toMilliseconds();
|
1917
|
+
|
1918
|
+
this.getProperty = function() {
|
1919
|
+
var attributeType = this.attribute('attributeType').value;
|
1920
|
+
var attributeName = this.attribute('attributeName').value;
|
1921
|
+
|
1922
|
+
if (attributeType == 'CSS') {
|
1923
|
+
return this.parent.style(attributeName, true);
|
1924
|
+
}
|
1925
|
+
return this.parent.attribute(attributeName, true);
|
1926
|
+
};
|
1927
|
+
|
1928
|
+
this.initialValue = null;
|
1929
|
+
this.removed = false;
|
1930
|
+
|
1931
|
+
this.calcValue = function() {
|
1932
|
+
// OVERRIDE ME!
|
1933
|
+
return '';
|
1934
|
+
}
|
1935
|
+
|
1936
|
+
this.update = function(delta) {
|
1937
|
+
// set initial value
|
1938
|
+
if (this.initialValue == null) {
|
1939
|
+
this.initialValue = this.getProperty().value;
|
1940
|
+
}
|
1941
|
+
|
1942
|
+
// if we're past the end time
|
1943
|
+
if (this.duration > this.maxDuration) {
|
1944
|
+
// loop for indefinitely repeating animations
|
1945
|
+
if (this.attribute('repeatCount').value == 'indefinite') {
|
1946
|
+
this.duration = 0.0
|
1947
|
+
}
|
1948
|
+
else if (this.attribute('fill').valueOrDefault('remove') == 'remove' && !this.removed) {
|
1949
|
+
this.removed = true;
|
1950
|
+
this.getProperty().value = this.initialValue;
|
1951
|
+
return true;
|
1952
|
+
}
|
1953
|
+
else {
|
1954
|
+
return false; // no updates made
|
1955
|
+
}
|
1956
|
+
}
|
1957
|
+
this.duration = this.duration + delta;
|
1958
|
+
|
1959
|
+
// if we're past the begin time
|
1960
|
+
var updated = false;
|
1961
|
+
if (this.begin < this.duration) {
|
1962
|
+
var newValue = this.calcValue(); // tween
|
1963
|
+
|
1964
|
+
if (this.attribute('type').hasValue()) {
|
1965
|
+
// for transform, etc.
|
1966
|
+
var type = this.attribute('type').value;
|
1967
|
+
newValue = type + '(' + newValue + ')';
|
1968
|
+
}
|
1969
|
+
|
1970
|
+
this.getProperty().value = newValue;
|
1971
|
+
updated = true;
|
1972
|
+
}
|
1973
|
+
|
1974
|
+
return updated;
|
1975
|
+
}
|
1976
|
+
|
1977
|
+
// fraction of duration we've covered
|
1978
|
+
this.progress = function() {
|
1979
|
+
return ((this.duration - this.begin) / (this.maxDuration - this.begin));
|
1980
|
+
}
|
1981
|
+
}
|
1982
|
+
svg.Element.AnimateBase.prototype = new svg.Element.ElementBase;
|
1983
|
+
|
1984
|
+
// animate element
|
1985
|
+
svg.Element.animate = function(node) {
|
1986
|
+
this.base = svg.Element.AnimateBase;
|
1987
|
+
this.base(node);
|
1988
|
+
|
1989
|
+
this.calcValue = function() {
|
1990
|
+
var from = this.attribute('from').numValue();
|
1991
|
+
var to = this.attribute('to').numValue();
|
1992
|
+
|
1993
|
+
// tween value linearly
|
1994
|
+
return from + (to - from) * this.progress();
|
1995
|
+
};
|
1996
|
+
}
|
1997
|
+
svg.Element.animate.prototype = new svg.Element.AnimateBase;
|
1998
|
+
|
1999
|
+
// animate color element
|
2000
|
+
svg.Element.animateColor = function(node) {
|
2001
|
+
this.base = svg.Element.AnimateBase;
|
2002
|
+
this.base(node);
|
2003
|
+
|
2004
|
+
this.calcValue = function() {
|
2005
|
+
var from = new RGBColor(this.attribute('from').value);
|
2006
|
+
var to = new RGBColor(this.attribute('to').value);
|
2007
|
+
|
2008
|
+
if (from.ok && to.ok) {
|
2009
|
+
// tween color linearly
|
2010
|
+
var r = from.r + (to.r - from.r) * this.progress();
|
2011
|
+
var g = from.g + (to.g - from.g) * this.progress();
|
2012
|
+
var b = from.b + (to.b - from.b) * this.progress();
|
2013
|
+
return 'rgb('+parseInt(r,10)+','+parseInt(g,10)+','+parseInt(b,10)+')';
|
2014
|
+
}
|
2015
|
+
return this.attribute('from').value;
|
2016
|
+
};
|
2017
|
+
}
|
2018
|
+
svg.Element.animateColor.prototype = new svg.Element.AnimateBase;
|
2019
|
+
|
2020
|
+
// animate transform element
|
2021
|
+
svg.Element.animateTransform = function(node) {
|
2022
|
+
this.base = svg.Element.animate;
|
2023
|
+
this.base(node);
|
2024
|
+
}
|
2025
|
+
svg.Element.animateTransform.prototype = new svg.Element.animate;
|
2026
|
+
|
2027
|
+
// font element
|
2028
|
+
svg.Element.font = function(node) {
|
2029
|
+
this.base = svg.Element.ElementBase;
|
2030
|
+
this.base(node);
|
2031
|
+
|
2032
|
+
this.horizAdvX = this.attribute('horiz-adv-x').numValue();
|
2033
|
+
|
2034
|
+
this.isRTL = false;
|
2035
|
+
this.isArabic = false;
|
2036
|
+
this.fontFace = null;
|
2037
|
+
this.missingGlyph = null;
|
2038
|
+
this.glyphs = [];
|
2039
|
+
for (var i=0; i<this.children.length; i++) {
|
2040
|
+
var child = this.children[i];
|
2041
|
+
if (child.type == 'font-face') {
|
2042
|
+
this.fontFace = child;
|
2043
|
+
if (child.style('font-family').hasValue()) {
|
2044
|
+
svg.Definitions[child.style('font-family').value] = this;
|
2045
|
+
}
|
2046
|
+
}
|
2047
|
+
else if (child.type == 'missing-glyph') this.missingGlyph = child;
|
2048
|
+
else if (child.type == 'glyph') {
|
2049
|
+
if (child.arabicForm != '') {
|
2050
|
+
this.isRTL = true;
|
2051
|
+
this.isArabic = true;
|
2052
|
+
if (typeof(this.glyphs[child.unicode]) == 'undefined') this.glyphs[child.unicode] = [];
|
2053
|
+
this.glyphs[child.unicode][child.arabicForm] = child;
|
2054
|
+
}
|
2055
|
+
else {
|
2056
|
+
this.glyphs[child.unicode] = child;
|
2057
|
+
}
|
2058
|
+
}
|
2059
|
+
}
|
2060
|
+
}
|
2061
|
+
svg.Element.font.prototype = new svg.Element.ElementBase;
|
2062
|
+
|
2063
|
+
// font-face element
|
2064
|
+
svg.Element.fontface = function(node) {
|
2065
|
+
this.base = svg.Element.ElementBase;
|
2066
|
+
this.base(node);
|
2067
|
+
|
2068
|
+
this.ascent = this.attribute('ascent').value;
|
2069
|
+
this.descent = this.attribute('descent').value;
|
2070
|
+
this.unitsPerEm = this.attribute('units-per-em').numValue();
|
2071
|
+
}
|
2072
|
+
svg.Element.fontface.prototype = new svg.Element.ElementBase;
|
2073
|
+
|
2074
|
+
// missing-glyph element
|
2075
|
+
svg.Element.missingglyph = function(node) {
|
2076
|
+
this.base = svg.Element.path;
|
2077
|
+
this.base(node);
|
2078
|
+
|
2079
|
+
this.horizAdvX = 0;
|
2080
|
+
}
|
2081
|
+
svg.Element.missingglyph.prototype = new svg.Element.path;
|
2082
|
+
|
2083
|
+
// glyph element
|
2084
|
+
svg.Element.glyph = function(node) {
|
2085
|
+
this.base = svg.Element.path;
|
2086
|
+
this.base(node);
|
2087
|
+
|
2088
|
+
this.horizAdvX = this.attribute('horiz-adv-x').numValue();
|
2089
|
+
this.unicode = this.attribute('unicode').value;
|
2090
|
+
this.arabicForm = this.attribute('arabic-form').value;
|
2091
|
+
}
|
2092
|
+
svg.Element.glyph.prototype = new svg.Element.path;
|
2093
|
+
|
2094
|
+
// text element
|
2095
|
+
svg.Element.text = function(node) {
|
2096
|
+
this.base = svg.Element.RenderedElementBase;
|
2097
|
+
this.base(node);
|
2098
|
+
|
2099
|
+
if (node != null) {
|
2100
|
+
// add children
|
2101
|
+
this.children = [];
|
2102
|
+
for (var i=0; i<node.childNodes.length; i++) {
|
2103
|
+
var childNode = node.childNodes[i];
|
2104
|
+
if (childNode.nodeType == 1) { // capture tspan and tref nodes
|
2105
|
+
this.addChild(childNode, true);
|
2106
|
+
}
|
2107
|
+
else if (childNode.nodeType == 3) { // capture text
|
2108
|
+
this.addChild(new svg.Element.tspan(childNode), false);
|
2109
|
+
}
|
2110
|
+
}
|
2111
|
+
}
|
2112
|
+
|
2113
|
+
this.baseSetContext = this.setContext;
|
2114
|
+
this.setContext = function(ctx) {
|
2115
|
+
this.baseSetContext(ctx);
|
2116
|
+
if (this.style('dominant-baseline').hasValue()) ctx.textBaseline = this.style('dominant-baseline').value;
|
2117
|
+
if (this.style('alignment-baseline').hasValue()) ctx.textBaseline = this.style('alignment-baseline').value;
|
2118
|
+
}
|
2119
|
+
|
2120
|
+
this.renderChildren = function(ctx) {
|
2121
|
+
var textAnchor = this.style('text-anchor').valueOrDefault('start');
|
2122
|
+
var x = this.attribute('x').Length.toPixels('x');
|
2123
|
+
var y = this.attribute('y').Length.toPixels('y');
|
2124
|
+
for (var i=0; i<this.children.length; i++) {
|
2125
|
+
var child = this.children[i];
|
2126
|
+
|
2127
|
+
if (child.attribute('x').hasValue()) {
|
2128
|
+
child.x = child.attribute('x').Length.toPixels('x');
|
2129
|
+
}
|
2130
|
+
else {
|
2131
|
+
if (child.attribute('dx').hasValue()) x += child.attribute('dx').Length.toPixels('x');
|
2132
|
+
child.x = x;
|
2133
|
+
}
|
2134
|
+
|
2135
|
+
var childLength = child.measureText(ctx);
|
2136
|
+
if (textAnchor != 'start' && (i==0 || child.attribute('x').hasValue())) { // new group?
|
2137
|
+
// loop through rest of children
|
2138
|
+
var groupLength = childLength;
|
2139
|
+
for (var j=i+1; j<this.children.length; j++) {
|
2140
|
+
var childInGroup = this.children[j];
|
2141
|
+
if (childInGroup.attribute('x').hasValue()) break; // new group
|
2142
|
+
groupLength += childInGroup.measureText(ctx);
|
2143
|
+
}
|
2144
|
+
child.x -= (textAnchor == 'end' ? groupLength : groupLength / 2.0);
|
2145
|
+
}
|
2146
|
+
x = child.x + childLength;
|
2147
|
+
|
2148
|
+
if (child.attribute('y').hasValue()) {
|
2149
|
+
child.y = child.attribute('y').Length.toPixels('y');
|
2150
|
+
}
|
2151
|
+
else {
|
2152
|
+
if (child.attribute('dy').hasValue()) y += child.attribute('dy').Length.toPixels('y');
|
2153
|
+
child.y = y;
|
2154
|
+
}
|
2155
|
+
y = child.y;
|
2156
|
+
|
2157
|
+
child.render(ctx);
|
2158
|
+
}
|
2159
|
+
}
|
2160
|
+
}
|
2161
|
+
svg.Element.text.prototype = new svg.Element.RenderedElementBase;
|
2162
|
+
|
2163
|
+
// text base
|
2164
|
+
svg.Element.TextElementBase = function(node) {
|
2165
|
+
this.base = svg.Element.RenderedElementBase;
|
2166
|
+
this.base(node);
|
2167
|
+
|
2168
|
+
this.getGlyph = function(font, text, i) {
|
2169
|
+
var c = text[i];
|
2170
|
+
var glyph = null;
|
2171
|
+
if (font.isArabic) {
|
2172
|
+
var arabicForm = 'isolated';
|
2173
|
+
if ((i==0 || text[i-1]==' ') && i<text.length-2 && text[i+1]!=' ') arabicForm = 'terminal';
|
2174
|
+
if (i>0 && text[i-1]!=' ' && i<text.length-2 && text[i+1]!=' ') arabicForm = 'medial';
|
2175
|
+
if (i>0 && text[i-1]!=' ' && (i == text.length-1 || text[i+1]==' ')) arabicForm = 'initial';
|
2176
|
+
if (typeof(font.glyphs[c]) != 'undefined') {
|
2177
|
+
glyph = font.glyphs[c][arabicForm];
|
2178
|
+
if (glyph == null && font.glyphs[c].type == 'glyph') glyph = font.glyphs[c];
|
2179
|
+
}
|
2180
|
+
}
|
2181
|
+
else {
|
2182
|
+
glyph = font.glyphs[c];
|
2183
|
+
}
|
2184
|
+
if (glyph == null) glyph = font.missingGlyph;
|
2185
|
+
return glyph;
|
2186
|
+
}
|
2187
|
+
|
2188
|
+
this.renderChildren = function(ctx) {
|
2189
|
+
var customFont = this.parent.style('font-family').Definition.getDefinition();
|
2190
|
+
if (customFont != null) {
|
2191
|
+
var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
|
2192
|
+
var fontStyle = this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle);
|
2193
|
+
var text = this.getText();
|
2194
|
+
if (customFont.isRTL) text = text.split("").reverse().join("");
|
2195
|
+
|
2196
|
+
var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
|
2197
|
+
for (var i=0; i<text.length; i++) {
|
2198
|
+
var glyph = this.getGlyph(customFont, text, i);
|
2199
|
+
var scale = fontSize / customFont.fontFace.unitsPerEm;
|
2200
|
+
ctx.translate(this.x, this.y);
|
2201
|
+
ctx.scale(scale, -scale);
|
2202
|
+
var lw = ctx.lineWidth;
|
2203
|
+
ctx.lineWidth = ctx.lineWidth * customFont.fontFace.unitsPerEm / fontSize;
|
2204
|
+
if (fontStyle == 'italic') ctx.transform(1, 0, .4, 1, 0, 0);
|
2205
|
+
glyph.render(ctx);
|
2206
|
+
if (fontStyle == 'italic') ctx.transform(1, 0, -.4, 1, 0, 0);
|
2207
|
+
ctx.lineWidth = lw;
|
2208
|
+
ctx.scale(1/scale, -1/scale);
|
2209
|
+
ctx.translate(-this.x, -this.y);
|
2210
|
+
|
2211
|
+
this.x += fontSize * (glyph.horizAdvX || customFont.horizAdvX) / customFont.fontFace.unitsPerEm;
|
2212
|
+
if (typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) {
|
2213
|
+
this.x += dx[i];
|
2214
|
+
}
|
2215
|
+
}
|
2216
|
+
return;
|
2217
|
+
}
|
2218
|
+
|
2219
|
+
if (ctx.strokeStyle != '') ctx.strokeText(svg.compressSpaces(this.getText()), this.x, this.y);
|
2220
|
+
if (ctx.fillStyle != '') ctx.fillText(svg.compressSpaces(this.getText()), this.x, this.y);
|
2221
|
+
}
|
2222
|
+
|
2223
|
+
this.getText = function() {
|
2224
|
+
// OVERRIDE ME
|
2225
|
+
}
|
2226
|
+
|
2227
|
+
this.measureText = function(ctx) {
|
2228
|
+
var customFont = this.parent.style('font-family').Definition.getDefinition();
|
2229
|
+
if (customFont != null) {
|
2230
|
+
var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
|
2231
|
+
var measure = 0;
|
2232
|
+
var text = this.getText();
|
2233
|
+
if (customFont.isRTL) text = text.split("").reverse().join("");
|
2234
|
+
var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
|
2235
|
+
for (var i=0; i<text.length; i++) {
|
2236
|
+
var glyph = this.getGlyph(customFont, text, i);
|
2237
|
+
measure += (glyph.horizAdvX || customFont.horizAdvX) * fontSize / customFont.fontFace.unitsPerEm;
|
2238
|
+
if (typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) {
|
2239
|
+
measure += dx[i];
|
2240
|
+
}
|
2241
|
+
}
|
2242
|
+
return measure;
|
2243
|
+
}
|
2244
|
+
|
2245
|
+
var textToMeasure = svg.compressSpaces(this.getText());
|
2246
|
+
if (!ctx.measureText) return textToMeasure.length * 10;
|
2247
|
+
|
2248
|
+
ctx.save();
|
2249
|
+
this.setContext(ctx);
|
2250
|
+
var width = ctx.measureText(textToMeasure).width;
|
2251
|
+
ctx.restore();
|
2252
|
+
return width;
|
2253
|
+
}
|
2254
|
+
}
|
2255
|
+
svg.Element.TextElementBase.prototype = new svg.Element.RenderedElementBase;
|
2256
|
+
|
2257
|
+
// tspan
|
2258
|
+
svg.Element.tspan = function(node) {
|
2259
|
+
this.base = svg.Element.TextElementBase;
|
2260
|
+
this.base(node);
|
2261
|
+
|
2262
|
+
this.text = node.nodeType == 3 ? node.nodeValue : // text
|
2263
|
+
node.childNodes.length > 0 ? node.childNodes[0].nodeValue : // element
|
2264
|
+
node.text;
|
2265
|
+
this.getText = function() {
|
2266
|
+
return this.text;
|
2267
|
+
}
|
2268
|
+
}
|
2269
|
+
svg.Element.tspan.prototype = new svg.Element.TextElementBase;
|
2270
|
+
|
2271
|
+
// tref
|
2272
|
+
svg.Element.tref = function(node) {
|
2273
|
+
this.base = svg.Element.TextElementBase;
|
2274
|
+
this.base(node);
|
2275
|
+
|
2276
|
+
this.getText = function() {
|
2277
|
+
var element = this.attribute('xlink:href').Definition.getDefinition();
|
2278
|
+
if (element != null) return element.children[0].getText();
|
2279
|
+
}
|
2280
|
+
}
|
2281
|
+
svg.Element.tref.prototype = new svg.Element.TextElementBase;
|
2282
|
+
|
2283
|
+
// a element
|
2284
|
+
svg.Element.a = function(node) {
|
2285
|
+
this.base = svg.Element.TextElementBase;
|
2286
|
+
this.base(node);
|
2287
|
+
|
2288
|
+
this.hasText = true;
|
2289
|
+
for (var i=0; i<node.childNodes.length; i++) {
|
2290
|
+
if (node.childNodes[i].nodeType != 3) this.hasText = false;
|
2291
|
+
}
|
2292
|
+
|
2293
|
+
// this might contain text
|
2294
|
+
this.text = this.hasText ? node.childNodes[0].nodeValue : '';
|
2295
|
+
this.getText = function() {
|
2296
|
+
return this.text;
|
2297
|
+
}
|
2298
|
+
|
2299
|
+
this.baseRenderChildren = this.renderChildren;
|
2300
|
+
this.renderChildren = function(ctx) {
|
2301
|
+
if (this.hasText) {
|
2302
|
+
// render as text element
|
2303
|
+
this.baseRenderChildren(ctx);
|
2304
|
+
var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
|
2305
|
+
svg.Mouse.checkBoundingBox(this, new svg.BoundingBox(this.x, this.y - fontSize.Length.toPixels('y'), this.x + this.measureText(ctx), this.y));
|
2306
|
+
}
|
2307
|
+
else {
|
2308
|
+
// render as temporary group
|
2309
|
+
var g = new svg.Element.g();
|
2310
|
+
g.children = this.children;
|
2311
|
+
g.parent = this;
|
2312
|
+
g.render(ctx);
|
2313
|
+
}
|
2314
|
+
}
|
2315
|
+
|
2316
|
+
this.onclick = function() {
|
2317
|
+
window.open(this.attribute('xlink:href').value);
|
2318
|
+
}
|
2319
|
+
|
2320
|
+
this.onmousemove = function() {
|
2321
|
+
svg.ctx.canvas.style.cursor = 'pointer';
|
2322
|
+
}
|
2323
|
+
}
|
2324
|
+
svg.Element.a.prototype = new svg.Element.TextElementBase;
|
2325
|
+
|
2326
|
+
// image element
|
2327
|
+
svg.Element.image = function(node) {
|
2328
|
+
this.base = svg.Element.RenderedElementBase;
|
2329
|
+
this.base(node);
|
2330
|
+
|
2331
|
+
svg.Images.push(this);
|
2332
|
+
this.img = document.createElement('img');
|
2333
|
+
this.loaded = false;
|
2334
|
+
var that = this;
|
2335
|
+
this.img.onload = function() { that.loaded = true; }
|
2336
|
+
this.img.src = this.attribute('xlink:href').value;
|
2337
|
+
|
2338
|
+
this.renderChildren = function(ctx) {
|
2339
|
+
var x = this.attribute('x').Length.toPixels('x');
|
2340
|
+
var y = this.attribute('y').Length.toPixels('y');
|
2341
|
+
|
2342
|
+
var width = this.attribute('width').Length.toPixels('x');
|
2343
|
+
var height = this.attribute('height').Length.toPixels('y');
|
2344
|
+
if (width == 0 || height == 0) return;
|
2345
|
+
|
2346
|
+
ctx.save();
|
2347
|
+
ctx.translate(x, y);
|
2348
|
+
svg.AspectRatio(ctx,
|
2349
|
+
this.attribute('preserveAspectRatio').value,
|
2350
|
+
width,
|
2351
|
+
this.img.width,
|
2352
|
+
height,
|
2353
|
+
this.img.height,
|
2354
|
+
0,
|
2355
|
+
0);
|
2356
|
+
ctx.drawImage(this.img, 0, 0);
|
2357
|
+
ctx.restore();
|
2358
|
+
}
|
2359
|
+
}
|
2360
|
+
svg.Element.image.prototype = new svg.Element.RenderedElementBase;
|
2361
|
+
|
2362
|
+
// group element
|
2363
|
+
svg.Element.g = function(node) {
|
2364
|
+
this.base = svg.Element.RenderedElementBase;
|
2365
|
+
this.base(node);
|
2366
|
+
|
2367
|
+
this.getBoundingBox = function() {
|
2368
|
+
var bb = new svg.BoundingBox();
|
2369
|
+
for (var i=0; i<this.children.length; i++) {
|
2370
|
+
bb.addBoundingBox(this.children[i].getBoundingBox());
|
2371
|
+
}
|
2372
|
+
return bb;
|
2373
|
+
};
|
2374
|
+
}
|
2375
|
+
svg.Element.g.prototype = new svg.Element.RenderedElementBase;
|
2376
|
+
|
2377
|
+
// symbol element
|
2378
|
+
svg.Element.symbol = function(node) {
|
2379
|
+
this.base = svg.Element.RenderedElementBase;
|
2380
|
+
this.base(node);
|
2381
|
+
|
2382
|
+
this.baseSetContext = this.setContext;
|
2383
|
+
this.setContext = function(ctx) {
|
2384
|
+
this.baseSetContext(ctx);
|
2385
|
+
|
2386
|
+
// viewbox
|
2387
|
+
if (this.attribute('viewBox').hasValue()) {
|
2388
|
+
var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
|
2389
|
+
var minX = viewBox[0];
|
2390
|
+
var minY = viewBox[1];
|
2391
|
+
width = viewBox[2];
|
2392
|
+
height = viewBox[3];
|
2393
|
+
|
2394
|
+
svg.AspectRatio(ctx,
|
2395
|
+
this.attribute('preserveAspectRatio').value,
|
2396
|
+
this.attribute('width').Length.toPixels('x'),
|
2397
|
+
width,
|
2398
|
+
this.attribute('height').Length.toPixels('y'),
|
2399
|
+
height,
|
2400
|
+
minX,
|
2401
|
+
minY);
|
2402
|
+
|
2403
|
+
svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
|
2404
|
+
}
|
2405
|
+
}
|
2406
|
+
}
|
2407
|
+
svg.Element.symbol.prototype = new svg.Element.RenderedElementBase;
|
2408
|
+
|
2409
|
+
// style element
|
2410
|
+
svg.Element.style = function(node) {
|
2411
|
+
this.base = svg.Element.ElementBase;
|
2412
|
+
this.base(node);
|
2413
|
+
|
2414
|
+
// text, or spaces then CDATA
|
2415
|
+
var css = node.childNodes[0].nodeValue + (node.childNodes.length > 1 ? node.childNodes[1].nodeValue : '');
|
2416
|
+
css = css.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm, ''); // remove comments
|
2417
|
+
css = svg.compressSpaces(css); // replace whitespace
|
2418
|
+
var cssDefs = css.split('}');
|
2419
|
+
for (var i=0; i<cssDefs.length; i++) {
|
2420
|
+
if (svg.trim(cssDefs[i]) != '') {
|
2421
|
+
var cssDef = cssDefs[i].split('{');
|
2422
|
+
var cssClasses = cssDef[0].split(',');
|
2423
|
+
var cssProps = cssDef[1].split(';');
|
2424
|
+
for (var j=0; j<cssClasses.length; j++) {
|
2425
|
+
var cssClass = svg.trim(cssClasses[j]);
|
2426
|
+
if (cssClass != '') {
|
2427
|
+
var props = {};
|
2428
|
+
for (var k=0; k<cssProps.length; k++) {
|
2429
|
+
var prop = cssProps[k].indexOf(':');
|
2430
|
+
var name = cssProps[k].substr(0, prop);
|
2431
|
+
var value = cssProps[k].substr(prop + 1, cssProps[k].length - prop);
|
2432
|
+
if (name != null && value != null) {
|
2433
|
+
props[svg.trim(name)] = new svg.Property(svg.trim(name), svg.trim(value));
|
2434
|
+
}
|
2435
|
+
}
|
2436
|
+
svg.Styles[cssClass] = props;
|
2437
|
+
if (cssClass == '@font-face') {
|
2438
|
+
var fontFamily = props['font-family'].value.replace(/"/g,'');
|
2439
|
+
var srcs = props['src'].value.split(',');
|
2440
|
+
for (var s=0; s<srcs.length; s++) {
|
2441
|
+
if (srcs[s].indexOf('format("svg")') > 0) {
|
2442
|
+
var urlStart = srcs[s].indexOf('url');
|
2443
|
+
var urlEnd = srcs[s].indexOf(')', urlStart);
|
2444
|
+
var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6);
|
2445
|
+
var doc = svg.parseXml(svg.ajax(url));
|
2446
|
+
var fonts = doc.getElementsByTagName('font');
|
2447
|
+
for (var f=0; f<fonts.length; f++) {
|
2448
|
+
var font = svg.CreateElement(fonts[f]);
|
2449
|
+
svg.Definitions[fontFamily] = font;
|
2450
|
+
}
|
2451
|
+
}
|
2452
|
+
}
|
2453
|
+
}
|
2454
|
+
}
|
2455
|
+
}
|
2456
|
+
}
|
2457
|
+
}
|
2458
|
+
}
|
2459
|
+
svg.Element.style.prototype = new svg.Element.ElementBase;
|
2460
|
+
|
2461
|
+
// use element
|
2462
|
+
svg.Element.use = function(node) {
|
2463
|
+
this.base = svg.Element.RenderedElementBase;
|
2464
|
+
this.base(node);
|
2465
|
+
|
2466
|
+
this.baseSetContext = this.setContext;
|
2467
|
+
this.setContext = function(ctx) {
|
2468
|
+
this.baseSetContext(ctx);
|
2469
|
+
if (this.attribute('x').hasValue()) ctx.translate(this.attribute('x').Length.toPixels('x'), 0);
|
2470
|
+
if (this.attribute('y').hasValue()) ctx.translate(0, this.attribute('y').Length.toPixels('y'));
|
2471
|
+
}
|
2472
|
+
|
2473
|
+
this.getDefinition = function() {
|
2474
|
+
var element = this.attribute('xlink:href').Definition.getDefinition();
|
2475
|
+
if (this.attribute('width').hasValue()) element.attribute('width', true).value = this.attribute('width').value;
|
2476
|
+
if (this.attribute('height').hasValue()) element.attribute('height', true).value = this.attribute('height').value;
|
2477
|
+
return element;
|
2478
|
+
}
|
2479
|
+
|
2480
|
+
this.path = function(ctx) {
|
2481
|
+
var element = this.getDefinition();
|
2482
|
+
if (element != null) element.path(ctx);
|
2483
|
+
}
|
2484
|
+
|
2485
|
+
this.renderChildren = function(ctx) {
|
2486
|
+
var element = this.getDefinition();
|
2487
|
+
if (element != null) element.render(ctx);
|
2488
|
+
}
|
2489
|
+
}
|
2490
|
+
svg.Element.use.prototype = new svg.Element.RenderedElementBase;
|
2491
|
+
|
2492
|
+
// mask element
|
2493
|
+
svg.Element.mask = function(node) {
|
2494
|
+
this.base = svg.Element.ElementBase;
|
2495
|
+
this.base(node);
|
2496
|
+
|
2497
|
+
this.apply = function(ctx, element) {
|
2498
|
+
// render as temp svg
|
2499
|
+
var x = this.attribute('x').Length.toPixels('x');
|
2500
|
+
var y = this.attribute('y').Length.toPixels('y');
|
2501
|
+
var width = this.attribute('width').Length.toPixels('x');
|
2502
|
+
var height = this.attribute('height').Length.toPixels('y');
|
2503
|
+
|
2504
|
+
// temporarily remove mask to avoid recursion
|
2505
|
+
var mask = element.attribute('mask').value;
|
2506
|
+
element.attribute('mask').value = '';
|
2507
|
+
|
2508
|
+
var cMask = document.createElement('canvas');
|
2509
|
+
cMask.width = x + width;
|
2510
|
+
cMask.height = y + height;
|
2511
|
+
var maskCtx = cMask.getContext('2d');
|
2512
|
+
this.renderChildren(maskCtx);
|
2513
|
+
|
2514
|
+
var c = document.createElement('canvas');
|
2515
|
+
c.width = x + width;
|
2516
|
+
c.height = y + height;
|
2517
|
+
var tempCtx = c.getContext('2d');
|
2518
|
+
element.render(tempCtx);
|
2519
|
+
tempCtx.globalCompositeOperation = 'destination-in';
|
2520
|
+
tempCtx.fillStyle = maskCtx.createPattern(cMask, 'no-repeat');
|
2521
|
+
tempCtx.fillRect(0, 0, x + width, y + height);
|
2522
|
+
|
2523
|
+
ctx.fillStyle = tempCtx.createPattern(c, 'no-repeat');
|
2524
|
+
ctx.fillRect(0, 0, x + width, y + height);
|
2525
|
+
|
2526
|
+
// reassign mask
|
2527
|
+
element.attribute('mask').value = mask;
|
2528
|
+
}
|
2529
|
+
|
2530
|
+
this.render = function(ctx) {
|
2531
|
+
// NO RENDER
|
2532
|
+
}
|
2533
|
+
}
|
2534
|
+
svg.Element.mask.prototype = new svg.Element.ElementBase;
|
2535
|
+
|
2536
|
+
// clip element
|
2537
|
+
svg.Element.clipPath = function(node) {
|
2538
|
+
this.base = svg.Element.ElementBase;
|
2539
|
+
this.base(node);
|
2540
|
+
|
2541
|
+
this.apply = function(ctx) {
|
2542
|
+
for (var i=0; i<this.children.length; i++) {
|
2543
|
+
if (this.children[i].path) {
|
2544
|
+
this.children[i].path(ctx);
|
2545
|
+
ctx.clip();
|
2546
|
+
}
|
2547
|
+
}
|
2548
|
+
}
|
2549
|
+
|
2550
|
+
this.render = function(ctx) {
|
2551
|
+
// NO RENDER
|
2552
|
+
}
|
2553
|
+
}
|
2554
|
+
svg.Element.clipPath.prototype = new svg.Element.ElementBase;
|
2555
|
+
|
2556
|
+
// filters
|
2557
|
+
svg.Element.filter = function(node) {
|
2558
|
+
this.base = svg.Element.ElementBase;
|
2559
|
+
this.base(node);
|
2560
|
+
|
2561
|
+
this.apply = function(ctx, element) {
|
2562
|
+
// render as temp svg
|
2563
|
+
var bb = element.getBoundingBox();
|
2564
|
+
var x = this.attribute('x').Length.toPixels('x');
|
2565
|
+
var y = this.attribute('y').Length.toPixels('y');
|
2566
|
+
if (x == 0 || y == 0) {
|
2567
|
+
x = bb.x1;
|
2568
|
+
y = bb.y1;
|
2569
|
+
}
|
2570
|
+
var width = this.attribute('width').Length.toPixels('x');
|
2571
|
+
var height = this.attribute('height').Length.toPixels('y');
|
2572
|
+
if (width == 0 || height == 0) {
|
2573
|
+
width = bb.width();
|
2574
|
+
height = bb.height();
|
2575
|
+
}
|
2576
|
+
|
2577
|
+
// temporarily remove filter to avoid recursion
|
2578
|
+
var filter = element.style('filter').value;
|
2579
|
+
element.style('filter').value = '';
|
2580
|
+
|
2581
|
+
// max filter distance
|
2582
|
+
var extraPercent = .20;
|
2583
|
+
var px = extraPercent * width;
|
2584
|
+
var py = extraPercent * height;
|
2585
|
+
|
2586
|
+
var c = document.createElement('canvas');
|
2587
|
+
c.width = width + 2*px;
|
2588
|
+
c.height = height + 2*py;
|
2589
|
+
var tempCtx = c.getContext('2d');
|
2590
|
+
tempCtx.translate(-x + px, -y + py);
|
2591
|
+
element.render(tempCtx);
|
2592
|
+
|
2593
|
+
// apply filters
|
2594
|
+
for (var i=0; i<this.children.length; i++) {
|
2595
|
+
this.children[i].apply(tempCtx, 0, 0, width + 2*px, height + 2*py);
|
2596
|
+
}
|
2597
|
+
|
2598
|
+
// render on me
|
2599
|
+
ctx.drawImage(c, 0, 0, width + 2*px, height + 2*py, x - px, y - py, width + 2*px, height + 2*py);
|
2600
|
+
|
2601
|
+
// reassign filter
|
2602
|
+
element.style('filter', true).value = filter;
|
2603
|
+
}
|
2604
|
+
|
2605
|
+
this.render = function(ctx) {
|
2606
|
+
// NO RENDER
|
2607
|
+
}
|
2608
|
+
}
|
2609
|
+
svg.Element.filter.prototype = new svg.Element.ElementBase;
|
2610
|
+
|
2611
|
+
svg.Element.feGaussianBlur = function(node) {
|
2612
|
+
this.base = svg.Element.ElementBase;
|
2613
|
+
this.base(node);
|
2614
|
+
|
2615
|
+
function make_fgauss(sigma) {
|
2616
|
+
sigma = Math.max(sigma, 0.01);
|
2617
|
+
var len = Math.ceil(sigma * 4.0) + 1;
|
2618
|
+
mask = [];
|
2619
|
+
for (var i = 0; i < len; i++) {
|
2620
|
+
mask[i] = Math.exp(-0.5 * (i / sigma) * (i / sigma));
|
2621
|
+
}
|
2622
|
+
return mask;
|
2623
|
+
}
|
2624
|
+
|
2625
|
+
function normalize(mask) {
|
2626
|
+
var sum = 0;
|
2627
|
+
for (var i = 1; i < mask.length; i++) {
|
2628
|
+
sum += Math.abs(mask[i]);
|
2629
|
+
}
|
2630
|
+
sum = 2 * sum + Math.abs(mask[0]);
|
2631
|
+
for (var i = 0; i < mask.length; i++) {
|
2632
|
+
mask[i] /= sum;
|
2633
|
+
}
|
2634
|
+
return mask;
|
2635
|
+
}
|
2636
|
+
|
2637
|
+
function convolve_even(src, dst, mask, width, height) {
|
2638
|
+
for (var y = 0; y < height; y++) {
|
2639
|
+
for (var x = 0; x < width; x++) {
|
2640
|
+
var a = imGet(src, x, y, width, height, 3)/255;
|
2641
|
+
for (var rgba = 0; rgba < 4; rgba++) {
|
2642
|
+
var sum = mask[0] * (a==0?255:imGet(src, x, y, width, height, rgba)) * (a==0||rgba==3?1:a);
|
2643
|
+
for (var i = 1; i < mask.length; i++) {
|
2644
|
+
var a1 = imGet(src, Math.max(x-i,0), y, width, height, 3)/255;
|
2645
|
+
var a2 = imGet(src, Math.min(x+i, width-1), y, width, height, 3)/255;
|
2646
|
+
sum += mask[i] *
|
2647
|
+
((a1==0?255:imGet(src, Math.max(x-i,0), y, width, height, rgba)) * (a1==0||rgba==3?1:a1) +
|
2648
|
+
(a2==0?255:imGet(src, Math.min(x+i, width-1), y, width, height, rgba)) * (a2==0||rgba==3?1:a2));
|
2649
|
+
}
|
2650
|
+
imSet(dst, y, x, height, width, rgba, sum);
|
2651
|
+
}
|
2652
|
+
}
|
2653
|
+
}
|
2654
|
+
}
|
2655
|
+
|
2656
|
+
function imGet(img, x, y, width, height, rgba) {
|
2657
|
+
return img[y*width*4 + x*4 + rgba];
|
2658
|
+
}
|
2659
|
+
|
2660
|
+
function imSet(img, x, y, width, height, rgba, val) {
|
2661
|
+
img[y*width*4 + x*4 + rgba] = val;
|
2662
|
+
}
|
2663
|
+
|
2664
|
+
function blur(ctx, width, height, sigma)
|
2665
|
+
{
|
2666
|
+
var srcData = ctx.getImageData(0, 0, width, height);
|
2667
|
+
var mask = make_fgauss(sigma);
|
2668
|
+
mask = normalize(mask);
|
2669
|
+
tmp = [];
|
2670
|
+
convolve_even(srcData.data, tmp, mask, width, height);
|
2671
|
+
convolve_even(tmp, srcData.data, mask, height, width);
|
2672
|
+
ctx.clearRect(0, 0, width, height);
|
2673
|
+
ctx.putImageData(srcData, 0, 0);
|
2674
|
+
}
|
2675
|
+
|
2676
|
+
this.apply = function(ctx, x, y, width, height) {
|
2677
|
+
// assuming x==0 && y==0 for now
|
2678
|
+
blur(ctx, width, height, this.attribute('stdDeviation').numValue());
|
2679
|
+
}
|
2680
|
+
}
|
2681
|
+
svg.Element.filter.prototype = new svg.Element.feGaussianBlur;
|
2682
|
+
|
2683
|
+
// title element, do nothing
|
2684
|
+
svg.Element.title = function(node) {
|
2685
|
+
}
|
2686
|
+
svg.Element.title.prototype = new svg.Element.ElementBase;
|
2687
|
+
|
2688
|
+
// desc element, do nothing
|
2689
|
+
svg.Element.desc = function(node) {
|
2690
|
+
}
|
2691
|
+
svg.Element.desc.prototype = new svg.Element.ElementBase;
|
2692
|
+
|
2693
|
+
svg.Element.MISSING = function(node) {
|
2694
|
+
console.log('ERROR: Element \'' + node.nodeName + '\' not yet implemented.');
|
2695
|
+
}
|
2696
|
+
svg.Element.MISSING.prototype = new svg.Element.ElementBase;
|
2697
|
+
|
2698
|
+
// element factory
|
2699
|
+
svg.CreateElement = function(node) {
|
2700
|
+
var className = node.nodeName.replace(/^[^:]+:/,''); // remove namespace
|
2701
|
+
className = className.replace(/\-/g,''); // remove dashes
|
2702
|
+
var e = null;
|
2703
|
+
if (typeof(svg.Element[className]) != 'undefined') {
|
2704
|
+
e = new svg.Element[className](node);
|
2705
|
+
}
|
2706
|
+
else {
|
2707
|
+
e = new svg.Element.MISSING(node);
|
2708
|
+
}
|
2709
|
+
|
2710
|
+
e.type = node.nodeName;
|
2711
|
+
return e;
|
2712
|
+
}
|
2713
|
+
|
2714
|
+
// load from url
|
2715
|
+
svg.load = function(ctx, url) {
|
2716
|
+
svg.loadXml(ctx, svg.ajax(url));
|
2717
|
+
}
|
2718
|
+
|
2719
|
+
// load from xml
|
2720
|
+
svg.loadXml = function(ctx, xml) {
|
2721
|
+
svg.loadXmlDoc(ctx, svg.parseXml(xml));
|
2722
|
+
}
|
2723
|
+
|
2724
|
+
svg.loadXmlDoc = function(ctx, dom) {
|
2725
|
+
svg.init(ctx);
|
2726
|
+
|
2727
|
+
var mapXY = function(p) {
|
2728
|
+
var e = ctx.canvas;
|
2729
|
+
while (e) {
|
2730
|
+
p.x -= e.offsetLeft;
|
2731
|
+
p.y -= e.offsetTop;
|
2732
|
+
e = e.offsetParent;
|
2733
|
+
}
|
2734
|
+
if (window.scrollX) p.x += window.scrollX;
|
2735
|
+
if (window.scrollY) p.y += window.scrollY;
|
2736
|
+
return p;
|
2737
|
+
}
|
2738
|
+
|
2739
|
+
// bind mouse
|
2740
|
+
if (svg.opts['ignoreMouse'] != true) {
|
2741
|
+
ctx.canvas.onclick = function(e) {
|
2742
|
+
var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
|
2743
|
+
svg.Mouse.onclick(p.x, p.y);
|
2744
|
+
};
|
2745
|
+
ctx.canvas.onmousemove = function(e) {
|
2746
|
+
var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
|
2747
|
+
svg.Mouse.onmousemove(p.x, p.y);
|
2748
|
+
};
|
2749
|
+
}
|
2750
|
+
|
2751
|
+
var e = svg.CreateElement(dom.documentElement);
|
2752
|
+
e.root = true;
|
2753
|
+
|
2754
|
+
// render loop
|
2755
|
+
var isFirstRender = true;
|
2756
|
+
var draw = function() {
|
2757
|
+
svg.ViewPort.Clear();
|
2758
|
+
if (ctx.canvas.parentNode) svg.ViewPort.SetCurrent(ctx.canvas.parentNode.clientWidth, ctx.canvas.parentNode.clientHeight);
|
2759
|
+
|
2760
|
+
if (svg.opts['ignoreDimensions'] != true) {
|
2761
|
+
// set canvas size
|
2762
|
+
if (e.style('width').hasValue()) {
|
2763
|
+
ctx.canvas.width = e.style('width').Length.toPixels('x');
|
2764
|
+
ctx.canvas.style.width = ctx.canvas.width + 'px';
|
2765
|
+
}
|
2766
|
+
if (e.style('height').hasValue()) {
|
2767
|
+
ctx.canvas.height = e.style('height').Length.toPixels('y');
|
2768
|
+
ctx.canvas.style.height = ctx.canvas.height + 'px';
|
2769
|
+
}
|
2770
|
+
}
|
2771
|
+
var cWidth = ctx.canvas.clientWidth || ctx.canvas.width;
|
2772
|
+
var cHeight = ctx.canvas.clientHeight || ctx.canvas.height;
|
2773
|
+
svg.ViewPort.SetCurrent(cWidth, cHeight);
|
2774
|
+
|
2775
|
+
if (svg.opts != null && svg.opts['offsetX'] != null) e.attribute('x', true).value = svg.opts['offsetX'];
|
2776
|
+
if (svg.opts != null && svg.opts['offsetY'] != null) e.attribute('y', true).value = svg.opts['offsetY'];
|
2777
|
+
if (svg.opts != null && svg.opts['scaleWidth'] != null && svg.opts['scaleHeight'] != null) {
|
2778
|
+
var xRatio = 1, yRatio = 1;
|
2779
|
+
if (e.attribute('width').hasValue()) xRatio = e.attribute('width').Length.toPixels('x') / svg.opts['scaleWidth'];
|
2780
|
+
if (e.attribute('height').hasValue()) yRatio = e.attribute('height').Length.toPixels('y') / svg.opts['scaleHeight'];
|
2781
|
+
|
2782
|
+
e.attribute('width', true).value = svg.opts['scaleWidth'];
|
2783
|
+
e.attribute('height', true).value = svg.opts['scaleHeight'];
|
2784
|
+
e.attribute('viewBox', true).value = '0 0 ' + (cWidth * xRatio) + ' ' + (cHeight * yRatio);
|
2785
|
+
e.attribute('preserveAspectRatio', true).value = 'none';
|
2786
|
+
}
|
2787
|
+
|
2788
|
+
// clear and render
|
2789
|
+
if (svg.opts['ignoreClear'] != true) {
|
2790
|
+
ctx.clearRect(0, 0, cWidth, cHeight);
|
2791
|
+
}
|
2792
|
+
e.render(ctx);
|
2793
|
+
if (isFirstRender) {
|
2794
|
+
isFirstRender = false;
|
2795
|
+
if (svg.opts != null && typeof(svg.opts['renderCallback']) == 'function') svg.opts['renderCallback']();
|
2796
|
+
}
|
2797
|
+
}
|
2798
|
+
|
2799
|
+
var waitingForImages = true;
|
2800
|
+
if (svg.ImagesLoaded()) {
|
2801
|
+
waitingForImages = false;
|
2802
|
+
draw();
|
2803
|
+
}
|
2804
|
+
svg.intervalID = setInterval(function() {
|
2805
|
+
var needUpdate = false;
|
2806
|
+
|
2807
|
+
if (waitingForImages && svg.ImagesLoaded()) {
|
2808
|
+
waitingForImages = false;
|
2809
|
+
needUpdate = true;
|
2810
|
+
}
|
2811
|
+
|
2812
|
+
// need update from mouse events?
|
2813
|
+
if (svg.opts['ignoreMouse'] != true) {
|
2814
|
+
needUpdate = needUpdate | svg.Mouse.hasEvents();
|
2815
|
+
}
|
2816
|
+
|
2817
|
+
// need update from animations?
|
2818
|
+
if (svg.opts['ignoreAnimation'] != true) {
|
2819
|
+
for (var i=0; i<svg.Animations.length; i++) {
|
2820
|
+
needUpdate = needUpdate | svg.Animations[i].update(1000 / svg.FRAMERATE);
|
2821
|
+
}
|
2822
|
+
}
|
2823
|
+
|
2824
|
+
// need update from redraw?
|
2825
|
+
if (svg.opts != null && typeof(svg.opts['forceRedraw']) == 'function') {
|
2826
|
+
if (svg.opts['forceRedraw']() == true) needUpdate = true;
|
2827
|
+
}
|
2828
|
+
|
2829
|
+
// render if needed
|
2830
|
+
if (needUpdate) {
|
2831
|
+
draw();
|
2832
|
+
svg.Mouse.runEvents(); // run and clear our events
|
2833
|
+
}
|
2834
|
+
}, 1000 / svg.FRAMERATE);
|
2835
|
+
}
|
2836
|
+
|
2837
|
+
svg.stop = function() {
|
2838
|
+
if (svg.intervalID) {
|
2839
|
+
clearInterval(svg.intervalID);
|
2840
|
+
}
|
2841
|
+
}
|
2842
|
+
|
2843
|
+
svg.Mouse = new (function() {
|
2844
|
+
this.events = [];
|
2845
|
+
this.hasEvents = function() { return this.events.length != 0; }
|
2846
|
+
|
2847
|
+
this.onclick = function(x, y) {
|
2848
|
+
this.events.push({ type: 'onclick', x: x, y: y,
|
2849
|
+
run: function(e) { if (e.onclick) e.onclick(); }
|
2850
|
+
});
|
2851
|
+
}
|
2852
|
+
|
2853
|
+
this.onmousemove = function(x, y) {
|
2854
|
+
this.events.push({ type: 'onmousemove', x: x, y: y,
|
2855
|
+
run: function(e) { if (e.onmousemove) e.onmousemove(); }
|
2856
|
+
});
|
2857
|
+
}
|
2858
|
+
|
2859
|
+
this.eventElements = [];
|
2860
|
+
|
2861
|
+
this.checkPath = function(element, ctx) {
|
2862
|
+
for (var i=0; i<this.events.length; i++) {
|
2863
|
+
var e = this.events[i];
|
2864
|
+
if (ctx.isPointInPath && ctx.isPointInPath(e.x, e.y)) this.eventElements[i] = element;
|
2865
|
+
}
|
2866
|
+
}
|
2867
|
+
|
2868
|
+
this.checkBoundingBox = function(element, bb) {
|
2869
|
+
for (var i=0; i<this.events.length; i++) {
|
2870
|
+
var e = this.events[i];
|
2871
|
+
if (bb.isPointInBox(e.x, e.y)) this.eventElements[i] = element;
|
2872
|
+
}
|
2873
|
+
}
|
2874
|
+
|
2875
|
+
this.runEvents = function() {
|
2876
|
+
svg.ctx.canvas.style.cursor = '';
|
2877
|
+
|
2878
|
+
for (var i=0; i<this.events.length; i++) {
|
2879
|
+
var e = this.events[i];
|
2880
|
+
var element = this.eventElements[i];
|
2881
|
+
while (element) {
|
2882
|
+
e.run(element);
|
2883
|
+
element = element.parent;
|
2884
|
+
}
|
2885
|
+
}
|
2886
|
+
|
2887
|
+
// done running, clear
|
2888
|
+
this.events = [];
|
2889
|
+
this.eventElements = [];
|
2890
|
+
}
|
2891
|
+
});
|
2892
|
+
|
2893
|
+
return svg;
|
2894
|
+
}
|
2895
|
+
})();
|
2896
|
+
|
2897
|
+
if (CanvasRenderingContext2D) {
|
2898
|
+
CanvasRenderingContext2D.prototype.drawSvg = function(s, dx, dy, dw, dh) {
|
2899
|
+
canvg(this.canvas, s, {
|
2900
|
+
ignoreMouse: true,
|
2901
|
+
ignoreAnimation: true,
|
2902
|
+
ignoreDimensions: true,
|
2903
|
+
ignoreClear: true,
|
2904
|
+
offsetX: dx,
|
2905
|
+
offsetY: dy,
|
2906
|
+
scaleWidth: dw,
|
2907
|
+
scaleHeight: dh
|
2908
|
+
});
|
2909
|
+
}
|
2910
|
+
}/**
|
2911
|
+
* @license Highcharts JS v2.2.0 (2012-02-16)
|
2912
|
+
* CanVGRenderer Extension module
|
2913
|
+
*
|
2914
|
+
* (c) 2011-2012 Torstein Hønsi, Erik Olsson
|
2915
|
+
*
|
2916
|
+
* License: www.highcharts.com/license
|
2917
|
+
*/
|
2918
|
+
|
2919
|
+
// JSLint options:
|
2920
|
+
/*global Highcharts */
|
2921
|
+
|
2922
|
+
(function (Highcharts) { // encapsulate
|
2923
|
+
var UNDEFINED,
|
2924
|
+
DIV = 'div',
|
2925
|
+
ABSOLUTE = 'absolute',
|
2926
|
+
RELATIVE = 'relative',
|
2927
|
+
HIDDEN = 'hidden',
|
2928
|
+
VISIBLE = 'visible',
|
2929
|
+
PX = 'px',
|
2930
|
+
css = Highcharts.css,
|
2931
|
+
CanVGRenderer = Highcharts.CanVGRenderer,
|
2932
|
+
SVGRenderer = Highcharts.SVGRenderer,
|
2933
|
+
extend = Highcharts.extend,
|
2934
|
+
merge = Highcharts.merge,
|
2935
|
+
addEvent = Highcharts.addEvent,
|
2936
|
+
placeBox = Highcharts.placeBox,
|
2937
|
+
createElement = Highcharts.createElement,
|
2938
|
+
discardElement = Highcharts.discardElement;
|
2939
|
+
|
2940
|
+
// Extend CanVG renderer on demand, inherit from SVGRenderer
|
2941
|
+
extend(CanVGRenderer.prototype, SVGRenderer.prototype);
|
2942
|
+
|
2943
|
+
// Add additional functionality:
|
2944
|
+
extend(CanVGRenderer.prototype, {
|
2945
|
+
create: function (chart, container, chartWidth, chartHeight) {
|
2946
|
+
this.setContainer(container, chartWidth, chartHeight);
|
2947
|
+
this.configure(chart);
|
2948
|
+
},
|
2949
|
+
setContainer: function (container, chartWidth, chartHeight) {
|
2950
|
+
var containerStyle = container.style,
|
2951
|
+
containerParent = container.parentNode,
|
2952
|
+
containerLeft = containerStyle.left,
|
2953
|
+
containerTop = containerStyle.top,
|
2954
|
+
containerOffsetWidth = container.offsetWidth,
|
2955
|
+
containerOffsetHeight = container.offsetHeight,
|
2956
|
+
canvas,
|
2957
|
+
initialHiddenStyle = { visibility: HIDDEN, position: ABSOLUTE };
|
2958
|
+
|
2959
|
+
this.init.apply(this, [container, chartWidth, chartHeight]);
|
2960
|
+
|
2961
|
+
// add the canvas above it
|
2962
|
+
canvas = createElement('canvas', {
|
2963
|
+
width: containerOffsetWidth,
|
2964
|
+
height: containerOffsetHeight
|
2965
|
+
}, {
|
2966
|
+
position: RELATIVE,
|
2967
|
+
left: containerLeft,
|
2968
|
+
top: containerTop
|
2969
|
+
}, container);
|
2970
|
+
this.canvas = canvas;
|
2971
|
+
|
2972
|
+
// Create the tooltip line and div, they are placed as siblings to
|
2973
|
+
// the container (and as direct childs to the div specified in the html page)
|
2974
|
+
this.ttLine = createElement(DIV, null, initialHiddenStyle, containerParent);
|
2975
|
+
this.ttDiv = createElement(DIV, null, initialHiddenStyle, containerParent);
|
2976
|
+
this.ttTimer = UNDEFINED;
|
2977
|
+
|
2978
|
+
// Move away the svg node to a new div inside the container's parent so we can hide it.
|
2979
|
+
var hiddenSvg = createElement(DIV, {
|
2980
|
+
width: containerOffsetWidth,
|
2981
|
+
height: containerOffsetHeight
|
2982
|
+
}, {
|
2983
|
+
visibility: HIDDEN,
|
2984
|
+
left: containerLeft,
|
2985
|
+
top: containerTop
|
2986
|
+
}, containerParent);
|
2987
|
+
this.hiddenSvg = hiddenSvg;
|
2988
|
+
hiddenSvg.appendChild(this.box);
|
2989
|
+
},
|
2990
|
+
|
2991
|
+
/**
|
2992
|
+
* Configures the renderer with the chart. Attach a listener to the event tooltipRefresh.
|
2993
|
+
**/
|
2994
|
+
configure: function (chart) {
|
2995
|
+
var renderer = this,
|
2996
|
+
options = chart.options.tooltip,
|
2997
|
+
borderWidth = options.borderWidth,
|
2998
|
+
tooltipDiv = renderer.ttDiv,
|
2999
|
+
tooltipDivStyle = options.style,
|
3000
|
+
tooltipLine = renderer.ttLine,
|
3001
|
+
padding = parseInt(tooltipDivStyle.padding, 10);
|
3002
|
+
|
3003
|
+
// Add border styling from options to the style
|
3004
|
+
tooltipDivStyle = merge(tooltipDivStyle, {
|
3005
|
+
padding: padding + PX,
|
3006
|
+
'background-color': options.backgroundColor,
|
3007
|
+
'border-style': 'solid',
|
3008
|
+
'border-width': borderWidth + PX,
|
3009
|
+
'border-radius': options.borderRadius + PX
|
3010
|
+
});
|
3011
|
+
|
3012
|
+
// Optionally add shadow
|
3013
|
+
if (options.shadow) {
|
3014
|
+
tooltipDivStyle = merge(tooltipDivStyle, {
|
3015
|
+
'box-shadow': '1px 1px 3px gray', // w3c
|
3016
|
+
'-webkit-box-shadow': '1px 1px 3px gray' // webkit
|
3017
|
+
});
|
3018
|
+
}
|
3019
|
+
css(tooltipDiv, tooltipDivStyle);
|
3020
|
+
|
3021
|
+
// Set simple style on the line
|
3022
|
+
css(tooltipLine, {
|
3023
|
+
'border-left': '1px solid darkgray'
|
3024
|
+
});
|
3025
|
+
|
3026
|
+
// This event is triggered when a new tooltip should be shown
|
3027
|
+
addEvent(chart, 'tooltipRefresh', function (args) {
|
3028
|
+
var chartContainer = chart.container,
|
3029
|
+
offsetLeft = chartContainer.offsetLeft,
|
3030
|
+
offsetTop = chartContainer.offsetTop,
|
3031
|
+
position;
|
3032
|
+
|
3033
|
+
// Set the content of the tooltip
|
3034
|
+
tooltipDiv.innerHTML = args.text;
|
3035
|
+
|
3036
|
+
// Compute the best position for the tooltip based on the divs size and container size.
|
3037
|
+
position = placeBox(tooltipDiv.offsetWidth, tooltipDiv.offsetHeight, offsetLeft, offsetTop, chartContainer.offsetWidth, chartContainer.offsetHeight, {x: args.x, y: args.y}, 12);
|
3038
|
+
|
3039
|
+
css(tooltipDiv, {
|
3040
|
+
visibility: VISIBLE,
|
3041
|
+
left: position.x + PX,
|
3042
|
+
top: position.y + PX,
|
3043
|
+
'border-color': args.borderColor
|
3044
|
+
});
|
3045
|
+
|
3046
|
+
// Position the tooltip line
|
3047
|
+
css(tooltipLine, {
|
3048
|
+
visibility: VISIBLE,
|
3049
|
+
left: offsetLeft + args.x + PX,
|
3050
|
+
top: offsetTop + chart.plotTop + PX,
|
3051
|
+
height: chart.plotHeight + PX
|
3052
|
+
});
|
3053
|
+
|
3054
|
+
// This timeout hides the tooltip after 3 seconds
|
3055
|
+
// First clear any existing timer
|
3056
|
+
if (renderer.ttTimer !== UNDEFINED) {
|
3057
|
+
clearTimeout(renderer.ttTimer);
|
3058
|
+
}
|
3059
|
+
|
3060
|
+
// Start a new timer that hides tooltip and line
|
3061
|
+
renderer.ttTimer = setTimeout(function () {
|
3062
|
+
css(tooltipDiv, { visibility: HIDDEN });
|
3063
|
+
css(tooltipLine, { visibility: HIDDEN });
|
3064
|
+
}, 3000);
|
3065
|
+
});
|
3066
|
+
},
|
3067
|
+
|
3068
|
+
/**
|
3069
|
+
* Extend SVGRenderer.destroy to also destroy the elements added by CanVGRenderer.
|
3070
|
+
*/
|
3071
|
+
destroy: function () {
|
3072
|
+
var renderer = this;
|
3073
|
+
|
3074
|
+
// Remove the canvas
|
3075
|
+
discardElement(renderer.canvas);
|
3076
|
+
|
3077
|
+
// Kill the timer
|
3078
|
+
if (renderer.ttTimer !== UNDEFINED) {
|
3079
|
+
clearTimeout(renderer.ttTimer);
|
3080
|
+
}
|
3081
|
+
|
3082
|
+
// Remove the divs for tooltip and line
|
3083
|
+
discardElement(renderer.ttLine);
|
3084
|
+
discardElement(renderer.ttDiv);
|
3085
|
+
discardElement(renderer.hiddenSvg);
|
3086
|
+
|
3087
|
+
// Continue with base class
|
3088
|
+
return SVGRenderer.prototype.destroy.apply(renderer);
|
3089
|
+
},
|
3090
|
+
|
3091
|
+
/**
|
3092
|
+
* Take a color and return it if it's a string, do not make it a gradient even if it is a
|
3093
|
+
* gradient. Currently canvg cannot render gradients (turns out black),
|
3094
|
+
* see: http://code.google.com/p/canvg/issues/detail?id=104
|
3095
|
+
*
|
3096
|
+
* @param {Object} color The color or config object
|
3097
|
+
*/
|
3098
|
+
color: function (color, elem, prop) {
|
3099
|
+
if (color && color.linearGradient) {
|
3100
|
+
// Pick the end color and forward to base implementation
|
3101
|
+
color = color.stops[color.stops.length - 1][1];
|
3102
|
+
}
|
3103
|
+
return SVGRenderer.prototype.color.call(this, color, elem, prop);
|
3104
|
+
},
|
3105
|
+
|
3106
|
+
/**
|
3107
|
+
* Draws the SVG on the canvas or adds a draw invokation to the deferred list.
|
3108
|
+
*/
|
3109
|
+
draw: function () {
|
3110
|
+
var renderer = this;
|
3111
|
+
window.canvg(renderer.canvas, renderer.hiddenSvg.innerHTML);
|
3112
|
+
}
|
3113
|
+
});
|
3114
|
+
}(Highcharts));
|