highcharts-rails 5.0.14 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +60 -0
- data/Rakefile +54 -5
- data/app/assets/images/highcharts/earth.svg +432 -0
- data/app/assets/javascripts/highcharts.js +5103 -3147
- data/app/assets/javascripts/highcharts/highcharts-3d.js +930 -277
- data/app/assets/javascripts/highcharts/highcharts-more.js +1374 -249
- data/app/assets/javascripts/highcharts/lib/canvg.js +3073 -0
- data/app/assets/javascripts/highcharts/lib/jspdf.js +16624 -0
- data/app/assets/javascripts/highcharts/lib/rgbcolor.js +299 -0
- data/app/assets/javascripts/highcharts/lib/svg2pdf.js +3488 -0
- data/app/assets/javascripts/highcharts/modules/accessibility.js +654 -212
- data/app/assets/javascripts/highcharts/modules/annotations.js +1552 -274
- data/app/assets/javascripts/highcharts/modules/boost-canvas.js +773 -0
- data/app/assets/javascripts/highcharts/modules/boost.js +636 -210
- data/app/assets/javascripts/highcharts/modules/broken-axis.js +2 -2
- data/app/assets/javascripts/highcharts/modules/bullet.js +364 -0
- data/app/assets/javascripts/highcharts/modules/data.js +766 -38
- data/app/assets/javascripts/highcharts/modules/drag-panes.js +588 -0
- data/app/assets/javascripts/highcharts/modules/drilldown.js +106 -36
- data/app/assets/javascripts/highcharts/modules/export-data.js +597 -0
- data/app/assets/javascripts/highcharts/modules/exporting.js +424 -162
- data/app/assets/javascripts/highcharts/modules/funnel.js +144 -22
- data/app/assets/javascripts/highcharts/modules/gantt.js +1154 -0
- data/app/assets/javascripts/highcharts/modules/grid-axis.js +1 -1
- data/app/assets/javascripts/highcharts/modules/heatmap.js +406 -80
- data/app/assets/javascripts/highcharts/modules/histogram-bellcurve.js +513 -0
- data/app/assets/javascripts/highcharts/modules/item-series.js +126 -0
- data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +31 -13
- data/app/assets/javascripts/highcharts/modules/offline-exporting.js +179 -57
- data/app/assets/javascripts/highcharts/modules/oldie.js +1378 -0
- data/app/assets/javascripts/highcharts/modules/overlapping-datalabels.js +8 -6
- data/app/assets/javascripts/highcharts/modules/parallel-coordinates.js +494 -0
- data/app/assets/javascripts/highcharts/modules/pareto.js +275 -0
- data/app/assets/javascripts/highcharts/modules/sankey.js +641 -0
- data/app/assets/javascripts/highcharts/modules/series-label.js +355 -145
- data/app/assets/javascripts/highcharts/modules/solid-gauge.js +122 -1
- data/app/assets/javascripts/highcharts/modules/static-scale.js +64 -0
- data/app/assets/javascripts/highcharts/modules/stock.js +1944 -676
- data/app/assets/javascripts/highcharts/modules/streamgraph.js +139 -0
- data/app/assets/javascripts/highcharts/modules/sunburst.js +2403 -0
- data/app/assets/javascripts/highcharts/modules/tilemap.js +1199 -0
- data/app/assets/javascripts/highcharts/modules/treemap.js +538 -134
- data/app/assets/javascripts/highcharts/modules/variable-pie.js +490 -0
- data/app/assets/javascripts/highcharts/modules/variwide.js +283 -0
- data/app/assets/javascripts/highcharts/modules/vector.js +294 -0
- data/app/assets/javascripts/highcharts/modules/windbarb.js +490 -0
- data/app/assets/javascripts/highcharts/modules/wordcloud.js +681 -0
- data/app/assets/javascripts/highcharts/modules/xrange.js +615 -0
- data/app/assets/javascripts/highcharts/themes/avocado.js +54 -0
- data/app/assets/javascripts/highcharts/themes/dark-blue.js +6 -6
- data/app/assets/javascripts/highcharts/themes/dark-green.js +6 -6
- data/app/assets/javascripts/highcharts/themes/dark-unica.js +6 -6
- data/app/assets/javascripts/highcharts/themes/gray.js +14 -10
- data/app/assets/javascripts/highcharts/themes/grid-light.js +6 -6
- data/app/assets/javascripts/highcharts/themes/grid.js +7 -5
- data/app/assets/javascripts/highcharts/themes/sand-signika.js +8 -7
- data/app/assets/javascripts/highcharts/themes/skies.js +15 -9
- data/app/assets/javascripts/highcharts/themes/sunset.js +53 -0
- data/app/assets/stylesheets/highcharts/highcharts.css +802 -0
- data/app/assets/stylesheets/highcharts/highcharts.scss +665 -0
- data/lib/highcharts/version.rb +1 -1
- metadata +31 -1
@@ -0,0 +1,1378 @@
|
|
1
|
+
/**
|
2
|
+
* @license Highcharts JS v6.0.0 (2017-10-04)
|
3
|
+
* Old IE (v6, v7, v8) module for Highcharts v6+.
|
4
|
+
*
|
5
|
+
* (c) 2010-2017 Highsoft AS
|
6
|
+
* Author: Torstein Honsi
|
7
|
+
*
|
8
|
+
* License: www.highcharts.com/license
|
9
|
+
*/
|
10
|
+
'use strict';
|
11
|
+
(function(factory) {
|
12
|
+
if (typeof module === 'object' && module.exports) {
|
13
|
+
module.exports = factory;
|
14
|
+
} else {
|
15
|
+
factory(Highcharts);
|
16
|
+
}
|
17
|
+
}(function(Highcharts) {
|
18
|
+
(function(H) {
|
19
|
+
/**
|
20
|
+
* (c) 2010-2017 Torstein Honsi
|
21
|
+
*
|
22
|
+
* Support for old IE browsers (6, 7 and 8) in Highcharts v6+.
|
23
|
+
*
|
24
|
+
* License: www.highcharts.com/license
|
25
|
+
*/
|
26
|
+
|
27
|
+
/* eslint max-len: 0 */
|
28
|
+
var VMLRenderer,
|
29
|
+
VMLRendererExtension,
|
30
|
+
VMLElement,
|
31
|
+
|
32
|
+
Chart = H.Chart,
|
33
|
+
createElement = H.createElement,
|
34
|
+
css = H.css,
|
35
|
+
defined = H.defined,
|
36
|
+
deg2rad = H.deg2rad,
|
37
|
+
discardElement = H.discardElement,
|
38
|
+
doc = H.doc,
|
39
|
+
each = H.each,
|
40
|
+
erase = H.erase,
|
41
|
+
extend = H.extend,
|
42
|
+
extendClass = H.extendClass,
|
43
|
+
isArray = H.isArray,
|
44
|
+
isNumber = H.isNumber,
|
45
|
+
isObject = H.isObject,
|
46
|
+
merge = H.merge,
|
47
|
+
noop = H.noop,
|
48
|
+
pick = H.pick,
|
49
|
+
pInt = H.pInt,
|
50
|
+
svg = H.svg,
|
51
|
+
SVGElement = H.SVGElement,
|
52
|
+
SVGRenderer = H.SVGRenderer,
|
53
|
+
win = H.win,
|
54
|
+
wrap = H.wrap;
|
55
|
+
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Path to the pattern image required by VML browsers in order to
|
59
|
+
* draw radial gradients.
|
60
|
+
*
|
61
|
+
* @type {String}
|
62
|
+
* @apioption global.VMLRadialGradientURL
|
63
|
+
* @default {highcharts}
|
64
|
+
* http://code.highcharts.com/{version}/gfx/vml-radial-gradient.png
|
65
|
+
* @default {highstock}
|
66
|
+
* http://code.highcharts.com/highstock/{version}/gfx/vml-radial-gradient.png
|
67
|
+
* @default {highmaps}
|
68
|
+
* http://code.highcharts.com/{version}/gfx/vml-radial-gradient.png
|
69
|
+
* @since 2.3.0
|
70
|
+
*/
|
71
|
+
H.getOptions().global.VMLRadialGradientURL =
|
72
|
+
'http://code.highcharts.com/6.0.0/gfx/vml-radial-gradient.png';
|
73
|
+
|
74
|
+
|
75
|
+
// Utilites
|
76
|
+
if (doc && !doc.defaultView) {
|
77
|
+
H.getStyle = function(el, prop) {
|
78
|
+
var val,
|
79
|
+
alias = {
|
80
|
+
width: 'clientWidth',
|
81
|
+
height: 'clientHeight'
|
82
|
+
}[prop];
|
83
|
+
|
84
|
+
if (el.style[prop]) {
|
85
|
+
return H.pInt(el.style[prop]);
|
86
|
+
}
|
87
|
+
if (prop === 'opacity') {
|
88
|
+
prop = 'filter';
|
89
|
+
}
|
90
|
+
|
91
|
+
// Getting the rendered width and height
|
92
|
+
if (alias) {
|
93
|
+
el.style.zoom = 1;
|
94
|
+
return Math.max(el[alias] - 2 * H.getStyle(el, 'padding'), 0);
|
95
|
+
}
|
96
|
+
|
97
|
+
val = el.currentStyle[prop.replace(/\-(\w)/g, function(a, b) {
|
98
|
+
return b.toUpperCase();
|
99
|
+
})];
|
100
|
+
if (prop === 'filter') {
|
101
|
+
val = val.replace(
|
102
|
+
/alpha\(opacity=([0-9]+)\)/,
|
103
|
+
function(a, b) {
|
104
|
+
return b / 100;
|
105
|
+
}
|
106
|
+
);
|
107
|
+
}
|
108
|
+
|
109
|
+
return val === '' ? 1 : H.pInt(val);
|
110
|
+
};
|
111
|
+
}
|
112
|
+
|
113
|
+
if (!Array.prototype.forEach) {
|
114
|
+
H.forEachPolyfill = function(fn, ctx) {
|
115
|
+
var i = 0,
|
116
|
+
len = this.length;
|
117
|
+
for (; i < len; i++) {
|
118
|
+
if (fn.call(ctx, this[i], i, this) === false) {
|
119
|
+
return i;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
};
|
123
|
+
}
|
124
|
+
|
125
|
+
if (!Array.prototype.indexOf) {
|
126
|
+
H.indexOfPolyfill = function(arr) {
|
127
|
+
var len,
|
128
|
+
i = 0;
|
129
|
+
|
130
|
+
if (arr) {
|
131
|
+
len = arr.length;
|
132
|
+
|
133
|
+
for (; i < len; i++) {
|
134
|
+
if (arr[i] === this) {
|
135
|
+
return i;
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
return -1;
|
141
|
+
};
|
142
|
+
}
|
143
|
+
|
144
|
+
if (!Array.prototype.filter) {
|
145
|
+
H.filterPolyfill = function(fn) {
|
146
|
+
var ret = [],
|
147
|
+
i = 0,
|
148
|
+
length = this.length;
|
149
|
+
|
150
|
+
for (; i < length; i++) {
|
151
|
+
if (fn(this[i], i)) {
|
152
|
+
ret.push(this[i]);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
return ret;
|
157
|
+
};
|
158
|
+
}
|
159
|
+
|
160
|
+
if (!Array.prototype.find) {
|
161
|
+
H.findPolyfill = function(fn) {
|
162
|
+
var i,
|
163
|
+
length = this.length;
|
164
|
+
|
165
|
+
for (i = 0; i < length; i++) {
|
166
|
+
if (fn(this[i], i)) {
|
167
|
+
return this[i];
|
168
|
+
}
|
169
|
+
}
|
170
|
+
};
|
171
|
+
}
|
172
|
+
|
173
|
+
if (!Array.prototype.reduce) {
|
174
|
+
H.reducePolyfill = function(func, initialValue) {
|
175
|
+
var context = this,
|
176
|
+
accumulator = initialValue || {},
|
177
|
+
len = this.length;
|
178
|
+
for (var i = 0; i < len; ++i) {
|
179
|
+
accumulator = func.call(context, accumulator, this[i], i, this);
|
180
|
+
}
|
181
|
+
return accumulator;
|
182
|
+
};
|
183
|
+
}
|
184
|
+
|
185
|
+
if (!svg) {
|
186
|
+
|
187
|
+
// Prevent wrapping from creating false offsetWidths in export in legacy IE.
|
188
|
+
// This applies only to charts for export, where IE runs the SVGRenderer
|
189
|
+
// instead of the VMLRenderer
|
190
|
+
// (#1079, #1063)
|
191
|
+
wrap(H.SVGRenderer.prototype, 'text', function(proceed) {
|
192
|
+
return proceed.apply(
|
193
|
+
this,
|
194
|
+
Array.prototype.slice.call(arguments, 1)
|
195
|
+
).css({
|
196
|
+
position: 'absolute'
|
197
|
+
});
|
198
|
+
});
|
199
|
+
|
200
|
+
/**
|
201
|
+
* Old IE override for pointer normalize, adds chartX and chartY to event
|
202
|
+
* arguments.
|
203
|
+
*/
|
204
|
+
H.Pointer.prototype.normalize = function(e, chartPosition) {
|
205
|
+
|
206
|
+
e = e || win.event;
|
207
|
+
if (!e.target) {
|
208
|
+
e.target = e.srcElement;
|
209
|
+
}
|
210
|
+
|
211
|
+
// Get mouse position
|
212
|
+
if (!chartPosition) {
|
213
|
+
this.chartPosition = chartPosition = H.offset(this.chart.container);
|
214
|
+
}
|
215
|
+
|
216
|
+
return H.extend(e, {
|
217
|
+
// #2005, #2129: the second case is for IE10 quirks mode within
|
218
|
+
// framesets
|
219
|
+
chartX: Math.round(Math.max(e.x, e.clientX - chartPosition.left)),
|
220
|
+
chartY: Math.round(e.y)
|
221
|
+
});
|
222
|
+
};
|
223
|
+
|
224
|
+
/**
|
225
|
+
* Further sanitize the mock-SVG that is generated when exporting charts in
|
226
|
+
* oldIE.
|
227
|
+
*/
|
228
|
+
Chart.prototype.ieSanitizeSVG = function(svg) {
|
229
|
+
svg = svg
|
230
|
+
.replace(/<IMG /g, '<image ')
|
231
|
+
.replace(/<(\/?)TITLE>/g, '<$1title>')
|
232
|
+
.replace(/height=([^" ]+)/g, 'height="$1"')
|
233
|
+
.replace(/width=([^" ]+)/g, 'width="$1"')
|
234
|
+
.replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')
|
235
|
+
.replace(/ id=([^" >]+)/g, ' id="$1"') // #4003
|
236
|
+
.replace(/class=([^" >]+)/g, 'class="$1"')
|
237
|
+
.replace(/ transform /g, ' ')
|
238
|
+
.replace(/:(path|rect)/g, '$1')
|
239
|
+
.replace(/style="([^"]+)"/g, function(s) {
|
240
|
+
return s.toLowerCase();
|
241
|
+
});
|
242
|
+
|
243
|
+
return svg;
|
244
|
+
};
|
245
|
+
|
246
|
+
// IE compatibility hack for generating SVG content that it doesn't really
|
247
|
+
// understand. Used by the exporting module.
|
248
|
+
if (!doc.createElementNS) {
|
249
|
+
doc.createElementNS = function(ns, tagName) {
|
250
|
+
return doc.createElement(tagName);
|
251
|
+
};
|
252
|
+
}
|
253
|
+
|
254
|
+
/**
|
255
|
+
* Old IE polyfill for addEventListener, called from inside the addEvent
|
256
|
+
* function.
|
257
|
+
*/
|
258
|
+
H.addEventListenerPolyfill = function(type, fn) {
|
259
|
+
var el = this;
|
260
|
+
|
261
|
+
function wrappedFn(e) {
|
262
|
+
e.target = e.srcElement || win; // #2820
|
263
|
+
fn.call(el, e);
|
264
|
+
}
|
265
|
+
|
266
|
+
if (el.attachEvent) {
|
267
|
+
if (!el.hcEventsIE) {
|
268
|
+
el.hcEventsIE = {};
|
269
|
+
}
|
270
|
+
|
271
|
+
// unique function string (#6746)
|
272
|
+
if (!fn.hcKey) {
|
273
|
+
fn.hcKey = H.uniqueKey();
|
274
|
+
}
|
275
|
+
|
276
|
+
// Link wrapped fn with original fn, so we can get this in
|
277
|
+
// removeEvent
|
278
|
+
el.hcEventsIE[fn.hcKey] = wrappedFn;
|
279
|
+
|
280
|
+
el.attachEvent('on' + type, wrappedFn);
|
281
|
+
}
|
282
|
+
|
283
|
+
};
|
284
|
+
H.removeEventListenerPolyfill = function(type, fn) {
|
285
|
+
if (this.detachEvent) {
|
286
|
+
fn = this.hcEventsIE[fn.hcKey];
|
287
|
+
this.detachEvent('on' + type, fn);
|
288
|
+
}
|
289
|
+
};
|
290
|
+
|
291
|
+
|
292
|
+
/**
|
293
|
+
* The VML element wrapper.
|
294
|
+
*/
|
295
|
+
VMLElement = {
|
296
|
+
|
297
|
+
docMode8: doc && doc.documentMode === 8,
|
298
|
+
|
299
|
+
/**
|
300
|
+
* Initialize a new VML element wrapper. It builds the markup as a string
|
301
|
+
* to minimize DOM traffic.
|
302
|
+
* @param {Object} renderer
|
303
|
+
* @param {Object} nodeName
|
304
|
+
*/
|
305
|
+
init: function(renderer, nodeName) {
|
306
|
+
var wrapper = this,
|
307
|
+
markup = ['<', nodeName, ' filled="f" stroked="f"'],
|
308
|
+
style = ['position: ', 'absolute', ';'],
|
309
|
+
isDiv = nodeName === 'div';
|
310
|
+
|
311
|
+
// divs and shapes need size
|
312
|
+
if (nodeName === 'shape' || isDiv) {
|
313
|
+
style.push('left:0;top:0;width:1px;height:1px;');
|
314
|
+
}
|
315
|
+
style.push('visibility: ', isDiv ? 'hidden' : 'visible');
|
316
|
+
|
317
|
+
markup.push(' style="', style.join(''), '"/>');
|
318
|
+
|
319
|
+
// create element with default attributes and style
|
320
|
+
if (nodeName) {
|
321
|
+
markup = isDiv || nodeName === 'span' || nodeName === 'img' ?
|
322
|
+
markup.join('') :
|
323
|
+
renderer.prepVML(markup);
|
324
|
+
wrapper.element = createElement(markup);
|
325
|
+
}
|
326
|
+
|
327
|
+
wrapper.renderer = renderer;
|
328
|
+
},
|
329
|
+
|
330
|
+
/**
|
331
|
+
* Add the node to the given parent
|
332
|
+
* @param {Object} parent
|
333
|
+
*/
|
334
|
+
add: function(parent) {
|
335
|
+
var wrapper = this,
|
336
|
+
renderer = wrapper.renderer,
|
337
|
+
element = wrapper.element,
|
338
|
+
box = renderer.box,
|
339
|
+
inverted = parent && parent.inverted,
|
340
|
+
|
341
|
+
// get the parent node
|
342
|
+
parentNode = parent ?
|
343
|
+
parent.element || parent :
|
344
|
+
box;
|
345
|
+
|
346
|
+
if (parent) {
|
347
|
+
this.parentGroup = parent;
|
348
|
+
}
|
349
|
+
|
350
|
+
// if the parent group is inverted, apply inversion on all children
|
351
|
+
if (inverted) { // only on groups
|
352
|
+
renderer.invertChild(element, parentNode);
|
353
|
+
}
|
354
|
+
|
355
|
+
// append it
|
356
|
+
parentNode.appendChild(element);
|
357
|
+
|
358
|
+
// align text after adding to be able to read offset
|
359
|
+
wrapper.added = true;
|
360
|
+
if (wrapper.alignOnAdd && !wrapper.deferUpdateTransform) {
|
361
|
+
wrapper.updateTransform();
|
362
|
+
}
|
363
|
+
|
364
|
+
// fire an event for internal hooks
|
365
|
+
if (wrapper.onAdd) {
|
366
|
+
wrapper.onAdd();
|
367
|
+
}
|
368
|
+
|
369
|
+
// IE8 Standards can't set the class name before the element is appended
|
370
|
+
if (this.className) {
|
371
|
+
this.attr('class', this.className);
|
372
|
+
}
|
373
|
+
|
374
|
+
return wrapper;
|
375
|
+
},
|
376
|
+
|
377
|
+
/**
|
378
|
+
* VML always uses htmlUpdateTransform
|
379
|
+
*/
|
380
|
+
updateTransform: SVGElement.prototype.htmlUpdateTransform,
|
381
|
+
|
382
|
+
/**
|
383
|
+
* Set the rotation of a span with oldIE's filter
|
384
|
+
*/
|
385
|
+
setSpanRotation: function() {
|
386
|
+
// Adjust for alignment and rotation. Rotation of useHTML content is not yet implemented
|
387
|
+
// but it can probably be implemented for Firefox 3.5+ on user request. FF3.5+
|
388
|
+
// has support for CSS3 transform. The getBBox method also needs to be updated
|
389
|
+
// to compensate for the rotation, like it currently does for SVG.
|
390
|
+
// Test case: http://jsfiddle.net/highcharts/Ybt44/
|
391
|
+
|
392
|
+
var rotation = this.rotation,
|
393
|
+
costheta = Math.cos(rotation * deg2rad),
|
394
|
+
sintheta = Math.sin(rotation * deg2rad);
|
395
|
+
|
396
|
+
css(this.element, {
|
397
|
+
filter: rotation ? ['progid:DXImageTransform.Microsoft.Matrix(M11=', costheta,
|
398
|
+
', M12=', -sintheta, ', M21=', sintheta, ', M22=', costheta,
|
399
|
+
', sizingMethod=\'auto expand\')'
|
400
|
+
].join('') : 'none'
|
401
|
+
});
|
402
|
+
},
|
403
|
+
|
404
|
+
/**
|
405
|
+
* Get the positioning correction for the span after rotating.
|
406
|
+
*/
|
407
|
+
getSpanCorrection: function(width, baseline, alignCorrection, rotation, align) {
|
408
|
+
|
409
|
+
var costheta = rotation ? Math.cos(rotation * deg2rad) : 1,
|
410
|
+
sintheta = rotation ? Math.sin(rotation * deg2rad) : 0,
|
411
|
+
height = pick(this.elemHeight, this.element.offsetHeight),
|
412
|
+
quad,
|
413
|
+
nonLeft = align && align !== 'left';
|
414
|
+
|
415
|
+
// correct x and y
|
416
|
+
this.xCorr = costheta < 0 && -width;
|
417
|
+
this.yCorr = sintheta < 0 && -height;
|
418
|
+
|
419
|
+
// correct for baseline and corners spilling out after rotation
|
420
|
+
quad = costheta * sintheta < 0;
|
421
|
+
this.xCorr += sintheta * baseline * (quad ? 1 - alignCorrection : alignCorrection);
|
422
|
+
this.yCorr -= costheta * baseline * (rotation ? (quad ? alignCorrection : 1 - alignCorrection) : 1);
|
423
|
+
// correct for the length/height of the text
|
424
|
+
if (nonLeft) {
|
425
|
+
this.xCorr -= width * alignCorrection * (costheta < 0 ? -1 : 1);
|
426
|
+
if (rotation) {
|
427
|
+
this.yCorr -= height * alignCorrection * (sintheta < 0 ? -1 : 1);
|
428
|
+
}
|
429
|
+
css(this.element, {
|
430
|
+
textAlign: align
|
431
|
+
});
|
432
|
+
}
|
433
|
+
},
|
434
|
+
|
435
|
+
/**
|
436
|
+
* Converts a subset of an SVG path definition to its VML counterpart. Takes an array
|
437
|
+
* as the parameter and returns a string.
|
438
|
+
*/
|
439
|
+
pathToVML: function(value) {
|
440
|
+
// convert paths
|
441
|
+
var i = value.length,
|
442
|
+
path = [];
|
443
|
+
|
444
|
+
while (i--) {
|
445
|
+
|
446
|
+
// Multiply by 10 to allow subpixel precision.
|
447
|
+
// Substracting half a pixel seems to make the coordinates
|
448
|
+
// align with SVG, but this hasn't been tested thoroughly
|
449
|
+
if (isNumber(value[i])) {
|
450
|
+
path[i] = Math.round(value[i] * 10) - 5;
|
451
|
+
} else if (value[i] === 'Z') { // close the path
|
452
|
+
path[i] = 'x';
|
453
|
+
} else {
|
454
|
+
path[i] = value[i];
|
455
|
+
|
456
|
+
// When the start X and end X coordinates of an arc are too close,
|
457
|
+
// they are rounded to the same value above. In this case, substract or
|
458
|
+
// add 1 from the end X and Y positions. #186, #760, #1371, #1410.
|
459
|
+
if (value.isArc && (value[i] === 'wa' || value[i] === 'at')) {
|
460
|
+
// Start and end X
|
461
|
+
if (path[i + 5] === path[i + 7]) {
|
462
|
+
path[i + 7] += value[i + 7] > value[i + 5] ? 1 : -1;
|
463
|
+
}
|
464
|
+
// Start and end Y
|
465
|
+
if (path[i + 6] === path[i + 8]) {
|
466
|
+
path[i + 8] += value[i + 8] > value[i + 6] ? 1 : -1;
|
467
|
+
}
|
468
|
+
}
|
469
|
+
}
|
470
|
+
}
|
471
|
+
|
472
|
+
return path.join(' ') || 'x';
|
473
|
+
},
|
474
|
+
|
475
|
+
/**
|
476
|
+
* Set the element's clipping to a predefined rectangle
|
477
|
+
*
|
478
|
+
* @param {String} id The id of the clip rectangle
|
479
|
+
*/
|
480
|
+
clip: function(clipRect) {
|
481
|
+
var wrapper = this,
|
482
|
+
clipMembers,
|
483
|
+
cssRet;
|
484
|
+
|
485
|
+
if (clipRect) {
|
486
|
+
clipMembers = clipRect.members;
|
487
|
+
erase(clipMembers, wrapper); // Ensure unique list of elements (#1258)
|
488
|
+
clipMembers.push(wrapper);
|
489
|
+
wrapper.destroyClip = function() {
|
490
|
+
erase(clipMembers, wrapper);
|
491
|
+
};
|
492
|
+
cssRet = clipRect.getCSS(wrapper);
|
493
|
+
|
494
|
+
} else {
|
495
|
+
if (wrapper.destroyClip) {
|
496
|
+
wrapper.destroyClip();
|
497
|
+
}
|
498
|
+
cssRet = {
|
499
|
+
clip: wrapper.docMode8 ? 'inherit' : 'rect(auto)'
|
500
|
+
}; // #1214
|
501
|
+
}
|
502
|
+
|
503
|
+
return wrapper.css(cssRet);
|
504
|
+
|
505
|
+
},
|
506
|
+
|
507
|
+
/**
|
508
|
+
* Set styles for the element
|
509
|
+
* @param {Object} styles
|
510
|
+
*/
|
511
|
+
css: SVGElement.prototype.htmlCss,
|
512
|
+
|
513
|
+
/**
|
514
|
+
* Removes a child either by removeChild or move to garbageBin.
|
515
|
+
* Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not.
|
516
|
+
*/
|
517
|
+
safeRemoveChild: function(element) {
|
518
|
+
// discardElement will detach the node from its parent before attaching it
|
519
|
+
// to the garbage bin. Therefore it is important that the node is attached and have parent.
|
520
|
+
if (element.parentNode) {
|
521
|
+
discardElement(element);
|
522
|
+
}
|
523
|
+
},
|
524
|
+
|
525
|
+
/**
|
526
|
+
* Extend element.destroy by removing it from the clip members array
|
527
|
+
*/
|
528
|
+
destroy: function() {
|
529
|
+
if (this.destroyClip) {
|
530
|
+
this.destroyClip();
|
531
|
+
}
|
532
|
+
|
533
|
+
return SVGElement.prototype.destroy.apply(this);
|
534
|
+
},
|
535
|
+
|
536
|
+
/**
|
537
|
+
* Add an event listener. VML override for normalizing event parameters.
|
538
|
+
* @param {String} eventType
|
539
|
+
* @param {Function} handler
|
540
|
+
*/
|
541
|
+
on: function(eventType, handler) {
|
542
|
+
// simplest possible event model for internal use
|
543
|
+
this.element['on' + eventType] = function() {
|
544
|
+
var evt = win.event;
|
545
|
+
evt.target = evt.srcElement;
|
546
|
+
handler(evt);
|
547
|
+
};
|
548
|
+
return this;
|
549
|
+
},
|
550
|
+
|
551
|
+
/**
|
552
|
+
* In stacked columns, cut off the shadows so that they don't overlap
|
553
|
+
*/
|
554
|
+
cutOffPath: function(path, length) {
|
555
|
+
|
556
|
+
var len;
|
557
|
+
|
558
|
+
path = path.split(/[ ,]/); // The extra comma tricks the trailing comma remover in "gulp scripts" task
|
559
|
+
len = path.length;
|
560
|
+
|
561
|
+
if (len === 9 || len === 11) {
|
562
|
+
path[len - 4] = path[len - 2] = pInt(path[len - 2]) - 10 * length;
|
563
|
+
}
|
564
|
+
return path.join(' ');
|
565
|
+
},
|
566
|
+
|
567
|
+
/**
|
568
|
+
* Apply a drop shadow by copying elements and giving them different strokes
|
569
|
+
* @param {Boolean|Object} shadowOptions
|
570
|
+
*/
|
571
|
+
shadow: function(shadowOptions, group, cutOff) {
|
572
|
+
var shadows = [],
|
573
|
+
i,
|
574
|
+
element = this.element,
|
575
|
+
renderer = this.renderer,
|
576
|
+
shadow,
|
577
|
+
elemStyle = element.style,
|
578
|
+
markup,
|
579
|
+
path = element.path,
|
580
|
+
strokeWidth,
|
581
|
+
modifiedPath,
|
582
|
+
shadowWidth,
|
583
|
+
shadowElementOpacity;
|
584
|
+
|
585
|
+
// some times empty paths are not strings
|
586
|
+
if (path && typeof path.value !== 'string') {
|
587
|
+
path = 'x';
|
588
|
+
}
|
589
|
+
modifiedPath = path;
|
590
|
+
|
591
|
+
if (shadowOptions) {
|
592
|
+
shadowWidth = pick(shadowOptions.width, 3);
|
593
|
+
shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth;
|
594
|
+
for (i = 1; i <= 3; i++) {
|
595
|
+
|
596
|
+
strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
|
597
|
+
|
598
|
+
// Cut off shadows for stacked column items
|
599
|
+
if (cutOff) {
|
600
|
+
modifiedPath = this.cutOffPath(path.value, strokeWidth + 0.5);
|
601
|
+
}
|
602
|
+
|
603
|
+
markup = ['<shape isShadow="true" strokeweight="', strokeWidth,
|
604
|
+
'" filled="false" path="', modifiedPath,
|
605
|
+
'" coordsize="10 10" style="', element.style.cssText, '" />'
|
606
|
+
];
|
607
|
+
|
608
|
+
shadow = createElement(renderer.prepVML(markup),
|
609
|
+
null, {
|
610
|
+
left: pInt(elemStyle.left) + pick(shadowOptions.offsetX, 1),
|
611
|
+
top: pInt(elemStyle.top) + pick(shadowOptions.offsetY, 1)
|
612
|
+
}
|
613
|
+
);
|
614
|
+
if (cutOff) {
|
615
|
+
shadow.cutOff = strokeWidth + 1;
|
616
|
+
}
|
617
|
+
|
618
|
+
// apply the opacity
|
619
|
+
markup = [
|
620
|
+
'<stroke color="',
|
621
|
+
shadowOptions.color || '#000000',
|
622
|
+
'" opacity="', shadowElementOpacity * i, '"/>'
|
623
|
+
];
|
624
|
+
createElement(renderer.prepVML(markup), null, null, shadow);
|
625
|
+
|
626
|
+
|
627
|
+
// insert it
|
628
|
+
if (group) {
|
629
|
+
group.element.appendChild(shadow);
|
630
|
+
} else {
|
631
|
+
element.parentNode.insertBefore(shadow, element);
|
632
|
+
}
|
633
|
+
|
634
|
+
// record it
|
635
|
+
shadows.push(shadow);
|
636
|
+
|
637
|
+
}
|
638
|
+
|
639
|
+
this.shadows = shadows;
|
640
|
+
}
|
641
|
+
return this;
|
642
|
+
},
|
643
|
+
updateShadows: noop, // Used in SVG only
|
644
|
+
|
645
|
+
setAttr: function(key, value) {
|
646
|
+
if (this.docMode8) { // IE8 setAttribute bug
|
647
|
+
this.element[key] = value;
|
648
|
+
} else {
|
649
|
+
this.element.setAttribute(key, value);
|
650
|
+
}
|
651
|
+
},
|
652
|
+
classSetter: function(value) {
|
653
|
+
// IE8 Standards mode has problems retrieving the className unless set like this.
|
654
|
+
// IE8 Standards can't set the class name before the element is appended.
|
655
|
+
(this.added ? this.element : this).className = value;
|
656
|
+
},
|
657
|
+
dashstyleSetter: function(value, key, element) {
|
658
|
+
var strokeElem = element.getElementsByTagName('stroke')[0] ||
|
659
|
+
createElement(this.renderer.prepVML(['<stroke/>']), null, null, element);
|
660
|
+
strokeElem[key] = value || 'solid';
|
661
|
+
this[key] = value;
|
662
|
+
/* because changing stroke-width will change the dash length
|
663
|
+
and cause an epileptic effect */
|
664
|
+
},
|
665
|
+
dSetter: function(value, key, element) {
|
666
|
+
var i,
|
667
|
+
shadows = this.shadows;
|
668
|
+
value = value || [];
|
669
|
+
this.d = value.join && value.join(' '); // used in getter for animation
|
670
|
+
|
671
|
+
element.path = value = this.pathToVML(value);
|
672
|
+
|
673
|
+
// update shadows
|
674
|
+
if (shadows) {
|
675
|
+
i = shadows.length;
|
676
|
+
while (i--) {
|
677
|
+
shadows[i].path = shadows[i].cutOff ? this.cutOffPath(value, shadows[i].cutOff) : value;
|
678
|
+
}
|
679
|
+
}
|
680
|
+
this.setAttr(key, value);
|
681
|
+
},
|
682
|
+
fillSetter: function(value, key, element) {
|
683
|
+
var nodeName = element.nodeName;
|
684
|
+
if (nodeName === 'SPAN') { // text color
|
685
|
+
element.style.color = value;
|
686
|
+
} else if (nodeName !== 'IMG') { // #1336
|
687
|
+
element.filled = value !== 'none';
|
688
|
+
this.setAttr('fillcolor', this.renderer.color(value, element, key, this));
|
689
|
+
}
|
690
|
+
},
|
691
|
+
'fill-opacitySetter': function(value, key, element) {
|
692
|
+
createElement(
|
693
|
+
this.renderer.prepVML(['<', key.split('-')[0], ' opacity="', value, '"/>']),
|
694
|
+
null,
|
695
|
+
null,
|
696
|
+
element
|
697
|
+
);
|
698
|
+
},
|
699
|
+
opacitySetter: noop, // Don't bother - animation is too slow and filters introduce artifacts
|
700
|
+
rotationSetter: function(value, key, element) {
|
701
|
+
var style = element.style;
|
702
|
+
this[key] = style[key] = value; // style is for #1873
|
703
|
+
|
704
|
+
// Correction for the 1x1 size of the shape container. Used in gauge needles.
|
705
|
+
style.left = -Math.round(Math.sin(value * deg2rad) + 1) + 'px';
|
706
|
+
style.top = Math.round(Math.cos(value * deg2rad)) + 'px';
|
707
|
+
},
|
708
|
+
strokeSetter: function(value, key, element) {
|
709
|
+
this.setAttr('strokecolor', this.renderer.color(value, element, key, this));
|
710
|
+
},
|
711
|
+
'stroke-widthSetter': function(value, key, element) {
|
712
|
+
element.stroked = !!value; // VML "stroked" attribute
|
713
|
+
this[key] = value; // used in getter, issue #113
|
714
|
+
if (isNumber(value)) {
|
715
|
+
value += 'px';
|
716
|
+
}
|
717
|
+
this.setAttr('strokeweight', value);
|
718
|
+
},
|
719
|
+
titleSetter: function(value, key) {
|
720
|
+
this.setAttr(key, value);
|
721
|
+
},
|
722
|
+
visibilitySetter: function(value, key, element) {
|
723
|
+
|
724
|
+
// Handle inherited visibility
|
725
|
+
if (value === 'inherit') {
|
726
|
+
value = 'visible';
|
727
|
+
}
|
728
|
+
|
729
|
+
// Let the shadow follow the main element
|
730
|
+
if (this.shadows) {
|
731
|
+
each(this.shadows, function(shadow) {
|
732
|
+
shadow.style[key] = value;
|
733
|
+
});
|
734
|
+
}
|
735
|
+
|
736
|
+
// Instead of toggling the visibility CSS property, move the div out of the viewport.
|
737
|
+
// This works around #61 and #586
|
738
|
+
if (element.nodeName === 'DIV') {
|
739
|
+
value = value === 'hidden' ? '-999em' : 0;
|
740
|
+
|
741
|
+
// In order to redraw, IE7 needs the div to be visible when tucked away
|
742
|
+
// outside the viewport. So the visibility is actually opposite of
|
743
|
+
// the expected value. This applies to the tooltip only.
|
744
|
+
if (!this.docMode8) {
|
745
|
+
element.style[key] = value ? 'visible' : 'hidden';
|
746
|
+
}
|
747
|
+
key = 'top';
|
748
|
+
}
|
749
|
+
element.style[key] = value;
|
750
|
+
},
|
751
|
+
xSetter: function(value, key, element) {
|
752
|
+
this[key] = value; // used in getter
|
753
|
+
|
754
|
+
if (key === 'x') {
|
755
|
+
key = 'left';
|
756
|
+
} else if (key === 'y') {
|
757
|
+
key = 'top';
|
758
|
+
}
|
759
|
+
/* else {
|
760
|
+
value = Math.max(0, value); // don't set width or height below zero (#311)
|
761
|
+
}*/
|
762
|
+
|
763
|
+
// clipping rectangle special
|
764
|
+
if (this.updateClipping) {
|
765
|
+
this[key] = value; // the key is now 'left' or 'top' for 'x' and 'y'
|
766
|
+
this.updateClipping();
|
767
|
+
} else {
|
768
|
+
// normal
|
769
|
+
element.style[key] = value;
|
770
|
+
}
|
771
|
+
},
|
772
|
+
zIndexSetter: function(value, key, element) {
|
773
|
+
element.style[key] = value;
|
774
|
+
}
|
775
|
+
};
|
776
|
+
VMLElement['stroke-opacitySetter'] = VMLElement['fill-opacitySetter'];
|
777
|
+
H.VMLElement = VMLElement = extendClass(SVGElement, VMLElement);
|
778
|
+
|
779
|
+
// Some shared setters
|
780
|
+
VMLElement.prototype.ySetter =
|
781
|
+
VMLElement.prototype.widthSetter =
|
782
|
+
VMLElement.prototype.heightSetter =
|
783
|
+
VMLElement.prototype.xSetter;
|
784
|
+
|
785
|
+
|
786
|
+
/**
|
787
|
+
* The VML renderer
|
788
|
+
*/
|
789
|
+
VMLRendererExtension = { // inherit SVGRenderer
|
790
|
+
|
791
|
+
Element: VMLElement,
|
792
|
+
isIE8: win.navigator.userAgent.indexOf('MSIE 8.0') > -1,
|
793
|
+
|
794
|
+
|
795
|
+
/**
|
796
|
+
* Initialize the VMLRenderer
|
797
|
+
* @param {Object} container
|
798
|
+
* @param {Number} width
|
799
|
+
* @param {Number} height
|
800
|
+
*/
|
801
|
+
init: function(container, width, height) {
|
802
|
+
var renderer = this,
|
803
|
+
boxWrapper,
|
804
|
+
box,
|
805
|
+
css;
|
806
|
+
|
807
|
+
renderer.alignedObjects = [];
|
808
|
+
|
809
|
+
boxWrapper = renderer.createElement('div')
|
810
|
+
.css({
|
811
|
+
position: 'relative'
|
812
|
+
});
|
813
|
+
box = boxWrapper.element;
|
814
|
+
container.appendChild(boxWrapper.element);
|
815
|
+
|
816
|
+
|
817
|
+
// generate the containing box
|
818
|
+
renderer.isVML = true;
|
819
|
+
renderer.box = box;
|
820
|
+
renderer.boxWrapper = boxWrapper;
|
821
|
+
renderer.gradients = {};
|
822
|
+
renderer.cache = {}; // Cache for numerical bounding boxes
|
823
|
+
renderer.cacheKeys = [];
|
824
|
+
renderer.imgCount = 0;
|
825
|
+
|
826
|
+
|
827
|
+
renderer.setSize(width, height, false);
|
828
|
+
|
829
|
+
// The only way to make IE6 and IE7 print is to use a global namespace. However,
|
830
|
+
// with IE8 the only way to make the dynamic shapes visible in screen and print mode
|
831
|
+
// seems to be to add the xmlns attribute and the behaviour style inline.
|
832
|
+
if (!doc.namespaces.hcv) {
|
833
|
+
|
834
|
+
doc.namespaces.add('hcv', 'urn:schemas-microsoft-com:vml');
|
835
|
+
|
836
|
+
// Setup default CSS (#2153, #2368, #2384)
|
837
|
+
css = 'hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke' +
|
838
|
+
'{ behavior:url(#default#VML); display: inline-block; } ';
|
839
|
+
try {
|
840
|
+
doc.createStyleSheet().cssText = css;
|
841
|
+
} catch (e) {
|
842
|
+
doc.styleSheets[0].cssText += css;
|
843
|
+
}
|
844
|
+
|
845
|
+
}
|
846
|
+
},
|
847
|
+
|
848
|
+
|
849
|
+
/**
|
850
|
+
* Detect whether the renderer is hidden. This happens when one of the parent elements
|
851
|
+
* has display: none
|
852
|
+
*/
|
853
|
+
isHidden: function() {
|
854
|
+
return !this.box.offsetWidth;
|
855
|
+
},
|
856
|
+
|
857
|
+
/**
|
858
|
+
* Define a clipping rectangle. In VML it is accomplished by storing the values
|
859
|
+
* for setting the CSS style to all associated members.
|
860
|
+
*
|
861
|
+
* @param {Number} x
|
862
|
+
* @param {Number} y
|
863
|
+
* @param {Number} width
|
864
|
+
* @param {Number} height
|
865
|
+
*/
|
866
|
+
clipRect: function(x, y, width, height) {
|
867
|
+
|
868
|
+
// create a dummy element
|
869
|
+
var clipRect = this.createElement(),
|
870
|
+
isObj = isObject(x);
|
871
|
+
|
872
|
+
// mimic a rectangle with its style object for automatic updating in attr
|
873
|
+
return extend(clipRect, {
|
874
|
+
members: [],
|
875
|
+
count: 0,
|
876
|
+
left: (isObj ? x.x : x) + 1,
|
877
|
+
top: (isObj ? x.y : y) + 1,
|
878
|
+
width: (isObj ? x.width : width) - 1,
|
879
|
+
height: (isObj ? x.height : height) - 1,
|
880
|
+
getCSS: function(wrapper) {
|
881
|
+
var element = wrapper.element,
|
882
|
+
nodeName = element.nodeName,
|
883
|
+
isShape = nodeName === 'shape',
|
884
|
+
inverted = wrapper.inverted,
|
885
|
+
rect = this,
|
886
|
+
top = rect.top - (isShape ? element.offsetTop : 0),
|
887
|
+
left = rect.left,
|
888
|
+
right = left + rect.width,
|
889
|
+
bottom = top + rect.height,
|
890
|
+
ret = {
|
891
|
+
clip: 'rect(' +
|
892
|
+
Math.round(inverted ? left : top) + 'px,' +
|
893
|
+
Math.round(inverted ? bottom : right) + 'px,' +
|
894
|
+
Math.round(inverted ? right : bottom) + 'px,' +
|
895
|
+
Math.round(inverted ? top : left) + 'px)'
|
896
|
+
};
|
897
|
+
|
898
|
+
// issue 74 workaround
|
899
|
+
if (!inverted && wrapper.docMode8 && nodeName === 'DIV') {
|
900
|
+
extend(ret, {
|
901
|
+
width: right + 'px',
|
902
|
+
height: bottom + 'px'
|
903
|
+
});
|
904
|
+
}
|
905
|
+
return ret;
|
906
|
+
},
|
907
|
+
|
908
|
+
// used in attr and animation to update the clipping of all members
|
909
|
+
updateClipping: function() {
|
910
|
+
each(clipRect.members, function(member) {
|
911
|
+
// Member.element is falsy on deleted series, like in
|
912
|
+
// stock/members/series-remove demo. Should be removed
|
913
|
+
// from members, but this will do.
|
914
|
+
if (member.element) {
|
915
|
+
member.css(clipRect.getCSS(member));
|
916
|
+
}
|
917
|
+
});
|
918
|
+
}
|
919
|
+
});
|
920
|
+
|
921
|
+
},
|
922
|
+
|
923
|
+
|
924
|
+
/**
|
925
|
+
* Take a color and return it if it's a string, make it a gradient if it's a
|
926
|
+
* gradient configuration object, and apply opacity.
|
927
|
+
*
|
928
|
+
* @param {Object} color The color or config object
|
929
|
+
*/
|
930
|
+
color: function(color, elem, prop, wrapper) {
|
931
|
+
var renderer = this,
|
932
|
+
colorObject,
|
933
|
+
regexRgba = /^rgba/,
|
934
|
+
markup,
|
935
|
+
fillType,
|
936
|
+
ret = 'none';
|
937
|
+
|
938
|
+
// Check for linear or radial gradient
|
939
|
+
if (color && color.linearGradient) {
|
940
|
+
fillType = 'gradient';
|
941
|
+
} else if (color && color.radialGradient) {
|
942
|
+
fillType = 'pattern';
|
943
|
+
}
|
944
|
+
|
945
|
+
|
946
|
+
if (fillType) {
|
947
|
+
|
948
|
+
var stopColor,
|
949
|
+
stopOpacity,
|
950
|
+
gradient = color.linearGradient || color.radialGradient,
|
951
|
+
x1,
|
952
|
+
y1,
|
953
|
+
x2,
|
954
|
+
y2,
|
955
|
+
opacity1,
|
956
|
+
opacity2,
|
957
|
+
color1,
|
958
|
+
color2,
|
959
|
+
fillAttr = '',
|
960
|
+
stops = color.stops,
|
961
|
+
firstStop,
|
962
|
+
lastStop,
|
963
|
+
colors = [],
|
964
|
+
addFillNode = function() {
|
965
|
+
// Add the fill subnode. When colors attribute is used, the meanings of opacity and o:opacity2
|
966
|
+
// are reversed.
|
967
|
+
markup = ['<fill colors="' + colors.join(',') +
|
968
|
+
'" opacity="', opacity2, '" o:opacity2="',
|
969
|
+
opacity1, '" type="', fillType, '" ', fillAttr,
|
970
|
+
'focus="100%" method="any" />'
|
971
|
+
];
|
972
|
+
createElement(renderer.prepVML(markup), null, null, elem);
|
973
|
+
};
|
974
|
+
|
975
|
+
// Extend from 0 to 1
|
976
|
+
firstStop = stops[0];
|
977
|
+
lastStop = stops[stops.length - 1];
|
978
|
+
if (firstStop[0] > 0) {
|
979
|
+
stops.unshift([
|
980
|
+
0,
|
981
|
+
firstStop[1]
|
982
|
+
]);
|
983
|
+
}
|
984
|
+
if (lastStop[0] < 1) {
|
985
|
+
stops.push([
|
986
|
+
1,
|
987
|
+
lastStop[1]
|
988
|
+
]);
|
989
|
+
}
|
990
|
+
|
991
|
+
// Compute the stops
|
992
|
+
each(stops, function(stop, i) {
|
993
|
+
if (regexRgba.test(stop[1])) {
|
994
|
+
colorObject = H.color(stop[1]);
|
995
|
+
stopColor = colorObject.get('rgb');
|
996
|
+
stopOpacity = colorObject.get('a');
|
997
|
+
} else {
|
998
|
+
stopColor = stop[1];
|
999
|
+
stopOpacity = 1;
|
1000
|
+
}
|
1001
|
+
|
1002
|
+
// Build the color attribute
|
1003
|
+
colors.push((stop[0] * 100) + '% ' + stopColor);
|
1004
|
+
|
1005
|
+
// Only start and end opacities are allowed, so we use the first and the last
|
1006
|
+
if (!i) {
|
1007
|
+
opacity1 = stopOpacity;
|
1008
|
+
color2 = stopColor;
|
1009
|
+
} else {
|
1010
|
+
opacity2 = stopOpacity;
|
1011
|
+
color1 = stopColor;
|
1012
|
+
}
|
1013
|
+
});
|
1014
|
+
|
1015
|
+
// Apply the gradient to fills only.
|
1016
|
+
if (prop === 'fill') {
|
1017
|
+
|
1018
|
+
// Handle linear gradient angle
|
1019
|
+
if (fillType === 'gradient') {
|
1020
|
+
x1 = gradient.x1 || gradient[0] || 0;
|
1021
|
+
y1 = gradient.y1 || gradient[1] || 0;
|
1022
|
+
x2 = gradient.x2 || gradient[2] || 0;
|
1023
|
+
y2 = gradient.y2 || gradient[3] || 0;
|
1024
|
+
fillAttr = 'angle="' + (90 - Math.atan(
|
1025
|
+
(y2 - y1) / // y vector
|
1026
|
+
(x2 - x1) // x vector
|
1027
|
+
) * 180 / Math.PI) + '"';
|
1028
|
+
|
1029
|
+
addFillNode();
|
1030
|
+
|
1031
|
+
// Radial (circular) gradient
|
1032
|
+
} else {
|
1033
|
+
|
1034
|
+
var r = gradient.r,
|
1035
|
+
sizex = r * 2,
|
1036
|
+
sizey = r * 2,
|
1037
|
+
cx = gradient.cx,
|
1038
|
+
cy = gradient.cy,
|
1039
|
+
radialReference = elem.radialReference,
|
1040
|
+
bBox,
|
1041
|
+
applyRadialGradient = function() {
|
1042
|
+
if (radialReference) {
|
1043
|
+
bBox = wrapper.getBBox();
|
1044
|
+
cx += (radialReference[0] - bBox.x) / bBox.width - 0.5;
|
1045
|
+
cy += (radialReference[1] - bBox.y) / bBox.height - 0.5;
|
1046
|
+
sizex *= radialReference[2] / bBox.width;
|
1047
|
+
sizey *= radialReference[2] / bBox.height;
|
1048
|
+
}
|
1049
|
+
fillAttr = 'src="' + H.getOptions().global.VMLRadialGradientURL + '" ' +
|
1050
|
+
'size="' + sizex + ',' + sizey + '" ' +
|
1051
|
+
'origin="0.5,0.5" ' +
|
1052
|
+
'position="' + cx + ',' + cy + '" ' +
|
1053
|
+
'color2="' + color2 + '" ';
|
1054
|
+
|
1055
|
+
addFillNode();
|
1056
|
+
};
|
1057
|
+
|
1058
|
+
// Apply radial gradient
|
1059
|
+
if (wrapper.added) {
|
1060
|
+
applyRadialGradient();
|
1061
|
+
} else {
|
1062
|
+
// We need to know the bounding box to get the size and position right
|
1063
|
+
wrapper.onAdd = applyRadialGradient;
|
1064
|
+
}
|
1065
|
+
|
1066
|
+
// The fill element's color attribute is broken in IE8 standards mode, so we
|
1067
|
+
// need to set the parent shape's fillcolor attribute instead.
|
1068
|
+
ret = color1;
|
1069
|
+
}
|
1070
|
+
|
1071
|
+
// Gradients are not supported for VML stroke, return the first color. #722.
|
1072
|
+
} else {
|
1073
|
+
ret = stopColor;
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
// If the color is an rgba color, split it and add a fill node
|
1077
|
+
// to hold the opacity component
|
1078
|
+
} else if (regexRgba.test(color) && elem.tagName !== 'IMG') {
|
1079
|
+
|
1080
|
+
colorObject = H.color(color);
|
1081
|
+
|
1082
|
+
wrapper[prop + '-opacitySetter'](colorObject.get('a'), prop, elem);
|
1083
|
+
|
1084
|
+
ret = colorObject.get('rgb');
|
1085
|
+
|
1086
|
+
|
1087
|
+
} else {
|
1088
|
+
var propNodes = elem.getElementsByTagName(prop); // 'stroke' or 'fill' node
|
1089
|
+
if (propNodes.length) {
|
1090
|
+
propNodes[0].opacity = 1;
|
1091
|
+
propNodes[0].type = 'solid';
|
1092
|
+
}
|
1093
|
+
ret = color;
|
1094
|
+
}
|
1095
|
+
|
1096
|
+
return ret;
|
1097
|
+
},
|
1098
|
+
|
1099
|
+
/**
|
1100
|
+
* Take a VML string and prepare it for either IE8 or IE6/IE7.
|
1101
|
+
* @param {Array} markup A string array of the VML markup to prepare
|
1102
|
+
*/
|
1103
|
+
prepVML: function(markup) {
|
1104
|
+
var vmlStyle = 'display:inline-block;behavior:url(#default#VML);',
|
1105
|
+
isIE8 = this.isIE8;
|
1106
|
+
|
1107
|
+
markup = markup.join('');
|
1108
|
+
|
1109
|
+
if (isIE8) { // add xmlns and style inline
|
1110
|
+
markup = markup.replace('/>', ' xmlns="urn:schemas-microsoft-com:vml" />');
|
1111
|
+
if (markup.indexOf('style="') === -1) {
|
1112
|
+
markup = markup.replace('/>', ' style="' + vmlStyle + '" />');
|
1113
|
+
} else {
|
1114
|
+
markup = markup.replace('style="', 'style="' + vmlStyle);
|
1115
|
+
}
|
1116
|
+
|
1117
|
+
} else { // add namespace
|
1118
|
+
markup = markup.replace('<', '<hcv:');
|
1119
|
+
}
|
1120
|
+
|
1121
|
+
return markup;
|
1122
|
+
},
|
1123
|
+
|
1124
|
+
/**
|
1125
|
+
* Create rotated and aligned text
|
1126
|
+
* @param {String} str
|
1127
|
+
* @param {Number} x
|
1128
|
+
* @param {Number} y
|
1129
|
+
*/
|
1130
|
+
text: SVGRenderer.prototype.html,
|
1131
|
+
|
1132
|
+
/**
|
1133
|
+
* Create and return a path element
|
1134
|
+
* @param {Array} path
|
1135
|
+
*/
|
1136
|
+
path: function(path) {
|
1137
|
+
var attr = {
|
1138
|
+
// subpixel precision down to 0.1 (width and height = 1px)
|
1139
|
+
coordsize: '10 10'
|
1140
|
+
};
|
1141
|
+
if (isArray(path)) {
|
1142
|
+
attr.d = path;
|
1143
|
+
} else if (isObject(path)) { // attributes
|
1144
|
+
extend(attr, path);
|
1145
|
+
}
|
1146
|
+
// create the shape
|
1147
|
+
return this.createElement('shape').attr(attr);
|
1148
|
+
},
|
1149
|
+
|
1150
|
+
/**
|
1151
|
+
* Create and return a circle element. In VML circles are implemented as
|
1152
|
+
* shapes, which is faster than v:oval
|
1153
|
+
* @param {Number} x
|
1154
|
+
* @param {Number} y
|
1155
|
+
* @param {Number} r
|
1156
|
+
*/
|
1157
|
+
circle: function(x, y, r) {
|
1158
|
+
var circle = this.symbol('circle');
|
1159
|
+
if (isObject(x)) {
|
1160
|
+
r = x.r;
|
1161
|
+
y = x.y;
|
1162
|
+
x = x.x;
|
1163
|
+
}
|
1164
|
+
circle.isCircle = true; // Causes x and y to mean center (#1682)
|
1165
|
+
circle.r = r;
|
1166
|
+
return circle.attr({
|
1167
|
+
x: x,
|
1168
|
+
y: y
|
1169
|
+
});
|
1170
|
+
},
|
1171
|
+
|
1172
|
+
/**
|
1173
|
+
* Create a group using an outer div and an inner v:group to allow rotating
|
1174
|
+
* and flipping. A simple v:group would have problems with positioning
|
1175
|
+
* child HTML elements and CSS clip.
|
1176
|
+
*
|
1177
|
+
* @param {String} name The name of the group
|
1178
|
+
*/
|
1179
|
+
g: function(name) {
|
1180
|
+
var wrapper,
|
1181
|
+
attribs;
|
1182
|
+
|
1183
|
+
// set the class name
|
1184
|
+
if (name) {
|
1185
|
+
attribs = {
|
1186
|
+
'className': 'highcharts-' + name,
|
1187
|
+
'class': 'highcharts-' + name
|
1188
|
+
};
|
1189
|
+
}
|
1190
|
+
|
1191
|
+
// the div to hold HTML and clipping
|
1192
|
+
wrapper = this.createElement('div').attr(attribs);
|
1193
|
+
|
1194
|
+
return wrapper;
|
1195
|
+
},
|
1196
|
+
|
1197
|
+
/**
|
1198
|
+
* VML override to create a regular HTML image
|
1199
|
+
* @param {String} src
|
1200
|
+
* @param {Number} x
|
1201
|
+
* @param {Number} y
|
1202
|
+
* @param {Number} width
|
1203
|
+
* @param {Number} height
|
1204
|
+
*/
|
1205
|
+
image: function(src, x, y, width, height) {
|
1206
|
+
var obj = this.createElement('img')
|
1207
|
+
.attr({
|
1208
|
+
src: src
|
1209
|
+
});
|
1210
|
+
|
1211
|
+
if (arguments.length > 1) {
|
1212
|
+
obj.attr({
|
1213
|
+
x: x,
|
1214
|
+
y: y,
|
1215
|
+
width: width,
|
1216
|
+
height: height
|
1217
|
+
});
|
1218
|
+
}
|
1219
|
+
return obj;
|
1220
|
+
},
|
1221
|
+
|
1222
|
+
/**
|
1223
|
+
* For rectangles, VML uses a shape for rect to overcome bugs and rotation problems
|
1224
|
+
*/
|
1225
|
+
createElement: function(nodeName) {
|
1226
|
+
return nodeName === 'rect' ?
|
1227
|
+
this.symbol(nodeName) :
|
1228
|
+
SVGRenderer.prototype.createElement.call(this, nodeName);
|
1229
|
+
},
|
1230
|
+
|
1231
|
+
/**
|
1232
|
+
* In the VML renderer, each child of an inverted div (group) is inverted
|
1233
|
+
* @param {Object} element
|
1234
|
+
* @param {Object} parentNode
|
1235
|
+
*/
|
1236
|
+
invertChild: function(element, parentNode) {
|
1237
|
+
var ren = this,
|
1238
|
+
parentStyle = parentNode.style,
|
1239
|
+
imgStyle = element.tagName === 'IMG' && element.style; // #1111
|
1240
|
+
|
1241
|
+
css(element, {
|
1242
|
+
flip: 'x',
|
1243
|
+
left: pInt(parentStyle.width) - (imgStyle ? pInt(imgStyle.top) : 1),
|
1244
|
+
top: pInt(parentStyle.height) - (imgStyle ? pInt(imgStyle.left) : 1),
|
1245
|
+
rotation: -90
|
1246
|
+
});
|
1247
|
+
|
1248
|
+
// Recursively invert child elements, needed for nested composite
|
1249
|
+
// shapes like box plots and error bars. #1680, #1806.
|
1250
|
+
each(element.childNodes, function(child) {
|
1251
|
+
ren.invertChild(child, element);
|
1252
|
+
});
|
1253
|
+
},
|
1254
|
+
|
1255
|
+
/**
|
1256
|
+
* Symbol definitions that override the parent SVG renderer's symbols
|
1257
|
+
*
|
1258
|
+
*/
|
1259
|
+
symbols: {
|
1260
|
+
// VML specific arc function
|
1261
|
+
arc: function(x, y, w, h, options) {
|
1262
|
+
var start = options.start,
|
1263
|
+
end = options.end,
|
1264
|
+
radius = options.r || w || h,
|
1265
|
+
innerRadius = options.innerR,
|
1266
|
+
cosStart = Math.cos(start),
|
1267
|
+
sinStart = Math.sin(start),
|
1268
|
+
cosEnd = Math.cos(end),
|
1269
|
+
sinEnd = Math.sin(end),
|
1270
|
+
ret;
|
1271
|
+
|
1272
|
+
if (end - start === 0) { // no angle, don't show it.
|
1273
|
+
return ['x'];
|
1274
|
+
}
|
1275
|
+
|
1276
|
+
ret = [
|
1277
|
+
'wa', // clockwise arc to
|
1278
|
+
x - radius, // left
|
1279
|
+
y - radius, // top
|
1280
|
+
x + radius, // right
|
1281
|
+
y + radius, // bottom
|
1282
|
+
x + radius * cosStart, // start x
|
1283
|
+
y + radius * sinStart, // start y
|
1284
|
+
x + radius * cosEnd, // end x
|
1285
|
+
y + radius * sinEnd // end y
|
1286
|
+
];
|
1287
|
+
|
1288
|
+
if (options.open && !innerRadius) {
|
1289
|
+
ret.push(
|
1290
|
+
'e',
|
1291
|
+
'M',
|
1292
|
+
x, // - innerRadius,
|
1293
|
+
y // - innerRadius
|
1294
|
+
);
|
1295
|
+
}
|
1296
|
+
|
1297
|
+
ret.push(
|
1298
|
+
'at', // anti clockwise arc to
|
1299
|
+
x - innerRadius, // left
|
1300
|
+
y - innerRadius, // top
|
1301
|
+
x + innerRadius, // right
|
1302
|
+
y + innerRadius, // bottom
|
1303
|
+
x + innerRadius * cosEnd, // start x
|
1304
|
+
y + innerRadius * sinEnd, // start y
|
1305
|
+
x + innerRadius * cosStart, // end x
|
1306
|
+
y + innerRadius * sinStart, // end y
|
1307
|
+
'x', // finish path
|
1308
|
+
'e' // close
|
1309
|
+
);
|
1310
|
+
|
1311
|
+
ret.isArc = true;
|
1312
|
+
return ret;
|
1313
|
+
|
1314
|
+
},
|
1315
|
+
// Add circle symbol path. This performs significantly faster than v:oval.
|
1316
|
+
circle: function(x, y, w, h, wrapper) {
|
1317
|
+
|
1318
|
+
if (wrapper && defined(wrapper.r)) {
|
1319
|
+
w = h = 2 * wrapper.r;
|
1320
|
+
}
|
1321
|
+
|
1322
|
+
// Center correction, #1682
|
1323
|
+
if (wrapper && wrapper.isCircle) {
|
1324
|
+
x -= w / 2;
|
1325
|
+
y -= h / 2;
|
1326
|
+
}
|
1327
|
+
|
1328
|
+
// Return the path
|
1329
|
+
return [
|
1330
|
+
'wa', // clockwisearcto
|
1331
|
+
x, // left
|
1332
|
+
y, // top
|
1333
|
+
x + w, // right
|
1334
|
+
y + h, // bottom
|
1335
|
+
x + w, // start x
|
1336
|
+
y + h / 2, // start y
|
1337
|
+
x + w, // end x
|
1338
|
+
y + h / 2, // end y
|
1339
|
+
'e' // close
|
1340
|
+
];
|
1341
|
+
},
|
1342
|
+
/**
|
1343
|
+
* Add rectangle symbol path which eases rotation and omits arcsize problems
|
1344
|
+
* compared to the built-in VML roundrect shape. When borders are not rounded,
|
1345
|
+
* use the simpler square path, else use the callout path without the arrow.
|
1346
|
+
*/
|
1347
|
+
rect: function(x, y, w, h, options) {
|
1348
|
+
return SVGRenderer.prototype.symbols[!defined(options) || !options.r ? 'square' : 'callout'].call(0, x, y, w, h, options);
|
1349
|
+
}
|
1350
|
+
}
|
1351
|
+
};
|
1352
|
+
H.VMLRenderer = VMLRenderer = function() {
|
1353
|
+
this.init.apply(this, arguments);
|
1354
|
+
};
|
1355
|
+
VMLRenderer.prototype = merge(SVGRenderer.prototype, VMLRendererExtension);
|
1356
|
+
|
1357
|
+
// general renderer
|
1358
|
+
H.Renderer = VMLRenderer;
|
1359
|
+
}
|
1360
|
+
|
1361
|
+
// This method is used with exporting in old IE, when emulating SVG (see #2314)
|
1362
|
+
SVGRenderer.prototype.measureSpanWidth = function(text, styles) {
|
1363
|
+
var measuringSpan = doc.createElement('span'),
|
1364
|
+
offsetWidth,
|
1365
|
+
textNode = doc.createTextNode(text);
|
1366
|
+
|
1367
|
+
measuringSpan.appendChild(textNode);
|
1368
|
+
css(measuringSpan, styles);
|
1369
|
+
this.box.appendChild(measuringSpan);
|
1370
|
+
offsetWidth = measuringSpan.offsetWidth;
|
1371
|
+
discardElement(measuringSpan); // #2463
|
1372
|
+
return offsetWidth;
|
1373
|
+
};
|
1374
|
+
|
1375
|
+
|
1376
|
+
|
1377
|
+
}(Highcharts));
|
1378
|
+
}));
|