oldie.src.js 50 KB

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