exporting.src.js 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667
  1. /**
  2. * @license Highcharts JS v6.1.0 (2018-04-13)
  3. * Exporting module
  4. *
  5. * (c) 2010-2017 Torstein Honsi
  6. *
  7. * License: www.highcharts.com/license
  8. */
  9. 'use strict';
  10. (function (factory) {
  11. if (typeof module === 'object' && module.exports) {
  12. module.exports = factory;
  13. } else {
  14. factory(Highcharts);
  15. }
  16. }(function (Highcharts) {
  17. (function (H) {
  18. /**
  19. * Exporting module
  20. *
  21. * (c) 2010-2017 Torstein Honsi
  22. *
  23. * License: www.highcharts.com/license
  24. */
  25. /* eslint indent:0 */
  26. // create shortcuts
  27. var defaultOptions = H.defaultOptions,
  28. doc = H.doc,
  29. Chart = H.Chart,
  30. addEvent = H.addEvent,
  31. removeEvent = H.removeEvent,
  32. fireEvent = H.fireEvent,
  33. createElement = H.createElement,
  34. discardElement = H.discardElement,
  35. css = H.css,
  36. merge = H.merge,
  37. pick = H.pick,
  38. each = H.each,
  39. objectEach = H.objectEach,
  40. extend = H.extend,
  41. isTouchDevice = H.isTouchDevice,
  42. win = H.win,
  43. userAgent = win.navigator.userAgent,
  44. SVGRenderer = H.SVGRenderer,
  45. symbols = H.Renderer.prototype.symbols,
  46. isMSBrowser = /Edge\/|Trident\/|MSIE /.test(userAgent),
  47. isFirefoxBrowser = /firefox/i.test(userAgent);
  48. // Add language
  49. extend(defaultOptions.lang, {
  50. /**
  51. * Exporting module only. The text for the menu item to print the chart.
  52. *
  53. * @type {String}
  54. * @default Print chart
  55. * @since 3.0.1
  56. * @apioption lang.printChart
  57. */
  58. printChart: 'Print chart',
  59. /**
  60. * Exporting module only. The text for the PNG download menu item.
  61. *
  62. * @type {String}
  63. * @default Download PNG image
  64. * @since 2.0
  65. * @apioption lang.downloadPNG
  66. */
  67. downloadPNG: 'Download PNG image',
  68. /**
  69. * Exporting module only. The text for the JPEG download menu item.
  70. *
  71. * @type {String}
  72. * @default Download JPEG image
  73. * @since 2.0
  74. * @apioption lang.downloadJPEG
  75. */
  76. downloadJPEG: 'Download JPEG image',
  77. /**
  78. * Exporting module only. The text for the PDF download menu item.
  79. *
  80. * @type {String}
  81. * @default Download PDF document
  82. * @since 2.0
  83. * @apioption lang.downloadPDF
  84. */
  85. downloadPDF: 'Download PDF document',
  86. /**
  87. * Exporting module only. The text for the SVG download menu item.
  88. *
  89. * @type {String}
  90. * @default Download SVG vector image
  91. * @since 2.0
  92. * @apioption lang.downloadSVG
  93. */
  94. downloadSVG: 'Download SVG vector image',
  95. /**
  96. * Exporting module menu. The tooltip title for the context menu holding
  97. * print and export menu items.
  98. *
  99. * @type {String}
  100. * @default Chart context menu
  101. * @since 3.0
  102. * @apioption lang.contextButtonTitle
  103. */
  104. contextButtonTitle: 'Chart context menu'
  105. });
  106. // Buttons and menus are collected in a separate config option set called
  107. // 'navigation'. This can be extended later to add control buttons like zoom and
  108. // pan right click menus.
  109. defaultOptions.navigation = {
  110. buttonOptions: {
  111. theme: {},
  112. /**
  113. * Whether to enable buttons.
  114. *
  115. * @type {Boolean}
  116. * @sample highcharts/navigation/buttonoptions-enabled/
  117. * Exporting module loaded but buttons disabled
  118. * @default true
  119. * @since 2.0
  120. * @apioption navigation.buttonOptions.enabled
  121. */
  122. /**
  123. * The pixel size of the symbol on the button.
  124. *
  125. * @type {Number}
  126. * @sample highcharts/navigation/buttonoptions-height/
  127. * Bigger buttons
  128. * @default 14
  129. * @since 2.0
  130. * @apioption navigation.buttonOptions.symbolSize
  131. */
  132. symbolSize: 14,
  133. /**
  134. * The x position of the center of the symbol inside the button.
  135. *
  136. * @type {Number}
  137. * @sample highcharts/navigation/buttonoptions-height/
  138. * Bigger buttons
  139. * @default 12.5
  140. * @since 2.0
  141. * @apioption navigation.buttonOptions.symbolX
  142. */
  143. symbolX: 12.5,
  144. /**
  145. * The y position of the center of the symbol inside the button.
  146. *
  147. * @type {Number}
  148. * @sample highcharts/navigation/buttonoptions-height/
  149. * Bigger buttons
  150. * @default 10.5
  151. * @since 2.0
  152. * @apioption navigation.buttonOptions.symbolY
  153. */
  154. symbolY: 10.5,
  155. /**
  156. * Alignment for the buttons.
  157. *
  158. * @validvalue ["left", "center", "right"]
  159. * @type {String}
  160. * @sample highcharts/navigation/buttonoptions-align/
  161. * Center aligned
  162. * @default right
  163. * @since 2.0
  164. * @apioption navigation.buttonOptions.align
  165. */
  166. align: 'right',
  167. /**
  168. * The pixel spacing between buttons.
  169. *
  170. * @type {Number}
  171. * @default 3
  172. * @since 2.0
  173. * @apioption navigation.buttonOptions.buttonSpacing
  174. */
  175. buttonSpacing: 3,
  176. /**
  177. * Pixel height of the buttons.
  178. *
  179. * @type {Number}
  180. * @sample highcharts/navigation/buttonoptions-height/
  181. * Bigger buttons
  182. * @default 22
  183. * @since 2.0
  184. * @apioption navigation.buttonOptions.height
  185. */
  186. height: 22,
  187. /**
  188. * A text string to add to the individual button.
  189. *
  190. * @type {String}
  191. * @sample highcharts/exporting/buttons-text/
  192. * Full text button
  193. * @sample highcharts/exporting/buttons-text-symbol/
  194. * Combined symbol and text
  195. * @default null
  196. * @since 3.0
  197. * @apioption navigation.buttonOptions.text
  198. */
  199. /**
  200. * The vertical offset of the button's position relative to its
  201. * `verticalAlign`.
  202. *
  203. * @type {Number}
  204. * @sample highcharts/navigation/buttonoptions-verticalalign/
  205. * Buttons at lower right
  206. * @default 0
  207. * @since 2.0
  208. * @apioption navigation.buttonOptions.y
  209. */
  210. /**
  211. * The vertical alignment of the buttons. Can be one of "top", "middle"
  212. * or "bottom".
  213. *
  214. * @validvalue ["top", "middle", "bottom"]
  215. * @type {String}
  216. * @sample highcharts/navigation/buttonoptions-verticalalign/
  217. * Buttons at lower right
  218. * @default top
  219. * @since 2.0
  220. * @apioption navigation.buttonOptions.verticalAlign
  221. */
  222. verticalAlign: 'top',
  223. /**
  224. * The pixel width of the button.
  225. *
  226. * @type {Number}
  227. * @sample highcharts/navigation/buttonoptions-height/
  228. * Bigger buttons
  229. * @default 24
  230. * @since 2.0
  231. * @apioption navigation.buttonOptions.width
  232. */
  233. width: 24
  234. }
  235. };
  236. // Presentational attributes
  237. merge(true, defaultOptions.navigation,
  238. /**
  239. * A collection of options for buttons and menus appearing in the exporting
  240. * module.
  241. * @type {Object}
  242. * @optionparent navigation
  243. */
  244. {
  245. /**
  246. * CSS styles for the popup menu appearing by default when the export
  247. * icon is clicked. This menu is rendered in HTML.
  248. *
  249. * @type {CSSObject}
  250. * @see In styled mode, the menu is styled with the `.highcharts-menu`
  251. * class.
  252. * @sample highcharts/navigation/menustyle/ Light gray menu background
  253. * @default { "border": "1px solid #999999", "background": "#ffffff", "padding": "5px 0" }
  254. * @since 2.0
  255. */
  256. menuStyle: {
  257. border: '1px solid #999999',
  258. background: '#ffffff',
  259. padding: '5px 0'
  260. },
  261. /**
  262. * CSS styles for the individual items within the popup menu appearing
  263. * by default when the export icon is clicked. The menu items are rendered
  264. * in HTML.
  265. *
  266. * @type {CSSObject}
  267. * @see In styled mode, the menu items are styled with the
  268. * `.highcharts-menu-item` class.
  269. * @sample {highcharts} highcharts/navigation/menuitemstyle/
  270. * Add a grey stripe to the left
  271. * @default { "padding": "0.5em 1em", "color": "#333333", "background": "none" }
  272. * @since 2.0
  273. */
  274. menuItemStyle: {
  275. padding: '0.5em 1em',
  276. background: 'none',
  277. color: '#333333',
  278. /**
  279. * Defaults to `14px` on touch devices and `11px` on desktop.
  280. * @type {String}
  281. */
  282. fontSize: isTouchDevice ? '14px' : '11px',
  283. transition: 'background 250ms, color 250ms'
  284. },
  285. /**
  286. * CSS styles for the hover state of the individual items within the
  287. * popup menu appearing by default when the export icon is clicked.
  288. * The menu items are rendered in HTML.
  289. *
  290. * @type {CSSObject}
  291. * @see In styled mode, the menu items are styled with the
  292. * `.highcharts-menu-item` class.
  293. * @sample highcharts/navigation/menuitemhoverstyle/ Bold text on hover
  294. * @default { "background": "#335cad", "color": "#ffffff" }
  295. * @since 2.0
  296. */
  297. menuItemHoverStyle: {
  298. background: '#335cad',
  299. color: '#ffffff'
  300. },
  301. /**
  302. * A collection of options for buttons appearing in the exporting module.
  303. *
  304. *
  305. * In styled mode, the buttons are styled with the
  306. * `.highcharts-contextbutton` and `.highcharts-button-symbol` classes.
  307. *
  308. */
  309. buttonOptions: {
  310. /**
  311. * Fill color for the symbol within the button.
  312. *
  313. * @type {Color}
  314. * @sample highcharts/navigation/buttonoptions-symbolfill/
  315. * Blue symbol stroke for one of the buttons
  316. * @default #666666
  317. * @since 2.0
  318. */
  319. symbolFill: '#666666',
  320. /**
  321. * The color of the symbol's stroke or line.
  322. *
  323. * @type {Color}
  324. * @sample highcharts/navigation/buttonoptions-symbolstroke/
  325. * Blue symbol stroke
  326. * @default #666666
  327. * @since 2.0
  328. */
  329. symbolStroke: '#666666',
  330. /**
  331. * The pixel stroke width of the symbol on the button.
  332. *
  333. * @type {Number}
  334. * @sample highcharts/navigation/buttonoptions-height/
  335. * Bigger buttons
  336. * @default 1
  337. * @since 2.0
  338. */
  339. symbolStrokeWidth: 3,
  340. /**
  341. * A configuration object for the button theme. The object accepts
  342. * SVG properties like `stroke-width`, `stroke` and `fill`. Tri-state
  343. * button styles are supported by the `states.hover` and `states.select`
  344. * objects.
  345. *
  346. * @type {Object}
  347. * @sample highcharts/navigation/buttonoptions-theme/
  348. * Theming the buttons
  349. * @since 3.0
  350. */
  351. theme: {
  352. /**
  353. * The default fill exists only to capture hover events.
  354. * @type {String}
  355. */
  356. fill: '#ffffff',
  357. /**
  358. * @type {String}
  359. */
  360. stroke: 'none',
  361. /**
  362. * @type {Number}
  363. * @default 5
  364. */
  365. padding: 5
  366. }
  367. }
  368. });
  369. // Add the export related options
  370. /**
  371. * Options for the exporting module. For an overview on the matter, see
  372. * [the docs](http://www.highcharts.com/docs/export-module/export-module-overview).
  373. * @type {Object}
  374. * @optionparent exporting
  375. */
  376. defaultOptions.exporting = {
  377. /**
  378. * Experimental setting to allow HTML inside the chart (added through
  379. * the `useHTML` options), directly in the exported image. This allows
  380. * you to preserve complicated HTML structures like tables or bi-directional
  381. * text in exported charts.
  382. *
  383. * Disclaimer: The HTML is rendered in a `foreignObject` tag in the
  384. * generated SVG. The official export server is based on PhantomJS,
  385. * which supports this, but other SVG clients, like Batik, does not
  386. * support it. This also applies to downloaded SVG that you want to
  387. * open in a desktop client.
  388. *
  389. * @type {Boolean}
  390. * @default false
  391. * @since 4.1.8
  392. * @apioption exporting.allowHTML
  393. */
  394. /**
  395. * Additional chart options to be merged into an exported chart. For
  396. * example, a common use case is to add data labels to improve readability
  397. * of the exported chart, or to add a printer-friendly color scheme.
  398. *
  399. * @type {Object}
  400. * @sample {highcharts} highcharts/exporting/chartoptions-data-labels/
  401. * Added data labels
  402. * @sample {highstock} highcharts/exporting/chartoptions-data-labels/
  403. * Added data labels
  404. * @default null
  405. * @apioption exporting.chartOptions
  406. */
  407. /**
  408. * Whether to enable the exporting module. Disabling the module will
  409. * hide the context button, but API methods will still be available.
  410. *
  411. * @type {Boolean}
  412. * @sample {highcharts} highcharts/exporting/enabled-false/
  413. * Exporting module is loaded but disabled
  414. * @sample {highstock} highcharts/exporting/enabled-false/
  415. * Exporting module is loaded but disabled
  416. * @default true
  417. * @since 2.0
  418. * @apioption exporting.enabled
  419. */
  420. /**
  421. * Function to call if the offline-exporting module fails to export
  422. * a chart on the client side, and [fallbackToExportServer](
  423. * #exporting.fallbackToExportServer) is disabled. If left undefined, an
  424. * exception is thrown instead.
  425. *
  426. * @type {Function}
  427. * @see [fallbackToExportServer](#exporting.fallbackToExportServer)
  428. * @default undefined
  429. * @since 5.0.0
  430. * @apioption exporting.error
  431. */
  432. /**
  433. * Whether or not to fall back to the export server if the offline-exporting
  434. * module is unable to export the chart on the client side.
  435. *
  436. * @type {Boolean}
  437. * @default true
  438. * @since 4.1.8
  439. * @apioption exporting.fallbackToExportServer
  440. */
  441. /**
  442. * The filename, without extension, to use for the exported chart.
  443. *
  444. * @type {String}
  445. * @sample {highcharts} highcharts/exporting/filename/ Custom file name
  446. * @sample {highstock} highcharts/exporting/filename/ Custom file name
  447. * @default chart
  448. * @since 2.0
  449. * @apioption exporting.filename
  450. */
  451. /**
  452. * An object containing additional attributes for the POST form that
  453. * sends the SVG to the export server. For example, a `target` can be
  454. * set to make sure the generated image is received in another frame,
  455. * or a custom `enctype` or `encoding` can be set.
  456. *
  457. * @type {Object}
  458. * @since 3.0.8
  459. * @apioption exporting.formAttributes
  460. */
  461. /**
  462. * Path where Highcharts will look for export module dependencies to
  463. * load on demand if they don't already exist on `window`. Should currently
  464. * point to location of [CanVG](https://github.com/canvg/canvg) library,
  465. * [RGBColor.js](https://github.com/canvg/canvg), [jsPDF](https://github.
  466. * com/yWorks/jsPDF) and [svg2pdf.js](https://github.com/yWorks/svg2pdf.
  467. * js), required for client side export in certain browsers.
  468. *
  469. * @type {String}
  470. * @default https://code.highcharts.com/{version}/lib
  471. * @since 5.0.0
  472. * @apioption exporting.libURL
  473. */
  474. /**
  475. * Analogous to [sourceWidth](#exporting.sourceWidth).
  476. *
  477. * @type {Number}
  478. * @since 3.0
  479. * @apioption exporting.sourceHeight
  480. */
  481. /**
  482. * The width of the original chart when exported, unless an explicit
  483. * [chart.width](#chart.width) is set. The width exported raster image
  484. * is then multiplied by [scale](#exporting.scale).
  485. *
  486. * @type {Number}
  487. * @sample {highcharts} highcharts/exporting/sourcewidth/ Source size demo
  488. * @sample {highstock} highcharts/exporting/sourcewidth/ Source size demo
  489. * @sample {highmaps} maps/exporting/sourcewidth/ Source size demo
  490. * @since 3.0
  491. * @apioption exporting.sourceWidth
  492. */
  493. /**
  494. * The pixel width of charts exported to PNG or JPG. As of Highcharts
  495. * 3.0, the default pixel width is a function of the [chart.width](
  496. * #chart.width) or [exporting.sourceWidth](#exporting.sourceWidth) and the
  497. * [exporting.scale](#exporting.scale).
  498. *
  499. * @type {Number}
  500. * @sample {highcharts} highcharts/exporting/width/
  501. * Export to 200px wide images
  502. * @sample {highstock} highcharts/exporting/width/
  503. * Export to 200px wide images
  504. * @default undefined
  505. * @since 2.0
  506. * @apioption exporting.width
  507. */
  508. /**
  509. * Default MIME type for exporting if `chart.exportChart()` is called
  510. * without specifying a `type` option. Possible values are `image/png`,
  511. * `image/jpeg`, `application/pdf` and `image/svg+xml`.
  512. *
  513. * @validvalue ["image/png", "image/jpeg", "application/pdf", "image/svg+xml"]
  514. * @since 2.0
  515. */
  516. type: 'image/png',
  517. /**
  518. * The URL for the server module converting the SVG string to an image
  519. * format. By default this points to Highchart's free web service.
  520. *
  521. * @type {String}
  522. * @default https://export.highcharts.com
  523. * @since 2.0
  524. */
  525. url: 'https://export.highcharts.com/',
  526. /**
  527. * When printing the chart from the menu item in the burger menu, if
  528. * the on-screen chart exceeds this width, it is resized. After printing
  529. * or cancelled, it is restored. The default width makes the chart
  530. * fit into typical paper format. Note that this does not affect the
  531. * chart when printing the web page as a whole.
  532. *
  533. * @type {Number}
  534. * @default 780
  535. * @since 4.2.5
  536. */
  537. printMaxWidth: 780,
  538. /**
  539. * Defines the scale or zoom factor for the exported image compared
  540. * to the on-screen display. While for instance a 600px wide chart
  541. * may look good on a website, it will look bad in print. The default
  542. * scale of 2 makes this chart export to a 1200px PNG or JPG.
  543. *
  544. * @see [chart.width](#chart.width),
  545. * [exporting.sourceWidth](#exporting.sourceWidth)
  546. * @sample {highcharts} highcharts/exporting/scale/ Scale demonstrated
  547. * @sample {highstock} highcharts/exporting/scale/ Scale demonstrated
  548. * @sample {highmaps} maps/exporting/scale/ Scale demonstrated
  549. * @since 3.0
  550. */
  551. scale: 2,
  552. /**
  553. * Options for the export related buttons, print and export. In addition
  554. * to the default buttons listed here, custom buttons can be added.
  555. * See [navigation.buttonOptions](#navigation.buttonOptions) for general
  556. * options.
  557. *
  558. */
  559. buttons: {
  560. /**
  561. * Options for the export button.
  562. *
  563. * In styled mode, export button styles can be applied with the
  564. * `.highcharts-contextbutton` class.
  565. *
  566. * @extends navigation.buttonOptions
  567. */
  568. contextButton: {
  569. /**
  570. * A click handler callback to use on the button directly instead of
  571. * the popup menu.
  572. *
  573. * @type {Function}
  574. * @sample highcharts/exporting/buttons-contextbutton-onclick/
  575. * Skip the menu and export the chart directly
  576. * @since 2.0
  577. * @apioption exporting.buttons.contextButton.onclick
  578. */
  579. /**
  580. * See [navigation.buttonOptions.symbolFill](
  581. * #navigation.buttonOptions.symbolFill).
  582. *
  583. * @type {Color}
  584. * @default #666666
  585. * @since 2.0
  586. * @apioption exporting.buttons.contextButton.symbolFill
  587. */
  588. /**
  589. * The horizontal position of the button relative to the `align`
  590. * option.
  591. *
  592. * @type {Number}
  593. * @default -10
  594. * @since 2.0
  595. * @apioption exporting.buttons.contextButton.x
  596. */
  597. /**
  598. * The class name of the context button.
  599. * @type {String}
  600. */
  601. className: 'highcharts-contextbutton',
  602. /**
  603. * The class name of the menu appearing from the button.
  604. * @type {String}
  605. */
  606. menuClassName: 'highcharts-contextmenu',
  607. /**
  608. * The symbol for the button. Points to a definition function in
  609. * the `Highcharts.Renderer.symbols` collection. The default
  610. * `exportIcon` function is part of the exporting module.
  611. *
  612. * @validvalue ["circle", "square", "diamond", "triangle", "triangle-down", "menu"]
  613. * @type {String}
  614. * @sample highcharts/exporting/buttons-contextbutton-symbol/
  615. * Use a circle for symbol
  616. * @sample highcharts/exporting/buttons-contextbutton-symbol-custom/
  617. * Custom shape as symbol
  618. * @default menu
  619. * @since 2.0
  620. */
  621. symbol: 'menu',
  622. /**
  623. * The key to a [lang](#lang) option setting that is used for the
  624. * button's title tooltip. When the key is `contextButtonTitle`, it
  625. * refers to [lang.contextButtonTitle](#lang.contextButtonTitle)
  626. * that defaults to "Chart context menu".
  627. * @type {String}
  628. */
  629. _titleKey: 'contextButtonTitle',
  630. /**
  631. * A collection of strings pointing to config options for the menu
  632. * items. The config options are defined in the
  633. * `menuItemDefinitions` option.
  634. *
  635. * By default, there is the "Print" menu item plus one menu item
  636. * for each of the available export types.
  637. *
  638. * Defaults to
  639. * <pre>
  640. * [
  641. * 'printChart',
  642. * 'separator',
  643. * 'downloadPNG',
  644. * 'downloadJPEG',
  645. * 'downloadPDF',
  646. * 'downloadSVG'
  647. * ]
  648. * </pre>
  649. *
  650. * @type {Array<String>|Array<Object>}
  651. * @sample {highcharts} highcharts/exporting/menuitemdefinitions/
  652. * Menu item definitions
  653. * @sample {highstock} highcharts/exporting/menuitemdefinitions/
  654. * Menu item definitions
  655. * @sample {highmaps} highcharts/exporting/menuitemdefinitions/
  656. * Menu item definitions
  657. * @since 2.0
  658. */
  659. menuItems: [
  660. 'printChart',
  661. 'separator',
  662. 'downloadPNG',
  663. 'downloadJPEG',
  664. 'downloadPDF',
  665. 'downloadSVG'
  666. ]
  667. }
  668. },
  669. /**
  670. * An object consisting of definitions for the menu items in the context
  671. * menu. Each key value pair has a `key` that is referenced in the
  672. * [menuItems](#exporting.buttons.contextButton.menuItems) setting,
  673. * and a `value`, which is an object with the following properties:
  674. *
  675. * <dl>
  676. *
  677. * <dt>onclick</dt>
  678. *
  679. * <dd>The click handler for the menu item</dd>
  680. *
  681. * <dt>text</dt>
  682. *
  683. * <dd>The text for the menu item</dd>
  684. *
  685. * <dt>textKey</dt>
  686. *
  687. * <dd>If internationalization is required, the key to a language string
  688. * </dd>
  689. *
  690. * </dl>
  691. *
  692. * @type {Object}
  693. * @sample {highcharts} highcharts/exporting/menuitemdefinitions/
  694. * Menu item definitions
  695. * @sample {highstock} highcharts/exporting/menuitemdefinitions/
  696. * Menu item definitions
  697. * @sample {highmaps} highcharts/exporting/menuitemdefinitions/
  698. * Menu item definitions
  699. * @since 5.0.13
  700. */
  701. menuItemDefinitions: {
  702. /**
  703. * @ignore
  704. */
  705. printChart: {
  706. textKey: 'printChart',
  707. onclick: function () {
  708. this.print();
  709. }
  710. },
  711. /**
  712. * @ignore
  713. */
  714. separator: {
  715. separator: true
  716. },
  717. /**
  718. * @ignore
  719. */
  720. downloadPNG: {
  721. textKey: 'downloadPNG',
  722. onclick: function () {
  723. this.exportChart();
  724. }
  725. },
  726. /**
  727. * @ignore
  728. */
  729. downloadJPEG: {
  730. textKey: 'downloadJPEG',
  731. onclick: function () {
  732. this.exportChart({
  733. type: 'image/jpeg'
  734. });
  735. }
  736. },
  737. /**
  738. * @ignore
  739. */
  740. downloadPDF: {
  741. textKey: 'downloadPDF',
  742. onclick: function () {
  743. this.exportChart({
  744. type: 'application/pdf'
  745. });
  746. }
  747. },
  748. /**
  749. * @ignore
  750. */
  751. downloadSVG: {
  752. textKey: 'downloadSVG',
  753. onclick: function () {
  754. this.exportChart({
  755. type: 'image/svg+xml'
  756. });
  757. }
  758. }
  759. }
  760. };
  761. /**
  762. * Fires after a chart is printed through the context menu item or the
  763. * `Chart.print` method. Requires the exporting module.
  764. *
  765. * @type {Function}
  766. * @context Chart
  767. * @sample highcharts/chart/events-beforeprint-afterprint/
  768. * Rescale the chart to print
  769. * @since 4.1.0
  770. * @apioption chart.events.afterPrint
  771. */
  772. /**
  773. * Fires before a chart is printed through the context menu item or
  774. * the `Chart.print` method. Requires the exporting module.
  775. *
  776. * @type {Function}
  777. * @context Chart
  778. * @sample highcharts/chart/events-beforeprint-afterprint/
  779. * Rescale the chart to print
  780. * @since 4.1.0
  781. * @apioption chart.events.beforePrint
  782. */
  783. // Add the H.post utility
  784. H.post = function (url, data, formAttributes) {
  785. // create the form
  786. var form = createElement('form', merge({
  787. method: 'post',
  788. action: url,
  789. enctype: 'multipart/form-data'
  790. }, formAttributes), {
  791. display: 'none'
  792. }, doc.body);
  793. // add the data
  794. objectEach(data, function (val, name) {
  795. createElement('input', {
  796. type: 'hidden',
  797. name: name,
  798. value: val
  799. }, null, form);
  800. });
  801. // submit
  802. form.submit();
  803. // clean up
  804. discardElement(form);
  805. };
  806. extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
  807. /**
  808. * Exporting module only. A collection of fixes on the produced SVG to
  809. * account for expando properties, browser bugs, VML problems and other.
  810. * Returns a cleaned SVG.
  811. *
  812. * @private
  813. */
  814. sanitizeSVG: function (svg, options) {
  815. // Move HTML into a foreignObject
  816. if (options && options.exporting && options.exporting.allowHTML) {
  817. var html = svg.match(/<\/svg>(.*?$)/);
  818. if (html && html[1]) {
  819. html = '<foreignObject x="0" y="0" ' +
  820. 'width="' + options.chart.width + '" ' +
  821. 'height="' + options.chart.height + '">' +
  822. '<body xmlns="http://www.w3.org/1999/xhtml">' +
  823. html[1] +
  824. '</body>' +
  825. '</foreignObject>';
  826. svg = svg.replace('</svg>', html + '</svg>');
  827. }
  828. }
  829. svg = svg
  830. .replace(/zIndex="[^"]+"/g, '')
  831. .replace(/isShadow="[^"]+"/g, '')
  832. .replace(/symbolName="[^"]+"/g, '')
  833. .replace(/jQuery[0-9]+="[^"]+"/g, '')
  834. .replace(/url\(("|&quot;)(\S+)("|&quot;)\)/g, 'url($2)')
  835. .replace(/url\([^#]+#/g, 'url(#')
  836. .replace(
  837. /<svg /,
  838. '<svg xmlns:xlink="http://www.w3.org/1999/xlink" '
  839. )
  840. .replace(/ (|NS[0-9]+\:)href=/g, ' xlink:href=') // #3567
  841. .replace(/\n/, ' ')
  842. // Any HTML added to the container after the SVG (#894)
  843. .replace(/<\/svg>.*?$/, '</svg>')
  844. // Batik doesn't support rgba fills and strokes (#3095)
  845. .replace(
  846. /(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, // eslint-disable-line max-len
  847. '$1="rgb($2)" $1-opacity="$3"'
  848. )
  849. // Replace HTML entities, issue #347
  850. .replace(/&nbsp;/g, '\u00A0') // no-break space
  851. .replace(/&shy;/g, '\u00AD'); // soft hyphen
  852. // Further sanitize for oldIE
  853. if (this.ieSanitizeSVG) {
  854. svg = this.ieSanitizeSVG(svg);
  855. }
  856. return svg;
  857. },
  858. /**
  859. * Return the unfiltered innerHTML of the chart container. Used as hook for
  860. * plugins. In styled mode, it also takes care of inlining CSS style rules.
  861. *
  862. * @see Chart#getSVG
  863. *
  864. * @returns {String}
  865. * The unfiltered SVG of the chart.
  866. */
  867. getChartHTML: function () {
  868. return this.container.innerHTML;
  869. },
  870. /**
  871. * Return an SVG representation of the chart.
  872. *
  873. * @param chartOptions {Options}
  874. * Additional chart options for the generated SVG representation.
  875. * For collections like `xAxis`, `yAxis` or `series`, the additional
  876. * options is either merged in to the orininal item of the same
  877. * `id`, or to the first item if a common id is not found.
  878. * @return {String}
  879. * The SVG representation of the rendered chart.
  880. * @sample highcharts/members/chart-getsvg/
  881. * View the SVG from a button
  882. */
  883. getSVG: function (chartOptions) {
  884. var chart = this,
  885. chartCopy,
  886. sandbox,
  887. svg,
  888. seriesOptions,
  889. sourceWidth,
  890. sourceHeight,
  891. cssWidth,
  892. cssHeight,
  893. // Copy the options and add extra options
  894. options = merge(chart.options, chartOptions);
  895. // create a sandbox where a new chart will be generated
  896. sandbox = createElement('div', null, {
  897. position: 'absolute',
  898. top: '-9999em',
  899. width: chart.chartWidth + 'px',
  900. height: chart.chartHeight + 'px'
  901. }, doc.body);
  902. // get the source size
  903. cssWidth = chart.renderTo.style.width;
  904. cssHeight = chart.renderTo.style.height;
  905. sourceWidth = options.exporting.sourceWidth ||
  906. options.chart.width ||
  907. (/px$/.test(cssWidth) && parseInt(cssWidth, 10)) ||
  908. 600;
  909. sourceHeight = options.exporting.sourceHeight ||
  910. options.chart.height ||
  911. (/px$/.test(cssHeight) && parseInt(cssHeight, 10)) ||
  912. 400;
  913. // override some options
  914. extend(options.chart, {
  915. animation: false,
  916. renderTo: sandbox,
  917. forExport: true,
  918. renderer: 'SVGRenderer',
  919. width: sourceWidth,
  920. height: sourceHeight
  921. });
  922. options.exporting.enabled = false; // hide buttons in print
  923. delete options.data; // #3004
  924. // prepare for replicating the chart
  925. options.series = [];
  926. each(chart.series, function (serie) {
  927. seriesOptions = merge(serie.userOptions, { // #4912
  928. animation: false, // turn off animation
  929. enableMouseTracking: false,
  930. showCheckbox: false,
  931. visible: serie.visible
  932. });
  933. // Used for the navigator series that has its own option set
  934. if (!seriesOptions.isInternal) {
  935. options.series.push(seriesOptions);
  936. }
  937. });
  938. // Assign an internal key to ensure a one-to-one mapping (#5924)
  939. each(chart.axes, function (axis) {
  940. if (!axis.userOptions.internalKey) { // #6444
  941. axis.userOptions.internalKey = H.uniqueKey();
  942. }
  943. });
  944. // generate the chart copy
  945. chartCopy = new H.Chart(options, chart.callback);
  946. // Axis options and series options (#2022, #3900, #5982)
  947. if (chartOptions) {
  948. each(['xAxis', 'yAxis', 'series'], function (coll) {
  949. var collOptions = {};
  950. if (chartOptions[coll]) {
  951. collOptions[coll] = chartOptions[coll];
  952. chartCopy.update(collOptions);
  953. }
  954. });
  955. }
  956. // Reflect axis extremes in the export (#5924)
  957. each(chart.axes, function (axis) {
  958. var axisCopy = H.find(chartCopy.axes, function (copy) {
  959. return copy.options.internalKey ===
  960. axis.userOptions.internalKey;
  961. }),
  962. extremes = axis.getExtremes(),
  963. userMin = extremes.userMin,
  964. userMax = extremes.userMax;
  965. if (axisCopy && (userMin !== undefined || userMax !== undefined)) {
  966. axisCopy.setExtremes(userMin, userMax, true, false);
  967. }
  968. });
  969. // Get the SVG from the container's innerHTML
  970. svg = chartCopy.getChartHTML();
  971. svg = chart.sanitizeSVG(svg, options);
  972. // free up memory
  973. options = null;
  974. chartCopy.destroy();
  975. discardElement(sandbox);
  976. return svg;
  977. },
  978. getSVGForExport: function (options, chartOptions) {
  979. var chartExportingOptions = this.options.exporting;
  980. return this.getSVG(merge(
  981. { chart: { borderRadius: 0 } },
  982. chartExportingOptions.chartOptions,
  983. chartOptions,
  984. {
  985. exporting: {
  986. sourceWidth: (
  987. (options && options.sourceWidth) ||
  988. chartExportingOptions.sourceWidth
  989. ),
  990. sourceHeight: (
  991. (options && options.sourceHeight) ||
  992. chartExportingOptions.sourceHeight
  993. )
  994. }
  995. }
  996. ));
  997. },
  998. /**
  999. * Exporting module required. Submit an SVG version of the chart to a server
  1000. * along with some parameters for conversion.
  1001. * @param {Object} exportingOptions
  1002. * Exporting options in addition to those defined in {@link
  1003. * https://api.highcharts.com/highcharts/exporting|exporting}.
  1004. * @param {String} exportingOptions.filename
  1005. * The file name for the export without extension.
  1006. * @param {String} exportingOptions.url
  1007. * The URL for the server module to do the conversion.
  1008. * @param {Number} exportingOptions.width
  1009. * The width of the PNG or JPG image generated on the server.
  1010. * @param {String} exportingOptions.type
  1011. * The MIME type of the converted image.
  1012. * @param {Number} exportingOptions.sourceWidth
  1013. * The pixel width of the source (in-page) chart.
  1014. * @param {Number} exportingOptions.sourceHeight
  1015. * The pixel height of the source (in-page) chart.
  1016. * @param {Options} chartOptions
  1017. * Additional chart options for the exported chart. For example a
  1018. * different background color can be added here, or `dataLabels`
  1019. * for export only.
  1020. *
  1021. * @sample highcharts/members/chart-exportchart/
  1022. * Export with no options
  1023. * @sample highcharts/members/chart-exportchart-filename/
  1024. * PDF type and custom filename
  1025. * @sample highcharts/members/chart-exportchart-custom-background/
  1026. * Different chart background in export
  1027. * @sample stock/members/chart-exportchart/
  1028. * Export with Highstock
  1029. */
  1030. exportChart: function (exportingOptions, chartOptions) {
  1031. var svg = this.getSVGForExport(exportingOptions, chartOptions);
  1032. // merge the options
  1033. exportingOptions = merge(this.options.exporting, exportingOptions);
  1034. // do the post
  1035. H.post(exportingOptions.url, {
  1036. filename: exportingOptions.filename || 'chart',
  1037. type: exportingOptions.type,
  1038. // IE8 fails to post undefined correctly, so use 0
  1039. width: exportingOptions.width || 0,
  1040. scale: exportingOptions.scale,
  1041. svg: svg
  1042. }, exportingOptions.formAttributes);
  1043. },
  1044. /**
  1045. * Exporting module required. Clears away other elements in the page and
  1046. * prints the chart as it is displayed. By default, when the exporting
  1047. * module is enabled, a context button with a drop down menu in the upper
  1048. * right corner accesses this function.
  1049. *
  1050. * @sample highcharts/members/chart-print/
  1051. * Print from a HTML button
  1052. */
  1053. print: function () {
  1054. var chart = this,
  1055. container = chart.container,
  1056. origDisplay = [],
  1057. origParent = container.parentNode,
  1058. body = doc.body,
  1059. childNodes = body.childNodes,
  1060. printMaxWidth = chart.options.exporting.printMaxWidth,
  1061. resetParams,
  1062. handleMaxWidth;
  1063. if (chart.isPrinting) { // block the button while in printing mode
  1064. return;
  1065. }
  1066. chart.isPrinting = true;
  1067. chart.pointer.reset(null, 0);
  1068. fireEvent(chart, 'beforePrint');
  1069. // Handle printMaxWidth
  1070. handleMaxWidth = printMaxWidth && chart.chartWidth > printMaxWidth;
  1071. if (handleMaxWidth) {
  1072. resetParams = [chart.options.chart.width, undefined, false];
  1073. chart.setSize(printMaxWidth, undefined, false);
  1074. }
  1075. // hide all body content
  1076. each(childNodes, function (node, i) {
  1077. if (node.nodeType === 1) {
  1078. origDisplay[i] = node.style.display;
  1079. node.style.display = 'none';
  1080. }
  1081. });
  1082. // pull out the chart
  1083. body.appendChild(container);
  1084. // print
  1085. win.focus(); // #1510
  1086. win.print();
  1087. // allow the browser to prepare before reverting
  1088. setTimeout(function () {
  1089. // put the chart back in
  1090. origParent.appendChild(container);
  1091. // restore all body content
  1092. each(childNodes, function (node, i) {
  1093. if (node.nodeType === 1) {
  1094. node.style.display = origDisplay[i];
  1095. }
  1096. });
  1097. chart.isPrinting = false;
  1098. // Reset printMaxWidth
  1099. if (handleMaxWidth) {
  1100. chart.setSize.apply(chart, resetParams);
  1101. }
  1102. fireEvent(chart, 'afterPrint');
  1103. }, 1000);
  1104. },
  1105. /**
  1106. * Display a popup menu for choosing the export type.
  1107. *
  1108. * @private
  1109. *
  1110. * @param {String} className An identifier for the menu
  1111. * @param {Array} items A collection with text and onclicks for the items
  1112. * @param {Number} x The x position of the opener button
  1113. * @param {Number} y The y position of the opener button
  1114. * @param {Number} width The width of the opener button
  1115. * @param {Number} height The height of the opener button
  1116. */
  1117. contextMenu: function (className, items, x, y, width, height, button) {
  1118. var chart = this,
  1119. navOptions = chart.options.navigation,
  1120. chartWidth = chart.chartWidth,
  1121. chartHeight = chart.chartHeight,
  1122. cacheName = 'cache-' + className,
  1123. menu = chart[cacheName],
  1124. menuPadding = Math.max(width, height), // for mouse leave detection
  1125. innerMenu,
  1126. hide,
  1127. menuStyle;
  1128. // create the menu only the first time
  1129. if (!menu) {
  1130. // create a HTML element above the SVG
  1131. chart[cacheName] = menu = createElement('div', {
  1132. className: className
  1133. }, {
  1134. position: 'absolute',
  1135. zIndex: 1000,
  1136. padding: menuPadding + 'px'
  1137. }, chart.container);
  1138. innerMenu = createElement(
  1139. 'div',
  1140. { className: 'highcharts-menu' },
  1141. null,
  1142. menu
  1143. );
  1144. // Presentational CSS
  1145. css(innerMenu, extend({
  1146. MozBoxShadow: '3px 3px 10px #888',
  1147. WebkitBoxShadow: '3px 3px 10px #888',
  1148. boxShadow: '3px 3px 10px #888'
  1149. }, navOptions.menuStyle));
  1150. // hide on mouse out
  1151. hide = function () {
  1152. css(menu, { display: 'none' });
  1153. if (button) {
  1154. button.setState(0);
  1155. }
  1156. chart.openMenu = false;
  1157. };
  1158. // Hide the menu some time after mouse leave (#1357)
  1159. chart.exportEvents.push(
  1160. addEvent(menu, 'mouseleave', function () {
  1161. menu.hideTimer = setTimeout(hide, 500);
  1162. }),
  1163. addEvent(menu, 'mouseenter', function () {
  1164. H.clearTimeout(menu.hideTimer);
  1165. }),
  1166. // Hide it on clicking or touching outside the menu (#2258,
  1167. // #2335, #2407)
  1168. addEvent(doc, 'mouseup', function (e) {
  1169. if (!chart.pointer.inClass(e.target, className)) {
  1170. hide();
  1171. }
  1172. })
  1173. );
  1174. // create the items
  1175. each(items, function (item) {
  1176. if (typeof item === 'string') {
  1177. item = chart.options.exporting.menuItemDefinitions[item];
  1178. }
  1179. if (H.isObject(item, true)) {
  1180. var element;
  1181. if (item.separator) {
  1182. element = createElement('hr', null, null, innerMenu);
  1183. } else {
  1184. element = createElement('div', {
  1185. className: 'highcharts-menu-item',
  1186. onclick: function (e) {
  1187. if (e) { // IE7
  1188. e.stopPropagation();
  1189. }
  1190. hide();
  1191. if (item.onclick) {
  1192. item.onclick.apply(chart, arguments);
  1193. }
  1194. },
  1195. innerHTML: (
  1196. item.text ||
  1197. chart.options.lang[item.textKey]
  1198. )
  1199. }, null, innerMenu);
  1200. element.onmouseover = function () {
  1201. css(this, navOptions.menuItemHoverStyle);
  1202. };
  1203. element.onmouseout = function () {
  1204. css(this, navOptions.menuItemStyle);
  1205. };
  1206. css(element, extend({
  1207. cursor: 'pointer'
  1208. }, navOptions.menuItemStyle));
  1209. }
  1210. // Keep references to menu divs to be able to destroy them
  1211. chart.exportDivElements.push(element);
  1212. }
  1213. });
  1214. // Keep references to menu and innerMenu div to be able to destroy
  1215. // them
  1216. chart.exportDivElements.push(innerMenu, menu);
  1217. chart.exportMenuWidth = menu.offsetWidth;
  1218. chart.exportMenuHeight = menu.offsetHeight;
  1219. }
  1220. menuStyle = { display: 'block' };
  1221. // if outside right, right align it
  1222. if (x + chart.exportMenuWidth > chartWidth) {
  1223. menuStyle.right = (chartWidth - x - width - menuPadding) + 'px';
  1224. } else {
  1225. menuStyle.left = (x - menuPadding) + 'px';
  1226. }
  1227. // if outside bottom, bottom align it
  1228. if (
  1229. y + height + chart.exportMenuHeight > chartHeight &&
  1230. button.alignOptions.verticalAlign !== 'top'
  1231. ) {
  1232. menuStyle.bottom = (chartHeight - y - menuPadding) + 'px';
  1233. } else {
  1234. menuStyle.top = (y + height - menuPadding) + 'px';
  1235. }
  1236. css(menu, menuStyle);
  1237. chart.openMenu = true;
  1238. },
  1239. /**
  1240. * Add the export button to the chart, with options.
  1241. *
  1242. * @private
  1243. */
  1244. addButton: function (options) {
  1245. var chart = this,
  1246. renderer = chart.renderer,
  1247. btnOptions = merge(chart.options.navigation.buttonOptions, options),
  1248. onclick = btnOptions.onclick,
  1249. menuItems = btnOptions.menuItems,
  1250. symbol,
  1251. button,
  1252. symbolSize = btnOptions.symbolSize || 12;
  1253. if (!chart.btnCount) {
  1254. chart.btnCount = 0;
  1255. }
  1256. // Keeps references to the button elements
  1257. if (!chart.exportDivElements) {
  1258. chart.exportDivElements = [];
  1259. chart.exportSVGElements = [];
  1260. }
  1261. if (btnOptions.enabled === false) {
  1262. return;
  1263. }
  1264. var attr = btnOptions.theme,
  1265. states = attr.states,
  1266. hover = states && states.hover,
  1267. select = states && states.select,
  1268. callback;
  1269. delete attr.states;
  1270. if (onclick) {
  1271. callback = function (e) {
  1272. e.stopPropagation();
  1273. onclick.call(chart, e);
  1274. };
  1275. } else if (menuItems) {
  1276. callback = function () {
  1277. chart.contextMenu(
  1278. button.menuClassName,
  1279. menuItems,
  1280. button.translateX,
  1281. button.translateY,
  1282. button.width,
  1283. button.height,
  1284. button
  1285. );
  1286. button.setState(2);
  1287. };
  1288. }
  1289. if (btnOptions.text && btnOptions.symbol) {
  1290. attr.paddingLeft = pick(attr.paddingLeft, 25);
  1291. } else if (!btnOptions.text) {
  1292. extend(attr, {
  1293. width: btnOptions.width,
  1294. height: btnOptions.height,
  1295. padding: 0
  1296. });
  1297. }
  1298. button = renderer
  1299. .button(btnOptions.text, 0, 0, callback, attr, hover, select)
  1300. .addClass(options.className)
  1301. .attr({
  1302. 'stroke-linecap': 'round',
  1303. title: pick(chart.options.lang[btnOptions._titleKey], ''),
  1304. zIndex: 3 // #4955
  1305. });
  1306. button.menuClassName = (
  1307. options.menuClassName ||
  1308. 'highcharts-menu-' + chart.btnCount++
  1309. );
  1310. if (btnOptions.symbol) {
  1311. symbol = renderer.symbol(
  1312. btnOptions.symbol,
  1313. btnOptions.symbolX - (symbolSize / 2),
  1314. btnOptions.symbolY - (symbolSize / 2),
  1315. symbolSize,
  1316. symbolSize,
  1317. // If symbol is an image, scale it (#7957)
  1318. {
  1319. width: symbolSize,
  1320. height: symbolSize
  1321. }
  1322. )
  1323. .addClass('highcharts-button-symbol')
  1324. .attr({
  1325. zIndex: 1
  1326. }).add(button);
  1327. symbol.attr({
  1328. stroke: btnOptions.symbolStroke,
  1329. fill: btnOptions.symbolFill,
  1330. 'stroke-width': btnOptions.symbolStrokeWidth || 1
  1331. });
  1332. }
  1333. button.add()
  1334. .align(extend(btnOptions, {
  1335. width: button.width,
  1336. x: pick(btnOptions.x, chart.buttonOffset) // #1654
  1337. }), true, 'spacingBox');
  1338. chart.buttonOffset += (
  1339. (button.width + btnOptions.buttonSpacing) *
  1340. (btnOptions.align === 'right' ? -1 : 1)
  1341. );
  1342. chart.exportSVGElements.push(button, symbol);
  1343. },
  1344. /**
  1345. * Destroy the export buttons.
  1346. *
  1347. * @private
  1348. */
  1349. destroyExport: function (e) {
  1350. var chart = e ? e.target : this,
  1351. exportSVGElements = chart.exportSVGElements,
  1352. exportDivElements = chart.exportDivElements,
  1353. exportEvents = chart.exportEvents,
  1354. cacheName;
  1355. // Destroy the extra buttons added
  1356. if (exportSVGElements) {
  1357. each(exportSVGElements, function (elem, i) {
  1358. // Destroy and null the svg elements
  1359. if (elem) { // #1822
  1360. elem.onclick = elem.ontouchstart = null;
  1361. cacheName = 'cache-' + elem.menuClassName;
  1362. if (chart[cacheName]) {
  1363. delete chart[cacheName];
  1364. }
  1365. chart.exportSVGElements[i] = elem.destroy();
  1366. }
  1367. });
  1368. exportSVGElements.length = 0;
  1369. }
  1370. // Destroy the divs for the menu
  1371. if (exportDivElements) {
  1372. each(exportDivElements, function (elem, i) {
  1373. // Remove the event handler
  1374. H.clearTimeout(elem.hideTimer); // #5427
  1375. removeEvent(elem, 'mouseleave');
  1376. // Remove inline events
  1377. chart.exportDivElements[i] =
  1378. elem.onmouseout =
  1379. elem.onmouseover =
  1380. elem.ontouchstart =
  1381. elem.onclick = null;
  1382. // Destroy the div by moving to garbage bin
  1383. discardElement(elem);
  1384. });
  1385. exportDivElements.length = 0;
  1386. }
  1387. if (exportEvents) {
  1388. each(exportEvents, function (unbind) {
  1389. unbind();
  1390. });
  1391. exportEvents.length = 0;
  1392. }
  1393. }
  1394. });
  1395. symbols.menu = function (x, y, width, height) {
  1396. var arr = [
  1397. 'M', x, y + 2.5,
  1398. 'L', x + width, y + 2.5,
  1399. 'M', x, y + height / 2 + 0.5,
  1400. 'L', x + width, y + height / 2 + 0.5,
  1401. 'M', x, y + height - 1.5,
  1402. 'L', x + width, y + height - 1.5
  1403. ];
  1404. return arr;
  1405. };
  1406. // Add the buttons on chart load
  1407. Chart.prototype.renderExporting = function () {
  1408. var chart = this,
  1409. exportingOptions = chart.options.exporting,
  1410. buttons = exportingOptions.buttons,
  1411. isDirty = chart.isDirtyExporting || !chart.exportSVGElements;
  1412. chart.buttonOffset = 0;
  1413. if (chart.isDirtyExporting) {
  1414. chart.destroyExport();
  1415. }
  1416. if (isDirty && exportingOptions.enabled !== false) {
  1417. chart.exportEvents = [];
  1418. objectEach(buttons, function (button) {
  1419. chart.addButton(button);
  1420. });
  1421. chart.isDirtyExporting = false;
  1422. }
  1423. // Destroy the export elements at chart destroy
  1424. addEvent(chart, 'destroy', chart.destroyExport);
  1425. };
  1426. Chart.prototype.callbacks.push(function (chart) {
  1427. function update(prop, options, redraw) {
  1428. chart.isDirtyExporting = true;
  1429. merge(true, chart.options[prop], options);
  1430. if (pick(redraw, true)) {
  1431. chart.redraw();
  1432. }
  1433. }
  1434. chart.renderExporting();
  1435. addEvent(chart, 'redraw', chart.renderExporting);
  1436. // Add update methods to handle chart.update and chart.exporting.update
  1437. // and chart.navigation.update.
  1438. each(['exporting', 'navigation'], function (prop) {
  1439. chart[prop] = {
  1440. update: function (options, redraw) {
  1441. update(prop, options, redraw);
  1442. }
  1443. };
  1444. });
  1445. // Uncomment this to see a button directly below the chart, for quick
  1446. // testing of export
  1447. /*
  1448. if (!chart.renderer.forExport) {
  1449. var button;
  1450. // View SVG Image
  1451. button = doc.createElement('button');
  1452. button.innerHTML = 'View SVG Image';
  1453. chart.renderTo.parentNode.appendChild(button);
  1454. button.onclick = function () {
  1455. var div = doc.createElement('div');
  1456. div.innerHTML = chart.getSVGForExport();
  1457. chart.renderTo.parentNode.appendChild(div);
  1458. };
  1459. // View SVG Source
  1460. button = doc.createElement('button');
  1461. button.innerHTML = 'View SVG Source';
  1462. chart.renderTo.parentNode.appendChild(button);
  1463. button.onclick = function () {
  1464. var pre = doc.createElement('pre');
  1465. pre.innerHTML = chart.getSVGForExport()
  1466. .replace(/</g, '\n&lt;')
  1467. .replace(/>/g, '&gt;');
  1468. chart.renderTo.parentNode.appendChild(pre);
  1469. };
  1470. }
  1471. //*/
  1472. });
  1473. }(Highcharts));
  1474. }));