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.
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
+ }));