highcharts-rails 5.0.14 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +60 -0
  3. data/Rakefile +54 -5
  4. data/app/assets/images/highcharts/earth.svg +432 -0
  5. data/app/assets/javascripts/highcharts.js +5103 -3147
  6. data/app/assets/javascripts/highcharts/highcharts-3d.js +930 -277
  7. data/app/assets/javascripts/highcharts/highcharts-more.js +1374 -249
  8. data/app/assets/javascripts/highcharts/lib/canvg.js +3073 -0
  9. data/app/assets/javascripts/highcharts/lib/jspdf.js +16624 -0
  10. data/app/assets/javascripts/highcharts/lib/rgbcolor.js +299 -0
  11. data/app/assets/javascripts/highcharts/lib/svg2pdf.js +3488 -0
  12. data/app/assets/javascripts/highcharts/modules/accessibility.js +654 -212
  13. data/app/assets/javascripts/highcharts/modules/annotations.js +1552 -274
  14. data/app/assets/javascripts/highcharts/modules/boost-canvas.js +773 -0
  15. data/app/assets/javascripts/highcharts/modules/boost.js +636 -210
  16. data/app/assets/javascripts/highcharts/modules/broken-axis.js +2 -2
  17. data/app/assets/javascripts/highcharts/modules/bullet.js +364 -0
  18. data/app/assets/javascripts/highcharts/modules/data.js +766 -38
  19. data/app/assets/javascripts/highcharts/modules/drag-panes.js +588 -0
  20. data/app/assets/javascripts/highcharts/modules/drilldown.js +106 -36
  21. data/app/assets/javascripts/highcharts/modules/export-data.js +597 -0
  22. data/app/assets/javascripts/highcharts/modules/exporting.js +424 -162
  23. data/app/assets/javascripts/highcharts/modules/funnel.js +144 -22
  24. data/app/assets/javascripts/highcharts/modules/gantt.js +1154 -0
  25. data/app/assets/javascripts/highcharts/modules/grid-axis.js +1 -1
  26. data/app/assets/javascripts/highcharts/modules/heatmap.js +406 -80
  27. data/app/assets/javascripts/highcharts/modules/histogram-bellcurve.js +513 -0
  28. data/app/assets/javascripts/highcharts/modules/item-series.js +126 -0
  29. data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +31 -13
  30. data/app/assets/javascripts/highcharts/modules/offline-exporting.js +179 -57
  31. data/app/assets/javascripts/highcharts/modules/oldie.js +1378 -0
  32. data/app/assets/javascripts/highcharts/modules/overlapping-datalabels.js +8 -6
  33. data/app/assets/javascripts/highcharts/modules/parallel-coordinates.js +494 -0
  34. data/app/assets/javascripts/highcharts/modules/pareto.js +275 -0
  35. data/app/assets/javascripts/highcharts/modules/sankey.js +641 -0
  36. data/app/assets/javascripts/highcharts/modules/series-label.js +355 -145
  37. data/app/assets/javascripts/highcharts/modules/solid-gauge.js +122 -1
  38. data/app/assets/javascripts/highcharts/modules/static-scale.js +64 -0
  39. data/app/assets/javascripts/highcharts/modules/stock.js +1944 -676
  40. data/app/assets/javascripts/highcharts/modules/streamgraph.js +139 -0
  41. data/app/assets/javascripts/highcharts/modules/sunburst.js +2403 -0
  42. data/app/assets/javascripts/highcharts/modules/tilemap.js +1199 -0
  43. data/app/assets/javascripts/highcharts/modules/treemap.js +538 -134
  44. data/app/assets/javascripts/highcharts/modules/variable-pie.js +490 -0
  45. data/app/assets/javascripts/highcharts/modules/variwide.js +283 -0
  46. data/app/assets/javascripts/highcharts/modules/vector.js +294 -0
  47. data/app/assets/javascripts/highcharts/modules/windbarb.js +490 -0
  48. data/app/assets/javascripts/highcharts/modules/wordcloud.js +681 -0
  49. data/app/assets/javascripts/highcharts/modules/xrange.js +615 -0
  50. data/app/assets/javascripts/highcharts/themes/avocado.js +54 -0
  51. data/app/assets/javascripts/highcharts/themes/dark-blue.js +6 -6
  52. data/app/assets/javascripts/highcharts/themes/dark-green.js +6 -6
  53. data/app/assets/javascripts/highcharts/themes/dark-unica.js +6 -6
  54. data/app/assets/javascripts/highcharts/themes/gray.js +14 -10
  55. data/app/assets/javascripts/highcharts/themes/grid-light.js +6 -6
  56. data/app/assets/javascripts/highcharts/themes/grid.js +7 -5
  57. data/app/assets/javascripts/highcharts/themes/sand-signika.js +8 -7
  58. data/app/assets/javascripts/highcharts/themes/skies.js +15 -9
  59. data/app/assets/javascripts/highcharts/themes/sunset.js +53 -0
  60. data/app/assets/stylesheets/highcharts/highcharts.css +802 -0
  61. data/app/assets/stylesheets/highcharts/highcharts.scss +665 -0
  62. data/lib/highcharts/version.rb +1 -1
  63. 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
+ }));