oldie.src.js 53 KB

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