oldie.src.js 59 KB

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